/*
* The Kuali Financial System, a comprehensive financial management system for higher education.
*
* Copyright 2005-2014 The Kuali Foundation
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.kuali.kfs.module.tem.report.service.impl;
import static org.kuali.kfs.module.tem.TemConstants.TravelReimbursementParameters.LODGING_TYPE_CODES;
import static org.kuali.kfs.module.tem.TemConstants.TravelReimbursementParameters.TRANSPORTATION_TYPE_CODES;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collection;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.Map;
import java.util.TreeMap;
import org.apache.log4j.Logger;
import org.kuali.kfs.module.tem.TemPropertyConstants;
import org.kuali.kfs.module.tem.businessobject.ActualExpense;
import org.kuali.kfs.module.tem.businessobject.PerDiemExpense;
import org.kuali.kfs.module.tem.document.TravelDocument;
import org.kuali.kfs.module.tem.document.TravelReimbursementDocument;
import org.kuali.kfs.module.tem.document.service.TravelDocumentService;
import org.kuali.kfs.module.tem.report.SummaryByDayReport;
import org.kuali.kfs.module.tem.report.service.SummaryByDayReportService;
import org.kuali.kfs.sys.service.impl.KfsParameterConstants;
import org.kuali.kfs.sys.util.KfsDateUtils;
import org.kuali.rice.core.api.config.property.ConfigurationService;
import org.kuali.rice.core.api.util.type.KualiDecimal;
import org.kuali.rice.coreservice.framework.parameter.ParameterService;
import org.kuali.rice.kim.api.identity.PersonService;
import org.springframework.transaction.annotation.Transactional;
/**
* Service implementation of ExpenseSummaryReportService.
*/
@Transactional
public class SummaryByDayReportServiceImpl implements SummaryByDayReportService {
public static Logger LOG = Logger.getLogger(SummaryByDayReportServiceImpl.class);
protected ConfigurationService configurationService;
protected ParameterService parameterService;
protected PersonService personService;
protected TravelDocumentService travelDocumentService;
protected SimpleDateFormat monthDay = new SimpleDateFormat("MM/dd");
public ConfigurationService getConfigurationService() {
return configurationService;
}
public void setConfigurationService(final ConfigurationService ConfigurationService) {
this.configurationService = ConfigurationService;
}
public PersonService getPersonService() {
return personService;
}
public void setPersonService(final PersonService personService) {
this.personService = personService;
}
@Override
public SummaryByDayReport buildReport(final TravelDocument travelDocument) {
LOG.debug("Building a "+ SummaryByDayReport.class+ " report for trip id "+ travelDocument.getTravelDocumentIdentifier());
final SummaryByDayReport retval = new SummaryByDayReport();
retval.setBeginDate(travelDocument.getTripBegin() != null ? KfsDateUtils.clearTimeFields(travelDocument.getTripBegin()) : new Date());
retval.setEndDate(travelDocument.getTripEnd() != null ? KfsDateUtils.clearTimeFields(travelDocument.getTripEnd()) : new Date());
retval.setTripId(travelDocument.getTravelDocumentIdentifier().toString());
retval.setPurpose(travelDocument.getReportPurpose() == null ? "" : travelDocument.getReportPurpose());
retval.setInstitution(getParameterService().getParameterValueAsString(KfsParameterConstants.FINANCIAL_SYSTEM_ALL.class, KfsParameterConstants.INSTITUTION_NAME));
travelDocument.refreshReferenceObject(TemPropertyConstants.ACTUAL_EXPENSES);
final Collection<SummaryByDayReport.Detail> transportation = new ArrayList<SummaryByDayReport.Detail>();
final Collection<SummaryByDayReport.Detail> other = new ArrayList<SummaryByDayReport.Detail>();
final Collection<SummaryByDayReport.Detail> lodging = new ArrayList<SummaryByDayReport.Detail>();
final Collection<SummaryByDayReport.Detail> meals = new ArrayList<SummaryByDayReport.Detail>();
final Collection<SummaryByDayReport.Detail> summary = new ArrayList<SummaryByDayReport.Detail>();
final Collection<SummaryByDayReport.Detail> perDiemWeeklyTotal = new ArrayList<SummaryByDayReport.Detail>();
final Map<String, KualiDecimal> summaryData = new TreeMap<String, KualiDecimal>();
Calendar cal = new GregorianCalendar();
cal.setTime(retval.getBeginDate());
int weekCount = 1;
KualiDecimal milageWeekTotal = KualiDecimal.ZERO;
KualiDecimal lodgingWeekTotal = KualiDecimal.ZERO;
KualiDecimal mealWeekTotal = KualiDecimal.ZERO;
Calendar firstDayOfWeek = new GregorianCalendar();
firstDayOfWeek.setTime(retval.getBeginDate());
firstDayOfWeek.add(Calendar.DAY_OF_WEEK, Calendar.MONDAY - cal.get(Calendar.DAY_OF_WEEK));
Calendar lastDayOfWeek = new GregorianCalendar();
lastDayOfWeek.setTime(firstDayOfWeek.getTime());
lastDayOfWeek.add(Calendar.DAY_OF_WEEK, 6);
firstDayOfWeek.add(Calendar.MILLISECOND, -1);
lastDayOfWeek.add(Calendar.MILLISECOND, 1);
int perDiemExpensesSize = travelDocument.getPerDiemExpenses().size();
for (final PerDiemExpense perDiemExpense: travelDocument.getPerDiemExpenses()){
if(!(perDiemExpense.getMileageDate().after(firstDayOfWeek.getTime()) && perDiemExpense.getMileageDate().before(lastDayOfWeek.getTime()) && --perDiemExpensesSize > 0)){
String weekCountFormat = weekCount < 10 ? "0"+weekCount : ""+weekCount;
SummaryByDayReport.Detail perDiemMilageWeekTotal = new SummaryByDayReport.Detail("Per Diem Milage", milageWeekTotal, "week " + weekCountFormat);
perDiemWeeklyTotal.add(perDiemMilageWeekTotal);
SummaryByDayReport.Detail perDiemLodgingWeekTotal = new SummaryByDayReport.Detail("Per Diem Lodging", lodgingWeekTotal, "week " + weekCountFormat);
perDiemWeeklyTotal.add(perDiemLodgingWeekTotal);
SummaryByDayReport.Detail perDiemMealWeekTotal = new SummaryByDayReport.Detail("Per Diem Meals & Incidentals", mealWeekTotal, "week " + weekCountFormat);
perDiemWeeklyTotal.add(perDiemMealWeekTotal);
firstDayOfWeek.add(Calendar.DAY_OF_WEEK, 7);
lastDayOfWeek.add(Calendar.DAY_OF_WEEK, 7);
weekCount++;
// reset totals
milageWeekTotal = KualiDecimal.ZERO;
lodgingWeekTotal = KualiDecimal.ZERO;
mealWeekTotal = KualiDecimal.ZERO;
}
final String expenseDate = monthDay.format(cal.getTime());
SummaryByDayReport.Detail perDiemMilage = new SummaryByDayReport.Detail("Per Diem Milage", perDiemExpense.getMileageTotal(), expenseDate);
transportation.add(perDiemMilage);
SummaryByDayReport.Detail perDiemLodging = new SummaryByDayReport.Detail("Per Diem Lodging", perDiemExpense.getLodgingTotal(), expenseDate);
lodging.add(perDiemLodging);
SummaryByDayReport.Detail perDiemMeal = new SummaryByDayReport.Detail("Per Diem Meals & Incidentals", perDiemExpense.getMealsAndIncidentals(), expenseDate);
meals.add(perDiemMeal);
milageWeekTotal = milageWeekTotal.add(perDiemExpense.getMileageTotal());
lodgingWeekTotal = lodgingWeekTotal.add(perDiemExpense.getLodgingTotal());
mealWeekTotal = mealWeekTotal.add(perDiemExpense.getMealsAndIncidentals());
incrementSummary(summaryData, expenseDate, perDiemExpense.getDailyTotal());
cal.add(Calendar.DAY_OF_YEAR, 1);
}
for (final ActualExpense expense : travelDocument.getActualExpenses()) {
expense.refreshReferenceObject(TemPropertyConstants.EXPENSE_TYPE_OBJECT_CODE);
final String expenseDate = monthDay.format(expense.getExpenseDate());
final SummaryByDayReport.Detail detail = new SummaryByDayReport.Detail(expense.getExpenseTypeObjectCode().getExpenseType().getName()==null?"":expense.getExpenseTypeObjectCode().getExpenseType().getName(), new KualiDecimal(expense.getExpenseAmount().bigDecimalValue().multiply(expense.getCurrencyRate())), expenseDate);
if (isTransportationExpense(expense)) {
transportation.add(detail);
}
else if (isLodgingExpense(expense)) {
lodging.add(detail);
}
else if (isMealsExpense(expense)) {
meals.add(detail);
}
else {
other.add(detail);
}
incrementSummary(summaryData, expenseDate, new KualiDecimal(expense.getExpenseAmount().bigDecimalValue().multiply(expense.getCurrencyRate())));
}
for (final String expenseDate : summaryData.keySet()) {
final KualiDecimal expenseAmount = summaryData.get(expenseDate);
final SummaryByDayReport.Detail detail = new SummaryByDayReport.Detail("TOTAL of all above expenses", expenseAmount, expenseDate);
summary.add(detail);
}
if (perDiemWeeklyTotal.size() > 0) {
LOG.debug("Adding "+ perDiemWeeklyTotal.size()+ " per diem weekly total");
retval.setWeeklyTotal(perDiemWeeklyTotal);
}
if (other.size() > 0) {
LOG.debug("Adding "+ other.size()+ " other expenses");
retval.setOtherExpenses(other);
}
if (transportation.size() > 0) {
LOG.debug("Adding "+ transportation.size()+ " transportation expenses");
retval.setTransportation(transportation);
}
if (lodging.size() > 0) {
LOG.debug("Adding "+ lodging.size()+ " lodging expenses");
retval.setLodging(lodging);
}
if (meals.size() > 0) {
LOG.debug("Adding "+ meals.size()+ " meals expenses");
retval.setMeals(meals);
}
if (summary.size() > 0) {
LOG.debug("Adding "+ summary.size()+ " summary");
retval.setSummary(summary);
}
return retval;
}
protected void incrementSummary(final Map<String, KualiDecimal> summaryData, String expenseDate, KualiDecimal expense) {
KualiDecimal summaryAmount = summaryData.get(expenseDate);
if (summaryAmount == null) {
summaryAmount = KualiDecimal.ZERO;
}
summaryAmount = summaryAmount.add(expense);
LOG.debug("Adding "+ summaryAmount+ " for "+ expenseDate+ " to summary data");
summaryData.put(expenseDate, summaryAmount);
}
protected boolean isTransportationExpense(final ActualExpense expense) {
LOG.debug("Checking if "+ expense+ " is a transportation ");
return expenseTypeCodeMatchesParameter(expense.getExpenseTypeCode(), TRANSPORTATION_TYPE_CODES);
}
protected boolean isLodgingExpense(final ActualExpense expense) {
LOG.debug("Checking if "+ expense+ " is a lodging ");
return expenseTypeCodeMatchesParameter(expense.getExpenseTypeCode(), LODGING_TYPE_CODES);
}
protected boolean isMealsExpense(final ActualExpense expense) {
LOG.debug("Checking if "+ expense+ " is a meal ");
return getTravelDocumentService().isHostedMeal(expense);
}
protected boolean expenseTypeCodeMatchesParameter(final String expenseTypeCode, final String parameter) {
return getParameterService().getParameterValuesAsString(TravelReimbursementDocument.class, parameter).contains(expenseTypeCode);
}
/**
* Gets the parameterService attribute.
*
* @return Returns the parameterService.
*/
public ParameterService getParameterService() {
return parameterService;
}
/**
* Sets the parameterService attribute value.
*
* @param parameterService The parameterService to set.
*/
public void setParameterService(ParameterService parameterService) {
this.parameterService = parameterService;
}
/**
* Gets the travelDocumentService attribute.
*
* @return Returns the travelDocumentService.
*/
public TravelDocumentService getTravelDocumentService() {
return travelDocumentService;
}
/**
* Sets the travelDocumentService attribute value.
*
* @param travelDocumentService The travelDocumentService to set.
*/
public void setTravelDocumentService(TravelDocumentService travelDocumentService) {
this.travelDocumentService = travelDocumentService;
}
}