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;
017
018 import java.math.BigDecimal;
019 import java.sql.Date;
020 import java.sql.Timestamp;
021 import java.util.ArrayList;
022 import java.util.Iterator;
023 import java.util.List;
024
025 import org.apache.commons.lang.StringUtils;
026 import org.kuali.kfs.module.ar.ArConstants;
027 import org.kuali.kfs.module.ar.businessobject.AccountsReceivableDocumentHeader;
028 import org.kuali.kfs.module.ar.businessobject.CustomerCreditMemoDetail;
029 import org.kuali.kfs.module.ar.businessobject.CustomerInvoiceDetail;
030 import org.kuali.kfs.module.ar.businessobject.ReceivableCustomerCreditMemoDetail;
031 import org.kuali.kfs.module.ar.businessobject.ReceivableCustomerInvoiceDetail;
032 import org.kuali.kfs.module.ar.businessobject.SalesTaxCustomerCreditMemoDetail;
033 import org.kuali.kfs.module.ar.document.service.AccountsReceivableDocumentHeaderService;
034 import org.kuali.kfs.module.ar.document.service.AccountsReceivableTaxService;
035 import org.kuali.kfs.module.ar.document.service.CustomerCreditMemoDocumentService;
036 import org.kuali.kfs.module.ar.document.service.CustomerInvoiceDetailService;
037 import org.kuali.kfs.module.ar.document.service.CustomerInvoiceGLPEService;
038 import org.kuali.kfs.sys.businessobject.GeneralLedgerPendingEntry;
039 import org.kuali.kfs.sys.businessobject.GeneralLedgerPendingEntrySequenceHelper;
040 import org.kuali.kfs.sys.businessobject.GeneralLedgerPendingEntrySourceDetail;
041 import org.kuali.kfs.sys.businessobject.TaxDetail;
042 import org.kuali.kfs.sys.context.SpringContext;
043 import org.kuali.kfs.sys.document.AmountTotaling;
044 import org.kuali.kfs.sys.document.GeneralLedgerPendingEntrySource;
045 import org.kuali.kfs.sys.document.GeneralLedgerPostingDocumentBase;
046 import org.kuali.kfs.sys.service.GeneralLedgerPendingEntryService;
047 import org.kuali.kfs.sys.service.TaxService;
048 import org.kuali.kfs.sys.service.UniversityDateService;
049 import org.kuali.rice.kew.dto.DocumentRouteStatusChangeDTO;
050 import org.kuali.rice.kns.exception.ValidationException;
051 import org.kuali.rice.kns.rule.event.BlanketApproveDocumentEvent;
052 import org.kuali.rice.kns.rule.event.KualiDocumentEvent;
053 import org.kuali.rice.kns.service.DataDictionaryService;
054 import org.kuali.rice.kns.service.DateTimeService;
055 import org.kuali.rice.kns.service.ParameterService;
056 import org.kuali.rice.kns.util.DateUtils;
057 import org.kuali.rice.kns.util.KualiDecimal;
058 import org.kuali.rice.kns.util.ObjectUtils;
059 import org.kuali.rice.kns.util.TypedArrayList;
060 import org.kuali.rice.kns.web.format.CurrencyFormatter;
061
062 /**
063 * @author Kuali Nervous System Team (kualidev@oncourse.iu.edu)
064 */
065 public class CustomerCreditMemoDocument extends GeneralLedgerPostingDocumentBase implements GeneralLedgerPendingEntrySource, AmountTotaling {
066
067 protected static org.apache.log4j.Logger LOG = org.apache.log4j.Logger.getLogger(CustomerCreditMemoDocument.class);
068
069 protected String statusCode;
070 protected String financialDocumentReferenceInvoiceNumber;
071
072 protected KualiDecimal crmTotalItemAmount = KualiDecimal.ZERO;
073 protected KualiDecimal crmTotalTaxAmount = KualiDecimal.ZERO;
074 protected KualiDecimal crmTotalAmount = KualiDecimal.ZERO;
075
076 protected Integer invOutstandingDays;
077
078 protected CustomerInvoiceDocument invoice;
079 protected AccountsReceivableDocumentHeader accountsReceivableDocumentHeader;
080
081 protected List<CustomerCreditMemoDetail> creditMemoDetails;
082
083 protected transient TaxService taxService;
084 protected transient AccountsReceivableTaxService arTaxService;
085
086 public CustomerCreditMemoDocument() {
087 super();
088 setPostingYear(SpringContext.getBean(UniversityDateService.class).getCurrentFiscalYear());
089 creditMemoDetails = new TypedArrayList(CustomerCreditMemoDetail.class);
090 setGeneralLedgerPendingEntries(new ArrayList<GeneralLedgerPendingEntry>());
091 }
092
093 /**
094 * Gets the creditMemoDetails attribute.
095 * @return Returns the creditMemoDetails.
096 */
097 public List<CustomerCreditMemoDetail> getCreditMemoDetails() {
098 return creditMemoDetails;
099 }
100
101 /**
102 * Sets the creditMemoDetails attribute value.
103 * @param creditMemoDetails The creditMemoDetails to set.
104 */
105 public void setCreditMemoDetails(List<CustomerCreditMemoDetail> creditMemoDetails) {
106 this.creditMemoDetails = creditMemoDetails;
107 }
108
109 /**
110 * Gets the financialDocumentReferenceInvoiceNumber attribute.
111 * @return Returns the financialDocumentReferenceInvoiceNumber.
112 */
113 public String getFinancialDocumentReferenceInvoiceNumber() {
114 return financialDocumentReferenceInvoiceNumber;
115 }
116
117 /**
118 * Sets the financialDocumentReferenceInvoiceNumber attribute value.
119 * @param financialDocumentReferenceInvoiceNumber The financialDocumentReferenceInvoiceNumber to set.
120 */
121 public void setFinancialDocumentReferenceInvoiceNumber(String financialDocumentReferenceInvoiceNumber) {
122 if (financialDocumentReferenceInvoiceNumber != null)
123 financialDocumentReferenceInvoiceNumber = financialDocumentReferenceInvoiceNumber.toUpperCase();
124
125 this.financialDocumentReferenceInvoiceNumber = financialDocumentReferenceInvoiceNumber;
126 }
127
128 /**
129 * Gets the invoice attribute.
130 * @return Returns the invoice.
131 */
132 public CustomerInvoiceDocument getInvoice() {
133 if (ObjectUtils.isNull(invoice) && StringUtils.isNotEmpty(financialDocumentReferenceInvoiceNumber)){
134 refreshReferenceObject("invoice");
135 }
136
137 return invoice;
138 }
139
140 /**
141 * Sets the invoice attribute value.
142 * @param invoice The invoice to set.
143 */
144 public void setInvoice(CustomerInvoiceDocument invoice) {
145 this.invoice = invoice;
146 }
147
148 /**
149 * Gets the statusCode attribute.
150 * @return Returns the statusCode.
151 */
152 public String getStatusCode() {
153 return statusCode;
154 }
155
156 /**
157 * Sets the statusCode attribute value.
158 * @param statusCode The statusCode to set.
159 */
160 public void setStatusCode(String statusCode) {
161 this.statusCode = statusCode;
162 }
163
164 /**
165 * Initializes the values for a new document.
166 */
167 public void initiateDocument() {
168 LOG.debug("initiateDocument() started");
169 setStatusCode(ArConstants.CustomerCreditMemoStatuses.INITIATE);
170 }
171
172 /**
173 * Clear out the initially populated fields.
174 */
175 public void clearInitFields() {
176 LOG.debug("clearDocument() started");
177
178 // Clearing document Init fields
179 setFinancialDocumentReferenceInvoiceNumber(null);
180 }
181
182 /**
183 * Gets the crmTotalAmount attribute.
184 * @return Returns the crmTotalAmount.
185 */
186 public KualiDecimal getCrmTotalAmount() {
187 return crmTotalAmount;
188 }
189
190 /**
191 * This method returns the crmTotalAmount as a currency formatted string.
192 * @return String
193 */
194 public String getCurrencyFormattedCrmTotalAmount() {
195 return (String) new CurrencyFormatter().format(crmTotalAmount);
196 }
197
198 /**
199 * Sets the crmTotalAmount attribute value.
200 * @param crmTotalAmount The crmTotalAmount to set.
201 */
202 public void setCrmTotalAmount(KualiDecimal crmTotalAmount) {
203 this.crmTotalAmount = crmTotalAmount;
204 }
205
206 /**
207 * Gets the crmTotalItemAmount attribute.
208 * @return Returns the crmTotalItemAmount.
209 */
210 public KualiDecimal getCrmTotalItemAmount() {
211 return crmTotalItemAmount;
212 }
213
214 /**
215 * This method returns the crmTotalItemAmount as a currency formatted string.
216 * @return String
217 */
218 public String getCurrencyFormattedCrmTotalItemAmount() {
219 return (String) new CurrencyFormatter().format(crmTotalItemAmount);
220 }
221
222 /**
223 * Sets the crmTotalItemAmount attribute value.
224 * @param crmTotalItemAmount The crmTotalItemAmount to set.
225 */
226 public void setCrmTotalItemAmount(KualiDecimal crmTotalItemAmount) {
227 this.crmTotalItemAmount = crmTotalItemAmount;
228 }
229
230 /**
231 * Gets the crmTotalTaxAmount attribute.
232 * @return Returns the crmTotalTaxAmount.
233 */
234 public KualiDecimal getCrmTotalTaxAmount() {
235 return crmTotalTaxAmount;
236 }
237
238 /**
239 * This method returns the crmTotalTaxAmount as a currency formatted string.
240 * @return String
241 */
242 public String getCurrencyFormattedCrmTotalTaxAmount() {
243 return (String) new CurrencyFormatter().format(crmTotalTaxAmount);
244 }
245
246 /**
247 * Sets the crmTotalTaxAmount attribute value.
248 * @param crmTotalTaxAmount The crmTotalTaxAmount to set.
249 */
250 public void setCrmTotalTaxAmount(KualiDecimal crmTotalTaxAmount) {
251 this.crmTotalTaxAmount = crmTotalTaxAmount;
252 }
253
254 /**
255 * Gets the invOutstandingDays attribute.
256 * @return Returns the invOutstandingDays.
257 */
258 public Integer getInvOutstandingDays() {
259 Timestamp invBillingDateTimestamp = new Timestamp(invoice.getBillingDate().getTime());
260 Timestamp todayDateTimestamp = new Timestamp(SpringContext.getBean(DateTimeService.class).getCurrentSqlDate().getTime());
261 double diffInDays = DateUtils.getDifferenceInDays(invBillingDateTimestamp, todayDateTimestamp);
262 invOutstandingDays = new Integer(new KualiDecimal(diffInDays).intValue());
263
264 return invOutstandingDays;
265 }
266
267 /**
268 * Sets the invOutstandingDays attribute value.
269 * @param invOutstandingDays The invOutstandingDays to set.
270 */
271 public void setInvOutstandingDays(Integer invOutstandingDays) {
272 this.invOutstandingDays = invOutstandingDays;
273 }
274
275 public void recalculateTotalsBasedOnChangedItemAmount(CustomerCreditMemoDetail customerCreditMemoDetail) {
276 KualiDecimal duplicateCreditMemoItemTotalAmount = customerCreditMemoDetail.getDuplicateCreditMemoItemTotalAmount();
277 KualiDecimal creditMemoItemTotalAmount = customerCreditMemoDetail.getCreditMemoItemTotalAmount();
278
279 // substract the 'old' item amount, tax amount, and total amount accordingly from totals
280 if (ObjectUtils.isNotNull(duplicateCreditMemoItemTotalAmount))
281 prepareTotalsForUpdate(duplicateCreditMemoItemTotalAmount,getArTaxService().isCustomerInvoiceDetailTaxable(getInvoice(), customerCreditMemoDetail.getCustomerInvoiceDetail()));
282
283 recalculateTotals(creditMemoItemTotalAmount,getArTaxService().isCustomerInvoiceDetailTaxable(getInvoice(), customerCreditMemoDetail.getCustomerInvoiceDetail()));
284
285 // update duplicate credit memo item amount with 'new' value
286 customerCreditMemoDetail.setDuplicateCreditMemoItemTotalAmount(creditMemoItemTotalAmount);
287 }
288
289 public void recalculateTotals(CustomerCreditMemoDetail customerCreditMemoDetail) {
290 KualiDecimal duplicateCreditMemoItemTotalAmount = customerCreditMemoDetail.getDuplicateCreditMemoItemTotalAmount();
291
292 // substract the 'old' item amount, tax amount, and total amount accordingly from totals
293 if (ObjectUtils.isNotNull(duplicateCreditMemoItemTotalAmount)) {
294 prepareTotalsForUpdate(duplicateCreditMemoItemTotalAmount,getArTaxService().isCustomerInvoiceDetailTaxable(getInvoice(), customerCreditMemoDetail.getCustomerInvoiceDetail()));
295 customerCreditMemoDetail.setDuplicateCreditMemoItemTotalAmount(null);
296 }
297 }
298
299 protected void prepareTotalsForUpdate(KualiDecimal oldItemAmount, boolean isTaxableItemFlag) {
300 KualiDecimal oldItemTaxAmount = KualiDecimal.ZERO;
301 if (isTaxableItemFlag)
302 oldItemTaxAmount = getTaxService().getTotalSalesTaxAmount(invoice.getBillingDate(), getPostalCode(), oldItemAmount);
303
304 crmTotalItemAmount = crmTotalItemAmount.subtract(oldItemAmount);
305 crmTotalTaxAmount = crmTotalTaxAmount.subtract(oldItemTaxAmount);
306 crmTotalAmount = crmTotalAmount.subtract(oldItemAmount.add(oldItemTaxAmount));
307 }
308
309 public void resetTotals() {
310 crmTotalItemAmount = KualiDecimal.ZERO;
311 crmTotalTaxAmount = KualiDecimal.ZERO;
312 crmTotalAmount = KualiDecimal.ZERO;
313 }
314
315 public void recalculateTotals(KualiDecimal itemAmount, boolean isTaxableItemFlag) {
316 crmTotalItemAmount = crmTotalItemAmount.add(itemAmount);
317 if (isTaxableItemFlag)
318 crmTotalTaxAmount = crmTotalTaxAmount.add(getTaxService().getTotalSalesTaxAmount(invoice.getBillingDate(), getPostalCode(), itemAmount));
319 crmTotalAmount = crmTotalItemAmount.add(crmTotalTaxAmount);
320 getDocumentHeader().setFinancialDocumentTotalAmount(crmTotalAmount);
321 }
322
323 /*
324 * populate customer credit memo details based on the invoice info
325 */
326 public void populateCustomerCreditMemoDetails() {
327 CustomerCreditMemoDetail customerCreditMemoDetail;
328 KualiDecimal invItemTaxAmount, openInvoiceQuantity, openInvoiceAmount;
329
330 CustomerInvoiceDetailService customerInvoiceDetailService = SpringContext.getBean(CustomerInvoiceDetailService.class);
331 setStatusCode(ArConstants.CustomerCreditMemoStatuses.IN_PROCESS);
332
333 //set accounts receivable document header
334 AccountsReceivableDocumentHeader accountsReceivableDocumentHeader = SpringContext.getBean(AccountsReceivableDocumentHeaderService.class).getNewAccountsReceivableDocumentHeaderForCurrentUser();
335 accountsReceivableDocumentHeader.setDocumentNumber(getDocumentNumber());
336 accountsReceivableDocumentHeader.setCustomerNumber(invoice.getAccountsReceivableDocumentHeader().getCustomerNumber());
337 setAccountsReceivableDocumentHeader(accountsReceivableDocumentHeader);
338
339 List<CustomerInvoiceDetail> customerInvoiceDetails = invoice.getCustomerInvoiceDetailsWithoutDiscounts();
340 for (CustomerInvoiceDetail customerInvoiceDetail : customerInvoiceDetails) {
341 customerCreditMemoDetail = new CustomerCreditMemoDetail();
342
343 if(ObjectUtils.isNull(customerInvoiceDetail.getInvoiceItemTaxAmount())){
344 customerInvoiceDetail.setInvoiceItemTaxAmount(KualiDecimal.ZERO);
345 }
346 customerCreditMemoDetail.setInvoiceLineTotalAmount(customerInvoiceDetail.getInvoiceItemTaxAmount(), customerInvoiceDetail.getInvoiceItemPreTaxAmount());
347 customerCreditMemoDetail.setReferenceInvoiceItemNumber(customerInvoiceDetail.getSequenceNumber());
348 openInvoiceAmount = customerInvoiceDetail.getAmountOpen();
349
350 customerCreditMemoDetail.setInvoiceOpenItemAmount(openInvoiceAmount);
351 customerCreditMemoDetail.setInvoiceOpenItemQuantity(getInvoiceOpenItemQuantity(customerCreditMemoDetail, customerInvoiceDetail));
352 customerCreditMemoDetail.setDocumentNumber(this.documentNumber);
353 customerCreditMemoDetail.setFinancialDocumentReferenceInvoiceNumber(this.financialDocumentReferenceInvoiceNumber);
354
355 // this is a hookup for institution custom to update financial object code for prior year(s) invoice
356 //customerInvoiceDetailService.updateFinancialObjectCode(customerCreditMemoDetail);
357
358 creditMemoDetails.add(customerCreditMemoDetail);
359 }
360
361 }
362
363 /**
364 * This method populates credit memo details that aren't saved in database
365 */
366 public void populateCustomerCreditMemoDetailsAfterLoad() {
367
368 KualiDecimal openInvoiceAmount, invItemTaxAmount, creditMemoItemAmount, creditMemoTaxAmount, taxRate;
369 CustomerInvoiceDetailService customerInvoiceDetailService = SpringContext.getBean(CustomerInvoiceDetailService.class);
370
371 List<CustomerInvoiceDetail> customerInvoiceDetails = getInvoice().getCustomerInvoiceDetailsWithoutDiscounts();
372 for (CustomerCreditMemoDetail creditMemoDetail : creditMemoDetails) {
373
374 creditMemoDetail.setFinancialDocumentReferenceInvoiceNumber(this.financialDocumentReferenceInvoiceNumber);
375 CustomerInvoiceDetail customerInvoiceDetail = creditMemoDetail.getCustomerInvoiceDetail();
376 openInvoiceAmount = customerInvoiceDetail.getAmountOpen();
377 creditMemoDetail.setInvoiceOpenItemAmount(openInvoiceAmount);
378
379 creditMemoDetail.setInvoiceOpenItemQuantity(getInvoiceOpenItemQuantity(creditMemoDetail, customerInvoiceDetail));
380
381 if(ObjectUtils.isNull(customerInvoiceDetail.getInvoiceItemTaxAmount())){
382 customerInvoiceDetail.setInvoiceItemTaxAmount(KualiDecimal.ZERO);
383 }
384 creditMemoDetail.setInvoiceLineTotalAmount(customerInvoiceDetail.getInvoiceItemTaxAmount(), customerInvoiceDetail.getInvoiceItemPreTaxAmount());
385
386 creditMemoItemAmount = creditMemoDetail.getCreditMemoItemTotalAmount();
387 creditMemoDetail.setDuplicateCreditMemoItemTotalAmount(creditMemoItemAmount);
388 if (ObjectUtils.isNotNull(creditMemoItemAmount)) {
389 if( getArTaxService().isCustomerInvoiceDetailTaxable(invoice, customerInvoiceDetail) )
390 creditMemoTaxAmount = getTaxService().getTotalSalesTaxAmount(invoice.getBillingDate(), getPostalCode(), creditMemoItemAmount);
391 else
392 creditMemoTaxAmount = KualiDecimal.ZERO;
393 creditMemoDetail.setCreditMemoItemTaxAmount(creditMemoTaxAmount);
394 creditMemoDetail.setCreditMemoLineTotalAmount(creditMemoItemAmount.add(creditMemoTaxAmount));
395
396 crmTotalItemAmount = crmTotalItemAmount.add(creditMemoItemAmount);
397 crmTotalTaxAmount = crmTotalTaxAmount.add(creditMemoTaxAmount);
398 crmTotalAmount = crmTotalAmount.add(creditMemoItemAmount.add(creditMemoTaxAmount));
399 }
400
401 // this is a hookup for institution custom to update financial object code for prior year(s) invoice
402 //customerInvoiceDetailService.updateFinancialObjectCode(creditMemoDetail);
403 }
404 }
405
406 public KualiDecimal getInvoiceOpenItemQuantity(CustomerCreditMemoDetail customerCreditMemoDetail,CustomerInvoiceDetail customerInvoiceDetail) {
407 KualiDecimal invoiceOpenItemQuantity;
408 BigDecimal invoiceItemUnitPrice = customerInvoiceDetail.getInvoiceItemUnitPrice();
409 if (ObjectUtils.isNull(invoiceItemUnitPrice) || invoiceItemUnitPrice.equals(BigDecimal.ZERO))
410 invoiceOpenItemQuantity = KualiDecimal.ZERO;
411 else {
412 KualiDecimal invoiceOpenItemAmount = customerCreditMemoDetail.getInvoiceOpenItemAmount();
413 KualiDecimal invoiceOpenItemPretaxAmount = invoiceOpenItemAmount;
414 if( getArTaxService().isCustomerInvoiceDetailTaxable(getInvoice(), customerInvoiceDetail))
415 invoiceOpenItemPretaxAmount = getCustomerInvoiceDetailOpenPretaxAmount(invoiceOpenItemAmount);
416
417 invoiceOpenItemQuantity = new KualiDecimal(invoiceOpenItemPretaxAmount.bigDecimalValue().divide(invoiceItemUnitPrice, 4));
418 }
419 return invoiceOpenItemQuantity;
420 }
421
422 protected KualiDecimal getCustomerInvoiceDetailOpenPretaxAmount(KualiDecimal openAmount) {
423 Date dateOfTransaction = getInvoice().getBillingDate();
424 KualiDecimal pretaxAmount = SpringContext.getBean(TaxService.class).getPretaxAmount(dateOfTransaction, getPostalCode(), openAmount);
425
426 return pretaxAmount;
427 }
428
429 /**
430 * do all the calculations before the document gets saved
431 * gets called for 'Submit', 'Save', and 'Blanket Approved'
432 * @see org.kuali.rice.kns.document.Document#prepareForSave(org.kuali.rice.kns.rule.event.KualiDocumentEvent)
433 */
434 public void prepareForSave(KualiDocumentEvent event) {
435 CustomerCreditMemoDocument customerCreditMemoDocument = (CustomerCreditMemoDocument)event.getDocument();
436 CustomerCreditMemoDocumentService customerCreditMemoDocumentService = SpringContext.getBean(CustomerCreditMemoDocumentService.class);
437 if (event instanceof BlanketApproveDocumentEvent)
438 customerCreditMemoDocumentService.recalculateCustomerCreditMemoDocument(customerCreditMemoDocument,true);
439 else
440 customerCreditMemoDocumentService.recalculateCustomerCreditMemoDocument(customerCreditMemoDocument,false);
441
442 // generate GLPEs
443 if (!SpringContext.getBean(GeneralLedgerPendingEntryService.class).generateGeneralLedgerPendingEntries(this)) {
444 logErrors();
445 throw new ValidationException("general ledger GLPE generation failed");
446 }
447 super.prepareForSave(event);
448 }
449
450 /**
451 * @see org.kuali.kfs.sys.document.GeneralLedgerPendingEntrySource#generateDocumentGeneralLedgerPendingEntries(org.kuali.kfs.sys.businessobject.GeneralLedgerPendingEntrySequenceHelper)
452 */
453 public boolean generateDocumentGeneralLedgerPendingEntries(GeneralLedgerPendingEntrySequenceHelper sequenceHelper) {
454 boolean success = true;
455 return success;
456 }
457
458 /**
459 * @see org.kuali.kfs.sys.document.GeneralLedgerPendingEntrySource#isDebit(org.kuali.kfs.sys.businessobject.GeneralLedgerPendingEntrySourceDetail)
460 */
461 public boolean isDebit(GeneralLedgerPendingEntrySourceDetail postable) {
462 return false;
463 }
464
465 /**
466 * @see org.kuali.kfs.sys.document.GeneralLedgerPendingEntrySource#clearAnyGeneralLedgerPendingEntries()
467 */
468 public void clearAnyGeneralLedgerPendingEntries() {
469 generalLedgerPendingEntries = new ArrayList<GeneralLedgerPendingEntry>();
470 }
471
472 /**
473 * @see org.kuali.kfs.sys.document.GeneralLedgerPendingEntrySource#getGeneralLedgerPostables()
474 */
475 public List<GeneralLedgerPendingEntrySourceDetail> getGeneralLedgerPendingEntrySourceDetails() {
476 List<GeneralLedgerPendingEntrySourceDetail> generalLedgerPendingEntrySourceDetails = new ArrayList<GeneralLedgerPendingEntrySourceDetail>();
477 if (creditMemoDetails != null) {
478 Iterator iter = creditMemoDetails.iterator();
479 CustomerCreditMemoDetail customerCreditMemoDetail;
480 KualiDecimal amount;
481 while (iter.hasNext()) {
482 customerCreditMemoDetail = (CustomerCreditMemoDetail)iter.next();
483 amount = customerCreditMemoDetail.getCreditMemoItemTotalAmount();
484
485 // get only non empty credit memo details to generate GLPEs
486 if (ObjectUtils.isNotNull(amount) && amount.isGreaterThan(KualiDecimal.ZERO))
487 generalLedgerPendingEntrySourceDetails.add((GeneralLedgerPendingEntrySourceDetail)customerCreditMemoDetail);
488 }
489 }
490 return generalLedgerPendingEntrySourceDetails;
491 }
492
493 /**
494 * @see org.kuali.kfs.sys.document.GeneralLedgerPendingEntrySource#addPendingEntry(org.kuali.kfs.sys.businessobject.GeneralLedgerPendingEntry)
495 */
496 public void addPendingEntry(GeneralLedgerPendingEntry entry) {
497 generalLedgerPendingEntries.add(entry);
498 }
499
500 /**
501 * @see org.kuali.kfs.sys.document.GeneralLedgerPendingEntrySource#getGeneralLedgerPendingEntryAmountForGeneralLedgerPostable(org.kuali.kfs.sys.businessobject.GeneralLedgerPendingEntrySourceDetail)
502 */
503 public KualiDecimal getGeneralLedgerPendingEntryAmountForDetail(GeneralLedgerPendingEntrySourceDetail postable) {
504 return postable.getAmount();
505 }
506
507 /**
508 * Returns the general ledger input type code for the given document, using the DataDictionaryService
509 * @return the general ledger input type code for the given document
510 */
511 public String getFinancialDocumentTypeCode() {
512 return SpringContext.getBean(DataDictionaryService.class).getDocumentTypeNameByClass(this.getClass());
513 }
514
515 @Override
516 public List<Long> getWorkflowEngineDocumentIdsToLock() {
517 // a credit memo wont always update the source invoice, but sometimes it will so we include it here
518 if (StringUtils.isNotBlank(getFinancialDocumentReferenceInvoiceNumber())) {
519 List<Long> documentIds = new ArrayList<Long>();
520 documentIds.add(new Long(getFinancialDocumentReferenceInvoiceNumber()));
521 return documentIds;
522 }
523 return null;
524 }
525
526 /**
527 * When document is processed do the following:
528 *
529 * 1) Apply amounts to writeoff invoice
530 * 2) Mark off invoice indiciator
531 *
532 * @see org.kuali.kfs.sys.document.GeneralLedgerPostingDocumentBase#doRouteStatusChange()
533 */
534 @Override
535 public void doRouteStatusChange(DocumentRouteStatusChangeDTO statusChangeEvent){
536 super.doRouteStatusChange(statusChangeEvent);
537 if (getDocumentHeader().getWorkflowDocument().stateIsProcessed()) {
538
539 //have to populate because not all the customer credit memo details are populated while doc is in workflow
540 populateCustomerCreditMemoDetailsAfterLoad();
541
542 // apply writeoff amounts by only retrieving only the invoice details that ARE NOT discounts
543 CustomerCreditMemoDocumentService service = SpringContext.getBean(CustomerCreditMemoDocumentService.class);
544 service.completeCustomerCreditMemo(this);
545 }
546 }
547
548 /**
549 * This method creates the following GLPE's for the customer credit memo
550 *
551 * 1. Credit to receivable object for total line amount (excluding sales tax)
552 * 2. Debit to income object for total line amount (excluding sales tax)
553 * 3. Credit to receivable object in sales tax account(if sales tax exists)
554 * 4. Debit to liability object in sales tax account(if sales tax exists)
555 * 5. Credit to receivable object in district tax account(if district tax exists)
556 * 6. Debit to liability object code in district tax account(if district tax exists)
557 *
558 * @see org.kuali.kfs.service.impl.GenericGeneralLedgerPendingEntryGenerationProcessImpl#processGenerateGeneralLedgerPendingEntries(org.kuali.kfs.sys.document.GeneralLedgerPendingEntrySource, org.kuali.kfs.sys.businessobject.GeneralLedgerPendingEntrySourceDetail, org.kuali.kfs.sys.businessobject.GeneralLedgerPendingEntrySequenceHelper)
559 */
560
561 public boolean generateGeneralLedgerPendingEntries(GeneralLedgerPendingEntrySourceDetail glpeSourceDetail, GeneralLedgerPendingEntrySequenceHelper sequenceHelper) {
562
563 String receivableOffsetOption = SpringContext.getBean(ParameterService.class).getParameterValue(CustomerInvoiceDocument.class, ArConstants.GLPE_RECEIVABLE_OFFSET_GENERATION_METHOD);
564 boolean hasClaimOnCashOffset = ArConstants.GLPE_RECEIVABLE_OFFSET_GENERATION_METHOD_FAU.equals(receivableOffsetOption);
565
566 addReceivableGLPEs(sequenceHelper, glpeSourceDetail, hasClaimOnCashOffset);
567 sequenceHelper.increment();
568 addIncomeGLPEs(sequenceHelper, glpeSourceDetail, hasClaimOnCashOffset);
569
570 //if sales tax is enabled generate GLPEs
571 CustomerInvoiceDetail invoiceDetail = ((CustomerCreditMemoDetail) glpeSourceDetail).getCustomerInvoiceDetail();
572 if( getArTaxService().isCustomerInvoiceDetailTaxable(getInvoice(), invoiceDetail) )
573 addSalesTaxGLPEs( sequenceHelper, glpeSourceDetail, hasClaimOnCashOffset );
574
575 return true;
576 }
577
578 /**
579 * This method creates the receivable GLPEs for credit memo detail lines.
580 *
581 * @param poster
582 * @param sequenceHelper
583 * @param postable
584 * @param explicitEntry
585 */
586 protected void addReceivableGLPEs(GeneralLedgerPendingEntrySequenceHelper sequenceHelper, GeneralLedgerPendingEntrySourceDetail glpeSourceDetail, boolean hasClaimOnCashOffset) {
587
588 CustomerCreditMemoDetail customerCreditMemoDetail = (CustomerCreditMemoDetail)glpeSourceDetail;
589 CustomerInvoiceDetail customerInvoiceDetail = customerCreditMemoDetail.getCustomerInvoiceDetail();
590 ReceivableCustomerInvoiceDetail receivableCustomerInvoiceDetail = new ReceivableCustomerInvoiceDetail(customerInvoiceDetail, this.getInvoice());
591 boolean isDebit = false;
592
593 CustomerInvoiceGLPEService service = SpringContext.getBean(CustomerInvoiceGLPEService.class);
594 service.createAndAddGenericInvoiceRelatedGLPEs(this, receivableCustomerInvoiceDetail, sequenceHelper, isDebit, hasClaimOnCashOffset, customerCreditMemoDetail.getCreditMemoItemTotalAmount());
595 }
596
597 /**
598 * This method adds pending entry with transaction ledger entry amount set to item price * quantity
599 *
600 * @param poster
601 * @param sequenceHelper
602 * @param postable
603 * @param explicitEntry
604 */
605 protected void addIncomeGLPEs(GeneralLedgerPendingEntrySequenceHelper sequenceHelper, GeneralLedgerPendingEntrySourceDetail glpeSourceDetail, boolean hasClaimOnCashOffset) {
606
607 CustomerCreditMemoDetail customerCreditMemoDetail = (CustomerCreditMemoDetail)glpeSourceDetail;
608 boolean isDebit = true;
609
610 CustomerInvoiceGLPEService service = SpringContext.getBean(CustomerInvoiceGLPEService.class);
611 service.createAndAddGenericInvoiceRelatedGLPEs(this, customerCreditMemoDetail, sequenceHelper, isDebit, hasClaimOnCashOffset, customerCreditMemoDetail.getCreditMemoItemTotalAmount());
612 }
613
614 /**
615 * This method add pending entries for every tax detail that exists for a particular postal code
616 *
617 * @param sequenceHelper
618 * @param glpeSourceDetail
619 * @param hasClaimOnCashOffset
620 */
621 protected void addSalesTaxGLPEs(GeneralLedgerPendingEntrySequenceHelper sequenceHelper, GeneralLedgerPendingEntrySourceDetail glpeSourceDetail, boolean hasClaimOnCashOffset){
622
623 CustomerCreditMemoDetail customerCreditMemoDetail = (CustomerCreditMemoDetail)glpeSourceDetail;
624 boolean isDebit = false;
625
626 String postalCode = getPostalCode();
627 Date dateOfTransaction = getInvoice().getBillingDate();
628
629 List<TaxDetail> salesTaxDetails = getTaxService().getSalesTaxDetails(dateOfTransaction, postalCode, customerCreditMemoDetail.getCreditMemoItemTotalAmount());
630
631 CustomerInvoiceGLPEService service = SpringContext.getBean(CustomerInvoiceGLPEService.class);
632 SalesTaxCustomerCreditMemoDetail salesTaxCustomerCreditMemoDetail;
633 ReceivableCustomerCreditMemoDetail receivableCustomerCreditMemoDetail;
634 for( TaxDetail salesTaxDetail : salesTaxDetails ){
635
636 salesTaxCustomerCreditMemoDetail = new SalesTaxCustomerCreditMemoDetail( salesTaxDetail, customerCreditMemoDetail );
637 salesTaxCustomerCreditMemoDetail.setCustomerInvoiceDetail(customerCreditMemoDetail.getCustomerInvoiceDetail());
638 receivableCustomerCreditMemoDetail = new ReceivableCustomerCreditMemoDetail(salesTaxCustomerCreditMemoDetail, this);
639 receivableCustomerCreditMemoDetail.setCustomerInvoiceDetail(customerCreditMemoDetail.getCustomerInvoiceDetail());
640
641 sequenceHelper.increment();
642 service.createAndAddGenericInvoiceRelatedGLPEs(this, receivableCustomerCreditMemoDetail, sequenceHelper, isDebit, hasClaimOnCashOffset, salesTaxDetail.getTaxAmount());
643
644 sequenceHelper.increment();
645 service.createAndAddGenericInvoiceRelatedGLPEs(this, salesTaxCustomerCreditMemoDetail, sequenceHelper, !isDebit, hasClaimOnCashOffset, salesTaxDetail.getTaxAmount());
646 }
647 }
648
649 public KualiDecimal getTotalDollarAmount() {
650 return getDocumentHeader().getFinancialDocumentTotalAmount();
651 }
652
653
654 public AccountsReceivableDocumentHeader getAccountsReceivableDocumentHeader() {
655 return accountsReceivableDocumentHeader;
656 }
657
658 public void setAccountsReceivableDocumentHeader(AccountsReceivableDocumentHeader accountsReceivableDocumentHeader) {
659 this.accountsReceivableDocumentHeader = accountsReceivableDocumentHeader;
660 }
661
662 public String getPostalCode() {
663 String postalCode = getArTaxService().getPostalCodeForTaxation(getInvoice());
664 return postalCode;
665 }
666
667 /**
668 * Gets the taxService attribute.
669 * @return Returns the taxService.
670 */
671 public TaxService getTaxService() {
672 // lazy init the service if its been nullified by session-izing the document
673 if (taxService == null) taxService = SpringContext.getBean(TaxService.class);
674 return taxService;
675 }
676
677 /**
678 * Gets the arTaxService attribute.
679 * @return Returns the arTaxService.
680 */
681 public AccountsReceivableTaxService getArTaxService() {
682 // lazy init the service if its been nullified by session-izing the document
683 if (arTaxService == null) arTaxService = SpringContext.getBean(AccountsReceivableTaxService.class);
684 return arTaxService;
685 }
686
687 }