001/*
002 * The Kuali Financial System, a comprehensive financial management system for higher education.
003 * 
004 * Copyright 2005-2014 The Kuali Foundation
005 * 
006 * This program is free software: you can redistribute it and/or modify
007 * it under the terms of the GNU Affero General Public License as
008 * published by the Free Software Foundation, either version 3 of the
009 * License, or (at your option) any later version.
010 * 
011 * This program is distributed in the hope that it will be useful,
012 * but WITHOUT ANY WARRANTY; without even the implied warranty of
013 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
014 * GNU Affero General Public License for more details.
015 * 
016 * You should have received a copy of the GNU Affero General Public License
017 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
018 */
019package org.kuali.kfs.pdp.batch.service.impl;
020
021import java.sql.Date;
022import java.util.ArrayList;
023import java.util.Collections;
024import java.util.Iterator;
025import java.util.List;
026import java.util.Map;
027import java.util.Set;
028
029import org.kuali.kfs.integration.purap.PurchasingAccountsPayableModuleService;
030import org.kuali.kfs.pdp.PdpConstants;
031import org.kuali.kfs.pdp.batch.service.ProcessPdpCancelPaidService;
032import org.kuali.kfs.pdp.businessobject.ExtractionUnit;
033import org.kuali.kfs.pdp.businessobject.PaymentDetail;
034import org.kuali.kfs.pdp.service.PaymentDetailService;
035import org.kuali.kfs.pdp.service.PaymentGroupService;
036import org.kuali.kfs.sys.KFSParameterKeyConstants;
037import org.kuali.kfs.sys.batch.service.PaymentSourceToExtractService;
038import org.kuali.kfs.sys.context.SpringContext;
039import org.kuali.kfs.sys.document.PaymentSource;
040import org.kuali.kfs.sys.service.impl.KfsParameterConstants;
041import org.kuali.rice.core.api.datetime.DateTimeService;
042import org.kuali.rice.coreservice.framework.parameter.ParameterService;
043import org.kuali.rice.kew.api.exception.WorkflowException;
044import org.kuali.rice.krad.service.DocumentService;
045import org.springframework.transaction.annotation.Transactional;
046
047/**
048 * Implementation of ProcessPdpCancelPaidService
049 */
050@Transactional
051public class ProcessPdpCancelPaidServiceImpl implements ProcessPdpCancelPaidService {
052    private static org.apache.log4j.Logger LOG = org.apache.log4j.Logger.getLogger(ProcessPdpCancelPaidServiceImpl.class);
053
054    protected PaymentGroupService paymentGroupService;
055    protected PaymentDetailService paymentDetailService;
056    protected ParameterService parameterService;
057    protected DateTimeService dateTimeService;
058    protected PurchasingAccountsPayableModuleService purchasingAccountsPayableModuleService;
059    protected DocumentService documentService;
060
061    protected volatile Set<String> paymentSourceCheckACHDocumentTypes;
062    protected volatile List<PaymentSourceToExtractService<PaymentSource>> paymentSourceToExtractServices;
063
064    /**
065     * @see org.kuali.kfs.module.purap.service.ProcessPdpCancelPaidService#processPdpCancels()
066     */
067    @Override
068    public void processPdpCancels() {
069        LOG.debug("processPdpCancels() started");
070
071        Date processDate = dateTimeService.getCurrentSqlDate();
072
073        final List<ExtractionUnit> extractionUnits = getExtractionUnits();
074        Iterator<PaymentDetail> details = paymentDetailService.getUnprocessedCancelledDetails(extractionUnits);
075        while (details.hasNext()) {
076            PaymentDetail paymentDetail = details.next();
077
078            String documentTypeCode = paymentDetail.getFinancialDocumentTypeCode();
079            String documentNumber = paymentDetail.getCustPaymentDocNbr();
080
081            boolean primaryCancel = paymentDetail.getPrimaryCancelledPayment();
082            boolean disbursedPayment = PdpConstants.PaymentStatusCodes.CANCEL_DISBURSEMENT.equals(paymentDetail.getPaymentGroup().getPaymentStatusCode());
083
084            if(purchasingAccountsPayableModuleService.isPurchasingBatchDocument(documentTypeCode)) {
085                purchasingAccountsPayableModuleService.handlePurchasingBatchCancels(documentNumber, documentTypeCode, primaryCancel, disbursedPayment);
086            }
087            else {
088                PaymentSourceToExtractService<PaymentSource> extractService = getPaymentSourceToExtractService(paymentDetail);
089                if (extractService != null) {
090                    try {
091                        PaymentSource dv = (PaymentSource)getDocumentService().getByDocumentHeaderId(documentNumber);
092                        if (dv != null) {
093                            if (disbursedPayment || primaryCancel) {
094                                extractService.cancelPayment(dv, processDate);
095                            } else {
096                                extractService.resetFromExtraction(dv);
097                            }
098                        }
099                    } catch (WorkflowException we) {
100                        throw new RuntimeException("Could not retrieve document #"+documentNumber, we);
101                    }
102                } else {
103                    LOG.warn("processPdpCancels() Unknown document type (" + documentTypeCode + ") for document ID: " + documentNumber);
104                    continue;
105                }
106            }
107
108            paymentGroupService.processCancelledGroup(paymentDetail.getPaymentGroup(), processDate);
109        }
110    }
111
112    /**
113     * @see org.kuali.kfs.module.purap.service.ProcessPdpCancelPaidService#processPdpPaids()
114     */
115    @Override
116    public void processPdpPaids() {
117        LOG.debug("processPdpPaids() started");
118
119        Date processDate = dateTimeService.getCurrentSqlDate();
120
121        final List<ExtractionUnit> extractionUnits = getExtractionUnits();
122        Iterator<PaymentDetail> details = paymentDetailService.getUnprocessedPaidDetails(extractionUnits);
123        while (details.hasNext()) {
124            PaymentDetail paymentDetail = details.next();
125
126            String documentTypeCode = paymentDetail.getFinancialDocumentTypeCode();
127            String documentNumber = paymentDetail.getCustPaymentDocNbr();
128
129            if(purchasingAccountsPayableModuleService.isPurchasingBatchDocument(documentTypeCode)) {
130                purchasingAccountsPayableModuleService.handlePurchasingBatchPaids(documentNumber, documentTypeCode, processDate);
131            }
132            else {
133                PaymentSourceToExtractService<PaymentSource> extractService = getPaymentSourceToExtractService(paymentDetail);
134                if (extractService != null) {
135                    try {
136                        PaymentSource dv = (PaymentSource)getDocumentService().getByDocumentHeaderId(documentNumber);
137                        extractService.markAsPaid(dv, processDate);
138                    } catch (WorkflowException we) {
139                        throw new RuntimeException("Could not retrieve document #"+documentNumber, we);
140                    }
141                } else {
142                    LOG.warn("processPdpPaids() Unknown document type (" + documentTypeCode + ") for document ID: " + documentNumber);
143                    continue;
144                }
145            }
146
147            paymentGroupService.processPaidGroup(paymentDetail.getPaymentGroup(), processDate);
148        }
149    }
150
151    /**
152     * @see org.kuali.kfs.module.purap.service.ProcessPdpCancelPaidService#processPdpCancelsAndPaids()
153     */
154    @Override
155    public void processPdpCancelsAndPaids() {
156        LOG.debug("processPdpCancelsAndPaids() started");
157
158        processPdpCancels();
159        processPdpPaids();
160    }
161
162    /**
163     * @return a List of all available PaymentSourceToExtractService implementations
164     */
165    protected List<PaymentSourceToExtractService<PaymentSource>> getPaymentSourceToExtractServices() {
166        if (paymentSourceToExtractServices == null) {
167            paymentSourceToExtractServices = new ArrayList<PaymentSourceToExtractService<PaymentSource>>();
168            Map<String, PaymentSourceToExtractService> extractionServices = SpringContext.getBeansOfType(PaymentSourceToExtractService.class);
169            for (PaymentSourceToExtractService<PaymentSource> extractionService : extractionServices.values()) {
170                paymentSourceToExtractServices.add(extractionService);
171            }
172        }
173        return paymentSourceToExtractServices;
174    }
175
176    /**
177     * Looks up the PaymentSourceToExtractService which can act upon the given PaymentDetail, based on the PaymentDetail's document type
178     * @param paymentDetail the payment detail to find an extraction service to act upon
179     * @return the matching PaymentSourceToExtractService, or null if a matching service could not be found (which would be weird, because _something_ created this PaymentDetail, but...whatever)
180     */
181    protected PaymentSourceToExtractService<PaymentSource> getPaymentSourceToExtractService(PaymentDetail paymentDetail) {
182        for (PaymentSourceToExtractService<PaymentSource> extractionService : getPaymentSourceToExtractServices()) {
183            if (extractionService.handlesAchCheckDocumentType(paymentDetail.getFinancialDocumentTypeCode())) {
184                return extractionService;
185            }
186        }
187        return null;
188    }
189
190    /**
191     * Loops through the PaymentSourceToExtractService List and builds ExtractionUnits for each
192     * @return a List of ExtractionUnits for each customer profile organization and sub-organization handled by PaymentSourceToExtractServices
193     */
194    protected List<ExtractionUnit> getExtractionUnitsForPaymentSourceToExtractServices() {
195        List<ExtractionUnit> extractionUnits = new ArrayList<ExtractionUnit>();
196        for (PaymentSourceToExtractService<PaymentSource> extractionService : getPaymentSourceToExtractServices()) {
197            final ExtractionUnit extractionUnit = new ExtractionUnit(extractionService.getPreDisbursementCustomerProfileUnit(), extractionService.getPreDisbursementCustomerProfileSubUnit());
198            if (!extractionUnits.contains(extractionUnit)) {
199                extractionUnits.add(extractionUnit);
200            }
201        }
202        return extractionUnits;
203    }
204
205    /**
206     * @return a List of all known ExtractionUnits
207     */
208    protected List<ExtractionUnit> getExtractionUnits() {
209        List<ExtractionUnit> extractionUnits = getExtractionUnitsForPaymentSourceToExtractServices();
210        final String purapOrg = parameterService.getParameterValueAsString(KfsParameterConstants.PURCHASING_BATCH.class, KFSParameterKeyConstants.PurapPdpParameterConstants.PURAP_PDP_ORG_CODE);
211        final String purapSubUnit = parameterService.getParameterValueAsString(KfsParameterConstants.PURCHASING_BATCH.class, KFSParameterKeyConstants.PurapPdpParameterConstants.PURAP_PDP_SUB_UNIT_CODE);
212        final ExtractionUnit purapExtractionUnit = new ExtractionUnit(purapOrg, purapSubUnit);
213        extractionUnits.add(purapExtractionUnit);
214        return Collections.unmodifiableList(extractionUnits);
215    }
216
217    public void setPaymentDetailService(PaymentDetailService paymentDetailService) {
218        this.paymentDetailService = paymentDetailService;
219    }
220
221    public void setPaymentGroupService(PaymentGroupService paymentGroupService) {
222        this.paymentGroupService = paymentGroupService;
223    }
224
225    public void setParameterService(ParameterService parameterService) {
226        this.parameterService = parameterService;
227    }
228
229    public void setPurchasingAccountsPayableModuleService(PurchasingAccountsPayableModuleService purchasingAccountsPayableModuleService) {
230        this.purchasingAccountsPayableModuleService = purchasingAccountsPayableModuleService;
231    }
232
233    public void setDateTimeService(DateTimeService dts) {
234        this.dateTimeService = dts;
235    }
236
237    /**
238     * @return the implementation of the DocumentService to use
239     */
240    public DocumentService getDocumentService() {
241        return documentService;
242    }
243
244    /**
245     * Sets the implementation of the DocumentService to use
246     * @param documentService the implementation of the DocumentService to use
247     */
248    public void setDocumentService(DocumentService documentService) {
249        this.documentService = documentService;
250    }
251
252}