/* * 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; } }