/*
* 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.DISPLAY_TRAVEL_AUTHORIZATION_ESTIMATE_IN_SUMMARY_REPORT_IND;
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.ParseException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import java.util.List;
import org.apache.log4j.Logger;
import org.kuali.kfs.module.tem.TemConstants;
import org.kuali.kfs.module.tem.TemPropertyConstants;
import org.kuali.kfs.module.tem.businessobject.AccountingDistribution;
import org.kuali.kfs.module.tem.businessobject.ActualExpense;
import org.kuali.kfs.module.tem.businessobject.PerDiemExpense;
import org.kuali.kfs.module.tem.businessobject.TemSourceAccountingLine;
import org.kuali.kfs.module.tem.document.TravelDocument;
import org.kuali.kfs.module.tem.document.TravelReimbursementDocument;
import org.kuali.kfs.module.tem.document.service.TravelAuthorizationService;
import org.kuali.kfs.module.tem.document.service.TravelDocumentService;
import org.kuali.kfs.module.tem.report.ExpenseSummaryReport;
import org.kuali.kfs.module.tem.report.service.ExpenseSummaryReportService;
import org.kuali.kfs.module.tem.service.AccountingDistributionService;
import org.kuali.kfs.sys.service.impl.KfsParameterConstants;
import org.kuali.rice.core.api.config.property.ConfigurationService;
import org.kuali.rice.core.api.datetime.DateTimeService;
import org.kuali.rice.core.api.util.type.KualiDecimal;
import org.kuali.rice.coreservice.framework.parameter.ParameterService;
import org.kuali.rice.kim.api.identity.Person;
import org.kuali.rice.kim.api.identity.PersonService;
import org.kuali.rice.krad.util.ObjectUtils;
import org.springframework.transaction.annotation.Transactional;
/**
* Service implementation of ExpenseSummaryReportService.
*/
@Transactional
public class ExpenseSummaryReportServiceImpl implements ExpenseSummaryReportService {
public static Logger LOG = Logger.getLogger(ExpenseSummaryReportServiceImpl.class);
protected ConfigurationService configurationService;
protected ParameterService parameterService;
protected PersonService personService;
protected TravelAuthorizationService travelAuthorizationService;
protected AccountingDistributionService accountingDistributionService;
protected TravelDocumentService travelDocumentService;
protected DateTimeService dateTimeService;
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;
}
/**
* Creates a {@link ReportInfoHolder} instance that is used with the {@link TravelReportService}
*/
@Override
public ExpenseSummaryReport buildReport(TravelDocument travelDocument) {
LOG.info("Building report objects");
final ExpenseSummaryReport retval = new ExpenseSummaryReport();
if (!ObjectUtils.isNull(travelDocument.getTraveler())) {
retval.setTraveler(travelDocument.getTraveler().getFirstName() + " " + travelDocument.getTraveler().getLastName());
}
final String initiatorId = travelDocument.getDocumentHeader().getWorkflowDocument().getInitiatorPrincipalId();
final Person initiator = getPersonService().getPerson(initiatorId);
retval.setInitiator(initiator.getFirstName() + " " + initiator.getLastName());
try {
retval.setBeginDate(travelDocument.getTripBegin() != null ? dateTimeService.convertToSqlDate (travelDocument.getTripBegin()) : new Date());
retval.setEndDate(travelDocument.getTripEnd() != null ? dateTimeService.convertToSqlDate (travelDocument.getTripEnd()) : new Date());
}catch (ParseException pe) {
LOG.error("error while parsing date " + pe);
}
retval.setLocations(travelDocument.getPrimaryDestinationName());
retval.setPurpose(travelDocument.getReportPurpose() == null ? "" : travelDocument.getReportPurpose());
retval.setTripId(travelDocument.getTravelDocumentIdentifier() + "");
retval.setInstitution(getParameterService().getParameterValueAsString(KfsParameterConstants.FINANCIAL_SYSTEM_ALL.class, KfsParameterConstants.INSTITUTION_NAME));
final Collection<ExpenseSummaryReport.Detail> expenses = new ArrayList<ExpenseSummaryReport.Detail>();
final Collection<ExpenseSummaryReport.Detail> summary = new ArrayList<ExpenseSummaryReport.Detail>();
List<ExpenseSummaryReport.Detail> perDiemMilageList = new ArrayList<ExpenseSummaryReport.Detail>();
List<ExpenseSummaryReport.Detail> perDiemLodgingList = new ArrayList<ExpenseSummaryReport.Detail>();
List<ExpenseSummaryReport.Detail> perDiemMealList = new ArrayList<ExpenseSummaryReport.Detail>();
for (final PerDiemExpense perDiemExpense: travelDocument.getPerDiemExpenses()){
if (perDiemExpense.getMileageTotal().isGreaterThan(KualiDecimal.ZERO)) {
ExpenseSummaryReport.Detail perDiemMilage = new ExpenseSummaryReport.Detail("Per Diem Milage", "TRANSPORTATION", perDiemExpense.getMileageTotal(), perDiemExpense.getMileageDate());
perDiemMilageList.add(perDiemMilage);
}
if (perDiemExpense.getLodgingTotal().isGreaterThan(KualiDecimal.ZERO)) {
ExpenseSummaryReport.Detail perDiemLodging = new ExpenseSummaryReport.Detail("Per Diem Lodging", "LODGING", perDiemExpense.getLodgingTotal(), perDiemExpense.getMileageDate());
perDiemLodgingList.add(perDiemLodging);
}
if (perDiemExpense.getMealsAndIncidentals().isGreaterThan(KualiDecimal.ZERO)) {
ExpenseSummaryReport.Detail perDiemMeal = new ExpenseSummaryReport.Detail("Per Diem Meals", "MEALS", perDiemExpense.getMealsAndIncidentals(), perDiemExpense.getMileageDate());
perDiemMealList.add(perDiemMeal);
}
}
if (perDiemMilageList.size() > 0) {
expenses.addAll(perDiemMilageList);
}
if (perDiemLodgingList.size() > 0) {
expenses.addAll(perDiemLodgingList);
}
if (perDiemMealList.size() > 0) {
expenses.addAll(perDiemMealList);
}
LOG.info("Adding details from other expenses");
LOG.debug("There are "+ travelDocument.getActualExpenses().size() + " other expenses");
for (final ActualExpense expense : travelDocument.getActualExpenses()) {
expense.refreshReferenceObject(TemPropertyConstants.EXPENSE_TYPE_OBJECT_CODE);
if (expense.getNonReimbursable()) {
LOG.debug("Adding detail for non reimbursable item");
expenses.add(new ExpenseSummaryReport.Detail(expense.getExpenseTypeObjectCode().getExpenseType().getName(), "NONREIMBURSABLE", expense.getConvertedAmount(), expense.getExpenseDate()));
}
String expenseType = "OTHER";
if (isTransportationExpense(expense)) {
expenseType = "TRANSPORTATION";
}
else if (isLodgingExpense(expense)) {
expenseType = "LODGING";
}
else if (isMealsExpense(expense)) {
expenseType = "MEALS";
}
expenses.add(new ExpenseSummaryReport.Detail(expense.getExpenseTypeObjectCode().getExpenseType().getName(), expenseType, expense.getConvertedAmount(), expense.getExpenseDate()));
}
final KualiDecimal expenseLimit = travelDocument.getExpenseLimit() == null ? KualiDecimal.ZERO : travelDocument.getExpenseLimit();
final KualiDecimal manualPerDiemAdjustment = travelDocument.getPerDiemAdjustment();
final KualiDecimal ctsTotal = travelDocument.getCTSTotal();
final KualiDecimal corporateCardTotal = travelDocument.getCorporateCardTotal();
final KualiDecimal maxExpense = travelDocument.getReimbursableTotal();
String totalExpenseName = "Owed to Payee";
KualiDecimal owed = maxExpense;
boolean isTR = travelDocument.getDocumentHeader().getWorkflowDocument().getDocumentTypeName().equals(TemConstants.TravelDocTypes.TRAVEL_REIMBURSEMENT_DOCUMENT);
KualiDecimal advanceTotal = KualiDecimal.ZERO;
if (isTR) {
advanceTotal = getTravelDocumentService().getAdvancesTotalFor(travelDocument);
owed = maxExpense.subtract(advanceTotal);
if (advanceTotal.isGreaterThan(maxExpense)) {
totalExpenseName = "Owed to Institution";
owed = advanceTotal.subtract(maxExpense);
}
}
summary.add(new ExpenseSummaryReport.Detail("Expense Total", "SUMMARY", travelDocument.getDocumentGrandTotal(), travelDocument.getTripBegin()));
summary.add (new ExpenseSummaryReport.Detail("Manual Per Diem Adj", "SUMMARY",manualPerDiemAdjustment, travelDocument.getTripBegin()));
summary.add(new ExpenseSummaryReport.Detail("Expense Limit", "SUMMARY", travelDocument.getExpenseLimit(), travelDocument.getTripBegin()));
summary.add(new ExpenseSummaryReport.Detail("Less CTS Charges", "SUMMARY", ctsTotal, travelDocument.getTripBegin()));
summary.add(new ExpenseSummaryReport.Detail("Corp Credit Card Charges", "SUMMARY", corporateCardTotal, travelDocument.getTripBegin()));
if (isTR) {
summary.add(new ExpenseSummaryReport.Detail("Less Advances", "SUMMARY", advanceTotal, travelDocument.getTripBegin()));
}
summary.add(new ExpenseSummaryReport.Detail(totalExpenseName, "SUMMARY", owed, travelDocument.getTripBegin()));
if (isTR) {
final boolean showTAEstimate = getParameterService().getParameterValueAsBoolean(TravelReimbursementDocument.class, DISPLAY_TRAVEL_AUTHORIZATION_ESTIMATE_IN_SUMMARY_REPORT_IND);
if (showTAEstimate) {
TravelDocument ta = getTravelDocumentService().findCurrentTravelAuthorization(travelDocument);
if (ta != null) {
summary.add(new ExpenseSummaryReport.Detail("Expense Total Estimate", "SUMMARY", ta.getDocumentGrandTotal(), ta.getTripBegin()));
}
}
}
List<AccountingDistribution> accountingDistributions = getAccountingDistributionService().buildDistributionFrom(travelDocument);
if(accountingDistributions != null){
for(AccountingDistribution ad : accountingDistributions){
summary.add(new ExpenseSummaryReport.Detail("Distribution: "+ad.getObjectCode(), "OTHER", ad.getSubTotal().abs(), travelDocument.getTripBegin()));
}
}
travelDocument.refreshReferenceObject("sourceAccountingLines");
List<TemSourceAccountingLine> sourceAccountingLines = travelDocument.getSourceAccountingLines();
for(TemSourceAccountingLine account : sourceAccountingLines){
String accountDetails = account.getAccountNumber() + "-" + account.getFinancialObjectCode();
summary.add(new ExpenseSummaryReport.Detail("Account: "+ accountDetails, "OTHER", account.getAmount(), travelDocument.getTripBegin()));
}
if (summary.size() > 0) {
retval.setSummary(summary);
}
if (expenses.size() > 0) {
retval.setData(expenses);
}
return retval;
}
protected boolean isTransportationExpense(final ActualExpense expense) {
LOG.debug("Checking if " + expense+ " is a transportation ");
return expenseTypeCodeMatchesParameter(expense.getExpenseTypeCode(), TRANSPORTATION_TYPE_CODES);
}
protected boolean isMealsExpense(final ActualExpense expense) {
return getTravelDocumentService().isHostedMeal(expense);
}
protected boolean isLodgingExpense(final ActualExpense expense) {
LOG.debug("Checking if " + expense + " is a lodging ");
return expenseTypeCodeMatchesParameter(expense.getExpenseTypeCode(), LODGING_TYPE_CODES);
}
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 travelAuthorizationService attribute.
* @return Returns the travelAuthorizationService.
*/
public TravelAuthorizationService getTravelAuthorizationService() {
return travelAuthorizationService;
}
/**
* Sets the travelAuthorizationService attribute value.
* @param travelAuthorizationService The travelAuthorizationService to set.
*/
public void setTravelAuthorizationService(TravelAuthorizationService travelAuthorizationService) {
this.travelAuthorizationService = travelAuthorizationService;
}
/**
* Gets the travelAuthorizationService attribute.
* @return Returns the travelAuthorizationService.
*/
public TravelDocumentService getTravelDocumentService() {
return travelDocumentService;
}
/**
* Sets the travelDocumentService attribute value.
* @param travelDocumentService The travelDocumentService to set.
*/
public void setTravelDocumentService(TravelDocumentService travelDocumentService) {
this.travelDocumentService = travelDocumentService;
}
public AccountingDistributionService getAccountingDistributionService() {
return accountingDistributionService;
}
public void setAccountingDistributionService(AccountingDistributionService accountingDistributionService) {
this.accountingDistributionService = accountingDistributionService;
}
public void setDateTimeService(DateTimeService dateTimeService) {
this.dateTimeService = dateTimeService;
}
}