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.math.BigDecimal; 019 import java.util.Collection; 020 import java.util.HashMap; 021 import java.util.List; 022 import java.util.Map; 023 024 import org.kuali.kfs.module.ar.businessobject.CustomerCreditMemoDetail; 025 import org.kuali.kfs.module.ar.businessobject.CustomerInvoiceDetail; 026 import org.kuali.kfs.module.ar.businessobject.InvoicePaidApplied; 027 import org.kuali.kfs.module.ar.document.CustomerCreditMemoDocument; 028 import org.kuali.kfs.module.ar.document.CustomerInvoiceDocument; 029 import org.kuali.kfs.module.ar.document.service.AccountsReceivableTaxService; 030 import org.kuali.kfs.module.ar.document.service.CustomerCreditMemoDocumentService; 031 import org.kuali.kfs.module.ar.document.service.InvoicePaidAppliedService; 032 import org.kuali.kfs.sys.context.SpringContext; 033 import org.kuali.kfs.sys.service.UniversityDateService; 034 import org.kuali.rice.kew.exception.WorkflowException; 035 import org.kuali.rice.kns.service.BusinessObjectService; 036 import org.kuali.rice.kns.service.DateTimeService; 037 import org.kuali.rice.kns.service.DocumentService; 038 import org.kuali.rice.kns.util.KualiDecimal; 039 import org.kuali.rice.kns.util.ObjectUtils; 040 041 public class CustomerCreditMemoDocumentServiceImpl implements CustomerCreditMemoDocumentService { 042 043 private DocumentService documentService; 044 private InvoicePaidAppliedService<CustomerInvoiceDetail> paidAppliedService; 045 private UniversityDateService universityDateService; 046 private BusinessObjectService businessObjectService; 047 private DateTimeService dateTimeService; 048 049 public void completeCustomerCreditMemo(CustomerCreditMemoDocument creditMemo) { 050 051 // retrieve the document and make sure its not already closed, crash if so 052 String invoiceNumber = creditMemo.getFinancialDocumentReferenceInvoiceNumber(); 053 CustomerInvoiceDocument invoice; 054 try { 055 invoice = (CustomerInvoiceDocument) documentService.getByDocumentHeaderId(invoiceNumber); 056 } 057 catch (WorkflowException e) { 058 throw new RuntimeException("A WorkflowException was generated when trying to load Customer Invoice #" + invoiceNumber + ".", e); 059 } 060 if (!invoice.isOpenInvoiceIndicator()) { 061 throw new UnsupportedOperationException("The CreditMemo Document #" + creditMemo.getDocumentNumber() + " attempted to credit " + 062 "an Invoice [#" + invoiceNumber + "] that was already closed. This is not supported."); 063 } 064 065 // this needs a little explanation. we have to calculate manually 066 // whether we've written off the whole thing, because the regular 067 // code uses the invoice paid applieds to discount, but since those 068 // are added but not committed in this transaction, they're also not 069 // visible in this transaction, so we do it manually. 070 KualiDecimal openAmount = invoice.getOpenAmount(); 071 072 Integer paidAppliedItemNumber = 0; 073 074 // retrieve the customer invoice details, and generate paid applieds for each 075 List<CustomerCreditMemoDetail> details = creditMemo.getCreditMemoDetails(); 076 for (CustomerCreditMemoDetail detail : details) { 077 CustomerInvoiceDetail invoiceDetail = detail.getCustomerInvoiceDetail(); 078 079 // if credit amount is zero, do nothing 080 if (detail.getCreditMemoLineTotalAmount().isZero()) { 081 continue; 082 } 083 084 // if credit amount is greater than the open amount, crash and complain 085 if (detail.getCreditMemoLineTotalAmount().abs().isGreaterThan(invoiceDetail.getAmountOpen())) { 086 throw new UnsupportedOperationException("The credit detail for CreditMemo Document #" + creditMemo.getDocumentNumber() + " attempted " + 087 "to credit more than the Open Amount on the Invoice Detail. This is not supported."); 088 } 089 090 // retrieve the number of current paid applieds, so we dont have item number overlap 091 if (paidAppliedItemNumber == 0) { 092 paidAppliedItemNumber = paidAppliedService.getNumberOfInvoicePaidAppliedsForInvoiceDetail(invoiceNumber, 093 invoiceDetail.getInvoiceItemNumber()); 094 } 095 096 097 // create and save the paidApplied 098 InvoicePaidApplied invoicePaidApplied = new InvoicePaidApplied(); 099 invoicePaidApplied.setDocumentNumber(creditMemo.getDocumentNumber()); 100 invoicePaidApplied.setPaidAppliedItemNumber(paidAppliedItemNumber++); 101 invoicePaidApplied.setFinancialDocumentReferenceInvoiceNumber(invoiceNumber); 102 invoicePaidApplied.setInvoiceItemNumber(invoiceDetail.getInvoiceItemNumber()); 103 invoicePaidApplied.setUniversityFiscalYear(universityDateService.getCurrentFiscalYear()); 104 invoicePaidApplied.setUniversityFiscalPeriodCode(universityDateService.getCurrentUniversityDate().getUniversityFiscalAccountingPeriod()); 105 invoicePaidApplied.setInvoiceItemAppliedAmount(detail.getCreditMemoLineTotalAmount().abs()); 106 openAmount = openAmount.subtract(detail.getCreditMemoLineTotalAmount().abs()); 107 businessObjectService.save(invoicePaidApplied); 108 } 109 110 // if its open, but now with a zero openamount, then close it 111 if (invoice.isOpenInvoiceIndicator() && KualiDecimal.ZERO.equals(openAmount)) { 112 invoice.setOpenInvoiceIndicator(false); 113 invoice.setClosedDate(dateTimeService.getCurrentSqlDate()); 114 documentService.updateDocument(invoice); 115 } 116 } 117 118 public void recalculateCustomerCreditMemoDocument(CustomerCreditMemoDocument customerCreditMemoDocument, boolean blanketApproveDocumentEventFlag) { 119 KualiDecimal customerCreditMemoDetailItemAmount; 120 BigDecimal itemQuantity; 121 122 String invDocumentNumber = customerCreditMemoDocument.getFinancialDocumentReferenceInvoiceNumber(); 123 List<CustomerCreditMemoDetail> customerCreditMemoDetails = customerCreditMemoDocument.getCreditMemoDetails(); 124 125 if (!blanketApproveDocumentEventFlag) 126 customerCreditMemoDocument.resetTotals(); 127 128 for (CustomerCreditMemoDetail customerCreditMemoDetail:customerCreditMemoDetails) { 129 // no data entered for the current credit memo detail -> no processing needed 130 itemQuantity = customerCreditMemoDetail.getCreditMemoItemQuantity(); 131 customerCreditMemoDetailItemAmount = customerCreditMemoDetail.getCreditMemoItemTotalAmount(); 132 if (ObjectUtils.isNull(itemQuantity) && ObjectUtils.isNull(customerCreditMemoDetailItemAmount)) { 133 if (!blanketApproveDocumentEventFlag) 134 customerCreditMemoDetail.setDuplicateCreditMemoItemTotalAmount(null); 135 continue; 136 } 137 138 // if item amount was entered, it takes precedence, if not, use the item quantity to re-calc amount 139 if (ObjectUtils.isNotNull(customerCreditMemoDetailItemAmount)) { 140 customerCreditMemoDetail.recalculateBasedOnEnteredItemAmount(customerCreditMemoDocument); 141 } // if item quantity was entered 142 else { 143 customerCreditMemoDetail.recalculateBasedOnEnteredItemQty(customerCreditMemoDocument); 144 if (!blanketApproveDocumentEventFlag) 145 customerCreditMemoDetailItemAmount = customerCreditMemoDetail.getCreditMemoItemTotalAmount(); 146 } 147 148 if (!blanketApproveDocumentEventFlag) { 149 customerCreditMemoDetail.setDuplicateCreditMemoItemTotalAmount(customerCreditMemoDetailItemAmount); 150 boolean isCustomerInvoiceDetailTaxable = SpringContext.getBean(AccountsReceivableTaxService.class).isCustomerInvoiceDetailTaxable(customerCreditMemoDocument.getInvoice(), customerCreditMemoDetail.getCustomerInvoiceDetail()); 151 customerCreditMemoDocument.recalculateTotals(customerCreditMemoDetailItemAmount,isCustomerInvoiceDetailTaxable); 152 } 153 } 154 155 // force the docHeader docTotal 156 customerCreditMemoDocument.getDocumentHeader().setFinancialDocumentTotalAmount(customerCreditMemoDocument.getCrmTotalAmount()); 157 } 158 159 public Collection<CustomerCreditMemoDocument> getCustomerCreditMemoDocumentByInvoiceDocument(String invoiceNumber) { 160 Map<String, String> fieldValues = new HashMap<String, String>(); 161 fieldValues.put("financialDocumentReferenceInvoiceNumber", invoiceNumber); 162 BusinessObjectService service = SpringContext.getBean(BusinessObjectService.class); 163 164 Collection<CustomerCreditMemoDocument> creditMemos = 165 service.findMatching(CustomerCreditMemoDocument.class, fieldValues); 166 167 return creditMemos; 168 } 169 170 public boolean isThereNoDataToSubmit(CustomerCreditMemoDocument customerCreditMemoDocument) { 171 boolean success = true; 172 KualiDecimal customerCreditMemoDetailItemAmount; 173 BigDecimal itemQuantity; 174 List<CustomerCreditMemoDetail> customerCreditMemoDetails = customerCreditMemoDocument.getCreditMemoDetails(); 175 176 for (CustomerCreditMemoDetail customerCreditMemoDetail:customerCreditMemoDetails) { 177 // no data entered for the current credit memo detail -> no processing needed 178 itemQuantity = customerCreditMemoDetail.getCreditMemoItemQuantity(); 179 customerCreditMemoDetailItemAmount = customerCreditMemoDetail.getCreditMemoItemTotalAmount(); 180 if (ObjectUtils.isNotNull(itemQuantity) || ObjectUtils.isNotNull(customerCreditMemoDetailItemAmount)) { 181 success = false; 182 break; 183 } 184 } 185 return success; 186 } 187 188 public void setDocumentService(DocumentService documentService) { 189 this.documentService = documentService; 190 } 191 192 public void setPaidAppliedService(InvoicePaidAppliedService<CustomerInvoiceDetail> paidAppliedService) { 193 this.paidAppliedService = paidAppliedService; 194 } 195 196 public void setUniversityDateService(UniversityDateService universityDateService) { 197 this.universityDateService = universityDateService; 198 } 199 200 public void setBusinessObjectService(BusinessObjectService businessObjectService) { 201 this.businessObjectService = businessObjectService; 202 } 203 204 public void setDateTimeService(DateTimeService dateTimeService) { 205 this.dateTimeService = dateTimeService; 206 } 207 208 }