/*
* 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.document.service.impl;
import static org.kuali.kfs.module.tem.TemKeyConstants.TA_MESSAGE_CLOSE_DOCUMENT_TEXT;
import static org.kuali.kfs.module.tem.TemPropertyConstants.TRAVEL_DOCUMENT_IDENTIFIER;
import java.beans.PropertyChangeListener;
import java.math.BigDecimal;
import java.sql.Date;
import java.sql.Timestamp;
import java.text.ParseException;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.Callable;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang.time.DateUtils;
import org.apache.log4j.Logger;
import org.kuali.kfs.integration.ar.AccountsReceivableCustomer;
import org.kuali.kfs.integration.ar.AccountsReceivableCustomerAddress;
import org.kuali.kfs.integration.ar.AccountsReceivableCustomerInvoice;
import org.kuali.kfs.integration.ar.AccountsReceivableCustomerInvoiceDetail;
import org.kuali.kfs.integration.ar.AccountsReceivableCustomerInvoiceRecurrenceDetails;
import org.kuali.kfs.integration.ar.AccountsReceivableCustomerType;
import org.kuali.kfs.integration.ar.AccountsReceivableDocumentHeader;
import org.kuali.kfs.integration.ar.AccountsReceivableModuleService;
import org.kuali.kfs.integration.ar.AccountsReceivableOrganizationOptions;
import org.kuali.kfs.integration.ar.AccountsReceivableSystemInformation;
import org.kuali.kfs.module.tem.TemConstants;
import org.kuali.kfs.module.tem.TemConstants.TravelAuthorizationParameters;
import org.kuali.kfs.module.tem.TemConstants.TravelAuthorizationStatusCodeKeys;
import org.kuali.kfs.module.tem.businessobject.AccountingDocumentRelationship;
import org.kuali.kfs.module.tem.businessobject.ExpenseTypeObjectCode;
import org.kuali.kfs.module.tem.businessobject.TemProfile;
import org.kuali.kfs.module.tem.businessobject.TravelAdvance;
import org.kuali.kfs.module.tem.businessobject.TravelerDetail;
import org.kuali.kfs.module.tem.dataaccess.TravelAuthorizationDao;
import org.kuali.kfs.module.tem.document.TravelAuthorizationAmendmentDocument;
import org.kuali.kfs.module.tem.document.TravelAuthorizationCloseDocument;
import org.kuali.kfs.module.tem.document.TravelAuthorizationDocument;
import org.kuali.kfs.module.tem.document.TravelDocument;
import org.kuali.kfs.module.tem.document.TravelReimbursementDocument;
import org.kuali.kfs.module.tem.document.service.AccountingDocumentRelationshipService;
import org.kuali.kfs.module.tem.document.service.TravelAuthorizationService;
import org.kuali.kfs.module.tem.document.service.TravelDocumentService;
import org.kuali.kfs.module.tem.service.TemProfileService;
import org.kuali.kfs.module.tem.service.TravelerService;
import org.kuali.kfs.module.tem.util.MessageUtils;
import org.kuali.kfs.sys.KFSConstants;
import org.kuali.kfs.sys.KFSPropertyConstants;
import org.kuali.kfs.sys.businessobject.AccountingLine;
import org.kuali.kfs.sys.businessobject.FinancialSystemDocumentHeader;
import org.kuali.kfs.sys.businessobject.SourceAccountingLine;
import org.kuali.kfs.sys.context.SpringContext;
import org.kuali.kfs.sys.document.AmountTotaling;
import org.kuali.kfs.sys.document.FinancialSystemTransactionalDocumentBase;
import org.kuali.kfs.sys.document.validation.event.AddAccountingLineEvent;
import org.kuali.kfs.sys.service.UniversityDateService;
import org.kuali.kfs.sys.util.ObjectPopulationUtils;
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.kew.api.WorkflowDocument;
import org.kuali.rice.kew.api.exception.WorkflowException;
import org.kuali.rice.kim.api.identity.IdentityService;
import org.kuali.rice.kim.api.identity.principal.Principal;
import org.kuali.rice.kns.service.DataDictionaryService;
import org.kuali.rice.krad.UserSession;
import org.kuali.rice.krad.bo.Note;
import org.kuali.rice.krad.dao.DocumentDao;
import org.kuali.rice.krad.document.Document;
import org.kuali.rice.krad.service.BusinessObjectService;
import org.kuali.rice.krad.service.DocumentService;
import org.kuali.rice.krad.service.KeyValuesService;
import org.kuali.rice.krad.service.KualiRuleService;
import org.kuali.rice.krad.service.NoteService;
import org.kuali.rice.krad.util.GlobalVariables;
import org.kuali.rice.krad.util.KRADConstants;
import org.kuali.rice.krad.util.ObjectUtils;
import org.kuali.rice.krad.workflow.service.WorkflowDocumentService;
import org.springframework.transaction.annotation.Transactional;
@Transactional
public class TravelAuthorizationServiceImpl implements TravelAuthorizationService {
protected static Logger LOG = Logger.getLogger(TravelAuthorizationServiceImpl.class);
protected BusinessObjectService businessObjectService;
protected AccountsReceivableModuleService accountsReceivableModuleService;
protected ParameterService parameterService;
protected DocumentService documentService;
protected DateTimeService dateTimeService;
protected KualiRuleService kualiRuleService;
protected WorkflowDocumentService workflowDocumentService;
protected UniversityDateService universityDateService;
protected AccountingDocumentRelationshipService accountingDocumentRelationshipService;
protected TemProfileService temProfileService;
protected TravelDocumentService travelDocumentService;
protected DocumentDao documentDao;
protected DataDictionaryService dataDictionaryService;
protected IdentityService identityService;
protected NoteService noteService;
protected TravelAuthorizationDao travelAuthorizationDao;
protected List<PropertyChangeListener> propertyChangeListeners;
/**
* @see org.kuali.kfs.module.tem.document.service.TravelAuthorizationService#createCustomerInvoice(org.kuali.kfs.module.tem.document.TravelAuthorizationDocument)
*/
@Override
public void createCustomerInvoice(TravelAuthorizationDocument travelAuthorizationDocument) {
boolean enableInvoice = parameterService.getParameterValueAsBoolean(TravelAuthorizationDocument.class, TravelAuthorizationParameters.GENERATE_INVOICE_FOR_TRAVEL_ADVANCE_IND);
if (enableInvoice && travelAuthorizationDocument.shouldProcessAdvanceForDocument()) {
KualiDecimal amount = travelAuthorizationDocument.getTravelAdvance().getTravelAdvanceRequested();
if (KualiDecimal.ZERO.isLessThan(amount)) {
TemProfile profile = travelAuthorizationDocument.getTemProfile();
if (profile == null){
//Get the TEM Profile associated with this TA
profile = temProfileService.findTemProfileById(travelAuthorizationDocument.getTemProfileId());
}
AccountsReceivableCustomer customer = profile.getCustomer();
if (ObjectUtils.isNull(customer)) {
customer = createNewCustomer(profile);
// associate customer with traveler
travelAuthorizationDocument.getTraveler().setCustomerNumber(customer.getCustomerNumber());
}
createCustomerInvoiceFromAdvance(travelAuthorizationDocument, travelAuthorizationDocument.getTravelAdvance(), amount);
}
}
}
/**
* Create customer invoice from advance
*
* @param travelAuthorizationDocument
* @param advances
* @param amount
*/
protected void createCustomerInvoiceFromAdvance(final TravelAuthorizationDocument travelAuthorizationDocument, final TravelAdvance advance, final KualiDecimal amount) {
final int numDaysDue = Integer.parseInt(parameterService.getParameterValueAsString(TravelAuthorizationDocument.class, TravelAuthorizationParameters.DUE_DATE_DAYS));
final String invoiceItemCode = parameterService.getParameterValueAsString(TravelAuthorizationDocument.class, TravelAuthorizationParameters.TRAVEL_ADVANCE_INVOICE_ITEM_CODE);
final String processingOrgCode = parameterService.getParameterValueAsString(TravelAuthorizationDocument.class, TravelAuthorizationParameters.TRAVEL_ADVANCE_BILLING_ORGANIZATION);
final String processingChartCode = parameterService.getParameterValueAsString(TravelAuthorizationDocument.class, TravelAuthorizationParameters.TRAVEL_ADVANCE_BILLING_CHART);
try {
GlobalVariables.doInNewGlobalVariables(new UserSession(KFSConstants.SYSTEM_USER), new Callable<Object>() {
@Override
public Object call() {
// need to refactor this so the customer id is stored on the doc, not in travel advances
Calendar cal = Calendar.getInstance();
String customerNumber = travelAuthorizationDocument.getTemProfile().getCustomerNumber();
String orgInvoiceNumber = travelAuthorizationDocument.getTravelDocumentIdentifier();
java.util.Date billingDate = dateTimeService.getCurrentDate();
cal.setTime(travelAuthorizationDocument.getTripEnd());
cal.add(Calendar.DATE, numDaysDue);
java.util.Date dueDate = cal.getTime();
AccountsReceivableCustomerInvoice customerInvoiceDocument = accountsReceivableModuleService.createCustomerInvoiceDocument();
LOG.info("Created customer invoice document " + customerInvoiceDocument.getDocumentNumber());
setupDefaultValuesForNewCustomerInvoiceDocument(customerInvoiceDocument, processingChartCode, processingOrgCode);
customerInvoiceDocument.getDocumentHeader().setOrganizationDocumentNumber(travelAuthorizationDocument.getTravelDocumentIdentifier());
customerInvoiceDocument.getDocumentHeader().setDocumentDescription("Travel Advance - " + travelAuthorizationDocument.getTravelDocumentIdentifier() + " - " + travelAuthorizationDocument.getTraveler().getFirstName() + " " + travelAuthorizationDocument.getTraveler().getLastName());
final int documentDescriptionMaxLength = getDataDictionaryService().getAttributeMaxLength(customerInvoiceDocument.getDocumentHeader().getClass(), KFSPropertyConstants.DOCUMENT_DESCRIPTION);
if (customerInvoiceDocument.getDocumentHeader().getDocumentDescription().length() >= documentDescriptionMaxLength) {
String truncatedDocumentDescription = customerInvoiceDocument.getDocumentHeader().getDocumentDescription().substring(0, documentDescriptionMaxLength-1);
customerInvoiceDocument.getDocumentHeader().setDocumentDescription(truncatedDocumentDescription);
}
customerInvoiceDocument.getAccountsReceivableDocumentHeader().setCustomerNumber(customerNumber);
customerInvoiceDocument.setBillingDate(new java.sql.Date(billingDate.getTime()));
customerInvoiceDocument.setInvoiceDueDate(new java.sql.Date(dueDate.getTime()));
customerInvoiceDocument.setOrganizationInvoiceNumber(orgInvoiceNumber.toString());
//Make sure the address from the TA is a customer address for the Invoice that is getting created
AccountsReceivableCustomerAddress customerBillToAddress = null;
TravelerDetail traveler = travelAuthorizationDocument.getTraveler();
TemProfile profile = travelAuthorizationDocument.getTemProfile();
if (profile == null){
//Get the TEM Profile associated with this TA
profile = temProfileService.findTemProfileById(travelAuthorizationDocument.getTemProfileId());
}
AccountsReceivableCustomer customer = profile.getCustomer();
//Compare the address from the TA to the addresses for this customer to see if it already exists
for(AccountsReceivableCustomerAddress address: customer.getAccountsReceivableCustomerAddresses()) {
if (!compareAddress(address, traveler)) {
//Address found
customerBillToAddress = address;
break;
}
}
if (customerBillToAddress == null){
//This address from the TA was not found as a customer address so create a new one for this customer
customerBillToAddress = accountsReceivableModuleService.createCustomerAddress();
customerBillToAddress.setCustomerAddressTypeCodeAsAlternate();
//Customer's name as the customer address name
String tempName = profile.getFirstName() + " " + (StringUtils.isEmpty(profile.getMiddleName()) ? "" : profile.getMiddleName() + " ") + profile.getLastName();
if (tempName.length() > 40){
tempName = profile.getFirstName() + " " + profile.getLastName();
while (tempName.length() > 40){
tempName = tempName.substring(0, tempName.length()-1);
}
}
//Set all the fields for the new address
customerBillToAddress.setCustomerAddressName(tempName);
customer.setCustomerAddressChangeDate(dateTimeService.getCurrentSqlDate());
customerBillToAddress.setCustomerLine1StreetAddress(StringUtils.isNotEmpty(traveler.getStreetAddressLine1()) ? traveler.getStreetAddressLine1().toUpperCase() : "");
customerBillToAddress.setCustomerLine2StreetAddress(StringUtils.isNotEmpty(traveler.getStreetAddressLine2()) ? traveler.getStreetAddressLine2().toUpperCase() : "");
customerBillToAddress.setCustomerCityName(StringUtils.isNotEmpty(traveler.getCityName()) ? traveler.getCityName().toUpperCase() : "");
customerBillToAddress.setCustomerStateCode(StringUtils.isNotEmpty(traveler.getStateCode()) ? traveler.getStateCode().toUpperCase() : "");
customerBillToAddress.setCustomerZipCode(traveler.getZipCode());
customerBillToAddress.setCustomerCountryCode(StringUtils.isNotEmpty(traveler.getCountryCode()) ? traveler.getCountryCode().toUpperCase() : "");
customerBillToAddress.setCustomerEmailAddress(StringUtils.isNotEmpty(traveler.getEmailAddress()) ? traveler.getEmailAddress().toUpperCase() : "");
//Add the new address to the customer and save
List<AccountsReceivableCustomerAddress> customerAddresses = customer.getAccountsReceivableCustomerAddresses();
customerAddresses.add(customerBillToAddress);
customer.setAccountsReceivableCustomerAddresses(customerAddresses);
accountsReceivableModuleService.saveCustomer(customer);
}
customerBillToAddress.refresh();
customerInvoiceDocument.setCustomerBillToAddress(customerBillToAddress);
customerInvoiceDocument.setCustomerBillToAddressIdentifier(customerBillToAddress.getCustomerAddressIdentifier());
customerInvoiceDocument.setBillingAddressTypeCodeAsPrimary();
customerInvoiceDocument.setBillingAddressName(customer.getCustomerName());
customerInvoiceDocument.setBillingLine1StreetAddress(customerBillToAddress.getCustomerLine1StreetAddress());
customerInvoiceDocument.setBillingLine2StreetAddress(customerBillToAddress.getCustomerLine2StreetAddress());
customerInvoiceDocument.setBillingCityName(customerBillToAddress.getCustomerCityName());
customerInvoiceDocument.setBillingStateCode(customerBillToAddress.getCustomerStateCode());
customerInvoiceDocument.setBillingZipCode(customerBillToAddress.getCustomerZipCode());
customerInvoiceDocument.setBillingCountryCode(customerBillToAddress.getCustomerCountryCode());
customerInvoiceDocument.setBillingAddressInternationalProvinceName(customerBillToAddress.getCustomerAddressInternationalProvinceName());
customerInvoiceDocument.setBillingInternationalMailCode(customerBillToAddress.getCustomerInternationalMailCode());
customerInvoiceDocument.setBillingEmailAddress(customerBillToAddress.getCustomerEmailAddress());
try {
LOG.info("Saving customer invoice document " + customerInvoiceDocument.getDocumentNumber());
// getDocumentService().saveDocument(customerInvoiceDocument);
if (StringUtils.isEmpty(advance.getArInvoiceDocNumber())) {
AccountsReceivableCustomerInvoiceDetail detail = createInvoiceDetailFromAdvance(advance, customerInvoiceDocument.getDocumentNumber(), invoiceItemCode, processingOrgCode, processingChartCode);
addInvoiceDetailToDocument(detail, customerInvoiceDocument);
}
LOG.info("Saving customer invoice document after adding acctg lines " + customerInvoiceDocument.getDocumentNumber());
accountsReceivableModuleService.saveCustomerInvoiceDocument(customerInvoiceDocument);
// add relationship
String relationDescription = "TA - Customer Invoice";
accountingDocumentRelationshipService.save(new AccountingDocumentRelationship(travelAuthorizationDocument.getDocumentNumber(), customerInvoiceDocument.getDocumentNumber(), relationDescription));
//update AR Invoice Doc number to the travel advances
if (StringUtils.isEmpty(advance.getArInvoiceDocNumber())) {
advance.setArInvoiceDocNumber(customerInvoiceDocument.getDocumentNumber());
advance.setArCustomerId(customerNumber);
}
// route
WorkflowDocument originalWorkflowDocument = customerInvoiceDocument.getDocumentHeader().getWorkflowDocument();
try {
WorkflowDocument newWorkflowDocument = workflowDocumentService.loadWorkflowDocument(customerInvoiceDocument.getDocumentNumber(), GlobalVariables.getUserSession().getPerson());
newWorkflowDocument.setTitle(originalWorkflowDocument.getTitle());
customerInvoiceDocument.getDocumentHeader().setWorkflowDocument(newWorkflowDocument);
accountsReceivableModuleService.blanketApproveCustomerInvoiceDocument(customerInvoiceDocument);
}
finally {
customerInvoiceDocument.getDocumentHeader().setWorkflowDocument(originalWorkflowDocument);
}
LOG.info("Submitted customer invoice document "+ customerInvoiceDocument.getDocumentNumber() + " for " + customerNumber + " - " + dateTimeService.toDateString(billingDate) + "\n\n");
}
catch (WorkflowException e) {
throw new RuntimeException("Customer Invoice Document routing failed.");
}
return null;
}
});
} catch (Exception e) {
LOG.error(e.toString());
}
}
/**
*
* @param customerAddress
* @param traveler
* @return
*/
protected boolean compareAddress(AccountsReceivableCustomerAddress customerAddress, TravelerDetail traveler) {
if(!StringUtils.equalsIgnoreCase(customerAddress.getCustomerLine1StreetAddress(), traveler.getStreetAddressLine1())) {
return true;
}
if(!StringUtils.equalsIgnoreCase(customerAddress.getCustomerLine2StreetAddress(), traveler.getStreetAddressLine2())) {
return true;
}
if(!StringUtils.equalsIgnoreCase(customerAddress.getCustomerCityName(), traveler.getCityName())) {
return true;
}
if(!StringUtils.equalsIgnoreCase(customerAddress.getCustomerStateCode(), traveler.getStateCode())) {
return true;
}
if(!StringUtils.equalsIgnoreCase(customerAddress.getCustomerZipCode(), traveler.getZipCode())) {
return true;
}
if(!StringUtils.equalsIgnoreCase(customerAddress.getCustomerCountryCode(), traveler.getCountryCode())) {
return true;
}
return false;
}
/**
*
* @param document
* @param chartOfAccountsCode
* @param organizationCode
*/
protected void setupDefaultValuesForNewCustomerInvoiceDocument(AccountsReceivableCustomerInvoice document, String chartOfAccountsCode, String organizationCode) {
// setupBasicDefaultValuesForCustomerInvoiceDocument(document);
document.setBillByChartOfAccountCode(chartOfAccountsCode);
document.setBilledByOrganizationCode(organizationCode);
document.setOpenInvoiceIndicator(true);
// set up the default values for the AR DOC Header
AccountsReceivableDocumentHeader accountsReceivableDocumentHeader = accountsReceivableModuleService.createAccountsReceivableDocumentHeader();
// we try to get the processing org directly, which we'll get if the initiating user is an AR Processor
// Remove once we switch our parameters
AccountsReceivableSystemInformation processingOrg = accountsReceivableModuleService.getSystemInformationByProcessingChartOrgAndFiscalYear(chartOfAccountsCode, organizationCode, universityDateService.getCurrentFiscalYear());
if (processingOrg != null) {
accountsReceivableDocumentHeader.setProcessingChartOfAccountCode(processingOrg.getProcessingChartOfAccountCode());
accountsReceivableDocumentHeader.setProcessingOrganizationCode(processingOrg.getProcessingOrganizationCode());
// return accountsReceivableDocumentHeader;
}
// next we try to get the processing org through the initiating user's billing org, if that exists
AccountsReceivableOrganizationOptions orgOptions = accountsReceivableModuleService.getOrgOptionsIfExists(chartOfAccountsCode, organizationCode);
if (orgOptions != null) {
accountsReceivableDocumentHeader.setProcessingChartOfAccountCode(orgOptions.getProcessingChartOfAccountCode());
accountsReceivableDocumentHeader.setProcessingOrganizationCode(orgOptions.getProcessingOrganizationCode());
// return accountsReceivableDocumentHeader;
}
accountsReceivableDocumentHeader.setDocumentNumber(document.getDocumentNumber());
document.setAccountsReceivableDocumentHeader(accountsReceivableDocumentHeader);
// set up the primary key for AR_INV_RCURRNC_DTL_T
AccountsReceivableCustomerInvoiceRecurrenceDetails recurrenceDetails = accountsReceivableModuleService.createCustomerInvoiceRecurrenceDetails();
recurrenceDetails.setInvoiceNumber(document.getDocumentNumber());
// recurrenceDetails.setCustomerNumber(document.getCustomer().getCustomerNumber());
document.setCustomerInvoiceRecurrenceDetails(recurrenceDetails);
AccountsReceivableOrganizationOptions organizationOptions = accountsReceivableModuleService.getOrgOptionsIfExists(document.getBillByChartOfAccountCode(), document.getBilledByOrganizationCode());
if (ObjectUtils.isNotNull(organizationOptions)) {
document.setPrintInvoiceIndicator(organizationOptions.getPrintInvoiceIndicator());
document.setInvoiceTermsText(organizationOptions.getOrganizationPaymentTermsText());
}
}
protected AccountsReceivableCustomerInvoiceDetail createInvoiceDetailFromAdvance(TravelAdvance advance, String documentNumber, String invoiceItemCode, String processingOrgCode, String processingChartCode) {
AccountsReceivableCustomerInvoiceDetail customerInvoiceDetail = accountsReceivableModuleService.getCustomerInvoiceDetailFromCustomerInvoiceItemCode(invoiceItemCode, processingChartCode, processingOrgCode);
customerInvoiceDetail.setDocumentNumber(documentNumber);
customerInvoiceDetail.setInvoiceItemUnitPrice(advance.getTravelAdvanceRequested());
customerInvoiceDetail.setInvoiceItemQuantity(new BigDecimal(1));
customerInvoiceDetail.updateAmountBasedOnQuantityAndUnitPrice();
String accountsReceivableObjectCode = accountsReceivableModuleService.getAccountsReceivableObjectCodeBasedOnReceivableParameter(customerInvoiceDetail);
customerInvoiceDetail.setAccountsReceivableObjectCode(accountsReceivableObjectCode);
return customerInvoiceDetail;
}
/**
* This method takes a detail object, runs business rules and adds it to the source accounting lines of the invoice
*
* @param detail
* @param customerInvoiceDocument
*/
protected void addInvoiceDetailToDocument(AccountsReceivableCustomerInvoiceDetail detail, AccountsReceivableCustomerInvoice customerInvoiceDocument) {
accountsReceivableModuleService.recalculateCustomerInvoiceDetail(customerInvoiceDocument, detail);
// run rules
boolean rulePassed = true;
// check any business rules
rulePassed &= kualiRuleService.applyRules(new AddAccountingLineEvent(KFSConstants.NEW_SOURCE_ACCT_LINE_PROPERTY_NAME, (Document)customerInvoiceDocument, (AccountingLine)detail));
LOG.debug("running rules on new source line : " + rulePassed);
// add accountingLine
detail.refreshNonUpdateableReferences();
accountsReceivableModuleService.prepareCustomerInvoiceDetailForAdd(detail, customerInvoiceDocument);
customerInvoiceDocument.addSourceAccountingLine((SourceAccountingLine) detail);
if (customerInvoiceDocument instanceof AmountTotaling) {
((FinancialSystemDocumentHeader) customerInvoiceDocument.getDocumentHeader()).setFinancialDocumentTotalAmount(((AmountTotaling) customerInvoiceDocument).getTotalDollarAmount());
}
}
/**
* Locate all {@link TravelAuthorizationDocument} instances with the same <code>travelDocumentIdentifier</code>
*
* @param travelDocumentIdentifier to locate {@link TravelAuthorizationDocument} instances
* @return {@link Collection} of {@link TravelAuthorizationDocument} instances
*/
@Override
public Collection<TravelAuthorizationDocument> find(final String travelDocumentIdentifier) {
final Map<String, Object> criteria = new HashMap<String, Object>();
criteria.put(TRAVEL_DOCUMENT_IDENTIFIER, travelDocumentIdentifier);
return businessObjectService.findMatching(TravelAuthorizationDocument.class, criteria);
}
public void addListenersTo(final TravelAuthorizationDocument authorization) {
authorization.setPropertyChangeListeners(propertyChangeListeners);
}
/**
* Locate all {@link TravelAuthorizationAmendmentDocument} instances with the same <code>travelDocumentIdentifier</code>
*
* @param travelDocumentIdentifier to locate {@link TravelAuthorizationAmendmentDocument} instances
* @return {@link Collection} of {@link TravelAuthorizationAmendmentDocument} instances
*/
@Override
public Collection<TravelAuthorizationAmendmentDocument> findAmendment(Integer travelDocumentIdentifier) {
final Map<String, Object> criteria = new HashMap<String, Object>();
criteria.put(TRAVEL_DOCUMENT_IDENTIFIER, travelDocumentIdentifier);
return businessObjectService.findMatching(TravelAuthorizationAmendmentDocument.class, criteria);
}
/**
* This method creates a new travel auth document from a source document
*
* @param sourceDocument
* @param docType
* @return new Travel Authorization Document
* @throws WorkflowException
*/
@SuppressWarnings("rawtypes")
protected TravelAuthorizationDocument createTravelAuthorizationDocumentFromSourceDocument(TravelDocument sourceDocument, String docType) throws WorkflowException {
if (ObjectUtils.isNull(sourceDocument)) {
String errorMsg = "Attempting to create new Travel Authorization of type '" + docType + "' from source TA doc that is null";
LOG.error(errorMsg);
throw new RuntimeException(errorMsg);
}
TravelAuthorizationDocument newTravelAuthChangeDocument = (TravelAuthorizationDocument) documentService.getNewDocument(docType);
Set<Class> classesToExclude = new HashSet<Class>();
Class sourceObjectClass = FinancialSystemTransactionalDocumentBase.class;
classesToExclude.add(sourceObjectClass);
while (sourceObjectClass.getSuperclass() != null) {
sourceObjectClass = sourceObjectClass.getSuperclass();
classesToExclude.add(sourceObjectClass);
}
ObjectPopulationUtils.populateFromBaseWithSuper(sourceDocument, newTravelAuthChangeDocument, TemConstants.uncopyableFieldsForTravelAuthorization(), classesToExclude);
newTravelAuthChangeDocument.getDocumentHeader().setDocumentDescription(sourceDocument.getDocumentHeader().getDocumentDescription());
newTravelAuthChangeDocument.getDocumentHeader().setOrganizationDocumentNumber(sourceDocument.getDocumentHeader().getOrganizationDocumentNumber());
newTravelAuthChangeDocument.getDocumentHeader().setExplanation(sourceDocument.getDocumentHeader().getExplanation());
newTravelAuthChangeDocument.refreshNonUpdateableReferences();
return newTravelAuthChangeDocument;
}
/**
* @see org.kuali.kfs.module.tem.document.service.TravelAuthorizationService#getTravelAuthorizationBy(java.lang.String)
*/
@Override
public TravelAuthorizationDocument getTravelAuthorizationBy(String documentNumber) {
if (ObjectUtils.isNotNull(documentNumber)) {
try {
TravelAuthorizationDocument doc = (TravelAuthorizationDocument) documentService.getByDocumentHeaderId(documentNumber);
if (ObjectUtils.isNotNull(doc)) {
WorkflowDocument workflowDocument = doc.getDocumentHeader().getWorkflowDocument();
doc.refreshReferenceObject(KFSPropertyConstants.DOCUMENT_HEADER);
doc.getDocumentHeader().setWorkflowDocument(workflowDocument);
}
return doc;
}
catch (WorkflowException e) {
String errorMessage = "Error getting travel authorization document from document service";
LOG.error("getTravelAuthorizationByDocumentNumber() " + errorMessage, e);
throw new RuntimeException(errorMessage, e);
}
}
return null;
}
/**
* @see org.kuali.kfs.module.tem.document.service.TravelAuthorizationService#closeAuthorization(org.kuali.kfs.module.tem.document.TravelAuthorizationDocument, java.lang.String, java.lang.String, java.lang.String)
*/
@Override
public TravelAuthorizationCloseDocument closeAuthorization(TravelAuthorizationDocument authorization, String annotation, String initiatorPrincipalName, String reimbursementDocNum) {
TravelAuthorizationCloseDocument authorizationClose = null;
try {
String user = GlobalVariables.getUserSession().getPerson().getLastName() + ", " + GlobalVariables.getUserSession().getPerson().getFirstName();
String note = MessageUtils.getMessage(TA_MESSAGE_CLOSE_DOCUMENT_TEXT, user);
Principal kfsSystemUser = getIdentityService().getPrincipalByPrincipalName(KFSConstants.SYSTEM_USER);
authorization.updateAndSaveAppDocStatus(TravelAuthorizationStatusCodeKeys.RETIRED_VERSION);
documentDao.save(authorization);
// setting to initiator principal name
GlobalVariables.setUserSession(new UserSession(initiatorPrincipalName));
authorizationClose = authorization.toCopyTAC();
final Note newNoteTAC = documentService.createNoteFromDocument(authorizationClose, note);
newNoteTAC.setAuthorUniversalIdentifier(kfsSystemUser.getPrincipalId());
authorizationClose.addNote(newNoteTAC);
authorizationClose.setTravelReimbursementDocumentNumber(reimbursementDocNum);
// add relationship
String relationDescription = authorization.getDocumentTypeName() + " - " + authorizationClose.getDocumentTypeName();
accountingDocumentRelationshipService.save(new AccountingDocumentRelationship(authorization.getDocumentNumber(), authorizationClose.getDocumentNumber(), relationDescription));
// switching to KR user to route
GlobalVariables.setUserSession(new UserSession(KRADConstants.SYSTEM_USER));
authorizationClose.setApplicationDocumentStatus(TravelAuthorizationStatusCodeKeys.CLOSED);
documentService.routeDocument(authorizationClose, annotation, null);
}
catch (Exception e) {
LOG.error("Could not create TAC or route it with travel id " + authorization.getTravelDocumentIdentifier());
LOG.error(e.getMessage(), e);
}
return authorizationClose;
}
protected AccountsReceivableCustomer createNewCustomer(TemProfile profile) {
profile.setCustomer(accountsReceivableModuleService.createCustomer());
profile.getCustomer().setCustomerName(profile.getName());
String newCustNumber = accountsReceivableModuleService.getNextCustomerNumber(profile.getCustomer());
newCustNumber = newCustNumber.toUpperCase();
profile.setCustomerNumber(newCustNumber);
profile.getCustomer().setCustomerNumber(newCustNumber);
businessObjectService.save(profile);
//Set to customer type code to travel and make the customer active
String customerTypeCode = "";
List<AccountsReceivableCustomerType> customerTypes = accountsReceivableModuleService.findByCustomerTypeDescription(TemConstants.CUSTOMER_TRAVLER_TYPE_CODE);
for (AccountsReceivableCustomerType customerType : customerTypes) {
customerTypeCode = customerType.getCustomerTypeCode();
break;
}
profile.getCustomer().setCustomerTypeCode(customerTypeCode);
profile.getCustomer().setActive(true);
SpringContext.getBean(TravelerService.class).copyTemProfileToCustomer(profile, profile.getCustomer());
accountsReceivableModuleService.saveCustomer(profile.getCustomer());
return profile.getCustomer();
}
/**
* @see org.kuali.kfs.module.tem.document.service.TravelAuthorizationService#findEnrouteOrApprovedTravelReimbursement(org.kuali.kfs.module.tem.document.TravelAuthorizationDocument)
*/
@Override
public TravelReimbursementDocument findEnrouteOrProcessedTravelReimbursement(TravelAuthorizationDocument authorization) {
TravelReimbursementDocument reimbursement = null;
List<TravelReimbursementDocument> reimbursementDocumentList = travelDocumentService.findReimbursementDocuments(authorization.getTravelDocumentIdentifier());
//look for enroute TR document - return the first document if any is found
for (TravelReimbursementDocument document : reimbursementDocumentList){
WorkflowDocument workflowDocument = document.getDocumentHeader().getWorkflowDocument();
if (workflowDocument.isEnroute() || workflowDocument.isFinal() || workflowDocument.isProcessed()){
reimbursement = document;
}
}
return reimbursement;
}
/**
* This method checks to see if the travel expense type code is a prepaid expense
*
* @param travelExpenseTypeCodeCode
*/
public boolean checkNonReimbursable(String travelExpenseTypeCodeId) {
boolean nonReimbursable = false;
Map<String, Object> fieldValues = new HashMap<String, Object>();
fieldValues.put("travelExpenseTypeCodeId", travelExpenseTypeCodeId);
Collection<ExpenseTypeObjectCode> keyValueList = SpringContext.getBean(KeyValuesService.class).findMatching(ExpenseTypeObjectCode.class, fieldValues);
//should only return 1
for (ExpenseTypeObjectCode typeCode : keyValueList) {
nonReimbursable = typeCode.getExpenseType().isPrepaidExpense();
}
return nonReimbursable;
}
@Override
public List<String> findMatchingTrips(TravelAuthorizationDocument authorization) {
List<String> duplicateTrips = new ArrayList<String>();
Date tripBeginBufferDate = authorization.getTripBegin() == null ? null : getTripBeginDate(authorization.getTripBegin());
Date tripEndBufferDate = authorization.getTripEnd() == null ? null : getTripEndDate(authorization.getTripEnd());
if (tripBeginBufferDate == null || tripEndBufferDate == null) {
return duplicateTrips; // return the empty list. don't worry though - without a trip begin or end, they're certainly coming back
}
List<TravelAuthorizationDocument> authorizationDocuments = travelAuthorizationDao.findTravelAuthorizationByTraveler(authorization.getTemProfileId());
for (TravelAuthorizationDocument authorizationDocument : authorizationDocuments) {
List<TravelReimbursementDocument> travelReimbursementDocuments = travelDocumentService.findReimbursementDocuments(authorizationDocument.getTravelDocumentIdentifier());
if(!ObjectUtils.isNull(travelReimbursementDocuments) && !travelReimbursementDocuments.isEmpty()) {
boolean matchFound = matchReimbursements(travelReimbursementDocuments, tripBeginBufferDate, tripEndBufferDate);
if(matchFound) {
duplicateTrips.add(authorizationDocument.getDocumentNumber());
}
}
else {
// look for TA's
Date tripBeginDate = convertToSqlDate(authorizationDocument.getTripBegin());
Date tripEndDate = convertToSqlDate(authorizationDocument.getTripEnd());
if(!authorization.getDocumentNumber().equals(authorizationDocument.getDocumentNumber()) && doesDatesOverlap(tripBeginBufferDate, tripEndBufferDate, tripBeginDate,tripEndDate)){
duplicateTrips.add(authorizationDocument.getDocumentNumber());
}
}
}
return duplicateTrips;
}
private Date convertToSqlDate(Timestamp date) {
Date convertedDate = null;
try {
convertedDate = dateTimeService.convertToSqlDate(date);
}
catch (ParseException ex) {
LOG.error("Parse exception " + ex);
}
return convertedDate;
}
public boolean doesDatesOverlap(Date tripBeginBufferDate, Date tripEndBufferDate, Date tripBeginDate, Date tripEndDate) {
if ((tripBeginDate.compareTo(tripBeginBufferDate) >= 0 && tripBeginDate.compareTo(tripEndBufferDate) <= 0) ||
(tripEndDate.compareTo(tripBeginBufferDate) >= 0 && tripEndDate.compareTo(tripEndBufferDate) <=0 )) {
return true;
}
return false;
}
private boolean matchReimbursements(List<TravelReimbursementDocument> travelReimbursementDocuments, Date tripBeginBufferDate, Date tripEndBufferDate) {
Timestamp earliestTripBeginDate = null;
Timestamp greatestTripEndDate = null;
for (TravelReimbursementDocument document : travelReimbursementDocuments) {
Timestamp tripBegin = document.getTripBegin();
Timestamp tripEnd = document.getTripEnd();
if (ObjectUtils.isNull(earliestTripBeginDate) && ObjectUtils.isNull(greatestTripEndDate)) {
earliestTripBeginDate = tripBegin;
greatestTripEndDate = tripEnd;
}
else {
earliestTripBeginDate = tripBegin.before(earliestTripBeginDate) ? tripBegin :earliestTripBeginDate;
greatestTripEndDate = tripEnd.after(greatestTripEndDate)? tripEnd : greatestTripEndDate;
}
}
if(doesDatesOverlap(tripBeginBufferDate, tripEndBufferDate, convertToSqlDate(earliestTripBeginDate), convertToSqlDate(greatestTripEndDate))) {
return true;
}
return false;
}
private Integer getDuplicateTripDateRangeDays() {
String tripDateRangeDays = parameterService.getParameterValueAsString(TravelAuthorizationDocument.class, TemConstants.TravelParameters.DUPLICATE_TRIP_DATE_RANGE_DAYS);
Integer days = null;
if (!StringUtils.isNumeric(tripDateRangeDays)) {
days = TemConstants.DEFAULT_DUPLICATE_TRIP_DATE_RANGE_DAYS;
}
days = Integer.parseInt(tripDateRangeDays);
return days;
}
private Date getTripBeginDate(Timestamp tripBeginDate) {
Date tripBegin = null;
Integer days = getDuplicateTripDateRangeDays();
try {
tripBegin = dateTimeService.convertToSqlDate(dateTimeService.toDateString(DateUtils.addDays(tripBeginDate, (days * -1))));
} catch (ParseException pe) {
LOG.error("Exception while parsing trip begin date" + pe);
}
return tripBegin;
}
private Date getTripEndDate(Timestamp tripEndDate) {
Date tripEnd = null;
Integer days = getDuplicateTripDateRangeDays();
try {
tripEnd = dateTimeService.convertToSqlDate(dateTimeService.toDateString((DateUtils.addDays(tripEndDate, days ))));
} catch (ParseException pe) {
LOG.error("Exception while parsing trip end date" + pe);
}
return tripEnd;
}
public void setBusinessObjectService(BusinessObjectService businessObjectService) {
this.businessObjectService = businessObjectService;
}
public void setAccountsReceivableModuleService(AccountsReceivableModuleService accountsReceivableModuleService) {
this.accountsReceivableModuleService = accountsReceivableModuleService;
}
public void setPropertyChangeListeners(final List<PropertyChangeListener> propertyChangeListeners) {
this.propertyChangeListeners = propertyChangeListeners;
}
public void setParameterService(ParameterService parameterService) {
this.parameterService = parameterService;
}
public void setDocumentService(DocumentService documentService) {
this.documentService = documentService;
}
public void setWorkflowDocumentService(WorkflowDocumentService workflowDocumentService) {
this.workflowDocumentService = workflowDocumentService;
}
public void setDateTimeService(DateTimeService dateTimeService) {
this.dateTimeService = dateTimeService;
}
public void setTravelDocumentService(TravelDocumentService travelDocumentService) {
this.travelDocumentService = travelDocumentService;
}
public void setUniversityDateService(UniversityDateService universityDateService) {
this.universityDateService = universityDateService;
}
public void setRuleService(final KualiRuleService kualiRuleService) {
this.kualiRuleService = kualiRuleService;
}
public void setAccountingDocumentRelationshipService(AccountingDocumentRelationshipService accountingDocumentRelationshipService) {
this.accountingDocumentRelationshipService = accountingDocumentRelationshipService;
}
public void setTemProfileService(TemProfileService temProfileService) {
this.temProfileService = temProfileService;
}
public void setDocumentDao(DocumentDao documentDao) {
this.documentDao = documentDao;
}
/**
* @return the injected implementation of the data dictionary service
*/
public DataDictionaryService getDataDictionaryService() {
return dataDictionaryService;
}
/**
* Injects an implementation of the DataDictionaryService
* @param dataDictionaryService an implementation of the DataDictionaryService to inject
*/
public void setDataDictionaryService(DataDictionaryService dataDictionaryService) {
this.dataDictionaryService = dataDictionaryService;
}
public IdentityService getIdentityService() {
return identityService;
}
public void setIdentityService(IdentityService identityService) {
this.identityService = identityService;
}
public NoteService getNoteService() {
return noteService;
}
public void setNoteService(NoteService noteService) {
this.noteService = noteService;
}
public void setTravelAuthorizationDao(TravelAuthorizationDao travelAuthorizationDao) {
this.travelAuthorizationDao = travelAuthorizationDao;
}
}