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