001    /*
002     * Copyright 2011 The Kuali Foundation.
003     * 
004     * Licensed under the Educational Community License, Version 2.0 (the "License");
005     * you may not use this file except in compliance with the License.
006     * You may obtain a copy of the License at
007     * 
008     * http://www.opensource.org/licenses/ecl2.php
009     * 
010     * Unless required by applicable law or agreed to in writing, software
011     * distributed under the License is distributed on an "AS IS" BASIS,
012     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013     * See the License for the specific language governing permissions and
014     * limitations under the License.
015     */
016    package org.kuali.kfs.module.ar.document.service.impl;
017    
018    import java.util.Collection;
019    import java.util.List;
020    
021    import org.apache.commons.lang.StringUtils;
022    import org.apache.log4j.Logger;
023    import org.kuali.kfs.module.ar.businessobject.AccountsReceivableDocumentHeader;
024    import org.kuali.kfs.module.ar.businessobject.AppliedPayment;
025    import org.kuali.kfs.module.ar.businessobject.CashControlDetail;
026    import org.kuali.kfs.module.ar.businessobject.CustomerInvoiceDetail;
027    import org.kuali.kfs.module.ar.businessobject.InvoicePaidApplied;
028    import org.kuali.kfs.module.ar.businessobject.NonAppliedHolding;
029    import org.kuali.kfs.module.ar.document.CashControlDocument;
030    import org.kuali.kfs.module.ar.document.CustomerInvoiceDocument;
031    import org.kuali.kfs.module.ar.document.PaymentApplicationDocument;
032    import org.kuali.kfs.module.ar.document.dataaccess.CashControlDetailDao;
033    import org.kuali.kfs.module.ar.document.service.AccountsReceivableDocumentHeaderService;
034    import org.kuali.kfs.module.ar.document.service.InvoicePaidAppliedService;
035    import org.kuali.kfs.module.ar.document.service.NonAppliedHoldingService;
036    import org.kuali.kfs.module.ar.document.service.PaymentApplicationDocumentService;
037    import org.kuali.kfs.sys.context.SpringContext;
038    import org.kuali.kfs.sys.service.UniversityDateService;
039    import org.kuali.rice.kew.exception.WorkflowException;
040    import org.kuali.rice.kns.service.BusinessObjectService;
041    import org.kuali.rice.kns.service.DocumentService;
042    import org.kuali.rice.kns.util.KualiDecimal;
043    import org.springframework.transaction.annotation.Transactional;
044    
045    @Transactional
046    public class PaymentApplicationDocumentServiceImpl implements PaymentApplicationDocumentService {
047        private static Logger LOG = org.apache.log4j.Logger.getLogger(PaymentApplicationDocumentServiceImpl.class);;
048    
049        private DocumentService documentService;
050        private BusinessObjectService businessObjectService;
051        private NonAppliedHoldingService nonAppliedHoldingService;
052        private InvoicePaidAppliedService<AppliedPayment> invoicePaidAppliedService;
053        private UniversityDateService universityDateService;
054        private CashControlDetailDao cashControlDetailDao;
055        
056        /**
057         * 
058         * @param customerInvoiceDocument
059         * @return
060         * @throws WorkflowException
061         */
062        public PaymentApplicationDocument createPaymentApplicationToMatchInvoice(CustomerInvoiceDocument customerInvoiceDocument) throws WorkflowException {
063            
064            PaymentApplicationDocument applicationDocument = 
065                (PaymentApplicationDocument) documentService.getNewDocument(PaymentApplicationDocument.class);
066    
067            //  get the processing chart & org off the invoice, we'll create the payapp with the same processing org
068            String processingChartCode = customerInvoiceDocument.getAccountsReceivableDocumentHeader().getProcessingChartOfAccountCode();
069            String processingOrgCode = customerInvoiceDocument.getAccountsReceivableDocumentHeader().getProcessingOrganizationCode();
070            
071            AccountsReceivableDocumentHeaderService arDocHeaderService = SpringContext.getBean(AccountsReceivableDocumentHeaderService.class);
072            AccountsReceivableDocumentHeader arDocHeader = arDocHeaderService.getNewAccountsReceivableDocumentHeader(processingChartCode, processingOrgCode);
073            arDocHeader.setDocumentNumber(applicationDocument.getDocumentNumber());
074            applicationDocument.setAccountsReceivableDocumentHeader(arDocHeader);
075            
076            // This code is basically copied from PaymentApplicationDocumentAction.quickApply
077            int paidAppliedItemNumber = 1;
078            for(CustomerInvoiceDetail customerInvoiceDetail : customerInvoiceDocument.getCustomerInvoiceDetailsWithoutDiscounts()) {
079                InvoicePaidApplied invoicePaidApplied = 
080                    createInvoicePaidAppliedForInvoiceDetail(
081                        customerInvoiceDetail, applicationDocument, paidAppliedItemNumber);
082                // if there was not another invoice paid applied already created for the current detail then invoicePaidApplied will not be null
083                if (invoicePaidApplied != null) {
084                    // add it to the payment application document list of applied payments
085                    applicationDocument.getInvoicePaidApplieds().add(invoicePaidApplied);
086                    paidAppliedItemNumber++;
087                }
088            }
089            
090            return applicationDocument;
091        }
092    
093        /**
094         *
095         * @param customerInvoiceDocument
096         * @return
097         * @throws WorkflowException
098         */
099        public PaymentApplicationDocument createAndSavePaymentApplicationToMatchInvoice(CustomerInvoiceDocument customerInvoiceDocument) throws WorkflowException {
100            PaymentApplicationDocument applicationDocument = createPaymentApplicationToMatchInvoice(customerInvoiceDocument);
101            documentService.saveDocument(applicationDocument);
102            return applicationDocument;
103        }
104    
105        /**
106         *
107         * @param customerInvoiceDocument
108         * @param approvalAnnotation
109         * @param workflowNotificationRecipients
110         * @return
111         * @throws WorkflowException
112         */
113        public PaymentApplicationDocument createSaveAndApprovePaymentApplicationToMatchInvoice(CustomerInvoiceDocument customerInvoiceDocument, String approvalAnnotation, List workflowNotificationRecipients) throws WorkflowException {
114            DocumentService documentService = SpringContext.getBean(DocumentService.class);
115            PaymentApplicationDocument applicationDocument = createAndSavePaymentApplicationToMatchInvoice(customerInvoiceDocument);
116            documentService.approveDocument(applicationDocument, approvalAnnotation, workflowNotificationRecipients);
117            return applicationDocument;
118        }
119    
120        /**
121         *
122         * @param document
123         * @return
124         */
125        public KualiDecimal getTotalAppliedAmountForPaymentApplicationDocument(PaymentApplicationDocument document) {
126            KualiDecimal total = KualiDecimal.ZERO;
127            Collection<InvoicePaidApplied> invoicePaidApplieds = document.getInvoicePaidApplieds();
128    
129            for (InvoicePaidApplied invoicePaidApplied : invoicePaidApplieds) {
130                total = total.add(invoicePaidApplied.getInvoiceItemAppliedAmount());
131            }
132    
133            // Include non-ar funds as well
134            total = total.add(document.getSumOfNonInvoiceds());
135    
136            return total;
137        }
138    
139        /**
140         *
141         * @param document
142         * @return
143         */
144        public KualiDecimal getTotalUnappliedFundsForPaymentApplicationDocument(PaymentApplicationDocument document) {
145            KualiDecimal total = KualiDecimal.ZERO;
146    
147            String customerNumber = document.getAccountsReceivableDocumentHeader().getCustomerNumber();
148            Collection<NonAppliedHolding> nonAppliedHoldings = nonAppliedHoldingService.getNonAppliedHoldingsForCustomer(customerNumber);
149    
150            for (NonAppliedHolding nonAppliedHolding : nonAppliedHoldings) {
151                total = total.add(nonAppliedHolding.getFinancialDocumentLineAmount());
152            }
153    
154            // Add the amount for this document, if it's set
155            NonAppliedHolding nonAppliedHolding = document.getNonAppliedHolding();
156            if(null != nonAppliedHolding) {
157                KualiDecimal amount = nonAppliedHolding.getFinancialDocumentLineAmount();
158                if(null != amount) {
159                    total = total.add(amount);
160                }
161            }
162    
163            return total;
164        }
165    
166        /**
167         *
168         * @param document
169         * @return
170         */
171        public KualiDecimal getTotalUnappliedFundsToBeAppliedForPaymentApplicationDocument(PaymentApplicationDocument document) {
172            KualiDecimal totalUnapplied = getTotalUnappliedFundsForPaymentApplicationDocument(document);
173            KualiDecimal totalApplied = getTotalAppliedAmountForPaymentApplicationDocument(document);
174            return totalUnapplied.subtract(totalApplied);
175        }
176    
177        /**
178         * 
179         * @see org.kuali.kfs.module.ar.document.service.PaymentApplicationDocumentService#getCashControlDocumentForPaymentApplicationDocument(org.kuali.kfs.module.ar.document.PaymentApplicationDocument)
180         */
181        public CashControlDocument getCashControlDocumentForPaymentApplicationDocument(PaymentApplicationDocument paymentApplicationDocument) {
182            if (paymentApplicationDocument == null) {
183                throw new IllegalArgumentException("A null paymentApplicationDocument parameter was passed in.");
184            }
185            String payAppDocNumber = paymentApplicationDocument.getDocumentHeader().getDocumentNumber();
186            return getCashControlDocumentForPayAppDocNumber(payAppDocNumber);
187        }
188        
189        /**
190         * 
191         * @see org.kuali.kfs.module.ar.document.service.PaymentApplicationDocumentService#getCashControlDocumentForPayAppDocNumber(java.lang.String)
192         */
193        public CashControlDocument getCashControlDocumentForPayAppDocNumber(String paymentApplicationDocumentNumber) {
194            if (StringUtils.isBlank(paymentApplicationDocumentNumber)) {
195                throw new IllegalArgumentException("A null or blank paymentApplicationDocumentNumber paraemter was passed in.");
196            }
197            CashControlDetail cashControlDetail = getCashControlDetailForPayAppDocNumber(paymentApplicationDocumentNumber);
198            if (cashControlDetail == null) {
199                return null;
200            }
201            CashControlDocument cashControlDocument = null;
202            try {
203                cashControlDocument = (CashControlDocument) documentService.getByDocumentHeaderId(cashControlDetail.getDocumentNumber());
204            }
205            catch (WorkflowException e) {
206                //TODO we may need to swallow this ...
207                throw new RuntimeException("A workflow exception was thrown when trying to retrieve document [" + cashControlDetail.getDocumentNumber() + "].", e);
208            }
209            return cashControlDocument;
210        }
211    
212        /**
213         * @see org.kuali.kfs.module.ar.document.service.PaymentApplicationDocumentService#getCashControlDetailForPaymentApplicationDocument(org.kuali.kfs.module.ar.document.PaymentApplicationDocument)
214         */
215        public CashControlDetail getCashControlDetailForPaymentApplicationDocument(PaymentApplicationDocument document) {
216            if (document == null) {
217                throw new IllegalArgumentException("A null paymentApplicationDocument parameter was passed in.");
218            }
219            String payAppDocumentNumber = document.getDocumentNumber();
220            CashControlDetail cashControlDetail = getCashControlDetailForPayAppDocNumber(payAppDocumentNumber);
221            return cashControlDetail;
222        }
223        
224        /**
225         * 
226         * @see org.kuali.kfs.module.ar.document.service.PaymentApplicationDocumentService#getCashControlDetailForPayAppDocNumber(java.lang.String)
227         */
228        public CashControlDetail getCashControlDetailForPayAppDocNumber(String payAppDocNumber) {
229            if (StringUtils.isBlank(payAppDocNumber)) {
230                throw new IllegalArgumentException("A null or blank payAppDocNumber parameter was passed in.");
231            }
232            CashControlDetail cashControlDetail = cashControlDetailDao.getCashControlDetailByRefDocNumber(payAppDocNumber);
233            return cashControlDetail;
234        }
235        
236        public void setBusinessObjectService(BusinessObjectService businessObjectService) {
237            this.businessObjectService = businessObjectService;
238        }
239    
240        public PaymentApplicationDocument createInvoicePaidAppliedsForEntireInvoiceDocument(CustomerInvoiceDocument customerInvoiceDocument, PaymentApplicationDocument paymentApplicationDocument) {
241            
242            //  clear any existing paidapplieds
243            paymentApplicationDocument.getInvoicePaidApplieds().clear();
244    
245            int paidAppliedItemNumber = 1;
246            for(CustomerInvoiceDetail detail : customerInvoiceDocument.getCustomerInvoiceDetailsWithoutDiscounts()) {
247    
248                //  create the new paidapplied
249                InvoicePaidApplied invoicePaidApplied = createInvoicePaidAppliedForInvoiceDetail(
250                        detail, paymentApplicationDocument, paidAppliedItemNumber);
251                
252                // add it to the payment application document list of applied payments
253                paymentApplicationDocument.getInvoicePaidApplieds().add(invoicePaidApplied);
254                paidAppliedItemNumber++;
255            }
256            
257            return paymentApplicationDocument;
258        }
259        
260        /**
261         * @see org.kuali.kfs.module.ar.document.service.PaymentApplicationDocumentService#createInvoicePaidAppliedForInvoiceDetail(org.kuali.kfs.module.ar.businessobject.CustomerInvoiceDetail, org.kuali.rice.kns.util.KualiDecimal)
262         */
263        public InvoicePaidApplied createInvoicePaidAppliedForInvoiceDetail(CustomerInvoiceDetail customerInvoiceDetail, PaymentApplicationDocument paymentApplicationDocument, Integer paidAppliedItemNumber) {
264    
265            Integer universityFiscalYear = universityDateService.getCurrentFiscalYear();
266            String universityFiscalPeriodCode = universityDateService.getCurrentUniversityDate().getAccountingPeriod().getUniversityFiscalPeriodCode();
267            
268            InvoicePaidApplied invoicePaidApplied = new InvoicePaidApplied();
269            
270            // set the document number for the invoice paid applied to the payment application document number.
271            invoicePaidApplied.setDocumentNumber(paymentApplicationDocument.getDocumentNumber());
272            
273            // Set the invoice paid applied ref doc number to the document number for the customer invoice document
274            invoicePaidApplied.setFinancialDocumentReferenceInvoiceNumber(customerInvoiceDetail.getDocumentNumber());
275            
276            invoicePaidApplied.setInvoiceItemNumber(customerInvoiceDetail.getSequenceNumber());
277            invoicePaidApplied.setInvoiceItemAppliedAmount(customerInvoiceDetail.getAmountOpen());
278            invoicePaidApplied.setUniversityFiscalYear(universityFiscalYear);
279            invoicePaidApplied.setUniversityFiscalPeriodCode(universityFiscalPeriodCode);
280            invoicePaidApplied.setPaidAppliedItemNumber(paidAppliedItemNumber);
281    
282            return invoicePaidApplied;
283        }
284    
285        /**
286         * @see org.kuali.kfs.module.ar.document.service.PaymentApplicationDocumentService#customerInvoiceDetailPairsWithInvoicePaidApplied(org.kuali.kfs.module.ar.businessobject.CustomerInvoiceDetail, org.kuali.kfs.module.ar.businessobject.InvoicePaidApplied)
287         */
288        public boolean customerInvoiceDetailPairsWithInvoicePaidApplied(CustomerInvoiceDetail customerInvoiceDetail, InvoicePaidApplied invoicePaidApplied) {
289            boolean pairs = true;
290            pairs &= customerInvoiceDetail.getSequenceNumber().equals(invoicePaidApplied.getInvoiceItemNumber());
291            pairs &= customerInvoiceDetail.getDocumentNumber().equals(invoicePaidApplied.getFinancialDocumentReferenceInvoiceNumber());
292            return pairs;
293        }
294        
295        public DocumentService getDocumentService() {
296            return documentService;
297        }
298    
299        public void setDocumentService(DocumentService documentService) {
300            this.documentService = documentService;
301        }
302        
303        public void setInvoicePaidAppliedService(InvoicePaidAppliedService invoicePaidAppliedService) {
304            this.invoicePaidAppliedService = invoicePaidAppliedService;
305        }
306        
307        public BusinessObjectService getBusinessObjectService() {
308            return businessObjectService;
309        }
310    
311        public NonAppliedHoldingService getNonAppliedHoldingService() {
312            return nonAppliedHoldingService;
313        }
314    
315        public void setNonAppliedHoldingService(NonAppliedHoldingService nonAppliedHoldingService) {
316            this.nonAppliedHoldingService = nonAppliedHoldingService;
317        }
318        
319        public void setUniversityDateService(UniversityDateService universityDateService) {
320            this.universityDateService = universityDateService;
321        }
322    
323        public void setCashControlDetailDao(CashControlDetailDao cashControlDetailDao) {
324            this.cashControlDetailDao = cashControlDetailDao;
325        }
326        
327    }