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    
017    package org.kuali.kfs.module.purap.document;
018    
019    import java.sql.Date;
020    import java.sql.Timestamp;
021    import java.util.ArrayList;
022    import java.util.List;
023    
024    import org.apache.commons.lang.StringUtils;
025    import org.kuali.kfs.module.purap.PurapConstants;
026    import org.kuali.kfs.module.purap.PurapParameterConstants;
027    import org.kuali.kfs.module.purap.PurapPropertyConstants;
028    import org.kuali.kfs.module.purap.PurapWorkflowConstants;
029    import org.kuali.kfs.module.purap.PurapConstants.CREDIT_MEMO_TYPE_LABELS;
030    import org.kuali.kfs.module.purap.PurapConstants.CreditMemoStatuses;
031    import org.kuali.kfs.module.purap.PurapConstants.PurapDocTypeCodes;
032    import org.kuali.kfs.module.purap.PurapWorkflowConstants.NodeDetails;
033    import org.kuali.kfs.module.purap.PurapWorkflowConstants.CreditMemoDocument.NodeDetailEnum;
034    import org.kuali.kfs.module.purap.businessobject.CreditMemoItem;
035    import org.kuali.kfs.module.purap.businessobject.CreditMemoItemUseTax;
036    import org.kuali.kfs.module.purap.document.service.AccountsPayableDocumentSpecificService;
037    import org.kuali.kfs.module.purap.document.service.AccountsPayableService;
038    import org.kuali.kfs.module.purap.document.service.CreditMemoService;
039    import org.kuali.kfs.module.purap.document.service.PaymentRequestService;
040    import org.kuali.kfs.module.purap.document.service.PurapService;
041    import org.kuali.kfs.module.purap.document.validation.event.AttributedContinuePurapEvent;
042    import org.kuali.kfs.module.purap.service.PurapGeneralLedgerService;
043    import org.kuali.kfs.sys.KFSConstants;
044    import org.kuali.kfs.sys.businessobject.AccountingLine;
045    import org.kuali.kfs.sys.businessobject.GeneralLedgerPendingEntry;
046    import org.kuali.kfs.sys.businessobject.GeneralLedgerPendingEntrySourceDetail;
047    import org.kuali.kfs.sys.context.SpringContext;
048    import org.kuali.rice.kew.dto.DocumentRouteStatusChangeDTO;
049    import org.kuali.rice.kew.exception.WorkflowException;
050    import org.kuali.rice.kim.bo.Person;
051    import org.kuali.rice.kns.bo.Note;
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.NoteService;
056    import org.kuali.rice.kns.service.ParameterService;
057    import org.kuali.rice.kns.util.GlobalVariables;
058    import org.kuali.rice.kns.util.KualiDecimal;
059    import org.kuali.rice.kns.util.ObjectUtils;
060    import org.kuali.rice.kns.workflow.service.WorkflowDocumentService;
061    
062    /**
063     * Credit Memo Document Business Object. Contains the fields associated with the main document table.
064     */
065    public class VendorCreditMemoDocument extends AccountsPayableDocumentBase {
066        protected static org.apache.log4j.Logger LOG = org.apache.log4j.Logger.getLogger(VendorCreditMemoDocument.class);
067    
068        protected Integer paymentRequestIdentifier;
069        protected String creditMemoNumber;
070        protected Date creditMemoDate;
071        protected KualiDecimal creditMemoAmount;
072        protected Timestamp creditMemoPaidTimestamp;
073        protected String itemMiscellaneousCreditDescription;
074        protected Date purchaseOrderEndDate;
075        protected String vendorAttentionName;
076        
077        protected PaymentRequestDocument paymentRequestDocument;
078    
079        /**
080         * Default constructor.
081         */
082        public VendorCreditMemoDocument() {
083            super();
084        }
085    
086        public boolean isSourceDocumentPaymentRequest() {
087            return getPaymentRequestIdentifier() != null;
088        }
089    
090        public boolean isSourceDocumentPurchaseOrder() {
091            return (!isSourceDocumentPaymentRequest()) && (getPurchaseOrderIdentifier() != null);
092        }
093    
094        public boolean isSourceVendor() {
095            return (!isSourceDocumentPaymentRequest()) && (!isSourceDocumentPurchaseOrder());
096        }
097    
098        /**
099         * Overrides the method in PurchasingAccountsPayableDocumentBase to add the criteria
100         * specific to Credit Memo Document.
101         * 
102         * @see org.kuali.kfs.module.purap.document.PurchasingAccountsPayableDocumentBase#isInquiryRendered()
103         */
104        @Override
105        public boolean isInquiryRendered() {
106            if ( isPostingYearPrior() && 
107                 ( getStatusCode().equals(PurapConstants.CreditMemoStatuses.COMPLETE) ||
108                   getStatusCode().equals(PurapConstants.PaymentRequestStatuses.CANCELLED_POST_AP_APPROVE) ||
109                   getStatusCode().equals(PurapConstants.PaymentRequestStatuses.CANCELLED_IN_PROCESS) ) )  {
110                   return false;            
111            }
112            else {
113                return true;
114            }
115        }
116        
117        /**
118         * Initializes the values for a new document.
119         */
120        public void initiateDocument() {
121            LOG.debug("initiateDocument() started");
122            setStatusCode(PurapConstants.CreditMemoStatuses.INITIATE);
123    
124            Person currentUser = (Person) GlobalVariables.getUserSession().getPerson();
125            setAccountsPayableProcessorIdentifier(currentUser.getPrincipalId());
126            setProcessingCampusCode(currentUser.getCampusCode());
127        }
128    
129        /**
130         * Clear out the initially populated fields.
131         */
132        public void clearInitFields() {
133            LOG.debug("clearDocument() started");
134    
135            // Clearing document overview fields
136            getDocumentHeader().setDocumentDescription(null);
137            getDocumentHeader().setExplanation(null);
138            getDocumentHeader().setFinancialDocumentTotalAmount(null);
139            getDocumentHeader().setOrganizationDocumentNumber(null);
140    
141            // Clearing document Init fields
142            setPurchaseOrderIdentifier(null);
143            setCreditMemoNumber(null);
144            setCreditMemoDate(null);
145            setCreditMemoAmount(null);
146            setVendorNumber(null);
147            setPaymentRequestIdentifier(null);
148        }
149    
150        /**
151         * Returns the type of the Credit Memo that was selected on the init screen. It is based on them entering the Vendor, PO or PREQ #.
152         * 
153         * @return Vendor, PO or PREQ
154         */
155        public String getCreditMemoType() {
156            String type = CREDIT_MEMO_TYPE_LABELS.TYPE_VENDOR;
157            if (isSourceDocumentPaymentRequest()) {
158                type = CREDIT_MEMO_TYPE_LABELS.TYPE_PREQ;
159            }
160            else if (isSourceDocumentPurchaseOrder()) {
161                type = CREDIT_MEMO_TYPE_LABELS.TYPE_PO;
162            }
163            return type;
164        }
165    
166        /**
167         * @see org.kuali.rice.kns.bo.PersistableBusinessObjectBase#isBoNotesSupport()
168         */
169        @Override
170        public boolean isBoNotesSupport() {
171            return true;
172        }
173    
174        /**
175         * Determines if the purchase order has notes, using the note service.
176         * 
177         * @return - true if po has notes, false if po does not have notes
178         */
179        public boolean getPurchaseOrderNotes() {
180            boolean hasNotes = false;
181    
182            ArrayList poNotes = SpringContext.getBean(NoteService.class).getByRemoteObjectId((this.getPurchaseOrderIdentifier()).toString());
183            if (poNotes.size() > 0) {
184                hasNotes = true;
185            }
186    
187            return hasNotes;
188        }
189    
190        /**
191         * Determines the indicator text that will appear in the workflow document title
192         * 
193         * @return - Text of hold
194         */
195        protected String getTitleIndicator() {
196            if (isHoldIndicator()) {
197                return PurapConstants.PaymentRequestIndicatorText.HOLD;
198            }
199            else return "";
200        }
201        
202        /**
203         * @see org.kuali.rice.kns.document.DocumentBase#doRouteStatusChange()
204         */
205        @Override
206        public void doRouteStatusChange(DocumentRouteStatusChangeDTO statusChangeEvent) {
207            LOG.debug("doRouteStatusChange() started");
208            super.doRouteStatusChange(statusChangeEvent);
209            try {
210                // DOCUMENT PROCESSED
211                if (this.getDocumentHeader().getWorkflowDocument().stateIsProcessed()) {
212                    SpringContext.getBean(PurapService.class).updateStatus(this, PurapConstants.CreditMemoStatuses.COMPLETE);
213                    SpringContext.getBean(PurapService.class).saveDocumentNoValidation(this);
214    
215                    return;
216                }
217                // DOCUMENT DISAPPROVED
218                else if (this.getDocumentHeader().getWorkflowDocument().stateIsDisapproved()) {
219                    String nodeName = SpringContext.getBean(WorkflowDocumentService.class).getCurrentRouteLevelName(getDocumentHeader().getWorkflowDocument());
220                    NodeDetails currentNode = NodeDetailEnum.getNodeDetailEnumByName(nodeName);
221                    if (ObjectUtils.isNotNull(currentNode)) {
222                        String newStatusCode = currentNode.getDisapprovedStatusCode();
223                        if ((StringUtils.isBlank(newStatusCode)) && ((StringUtils.isBlank(currentNode.getDisapprovedStatusCode())) && ((CreditMemoStatuses.INITIATE.equals(getStatusCode())) || (CreditMemoStatuses.IN_PROCESS.equals(getStatusCode()))))) {
224                            newStatusCode = CreditMemoStatuses.CANCELLED_IN_PROCESS;
225                        }
226                        if (StringUtils.isNotBlank(newStatusCode)) {
227                            SpringContext.getBean(AccountsPayableService.class).cancelAccountsPayableDocument(this, nodeName);
228                            return;
229                        }
230                    }
231                    logAndThrowRuntimeException("No status found to set for document being disapproved in node '" + nodeName + "'");
232                }
233                // DOCUMENT CANCELED
234                else if (this.getDocumentHeader().getWorkflowDocument().stateIsCanceled()) {
235                    String currentNodeName = SpringContext.getBean(WorkflowDocumentService.class).getCurrentRouteLevelName(getDocumentHeader().getWorkflowDocument());
236                    SpringContext.getBean(AccountsPayableService.class).cancelAccountsPayableDocument(this, currentNodeName);
237                }
238            }
239            catch (WorkflowException e) {
240                logAndThrowRuntimeException("Error saving routing data while saving document with id " + getDocumentNumber(), e);
241            }
242        }
243    
244        /**
245         * Hook point for performing actions that occur after a route level change, in this case; Performs logic necessary after full
246         * entry has been completed when past Adhoc Review, or sets the AP approval date when past AP review.
247         * 
248         * @see org.kuali.kfs.module.purap.document.AccountsPayableDocumentBase#preProcessNodeChange(java.lang.String, java.lang.String)
249         */
250        public boolean processNodeChange(String newNodeName, String oldNodeName) {
251            if (NodeDetailEnum.ADHOC_REVIEW.getName().equals(oldNodeName)) {
252                SpringContext.getBean(AccountsPayableService.class).performLogicForFullEntryCompleted(this);
253            }
254    
255            // if we've hit Account node then reopen po
256            else if ("Account".equals(newNodeName) && this.isReopenPurchaseOrderIndicator()) {
257                SpringContext.getBean(PurapService.class).performLogicForCloseReopenPO(this);
258            }        
259            return true;
260        }
261    
262        /**
263         * @see org.kuali.rice.kns.document.DocumentBase#getDocumentTitle()
264         */
265        @Override
266        public String getDocumentTitle() {
267            if (SpringContext.getBean(ParameterService.class).getIndicatorParameter(VendorCreditMemoDocument.class, PurapParameterConstants.PURAP_OVERRIDE_CM_DOC_TITLE)) {
268                return getCustomDocumentTitle();
269            }
270            return super.getDocumentTitle();
271        }
272    
273        /**
274         * Returns a custom document title based on the workflow document title. 
275         * Depending on the document status, the PO, vendor, amount, etc may be added to the documents title.
276         * 
277         * @return - Customized document title text dependent upon route level.
278         */
279        protected String getCustomDocumentTitle() {
280            String popreq = "";
281            if (this.isSourceDocumentPurchaseOrder() || this.isSourceDocumentPaymentRequest()) {
282                String poNumber = getPurchaseOrderIdentifier().toString();
283                popreq = new StringBuffer("PO: ").append(poNumber).toString();
284            }
285            /*
286            else if (this.isSourceDocumentPaymentRequest()) {
287                String preqNumber = this.getPaymentRequestIdentifier().toString();
288                popreq = new StringBuffer("PREQ: ").append(preqNumber).toString();
289            }
290            */
291    
292            String vendorName = StringUtils.trimToEmpty(getVendorName());
293            String cmAmount = getGrandTotal().toString();
294            String indicator = getTitleIndicator();
295            String documentTitle = new StringBuffer(popreq).append(" Vendor: ").append(vendorName).append(" Amount: ").append(cmAmount).append(" ").append(indicator).toString();
296            return documentTitle;
297        }
298        
299        /**
300         * @see org.kuali.kfs.module.purap.document.AccountsPayableDocumentBase#getNodeDetailEnum(java.lang.String)
301         */
302        public NodeDetails getNodeDetailEnum(String nodeName) {
303            return NodeDetailEnum.getNodeDetailEnumByName(nodeName);
304        }
305    
306        /**
307         * @see org.kuali.kfs.module.purap.document.AccountsPayableDocumentBase#saveDocumentFromPostProcessing()
308         */
309        public void saveDocumentFromPostProcessing() {
310            SpringContext.getBean(PurapService.class).saveDocumentNoValidation(this);
311        }
312    
313        /**
314         * @see org.kuali.kfs.module.purap.document.PurchasingAccountsPayableDocumentBase#getItemClass()
315         */
316        @Override
317        public Class<CreditMemoItem> getItemClass() {
318            return CreditMemoItem.class;
319        }
320    
321        @Override
322        public Class getItemUseTaxClass() {
323            return CreditMemoItemUseTax.class;
324        }
325    
326        /**
327         * @see org.kuali.kfs.module.purap.document.PurchasingAccountsPayableDocumentBase#getPurApSourceDocumentIfPossible()
328         */
329        @Override
330        public PurchasingAccountsPayableDocument getPurApSourceDocumentIfPossible() {
331            PurchasingAccountsPayableDocument sourceDocument = null;
332            if (isSourceDocumentPaymentRequest()) {
333                sourceDocument = getPaymentRequestDocument();
334            }
335            else if (isSourceDocumentPurchaseOrder()) {
336                sourceDocument = getPurchaseOrderDocument();
337            }
338            return sourceDocument;
339        }
340    
341        /**
342         * @see org.kuali.kfs.module.purap.document.PurchasingAccountsPayableDocumentBase#getPurApSourceDocumentLabelIfPossible()
343         */
344        @Override
345        public String getPurApSourceDocumentLabelIfPossible() {
346            PurchasingAccountsPayableDocument document = getPurApSourceDocumentIfPossible();
347            if (ObjectUtils.isNotNull(document)) {
348                return SpringContext.getBean(DataDictionaryService.class).getDocumentLabelByClass(document.getClass());
349            }
350            return null;
351        }
352    
353        /**
354         * Calculates the pretax total of the above the line items
355         * 
356         * @return KualiDecimal - above the line item pretax total
357         */
358        public KualiDecimal getLineItemPreTaxTotal() {
359            KualiDecimal lineItemPreTaxTotal = KualiDecimal.ZERO;
360    
361            for (CreditMemoItem item : (List<CreditMemoItem>) getItems()) {
362                item.refreshReferenceObject(PurapPropertyConstants.ITEM_TYPE);
363                if (item.getItemType().isLineItemIndicator() && item.getExtendedPrice() != null) {
364                    lineItemPreTaxTotal = lineItemPreTaxTotal.add(item.getExtendedPrice());
365                }
366            }
367    
368            return lineItemPreTaxTotal;
369        }
370    
371        /**
372         * Calculates the total of the above the line items
373         * 
374         * @return KualiDecimal - above the line item total
375         */
376        public KualiDecimal getLineItemTotal() {
377            KualiDecimal lineItemTotal = KualiDecimal.ZERO;
378    
379            for (CreditMemoItem item : (List<CreditMemoItem>) getItems()) {
380                item.refreshReferenceObject(PurapPropertyConstants.ITEM_TYPE);
381                if (item.getItemType().isLineItemIndicator() && item.getTotalAmount() != null) {
382                    lineItemTotal = lineItemTotal.add(item.getTotalAmount());
383                }
384            }
385    
386            return lineItemTotal;
387        }
388    
389        /**
390         * Calculates the credit memo total: Sum of above the line - restocking fees + misc amount
391         * 
392         * @return KualiDecimal - credit memo document total
393         */
394        public KualiDecimal getGrandTotal() {
395            KualiDecimal grandTotal = KualiDecimal.ZERO;
396    
397            for (CreditMemoItem item : (List<CreditMemoItem>) getItems()) {
398                item.refreshReferenceObject(PurapPropertyConstants.ITEM_TYPE);
399    
400                if (item.getTotalAmount() != null) {
401                    // make sure restocking fee is negative
402                    if (StringUtils.equals(PurapConstants.ItemTypeCodes.ITEM_TYPE_RESTCK_FEE_CODE, item.getItemTypeCode())) {
403                        if( ObjectUtils.isNotNull(item.getExtendedPrice()) ){
404                            item.setExtendedPrice(item.getExtendedPrice().abs().negated());
405                        }else{
406                            item.setExtendedPrice(KualiDecimal.ZERO);
407                        }
408                    }
409                    grandTotal = grandTotal.add(item.getTotalAmount());
410                }
411            }
412    
413            return grandTotal;
414        }
415    
416        /**
417         * Calculates the credit memo pretax total: Sum of above the line - restocking fees + misc amount
418         * 
419         * @return KualiDecimal - credit memo document total
420         */
421        public KualiDecimal getGrandPreTaxTotal() {
422            KualiDecimal grandTotal = KualiDecimal.ZERO;
423    
424            for (CreditMemoItem item : (List<CreditMemoItem>) getItems()) {
425                item.refreshReferenceObject(PurapPropertyConstants.ITEM_TYPE);
426    
427                if (item.getExtendedPrice() != null) {
428                    // make sure restocking fee is negative
429                    if (StringUtils.equals(PurapConstants.ItemTypeCodes.ITEM_TYPE_RESTCK_FEE_CODE, item.getItemTypeCode())) {
430                        item.setExtendedPrice(item.getExtendedPrice().abs().negated());
431                    }
432                    grandTotal = grandTotal.add(item.getExtendedPrice());
433                }
434            }
435    
436            return grandTotal;
437        }
438    
439        /**
440         * Calculates the credit memo tax amount: Sum of above the line - 
441         * 
442         * @return KualiDecimal - credit memo document total
443         */
444        public KualiDecimal getGrandTaxAmount() {
445            KualiDecimal grandTotal = KualiDecimal.ZERO;
446    
447            for (CreditMemoItem item : (List<CreditMemoItem>) getItems()) {
448                item.refreshReferenceObject(PurapPropertyConstants.ITEM_TYPE);
449    
450                if (item.getItemTaxAmount() != null) {
451                    // make sure restocking fee is negative
452                    if (StringUtils.equals(PurapConstants.ItemTypeCodes.ITEM_TYPE_RESTCK_FEE_CODE, item.getItemTypeCode())) {
453                        item.setExtendedPrice(item.getItemTaxAmount().abs().negated());
454                    }
455                    grandTotal = grandTotal.add(item.getItemTaxAmount());
456                }
457            }
458    
459            return grandTotal;
460        }
461    
462        public KualiDecimal getGrandPreTaxTotalExcludingRestockingFee() {
463            String[] restockingFeeCode = new String[] { PurapConstants.ItemTypeCodes.ITEM_TYPE_RESTCK_FEE_CODE };
464            return this.getTotalPreTaxDollarAmountWithExclusions(restockingFeeCode, true);
465        }
466    
467        public KualiDecimal getGrandTotalExcludingRestockingFee() {
468            String[] restockingFeeCode = new String[] { PurapConstants.ItemTypeCodes.ITEM_TYPE_RESTCK_FEE_CODE };
469            return this.getTotalDollarAmountWithExclusions(restockingFeeCode, true);
470        }
471    
472        public Integer getPaymentRequestIdentifier() {
473            return paymentRequestIdentifier;
474        }
475    
476        public void setPaymentRequestIdentifier(Integer paymentRequestIdentifier) {
477            this.paymentRequestIdentifier = paymentRequestIdentifier;
478        }
479    
480        public String getCreditMemoNumber() {
481            return creditMemoNumber;
482        }
483    
484        public void setCreditMemoNumber(String creditMemoNumber) {
485            if (creditMemoNumber != null) {
486                creditMemoNumber = creditMemoNumber.toUpperCase();
487            }
488    
489            this.creditMemoNumber = creditMemoNumber;
490        }
491    
492        public Date getCreditMemoDate() {
493            return creditMemoDate;
494        }
495    
496        public void setCreditMemoDate(Date creditMemoDate) {
497            this.creditMemoDate = creditMemoDate;
498        }
499    
500        public KualiDecimal getCreditMemoAmount() {
501            return creditMemoAmount;
502        }
503    
504        public void setCreditMemoAmount(KualiDecimal creditMemoAmount) {
505            this.creditMemoAmount = creditMemoAmount;
506        }
507    
508        public String getItemMiscellaneousCreditDescription() {
509            return itemMiscellaneousCreditDescription;
510        }
511    
512        public void setItemMiscellaneousCreditDescription(String itemMiscellaneousCreditDescription) {
513            this.itemMiscellaneousCreditDescription = itemMiscellaneousCreditDescription;
514        }
515    
516        public Timestamp getCreditMemoPaidTimestamp() {
517            return creditMemoPaidTimestamp;
518        }
519    
520        public void setCreditMemoPaidTimestamp(Timestamp creditMemoPaidTimestamp) {
521            this.creditMemoPaidTimestamp = creditMemoPaidTimestamp;
522        }
523    
524        public PaymentRequestDocument getPaymentRequestDocument() {
525            if ((ObjectUtils.isNull(paymentRequestDocument)) && (ObjectUtils.isNotNull(getPaymentRequestIdentifier()))) {
526                setPaymentRequestDocument(SpringContext.getBean(PaymentRequestService.class).getPaymentRequestById(getPaymentRequestIdentifier()));
527            }
528            return this.paymentRequestDocument;
529        }
530    
531        public void setPaymentRequestDocument(PaymentRequestDocument paymentRequestDocument) {
532            if (ObjectUtils.isNull(paymentRequestDocument)) {
533                // do not blank out input, instead throw an error
534                // setPaymentRequestIdentifier(null);
535                this.paymentRequestDocument = null;
536            }
537            else {
538                setPaymentRequestIdentifier(paymentRequestDocument.getPurapDocumentIdentifier());
539                this.paymentRequestDocument = paymentRequestDocument;
540            }
541        }
542    
543        /**
544         * AS A REPLACEMENT USE getPaymentRequestDocument()
545         * 
546         * @deprecated
547         */
548        public PaymentRequestDocument getPaymentRequest() {
549            return getPaymentRequestDocument();
550        }
551    
552        /**
553         * AS A REPLACEMENT USE setPaymentRequestDocument(PaymentRequestDocument)
554         * 
555         * @deprecated
556         */
557        public void setPaymentRequest(PaymentRequestDocument paymentRequest) {
558            setPaymentRequestDocument(paymentRequest);
559        }
560    
561        /**
562         * AS A REPLACEMENT USE getPurchaseOrderDocument()
563         * 
564         * @deprecated
565         */
566        public PurchaseOrderDocument getPurchaseOrder() {
567            return getPurchaseOrderDocument();
568        }
569    
570        /**
571         * AS A REPLACEMENT USE setPurchaseOrderDocument(PurchaseOrderDocument)
572         * 
573         * @deprecated
574         */
575        public void setPurchaseOrder(PurchaseOrderDocument purchaseOrder) {
576            setPurchaseOrderDocument(purchaseOrder);
577        }
578    
579        public Date getPurchaseOrderEndDate() {
580            return purchaseOrderEndDate;
581        }
582    
583        public void setPurchaseOrderEndDate(Date purchaseOrderEndDate) {
584            this.purchaseOrderEndDate = purchaseOrderEndDate;
585        }
586    
587        /**
588         * USED FOR ROUTING ONLY
589         * 
590         * @deprecated
591         */
592        public String getStatusDescription() {
593            return "";
594        }
595    
596        /**
597         * USED FOR ROUTING ONLY
598         * 
599         * @deprecated
600         */
601        public void setStatusDescription(String statusDescription) {
602        }
603    
604        /**
605         * @see org.kuali.kfs.module.purap.document.AccountsPayableDocumentBase#getPoDocumentTypeForAccountsPayableDocumentApprove()
606         */
607        public String getPoDocumentTypeForAccountsPayableDocumentCancel() {
608            return PurapConstants.PurchaseOrderDocTypes.PURCHASE_ORDER_CLOSE_DOCUMENT;
609        }
610    
611        /**
612         * @see org.kuali.kfs.module.purap.document.AccountsPayableDocumentBase#getInitialAmount()
613         */
614        public KualiDecimal getInitialAmount() {
615            return this.getCreditMemoAmount();
616        }
617    
618        /**
619         * Credit Memo document is first populated on Continue AP Event, and then prepareForSave continues.
620         * 
621         * @see org.kuali.rice.kns.document.Document#prepareForSave(org.kuali.rice.kns.rule.event.KualiDocumentEvent)
622         */
623        @Override
624        public void prepareForSave(KualiDocumentEvent event) {
625    
626            // first populate, then call super
627            if (event instanceof AttributedContinuePurapEvent) {
628                SpringContext.getBean(CreditMemoService.class).populateDocumentAfterInit(this);
629            }
630    
631            super.prepareForSave(event);
632        }
633    
634        /**
635         * @see org.kuali.kfs.module.purap.document.AccountsPayableDocumentBase#isAttachmentRequired()
636         */
637        @Override
638        protected boolean isAttachmentRequired() {
639            return StringUtils.equalsIgnoreCase("Y", SpringContext.getBean(ParameterService.class).getParameterValue(VendorCreditMemoDocument.class, PurapParameterConstants.PURAP_CM_REQUIRE_ATTACHMENT));
640        }
641    
642        /**
643         * @see org.kuali.kfs.module.purap.document.AccountsPayableDocument#getDocumentSpecificService()
644         */
645        @Override
646        public AccountsPayableDocumentSpecificService getDocumentSpecificService() {
647            return SpringContext.getBean(CreditMemoService.class);
648        }
649    
650        /**
651         * Forces GL entries to be approved before document final approval.
652         * 
653         * @see org.kuali.module.purap.rules.PurapAccountingDocumentRuleBase#customizeExplicitGeneralLedgerPendingEntry(org.kuali.kfs.sys.document.AccountingDocument, org.kuali.kfs.sys.businessobject.AccountingLine, org.kuali.kfs.sys.businessobject.GeneralLedgerPendingEntry)
654         */
655        @Override
656        public void customizeExplicitGeneralLedgerPendingEntry(GeneralLedgerPendingEntrySourceDetail postable, GeneralLedgerPendingEntry explicitEntry) {
657            super.customizeExplicitGeneralLedgerPendingEntry(postable, explicitEntry);
658            
659            SpringContext.getBean(PurapGeneralLedgerService.class).customizeGeneralLedgerPendingEntry(this, (AccountingLine)postable, explicitEntry, getPurchaseOrderIdentifier(), getDebitCreditCodeForGLEntries(), PurapDocTypeCodes.CREDIT_MEMO_DOCUMENT, isGenerateEncumbranceEntries());
660    
661            // CMs do not wait for document final approval to post GL entries; here we are forcing them to be APPROVED
662            explicitEntry.setFinancialDocumentApprovedCode(KFSConstants.PENDING_ENTRY_APPROVED_STATUS_CODE.APPROVED);
663        }
664    
665        public Date getTransactionTaxDate() {
666            return getCreditMemoDate();
667        }
668    
669        public String getVendorAttentionName() {
670            return vendorAttentionName;
671        }
672    
673        public void setVendorAttentionName(String vendorAttentionName) {
674            this.vendorAttentionName = vendorAttentionName;
675        }
676    
677        /**
678         * Provides answers to the following splits:
679         * RequiresInvoiceAttachment
680         * @see org.kuali.kfs.sys.document.FinancialSystemTransactionalDocumentBase#answerSplitNodeQuestion(java.lang.String)
681         */
682        @Override
683        public boolean answerSplitNodeQuestion(String nodeName) throws UnsupportedOperationException {
684            if (nodeName.equals(PurapWorkflowConstants.REQUIRES_IMAGE_ATTACHMENT)) return requiresAccountsPayableReviewRouting();
685            throw new UnsupportedOperationException("Cannot answer split question for this node you call \""+nodeName+"\"");
686        }
687        
688        public String getPaidIndicatorForResult(){
689            return getCreditMemoPaidTimestamp() != null ? "Yes" : "No";
690        }
691        
692        /**
693         * Checks all documents notes for attachments.
694         * 
695         * @return - true if document does not have an image attached, false otherwise
696         */
697        public boolean documentHasNoImagesAttached() {
698            List boNotes = this.getDocumentBusinessObject().getBoNotes();
699            if (ObjectUtils.isNotNull(boNotes)) {
700                for (Object obj : boNotes) {
701                    Note note = (Note) obj;
702                    
703                    note.refreshReferenceObject("attachment");
704                    if (ObjectUtils.isNotNull(note.getAttachment()) && PurapConstants.AttachmentTypeCodes.ATTACHMENT_TYPE_CM_IMAGE.equals(note.getAttachment().getAttachmentTypeCode())) {
705                        return false;
706                    }
707                }
708            }
709            return true;
710        }
711        
712    }
713