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 static org.kuali.kfs.sys.KFSConstants.GL_DEBIT_CODE;
020    import static org.kuali.rice.kns.util.KualiDecimal.ZERO;
021    
022    import java.math.BigDecimal;
023    import java.sql.Date;
024    import java.sql.Timestamp;
025    import java.util.ArrayList;
026    import java.util.Collections;
027    import java.util.HashMap;
028    import java.util.Iterator;
029    import java.util.List;
030    import java.util.Map;
031    
032    import org.apache.commons.lang.StringUtils;
033    import org.kuali.kfs.coa.businessobject.Account;
034    import org.kuali.kfs.gl.service.SufficientFundsService;
035    import org.kuali.kfs.integration.purap.CapitalAssetSystem;
036    import org.kuali.kfs.module.purap.PurapConstants;
037    import org.kuali.kfs.module.purap.PurapKeyConstants;
038    import org.kuali.kfs.module.purap.PurapParameterConstants;
039    import org.kuali.kfs.module.purap.PurapPropertyConstants;
040    import org.kuali.kfs.module.purap.PurapWorkflowConstants;
041    import org.kuali.kfs.module.purap.PurapConstants.CreditMemoStatuses;
042    import org.kuali.kfs.module.purap.PurapConstants.PurapDocTypeCodes;
043    import org.kuali.kfs.module.purap.PurapConstants.PurchaseOrderStatuses;
044    import org.kuali.kfs.module.purap.PurapConstants.QuoteTypeDescriptions;
045    import org.kuali.kfs.module.purap.PurapConstants.RequisitionSources;
046    import org.kuali.kfs.module.purap.PurapWorkflowConstants.NodeDetails;
047    import org.kuali.kfs.module.purap.PurapWorkflowConstants.PurchaseOrderDocument.NodeDetailEnum;
048    import org.kuali.kfs.module.purap.businessobject.CreditMemoView;
049    import org.kuali.kfs.module.purap.businessobject.ItemType;
050    import org.kuali.kfs.module.purap.businessobject.PaymentRequestView;
051    import org.kuali.kfs.module.purap.businessobject.PurApAccountingLine;
052    import org.kuali.kfs.module.purap.businessobject.PurApItem;
053    import org.kuali.kfs.module.purap.businessobject.PurchaseOrderAccount;
054    import org.kuali.kfs.module.purap.businessobject.PurchaseOrderCapitalAssetItem;
055    import org.kuali.kfs.module.purap.businessobject.PurchaseOrderCapitalAssetSystem;
056    import org.kuali.kfs.module.purap.businessobject.PurchaseOrderItem;
057    import org.kuali.kfs.module.purap.businessobject.PurchaseOrderItemUseTax;
058    import org.kuali.kfs.module.purap.businessobject.PurchaseOrderSensitiveData;
059    import org.kuali.kfs.module.purap.businessobject.PurchaseOrderVendorChoice;
060    import org.kuali.kfs.module.purap.businessobject.PurchaseOrderVendorQuote;
061    import org.kuali.kfs.module.purap.businessobject.PurchaseOrderVendorStipulation;
062    import org.kuali.kfs.module.purap.businessobject.PurchaseOrderView;
063    import org.kuali.kfs.module.purap.businessobject.RecurringPaymentFrequency;
064    import org.kuali.kfs.module.purap.businessobject.RequisitionCapitalAssetItem;
065    import org.kuali.kfs.module.purap.businessobject.RequisitionCapitalAssetSystem;
066    import org.kuali.kfs.module.purap.businessobject.RequisitionItem;
067    import org.kuali.kfs.module.purap.document.service.PurapService;
068    import org.kuali.kfs.module.purap.document.service.PurchaseOrderService;
069    import org.kuali.kfs.module.purap.document.service.PurchasingDocumentSpecificService;
070    import org.kuali.kfs.module.purap.document.service.RequisitionService;
071    import org.kuali.kfs.module.purap.service.PurapAccountingService;
072    import org.kuali.kfs.module.purap.service.PurapGeneralLedgerService;
073    import org.kuali.kfs.module.purap.util.PurApItemUtils;
074    import org.kuali.kfs.sys.KFSConstants;
075    import org.kuali.kfs.sys.businessobject.AccountingLine;
076    import org.kuali.kfs.sys.businessobject.GeneralLedgerPendingEntry;
077    import org.kuali.kfs.sys.businessobject.GeneralLedgerPendingEntrySourceDetail;
078    import org.kuali.kfs.sys.businessobject.SourceAccountingLine;
079    import org.kuali.kfs.sys.businessobject.SufficientFundsItem;
080    import org.kuali.kfs.sys.context.SpringContext;
081    import org.kuali.kfs.sys.document.MultiselectableDocSearchConversion;
082    import org.kuali.kfs.sys.service.UniversityDateService;
083    import org.kuali.kfs.vnd.VendorConstants;
084    import org.kuali.kfs.vnd.businessobject.ContractManager;
085    import org.kuali.kfs.vnd.businessobject.PaymentTermType;
086    import org.kuali.kfs.vnd.businessobject.ShippingPaymentTerms;
087    import org.kuali.kfs.vnd.businessobject.ShippingTitle;
088    import org.kuali.kfs.vnd.businessobject.VendorDetail;
089    import org.kuali.kfs.vnd.document.service.VendorService;
090    import org.kuali.rice.kew.docsearch.DocSearchCriteriaDTO;
091    import org.kuali.rice.kew.docsearch.SearchAttributeCriteriaComponent;
092    import org.kuali.rice.kew.dto.ActionTakenEventDTO;
093    import org.kuali.rice.kew.dto.DocumentRouteLevelChangeDTO;
094    import org.kuali.rice.kew.dto.DocumentRouteStatusChangeDTO;
095    import org.kuali.rice.kew.dto.ReportCriteriaDTO;
096    import org.kuali.rice.kew.exception.WorkflowException;
097    import org.kuali.rice.kew.util.KEWConstants;
098    import org.kuali.rice.kim.bo.Person;
099    import org.kuali.rice.kim.bo.entity.KimPrincipal;
100    import org.kuali.rice.kim.service.IdentityManagementService;
101    import org.kuali.rice.kim.service.PersonService;
102    import org.kuali.rice.kns.bo.PersistableBusinessObject;
103    import org.kuali.rice.kns.rule.event.KualiDocumentEvent;
104    import org.kuali.rice.kns.service.BusinessObjectService;
105    import org.kuali.rice.kns.service.DataDictionaryService;
106    import org.kuali.rice.kns.service.DateTimeService;
107    import org.kuali.rice.kns.service.DocumentHeaderService;
108    import org.kuali.rice.kns.service.ParameterService;
109    import org.kuali.rice.kns.service.SequenceAccessorService;
110    import org.kuali.rice.kns.util.GlobalVariables;
111    import org.kuali.rice.kns.util.KualiDecimal;
112    import org.kuali.rice.kns.util.ObjectUtils;
113    import org.kuali.rice.kns.util.TypedArrayList;
114    import org.kuali.rice.kns.web.ui.Field;
115    import org.kuali.rice.kns.workflow.service.KualiWorkflowDocument;
116    import org.kuali.rice.kns.workflow.service.KualiWorkflowInfo;
117    import org.kuali.rice.kns.workflow.service.WorkflowDocumentService;
118    
119    /**
120     * Purchase Order Document
121     */
122    public class PurchaseOrderDocument extends PurchasingDocumentBase implements MultiselectableDocSearchConversion {
123        protected static org.apache.log4j.Logger LOG = org.apache.log4j.Logger.getLogger(PurchaseOrderDocument.class);
124    
125        protected Timestamp purchaseOrderCreateTimestamp;
126        protected Integer requisitionIdentifier;
127        protected String purchaseOrderVendorChoiceCode;
128        protected String recurringPaymentFrequencyCode;
129        protected KualiDecimal recurringPaymentAmount;
130        protected Date recurringPaymentDate;
131        protected KualiDecimal initialPaymentAmount;
132        protected Date initialPaymentDate;
133        protected KualiDecimal finalPaymentAmount;
134        protected Date finalPaymentDate;
135        protected Timestamp purchaseOrderInitialOpenTimestamp;
136        protected Timestamp purchaseOrderLastTransmitTimestamp;
137        protected Date purchaseOrderQuoteDueDate;
138        protected String purchaseOrderQuoteTypeCode;
139        protected String purchaseOrderQuoteVendorNoteText;
140        protected boolean purchaseOrderConfirmedIndicator;
141        protected String purchaseOrderCommodityDescription;
142        protected Integer purchaseOrderPreviousIdentifier;
143        protected Integer alternateVendorHeaderGeneratedIdentifier;
144        protected Integer alternateVendorDetailAssignedIdentifier;
145        protected Integer newQuoteVendorHeaderGeneratedIdentifier;
146        protected Integer newQuoteVendorDetailAssignedIdentifier;
147        protected String alternateVendorName;
148        protected boolean purchaseOrderCurrentIndicator = false;
149        protected boolean pendingActionIndicator = false;
150        protected Timestamp purchaseOrderFirstTransmissionTimestamp;
151        protected Integer contractManagerCode;
152        protected Date purchaseOrderQuoteInitializationDate;
153        protected Date purchaseOrderQuoteAwardedDate;
154        protected String assignedUserPrincipalId;
155        
156        // COLLECTIONS
157        protected List<PurchaseOrderVendorStipulation> purchaseOrderVendorStipulations;
158        protected List<PurchaseOrderVendorQuote> purchaseOrderVendorQuotes;
159        
160        // NOT PERSISTED IN DB
161        protected String statusChange;
162        protected String alternateVendorNumber;
163        protected String purchaseOrderRetransmissionMethodCode;
164        protected String retransmitHeader;
165        protected Integer purchaseOrderQuoteListIdentifier;
166        protected KualiDecimal internalPurchasingLimit;
167        protected boolean pendingSplit = false;           // Needed for authorization
168        protected boolean copyingNotesWhenSplitting;      // Check box on Split PO tab
169        protected boolean assigningSensitiveData = false; // whether the form is currently used for assigning sensitive data to the PO
170        protected List<PurchaseOrderSensitiveData> purchaseOrderSensitiveData;  
171        protected String assignedUserPrincipalName; // this serves as a temporary holder before validation is done
172        
173        //this is a holder for the accountinglines for GL purposes only; used only for PO change docs
174        protected List<SourceAccountingLine> glOnlySourceAccountingLines;
175        
176        // REFERENCE OBJECTS
177        protected PurchaseOrderVendorChoice purchaseOrderVendorChoice;
178        protected PaymentTermType vendorPaymentTerms;
179        protected ShippingTitle vendorShippingTitle;
180        protected ShippingPaymentTerms vendorShippingPaymentTerms;
181        protected RecurringPaymentFrequency recurringPaymentFrequency;
182        protected ContractManager contractManager;
183        //protected Person assignedUser;
184    
185        public static final String FIN_COA_CD_KEY = "fin_coa_cd";
186        protected static final String UNIVERSITY_FISCAL_YEAR_KEY = "univ_fiscal_year";
187        protected static final String VENDOR_IS_EMPLOYEE = "Employee Vendor";
188        protected static final String VENDOR_IS_FOREIGN = "Foreign Vendor";
189        protected static final String VENDOR_IS_FOREIGN_EMPLOYEE = "Foreign and Employee Vendor";
190        
191        /**
192         * Default constructor.
193         */
194        public PurchaseOrderDocument() {
195            super();
196            this.purchaseOrderVendorStipulations = new TypedArrayList(PurchaseOrderVendorStipulation.class);
197            this.purchaseOrderVendorQuotes = new TypedArrayList(PurchaseOrderVendorQuote.class);
198        }
199    
200        public PurchasingDocumentSpecificService getDocumentSpecificService() {
201            return SpringContext.getBean(PurchaseOrderService.class);    
202        }
203        
204        /**
205         * Overrides the method in PurchasingAccountsPayableDocumentBase to add the criteria
206         * specific to Purchase Order Document.
207         * 
208         * @see org.kuali.kfs.module.purap.document.PurchasingAccountsPayableDocumentBase#isInquiryRendered()
209         */
210        @Override
211        public boolean isInquiryRendered() {
212            if ( isPostingYearPrior() && 
213                 ( getStatusCode().equals(PurapConstants.PurchaseOrderStatuses.CLOSED) || 
214                   getStatusCode().equals(PurapConstants.PurchaseOrderStatuses.CANCELLED) ||
215                   getStatusCode().equals(PurapConstants.PurchaseOrderStatuses.VOID) ) )  {
216                   return false;            
217            }
218            else {
219                return true;
220            }
221        }
222        
223        /**
224         * @see org.kuali.rice.kns.document.DocumentBase#getDocumentTitle()
225         */
226        @Override
227        public String getDocumentTitle() {
228            if (SpringContext.getBean(ParameterService.class).getIndicatorParameter(PurchaseOrderDocument.class, PurapParameterConstants.PURAP_OVERRIDE_PO_DOC_TITLE)) {
229                return getCustomDocumentTitle();
230            }
231            
232            return this.buildDocumentTitle(super.getDocumentTitle());
233        }
234    
235        /**
236         * Returns a custom document title based on the workflow document title. 
237         * Depending on what route level the document is currently in, various info may be added to the documents title.
238         * 
239         * @return - Customized document title text dependent upon route level.
240         */
241        protected String getCustomDocumentTitle() {
242            try {
243                String poNumber = getPurapDocumentIdentifier().toString();
244                String cmCode = getContractManagerCode().toString();
245                String vendorName = StringUtils.trimToEmpty(getVendorName());
246                String totalAmount = getTotalDollarAmount().toString();
247                PurApAccountingLine accountingLine = getFirstAccount();
248                String chartAcctCode = accountingLine != null ? accountingLine.getChartOfAccountsCode() : "";
249                String accountNumber = accountingLine != null ? accountingLine.getAccountNumber() : "";
250                String chartCode = getChartOfAccountsCode();
251                String orgCode = getOrganizationCode();
252                String deliveryCampus = getDeliveryCampus() != null ? getDeliveryCampus().getCampus().getCampusShortName() : "";
253                String documentTitle = "";
254             
255                String[] nodeNames = getDocumentHeader().getWorkflowDocument().getNodeNames();
256                String routeLevel = "";
257                if (nodeNames.length == 1)
258                    routeLevel = nodeNames[0];
259                
260                if (getStatusCode().equals(PurchaseOrderStatuses.OPEN)) {
261                    documentTitle = super.getDocumentTitle();
262                }
263                else if (routeLevel.equals(NodeDetailEnum.BUDGET_OFFICE_REVIEW.getName()) || routeLevel.equals(NodeDetailEnum.CONTRACTS_AND_GRANTS_REVIEW.getName())) {
264                    // Budget & C&G approval levels
265                    documentTitle = "PO: " + poNumber + " Account Number: " + chartAcctCode + "-" + accountNumber + " Dept: " + chartCode + "-" + orgCode + " Delivery Campus: " + deliveryCampus;
266                }
267                else if (routeLevel.equals(NodeDetailEnum.VENDOR_TAX_REVIEW.getName())) {
268                    // Tax approval level
269                    documentTitle = "Vendor: " + vendorName + " PO: " + poNumber + " Account Number: " + chartCode + "-" + accountNumber + " Dept: " + chartCode + "-" + orgCode + " Delivery Campus: " + deliveryCampus;
270                }
271                else 
272                    documentTitle += "PO: " + poNumber + " Contract Manager: " + cmCode + " Vendor: " + vendorName + " Amount: " + totalAmount;
273                            
274                return documentTitle;
275            }
276            catch (WorkflowException e) {
277                LOG.error("Error updating Purchase Order document: " + e.getMessage());
278                throw new RuntimeException("Error updating Purchase Order document: " + e.getMessage());
279            }
280        }
281    
282        /**
283         * @see org.kuali.kfs.sys.document.AccountingDocument#getSourceAccountingLineClass()
284         */
285        @Override
286        public Class getSourceAccountingLineClass() {
287          //NOTE: do not do anything with this method as it is used by routing etc!
288            return super.getSourceAccountingLineClass();
289        } 
290        
291        /**
292         * Returns the first PO item's first accounting line (assuming the item list is sequentially ordered).
293         * 
294         * @return - The first accounting line of the first PO item.
295         */
296        protected PurApAccountingLine getFirstAccount() {
297            // loop through items, and pick the first item with non-empty accouting lines
298            if (getItems() != null && !getItems().isEmpty()) {
299                for (Iterator iter = getItems().iterator(); iter.hasNext();) {
300                    PurchaseOrderItem item = (PurchaseOrderItem)iter.next();
301                    if (item.isConsideredEntered() && item.getSourceAccountingLines() != null && !item.getSourceAccountingLines().isEmpty()) {
302                        // accounting lines are not empty so pick the first account
303                        PurApAccountingLine accountingLine = item.getSourceAccountingLine(0);
304                        accountingLine.refreshNonUpdateableReferences();
305                        return accountingLine;
306                    }
307                }
308            }
309            return null;
310        }
311                
312        public String getAssignedUserPrincipalId() {
313            return assignedUserPrincipalId;
314        }
315    
316        public void setAssignedUserPrincipalId(String assignedUserPrincipalId) {
317            this.assignedUserPrincipalId = assignedUserPrincipalId;
318        }
319    
320        public String getAssignedUserPrincipalName() {
321            // init this field when PO is first loaded and assigned user exists in PO
322            if (assignedUserPrincipalName == null && assignedUserPrincipalId != null) {
323                // extra caution in case ref obj didn't get refreshed
324                //if (assignedUser == null)
325                //    this.refreshReferenceObject("assignedUser");
326                Person assignedUser = SpringContext.getBean(PersonService.class).getPerson(assignedUserPrincipalId);
327                this.assignedUserPrincipalName = assignedUser.getPrincipalName();
328            }
329            // otherwise return its current value directly
330            return assignedUserPrincipalName;
331        }
332    
333        public void setAssignedUserPrincipalName(String assignedUserPrincipalName) {
334            this.assignedUserPrincipalName = assignedUserPrincipalName;           
335            // each time this field changes we need to update the assigned user ID and ref obj to keep consistent
336            // this code can be moved to where PO is saved and with validation too, which may be more appropriate
337            Person assignedUser = null;
338            if (assignedUserPrincipalName != null) 
339                assignedUser = SpringContext.getBean(PersonService.class).getPersonByPrincipalName(assignedUserPrincipalName);
340            if (assignedUser != null) 
341                assignedUserPrincipalId = assignedUser.getPrincipalId();        
342            else
343                assignedUserPrincipalId = null;        
344        }
345    
346        /*
347        public Person getAssignedUser() {
348            return assignedUser;
349        }
350    
351        public void setAssignedUser(Person assignedUser) {
352            this.assignedUser = assignedUser;
353        }
354        */
355        
356        /*
357        public String getAssignedUserName() {      
358            // assignedUserPrincipalId is either null or returned from lookup (thus valid)
359            if (StringUtils.isEmpty(assignedUserPrincipalId))
360                return null;  
361            Person person = SpringContext.getBean(PersonService.class).getPersonByPrincipalName(assignedUserPrincipalId);
362            if (person == null)
363                return null;
364            return person.getName();
365        }
366        */
367    
368        public boolean getAssigningSensitiveData() {
369            return assigningSensitiveData;
370        }
371    
372        public void setAssigningSensitiveData(boolean assigningSensitiveData) {
373            this.assigningSensitiveData = assigningSensitiveData;
374        }
375    
376        public List<PurchaseOrderSensitiveData> getPurchaseOrderSensitiveData() {
377            Map fieldValues = new HashMap();
378            fieldValues.put(PurapPropertyConstants.PURAP_DOC_ID, getPurapDocumentIdentifier());
379            return new ArrayList(SpringContext.getBean(BusinessObjectService.class).findMatching(PurchaseOrderSensitiveData.class, fieldValues));
380        }
381    
382        public void setPurchaseOrderSensitiveData(List<PurchaseOrderSensitiveData> purchaseOrderSensitiveData) {
383            this.purchaseOrderSensitiveData = purchaseOrderSensitiveData;
384        }
385        
386        public ContractManager getContractManager() {
387            if (ObjectUtils.isNull(contractManager))
388                refreshReferenceObject(PurapPropertyConstants.CONTRACT_MANAGER);
389            return contractManager;
390        }
391    
392        public void setContractManager(ContractManager contractManager) {
393            this.contractManager = contractManager;
394        }
395    
396        public Integer getContractManagerCode() {
397            return contractManagerCode;
398        }
399    
400        public void setContractManagerCode(Integer contractManagerCode) {
401            this.contractManagerCode = contractManagerCode;
402        }
403    
404        /**
405         * @see org.kuali.kfs.module.purap.document.PurchasingDocumentBase#buildListOfDeletionAwareLists()
406         */
407        @Override
408        public List buildListOfDeletionAwareLists() {
409            List managedLists = super.buildListOfDeletionAwareLists();
410            managedLists.add(this.getGeneralLedgerPendingEntries());
411            if (allowDeleteAwareCollection) {
412                managedLists.add(this.getPurchaseOrderVendorQuotes());
413            }
414            return managedLists;
415        }
416        
417        /**
418         * @see org.kuali.kfs.module.purap.document.PurchasingAccountsPayableDocumentBase#getOverrideWorkflowButtons()
419         */
420        @Override
421        public Boolean getOverrideWorkflowButtons() {
422            if (ObjectUtils.isNull(super.getOverrideWorkflowButtons())) {
423                // should only be null on the first call... never after
424                setOverrideWorkflowButtons(Boolean.TRUE);
425            }
426            return super.getOverrideWorkflowButtons();
427        }
428    
429        /**
430         * @see org.kuali.rice.kns.bo.PersistableBusinessObjectBase#isBoNotesSupport()
431         */
432        @Override
433        public boolean isBoNotesSupport() {
434            return true;
435        }
436    
437        /**
438         * @see org.kuali.kfs.module.purap.document.PurchasingAccountsPayableDocumentBase#customPrepareForSave()
439         */
440        @Override
441        public void customPrepareForSave(KualiDocumentEvent event) {
442            super.customPrepareForSave(event);
443            if (ObjectUtils.isNull(getPurapDocumentIdentifier())) {
444                // need retrieve the next available PO id to save in GL entries (only do if purap id is null which should be on first
445                // save)
446                SequenceAccessorService sas = SpringContext.getBean(SequenceAccessorService.class);
447                Long poSequenceNumber = sas.getNextAvailableSequenceNumber("PO_ID", this.getClass());
448                setPurapDocumentIdentifier(poSequenceNumber.intValue());
449            }
450    
451            // Set outstanding encumbered quantity/amount on items
452            for (Iterator items = this.getItems().iterator(); items.hasNext();) {
453                PurchaseOrderItem item = (PurchaseOrderItem) items.next();
454    
455                // Set quantities
456                item.setItemOutstandingEncumberedQuantity(item.getItemQuantity());
457                if (item.getItemInvoicedTotalQuantity() == null) {
458                    item.setItemInvoicedTotalQuantity(ZERO);
459                }
460                if (item.getItemInvoicedTotalAmount() == null) {
461                    item.setItemInvoicedTotalAmount(ZERO);
462                }
463    
464                // Set amount
465                item.setItemOutstandingEncumberedAmount(item.getTotalAmount() == null ? ZERO : item.getTotalAmount());
466    
467                List accounts = (List) item.getSourceAccountingLines();
468                Collections.sort(accounts);
469    
470                for (Iterator iterator = accounts.iterator(); iterator.hasNext();) {
471                    PurchaseOrderAccount account = (PurchaseOrderAccount) iterator.next();
472                    if (!account.isEmpty()) {
473                        account.setItemAccountOutstandingEncumbranceAmount(account.getAmount());
474                    }
475                }// endfor accounts
476            }// endfor items
477    
478            this.setSourceAccountingLines(SpringContext.getBean(PurapAccountingService.class).generateSummaryWithNoZeroTotals(this.getItems()));
479        }// end customPrepareForSave(KualiDocumentEvent)
480    
481        /**
482         * @see org.kuali.kfs.module.purap.document.PurchasingAccountsPayableDocumentBase#prepareForSave()
483         */
484        @Override
485        public void prepareForSave(KualiDocumentEvent event) {
486            KualiWorkflowDocument workFlowDocument = getDocumentHeader().getWorkflowDocument();
487            String documentType = workFlowDocument.getDocumentType();
488    
489            
490            if ((documentType.equals(PurapConstants.PurchaseOrderDocTypes.PURCHASE_ORDER_DOCUMENT)) ||
491                (documentType.equals(PurapConstants.PurchaseOrderDocTypes.PURCHASE_ORDER_SPLIT_DOCUMENT))) {
492                if (workFlowDocument.stateIsCanceled()) {
493                 // if doc is FINAL or canceled, saving should not be creating GL entries
494                    setGeneralLedgerPendingEntries(new ArrayList());
495                } else if (workFlowDocument.stateIsFinal()) {
496                } else {   
497                    super.prepareForSave(event);
498                }
499            }
500        }
501    
502        /**
503         * Sets default values for APO.
504         */
505        public void setDefaultValuesForAPO() {
506            this.setPurchaseOrderAutomaticIndicator(Boolean.TRUE);
507            if (!RequisitionSources.B2B.equals(this.getRequisitionSourceCode())) {
508                String paramName = PurapParameterConstants.DEFAULT_APO_VENDOR_CHOICE;
509                String paramValue = SpringContext.getBean(ParameterService.class).getParameterValue(PurchaseOrderDocument.class, paramName);
510                this.setPurchaseOrderVendorChoiceCode(paramValue);
511            }
512        }
513    
514        /**
515         * Populates this Purchase Order from the related Requisition Document.
516         * 
517         * @param requisitionDocument the Requisition Document from which field values are copied.
518         */
519        public void populatePurchaseOrderFromRequisition(RequisitionDocument requisitionDocument) {
520            this.setPurchaseOrderCreateTimestamp(SpringContext.getBean(DateTimeService.class).getCurrentTimestamp());
521            this.getDocumentHeader().setOrganizationDocumentNumber(requisitionDocument.getDocumentHeader().getOrganizationDocumentNumber());
522            this.getDocumentHeader().setDocumentDescription(requisitionDocument.getDocumentHeader().getDocumentDescription());
523            this.getDocumentHeader().setExplanation(requisitionDocument.getDocumentHeader().getExplanation());
524    
525            this.setBillingName(requisitionDocument.getBillingName());
526            this.setBillingLine1Address(requisitionDocument.getBillingLine1Address());
527            this.setBillingLine2Address(requisitionDocument.getBillingLine2Address());
528            this.setBillingCityName(requisitionDocument.getBillingCityName());
529            this.setBillingStateCode(requisitionDocument.getBillingStateCode());
530            this.setBillingPostalCode(requisitionDocument.getBillingPostalCode());
531            this.setBillingCountryCode(requisitionDocument.getBillingCountryCode());
532            this.setBillingPhoneNumber(requisitionDocument.getBillingPhoneNumber());
533            
534            this.setReceivingName(requisitionDocument.getReceivingName());
535            this.setReceivingCityName(requisitionDocument.getReceivingCityName());
536            this.setReceivingLine1Address(requisitionDocument.getReceivingLine1Address());
537            this.setReceivingLine2Address(requisitionDocument.getReceivingLine2Address());
538            this.setReceivingStateCode(requisitionDocument.getReceivingStateCode());
539            this.setReceivingPostalCode(requisitionDocument.getReceivingPostalCode());
540            this.setReceivingCountryCode(requisitionDocument.getReceivingCountryCode());
541            this.setAddressToVendorIndicator(requisitionDocument.getAddressToVendorIndicator());
542    
543            this.setDeliveryBuildingCode(requisitionDocument.getDeliveryBuildingCode());
544            this.setDeliveryBuildingRoomNumber(requisitionDocument.getDeliveryBuildingRoomNumber());
545            this.setDeliveryBuildingName(requisitionDocument.getDeliveryBuildingName());
546            this.setDeliveryCampusCode(requisitionDocument.getDeliveryCampusCode());
547            this.setDeliveryCityName(requisitionDocument.getDeliveryCityName());
548            this.setDeliveryCountryCode(requisitionDocument.getDeliveryCountryCode());
549            this.setDeliveryInstructionText(requisitionDocument.getDeliveryInstructionText());
550            this.setDeliveryBuildingLine1Address(requisitionDocument.getDeliveryBuildingLine1Address());
551            this.setDeliveryBuildingLine2Address(requisitionDocument.getDeliveryBuildingLine2Address());
552            this.setDeliveryPostalCode(requisitionDocument.getDeliveryPostalCode());
553            this.setDeliveryRequiredDate(requisitionDocument.getDeliveryRequiredDate());
554            this.setDeliveryRequiredDateReasonCode(requisitionDocument.getDeliveryRequiredDateReasonCode());
555            this.setDeliveryStateCode(requisitionDocument.getDeliveryStateCode());
556            this.setDeliveryToEmailAddress(requisitionDocument.getDeliveryToEmailAddress());
557            this.setDeliveryToName(requisitionDocument.getDeliveryToName());
558            this.setDeliveryToPhoneNumber(requisitionDocument.getDeliveryToPhoneNumber());
559            this.setDeliveryBuildingOtherIndicator(requisitionDocument.isDeliveryBuildingOtherIndicator());
560            
561            this.setPurchaseOrderBeginDate(requisitionDocument.getPurchaseOrderBeginDate());
562            this.setPurchaseOrderCostSourceCode(requisitionDocument.getPurchaseOrderCostSourceCode());
563            this.setPostingYear(requisitionDocument.getPostingYear());
564            this.setPurchaseOrderEndDate(requisitionDocument.getPurchaseOrderEndDate());
565            this.setChartOfAccountsCode(requisitionDocument.getChartOfAccountsCode());
566            this.setDocumentFundingSourceCode(requisitionDocument.getDocumentFundingSourceCode());
567            this.setInstitutionContactEmailAddress(requisitionDocument.getInstitutionContactEmailAddress());
568            this.setInstitutionContactName(requisitionDocument.getInstitutionContactName());
569            this.setInstitutionContactPhoneNumber(requisitionDocument.getInstitutionContactPhoneNumber());
570            this.setNonInstitutionFundAccountNumber(requisitionDocument.getNonInstitutionFundAccountNumber());
571            this.setNonInstitutionFundChartOfAccountsCode(requisitionDocument.getNonInstitutionFundChartOfAccountsCode());
572            this.setNonInstitutionFundOrgChartOfAccountsCode(requisitionDocument.getNonInstitutionFundOrgChartOfAccountsCode());
573            this.setNonInstitutionFundOrganizationCode(requisitionDocument.getNonInstitutionFundOrganizationCode());
574            this.setOrganizationCode(requisitionDocument.getOrganizationCode());
575            this.setRecurringPaymentTypeCode(requisitionDocument.getRecurringPaymentTypeCode());
576            this.setRequestorPersonEmailAddress(requisitionDocument.getRequestorPersonEmailAddress());
577            this.setRequestorPersonName(requisitionDocument.getRequestorPersonName());
578            this.setRequestorPersonPhoneNumber(requisitionDocument.getRequestorPersonPhoneNumber());
579            this.setRequisitionIdentifier(requisitionDocument.getPurapDocumentIdentifier());
580            this.setPurchaseOrderTotalLimit(requisitionDocument.getPurchaseOrderTotalLimit());
581            this.setPurchaseOrderTransmissionMethodCode(requisitionDocument.getPurchaseOrderTransmissionMethodCode());
582            this.setUseTaxIndicator( requisitionDocument.isUseTaxIndicator() );
583            
584            this.setVendorCityName(requisitionDocument.getVendorCityName());
585            this.setVendorContractGeneratedIdentifier(requisitionDocument.getVendorContractGeneratedIdentifier());
586            this.setVendorCountryCode(requisitionDocument.getVendorCountryCode());
587            this.setVendorCustomerNumber(requisitionDocument.getVendorCustomerNumber());
588            this.setVendorAttentionName(requisitionDocument.getVendorAttentionName());
589            this.setVendorDetailAssignedIdentifier(requisitionDocument.getVendorDetailAssignedIdentifier());
590            this.setVendorFaxNumber(requisitionDocument.getVendorFaxNumber());
591            this.setVendorHeaderGeneratedIdentifier(requisitionDocument.getVendorHeaderGeneratedIdentifier());
592            this.setVendorLine1Address(requisitionDocument.getVendorLine1Address());
593            this.setVendorLine2Address(requisitionDocument.getVendorLine2Address());
594            this.setVendorAddressInternationalProvinceName(requisitionDocument.getVendorAddressInternationalProvinceName());
595            this.setVendorName(requisitionDocument.getVendorName());
596            this.setVendorNoteText(requisitionDocument.getVendorNoteText());
597            this.setVendorPhoneNumber(requisitionDocument.getVendorPhoneNumber());
598            this.setVendorPostalCode(requisitionDocument.getVendorPostalCode());
599            this.setVendorStateCode(requisitionDocument.getVendorStateCode());
600            this.setVendorRestrictedIndicator(requisitionDocument.getVendorRestrictedIndicator());
601            
602            this.setExternalOrganizationB2bSupplierIdentifier(requisitionDocument.getExternalOrganizationB2bSupplierIdentifier());
603            this.setRequisitionSourceCode(requisitionDocument.getRequisitionSourceCode());
604            this.setAccountsPayablePurchasingDocumentLinkIdentifier(requisitionDocument.getAccountsPayablePurchasingDocumentLinkIdentifier());
605            this.setReceivingDocumentRequiredIndicator(requisitionDocument.isReceivingDocumentRequiredIndicator());
606            this.setPaymentRequestPositiveApprovalIndicator(requisitionDocument.isPaymentRequestPositiveApprovalIndicator());
607    
608            this.setStatusCode(PurapConstants.PurchaseOrderStatuses.IN_PROCESS);
609    
610            // Copy items from requisition (which will copy the item's accounts and capital assets)
611            List<PurchaseOrderItem> items = new ArrayList();
612            for (PurApItem reqItem : ((PurchasingAccountsPayableDocument)requisitionDocument).getItems()) {
613                RequisitionCapitalAssetItem reqCamsItem = (RequisitionCapitalAssetItem)requisitionDocument.getPurchasingCapitalAssetItemByItemIdentifier(reqItem.getItemIdentifier().intValue());
614                items.add(new PurchaseOrderItem((RequisitionItem)reqItem, this, reqCamsItem));
615            }
616            this.setItems(items);
617            
618            // Copy capital asset information that is directly off the document.
619            this.setCapitalAssetSystemTypeCode(requisitionDocument.getCapitalAssetSystemTypeCode());
620            this.setCapitalAssetSystemStateCode(requisitionDocument.getCapitalAssetSystemStateCode());
621            for (CapitalAssetSystem capitalAssetSystem : requisitionDocument.getPurchasingCapitalAssetSystems()) {
622                this.getPurchasingCapitalAssetSystems().add(new PurchaseOrderCapitalAssetSystem((RequisitionCapitalAssetSystem)capitalAssetSystem));
623            }
624            
625            this.fixItemReferences();
626        }
627    
628        /**
629         * Returns the Vendor Stipulation at the specified index in this Purchase Order.
630         * 
631         * @param index the specified index.
632         * @return the Vendor Stipulation at the specified index.
633         */
634        public PurchaseOrderVendorStipulation getPurchaseOrderVendorStipulation(int index) {
635            while (getPurchaseOrderVendorStipulations().size() <= index) {
636                getPurchaseOrderVendorStipulations().add(new PurchaseOrderVendorStipulation());
637            }
638            return (PurchaseOrderVendorStipulation) purchaseOrderVendorStipulations.get(index);
639        }
640    
641        @Override
642        public List<Long> getWorkflowEngineDocumentIdsToLock() {
643            List<String> docIdStrings = new ArrayList<String>();
644            docIdStrings.add(getDocumentNumber());
645            String currentDocumentTypeName = this.getDocumentHeader().getWorkflowDocument().getDocumentType();
646            
647            List<PurchaseOrderView> relatedPoViews = getRelatedViews().getRelatedPurchaseOrderViews();
648            for (PurchaseOrderView poView : relatedPoViews) {
649                //don't lock related PO's if this is a split PO that's in process
650                if(!(PurapConstants.PurchaseOrderStatuses.IN_PROCESS.equals(this.getStatusCode()) && PurapConstants.PurchaseOrderDocTypes.PURCHASE_ORDER_SPLIT_DOCUMENT.equals(currentDocumentTypeName))){
651                    docIdStrings.add(poView.getDocumentNumber());
652                }
653            }
654            
655            //  convert our easy to use List<String> to a Long[]
656            List<Long> docIds = new ArrayList<Long>();
657            for (int i = 0; i < docIdStrings.size(); i++) {
658                docIds.add(new Long(docIdStrings.get(i)));
659            }
660            LOG.info("***** getWorkflowEngineDocumentIdsToLock(" + this.documentNumber + ") = '" + printList(docIds) + "'");
661            return docIds;
662        }
663        
664        // Only used for debugging in the getWorkflowEngineDocumentIdsToLock above
665        protected String printList(List<Long> docIds) {
666            StringBuffer sb = new StringBuffer("[");
667            for (int i = 0; i < docIds.size(); i++) {
668                sb.append(new Long(docIds.get(i)).toString() + ",");
669            }
670            return sb.append("]").toString();
671        }
672        
673        /**
674         * @see org.kuali.kfs.sys.document.GeneralLedgerPostingDocumentBase#doRouteStatusChange()
675         */
676        @Override
677        public void doRouteStatusChange(DocumentRouteStatusChangeDTO statusChangeEvent) {
678            LOG.debug("doRouteStatusChange() started");
679            super.doRouteStatusChange(statusChangeEvent);
680            String currentDocumentTypeName = this.getDocumentHeader().getWorkflowDocument().getDocumentType();
681            // child classes need to call super, but we don't want to inherit the post-processing done by this PO class other than to the Split
682            if (PurapConstants.PurchaseOrderDocTypes.PURCHASE_ORDER_DOCUMENT.equals(currentDocumentTypeName) || PurapConstants.PurchaseOrderDocTypes.PURCHASE_ORDER_SPLIT_DOCUMENT.equals(currentDocumentTypeName)) { 
683                try {
684                    // DOCUMENT PROCESSED
685                    if (getDocumentHeader().getWorkflowDocument().stateIsProcessed()) {
686                        SpringContext.getBean(PurchaseOrderService.class).completePurchaseOrder(this);
687                    }
688                    // DOCUMENT DISAPPROVED
689                    else if (getDocumentHeader().getWorkflowDocument().stateIsDisapproved()) {
690                        String nodeName = SpringContext.getBean(WorkflowDocumentService.class).getCurrentRouteLevelName(getDocumentHeader().getWorkflowDocument());
691                        NodeDetails currentNode = NodeDetailEnum.getNodeDetailEnumByName(nodeName);
692                        if (ObjectUtils.isNotNull(currentNode)) {
693                            if (StringUtils.isNotBlank(currentNode.getDisapprovedStatusCode())) {
694                                SpringContext.getBean(PurapService.class).updateStatus(this, currentNode.getDisapprovedStatusCode());
695                                SpringContext.getBean(PurapService.class).saveDocumentNoValidation(this);
696                                RequisitionDocument req = getPurApSourceDocumentIfPossible();
697                                appSpecificRouteDocumentToUser(getDocumentHeader().getWorkflowDocument(), req.getDocumentHeader().getWorkflowDocument().getRoutedByUserNetworkId(), "Notification of Order Disapproval for Requisition " + req.getPurapDocumentIdentifier() + "(document id " + req.getDocumentNumber() + ")", "Requisition Routed By User");
698                                return;
699                            }
700                        }
701                        logAndThrowRuntimeException("No status found to set for document being disapproved in node '" + nodeName + "'");
702                    }
703                    // DOCUMENT CANCELED
704                    else if (getDocumentHeader().getWorkflowDocument().stateIsCanceled()) {
705                         SpringContext.getBean(PurapService.class).updateStatus(this, PurchaseOrderStatuses.CANCELLED);
706                        SpringContext.getBean(PurapService.class).saveDocumentNoValidation(this);
707                    }
708                }
709                catch (WorkflowException e) {
710                    logAndThrowRuntimeException("Error saving routing data while saving document with id " + getDocumentNumber(), e);
711                }
712            }
713        }
714    
715        /**
716         * Returns the name of the current route node.
717         * 
718         * @param wd the current workflow document.
719         * @return the name of the current route node.
720         * @throws WorkflowException
721         */
722        protected String getCurrentRouteNodeName(KualiWorkflowDocument wd) throws WorkflowException {
723            String[] nodeNames = wd.getNodeNames();
724            if ((nodeNames == null) || (nodeNames.length == 0)) {
725                return null;
726            }
727            else {
728                return nodeNames[0];
729            }
730        }
731    
732        /**
733         * Sends FYI workflow request to the given user on this document.
734         * 
735         * @param workflowDocument the associated workflow document.
736         * @param userNetworkId the network ID of the user to be sent to.
737         * @param annotation the annotation notes contained in this document.
738         * @param responsibility the responsibility specified in the request.
739         * @throws WorkflowException
740         */
741        public void appSpecificRouteDocumentToUser(KualiWorkflowDocument workflowDocument, String userNetworkId, String annotation, String responsibility) throws WorkflowException {
742            if (ObjectUtils.isNotNull(workflowDocument)) {
743                String annotationNote = (ObjectUtils.isNull(annotation)) ? "" : annotation;
744                String responsibilityNote = (ObjectUtils.isNull(responsibility)) ? "" : responsibility;
745                String currentNodeName = getCurrentRouteNodeName(workflowDocument);
746                KimPrincipal principal = SpringContext.getBean(IdentityManagementService.class).getPrincipalByPrincipalName(userNetworkId);
747                workflowDocument.adHocRouteDocumentToPrincipal(KEWConstants.ACTION_REQUEST_FYI_REQ, currentNodeName, annotationNote, principal.getPrincipalId(), responsibilityNote, true);
748            }
749        }
750    
751        /**
752         * @see org.kuali.rice.kns.document.DocumentBase#handleRouteLevelChange(org.kuali.rice.kew.clientapp.vo.DocumentRouteLevelChangeDTO)
753         */
754        @Override
755        public void doRouteLevelChange(DocumentRouteLevelChangeDTO levelChangeEvent) {
756            LOG.debug("handleRouteLevelChange() started");
757            super.doRouteLevelChange(levelChangeEvent);
758    
759            LOG.debug("handleRouteLevelChange() started");
760            String newNodeName = levelChangeEvent.getNewNodeName();
761            if (StringUtils.isNotBlank(newNodeName)) {
762                ReportCriteriaDTO reportCriteriaDTO = new ReportCriteriaDTO(Long.valueOf(getDocumentNumber()));
763                reportCriteriaDTO.setTargetNodeName(newNodeName);
764                try {
765                    NodeDetails newNodeDetails = NodeDetailEnum.getNodeDetailEnumByName(newNodeName);
766                    if (ObjectUtils.isNotNull(newNodeDetails)) {
767                        String newStatusCode = newNodeDetails.getAwaitingStatusCode();
768                        if (StringUtils.isNotBlank(newStatusCode)) {
769                            if (SpringContext.getBean(KualiWorkflowInfo.class).documentWillHaveAtLeastOneActionRequest(reportCriteriaDTO, new String[] { KEWConstants.ACTION_REQUEST_APPROVE_REQ, KEWConstants.ACTION_REQUEST_COMPLETE_REQ }, false)) {
770                                // if an approve or complete request will be created then we need to set the status as awaiting for
771                                // the new node
772                                SpringContext.getBean(PurapService.class).updateStatus(this, newStatusCode);
773                                
774                                SpringContext.getBean(PurapService.class).saveDocumentNoValidation(this);
775                            }
776                        }
777                    }
778                }
779                catch (WorkflowException e) {
780                    String errorMsg = "Workflow Error found checking actions requests on document with id " + getDocumentNumber() + ". *** WILL NOT UPDATE PURAP STATUS ***";
781                    LOG.warn(errorMsg, e);
782                }
783            }
784        }
785    
786        /**
787         * @see org.kuali.rice.kns.document.DocumentBase#doActionTaken(org.kuali.rice.kew.clientapp.vo.ActionTakenEventDTO)
788         */
789        @Override
790        public void doActionTaken(ActionTakenEventDTO event) {
791            super.doActionTaken(event);
792            // additional processing
793        }
794    
795        /**
796         * Gets the active items in this Purchase Order.
797         * 
798         * @return the list of all active items in this Purchase Order.
799         */
800        public List getItemsActiveOnly() {
801            List returnList = new ArrayList();
802            for (Iterator iter = getItems().iterator(); iter.hasNext();) {
803                PurchaseOrderItem item = (PurchaseOrderItem) iter.next();
804                if (item.isItemActiveIndicator()) {
805                    returnList.add(item);
806                }
807            }
808            return returnList;
809        }
810    
811        /**
812         * Gets the active items in this Purchase Order, and sets up the alternate amount for GL entry creation.
813         * 
814         * @return the list of all active items in this Purchase Order.
815         */
816        public List getItemsActiveOnlySetupAlternateAmount() {
817            List returnList = new ArrayList();
818            for (Iterator iter = getItems().iterator(); iter.hasNext();) {
819                PurchaseOrderItem item = (PurchaseOrderItem) iter.next();
820                if (item.isItemActiveIndicator()) {
821                    for (Iterator iterator = item.getSourceAccountingLines().iterator(); iterator.hasNext();) {
822                        PurchaseOrderAccount account = (PurchaseOrderAccount) iterator.next();
823                        account.setAlternateAmountForGLEntryCreation(account.getItemAccountOutstandingEncumbranceAmount());
824                    }
825                    returnList.add(item);
826                }
827            }
828            return returnList;
829        }
830    
831        public Integer getAlternateVendorDetailAssignedIdentifier() {
832            return alternateVendorDetailAssignedIdentifier;
833        }
834    
835        public void setAlternateVendorDetailAssignedIdentifier(Integer alternateVendorDetailAssignedIdentifier) {
836            this.alternateVendorDetailAssignedIdentifier = alternateVendorDetailAssignedIdentifier;
837        }
838    
839        public Integer getAlternateVendorHeaderGeneratedIdentifier() {
840            return alternateVendorHeaderGeneratedIdentifier;
841        }
842    
843        public void setAlternateVendorHeaderGeneratedIdentifier(Integer alternateVendorHeaderGeneratedIdentifier) {
844            this.alternateVendorHeaderGeneratedIdentifier = alternateVendorHeaderGeneratedIdentifier;
845        }
846    
847        public String getAlternateVendorName() {
848            return alternateVendorName;
849        }
850    
851        public void setAlternateVendorName(String alternateVendorName) {
852            this.alternateVendorName = alternateVendorName;
853        }
854    
855        public KualiDecimal getFinalPaymentAmount() {
856            return finalPaymentAmount;
857        }
858    
859        public void setFinalPaymentAmount(KualiDecimal finalPaymentAmount) {
860            this.finalPaymentAmount = finalPaymentAmount;
861        }
862    
863        public Date getFinalPaymentDate() {
864            return finalPaymentDate;
865        }
866    
867        public void setFinalPaymentDate(Date finalPaymentDate) {
868            this.finalPaymentDate = finalPaymentDate;
869        }
870    
871        public KualiDecimal getInitialPaymentAmount() {
872            return initialPaymentAmount;
873        }
874    
875        public void setInitialPaymentAmount(KualiDecimal initialPaymentAmount) {
876            this.initialPaymentAmount = initialPaymentAmount;
877        }
878    
879        public Date getInitialPaymentDate() {
880            return initialPaymentDate;
881        }
882    
883        public void setInitialPaymentDate(Date initialPaymentDate) {
884            this.initialPaymentDate = initialPaymentDate;
885        }
886    
887        public String getPurchaseOrderCommodityDescription() {
888            return purchaseOrderCommodityDescription;
889        }
890    
891        public void setPurchaseOrderCommodityDescription(String purchaseOrderCommodityDescription) {
892            this.purchaseOrderCommodityDescription = purchaseOrderCommodityDescription;
893        }
894    
895        public boolean isPurchaseOrderConfirmedIndicator() {
896            return purchaseOrderConfirmedIndicator;
897        }
898    
899        public void setPurchaseOrderConfirmedIndicator(boolean purchaseOrderConfirmedIndicator) {
900            this.purchaseOrderConfirmedIndicator = purchaseOrderConfirmedIndicator;
901        }
902    
903        public Timestamp getPurchaseOrderCreateTimestamp() {
904            return purchaseOrderCreateTimestamp;
905        }
906    
907        public void setPurchaseOrderCreateTimestamp(Timestamp purchaseOrderCreateTimestamp) {
908            this.purchaseOrderCreateTimestamp = purchaseOrderCreateTimestamp;
909        }
910    
911        public Timestamp getPurchaseOrderInitialOpenTimestamp() {
912            return purchaseOrderInitialOpenTimestamp;
913        }
914    
915        public void setPurchaseOrderInitialOpenTimestamp(Timestamp purchaseOrderInitialOpenDate) {
916            this.purchaseOrderInitialOpenTimestamp = purchaseOrderInitialOpenDate;
917        }
918    
919        public Timestamp getPurchaseOrderLastTransmitTimestamp() {
920            return purchaseOrderLastTransmitTimestamp;
921        }
922    
923        public void setPurchaseOrderLastTransmitTimestamp(Timestamp PurchaseOrderLastTransmitTimestamp) {
924            this.purchaseOrderLastTransmitTimestamp = PurchaseOrderLastTransmitTimestamp;
925        }
926    
927        public Integer getPurchaseOrderPreviousIdentifier() {
928            return purchaseOrderPreviousIdentifier;
929        }
930    
931        public void setPurchaseOrderPreviousIdentifier(Integer purchaseOrderPreviousIdentifier) {
932            this.purchaseOrderPreviousIdentifier = purchaseOrderPreviousIdentifier;
933        }
934    
935        public Date getPurchaseOrderQuoteDueDate() {
936            return purchaseOrderQuoteDueDate;
937        }
938    
939        public void setPurchaseOrderQuoteDueDate(Date purchaseOrderQuoteDueDate) {
940            this.purchaseOrderQuoteDueDate = purchaseOrderQuoteDueDate;
941        }
942    
943        public String getPurchaseOrderQuoteTypeDescription() {
944            String descript = purchaseOrderQuoteTypeCode;
945            if (PurapConstants.QuoteTypes.COMPETITIVE.equals(purchaseOrderQuoteTypeCode)) {
946                descript = QuoteTypeDescriptions.COMPETITIVE;
947            }
948            else if (PurapConstants.QuoteTypes.PRICE_CONFIRMATION.equals(purchaseOrderQuoteTypeCode)){
949                descript = QuoteTypeDescriptions.PRICE_CONFIRMATION;
950            }
951            return descript;
952        }
953    
954        public String getPurchaseOrderQuoteTypeCode() {
955            return purchaseOrderQuoteTypeCode;
956        }
957    
958        public void setPurchaseOrderQuoteTypeCode(String purchaseOrderQuoteTypeCode) {
959            this.purchaseOrderQuoteTypeCode = purchaseOrderQuoteTypeCode;
960        }
961    
962        public String getPurchaseOrderQuoteVendorNoteText() {
963            return purchaseOrderQuoteVendorNoteText;
964        }
965    
966        public void setPurchaseOrderQuoteVendorNoteText(String purchaseOrderQuoteVendorNoteText) {
967            this.purchaseOrderQuoteVendorNoteText = purchaseOrderQuoteVendorNoteText;
968        }
969    
970        public String getPurchaseOrderVendorChoiceCode() {
971            return purchaseOrderVendorChoiceCode;
972        }
973    
974        public void setPurchaseOrderVendorChoiceCode(String purchaseOrderVendorChoiceCode) {
975            this.purchaseOrderVendorChoiceCode = purchaseOrderVendorChoiceCode;
976        }
977    
978        public KualiDecimal getRecurringPaymentAmount() {
979            return recurringPaymentAmount;
980        }
981    
982        public void setRecurringPaymentAmount(KualiDecimal recurringPaymentAmount) {
983            this.recurringPaymentAmount = recurringPaymentAmount;
984        }
985    
986        public Date getRecurringPaymentDate() {
987            return recurringPaymentDate;
988        }
989    
990        public void setRecurringPaymentDate(Date recurringPaymentDate) {
991            this.recurringPaymentDate = recurringPaymentDate;
992        }
993    
994        public String getRecurringPaymentFrequencyCode() {
995            return recurringPaymentFrequencyCode;
996        }
997    
998        public void setRecurringPaymentFrequencyCode(String recurringPaymentFrequencyCode) {
999            this.recurringPaymentFrequencyCode = recurringPaymentFrequencyCode;
1000        }
1001    
1002        public Integer getRequisitionIdentifier() {
1003            return requisitionIdentifier;
1004        }
1005    
1006        public void setRequisitionIdentifier(Integer requisitionIdentifier) {
1007            this.requisitionIdentifier = requisitionIdentifier;
1008        }
1009    
1010        public PurchaseOrderVendorChoice getPurchaseOrderVendorChoice() {
1011            return purchaseOrderVendorChoice;
1012        }
1013    
1014        public void setPurchaseOrderVendorChoice(PurchaseOrderVendorChoice purchaseOrderVendorChoice) {
1015            this.purchaseOrderVendorChoice = purchaseOrderVendorChoice;
1016        }
1017    
1018        public RecurringPaymentFrequency getRecurringPaymentFrequency() {
1019            return recurringPaymentFrequency;
1020        }
1021    
1022        public void setRecurringPaymentFrequency(RecurringPaymentFrequency recurringPaymentFrequency) {
1023            this.recurringPaymentFrequency = recurringPaymentFrequency;
1024        }
1025    
1026        public PaymentTermType getVendorPaymentTerms() {
1027            return vendorPaymentTerms;
1028        }
1029    
1030        public void setVendorPaymentTerms(PaymentTermType vendorPaymentTerms) {
1031            this.vendorPaymentTerms = vendorPaymentTerms;
1032        }
1033    
1034        public ShippingPaymentTerms getVendorShippingPaymentTerms() {
1035            return vendorShippingPaymentTerms;
1036        }
1037    
1038        public void setVendorShippingPaymentTerms(ShippingPaymentTerms vendorShippingPaymentTerms) {
1039            this.vendorShippingPaymentTerms = vendorShippingPaymentTerms;
1040        }
1041    
1042        public ShippingTitle getVendorShippingTitle() {
1043            
1044            if( ObjectUtils.isNull(vendorShippingTitle) ){
1045                this.refreshReferenceObject("vendorShippingTitle");
1046            }
1047    
1048            return vendorShippingTitle;
1049        }
1050    
1051        public void setVendorShippingTitle(ShippingTitle vendorShippingTitle) {
1052            this.vendorShippingTitle = vendorShippingTitle;
1053        }
1054    
1055        public List getPurchaseOrderVendorStipulations() {
1056            return purchaseOrderVendorStipulations;
1057        }
1058    
1059        public String getStatusChange() {
1060            return statusChange;
1061        }
1062    
1063        public void setPurchaseOrderVendorStipulations(List purchaseOrderVendorStipulations) {
1064            this.purchaseOrderVendorStipulations = purchaseOrderVendorStipulations;
1065        }
1066    
1067        public List<PurchaseOrderVendorQuote> getPurchaseOrderVendorQuotes() {
1068            return purchaseOrderVendorQuotes;
1069        }
1070    
1071        public void setPurchaseOrderVendorQuotes(List<PurchaseOrderVendorQuote> purchaseOrderVendorQuotes) {
1072            this.purchaseOrderVendorQuotes = purchaseOrderVendorQuotes;
1073        }
1074    
1075        public PurchaseOrderVendorQuote getPurchaseOrderVendorQuote(int index) {
1076            while (getPurchaseOrderVendorQuotes().size() <= index) {
1077                getPurchaseOrderVendorQuotes().add(new PurchaseOrderVendorQuote());
1078            }
1079            return (PurchaseOrderVendorQuote) purchaseOrderVendorQuotes.get(index);
1080        }
1081    
1082        public void setStatusChange(String statusChange) {
1083            this.statusChange = statusChange;
1084        }
1085    
1086        public String getPurchaseOrderRetransmissionMethodCode() {
1087            return purchaseOrderRetransmissionMethodCode;
1088        }
1089    
1090        public void setPurchaseOrderRetransmissionMethodCode(String purchaseOrderRetransmissionMethodCode) {
1091            this.purchaseOrderRetransmissionMethodCode = purchaseOrderRetransmissionMethodCode;
1092        }
1093    
1094        public String getRetransmitHeader() {
1095            return retransmitHeader;
1096        }
1097    
1098        public void setRetransmitHeader(String retransmitHeader) {
1099            this.retransmitHeader = retransmitHeader;
1100        }
1101    
1102        public boolean isPendingActionIndicator() {
1103            return pendingActionIndicator;
1104        }
1105    
1106        public void setPendingActionIndicator(boolean pendingActionIndicator) {
1107            this.pendingActionIndicator = pendingActionIndicator;
1108        }
1109    
1110        public boolean isPurchaseOrderCurrentIndicator() {
1111            return purchaseOrderCurrentIndicator;
1112        }
1113    
1114        public void setPurchaseOrderCurrentIndicator(boolean purchaseOrderCurrentIndicator) {
1115            this.purchaseOrderCurrentIndicator = purchaseOrderCurrentIndicator;
1116        }
1117        
1118        public Timestamp getPurchaseOrderFirstTransmissionTimestamp() {
1119            return purchaseOrderFirstTransmissionTimestamp;
1120        }
1121    
1122        public void setPurchaseOrderFirstTransmissionTimestamp(Timestamp purchaseOrderFirstTransmissionTimestamp) {
1123            this.purchaseOrderFirstTransmissionTimestamp = purchaseOrderFirstTransmissionTimestamp;
1124        }
1125    
1126        /**
1127         * Gets the purchaseOrderQuoteAwardedDate attribute. 
1128         * @return Returns the purchaseOrderQuoteAwardedDate.
1129         */
1130        public Date getPurchaseOrderQuoteAwardedDate() {
1131            return purchaseOrderQuoteAwardedDate;
1132        }
1133    
1134        /**
1135         * Sets the purchaseOrderQuoteAwardedDate attribute value.
1136         * @param purchaseOrderQuoteAwardedDate The purchaseOrderQuoteAwardedDate to set.
1137         */
1138        public void setPurchaseOrderQuoteAwardedDate(Date purchaseOrderQuoteAwardedDate) {
1139            this.purchaseOrderQuoteAwardedDate = purchaseOrderQuoteAwardedDate;
1140        }
1141    
1142        /**
1143         * Gets the purchaseOrderQuoteInitializationDate attribute. 
1144         * @return Returns the purchaseOrderQuoteInitializationDate.
1145         */
1146        public Date getPurchaseOrderQuoteInitializationDate() {
1147            return purchaseOrderQuoteInitializationDate;
1148        }
1149    
1150        /**
1151         * Sets the purchaseOrderQuoteInitializationDate attribute value.
1152         * @param purchaseOrderQuoteInitializationDate The purchaseOrderQuoteInitializationDate to set.
1153         */
1154        public void setPurchaseOrderQuoteInitializationDate(Date purchaseOrderQuoteInitializationDate) {
1155            this.purchaseOrderQuoteInitializationDate = purchaseOrderQuoteInitializationDate;
1156        }
1157    
1158        /**
1159         * Gets the alternateVendorNumber attribute.
1160         * 
1161         * @return Returns the alternateVendorNumber.
1162         */
1163        public String getAlternateVendorNumber() {
1164            String hdrGenId = "";
1165            String detAssgndId = "";
1166            String vendorNumber = "";
1167            if (this.alternateVendorHeaderGeneratedIdentifier != null) {
1168                hdrGenId = this.alternateVendorHeaderGeneratedIdentifier.toString();
1169            }
1170            if (this.alternateVendorDetailAssignedIdentifier != null) {
1171                detAssgndId = this.alternateVendorDetailAssignedIdentifier.toString();
1172            }
1173            if (!StringUtils.isEmpty(hdrGenId) && !StringUtils.isEmpty(detAssgndId)) {
1174                vendorNumber = hdrGenId + "-" + detAssgndId;
1175            }
1176            return vendorNumber;
1177        }
1178    
1179        /**
1180         * Sets the alternateVendorNumber attribute value.
1181         * 
1182         * @param alternateVendorNumber The vendorNumber to set.
1183         */
1184        public void setAlternateVendorNumber(String vendorNumber) {
1185            if (!StringUtils.isEmpty(vendorNumber)) {
1186                int dashInd = vendorNumber.indexOf("-");
1187                if (vendorNumber.length() >= dashInd) {
1188                    String vndrHdrGenId = vendorNumber.substring(0, dashInd);
1189                    String vndrDetailAssgnedId = vendorNumber.substring(dashInd + 1);
1190                    if (!StringUtils.isEmpty(vndrHdrGenId) && !StringUtils.isEmpty(vndrDetailAssgnedId)) {
1191                        this.alternateVendorHeaderGeneratedIdentifier = new Integer(vndrHdrGenId);
1192                        this.alternateVendorDetailAssignedIdentifier = new Integer(vndrDetailAssgnedId);
1193                    }
1194                }
1195            }
1196            else {
1197                this.alternateVendorNumber = vendorNumber;
1198            }
1199        }
1200    
1201        /**
1202         * Sets alternate vendor fields based on a given VendorDetail.
1203         * 
1204         * @param vendorDetail the vendor detail used to set vendor fields.
1205         */
1206        public void templateAlternateVendor(VendorDetail vendorDetail) {
1207            if (vendorDetail == null) {
1208                return;
1209            }
1210            this.setAlternateVendorNumber(vendorDetail.getVendorHeaderGeneratedIdentifier() + VendorConstants.DASH + vendorDetail.getVendorDetailAssignedIdentifier());
1211            this.setAlternateVendorName(vendorDetail.getVendorName());
1212        }
1213    
1214        /**
1215         * Overriding this from the super class so that Note will use only the oldest PurchaseOrderDocument as the
1216         * documentBusinessObject.
1217         * 
1218         * @see org.kuali.rice.kns.document.Document#getDocumentBusinessObject()
1219         */
1220        @Override
1221        public PersistableBusinessObject getDocumentBusinessObject() {
1222            if (ObjectUtils.isNotNull(getPurapDocumentIdentifier()) && ((ObjectUtils.isNull(documentBusinessObject)) || ObjectUtils.isNull(((PurchaseOrderDocument) documentBusinessObject).getPurapDocumentIdentifier()))) {
1223                refreshDocumentBusinessObject();
1224            }
1225            else if (ObjectUtils.isNull(getPurapDocumentIdentifier()) && ObjectUtils.isNull(documentBusinessObject)) {
1226                // needed to keep populate happy
1227                documentBusinessObject = new PurchaseOrderDocument();
1228            }
1229            return documentBusinessObject;
1230        }
1231    
1232        public void refreshDocumentBusinessObject() {
1233            documentBusinessObject = SpringContext.getBean(PurchaseOrderService.class).getOldestPurchaseOrder(this, (PurchaseOrderDocument) this.documentBusinessObject);
1234        }
1235    
1236        public void setDocumentBusinessObject(PurchaseOrderDocument po) {
1237            documentBusinessObject = po;
1238        }
1239    
1240        /**
1241         * @see org.kuali.kfs.module.purap.document.PurchasingAccountsPayableDocumentBase#getItemClass()
1242         */
1243        @Override
1244        public Class getItemClass() {
1245            return PurchaseOrderItem.class;
1246        }
1247    
1248        @Override
1249        public Class getItemUseTaxClass() {
1250            return PurchaseOrderItemUseTax.class;
1251        }
1252    
1253        /**
1254         * @see org.kuali.kfs.module.purap.document.PurchasingAccountsPayableDocumentBase#getPurApSourceDocumentIfPossible()
1255         */
1256        @Override
1257        public RequisitionDocument getPurApSourceDocumentIfPossible() {
1258            RequisitionDocument sourceDoc = null;
1259            if (ObjectUtils.isNotNull(getRequisitionIdentifier())) {
1260                sourceDoc = SpringContext.getBean(RequisitionService.class).getRequisitionById(getRequisitionIdentifier());
1261            }
1262            return sourceDoc;
1263        }
1264    
1265        /**
1266         * @see org.kuali.kfs.module.purap.document.PurchasingAccountsPayableDocumentBase#getPurApSourceDocumentLabelIfPossible()
1267         */
1268        @Override
1269        public String getPurApSourceDocumentLabelIfPossible() {
1270            return SpringContext.getBean(DataDictionaryService.class).getDocumentLabelByTypeName(KFSConstants.FinancialDocumentTypeCodes.REQUISITION);
1271        }
1272    
1273        public Integer getNewQuoteVendorDetailAssignedIdentifier() {
1274            return newQuoteVendorDetailAssignedIdentifier;
1275        }
1276    
1277        public void setNewQuoteVendorDetailAssignedIdentifier(Integer newQuoteVendorDetailAssignedIdentifier) {
1278            this.newQuoteVendorDetailAssignedIdentifier = newQuoteVendorDetailAssignedIdentifier;
1279        }
1280    
1281        public Integer getNewQuoteVendorHeaderGeneratedIdentifier() {
1282            return newQuoteVendorHeaderGeneratedIdentifier;
1283        }
1284    
1285        public void setNewQuoteVendorHeaderGeneratedIdentifier(Integer newQuoteVendorHeaderGeneratedIdentifier) {
1286            this.newQuoteVendorHeaderGeneratedIdentifier = newQuoteVendorHeaderGeneratedIdentifier;
1287        }
1288    
1289        public Integer getPurchaseOrderQuoteListIdentifier() {
1290            return purchaseOrderQuoteListIdentifier;
1291        }
1292    
1293        public void setPurchaseOrderQuoteListIdentifier(Integer purchaseOrderQuoteListIdentifier) {
1294            this.purchaseOrderQuoteListIdentifier = purchaseOrderQuoteListIdentifier;
1295        }
1296    
1297        /**
1298         * Returns true if a vendor has been awarded for this Purchase Order.
1299         * 
1300         * @return true if a vendor has been awarded for this Purchase Order.
1301         */
1302        public boolean isPurchaseOrderAwarded() {
1303            return (getAwardedVendorQuote() != null);
1304        }
1305    
1306        /**
1307         * Returns the quote from the awarded vendor.
1308         * 
1309         * @return the quote from the awarded vendor.
1310         */
1311        public PurchaseOrderVendorQuote getAwardedVendorQuote() {
1312            for (PurchaseOrderVendorQuote vendorQuote : purchaseOrderVendorQuotes) {
1313                if (vendorQuote.getPurchaseOrderQuoteAwardTimestamp() != null) {
1314                    return vendorQuote;
1315                }
1316            }
1317            return null;
1318        }
1319    
1320        /**
1321         * @see org.kuali.kfs.module.purap.document.PurchasingDocumentBase#getTotalDollarAmount()
1322         */
1323        @Override
1324        public KualiDecimal getTotalDollarAmount() {
1325            // return total without inactive and with below the line
1326            return getTotalDollarAmount(false, true);
1327        }
1328    
1329        /**
1330         * @see org.kuali.kfs.module.purap.document.PurchasingAccountsPayableDocumentBase#getTotalDollarAmountAboveLineItems()
1331         */
1332        @Override
1333        public KualiDecimal getTotalDollarAmountAboveLineItems() {
1334            return getTotalDollarAmount(false, false);
1335        }
1336    
1337        /**
1338         * Gets the total dollar amount for this Purchase Order.
1339         * 
1340         * @param includeInactive indicates whether inactive items shall be included.
1341         * @param includeBelowTheLine indicates whether below the line items shall be included.
1342         * @return the total dollar amount for this Purchase Order.
1343         */
1344        public KualiDecimal getTotalDollarAmount(boolean includeInactive, boolean includeBelowTheLine) {
1345            KualiDecimal total = new KualiDecimal(BigDecimal.ZERO);
1346            for (PurApItem item : (List<PurApItem>) getItems()) {
1347                
1348                if (item.getPurapDocument() == null) {
1349                    item.setPurapDocument(this);
1350                }
1351                ItemType it = item.getItemType();
1352                if ((includeBelowTheLine || it.isLineItemIndicator()) && (includeInactive || PurApItemUtils.checkItemActive(item))) {
1353                    KualiDecimal totalAmount = item.getTotalAmount();
1354                    KualiDecimal itemTotal = (totalAmount != null) ? totalAmount : KualiDecimal.ZERO;
1355                    total = total.add(itemTotal);
1356                }
1357            }
1358            return total;
1359        }
1360    
1361        /**
1362         * @see org.kuali.kfs.module.purap.document.PurchasingAccountsPayableDocumentBase#getTotalPreTaxDollarAmount()
1363         */
1364        @Override
1365        public KualiDecimal getTotalPreTaxDollarAmount() {
1366            // return total without inactive and with below the line
1367            return getTotalPreTaxDollarAmount(false, true);
1368        }
1369    
1370        /**
1371         * @see org.kuali.kfs.module.purap.document.PurchasingAccountsPayableDocumentBase#getTotalPreTaxDollarAmountAboveLineItems()
1372         */
1373        @Override
1374        public KualiDecimal getTotalPreTaxDollarAmountAboveLineItems() {
1375            return getTotalPreTaxDollarAmount(false, false);
1376        }
1377    
1378        /**
1379         * Gets the pre tax total dollar amount for this Purchase Order.
1380         * 
1381         * @param includeInactive indicates whether inactive items shall be included.
1382         * @param includeBelowTheLine indicates whether below the line items shall be included.
1383         * @return the total dollar amount for this Purchase Order.
1384         */
1385        public KualiDecimal getTotalPreTaxDollarAmount(boolean includeInactive, boolean includeBelowTheLine) {
1386            KualiDecimal total = new KualiDecimal(BigDecimal.ZERO);
1387            for (PurchaseOrderItem item : (List<PurchaseOrderItem>) getItems()) {
1388                ItemType it = item.getItemType();
1389                if ((includeBelowTheLine || it.isLineItemIndicator()) && (includeInactive || item.isItemActiveIndicator())) {
1390                    KualiDecimal extendedPrice = item.getExtendedPrice();
1391                    KualiDecimal itemTotal = (extendedPrice != null) ? extendedPrice : KualiDecimal.ZERO;
1392                    total = total.add(itemTotal);
1393                }
1394            }
1395            return total;
1396        }
1397    
1398        
1399        @Override
1400        public KualiDecimal getTotalTaxAmount() {
1401            // return total without inactive and with below the line
1402            return getTotalTaxAmount(false, true);
1403        }
1404    
1405        @Override
1406        public KualiDecimal getTotalTaxAmountAboveLineItems() {
1407            return getTotalTaxAmount(false, false);
1408        }
1409    
1410        /**
1411         * Gets the tax total amount for this Purchase Order.
1412         * 
1413         * @param includeInactive indicates whether inactive items shall be included.
1414         * @param includeBelowTheLine indicates whether below the line items shall be included.
1415         * @return the total dollar amount for this Purchase Order.
1416         */
1417        public KualiDecimal getTotalTaxAmount(boolean includeInactive, boolean includeBelowTheLine) {
1418            KualiDecimal total = new KualiDecimal(BigDecimal.ZERO);
1419            for (PurchaseOrderItem item : (List<PurchaseOrderItem>) getItems()) {
1420                ItemType it = item.getItemType();
1421                if ((includeBelowTheLine || it.isLineItemIndicator()) && (includeInactive || item.isItemActiveIndicator())) {
1422                    KualiDecimal taxAmount = item.getItemTaxAmount();
1423                    KualiDecimal itemTotal = (taxAmount != null) ? taxAmount : KualiDecimal.ZERO;
1424                    total = total.add(itemTotal);
1425                }
1426            }
1427            return total;
1428        }
1429    
1430        /**
1431         * Returns true if this Purchase Order contains unpaid items in the Payment Request or Credit Memo.
1432         * 
1433         * @return true if this Purchase Order contains unpaid items in the Payment Request or Credit Memo.
1434         */
1435        public boolean getContainsUnpaidPaymentRequestsOrCreditMemos() {
1436            if (getRelatedViews().getRelatedPaymentRequestViews() != null) {
1437                for (PaymentRequestView element : getRelatedViews().getRelatedPaymentRequestViews()) {
1438                    // If the PREQ is neither cancelled nor voided, check whether the PREQ has been paid.
1439                    // If it has not been paid, then this method will return true.
1440                    if (!PurapConstants.PaymentRequestStatuses.CANCELLED_STATUSES.contains(element.getStatusCode())) {
1441                        if (element.getPaymentPaidTimestamp() == null) {
1442                            return true;
1443                        }
1444                    }
1445                }// endfor
1446            }
1447            if (getRelatedViews().getRelatedCreditMemoViews() != null) {
1448                for (CreditMemoView element : getRelatedViews().getRelatedCreditMemoViews()) {
1449                    // If the CM is cancelled, check whether the CM has been paid.
1450                    // If it has not been paid, then this method will return true.
1451                    if (!CreditMemoStatuses.CANCELLED_STATUSES.contains(element.getCreditMemoStatusCode())) {
1452                        if (element.getCreditMemoPaidTimestamp() == null) {
1453                            return true;
1454                        }
1455                    }
1456                }// endfor
1457            }
1458            return false;
1459        }
1460        
1461        public boolean getAdditionalChargesExist() {
1462            List<PurchaseOrderItem> items = this.getItems();
1463            for( PurchaseOrderItem item : items ) {
1464                if ((item != null) &&
1465                    (item.getItemType() != null) && 
1466                    (item.getItemType().isAdditionalChargeIndicator()) && 
1467                    (item.getExtendedPrice() != null) && 
1468                    (!KualiDecimal.ZERO.equals(item.getExtendedPrice()))) {
1469                    return true;
1470                }
1471            }
1472            return false;
1473        }
1474    
1475        /**
1476         * Used for routing only.
1477         * 
1478         * @deprecated
1479         */
1480        public String getContractManagerName() {
1481            return "";
1482        }
1483    
1484        /**
1485         * Used for routing only.
1486         * 
1487         * @deprecated
1488         */
1489        public void setContractManagerName(String contractManagerName) {
1490        }
1491    
1492        /**
1493         * Used for routing only.
1494         * 
1495         * @deprecated
1496         */
1497        public String getStatusDescription() {
1498            return "";
1499        }
1500    
1501        /**
1502         * Used for routing only.
1503         * 
1504         * @deprecated
1505         */
1506        public void setStatusDescription(String statusDescription) {
1507        }
1508    
1509        public KualiDecimal getInternalPurchasingLimit() {
1510            //FIXME need the following because at places this field remains null because contract manager is not refreshed and null
1511    
1512            if (internalPurchasingLimit == null) {
1513                setInternalPurchasingLimit(SpringContext.getBean(PurchaseOrderService.class).getInternalPurchasingDollarLimit(this));
1514            }
1515            return internalPurchasingLimit;
1516        }
1517    
1518        public void setInternalPurchasingLimit(KualiDecimal internalPurchasingLimit) {
1519            this.internalPurchasingLimit = internalPurchasingLimit;
1520        }
1521        
1522        public boolean isPendingSplit() {
1523            return pendingSplit;
1524        }
1525    
1526        public void setPendingSplit(boolean pendingSplit) {
1527            this.pendingSplit = pendingSplit;
1528        }
1529        
1530        public boolean isCopyingNotesWhenSplitting() {
1531            return copyingNotesWhenSplitting;
1532        }
1533    
1534        public void setCopyingNotesWhenSplitting(boolean copyingNotesWhenSplitting) {
1535            this.copyingNotesWhenSplitting = copyingNotesWhenSplitting;
1536        }
1537    
1538        /**
1539         * @see org.kuali.module.purap.rules.PurapAccountingDocumentRuleBase#customizeExplicitGeneralLedgerPendingEntry(org.kuali.kfs.sys.document.AccountingDocument,
1540         *      org.kuali.kfs.sys.businessobject.AccountingLine, org.kuali.kfs.sys.businessobject.GeneralLedgerPendingEntry)
1541         */
1542        @Override
1543        public void customizeExplicitGeneralLedgerPendingEntry(GeneralLedgerPendingEntrySourceDetail postable, GeneralLedgerPendingEntry explicitEntry) {
1544            super.customizeExplicitGeneralLedgerPendingEntry(postable, explicitEntry);
1545    
1546            SpringContext.getBean(PurapGeneralLedgerService.class).customizeGeneralLedgerPendingEntry(this, (AccountingLine)postable, explicitEntry, getPurapDocumentIdentifier(), GL_DEBIT_CODE, PurapDocTypeCodes.PO_DOCUMENT, true);
1547    
1548            // don't think i should have to override this, but default isn't getting the right PO doc
1549            explicitEntry.setFinancialDocumentTypeCode(PurapDocTypeCodes.PO_DOCUMENT);
1550        }
1551    
1552        @Override
1553        public Class getPurchasingCapitalAssetItemClass() {
1554            return PurchaseOrderCapitalAssetItem.class;
1555        }
1556    
1557        @Override
1558        public Class getPurchasingCapitalAssetSystemClass() {
1559            return PurchaseOrderCapitalAssetSystem.class;
1560        }
1561        
1562        /**
1563         * Validates whether we can indeed close the PO. Return false and give error if 
1564         * the outstanding encumbrance amount of the trade in item is less than 0.
1565         * 
1566         * @param po
1567         * @return
1568         */
1569        public boolean canClosePOForTradeIn () {
1570            for (PurchaseOrderItem item : (List<PurchaseOrderItem>)getItems()) {
1571                if (item.getItemTypeCode().equals(PurapConstants.ItemTypeCodes.ITEM_TYPE_TRADE_IN_CODE) && item.getItemOutstandingEncumberedAmount().isLessThan(new KualiDecimal(0))) {
1572                    GlobalVariables.getMessageMap().putError(PurapConstants.ITEM_TAB_ERROR_PROPERTY, PurapKeyConstants.ERROR_ITEM_TRADE_IN_OUTSTANDING_ENCUMBERED_AMOUNT_NEGATIVE, "amend the PO");
1573                    return false;
1574                }
1575            }
1576            return true;
1577        }
1578        
1579        /**
1580         * Provides answers to the following splits:
1581         * RequiresContractManagementReview
1582         * RequiresBudgetReview
1583         * VendorIsEmployeeOrNonResidentAlien
1584         * TransmissionMethodIsPrint
1585         * 
1586         * @see org.kuali.kfs.sys.document.FinancialSystemTransactionalDocumentBase#answerSplitNodeQuestion(java.lang.String)
1587         */
1588        @Override
1589        public boolean answerSplitNodeQuestion(String nodeName) throws UnsupportedOperationException {
1590            if (nodeName.equals(PurapWorkflowConstants.CONTRACT_MANAGEMENT_REVIEW_REQUIRED)) return isContractManagementReviewRequired();
1591            if (nodeName.equals(PurapWorkflowConstants.AWARD_REVIEW_REQUIRED)) return isAwardReviewRequired();
1592            if (nodeName.equals(PurapWorkflowConstants.BUDGET_REVIEW_REQUIRED)) return isBudgetReviewRequired();
1593            if (nodeName.equals(PurapWorkflowConstants.VENDOR_IS_EMPLOYEE_OR_NON_RESIDENT_ALIEN)) return isVendorEmployeeOrNonResidentAlien();
1594            throw new UnsupportedOperationException("Cannot answer split question for this node you call \""+nodeName+"\"");
1595        }
1596        
1597        protected boolean isContractManagementReviewRequired() {
1598            KualiDecimal internalPurchasingLimit = SpringContext.getBean(PurchaseOrderService.class).getInternalPurchasingDollarLimit(this);
1599            return ((ObjectUtils.isNull(internalPurchasingLimit)) || (internalPurchasingLimit.compareTo(this.getTotalDollarAmount()) < 0));
1600    
1601        }
1602    
1603        protected boolean isAwardReviewRequired() {
1604            ParameterService parameterService = SpringContext.getBean(ParameterService.class);
1605            boolean objectCodeAllowed = true;
1606            
1607            for (PurApItem item : (List<PurApItem>) this.getItems()) {
1608                for (PurApAccountingLine accountingLine : item.getSourceAccountingLines()) {
1609                    
1610                    objectCodeAllowed = isObjectCodeAllowedForAwardRouting(accountingLine, parameterService);
1611                    // We should return true as soon as we have at least one objectCodeAllowed=true so that the PO will stop at Award
1612                    // level.
1613                    if (objectCodeAllowed) {
1614                        return objectCodeAllowed;
1615                    }
1616    
1617                }
1618            }
1619            return objectCodeAllowed;        
1620        }
1621        
1622        protected boolean isObjectCodeAllowedForAwardRouting(PurApAccountingLine accountingLine, ParameterService parameterService) {
1623            if (ObjectUtils.isNull(accountingLine.getObjectCode())) {
1624                return false;
1625            }
1626    
1627            // make sure object code is active
1628            if (!accountingLine.getObjectCode().isFinancialObjectActiveCode()) {
1629                return false;
1630            }
1631    
1632            String chartCode = accountingLine.getChartOfAccountsCode();
1633            // check object level is in permitted list for award routing
1634            boolean objectCodeAllowed = parameterService.getParameterEvaluator(PurchaseOrderDocument.class, PurapParameterConstants.CG_ROUTE_OBJECT_LEVELS_BY_CHART, PurapParameterConstants.NO_CG_ROUTE_OBJECT_LEVELS_BY_CHART, chartCode, accountingLine.getObjectCode().getFinancialObjectLevelCode()).evaluationSucceeds();
1635    
1636            if (!objectCodeAllowed) {
1637                // If the object level is not permitting for award routing, then we need to also
1638                // check object code is in permitted list for award routing
1639                objectCodeAllowed = parameterService.getParameterEvaluator(PurchaseOrderDocument.class, PurapParameterConstants.CG_ROUTE_OBJECT_CODES_BY_CHART, PurapParameterConstants.NO_CG_ROUTE_OBJECT_CODES_BY_CHART, chartCode, accountingLine.getFinancialObjectCode()).evaluationSucceeds();
1640            }
1641            return objectCodeAllowed;
1642        }
1643        
1644        protected boolean isBudgetReviewRequired() {
1645            boolean alwaysRoutes = true;
1646            String documentHeaderId = null;
1647            String currentXpathExpression = null;
1648            String documentFiscalYearString = this.getPostingYear().toString();
1649    
1650            // if document's fiscal year is less than or equal to the current fiscal year
1651            if (SpringContext.getBean(UniversityDateService.class).getCurrentFiscalYear().compareTo(Integer.valueOf(documentFiscalYearString)) >= 0) {
1652                // get list of sufficientfundItems
1653                List<SufficientFundsItem> fundsItems = SpringContext.getBean(SufficientFundsService.class).checkSufficientFunds(getPendingLedgerEntriesForSufficientFundsChecking());
1654                    for (SufficientFundsItem fundsItem : fundsItems) {
1655                        if (this.getChartOfAccountsCode().equalsIgnoreCase(fundsItem.getAccount().getChartOfAccountsCode())) {
1656                        LOG.debug("Chart code of rule extension matches chart code of at least one Sufficient Funds Item");
1657                        return true;
1658                    }
1659                }
1660            }
1661    
1662            return false;
1663        }
1664    
1665    
1666        protected boolean isVendorEmployeeOrNonResidentAlien() {        
1667            if (ObjectUtils.isNull(this.getVendorHeaderGeneratedIdentifier())) {
1668                // no vendor header id so can't check for proper tax routing
1669                return false;
1670            }
1671            String vendorHeaderGeneratedId = this.getVendorHeaderGeneratedIdentifier().toString();
1672            VendorService vendorService = SpringContext.getBean(VendorService.class);
1673            boolean routeDocumentAsEmployeeVendor = vendorService.isVendorInstitutionEmployee(Integer.valueOf(vendorHeaderGeneratedId));
1674            boolean routeDocumentAsForeignVendor = vendorService.isVendorForeign(Integer.valueOf(vendorHeaderGeneratedId));
1675            if ((!routeDocumentAsEmployeeVendor) && (!routeDocumentAsForeignVendor)) {
1676                // no need to route
1677                return false;
1678            }
1679    
1680            return true;
1681        }
1682        
1683        public List<Account> getAccountsForAwardRouting() {
1684            List<Account> accounts = new ArrayList<Account>();
1685            
1686            ParameterService parameterService = SpringContext.getBean(ParameterService.class);
1687            for (PurApItem item : (List<PurApItem>) this.getItems()) {
1688                for (PurApAccountingLine accountingLine : item.getSourceAccountingLines()) {
1689                    if (isObjectCodeAllowedForAwardRouting(accountingLine, parameterService)) {
1690                        if (ObjectUtils.isNull(accountingLine.getAccount())) {
1691                            accountingLine.refreshReferenceObject("account");
1692                        }
1693                        if (accountingLine.getAccount() != null && !accounts.contains(accountingLine.getAccount())) {
1694                            accounts.add(accountingLine.getAccount());
1695                        }
1696                    }
1697                }
1698            }
1699            return accounts;
1700        }
1701        
1702        public DocSearchCriteriaDTO convertSelections(DocSearchCriteriaDTO searchCriteria) {
1703    
1704            for (SearchAttributeCriteriaComponent comp : searchCriteria.getSearchableAttributes()) {  
1705                if (comp.getLookupableFieldType().equals(Field.MULTISELECT)) {
1706                    List<String> values = comp.getValues();
1707                    List<String> newVals = new ArrayList<String>();
1708                    if (values.contains("INCOMPLETE")) {
1709                        for (String str : PurchaseOrderStatuses.INCOMPLETE_STATUSES)
1710                            newVals.add(str);
1711                    } if (values.contains("COMPLETE")) {
1712                        for (String str : PurchaseOrderStatuses.COMPLETE_STATUSES)
1713                            newVals.add(str);
1714                    } 
1715                    
1716                    for (String str : values) {
1717                        newVals.add(str);
1718                    }
1719                    
1720                    comp.setValues(newVals);
1721                }
1722            }
1723            return searchCriteria;
1724        }
1725        
1726        /**
1727         * @return the purchase order current indicator
1728         */
1729        public boolean getPurchaseOrderCurrentIndicatorForSearching() {
1730            return purchaseOrderCurrentIndicator;
1731        }
1732        
1733        public String getDocumentTitleForResult() throws WorkflowException{
1734            return SpringContext.getBean(KualiWorkflowInfo.class).getDocType(this.getDocumentHeader().getWorkflowDocument().getDocumentType()).getDocTypeLabel();
1735        }
1736        
1737        /**
1738         * Checks whether the purchase order needs a warning to be displayed, i.e. it never has been opened.
1739         * @return true if the purchase order needs a warning; false otherwise.
1740         */
1741        public boolean getNeedWarning() {
1742            return getPurchaseOrderInitialOpenTimestamp() == null;
1743        }
1744    
1745        public List<SourceAccountingLine> getGlOnlySourceAccountingLines() {
1746            return glOnlySourceAccountingLines;
1747        }
1748    
1749        public void setGlOnlySourceAccountingLines(List<SourceAccountingLine> glOnlySourceAccountingLines) {
1750            this.glOnlySourceAccountingLines = glOnlySourceAccountingLines;
1751        }
1752    
1753    }