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 }