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 }