/* * 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 java.sql.Date; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.List; import java.util.Map; import org.apache.commons.lang.StringUtils; import org.kuali.kfs.module.tem.TemConstants; import org.kuali.kfs.module.tem.TemParameterConstants; import org.kuali.kfs.module.tem.dataaccess.TravelDocumentDao; import org.kuali.kfs.module.tem.document.TravelAuthorizationDocument; import org.kuali.kfs.module.tem.document.service.TravelPaymentsHelperService; import org.kuali.kfs.pdp.businessobject.PaymentAccountDetail; import org.kuali.kfs.pdp.businessobject.PaymentDetail; import org.kuali.kfs.pdp.businessobject.PaymentGroup; import org.kuali.kfs.sys.KFSConstants; import org.kuali.kfs.sys.KFSParameterKeyConstants; import org.kuali.kfs.sys.batch.service.PaymentSourceToExtractService; import org.kuali.kfs.sys.businessobject.GeneralLedgerPendingEntry; import org.kuali.kfs.sys.document.service.PaymentSourceHelperService; import org.kuali.kfs.sys.document.validation.event.AccountingDocumentSaveWithNoLedgerEntryGenerationEvent; import org.kuali.rice.core.api.util.type.KualiDecimal; import org.kuali.rice.core.api.util.type.KualiInteger; import org.kuali.rice.coreservice.framework.parameter.ParameterService; import org.kuali.rice.kew.api.exception.WorkflowException; import org.kuali.rice.krad.service.DocumentService; import org.springframework.transaction.annotation.Transactional; /** * Implementation of the PaymentSourceToExtractServiceImpl and TravelAuthorizatoinDocumentPaymentServices which will feed travel authorizations * and travel authorization amendments with travel advances to PDP */ @Transactional public class TravelAuthorizationDocumentExtractionHelperServiceImpl implements PaymentSourceToExtractService<TravelAuthorizationDocument> { org.apache.log4j.Logger LOG = org.apache.log4j.Logger.getLogger(TravelAuthorizationDocumentExtractionHelperServiceImpl.class); protected TravelDocumentDao travelDocumentDao; protected TravelPaymentsHelperService travelPaymentsHelperService; protected DocumentService documentService; protected PaymentSourceHelperService paymentSourceHelperService; protected ParameterService parameterService; /** * * @see org.kuali.kfs.sys.batch.service.PaymentSourceToExtractService#retrievePaymentSourcesByCampus(boolean) */ @Override public Map<String, List<TravelAuthorizationDocument>> retrievePaymentSourcesByCampus(boolean immediatesOnly) { if (LOG.isDebugEnabled()) { LOG.debug("retrievePaymentSourcesByCampus() started"); } Map<String, List<TravelAuthorizationDocument>> documentsByCampus = new HashMap<String, List<TravelAuthorizationDocument>>(); final List<? extends TravelAuthorizationDocument> authorizations = retrieveAllApprovedAuthorizationDocuments(immediatesOnly); Map<String, String> initiatorCampuses = new HashMap<String, String>(); for (TravelAuthorizationDocument document : authorizations) { final String campusCode = getTravelPaymentsHelperService().findCampusForDocument(document, initiatorCampuses); if (!StringUtils.isBlank(campusCode)) { List<TravelAuthorizationDocument> documentsForCampus = documentsByCampus.get(campusCode); if (documentsForCampus == null) { documentsForCampus = new ArrayList<TravelAuthorizationDocument>(); documentsByCampus.put(campusCode, documentsForCampus); } documentsForCampus.add(document); } } return documentsByCampus; } /** * Retrieves all the TravelReimbursement, TravelRelocation, and TravelEntertainment documents paid by check at approved status in one convenient call * @param immediatesOnly true if only those documents marked for immediate payment should be retrieved, false if all qualifying documents should be retrieved * @return all of the documents to process in a list */ protected List<? extends TravelAuthorizationDocument> retrieveAllApprovedAuthorizationDocuments(boolean immediatesOnly) { List<TravelAuthorizationDocument> authorizationsToProcess = new ArrayList<TravelAuthorizationDocument>(); final Collection<? extends TravelAuthorizationDocument> authorizations = getTravelDocumentDao().getAuthorizationsAndAmendmentsByHeaderStatus(KFSConstants.DocumentStatusCodes.APPROVED, immediatesOnly); for (TravelAuthorizationDocument authorization : authorizations) { if (authorization.shouldProcessAdvanceForDocument()) { authorizationsToProcess.add(authorization); } } return authorizationsToProcess; } /** * * @see org.kuali.kfs.module.tem.document.service.TravelAuthorizationDocumentPaymentService#cancelReimbursableDocument(org.kuali.kfs.module.tem.document.TravelAuthorizationDocument, java.sql.Date) */ @Override public void cancelPayment(TravelAuthorizationDocument authorizationDoc, Date cancelDate) { if (authorizationDoc.getAdvanceTravelPayment().getCancelDate() == null) { try { authorizationDoc.getAdvanceTravelPayment().setCancelDate(cancelDate); getPaymentSourceHelperService().handleEntryCancellation(authorizationDoc, this); authorizationDoc.getFinancialSystemDocumentHeader().setFinancialDocumentStatusCode(KFSConstants.DocumentStatusCodes.CANCELLED); // save the document getDocumentService().saveDocument(authorizationDoc, AccountingDocumentSaveWithNoLedgerEntryGenerationEvent.class); } catch (WorkflowException we) { LOG.error("encountered workflow exception while attempting to save Disbursement Voucher: " + authorizationDoc.getDocumentNumber() + " " + we); throw new RuntimeException(we); } } } /** * Returns true if the GLPE has a doc type of TACA or TAWF * @see org.kuali.kfs.sys.batch.service.PaymentSourceToExtractService#shouldRollBackPendingEntry(org.kuali.kfs.sys.businessobject.GeneralLedgerPendingEntry) */ @Override public boolean shouldRollBackPendingEntry(GeneralLedgerPendingEntry entry) { return StringUtils.equals(entry.getFinancialDocumentTypeCode(), TemConstants.TravelDocTypes.TRAVEL_AUTHORIZATION_CHECK_ACH_DOCUMENT) || StringUtils.equals(entry.getFinancialDocumentTypeCode(), TemConstants.TravelDocTypes.TRAVEL_AUTHORIZATION_WIRE_OR_FOREIGN_DRAFT_DOCUMENT); } /** * Sets the paid date on the travel payment * @see org.kuali.kfs.sys.batch.service.PaymentSourceToExtractService#markAsPaid(org.kuali.kfs.sys.document.PaymentSource, java.sql.Date) */ @Override public void markAsPaid(TravelAuthorizationDocument paymentSource, Date processDate) { try { paymentSource.getAdvanceTravelPayment().setPaidDate(processDate); getDocumentService().saveDocument(paymentSource, AccountingDocumentSaveWithNoLedgerEntryGenerationEvent.class); } catch (WorkflowException we) { LOG.error("encountered workflow exception while attempting to save Disbursement Voucher: " + paymentSource.getDocumentNumber() + " " + we); throw new RuntimeException(we); } } /** * This involves rolling back the extracted and paid dates and setting the document to approved * @see org.kuali.kfs.sys.batch.service.PaymentSourceToExtractService#resetFromExtraction(org.kuali.kfs.sys.document.PaymentSource) */ @Override public void resetFromExtraction(TravelAuthorizationDocument paymentSource) { try { paymentSource.getAdvanceTravelPayment().setExtractDate(null); paymentSource.getAdvanceTravelPayment().setPaidDate(null); paymentSource.getFinancialSystemDocumentHeader().setFinancialDocumentStatusCode(KFSConstants.DocumentStatusCodes.APPROVED); getDocumentService().saveDocument(paymentSource, AccountingDocumentSaveWithNoLedgerEntryGenerationEvent.class); } catch (WorkflowException we) { LOG.error("encountered workflow exception while attempting to save Disbursement Voucher: " + paymentSource.getDocumentNumber() + " " + we); throw new RuntimeException(we); } } /** * @see org.kuali.kfs.sys.batch.service.PaymentSourceToExtractService#createPaymentGroup(org.kuali.rice.krad.document.Document, java.sql.Date) */ @Override public PaymentGroup createPaymentGroup(TravelAuthorizationDocument authorizationDoc, Date processDate) { if (authorizationDoc.shouldProcessAdvanceForDocument()) { PaymentGroup pg = getTravelPaymentsHelperService().buildGenericPaymentGroup(authorizationDoc.getTraveler(), authorizationDoc.getTemProfile(), authorizationDoc.getAdvanceTravelPayment(), authorizationDoc.getFinancialDocumentBankCode()); // now add the payment detail final PaymentDetail paymentDetail = buildPaymentDetail(authorizationDoc, processDate); pg.addPaymentDetails(paymentDetail); paymentDetail.setPaymentGroup(pg); return pg; } return null; } /** * Builds the PaymentDetail for the given authorization document * @param document the authorization document to create a payment for * @param processRunDate the date when the extraction is occurring * @return a PaymentDetail to add to the PaymentGroup */ protected PaymentDetail buildPaymentDetail(TravelAuthorizationDocument document, Date processRunDate) { if (LOG.isDebugEnabled()) { LOG.debug("buildPaymentDetail() started"); } PaymentDetail pd = getTravelPaymentsHelperService().buildGenericPaymentDetail(document.getDocumentHeader(), processRunDate, document.getAdvanceTravelPayment(), getTravelPaymentsHelperService().getInitiator(document), getAchCheckDocumentType(document)); pd.setPurchaseOrderNbr(document.getTravelDocumentIdentifier()); pd.setOrganizationDocNbr(document.getTravelDocumentIdentifier()); // Handle accounts final List<PaymentAccountDetail> paymentAccounts = this.getTravelPaymentsHelperService().buildGenericPaymentAccountDetails(document.getAdvanceAccountingLines()); for (PaymentAccountDetail pad : paymentAccounts) { pd.addAccountDetail(pad); } return pd; } /** * Uses the value in the KFS-TEM / Document / PRE_DISBURSEMENT_EXTRACT_ORGANIZATION parameter * @see org.kuali.kfs.sys.document.PaymentSource#getPreDisbursementCustomerProfileUnit() */ @Override public String getPreDisbursementCustomerProfileUnit() { final String unit = getParameterService().getParameterValueAsString(TemParameterConstants.TEM_DOCUMENT.class, KFSParameterKeyConstants.PdpExtractBatchParameters.PDP_ORG_CODE); return unit; } /** * Uses the value in the KFS-TEM / Document / PRE_DISBURSEMENT_EXTRACT_SUB_UNIT * @see org.kuali.kfs.sys.document.PaymentSource#getPreDisbursementCustomerProfileSubUnit() */ @Override public String getPreDisbursementCustomerProfileSubUnit() { final String subUnit = getParameterService().getParameterValueAsString(TemParameterConstants.TEM_DOCUMENT.class, KFSParameterKeyConstants.PdpExtractBatchParameters.PDP_SBUNT_CODE); return subUnit; } /** * Marks the advance travel payment as extracted * @see org.kuali.kfs.sys.batch.service.PaymentSourceToExtractService#markAsExtracted(org.kuali.rice.krad.document.Document, java.sql.Date) */ @Override public void markAsExtracted(TravelAuthorizationDocument document, Date sqlProcessRunDate, KualiInteger paymentGroupId) { try { document.getFinancialSystemDocumentHeader().setFinancialDocumentStatusCode(KFSConstants.DocumentStatusCodes.Payments.EXTRACTED); document.getAdvanceTravelPayment().setExtractDate(sqlProcessRunDate); getDocumentService().saveDocument(document, AccountingDocumentSaveWithNoLedgerEntryGenerationEvent.class); } catch (WorkflowException we) { LOG.error("Could not save TravelAuthorizationDocument document #" + document.getDocumentNumber() + ": " + we); throw new RuntimeException(we); } } /** * If advance on the document should be processed, returns the check total from the advance travel payment; otherwise returns 0 * @see org.kuali.kfs.sys.batch.service.PaymentSourceToExtractService#getPaymentAmount(org.kuali.rice.krad.document.Document) */ @Override public KualiDecimal getPaymentAmount(TravelAuthorizationDocument document) { if (document.shouldProcessAdvanceForDocument()) { return document.getAdvanceTravelPayment().getCheckTotalAmount(); } return KualiDecimal.ZERO; } /** * Returns "TACA" * @see org.kuali.kfs.sys.document.PaymentSource#getAchCheckDocumentType() */ @Override public String getAchCheckDocumentType(TravelAuthorizationDocument document) { return TemConstants.TravelDocTypes.TRAVEL_AUTHORIZATION_CHECK_ACH_DOCUMENT; } /** * Returns true if doc type is TACA, false otherwise * @see org.kuali.kfs.sys.batch.service.PaymentSourceToExtractService#handlesAchCheckDocumentType(java.lang.String) */ @Override public boolean handlesAchCheckDocumentType(String achCheckDocumentType) { return StringUtils.equals(achCheckDocumentType, TemConstants.TravelDocTypes.TRAVEL_AUTHORIZATION_CHECK_ACH_DOCUMENT); } /** * Returns the value of the KFS-TEM / Document / IMMEDIATE_EXTRACT_NOTIFICATION_FROM_EMAIL_ADDRESS parameter * @see org.kuali.kfs.sys.document.PaymentSource#getImmediateExtractEMailFromAddress() */ @Override public String getImmediateExtractEMailFromAddress() { return getParameterService().getParameterValueAsString(TemParameterConstants.TEM_DOCUMENT.class, KFSParameterKeyConstants.PdpExtractBatchParameters.IMMEDIATE_EXTRACT_FROM_ADDRESS_PARM_NM); } /** * Returns the value of the KFS-TEM / Document / IMMEDIATE_EXTRACT_NOTIFICATION_TO_EMAIL_ADDRESSES parameter * @see org.kuali.kfs.sys.document.PaymentSource#getImmediateExtractEmailToAddresses() */ @Override public List<String> getImmediateExtractEmailToAddresses() { List<String> toAddresses = new ArrayList<String>(); toAddresses.addAll(getParameterService().getParameterValuesAsString(TemParameterConstants.TEM_DOCUMENT.class, KFSParameterKeyConstants.PdpExtractBatchParameters.IMMEDIATE_EXTRACT_TO_ADDRESSES_PARM_NM)); return toAddresses; } /** * Determines if the payment would be 0 - if it's greater than that, it should be extracted * @see org.kuali.kfs.sys.batch.service.PaymentSourceToExtractService#shouldExtractPayment(org.kuali.kfs.sys.document.PaymentSource) */ @Override public boolean shouldExtractPayment(TravelAuthorizationDocument paymentSource) { return KualiDecimal.ZERO.isLessThan(getPaymentAmount(paymentSource)); } /** * @return an implementation of the DAO for TravelDocuments */ public TravelDocumentDao getTravelDocumentDao() { return travelDocumentDao; } /** * Sets the implementation of the DAO for TravelDocuments for this service to use * @param parameterService an implementation of the data access object for travel documents */ public void setTravelDocumentDao(TravelDocumentDao travelDocumentDao) { this.travelDocumentDao = travelDocumentDao; } /** * @return an implementation of the TravelPaymentsHelperService */ public TravelPaymentsHelperService getTravelPaymentsHelperService() { return travelPaymentsHelperService; } /** * Sets the implementation of the TravelPaymentsHelperService for this service to use * @param travelPaymentsHelperService an implementation of the TravelPaymentsHelperService */ public void setTravelPaymentsHelperService(TravelPaymentsHelperService travelPaymentsHelperService) { this.travelPaymentsHelperService = travelPaymentsHelperService; } /** * @return an implementation of the DocumentService */ public DocumentService getDocumentService() { return documentService; } /** * Sets the implementation of the DocumentService for this service to use * @param parameterService an implementation of DocumentService */ public void setDocumentService(DocumentService documentService) { this.documentService = documentService; } /** * @return an implementation of the PaymentSourceHelperService */ public PaymentSourceHelperService getPaymentSourceHelperService() { return paymentSourceHelperService; } /** * Sets the implementation of the PaymentSourceHelperService for this service to use * @param parameterService an implementation of PaymentSourceHelperService */ public void setPaymentSourceHelperService(PaymentSourceHelperService paymentSourceHelperService) { this.paymentSourceHelperService = paymentSourceHelperService; } /** * @return the injected implementation of the ParameterService */ public ParameterService getParameterService() { return parameterService; } /** * Injects an implementation of the ParameterService * @param parameterService the implementation of the ParameterService */ public void setParameterService(ParameterService parameterService) { this.parameterService = parameterService; } }