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    }