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 }