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.math.BigDecimal;
020    import java.sql.Date;
021    import java.sql.Timestamp;
022    import java.text.SimpleDateFormat;
023    import java.util.ArrayList;
024    import java.util.Arrays;
025    import java.util.HashMap;
026    import java.util.Iterator;
027    import java.util.List;
028    
029    import org.apache.commons.lang.StringUtils;
030    import org.kuali.kfs.module.purap.PurapConstants;
031    import org.kuali.kfs.module.purap.PurapParameterConstants;
032    import org.kuali.kfs.module.purap.PurapPropertyConstants;
033    import org.kuali.kfs.module.purap.PurapWorkflowConstants;
034    import org.kuali.kfs.module.purap.PurapConstants.PaymentRequestStatuses;
035    import org.kuali.kfs.module.purap.PurapConstants.PurapDocTypeCodes;
036    import org.kuali.kfs.module.purap.PurapWorkflowConstants.NodeDetails;
037    import org.kuali.kfs.module.purap.PurapWorkflowConstants.PaymentRequestDocument.NodeDetailEnum;
038    import org.kuali.kfs.module.purap.businessobject.ItemType;
039    import org.kuali.kfs.module.purap.businessobject.PaymentRequestItem;
040    import org.kuali.kfs.module.purap.businessobject.PaymentRequestItemUseTax;
041    import org.kuali.kfs.module.purap.businessobject.PurApAccountingLine;
042    import org.kuali.kfs.module.purap.businessobject.PurApItem;
043    import org.kuali.kfs.module.purap.businessobject.PurApItemUseTax;
044    import org.kuali.kfs.module.purap.businessobject.PurchaseOrderItem;
045    import org.kuali.kfs.module.purap.businessobject.PurchaseOrderItemUseTax;
046    import org.kuali.kfs.module.purap.businessobject.PurchasingCapitalAssetItem;
047    import org.kuali.kfs.module.purap.businessobject.RecurringPaymentType;
048    import org.kuali.kfs.module.purap.document.service.AccountsPayableDocumentSpecificService;
049    import org.kuali.kfs.module.purap.document.service.AccountsPayableService;
050    import org.kuali.kfs.module.purap.document.service.PaymentRequestService;
051    import org.kuali.kfs.module.purap.document.service.PurapService;
052    import org.kuali.kfs.module.purap.document.validation.event.AttributedContinuePurapEvent;
053    import org.kuali.kfs.module.purap.service.PurapGeneralLedgerService;
054    import org.kuali.kfs.module.purap.util.ExpiredOrClosedAccountEntry;
055    import org.kuali.kfs.module.purap.util.UseTaxContainer;
056    import org.kuali.kfs.sys.KFSConstants;
057    import org.kuali.kfs.sys.businessobject.AccountingLine;
058    import org.kuali.kfs.sys.businessobject.GeneralLedgerPendingEntry;
059    import org.kuali.kfs.sys.businessobject.GeneralLedgerPendingEntrySourceDetail;
060    import org.kuali.kfs.sys.context.SpringContext;
061    import org.kuali.kfs.sys.service.UniversityDateService;
062    import org.kuali.kfs.vnd.VendorConstants;
063    import org.kuali.kfs.vnd.VendorPropertyConstants;
064    import org.kuali.kfs.vnd.businessobject.PaymentTermType;
065    import org.kuali.kfs.vnd.businessobject.PurchaseOrderCostSource;
066    import org.kuali.kfs.vnd.businessobject.ShippingPaymentTerms;
067    import org.kuali.kfs.vnd.businessobject.VendorAddress;
068    import org.kuali.kfs.vnd.businessobject.VendorDetail;
069    import org.kuali.kfs.vnd.document.service.VendorService;
070    import org.kuali.rice.kew.dto.ActionTakenEventDTO;
071    import org.kuali.rice.kew.dto.DocumentRouteStatusChangeDTO;
072    import org.kuali.rice.kew.exception.WorkflowException;
073    import org.kuali.rice.kim.bo.Person;
074    import org.kuali.rice.kns.bo.Note;
075    import org.kuali.rice.kns.rule.event.KualiDocumentEvent;
076    import org.kuali.rice.kns.service.DataDictionaryService;
077    import org.kuali.rice.kns.service.DateTimeService;
078    import org.kuali.rice.kns.service.KualiConfigurationService;
079    import org.kuali.rice.kns.service.ParameterService;
080    import org.kuali.rice.kns.util.GlobalVariables;
081    import org.kuali.rice.kns.util.KualiDecimal;
082    import org.kuali.rice.kns.util.ObjectUtils;
083    import org.kuali.rice.kns.workflow.service.KualiWorkflowDocument;
084    import org.kuali.rice.kns.workflow.service.WorkflowDocumentService;
085    
086    /**
087     * Payment Request Document Business Object. Contains the fields associated with the main document table.
088     */
089    public class PaymentRequestDocument extends AccountsPayableDocumentBase {
090        protected static org.apache.log4j.Logger LOG = org.apache.log4j.Logger.getLogger(PaymentRequestDocument.class);
091    
092        protected Date invoiceDate;
093        protected String invoiceNumber;
094        protected KualiDecimal vendorInvoiceAmount;
095        protected String vendorPaymentTermsCode;
096        protected String vendorShippingPaymentTermsCode;
097        protected Date paymentRequestPayDate;
098        protected String paymentRequestCostSourceCode;
099        protected boolean paymentRequestedCancelIndicator;
100        protected boolean paymentAttachmentIndicator;
101        protected boolean immediatePaymentIndicator;
102        protected String specialHandlingInstructionLine1Text;
103        protected String specialHandlingInstructionLine2Text;
104        protected String specialHandlingInstructionLine3Text;
105        protected Timestamp paymentPaidTimestamp;
106        protected boolean paymentRequestElectronicInvoiceIndicator;
107        protected String accountsPayableRequestCancelIdentifier;
108        protected Integer originalVendorHeaderGeneratedIdentifier;
109        protected Integer originalVendorDetailAssignedIdentifier;
110        protected Integer alternateVendorHeaderGeneratedIdentifier;
111        protected Integer alternateVendorDetailAssignedIdentifier;
112        protected String purchaseOrderNotes;
113        protected String recurringPaymentTypeCode;
114        protected boolean receivingDocumentRequiredIndicator;
115        protected boolean paymentRequestPositiveApprovalIndicator;
116    
117        // TAX EDIT AREA FIELDS
118        protected String taxClassificationCode;
119        protected String taxCountryCode;
120        protected String taxNQIId;
121        protected BigDecimal taxFederalPercent; // number is in whole form so 5% is 5.00
122        protected BigDecimal taxStatePercent; // number is in whole form so 5% is 5.00
123        protected KualiDecimal taxSpecialW4Amount;
124        protected Boolean taxGrossUpIndicator;
125        protected Boolean taxExemptTreatyIndicator;
126        protected Boolean taxForeignSourceIndicator;
127        protected Boolean taxUSAIDPerDiemIndicator;
128        protected Boolean taxOtherExemptIndicator;
129    
130        // NOT PERSISTED IN DB
131        protected String vendorShippingTitleCode;
132        protected Date purchaseOrderEndDate;
133        protected String primaryVendorName;
134    
135        // BELOW USED BY ROUTING
136        protected Integer requisitionIdentifier;
137    
138        // REFERENCE OBJECTS
139        protected PaymentTermType vendorPaymentTerms;
140        protected ShippingPaymentTerms vendorShippingPaymentTerms;
141        protected PurchaseOrderCostSource paymentRequestCostSource;
142        protected RecurringPaymentType recurringPaymentType;
143    
144        /**
145         * Default constructor.
146         */
147        public PaymentRequestDocument() {
148            super();
149        }
150    
151        /**
152         * @see org.kuali.rice.kns.bo.PersistableBusinessObjectBase#isBoNotesSupport()
153         */
154        @Override
155        public boolean isBoNotesSupport() {
156            return true;
157        }
158    
159        public Integer getPostingYearPriorOrCurrent() {
160            if (SpringContext.getBean(PaymentRequestService.class).allowBackpost(this)) {
161                // allow prior; use it
162                return SpringContext.getBean(UniversityDateService.class).getCurrentFiscalYear() - 1;
163            }
164            // don't allow prior; use CURRENT
165            return SpringContext.getBean(UniversityDateService.class).getCurrentFiscalYear();
166        }
167    
168    
169        /**
170         * Overrides the method in PurchasingAccountsPayableDocumentBase to add the criteria specific to Payment Request Document.
171         * 
172         * @see org.kuali.kfs.module.purap.document.PurchasingAccountsPayableDocumentBase#isInquiryRendered()
173         */
174        @Override
175        public boolean isInquiryRendered() {
176            if (isPostingYearPrior() && (getStatusCode().equals(PurapConstants.PaymentRequestStatuses.DEPARTMENT_APPROVED) || getStatusCode().equals(PurapConstants.PaymentRequestStatuses.AUTO_APPROVED) || getStatusCode().equals(PurapConstants.PaymentRequestStatuses.CANCELLED_POST_AP_APPROVE) || getStatusCode().equals(PurapConstants.PaymentRequestStatuses.CANCELLED_IN_PROCESS))) {
177                return false;
178            }
179            else {
180                return true;
181            }
182        }
183    
184        public Integer getRequisitionIdentifier() {
185            return getPurchaseOrderDocument().getRequisitionIdentifier();
186        }
187    
188        public void setRequisitionIdentifier(Integer requisitionIdentifier) {
189            this.requisitionIdentifier = requisitionIdentifier;
190        }
191    
192        /**
193         * @see org.kuali.kfs.module.purap.document.AccountsPayableDocumentBase#populateDocumentForRouting()
194         */
195        @Override
196        public void populateDocumentForRouting() {
197            this.setRequisitionIdentifier(getPurchaseOrderDocument().getRequisitionIdentifier());
198            super.populateDocumentForRouting();
199        }
200    
201        public Date getInvoiceDate() {
202            return invoiceDate;
203        }
204    
205        public void setInvoiceDate(Date invoiceDate) {
206            this.invoiceDate = invoiceDate;
207        }
208    
209        public String getInvoiceNumber() {
210            return invoiceNumber;
211        }
212    
213        public void setInvoiceNumber(String invoiceNumber) {
214            if (!StringUtils.isEmpty(invoiceNumber))
215                this.invoiceNumber = invoiceNumber.toUpperCase();
216            else
217                this.invoiceNumber = invoiceNumber;
218        }
219    
220        public KualiDecimal getVendorInvoiceAmount() {
221            return vendorInvoiceAmount;
222        }
223    
224        public void setVendorInvoiceAmount(KualiDecimal vendorInvoiceAmount) {
225            this.vendorInvoiceAmount = vendorInvoiceAmount;
226        }
227    
228        public String getVendorPaymentTermsCode() {
229            return vendorPaymentTermsCode;
230        }
231    
232        public void setVendorPaymentTermsCode(String vendorPaymentTermsCode) {
233            this.vendorPaymentTermsCode = vendorPaymentTermsCode;
234            refreshReferenceObject("vendorPaymentTerms");
235        }
236    
237        public PaymentTermType getVendorPaymentTerms() {
238            if (ObjectUtils.isNull(vendorPaymentTerms) || !StringUtils.equalsIgnoreCase(getVendorPaymentTermsCode(), vendorPaymentTerms.getVendorPaymentTermsCode())) {
239                refreshReferenceObject(VendorPropertyConstants.VENDOR_PAYMENT_TERMS);
240            }
241            return vendorPaymentTerms;
242        }
243    
244        public void setVendorPaymentTerms(PaymentTermType vendorPaymentTerms) {
245            this.vendorPaymentTerms = vendorPaymentTerms;
246        }
247    
248        public String getVendorShippingPaymentTermsCode() {
249            if (ObjectUtils.isNull(vendorPaymentTerms)) {
250                refreshReferenceObject(VendorPropertyConstants.VENDOR_SHIPPING_PAYMENT_TERMS);
251            }
252            return vendorShippingPaymentTermsCode;
253        }
254    
255        public void setVendorShippingPaymentTermsCode(String vendorShippingPaymentTermsCode) {
256            this.vendorShippingPaymentTermsCode = vendorShippingPaymentTermsCode;
257        }
258    
259        public Date getPaymentRequestPayDate() {
260            return paymentRequestPayDate;
261        }
262    
263        public void setPaymentRequestPayDate(Date paymentRequestPayDate) {
264            this.paymentRequestPayDate = paymentRequestPayDate;
265        }
266    
267        public String getPaymentRequestCostSourceCode() {
268            return paymentRequestCostSourceCode;
269        }
270    
271        public void setPaymentRequestCostSourceCode(String paymentRequestCostSourceCode) {
272            this.paymentRequestCostSourceCode = paymentRequestCostSourceCode;
273        }
274    
275        public boolean getPaymentRequestedCancelIndicator() {
276            return paymentRequestedCancelIndicator;
277        }
278    
279        public boolean isPaymentRequestedCancelIndicator() {
280            return paymentRequestedCancelIndicator;
281        }
282    
283        public void setPaymentRequestedCancelIndicator(boolean paymentRequestedCancelIndicator) {
284            this.paymentRequestedCancelIndicator = paymentRequestedCancelIndicator;
285        }
286    
287        public boolean getPaymentAttachmentIndicator() {
288            return paymentAttachmentIndicator;
289        }
290    
291        public void setPaymentAttachmentIndicator(boolean paymentAttachmentIndicator) {
292            this.paymentAttachmentIndicator = paymentAttachmentIndicator;
293        }
294    
295        public boolean getImmediatePaymentIndicator() {
296            return immediatePaymentIndicator;
297        }
298    
299        public void setImmediatePaymentIndicator(boolean immediatePaymentIndicator) {
300            this.immediatePaymentIndicator = immediatePaymentIndicator;
301        }
302    
303        public String getSpecialHandlingInstructionLine1Text() {
304            return specialHandlingInstructionLine1Text;
305        }
306    
307        public void setSpecialHandlingInstructionLine1Text(String specialHandlingInstructionLine1Text) {
308            this.specialHandlingInstructionLine1Text = specialHandlingInstructionLine1Text;
309        }
310    
311        public String getSpecialHandlingInstructionLine2Text() {
312            return specialHandlingInstructionLine2Text;
313        }
314    
315        public void setSpecialHandlingInstructionLine2Text(String specialHandlingInstructionLine2Text) {
316            this.specialHandlingInstructionLine2Text = specialHandlingInstructionLine2Text;
317        }
318    
319        public String getSpecialHandlingInstructionLine3Text() {
320            return specialHandlingInstructionLine3Text;
321        }
322    
323        public void setSpecialHandlingInstructionLine3Text(String specialHandlingInstructionLine3Text) {
324            this.specialHandlingInstructionLine3Text = specialHandlingInstructionLine3Text;
325        }
326    
327        public Timestamp getPaymentPaidTimestamp() {
328            return paymentPaidTimestamp;
329        }
330    
331        public void setPaymentPaidTimestamp(Timestamp paymentPaidTimestamp) {
332            this.paymentPaidTimestamp = paymentPaidTimestamp;
333        }
334    
335        public boolean getPaymentRequestElectronicInvoiceIndicator() {
336            return paymentRequestElectronicInvoiceIndicator;
337        }
338    
339        public void setPaymentRequestElectronicInvoiceIndicator(boolean paymentRequestElectronicInvoiceIndicator) {
340            this.paymentRequestElectronicInvoiceIndicator = paymentRequestElectronicInvoiceIndicator;
341        }
342    
343        public String getAccountsPayableRequestCancelIdentifier() {
344            return accountsPayableRequestCancelIdentifier;
345        }
346    
347        public void setAccountsPayableRequestCancelIdentifier(String accountsPayableRequestCancelIdentifier) {
348            this.accountsPayableRequestCancelIdentifier = accountsPayableRequestCancelIdentifier;
349        }
350    
351        public Integer getOriginalVendorHeaderGeneratedIdentifier() {
352            return originalVendorHeaderGeneratedIdentifier;
353        }
354    
355        public void setOriginalVendorHeaderGeneratedIdentifier(Integer originalVendorHeaderGeneratedIdentifier) {
356            this.originalVendorHeaderGeneratedIdentifier = originalVendorHeaderGeneratedIdentifier;
357        }
358    
359        public Integer getOriginalVendorDetailAssignedIdentifier() {
360            return originalVendorDetailAssignedIdentifier;
361        }
362    
363        public void setOriginalVendorDetailAssignedIdentifier(Integer originalVendorDetailAssignedIdentifier) {
364            this.originalVendorDetailAssignedIdentifier = originalVendorDetailAssignedIdentifier;
365        }
366    
367        public Integer getAlternateVendorHeaderGeneratedIdentifier() {
368            return alternateVendorHeaderGeneratedIdentifier;
369        }
370    
371        public void setAlternateVendorHeaderGeneratedIdentifier(Integer alternateVendorHeaderGeneratedIdentifier) {
372            this.alternateVendorHeaderGeneratedIdentifier = alternateVendorHeaderGeneratedIdentifier;
373        }
374    
375        public Integer getAlternateVendorDetailAssignedIdentifier() {
376            return alternateVendorDetailAssignedIdentifier;
377        }
378    
379        public void setAlternateVendorDetailAssignedIdentifier(Integer alternateVendorDetailAssignedIdentifier) {
380            this.alternateVendorDetailAssignedIdentifier = alternateVendorDetailAssignedIdentifier;
381        }
382    
383        public ShippingPaymentTerms getVendorShippingPaymentTerms() {
384            return vendorShippingPaymentTerms;
385        }
386    
387        public void setVendorShippingPaymentTerms(ShippingPaymentTerms vendorShippingPaymentTerms) {
388            this.vendorShippingPaymentTerms = vendorShippingPaymentTerms;
389        }
390    
391        public String getVendorShippingTitleCode() {
392            if (ObjectUtils.isNotNull(this.getPurchaseOrderDocument())) {
393                return this.getPurchaseOrderDocument().getVendorShippingTitleCode();
394            }
395            return vendorShippingTitleCode;
396        }
397    
398        public void setVendorShippingTitleCode(String vendorShippingTitleCode) {
399            this.vendorShippingTitleCode = vendorShippingTitleCode;
400        }
401    
402        public Date getPurchaseOrderEndDate() {
403            return purchaseOrderEndDate;
404        }
405    
406        public void setPurchaseOrderEndDate(Date purchaseOrderEndDate) {
407            this.purchaseOrderEndDate = purchaseOrderEndDate;
408        }
409    
410        /**
411         * Gets the paymentRequestPositiveApprovalIndicator attribute.
412         * 
413         * @return Returns the paymentRequestPositiveApprovalIndicator.
414         */
415        public boolean isPaymentRequestPositiveApprovalIndicator() {
416            return paymentRequestPositiveApprovalIndicator;
417        }
418    
419        /**
420         * Sets the paymentRequestPositiveApprovalIndicator attribute value.
421         * 
422         * @param paymentRequestPositiveApprovalIndicator The paymentRequestPositiveApprovalIndicator to set.
423         */
424        public void setPaymentRequestPositiveApprovalIndicator(boolean paymentRequestPositiveApprovalIndicator) {
425            this.paymentRequestPositiveApprovalIndicator = paymentRequestPositiveApprovalIndicator;
426        }
427    
428        /**
429         * Gets the receivingDocumentRequiredIndicator attribute.
430         * 
431         * @return Returns the receivingDocumentRequiredIndicator.
432         */
433        public boolean isReceivingDocumentRequiredIndicator() {
434            return receivingDocumentRequiredIndicator;
435        }
436    
437        /**
438         * Sets the receivingDocumentRequiredIndicator attribute value.
439         * 
440         * @param receivingDocumentRequiredIndicator The receivingDocumentRequiredIndicator to set.
441         */
442        public void setReceivingDocumentRequiredIndicator(boolean receivingDocumentRequiredIndicator) {
443            this.receivingDocumentRequiredIndicator = receivingDocumentRequiredIndicator;
444        }
445    
446        /**
447         * Perform logic needed to initiate PREQ Document
448         */
449        public void initiateDocument() {
450            LOG.debug("initiateDocument() started");
451            Person currentUser = (Person) GlobalVariables.getUserSession().getPerson();
452            this.setStatusCode(PurapConstants.PaymentRequestStatuses.INITIATE);
453            this.setAccountsPayableProcessorIdentifier(currentUser.getPrincipalId());
454            this.setProcessingCampusCode(currentUser.getCampusCode());
455            this.refreshNonUpdateableReferences();
456        }
457    
458        /**
459         * Perform logic needed to clear the initial fields on a PREQ Document
460         */
461        public void clearInitFields() {
462            LOG.debug("clearDocument() started");
463            // Clearing document overview fields
464            this.getDocumentHeader().setDocumentDescription(null);
465            this.getDocumentHeader().setExplanation(null);
466            this.getDocumentHeader().setFinancialDocumentTotalAmount(null);
467            this.getDocumentHeader().setOrganizationDocumentNumber(null);
468    
469            // Clearing document Init fields
470            this.setPurchaseOrderIdentifier(null);
471            this.setInvoiceNumber(null);
472            this.setInvoiceDate(null);
473            this.setVendorInvoiceAmount(null);
474            this.setSpecialHandlingInstructionLine1Text(null);
475            this.setSpecialHandlingInstructionLine2Text(null);
476            this.setSpecialHandlingInstructionLine3Text(null);
477        }
478    
479        /**
480         * Populates a preq from a PO - delegate method
481         * 
482         * @param po -
483         */
484        public void populatePaymentRequestFromPurchaseOrder(PurchaseOrderDocument po) {
485            populatePaymentRequestFromPurchaseOrder(po, new HashMap<String, ExpiredOrClosedAccountEntry>());
486        }
487    
488    
489        /**
490         * Populates a preq from a PO
491         * 
492         * @param po Purchase Order Document used for populating the PREQ
493         * @param expiredOrClosedAccountList a list of closed or expired accounts
494         */
495        public void populatePaymentRequestFromPurchaseOrder(PurchaseOrderDocument po, HashMap<String, ExpiredOrClosedAccountEntry> expiredOrClosedAccountList) {
496            this.setPurchaseOrderIdentifier(po.getPurapDocumentIdentifier());
497            this.getDocumentHeader().setOrganizationDocumentNumber(po.getDocumentHeader().getOrganizationDocumentNumber());
498            this.setPostingYear(po.getPostingYear());
499            this.setReceivingDocumentRequiredIndicator(po.isReceivingDocumentRequiredIndicator());
500            this.setUseTaxIndicator(po.isUseTaxIndicator());
501            this.setPaymentRequestPositiveApprovalIndicator(po.isPaymentRequestPositiveApprovalIndicator());
502            this.setVendorCustomerNumber(po.getVendorCustomerNumber());
503    
504            if (po.getPurchaseOrderCostSource() != null) {
505                this.setPaymentRequestCostSource(po.getPurchaseOrderCostSource());
506                this.setPaymentRequestCostSourceCode(po.getPurchaseOrderCostSourceCode());
507            }
508    
509            if (po.getVendorShippingPaymentTerms() != null) {
510                this.setVendorShippingPaymentTerms(po.getVendorShippingPaymentTerms());
511                this.setVendorShippingPaymentTermsCode(po.getVendorShippingPaymentTermsCode());
512            }
513    
514            if (po.getVendorPaymentTerms() != null) {
515                this.setVendorPaymentTermsCode(po.getVendorPaymentTermsCode());
516                this.setVendorPaymentTerms(po.getVendorPaymentTerms());
517            }
518    
519            if (po.getRecurringPaymentType() != null) {
520                this.setRecurringPaymentType(po.getRecurringPaymentType());
521                this.setRecurringPaymentTypeCode(po.getRecurringPaymentTypeCode());
522            }
523    
524            this.setVendorHeaderGeneratedIdentifier(po.getVendorHeaderGeneratedIdentifier());
525            this.setVendorDetailAssignedIdentifier(po.getVendorDetailAssignedIdentifier());
526            this.setVendorCustomerNumber(po.getVendorCustomerNumber());
527            this.setVendorName(po.getVendorName());
528    
529            // set original vendor
530            this.setOriginalVendorHeaderGeneratedIdentifier(po.getVendorHeaderGeneratedIdentifier());
531            this.setOriginalVendorDetailAssignedIdentifier(po.getVendorDetailAssignedIdentifier());
532    
533            // set alternate vendor info as well
534            this.setAlternateVendorHeaderGeneratedIdentifier(po.getAlternateVendorHeaderGeneratedIdentifier());
535            this.setAlternateVendorDetailAssignedIdentifier(po.getAlternateVendorDetailAssignedIdentifier());
536    
537            // populate preq vendor address with the default remit address type for the vendor if found
538            String userCampus = GlobalVariables.getUserSession().getPerson().getCampusCode();
539            VendorAddress vendorAddress = SpringContext.getBean(VendorService.class).getVendorDefaultAddress(po.getVendorHeaderGeneratedIdentifier(), po.getVendorDetailAssignedIdentifier(), VendorConstants.AddressTypes.REMIT, userCampus);
540            if (vendorAddress != null) {
541                this.templateVendorAddress(vendorAddress);
542                this.setVendorAddressGeneratedIdentifier(vendorAddress.getVendorAddressGeneratedIdentifier());
543                setVendorAttentionName(StringUtils.defaultString(vendorAddress.getVendorAttentionName()));
544            }
545            else {
546                // set address from PO
547                this.setVendorAddressGeneratedIdentifier(po.getVendorAddressGeneratedIdentifier());
548                this.setVendorLine1Address(po.getVendorLine1Address());
549                this.setVendorLine2Address(po.getVendorLine2Address());
550                this.setVendorCityName(po.getVendorCityName());
551                this.setVendorAddressInternationalProvinceName(po.getVendorAddressInternationalProvinceName());
552                this.setVendorStateCode(po.getVendorStateCode());
553                this.setVendorPostalCode(po.getVendorPostalCode());
554                this.setVendorCountryCode(po.getVendorCountryCode());
555    
556                boolean blankAttentionLine = StringUtils.equalsIgnoreCase("Y", SpringContext.getBean(KualiConfigurationService.class).getParameterValue(PurapConstants.PURAP_NAMESPACE, "Document", PurapParameterConstants.BLANK_ATTENTION_LINE_FOR_PO_TYPE_ADDRESS));
557    
558                if (blankAttentionLine) {
559                    setVendorAttentionName(StringUtils.EMPTY);
560                }
561                else {
562                    setVendorAttentionName(StringUtils.defaultString(po.getVendorAttentionName()));
563                }
564            }
565    
566            this.setPaymentRequestPayDate(SpringContext.getBean(PaymentRequestService.class).calculatePayDate(this.getInvoiceDate(), this.getVendorPaymentTerms()));
567    
568            AccountsPayableService accountsPayableService = SpringContext.getBean(AccountsPayableService.class);
569            
570            if(SpringContext.getBean(PaymentRequestService.class).encumberedItemExistsForInvoicing(po))
571            {
572                for (PurchaseOrderItem poi : (List<PurchaseOrderItem>) po.getItems()) {
573                    // check to make sure it's eligible for payment (i.e. active and has encumbrance available
574                    if (getDocumentSpecificService().poItemEligibleForAp(this, poi)) {
575                        PaymentRequestItem paymentRequestItem = new PaymentRequestItem(poi, this, expiredOrClosedAccountList);
576                        this.getItems().add(paymentRequestItem);
577                        PurchasingCapitalAssetItem purchasingCAMSItem = po.getPurchasingCapitalAssetItemByItemIdentifier(poi.getItemIdentifier());
578                        if (purchasingCAMSItem != null) {
579                            paymentRequestItem.setCapitalAssetTransactionTypeCode(purchasingCAMSItem.getCapitalAssetTransactionTypeCode());
580                        }
581                        
582                        /*
583                        // copy usetaxitems over
584                        paymentRequestItem.getUseTaxItems().clear();
585                        for (PurApItemUseTax useTax : poi.getUseTaxItems()) {
586                            paymentRequestItem.getUseTaxItems().add(useTax);
587                        }
588                        */
589                    }
590                }
591            }
592       
593            // add missing below the line
594            SpringContext.getBean(PurapService.class).addBelowLineItems(this);
595            this.setAccountsPayablePurchasingDocumentLinkIdentifier(po.getAccountsPayablePurchasingDocumentLinkIdentifier());
596    
597            //fix up below the line items
598            SpringContext.getBean(PaymentRequestService.class).removeIneligibleAdditionalCharges(this);
599            
600            this.fixItemReferences();
601            this.refreshNonUpdateableReferences();
602        }
603    
604        /**
605         * @see org.kuali.rice.kns.document.DocumentBase#getDocumentTitle()
606         */
607        @Override
608        public String getDocumentTitle() {
609           if (SpringContext.getBean(ParameterService.class).getIndicatorParameter(PaymentRequestDocument.class, PurapParameterConstants.PURAP_OVERRIDE_PREQ_DOC_TITLE)) {
610                return getCustomDocumentTitle();
611            }
612            return this.buildDocumentTitle(super.getDocumentTitle());
613        }
614    
615        /**
616         * Returns a custom document title based on the workflow document title. Depending on what route level the document is currently
617         * in, the PO, vendor, amount, account number, dept, campus may be added to the documents title.
618         * 
619         * @return - Customized document title text dependent upon route level.
620         */
621        protected String getCustomDocumentTitle() {
622           
623            try {
624                // set the workflow document title
625                String poNumber = getPurchaseOrderIdentifier().toString();
626                String vendorName = StringUtils.trimToEmpty(getVendorName());
627                String preqAmount = getGrandTotal().toString();
628    
629                String documentTitle = "";
630                String[] nodeNames = getDocumentHeader().getWorkflowDocument().getNodeNames();
631           
632                // if this doc is final or will be final
633                if (nodeNames.length == 0 || getDocumentHeader().getWorkflowDocument().stateIsFinal()) {
634                    documentTitle = (new StringBuffer("PO: ")).append(poNumber).append(" Vendor: ").append(vendorName).append(" Amount: ").append(preqAmount).toString();
635                }
636                else {
637                    PurApAccountingLine theAccount = getFirstAccount();                
638                    String accountNumber = (theAccount != null ? StringUtils.trimToEmpty(theAccount.getAccountNumber()) : "n/a");
639                    String accountChart = (theAccount != null ? theAccount.getChartOfAccountsCode() : "");
640                    String payDate = (new SimpleDateFormat("MM/dd/yyyy")).format(getPaymentRequestPayDate());
641                    String indicator = getTitleIndicator();
642     
643                    //set title to: PO# - VendorName - Chart/Account - total amt - Pay Date - Indicator (ie Hold, Request Cancel)
644                    documentTitle = (new StringBuffer("PO: ")).append(poNumber).append(" Vendor: ").append(vendorName).append(" Account: ").append(accountChart).append(" ").append(accountNumber).append(" Amount: ").append(preqAmount).append(" Pay Date: ").append(payDate).append(" ").append(indicator).toString();
645                }
646                return documentTitle;
647            }
648            catch (WorkflowException e) {
649                LOG.error("Error updating Payment Request document: " + e.getMessage());
650                throw new RuntimeException("Error updating Payment Request document: " + e.getMessage());
651            }
652        }
653    
654        /**
655         * Returns the first payment item's first account (assuming the item list is sequentially ordered).
656         * 
657         * @return - Accounting Line object for first account of first payment item.
658         */
659        public PurApAccountingLine getFirstAccount() {
660            // loop through items, and pick the first item
661            if ((getItems() != null) && (!getItems().isEmpty())) {
662                PaymentRequestItem itemToUse = null;
663                for (Iterator iter = getItems().iterator(); iter.hasNext();) {
664                    PaymentRequestItem item = (PaymentRequestItem) iter.next();
665                    if ((item.isConsideredEntered()) && ((item.getSourceAccountingLines() != null) && (!item.getSourceAccountingLines().isEmpty()))) {
666                        // accounting lines are not empty so pick the first account
667                        PurApAccountingLine accountLine = item.getSourceAccountingLine(0);
668                        accountLine.refreshNonUpdateableReferences();
669                        return accountLine;
670                    }
671                    /*
672                    if (((item.getExtendedPrice() != null) && item.getExtendedPrice().compareTo(BigDecimal.ZERO) > 0) && ((item.getAccounts() != null) && (!item.getAccounts().isEmpty()))) {
673                        // accounting lines are not empty so pick the first account
674                   List accts = (List)item.getAccounts();
675                   PaymentRequestAccount accountLine = (PaymentRequestAccount)accts.get(0);
676                        return accountLine.getFinancialChartOfAccountsCode() + "-" + accountLine.getAccountNumber();
677                    }
678                    */
679                }
680            }
681            return null;
682        }
683    
684        /**
685         * Determines the indicator text that will appear in the workflow document title
686         * 
687         * @return - Text of hold or request cancel
688         */
689        protected String getTitleIndicator() {
690            if (isHoldIndicator()) {
691                return PurapConstants.PaymentRequestIndicatorText.HOLD;
692            }
693            else if (isPaymentRequestedCancelIndicator()) {
694                return PurapConstants.PaymentRequestIndicatorText.REQUEST_CANCEL;
695            }
696            return "";
697        }
698    
699    
700        /**
701         * @see org.kuali.rice.kns.document.DocumentBase#doRouteStatusChange()
702         */
703        @Override
704        public void doRouteStatusChange(DocumentRouteStatusChangeDTO statusChangeEvent) {
705            LOG.debug("doRouteStatusChange() started");
706            
707            super.doRouteStatusChange(statusChangeEvent);
708            try {
709                // DOCUMENT PROCESSED
710                if (this.getDocumentHeader().getWorkflowDocument().stateIsProcessed()) {
711                    if (!PaymentRequestStatuses.AUTO_APPROVED.equals(getStatusCode())) {
712                        SpringContext.getBean(PurapService.class).updateStatus(this, PurapConstants.PaymentRequestStatuses.DEPARTMENT_APPROVED);
713                        populateDocumentForRouting();
714                        SpringContext.getBean(PurapService.class).saveDocumentNoValidation(this);
715                        return;
716                    }
717                }
718                // DOCUMENT DISAPPROVED
719                else if (this.getDocumentHeader().getWorkflowDocument().stateIsDisapproved()) {
720                    String nodeName = SpringContext.getBean(WorkflowDocumentService.class).getCurrentRouteLevelName(getDocumentHeader().getWorkflowDocument());
721                    NodeDetails currentNode = NodeDetailEnum.getNodeDetailEnumByName(nodeName);
722                    if (ObjectUtils.isNotNull(currentNode)) {
723                        String newStatusCode = currentNode.getDisapprovedStatusCode();
724                        if ((StringUtils.isBlank(newStatusCode)) && ((StringUtils.isBlank(currentNode.getDisapprovedStatusCode())) && ((PaymentRequestStatuses.INITIATE.equals(getStatusCode())) || (PaymentRequestStatuses.IN_PROCESS.equals(getStatusCode()))))) {
725                            newStatusCode = PaymentRequestStatuses.CANCELLED_IN_PROCESS;
726                        }
727                        if (StringUtils.isNotBlank(newStatusCode)) {
728                            SpringContext.getBean(AccountsPayableService.class).cancelAccountsPayableDocument(this, nodeName);
729                            return;
730                        }
731                    }
732                    logAndThrowRuntimeException("No status found to set for document being disapproved in node '" + nodeName + "'");
733                }
734                // DOCUMENT CANCELED
735                else if (this.getDocumentHeader().getWorkflowDocument().stateIsCanceled()) {
736                    String currentNodeName = SpringContext.getBean(WorkflowDocumentService.class).getCurrentRouteLevelName(this.getDocumentHeader().getWorkflowDocument());
737                    NodeDetails currentNode = NodeDetailEnum.getNodeDetailEnumByName(currentNodeName);
738                    if (ObjectUtils.isNotNull(currentNode)) {
739                        String cancelledStatusCode = currentNode.getDisapprovedStatusCode();
740                        if (StringUtils.isNotBlank(cancelledStatusCode)) {
741                            SpringContext.getBean(PurapService.class).updateStatus(this, cancelledStatusCode);
742                            SpringContext.getBean(PurapService.class).saveDocumentNoValidation(this);
743                            return;
744                        }
745                    }
746                    logAndThrowRuntimeException("No status found to set for document being canceled in node '" + currentNode + "'");
747                }
748            }
749            catch (WorkflowException e) {
750                logAndThrowRuntimeException("Error saving routing data while saving document with id " + getDocumentNumber(), e);
751            }
752        }
753    
754        /**
755         * Generates correcting entries to the GL if accounts are modified.
756         * 
757         * @see org.kuali.rice.kns.document.Document#doActionTaken(org.kuali.rice.kew.clientapp.vo.ActionTakenEventDTO)
758         */
759        @Override
760        public void doActionTaken(ActionTakenEventDTO event) {
761            super.doActionTaken(event);
762            KualiWorkflowDocument workflowDocument = getDocumentHeader().getWorkflowDocument();
763            try {
764                String currentNode = null;
765                if (workflowDocument.getNodeNames().length > 0) {
766                    currentNode = workflowDocument.getNodeNames()[0];
767                }
768    
769                // everything in the below list requires correcting entries to be written to the GL
770                if (NodeDetailEnum.getNodesRequiringCorrectingGeneralLedgerEntries().contains(currentNode)) {
771                    if (NodeDetailEnum.ACCOUNT_REVIEW.getName().equals(currentNode) || NodeDetailEnum.VENDOR_TAX_REVIEW.getName().equals(currentNode)) {
772                        SpringContext.getBean(PurapGeneralLedgerService.class).generateEntriesModifyPaymentRequest(this);
773                    }
774                }
775            }
776            catch (WorkflowException e) {
777                logAndThrowRuntimeException("Error saving routing data while saving document with id " + getDocumentNumber(), e);
778            }
779        }
780    
781        /**
782         * @see org.kuali.kfs.module.purap.document.AccountsPayableDocumentBase#preProcessNodeChange(java.lang.String, java.lang.String)
783         */
784        public boolean processNodeChange(String newNodeName, String oldNodeName) {
785            if (PaymentRequestStatuses.AUTO_APPROVED.equals(getStatusCode())) {
786                // do nothing for an auto approval
787                return false;
788            }
789            if (NodeDetailEnum.ADHOC_REVIEW.getName().equals(oldNodeName)) {
790                SpringContext.getBean(AccountsPayableService.class).performLogicForFullEntryCompleted(this);
791            }
792            return true;
793        }
794    
795        /**
796         * @see org.kuali.kfs.module.purap.document.AccountsPayableDocumentBase#getNodeDetailEnum(java.lang.String)
797         */
798        public NodeDetails getNodeDetailEnum(String nodeName) {
799            return NodeDetailEnum.getNodeDetailEnumByName(nodeName);
800        }
801    
802        /**
803         * @see org.kuali.kfs.module.purap.document.AccountsPayableDocumentBase#saveDocumentFromPostProcessing()
804         */
805        public void saveDocumentFromPostProcessing() {
806            SpringContext.getBean(PurapService.class).saveDocumentNoValidation(this);
807    
808            // if we've hit full entry completed then close/reopen po
809            if (SpringContext.getBean(PurapService.class).isFullDocumentEntryCompleted(this) && this.isClosePurchaseOrderIndicator()) {
810                SpringContext.getBean(PurapService.class).performLogicForCloseReopenPO(this);
811            }
812        }
813    
814        /**
815         * @see org.kuali.kfs.module.purap.document.PurchasingAccountsPayableDocumentBase#getItemClass()
816         */
817        @Override
818        public Class getItemClass() {
819            return PaymentRequestItem.class;
820        }
821    
822        @Override
823        public Class getItemUseTaxClass() {
824            return PaymentRequestItemUseTax.class;
825        }
826    
827        /**
828         * @see org.kuali.kfs.module.purap.document.PurchasingAccountsPayableDocumentBase#getPurApSourceDocumentIfPossible()
829         */
830        @Override
831        public PurchaseOrderDocument getPurApSourceDocumentIfPossible() {
832            return getPurchaseOrderDocument();
833        }
834    
835        /**
836         * @see org.kuali.kfs.module.purap.document.PurchasingAccountsPayableDocumentBase#getPurApSourceDocumentLabelIfPossible()
837         */
838        @Override
839        public String getPurApSourceDocumentLabelIfPossible() {
840            return SpringContext.getBean(DataDictionaryService.class).getDocumentLabelByTypeName(KFSConstants.FinancialDocumentTypeCodes.PURCHASE_ORDER);
841        }
842    
843        public String getPurchaseOrderNotes() {
844    
845            ArrayList poNotes = (ArrayList) this.getPurchaseOrderDocument().getBoNotes();
846    
847            if (poNotes.size() > 0) {
848                return "Yes";
849            }
850            return "No";
851        }
852    
853        public void setPurchaseOrderNotes(String purchaseOrderNotes) {
854            this.purchaseOrderNotes = purchaseOrderNotes;
855        }
856    
857        public String getRecurringPaymentTypeCode() {
858            return recurringPaymentTypeCode;
859        }
860    
861        public void setRecurringPaymentTypeCode(String recurringPaymentTypeCode) {
862            this.recurringPaymentTypeCode = recurringPaymentTypeCode;
863        }
864    
865        /**
866         * Returns the total encumbered amount from the purchase order excluding below the line.
867         * 
868         * @return Total cost excluding below the line
869         */
870        public KualiDecimal getItemTotalPoEncumbranceAmount() {
871            // get total from po excluding below the line and inactive
872            return this.getPurchaseOrderDocument().getTotalDollarAmount(false, false);
873        }
874    
875        public KualiDecimal getItemTotalPoEncumbranceAmountRelieved() {
876            return getItemTotalPoEncumbranceAmountRelieved(false);
877        }
878    
879        public KualiDecimal getItemTotalPoEncumbranceAmountRelieved(boolean includeBelowTheLine) {
880    
881            KualiDecimal total = KualiDecimal.ZERO;
882    
883            for (PurchaseOrderItem item : (List<PurchaseOrderItem>) getPurchaseOrderDocument().getItems()) {
884                ItemType it = item.getItemType();
885                if (includeBelowTheLine || it.isLineItemIndicator()) {
886                    total = total.add(item.getItemEncumbranceRelievedAmount());
887                }
888            }
889            return total;
890        }
891    
892        public KualiDecimal getLineItemTotal() {
893            return this.getTotalDollarAmountAboveLineItems();
894        }
895    
896        public KualiDecimal getLineItemPreTaxTotal() {
897            return this.getTotalPreTaxDollarAmountAboveLineItems();
898        }
899    
900        public KualiDecimal getLineItemTaxAmount() {
901            return this.getTotalTaxAmountAboveLineItems();
902        }
903    
904        public KualiDecimal getGrandTotal() {
905            return this.getTotalDollarAmount();
906        }
907    
908        public KualiDecimal getGrandTotalExcludingDiscount() {
909            String[] discountCode = new String[] { PurapConstants.ItemTypeCodes.ITEM_TYPE_PMT_TERMS_DISCOUNT_CODE };
910            return this.getTotalDollarAmountWithExclusions(discountCode, true);
911        }
912    
913        /**
914         * This method is here due to a setter requirement by the htmlControlAttribute
915         * 
916         * @param amount - Grand total for document, excluding discount
917         */
918        public void setGrandTotalExcludingDiscount(KualiDecimal amount) {
919            // do nothing
920        }
921    
922        public KualiDecimal getGrandPreTaxTotal() {
923            return this.getTotalPreTaxDollarAmount();
924        }
925    
926        public KualiDecimal getGrandPreTaxTotalExcludingDiscount() {
927            String[] discountCode = new String[] { PurapConstants.ItemTypeCodes.ITEM_TYPE_PMT_TERMS_DISCOUNT_CODE };
928            return this.getTotalPreTaxDollarAmountWithExclusions(discountCode, true);
929        }
930    
931        public KualiDecimal getGrandTaxAmount() {
932            return this.getTotalTaxAmount();
933        }
934    
935        public KualiDecimal getGrandTaxAmountExcludingDiscount() {
936            String[] discountCode = new String[] { PurapConstants.ItemTypeCodes.ITEM_TYPE_PMT_TERMS_DISCOUNT_CODE };
937            return this.getTotalTaxAmountWithExclusions(discountCode, true);
938        }
939    
940        public boolean isDiscount() {
941            return SpringContext.getBean(PaymentRequestService.class).hasDiscountItem(this);
942        }
943    
944        /**
945         * The total that was paid on the po excluding below the line
946         * 
947         * @return total paid
948         */
949        public KualiDecimal getItemTotalPoPaidAmount() {
950            KualiDecimal total = KualiDecimal.ZERO;
951            for (PurchaseOrderItem item : (List<PurchaseOrderItem>) getPurchaseOrderDocument().getItems()) {
952                ItemType iT = item.getItemType();
953                if (iT.isLineItemIndicator()) {
954                    KualiDecimal itemPaid = item.getItemPaidAmount();
955                    total = total.add(itemPaid);
956                }
957            }
958            return total;
959        }
960    
961        /**
962         * Returns the name of who requested cancel.
963         * 
964         * @return - name of who requested cancel.
965         */
966        public String getAccountsPayableRequestCancelPersonName() {
967            String personName = null;
968            Person user = SpringContext.getBean(org.kuali.rice.kim.service.PersonService.class).getPerson(getAccountsPayableRequestCancelIdentifier());
969            if (user != null) {
970                personName = user.getName();
971            }
972            else {
973                personName = "";
974            }
975    
976            return personName;
977        }
978    
979        /**
980         * Exists due to a setter requirement by the htmlControlAttribute
981         * 
982         * @param amount - total po amount paid
983         */
984        public void setItemTotalPoPaidAmount(KualiDecimal amount) {
985            // do nothing
986        }
987    
988        /**
989         * Exists due to a setter requirement by the htmlControlAttribute
990         * 
991         * @param amount - total po encumbrance
992         */
993        public void setItemTotalPoEncumbranceAmount(KualiDecimal amount) {
994            // do nothing
995        }
996    
997        /**
998         * Exists due to a setter requirement by the htmlControlAttribute
999         * 
1000         * @param amount - total po encumbrance amount relieved
1001         */
1002        public void setItemTotalPoEncumbranceAmountRelieved(KualiDecimal amount) {
1003            // do nothing
1004        }
1005    
1006        /**
1007         * Determinines the route levels for a given document.
1008         * 
1009         * @param workflowDocument - work flow document
1010         * @return List - list of route levels
1011         */
1012        protected List getCurrentRouteLevels(KualiWorkflowDocument workflowDocument) {
1013            try {
1014                return Arrays.asList(workflowDocument.getNodeNames());
1015            }
1016            catch (WorkflowException e) {
1017                throw new RuntimeException(e);
1018            }
1019        }
1020    
1021        /**
1022         * USED FOR ROUTING ONLY
1023         * 
1024         * @deprecated
1025         */
1026        public String getStatusDescription() {
1027            return "";
1028        }
1029    
1030        /**
1031         * USED FOR ROUTING ONLY
1032         * 
1033         * @deprecated
1034         */
1035        public void setStatusDescription(String statusDescription) {
1036        }
1037    
1038        public RecurringPaymentType getRecurringPaymentType() {
1039            if (ObjectUtils.isNull(recurringPaymentType)) {
1040                refreshReferenceObject(PurapPropertyConstants.RECURRING_PAYMENT_TYPE);
1041            }
1042            return recurringPaymentType;
1043        }
1044    
1045        public void setRecurringPaymentType(RecurringPaymentType recurringPaymentType) {
1046            this.recurringPaymentType = recurringPaymentType;
1047        }
1048    
1049        public PurchaseOrderCostSource getPaymentRequestCostSource() {
1050            return paymentRequestCostSource;
1051        }
1052    
1053        public void setPaymentRequestCostSource(PurchaseOrderCostSource paymentRequestCostSource) {
1054            this.paymentRequestCostSource = paymentRequestCostSource;
1055        }
1056    
1057        /**
1058         * @see org.kuali.kfs.module.purap.document.AccountsPayableDocumentBase#getPoDocumentTypeForAccountsPayableDocumentApprove()
1059         */
1060        public String getPoDocumentTypeForAccountsPayableDocumentCancel() {
1061            return PurapConstants.PurchaseOrderDocTypes.PURCHASE_ORDER_REOPEN_DOCUMENT;
1062        }
1063    
1064        /**
1065         * @see org.kuali.kfs.module.purap.document.AccountsPayableDocumentBase#getInitialAmount()
1066         */
1067        public KualiDecimal getInitialAmount() {
1068            return this.getVendorInvoiceAmount();
1069        }
1070    
1071        /**
1072         * Populates the payment request document, then continues with preparing for save.
1073         * 
1074         * @see org.kuali.rice.kns.document.Document#prepareForSave(org.kuali.rice.kns.rule.event.KualiDocumentEvent)
1075         */
1076        @Override
1077        public void prepareForSave(KualiDocumentEvent event) {
1078            KualiWorkflowDocument workflowDocument = this.getDocumentHeader().getWorkflowDocument();
1079            String workflowDocumentTitle = this.buildDocumentTitle(workflowDocument.getTitle());
1080    
1081            try {
1082                this.getDocumentHeader().getWorkflowDocument().setTitle(workflowDocumentTitle);
1083            }
1084            catch (WorkflowException e) {
1085                LOG.error("fail to access Workflow." + e);
1086            }
1087    
1088            // first populate, then call super
1089            if (event instanceof AttributedContinuePurapEvent) {
1090                SpringContext.getBean(PaymentRequestService.class).populatePaymentRequest(this);
1091            }
1092            super.prepareForSave(event);
1093         
1094        }
1095    
1096        /**
1097         * @see org.kuali.kfs.module.purap.document.AccountsPayableDocumentBase#isAttachmentRequired()
1098         */
1099        @Override
1100        protected boolean isAttachmentRequired() {
1101            if (getPaymentRequestElectronicInvoiceIndicator())
1102                return false;
1103            return StringUtils.equalsIgnoreCase("Y", SpringContext.getBean(ParameterService.class).getParameterValue(PaymentRequestDocument.class, PurapParameterConstants.PURAP_PREQ_REQUIRE_ATTACHMENT));
1104        }
1105    
1106        /**
1107         * @see org.kuali.kfs.module.purap.document.AccountsPayableDocument#getDocumentSpecificService()
1108         */
1109        @Override
1110        public AccountsPayableDocumentSpecificService getDocumentSpecificService() {
1111            return (AccountsPayableDocumentSpecificService) SpringContext.getBean(PaymentRequestService.class);
1112        }
1113    
1114        /**
1115         * @see org.kuali.kfs.module.purap.document.PurchasingAccountsPayableDocumentBase#getItem(int)
1116         */
1117        @Override
1118        public PurApItem getItem(int pos) {
1119            PaymentRequestItem item = (PaymentRequestItem) super.getItem(pos);
1120            if (item.getPaymentRequest() == null) {
1121                item.setPaymentRequest(this);
1122            }
1123            return item;
1124        }
1125    
1126        public String getPrimaryVendorName() {
1127    
1128            if (primaryVendorName == null) {
1129                VendorDetail vd = SpringContext.getBean(VendorService.class).getVendorDetail(this.getOriginalVendorHeaderGeneratedIdentifier(), this.getOriginalVendorDetailAssignedIdentifier());
1130    
1131                if (vd != null) {
1132                    primaryVendorName = vd.getVendorName();
1133                }
1134            }
1135    
1136            return primaryVendorName;
1137        }
1138    
1139        /**
1140         * @deprecated
1141         */
1142        public void setPrimaryVendorName(String primaryVendorName) {
1143        }
1144    
1145        /**
1146         * Forces general ledger entries to be approved, does not wait for payment request document final approval.
1147         * 
1148         * @see org.kuali.module.purap.rules.PurapAccountingDocumentRuleBase#customizeExplicitGeneralLedgerPendingEntry(org.kuali.kfs.sys.document.AccountingDocument,
1149         *      org.kuali.kfs.sys.businessobject.AccountingLine, org.kuali.kfs.sys.businessobject.GeneralLedgerPendingEntry)
1150         */
1151        @Override
1152        public void customizeExplicitGeneralLedgerPendingEntry(GeneralLedgerPendingEntrySourceDetail postable, GeneralLedgerPendingEntry explicitEntry) {
1153            super.customizeExplicitGeneralLedgerPendingEntry(postable, explicitEntry);
1154    
1155            SpringContext.getBean(PurapGeneralLedgerService.class).customizeGeneralLedgerPendingEntry(this, (AccountingLine) postable, explicitEntry, getPurchaseOrderIdentifier(), getDebitCreditCodeForGLEntries(), PurapDocTypeCodes.PAYMENT_REQUEST_DOCUMENT, isGenerateEncumbranceEntries());
1156    
1157            // PREQs do not wait for document final approval to post GL entries; here we are forcing them to be APPROVED
1158            explicitEntry.setFinancialDocumentApprovedCode(KFSConstants.PENDING_ENTRY_APPROVED_STATUS_CODE.APPROVED);
1159        }
1160    
1161        /**
1162         * Provides answers to the following splits: PurchaseWasReceived VendorIsEmployeeOrNonResidentAlien
1163         * 
1164         * @see org.kuali.kfs.sys.document.FinancialSystemTransactionalDocumentBase#answerSplitNodeQuestion(java.lang.String)
1165         */
1166        @Override
1167        public boolean answerSplitNodeQuestion(String nodeName) throws UnsupportedOperationException {
1168            if (nodeName.equals(PurapWorkflowConstants.REQUIRES_IMAGE_ATTACHMENT))
1169                return requiresAccountsPayableReviewRouting();
1170            if (nodeName.equals(PurapWorkflowConstants.PURCHASE_WAS_RECEIVED))
1171                return shouldWaitForReceiving();
1172            if (nodeName.equals(PurapWorkflowConstants.VENDOR_IS_EMPLOYEE_OR_NON_RESIDENT_ALIEN))
1173                return isVendorEmployeeOrNonResidentAlien();
1174            throw new UnsupportedOperationException("Cannot answer split question for this node you call \"" + nodeName + "\"");
1175        }
1176    
1177        protected boolean isVendorEmployeeOrNonResidentAlien() {
1178            String vendorHeaderGeneratedId = this.getVendorHeaderGeneratedIdentifier().toString();
1179            if (StringUtils.isBlank(vendorHeaderGeneratedId)) {
1180                // no vendor header id so can't check for proper tax routing
1181                return false;
1182            }
1183            VendorService vendorService = SpringContext.getBean(VendorService.class);
1184            boolean routeDocumentAsEmployeeVendor = vendorService.isVendorInstitutionEmployee(Integer.valueOf(vendorHeaderGeneratedId));
1185            boolean routeDocumentAsForeignVendor = vendorService.isVendorForeign(Integer.valueOf(vendorHeaderGeneratedId));
1186            if ((!routeDocumentAsEmployeeVendor) && (!routeDocumentAsForeignVendor)) {
1187                // no need to route
1188                return false;
1189            }
1190    
1191            return true;
1192        }
1193    
1194        /**
1195         * Payment Request needs to wait for receiving if the receiving requirements have NOT been met.
1196         * 
1197         * @return
1198         */
1199        protected boolean shouldWaitForReceiving() {
1200            // only require if PO was marked to require receiving
1201            if (isReceivingDocumentRequiredIndicator()) {
1202                return !isReceivingRequirementMet();
1203            }
1204            
1205            //receiving is not required or has already been fulfilled, no need to stop for routing
1206            return false;
1207        }
1208        
1209        /**
1210         * Determine if the receiving requirement has been met for all items on the payment request. If any item does not have receiving
1211         * requirements met, return false. Receiving requirement has NOT been met if the quantity invoiced on the Payment Request is
1212         * greater than the quantity of "unpaid and received" items determined by (poQtyReceived - (poQtyInvoiced - preqQtyInvoiced)).
1213         * We have to subtract preqQtyInvoiced from the poQtyInvoiced because this payment request has already updated the totals on the
1214         * po.
1215         * 
1216         * @return boolean return true if the receiving requirement has been met for all items on the payment request; false if
1217         *         requirement has not been met
1218         */
1219        public boolean isReceivingRequirementMet() {
1220    
1221            for (Iterator iter = getItems().iterator(); iter.hasNext();) {
1222                PaymentRequestItem preqItem = (PaymentRequestItem) iter.next();
1223    
1224                if (preqItem.getItemType().isQuantityBasedGeneralLedgerIndicator()) {
1225                    PurchaseOrderItem poItem = preqItem.getPurchaseOrderItem();
1226                    KualiDecimal preqQuantityInvoiced = preqItem.getItemQuantity() == null ? KualiDecimal.ZERO : preqItem.getItemQuantity();
1227                    KualiDecimal poQuantityReceived = poItem.getItemReceivedTotalQuantity() == null ? KualiDecimal.ZERO : poItem.getItemReceivedTotalQuantity();
1228                    KualiDecimal poQuantityInvoiced = poItem.getItemInvoicedTotalQuantity() == null ? KualiDecimal.ZERO : poItem.getItemInvoicedTotalQuantity();
1229    
1230                    // receiving has NOT been met if preqQtyInvoiced is greater than (poQtyReceived & (poQtyInvoiced & preqQtyInvoiced))
1231                    if (preqQuantityInvoiced.compareTo(poQuantityReceived.subtract(poQuantityInvoiced.subtract(preqQuantityInvoiced))) > 0) {
1232                        return false;
1233                    }
1234                }
1235            }
1236    
1237            return true;
1238        }
1239           
1240        public Date getTransactionTaxDate() {
1241            return getInvoiceDate();
1242        }
1243    
1244        public String getTaxClassificationCode() {
1245            return taxClassificationCode;
1246        }
1247    
1248        public void setTaxClassificationCode(String taxClassificationCode) {
1249            this.taxClassificationCode = taxClassificationCode;
1250        }
1251    
1252        public KualiDecimal getTaxFederalPercentShort() {
1253            return new KualiDecimal(taxFederalPercent);
1254        }
1255    
1256        public BigDecimal getTaxFederalPercent() {
1257            return taxFederalPercent;
1258        }
1259    
1260        public void setTaxFederalPercent(BigDecimal taxFederalPercent) {
1261            this.taxFederalPercent = taxFederalPercent;
1262        }
1263    
1264        public KualiDecimal getTaxStatePercentShort() {
1265            return new KualiDecimal(taxStatePercent);
1266        }
1267    
1268        public BigDecimal getTaxStatePercent() {
1269            return taxStatePercent;
1270        }
1271    
1272        public void setTaxStatePercent(BigDecimal taxStatePercent) {
1273            this.taxStatePercent = taxStatePercent;
1274        }
1275    
1276        public String getTaxCountryCode() {
1277            return taxCountryCode;
1278        }
1279    
1280        public void setTaxCountryCode(String taxCountryCode) {
1281            this.taxCountryCode = taxCountryCode;
1282        }
1283    
1284        public Boolean getTaxGrossUpIndicator() {
1285            return taxGrossUpIndicator;
1286        }
1287    
1288        public void setTaxGrossUpIndicator(Boolean taxGrossUpIndicator) {
1289            this.taxGrossUpIndicator = taxGrossUpIndicator;
1290        }
1291    
1292        public Boolean getTaxExemptTreatyIndicator() {
1293            return taxExemptTreatyIndicator;
1294        }
1295    
1296        public void setTaxExemptTreatyIndicator(Boolean taxExemptTreatyIndicator) {
1297            this.taxExemptTreatyIndicator = taxExemptTreatyIndicator;
1298        }
1299    
1300        public Boolean getTaxForeignSourceIndicator() {
1301            return taxForeignSourceIndicator;
1302        }
1303    
1304        public void setTaxForeignSourceIndicator(Boolean taxForeignSourceIndicator) {
1305            this.taxForeignSourceIndicator = taxForeignSourceIndicator;
1306        }
1307    
1308        public KualiDecimal getTaxSpecialW4Amount() {
1309            return taxSpecialW4Amount;
1310        }
1311    
1312        public void setTaxSpecialW4Amount(KualiDecimal taxSpecialW4Amount) {
1313            this.taxSpecialW4Amount = taxSpecialW4Amount;
1314        }
1315    
1316        public Boolean getTaxUSAIDPerDiemIndicator() {
1317            return taxUSAIDPerDiemIndicator;
1318        }
1319    
1320        public void setTaxUSAIDPerDiemIndicator(Boolean taxUSAIDPerDiemIndicator) {
1321            this.taxUSAIDPerDiemIndicator = taxUSAIDPerDiemIndicator;
1322        }
1323    
1324        public Boolean getTaxOtherExemptIndicator() {
1325            return taxOtherExemptIndicator;
1326        }
1327    
1328        public void setTaxOtherExemptIndicator(Boolean taxOtherExemptIndicator) {
1329            this.taxOtherExemptIndicator = taxOtherExemptIndicator;
1330        }
1331    
1332        public String getTaxNQIId() {
1333            return taxNQIId;
1334        }
1335    
1336        public void setTaxNQIId(String taxNQIId) {
1337            this.taxNQIId = taxNQIId;
1338        }
1339    
1340        public boolean isPaymentRequestedCancelIndicatorForSearching() {
1341            return paymentRequestedCancelIndicator;
1342        }
1343    
1344        /**
1345         * @return the payment request positive approval indicator
1346         */
1347        public boolean getPaymentRequestPositiveApprovalIndicatorForSearching() {
1348            return paymentRequestPositiveApprovalIndicator;
1349        }
1350    
1351        /**
1352         * @return the receiving document required indicator
1353         */
1354        public boolean getReceivingDocumentRequiredIndicatorForSearching() {
1355            return receivingDocumentRequiredIndicator;
1356        }
1357    
1358    
1359        public String getRequestCancelIndicatorForResult() {
1360            return isPaymentRequestedCancelIndicator() ? "Yes" : "No";
1361        }
1362    
1363        public String getPaidIndicatorForResult() {
1364            return getPaymentPaidTimestamp() != null ? "Yes" : "No";
1365        }
1366                
1367        public Date getAccountsPayableApprovalDateForSearching() {
1368            if (this.getAccountsPayableApprovalTimestamp() == null)
1369                return null;
1370            try {
1371                Date date = SpringContext.getBean(DateTimeService.class).convertToSqlDate(this.getAccountsPayableApprovalTimestamp());
1372                LOG.debug("getAccountsPayableApprovalDateForSearching() returns " + date);
1373                return date;
1374            }
1375            catch (Exception e) {
1376                return new Date(this.getAccountsPayableApprovalTimestamp().getTime());            
1377            }
1378        }
1379        
1380        /**
1381         * Checks all documents notes for attachments.
1382         * 
1383         * @return - true if document does not have an image attached, false otherwise
1384         */
1385        public boolean documentHasNoImagesAttached() {
1386            List boNotes = this.getDocumentBusinessObject().getBoNotes();
1387            if (ObjectUtils.isNotNull(boNotes)) {
1388                for (Object obj : boNotes) {
1389                    Note note = (Note) obj;
1390                    
1391                    note.refreshReferenceObject("attachment");
1392                    if (ObjectUtils.isNotNull(note.getAttachment()) && PurapConstants.AttachmentTypeCodes.ATTACHMENT_TYPE_INVOICE_IMAGE.equals(note.getAttachment().getAttachmentTypeCode())) {
1393                        return false;
1394                    }
1395                }
1396            }
1397            return true;
1398        }
1399    }