View Javadoc
1   /*
2    * Copyright 2006 The Kuali Foundation
3    *
4    * Licensed under the Educational Community License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    * http://www.opensource.org/licenses/ecl2.php
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  package org.kuali.ole.module.purap.document.service.impl;
17  
18  import org.apache.commons.lang.StringUtils;
19  import org.apache.commons.lang.text.StrBuilder;
20  import org.kuali.ole.coa.businessobject.Account;
21  import org.kuali.ole.coa.service.AccountService;
22  import org.kuali.ole.deliver.batch.OleMailer;
23  import org.kuali.ole.integration.purap.CapitalAssetSystem;
24  import org.kuali.ole.module.purap.*;
25  import org.kuali.ole.module.purap.PurapConstants.*;
26  import org.kuali.ole.module.purap.batch.AutoClosePurchaseOrdersStep;
27  import org.kuali.ole.module.purap.batch.AutoCloseRecurringOrdersStep;
28  import org.kuali.ole.module.purap.businessobject.*;
29  import org.kuali.ole.module.purap.document.*;
30  import org.kuali.ole.module.purap.document.dataaccess.PurchaseOrderDao;
31  import org.kuali.ole.module.purap.document.service.*;
32  import org.kuali.ole.module.purap.edi.PurchaseOrderEdi;
33  import org.kuali.ole.module.purap.pdf.PurchaseOrderParameters;
34  import org.kuali.ole.module.purap.pdf.PurchaseOrderPdf;
35  import org.kuali.ole.module.purap.pdf.PurchaseOrderTransmitParameters;
36  import org.kuali.ole.module.purap.util.PurApObjectUtils;
37  import org.kuali.ole.module.purap.util.ThresholdHelper;
38  import org.kuali.ole.module.purap.util.ThresholdHelper.ThresholdSummary;
39  import org.kuali.ole.select.document.service.OleSelectDocumentService;
40  import org.kuali.ole.select.service.OleTransmissionService;
41  import org.kuali.ole.sys.OLEConstants;
42  import org.kuali.ole.sys.OLEPropertyConstants;
43  import org.kuali.ole.sys.businessobject.SourceAccountingLine;
44  import org.kuali.ole.sys.context.SpringContext;
45  import org.kuali.ole.sys.document.FinancialSystemTransactionalDocumentBase;
46  import org.kuali.ole.sys.document.validation.event.AttributedRouteDocumentEvent;
47  import org.kuali.ole.sys.document.validation.event.DocumentSystemSaveEvent;
48  import org.kuali.ole.sys.service.impl.OleParameterConstants;
49  import org.kuali.ole.vnd.VendorConstants;
50  import org.kuali.ole.vnd.VendorConstants.AddressTypes;
51  import org.kuali.ole.vnd.businessobject.*;
52  import org.kuali.ole.vnd.document.service.VendorService;
53  import org.kuali.rice.core.api.config.property.ConfigContext;
54  import org.kuali.rice.core.api.config.property.ConfigurationService;
55  import org.kuali.rice.core.api.datetime.DateTimeService;
56  import org.kuali.rice.core.api.mail.*;
57  import org.kuali.rice.core.api.resourceloader.GlobalResourceLoader;
58  import org.kuali.rice.core.api.util.type.KualiDecimal;
59  import org.kuali.rice.coreservice.api.parameter.Parameter;
60  import org.kuali.rice.coreservice.framework.parameter.ParameterService;
61  import org.kuali.rice.kew.api.KewApiConstants;
62  import org.kuali.rice.kew.api.KewApiServiceLocator;
63  import org.kuali.rice.kew.api.WorkflowDocument;
64  import org.kuali.rice.kew.api.action.ActionRequestType;
65  import org.kuali.rice.kew.api.document.WorkflowDocumentService;
66  import org.kuali.rice.kew.api.document.attribute.DocumentAttributeIndexingQueue;
67  import org.kuali.rice.kew.api.document.search.DocumentSearchCriteria;
68  import org.kuali.rice.kew.api.document.search.DocumentSearchResult;
69  import org.kuali.rice.kew.api.document.search.DocumentSearchResults;
70  import org.kuali.rice.kew.api.exception.WorkflowException;
71  import org.kuali.rice.kew.routeheader.DocumentRouteHeaderValue;
72  import org.kuali.rice.kew.routeheader.service.RouteHeaderService;
73  import org.kuali.rice.kew.service.KEWServiceLocator;
74  import org.kuali.rice.kim.api.identity.Person;
75  import org.kuali.rice.kim.api.identity.PersonService;
76  import org.kuali.rice.kns.document.MaintenanceDocument;
77  import org.kuali.rice.kns.maintenance.Maintainable;
78  import org.kuali.rice.kns.service.DataDictionaryService;
79  import org.kuali.rice.kns.util.KNSGlobalVariables;
80  import org.kuali.rice.krad.bo.AdHocRoutePerson;
81  import org.kuali.rice.krad.bo.AdHocRouteRecipient;
82  import org.kuali.rice.krad.bo.Note;
83  import org.kuali.rice.krad.datadictionary.exception.UnknownDocumentTypeException;
84  import org.kuali.rice.krad.document.Document;
85  import org.kuali.rice.krad.document.DocumentBase;
86  import org.kuali.rice.krad.exception.ValidationException;
87  import org.kuali.rice.krad.rules.rule.event.RouteDocumentEvent;
88  import org.kuali.rice.krad.service.*;
89  import org.kuali.rice.krad.util.GlobalVariables;
90  import org.kuali.rice.krad.util.MessageMap;
91  import org.kuali.rice.krad.util.ObjectUtils;
92  import org.springframework.transaction.annotation.Transactional;
93  
94  import java.io.ByteArrayOutputStream;
95  import java.io.File;
96  import java.sql.Timestamp;
97  import java.text.ParseException;
98  import java.util.*;
99  
100 @Transactional
101 public class PurchaseOrderServiceImpl implements PurchaseOrderService {
102     private static final org.apache.log4j.Logger LOG = org.apache.log4j.Logger.getLogger(PurchaseOrderServiceImpl.class);
103 
104     protected BusinessObjectService businessObjectService;
105     protected DateTimeService dateTimeService;
106     protected DocumentService documentService;
107     protected NoteService noteService;
108     protected PurapService purapService;
109     protected PrintService printService;
110     protected PurchaseOrderDao purchaseOrderDao;
111     protected WorkflowDocumentService workflowDocumentService;
112     protected ConfigurationService kualiConfigurationService;
113     protected KualiRuleService kualiRuleService;
114     protected VendorService vendorService;
115     protected RequisitionService requisitionService;
116     protected PurApWorkflowIntegrationService purapWorkflowIntegrationService;
117     protected MaintenanceDocumentService maintenanceDocumentService;
118     protected ParameterService parameterService;
119     protected PersonService personService;
120     protected MailService mailService;
121     protected B2BPurchaseOrderService b2bPurchaseOrderService;
122     protected DataDictionaryService dataDictionaryService;
123     protected String documentNumber;
124     protected String fromEmailAddress;
125     protected List fileNameList;
126     protected String toEmailAddress;
127 
128     protected static final boolean TRANSMISSION_IS_RETRANSMIT = true;
129     protected static final boolean TRANSMISSION_IS_NOT_RETRANSMIT = !TRANSMISSION_IS_RETRANSMIT;
130     protected OlePurapService olePurapService;
131     private OleSelectDocumentService oleSelectDocumentService;
132 
133     public OlePurapService getOlePurapService() {
134         if (olePurapService == null) {
135             olePurapService = SpringContext.getBean(OlePurapService.class);
136         }
137         return olePurapService;
138     }
139 
140     @Override
141     public boolean isPurchaseOrderOpenForProcessing(Integer poId) {
142         return isPurchaseOrderOpenForProcessing(getCurrentPurchaseOrder(poId));
143     }
144 
145     @Override
146     public boolean isPurchaseOrderOpenForProcessing(PurchaseOrderDocument purchaseOrderDocument) {
147         boolean can = PurchaseOrderStatuses.APPDOC_OPEN.equals(purchaseOrderDocument.getApplicationDocumentStatus());
148         can = can && purchaseOrderDocument.isPurchaseOrderCurrentIndicator() && !purchaseOrderDocument.isPendingActionIndicator();
149 
150         // can't be any PREQ or CM that have not completed fullDocumentEntry
151         if (can) {
152             List<PaymentRequestView> preqViews = purchaseOrderDocument.getRelatedViews().getRelatedPaymentRequestViews();
153             if (preqViews != null) {
154                 for (PaymentRequestView preqView : preqViews) {
155                     if (!purapService.isPaymentRequestFullDocumentEntryCompleted(preqView.getApplicationDocumentStatus())) {
156                         return false;
157                     }
158                 }
159             }
160             List<CreditMemoView> cmViews = purchaseOrderDocument.getRelatedViews().getRelatedCreditMemoViews();
161             if (cmViews != null) {
162                 for (CreditMemoView cmView : cmViews) {
163                     if (!purapService.isVendorCreditMemoFullDocumentEntryCompleted(cmView.getApplicationDocumentStatus())) {
164                         return false;
165                     }
166                 }
167             }
168             List<InvoiceView> invViews = purchaseOrderDocument.getRelatedViews().getRelatedInvoiceViews();
169             if (invViews != null) {
170                 for (InvoiceView invView : invViews) {
171                     if (!purapService.isInvoiceFullDocumentEntryCompleted(invView.getApplicationDocumentStatus())) {
172                         return false;
173                     }
174                 }
175             }
176 
177         }
178 
179         // passed all conditions; return true
180         return can;
181     }
182 
183     @Override
184     public boolean isCommodityCodeRequiredOnPurchaseOrder() {
185         boolean enableCommodityCode = parameterService.getParameterValueAsBoolean(OleParameterConstants.PURCHASING_DOCUMENT.class, PurapParameterConstants.ENABLE_COMMODITY_CODE_IND);
186         if (!enableCommodityCode) {
187             return false;
188         } else {
189             return parameterService.getParameterValueAsBoolean(PurchaseOrderDocument.class, PurapRuleConstants.ITEMS_REQUIRE_COMMODITY_CODE_IND);
190         }
191     }
192 
193     /**
194      * Sets the error map to a new, empty error map before calling saveDocumentNoValidation to save the document.
195      *
196      * @param document The purchase order document to be saved.
197      */
198     protected void saveDocumentNoValidationUsingClearMessageMap(PurchaseOrderDocument document) {
199         MessageMap errorHolder = GlobalVariables.getMessageMap();
200         GlobalVariables.setMessageMap(new MessageMap());
201         try {
202             purapService.saveDocumentNoValidation(document);
203         } finally {
204             GlobalVariables.setMessageMap(errorHolder);
205         }
206     }
207 
208     /**
209      * Calls the saveDocument method of documentService to save the document.
210      *
211      * @param document The document to be saved.
212      */
213     protected void saveDocumentStandardSave(PurchaseOrderDocument document) {
214         try {
215             documentService.saveDocument(document);
216         } catch (WorkflowException we) {
217             String errorMsg = "Workflow Error saving document # " + document.getDocumentHeader().getDocumentNumber() + " " + we.getMessage();
218             LOG.error(errorMsg, we);
219             throw new RuntimeException(errorMsg, we);
220         }
221     }
222 
223     @Override
224     public PurchasingCapitalAssetItem createCamsItem(PurchasingDocument purDoc, PurApItem purapItem) {
225         PurchasingCapitalAssetItem camsItem = new PurchaseOrderCapitalAssetItem();
226         camsItem.setItemIdentifier(purapItem.getItemIdentifier());
227         // If the system type is INDIVIDUAL then for each of the capital asset items, we need a system attached to it.
228         if (purDoc.getCapitalAssetSystemTypeCode().equals(PurapConstants.CapitalAssetTabStrings.INDIVIDUAL_ASSETS)) {
229             CapitalAssetSystem resultSystem = new PurchaseOrderCapitalAssetSystem();
230             camsItem.setPurchasingCapitalAssetSystem(resultSystem);
231         }
232         camsItem.setPurchasingDocument(purDoc);
233 
234         return camsItem;
235     }
236 
237     @Override
238     public CapitalAssetSystem createCapitalAssetSystem() {
239         CapitalAssetSystem resultSystem = new PurchaseOrderCapitalAssetSystem();
240         return resultSystem;
241     }
242 
243     /**
244      * @see org.kuali.ole.module.purap.document.service.PurchaseOrderService#createAutomaticPurchaseOrderDocument(org.kuali.ole.module.purap.document.RequisitionDocument)
245      */
246     @Override
247     public void createAutomaticPurchaseOrderDocument(RequisitionDocument reqDocument) {
248         String newSessionUserId = getOleSelectDocumentService().getSelectParameterValue(OLEConstants.SYSTEM_USER);
249         try {
250 
251             LogicContainer logicToRun = new LogicContainer() {
252                 @Override
253                 public Object runLogic(Object[] objects) throws Exception {
254                     RequisitionDocument doc = (RequisitionDocument) objects[0];
255                     // update REQ data
256                     doc.setPurchaseOrderAutomaticIndicator(Boolean.TRUE);
257                     // create PO and populate with default data
258                     PurchaseOrderDocument po = generatePurchaseOrderFromRequisition(doc);
259                     po.setDefaultValuesForAPO();
260                     //check for print transmission method.. if print is selected
261                     //the doc status needs to be "Pending To Print"..
262                     checkForPrintTransmission(po);
263                     po.setContractManagerCode(PurapConstants.APO_CONTRACT_MANAGER);
264 
265                     //documentService.routeDocument(po, null, null);
266 
267                     final DocumentAttributeIndexingQueue documentAttributeIndexingQueue = KewApiServiceLocator.getDocumentAttributeIndexingQueue();
268                     documentAttributeIndexingQueue.indexDocument(po.getDocumentNumber());
269 
270                     return null;
271                 }
272             };
273             purapService.performLogicWithFakedUserSession(newSessionUserId, logicToRun, new Object[]{reqDocument});
274         } catch (WorkflowException e) {
275             String errorMsg = "Workflow Exception caught: " + e.getLocalizedMessage();
276             LOG.error(errorMsg, e);
277             throw new RuntimeException(errorMsg, e);
278         } catch (Exception e) {
279             throw new RuntimeException(e);
280         }
281     }
282 
283     /**
284      * checks for print option and if chosen then sets the app doc status to
285      * Pending To Print.
286      *
287      * @param po
288      */
289     protected void checkForPrintTransmission(PurchaseOrderDocument po) throws WorkflowException {
290         if (PurapConstants.POTransmissionMethods.PRINT.equals(po.getPurchaseOrderRetransmissionMethodCode())) {
291             po.updateAndSaveAppDocStatus(PurapConstants.PurchaseOrderStatuses.APPDOC_PENDING_PRINT);
292         }
293     }
294 
295     /**
296      * @see org.kuali.ole.module.purap.document.service.PurchaseOrderService#createPurchaseOrderDocument(org.kuali.ole.module.purap.document.RequisitionDocument,
297      * java.lang.String, java.lang.Integer)
298      */
299     @Override
300     public PurchaseOrderDocument createPurchaseOrderDocument(RequisitionDocument reqDocument, String newSessionUserId, Integer contractManagerCode) {
301         try {
302             LogicContainer logicToRun = new LogicContainer() {
303                 @Override
304                 public Object runLogic(Object[] objects) throws Exception {
305                     RequisitionDocument doc = (RequisitionDocument) objects[0];
306                     PurchaseOrderDocument po = generatePurchaseOrderFromRequisition(doc);
307                     Integer cmCode = (Integer) objects[1];
308                     po.setContractManagerCode(cmCode);
309                     purapService.saveDocumentNoValidation(po);
310                     return po;
311                 }
312             };
313             return (PurchaseOrderDocument) purapService.performLogicWithFakedUserSession(newSessionUserId, logicToRun, new Object[]{reqDocument, contractManagerCode});
314         } catch (WorkflowException e) {
315             String errorMsg = "Workflow Exception caught: " + e.getLocalizedMessage();
316             LOG.error(errorMsg, e);
317             throw new RuntimeException(errorMsg, e);
318         } catch (Exception e) {
319             throw new RuntimeException(e);
320         }
321     }
322 
323     /**
324      * Create Purchase Order and populate with data from Requisition and other default data
325      *
326      * @param reqDocument The requisition document from which we create the purchase order document.
327      * @return The purchase order document created by this method.
328      * @throws WorkflowException
329      */
330     protected PurchaseOrderDocument generatePurchaseOrderFromRequisition(RequisitionDocument reqDocument) throws WorkflowException {
331         PurchaseOrderDocument poDocument = null;
332         poDocument = (PurchaseOrderDocument) documentService.getNewDocument(PurchaseOrderDocTypes.PURCHASE_ORDER_DOCUMENT);
333         poDocument.populatePurchaseOrderFromRequisition(reqDocument);
334 
335         poDocument.updateAndSaveAppDocStatus(PurapConstants.PurchaseOrderStatuses.APPDOC_IN_PROCESS);
336 
337         poDocument.setPurchaseOrderCurrentIndicator(true);
338         poDocument.setPendingActionIndicator(false);
339         /*poDocument.setLicensingRequirementIndicator(reqDocument.isLicensingRequirementIndicator());*/
340        /* poDocument.setLicensingRequirementCode(reqDocument.getLicensingRequirementCode());*/
341 
342         if (RequisitionSources.B2B.equals(poDocument.getRequisitionSourceCode())) {
343             String paramName = PurapParameterConstants.DEFAULT_B2B_VENDOR_CHOICE;
344             String paramValue = parameterService.getParameterValueAsString(PurchaseOrderDocument.class, paramName);
345             poDocument.setPurchaseOrderVendorChoiceCode(paramValue);
346         }
347 
348         if (ObjectUtils.isNotNull(poDocument.getVendorContract())) {
349             poDocument.setVendorPaymentTermsCode(poDocument.getVendorContract().getVendorPaymentTermsCode());
350             poDocument.setVendorShippingPaymentTermsCode(poDocument.getVendorContract().getVendorShippingPaymentTermsCode());
351             poDocument.setVendorShippingTitleCode(poDocument.getVendorContract().getVendorShippingTitleCode());
352         } else {
353             VendorDetail vendor = vendorService.getVendorDetail(poDocument.getVendorHeaderGeneratedIdentifier(), poDocument.getVendorDetailAssignedIdentifier());
354             if (ObjectUtils.isNotNull(vendor)) {
355                 poDocument.setVendorPaymentTermsCode(vendor.getVendorPaymentTermsCode());
356                 poDocument.setVendorShippingPaymentTermsCode(vendor.getVendorShippingPaymentTermsCode());
357                 poDocument.setVendorShippingTitleCode(vendor.getVendorShippingTitleCode());
358             }
359         }
360 
361         if (!PurapConstants.RequisitionSources.B2B.equals(poDocument.getRequisitionSourceCode())) {
362             purapService.addBelowLineItems(poDocument);
363         }
364         poDocument.fixItemReferences();
365 
366         return poDocument;
367     }
368 
369     /**
370      * @see org.kuali.ole.module.purap.document.service.PurchaseOrderService#getInternalPurchasingDollarLimit(org.kuali.ole.module.purap.document.PurchaseOrderDocument)
371      */
372     @Override
373     public KualiDecimal getInternalPurchasingDollarLimit(PurchaseOrderDocument document) {
374         if ((document.getVendorContract() != null) && (document.getContractManager() != null)) {
375             KualiDecimal contractDollarLimit = vendorService.getApoLimitFromContract(document.getVendorContract().getVendorContractGeneratedIdentifier(), document.getChartOfAccountsCode(), document.getOrganizationCode());
376             // FIXME somehow data fields such as contractManagerDelegationDollarLimit in reference object contractManager didn't get
377             // retrieved
378             // (are null) as supposed to be (this happens whether or not proxy is set to true), even though contractManager is not
379             // null;
380             // so here we have to manually refresh the contractManager to retrieve the fields
381             if (document.getContractManager().getContractManagerDelegationDollarLimit() == null) {
382                 document.refreshReferenceObject(PurapPropertyConstants.CONTRACT_MANAGER);
383             }
384             KualiDecimal contractManagerLimit = document.getContractManager().getContractManagerDelegationDollarLimit();
385             if ((contractDollarLimit != null) && (contractManagerLimit != null)) {
386                 if (contractDollarLimit.compareTo(contractManagerLimit) > 0) {
387                     return contractDollarLimit;
388                 } else {
389                     return contractManagerLimit;
390                 }
391             } else if (contractDollarLimit != null) {
392                 return contractDollarLimit;
393             } else {
394                 return contractManagerLimit;
395             }
396         } else if ((document.getVendorContract() == null) && (document.getContractManager() != null)) {
397             // FIXME As above, here we have to manually refresh the contractManager to retrieve its field
398             if (document.getContractManager().getContractManagerDelegationDollarLimit() == null) {
399                 document.refreshReferenceObject(PurapPropertyConstants.CONTRACT_MANAGER);
400             }
401             return document.getContractManager().getContractManagerDelegationDollarLimit();
402         } else if ((document.getVendorContract() != null) && (document.getContractManager() == null)) {
403             return purapService.getApoLimit(document.getVendorContract().getVendorContractGeneratedIdentifier(), document.getChartOfAccountsCode(), document.getOrganizationCode());
404         } else {
405             String errorMsg = "No internal purchase order dollar limit found for purchase order '" + document.getPurapDocumentIdentifier() + "'.";
406             LOG.warn(errorMsg);
407             return null;
408         }
409     }
410 
411     /**
412      * Loops through the collection of error messages and adding each of them to the error map.
413      *
414      * @param errorKey The resource key used to retrieve the error text from the error message resource bundle.
415      * @param errors   The collection of error messages.
416      */
417     protected void addStringErrorMessagesToMessageMap(String errorKey, Collection<String> errors) {
418         if (ObjectUtils.isNotNull(errors)) {
419             for (String error : errors) {
420                 LOG.error("Adding error message using error key '" + errorKey + "' with text '" + error + "'");
421                 GlobalVariables.getMessageMap().putError(OLEConstants.GLOBAL_ERRORS, errorKey, error);
422             }
423         }
424     }
425 
426 
427     /**
428      * TODO RELEASE 3 - QUOTE
429      *
430      * @see org.kuali.ole.module.purap.document.service.PurchaseOrderService#printPurchaseOrderQuoteRequestsListPDF(org.kuali.ole.module.purap.document.PurchaseOrderDocument,
431      * java.io.ByteArrayOutputStream)
432      */
433     @Override
434     public boolean printPurchaseOrderQuoteRequestsListPDF(String documentNumber, ByteArrayOutputStream baosPDF) {
435         PurchaseOrderDocument po = getPurchaseOrderByDocumentNumber(documentNumber);
436         String environment = kualiConfigurationService.getPropertyValueAsString(OLEConstants.ENVIRONMENT_KEY);
437         Collection<String> generatePDFErrors = printService.generatePurchaseOrderQuoteRequestsListPdf(po, baosPDF);
438 
439         if (generatePDFErrors.size() > 0) {
440             addStringErrorMessagesToMessageMap(PurapKeyConstants.ERROR_PURCHASE_ORDER_PDF, generatePDFErrors);
441             return false;
442         } else {
443             return true;
444         }
445     }
446 
447     /**
448      * TODO RELEASE 3 - QUOTE
449      *
450      * @see org.kuali.ole.module.purap.document.service.PurchaseOrderService#printPurchaseOrderQuotePDF(org.kuali.ole.module.purap.document.PurchaseOrderDocument,
451      * org.kuali.ole.module.purap.businessobject.PurchaseOrderVendorQuote, java.io.ByteArrayOutputStream)
452      */
453     @Override
454     public boolean printPurchaseOrderQuotePDF(PurchaseOrderDocument po, PurchaseOrderVendorQuote povq, ByteArrayOutputStream baosPDF) {
455         String environment = kualiConfigurationService.getPropertyValueAsString(OLEConstants.ENVIRONMENT_KEY);
456         Collection<String> generatePDFErrors = printService.generatePurchaseOrderQuotePdf(po, povq, baosPDF, environment);
457 
458         if (generatePDFErrors.size() > 0) {
459             addStringErrorMessagesToMessageMap(PurapKeyConstants.ERROR_PURCHASE_ORDER_PDF, generatePDFErrors);
460             return false;
461         } else {
462             return true;
463         }
464     }
465 
466     /**
467      * @see org.kuali.ole.module.purap.document.service.PurchaseOrderService#performPurchaseOrderFirstTransmitViaPrinting(java.lang.String,
468      * java.io.ByteArrayOutputStream)
469      */
470     @Override
471     public void performPurchaseOrderFirstTransmitViaPrinting(String documentNumber, ByteArrayOutputStream baosPDF) {
472         PurchaseOrderDocument po = getPurchaseOrderByDocumentNumber(documentNumber);
473         String environment = kualiConfigurationService.getPropertyValueAsString(OLEConstants.ENVIRONMENT_KEY);
474         Collection<String> generatePDFErrors = printService.generatePurchaseOrderPdf(po, baosPDF, environment, null);
475         if (!generatePDFErrors.isEmpty()) {
476             addStringErrorMessagesToMessageMap(PurapKeyConstants.ERROR_PURCHASE_ORDER_PDF, generatePDFErrors);
477             throw new ValidationException("printing purchase order for first transmission failed");
478         }
479         if (ObjectUtils.isNotNull(po.getPurchaseOrderFirstTransmissionTimestamp())) {
480             // should not call this method for first transmission if document has already been transmitted
481             String errorMsg = "Method to perform first transmit was called on document (doc id " + documentNumber + ") with already filled in 'first transmit date'";
482             LOG.error(errorMsg);
483             throw new RuntimeException(errorMsg);
484         }
485         Timestamp currentDate = dateTimeService.getCurrentTimestamp();
486         po.setPurchaseOrderFirstTransmissionTimestamp(currentDate);
487         po.setPurchaseOrderLastTransmitTimestamp(currentDate);
488         po.setOverrideWorkflowButtons(Boolean.FALSE);
489         boolean performedAction = purapWorkflowIntegrationService.takeAllActionsForGivenCriteria(po, "Action taken automatically as part of document initial print transmission", PurapConstants.PurchaseOrderStatuses.NODE_DOCUMENT_TRANSMISSION, GlobalVariables.getUserSession().getPerson(), null);
490         if (!performedAction) {
491             Person systemUserPerson = getPersonService().getPersonByPrincipalName(getOleSelectDocumentService().getSelectParameterValue(OLEConstants.SYSTEM_USER));
492             purapWorkflowIntegrationService.takeAllActionsForGivenCriteria(po, "Action taken automatically as part of document initial print transmission by user " + GlobalVariables.getUserSession().getPerson().getName(), PurapConstants.PurchaseOrderStatuses.NODE_DOCUMENT_TRANSMISSION, systemUserPerson, getOleSelectDocumentService().getSelectParameterValue(OLEConstants.SYSTEM_USER));
493         }
494         po.setOverrideWorkflowButtons(Boolean.TRUE);
495         if (!po.getApplicationDocumentStatus().equals(PurapConstants.PurchaseOrderStatuses.APPDOC_OPEN)) {
496             attemptSetupOfInitialOpenOfDocument(po);
497         }
498         purapService.saveDocumentNoValidation(po);
499     }
500 
501     /**
502      * @see org.kuali.ole.module.purap.document.service.PurchaseOrderService#performPurchaseOrderPreviewPrinting(java.lang.String,
503      * java.io.ByteArrayOutputStream)
504      */
505     @Override
506     public void performPurchaseOrderPreviewPrinting(String documentNumber, ByteArrayOutputStream baosPDF) {
507         performPrintPurchaseOrderPDFOnly(documentNumber, baosPDF);
508     }
509 
510     /**
511      * @see org.kuali.ole.module.purap.document.service.PurchaseOrderService#performPrintPurchaseOrderPDFOnly(java.lang.String,
512      * java.io.ByteArrayOutputStream)
513      */
514     @Override
515     public void performPrintPurchaseOrderPDFOnly(String documentNumber, ByteArrayOutputStream baosPDF) {
516         PurchaseOrderDocument po = getPurchaseOrderByDocumentNumber(documentNumber);
517         String environment = kualiConfigurationService.getPropertyValueAsString(OLEConstants.ENVIRONMENT_KEY);
518         Collection<String> generatePDFErrors = printService.generatePurchaseOrderPdf(po, baosPDF, environment, null);
519         if (!generatePDFErrors.isEmpty()) {
520             addStringErrorMessagesToMessageMap(PurapKeyConstants.ERROR_PURCHASE_ORDER_PDF, generatePDFErrors);
521             throw new ValidationException("printing purchase order for first transmission failed");
522         }
523     }
524 
525     /**
526      * @see org.kuali.ole.module.purap.document.service.PurchaseOrderService#retransmitPurchaseOrderPDF(org.kuali.ole.module.purap.document.PurchaseOrderDocument,
527      * java.io.ByteArrayOutputStream)
528      */
529     @Override
530     public void retransmitPurchaseOrderPDF(PurchaseOrderDocument po, ByteArrayOutputStream baosPDF) {
531 
532         String environment = kualiConfigurationService.getPropertyValueAsString(OLEConstants.ENVIRONMENT_KEY);
533         List<PurchaseOrderItem> items = po.getItems();
534         List<PurchaseOrderItem> retransmitItems = new ArrayList<PurchaseOrderItem>();
535         for (PurchaseOrderItem item : items) {
536             if (item.isItemSelectedForRetransmitIndicator()) {
537                 retransmitItems.add(item);
538             }
539         }
540         Collection<String> generatePDFErrors = printService.generatePurchaseOrderPdfForRetransmission(po, baosPDF, environment, retransmitItems);
541 
542         if (generatePDFErrors.size() > 0) {
543             addStringErrorMessagesToMessageMap(PurapKeyConstants.ERROR_PURCHASE_ORDER_PDF, generatePDFErrors);
544             throw new ValidationException("found errors while trying to print po with doc id " + po.getDocumentNumber());
545         }
546         po.setPurchaseOrderLastTransmitTimestamp(dateTimeService.getCurrentTimestamp());
547         purapService.saveDocumentNoValidation(po);
548     }
549 
550     /**
551      * This method creates a new Purchase Order Document using the given document type based off the given source document. This
552      * method will return null if the source document given is null.<br>
553      * <br>
554      * ** THIS METHOD DOES NOT SAVE EITHER THE GIVEN SOURCE DOCUMENT OR THE NEW DOCUMENT CREATED
555      *
556      * @param sourceDocument - document the new Purchase Order Document should be based off of in terms of data
557      * @param docType        - document type of the potential new Purchase Order Document
558      * @return the new Purchase Order Document of the given document type or null if the given source document is null
559      * @throws WorkflowException if a new document cannot be created using the given type
560      */
561     protected PurchaseOrderDocument createPurchaseOrderDocumentFromSourceDocument(PurchaseOrderDocument sourceDocument, String docType) throws WorkflowException {
562         if (ObjectUtils.isNull(sourceDocument)) {
563             String errorMsg = "Attempting to create new PO of type '" + docType + "' from source PO doc that is null";
564             LOG.error(errorMsg);
565             throw new RuntimeException(errorMsg);
566         }
567 
568         PurchaseOrderDocument newPurchaseOrderChangeDocument = (PurchaseOrderDocument) documentService.getNewDocument(docType);
569         newPurchaseOrderChangeDocument.setAccountDistributionMethod(sourceDocument.getAccountDistributionMethod());
570 
571         Set classesToExclude = new HashSet();
572         Class sourceObjectClass = FinancialSystemTransactionalDocumentBase.class;
573         classesToExclude.add(sourceObjectClass);
574         while (sourceObjectClass.getSuperclass() != null) {
575             sourceObjectClass = sourceObjectClass.getSuperclass();
576             classesToExclude.add(sourceObjectClass);
577         }
578         PurApObjectUtils.populateFromBaseWithSuper(sourceDocument, newPurchaseOrderChangeDocument, PurapConstants.uncopyableFieldsForPurchaseOrder(), classesToExclude);
579         newPurchaseOrderChangeDocument.getDocumentHeader().setDocumentDescription(sourceDocument.getDocumentHeader().getDocumentDescription());
580         newPurchaseOrderChangeDocument.getDocumentHeader().setOrganizationDocumentNumber(sourceDocument.getDocumentHeader().getOrganizationDocumentNumber());
581         newPurchaseOrderChangeDocument.getDocumentHeader().setExplanation(sourceDocument.getDocumentHeader().getExplanation());
582         newPurchaseOrderChangeDocument.setPurchaseOrderCurrentIndicator(false);
583         newPurchaseOrderChangeDocument.setPendingActionIndicator(false);
584 
585         // TODO f2f: what is this doing?
586         // Need to find a way to make the ManageableArrayList to expand and populating the items and
587         // accounts, otherwise it will complain about the account on item 1 is missing.
588         for (PurApItem item : (List<PurApItem>) newPurchaseOrderChangeDocument.getItems()) {
589             item.getSourceAccountingLines().iterator();
590             // we only need to do this once to apply to all items, so we can break out of the loop now
591             SequenceAccessorService sas = SpringContext.getBean(SequenceAccessorService.class);
592             Integer itemIdentifier = sas.getNextAvailableSequenceNumber("PO_ITM_ID", PurApItem.class).intValue();
593             item.setItemIdentifier(itemIdentifier);
594         }
595 
596         updateCapitalAssetRelatedCollections(newPurchaseOrderChangeDocument);
597         newPurchaseOrderChangeDocument.refreshNonUpdateableReferences();
598 
599         return newPurchaseOrderChangeDocument;
600     }
601 
602     protected void updateCapitalAssetRelatedCollections(PurchaseOrderDocument newDocument) {
603 
604         for (PurchasingCapitalAssetItem capitalAssetItem : newDocument.getPurchasingCapitalAssetItems()) {
605             Integer lineNumber = capitalAssetItem.getPurchasingItem().getItemLineNumber();
606             PurApItem newItem = newDocument.getItemByLineNumber(lineNumber.intValue());
607             capitalAssetItem.setItemIdentifier(newItem.getItemIdentifier());
608             capitalAssetItem.setPurchasingDocument(newDocument);
609             capitalAssetItem.setCapitalAssetSystemIdentifier(null);
610             CapitalAssetSystem oldSystem = capitalAssetItem.getPurchasingCapitalAssetSystem();
611             capitalAssetItem.setPurchasingCapitalAssetSystem(new PurchaseOrderCapitalAssetSystem(oldSystem));
612 
613         }
614     }
615 
616     /**
617      * @see org.kuali.ole.module.purap.document.service.PurchaseOrderService#createAndSavePotentialChangeDocument(java.lang.String,
618      * java.lang.String, java.lang.String)
619      */
620     @Override
621     public PurchaseOrderDocument createAndSavePotentialChangeDocument(String documentNumber, String docType, String currentDocumentStatusCode) {
622         PurchaseOrderDocument currentDocument = getPurchaseOrderByDocumentNumber(documentNumber);
623 
624         try {
625             PurchaseOrderDocument newDocument = createPurchaseOrderDocumentFromSourceDocument(currentDocument, docType);
626 
627             if (ObjectUtils.isNotNull(newDocument)) {
628                 newDocument.updateAndSaveAppDocStatus(PurapConstants.PurchaseOrderStatuses.APPDOC_CHANGE_IN_PROCESS);
629 
630                 // set status if needed
631                 if (StringUtils.isNotBlank(currentDocumentStatusCode)) {
632                     currentDocument.updateAndSaveAppDocStatus(currentDocumentStatusCode);
633                 }
634                 try {
635                     documentService.saveDocument(newDocument, DocumentSystemSaveEvent.class);
636                 }
637                 // if we catch a ValidationException it means the new PO doc found errors
638                 catch (ValidationException ve) {
639                     throw ve;
640                 }
641                 // if no validation exception was thrown then rules have passed and we are ok to edit the current PO
642                 currentDocument.setPendingActionIndicator(true);
643                 Note note = new Note();
644                 List<Note> noteList = new ArrayList<Note>();
645                 currentDocument.setNotes(noteList);
646                 saveDocumentNoValidationUsingClearMessageMap(currentDocument);
647                 // savePurchaseOrderData(currentDocument);
648 
649                 return newDocument;
650             } else {
651                 String errorMsg = "Attempting to create new PO of type '" + docType + "' from source PO doc id "
652                         + documentNumber + " returned null for new document";
653                 LOG.error(errorMsg);
654                 throw new RuntimeException(errorMsg);
655             }
656         } catch (WorkflowException we) {
657             String errorMsg = "Workflow Exception caught trying to create and save PO document of type '" + docType
658                     + "' using source document with doc id '" + documentNumber + "'";
659             LOG.error(errorMsg, we);
660             throw new RuntimeException(errorMsg, we);
661         }
662     }
663 
664     /**
665      * @see org.kuali.ole.module.purap.document.service.PurchaseOrderService#createAndRoutePotentialChangeDocument(java.lang.String,
666      * java.lang.String, java.lang.String, java.util.List, java.lang.String)
667      */
668     @Override
669     public PurchaseOrderDocument createAndRoutePotentialChangeDocument(String documentNumber, String docType, String annotation, List adhocRoutingRecipients, String currentDocumentStatusCode) {
670         PurchaseOrderDocument currentDocument = getPurchaseOrderByDocumentNumber(documentNumber);
671 
672         try {
673             currentDocument.updateAndSaveAppDocStatus(currentDocumentStatusCode);
674         } catch (WorkflowException e) {
675             throw new RuntimeException("Error saving routing data while saving document with id " + currentDocument.getDocumentNumber(), e);
676         }
677 
678         try {
679             PurchaseOrderDocument newDocument = createPurchaseOrderDocumentFromSourceDocument(currentDocument, docType);
680             // newDocument.setStatusCode(PurchaseOrderStatuses.APPDOC_CHANGE_IN_PROCESS);
681             newDocument.updateAndSaveAppDocStatus(PurapConstants.PurchaseOrderStatuses.APPDOC_CHANGE_IN_PROCESS);
682             if (ObjectUtils.isNotNull(newDocument)) {
683                 try {
684                     // set the pending indictor before routing, so that when routing is done in synch mode, the pending indicator
685                     // won't be set again after route finishes and cause inconsistency
686                     currentDocument.setPendingActionIndicator(true);
687                     documentService.routeDocument(newDocument, annotation, adhocRoutingRecipients);
688                 }
689                 // if we catch a ValidationException it means the new PO doc found errors
690                 catch (ValidationException ve) {
691                     // clear the pending indictor if an exception occurs, to leave the existing PO intact
692                     currentDocument.setPendingActionIndicator(false);
693                     //savePurchaseOrderData(currentDocument);
694                     saveDocumentNoValidationUsingClearMessageMap(currentDocument);
695                     throw ve;
696                 }
697                 return newDocument;
698             } else {
699                 String errorMsg = "Attempting to create new PO of type '" + docType + "' from source PO doc id " + documentNumber + " returned null for new document";
700                 LOG.error(errorMsg);
701                 throw new RuntimeException(errorMsg);
702             }
703         } catch (WorkflowException we) {
704             String errorMsg = "Workflow Exception caught trying to create and route PO document of type '" + docType + "' using source document with doc id '" + documentNumber + "'";
705             LOG.error(errorMsg, we);
706             throw new RuntimeException(errorMsg, we);
707         }
708     }
709 
710     /**
711      * @see org.kuali.ole.module.purap.document.service.PurchaseOrderService#createAndSavePurchaseOrderSplitDocument(java.util.List,
712      * java.lang.String, boolean)
713      */
714     @Override
715     public PurchaseOrderSplitDocument createAndSavePurchaseOrderSplitDocument(List<PurchaseOrderItem> newPOItems, PurchaseOrderDocument currentDocument, boolean copyNotes, String splitNoteText) {
716 
717         if (ObjectUtils.isNull(currentDocument)) {
718             String errorMsg = "Attempting to create new PO of type PurchaseOrderSplitDocument from source PO doc that is null";
719             LOG.error(errorMsg);
720             throw new RuntimeException(errorMsg);
721         }
722         // Following code added to add notes to the current PO Document
723         try {
724             Note splitNote = SpringContext.getBean(DocumentService.class).createNoteFromDocument(currentDocument, splitNoteText);
725             currentDocument.addNote(splitNote);
726             noteService.save(splitNote);
727         } catch (Exception e) {
728             throw new RuntimeException(e);
729         }
730         String documentNumber = currentDocument.getDocumentNumber();
731 
732         try {
733             // Create the new Split PO document (throws WorkflowException)
734             PurchaseOrderSplitDocument newDocument = (PurchaseOrderSplitDocument) documentService.getNewDocument(PurchaseOrderDocTypes.PURCHASE_ORDER_SPLIT_DOCUMENT);
735 
736             if (ObjectUtils.isNotNull(newDocument)) {
737 
738                 // Prepare for copying fields over from the current document.
739                 Set<Class> classesToExclude = getClassesToExcludeFromCopy();
740                 Map<String, Class> uncopyableFields = PurapConstants.UNCOPYABLE_FIELDS_FOR_PO;
741                 uncopyableFields.putAll(PurapConstants.uncopyableFieldsForSplitPurchaseOrder());
742 
743                 // Copy all fields over from the current document except the items and the above-specified fields.
744                 PurApObjectUtils.populateFromBaseWithSuper(currentDocument, newDocument, uncopyableFields, classesToExclude);
745                 newDocument.getDocumentHeader().setDocumentDescription(currentDocument.getDocumentHeader().getDocumentDescription());
746                 newDocument.getDocumentHeader().setOrganizationDocumentNumber(currentDocument.getDocumentHeader().getOrganizationDocumentNumber());
747                 newDocument.setPurchaseOrderCurrentIndicator(true);
748                 newDocument.setPendingActionIndicator(false);
749 
750                 newDocument.setAccountDistributionMethod(currentDocument.getAccountDistributionMethod());
751                 // Add in and renumber the items that the new document should have.
752                 newDocument.setItems(newPOItems);
753                 purapService.addBelowLineItems(newDocument);
754                 newDocument.renumberItems(0);
755 
756                 newDocument.setPostingYear(currentDocument.getPostingYear());
757 
758                 if (copyNotes) {
759                     // Copy the old notes, except for the one that contains the split note text.
760                     List<Note> notes = currentDocument.getNotes();
761                     int noteLength = notes.size();
762                     if (noteLength > 0) {
763                         notes.subList(noteLength - 1, noteLength).clear();
764                         for (Note note : notes) {
765                             try {
766                                 Note copyingNote = documentService.createNoteFromDocument(newDocument, note.getNoteText());
767                                 newDocument.addNote(copyingNote);
768                                 noteService.save(copyingNote);
769                             } catch (Exception e) {
770                                 throw new RuntimeException(e);
771                             }
772                         }
773                     }
774                 }
775 
776                 newDocument.updateAndSaveAppDocStatus(PurapConstants.PurchaseOrderStatuses.APPDOC_IN_PROCESS);
777 
778                 // fix references before saving
779                 fixItemReferences(newDocument);
780 
781                 // need to save the document first before creating the note
782                 purapService.saveDocumentNoValidation(newDocument);
783 
784                 // Modify the split note text and add the note.
785                 splitNoteText = splitNoteText.substring(splitNoteText.indexOf(":") + 1);
786                 splitNoteText = PurapConstants.PODocumentsStrings.SPLIT_NOTE_PREFIX_NEW_DOC + currentDocument.getPurapDocumentIdentifier() + " : " + splitNoteText;
787                 try {
788                     Note splitNote = documentService.createNoteFromDocument(newDocument, splitNoteText);
789                     newDocument.addNote(splitNote);
790                     noteService.save(splitNote);
791                 } catch (Exception e) {
792                     throw new RuntimeException(e);
793                 }
794 
795                 return newDocument;
796             } else {
797                 String errorMsg = "Attempting to create new PO of type 'PurchaseOrderSplitDocument' from source PO doc id " + documentNumber + " returned null for new document";
798                 LOG.error(errorMsg);
799                 throw new RuntimeException(errorMsg);
800             }
801         } catch (WorkflowException we) {
802             String errorMsg = "Workflow Exception caught trying to create and save PO document of type PurchaseOrderSplitDocument using source document with doc id '" + documentNumber + "'";
803             LOG.error(errorMsg, we);
804             throw new RuntimeException(errorMsg, we);
805         }
806     }
807 
808     /**
809      * Gets a set of classes to exclude from those whose fields will be copied during a copy operation from one Document to another.
810      *
811      * @return A Set<Class>
812      */
813     protected Set<Class> getClassesToExcludeFromCopy() {
814         Set<Class> classesToExclude = new HashSet<Class>();
815         Class sourceObjectClass = DocumentBase.class;
816         classesToExclude.add(sourceObjectClass);
817         while (sourceObjectClass.getSuperclass() != null) {
818             sourceObjectClass = sourceObjectClass.getSuperclass();
819             classesToExclude.add(sourceObjectClass);
820         }
821         return classesToExclude;
822     }
823 
824     /**
825      * Returns the current route node name.
826      *
827      * @param wd The KualiWorkflowDocument object whose current route node we're trying to get.
828      * @return The current route node name.
829      * @throws WorkflowException
830      */
831     protected String getCurrentRouteNodeName(WorkflowDocument wd) throws WorkflowException {
832         String[] nodeNames = (String[]) wd.getNodeNames().toArray();
833         if ((nodeNames == null) || (nodeNames.length == 0)) {
834             return null;
835         } else {
836             return nodeNames[0];
837         }
838     }
839 
840     /**
841      * @see org.kuali.ole.module.purap.document.service.PurchaseOrderService#completePurchaseOrder(org.kuali.ole.module.purap.document.PurchaseOrderDocument)
842      */
843     @Override
844     public void completePurchaseOrder(PurchaseOrderDocument po) {
845         LOG.debug("completePurchaseOrder() started");
846         setCurrentAndPendingIndicatorsForApprovedPODocuments(po);
847         setupDocumentForPendingFirstTransmission(po);
848 
849         // check thresholds to see if receiving is required for purchase order
850         if (!po.isReceivingDocumentRequiredIndicator()) {
851             setReceivingRequiredIndicatorForPurchaseOrder(po);
852         }
853 
854         // update the vendor record if the commodity code used on the PO is not already associated with the vendor.
855         updateVendorCommodityCode(po);
856 
857         // PERFORM ANY LOGIC THAT COULD POTENTIALLY CAUSE THE DOCUMENT TO FAIL BEFORE THIS LINE
858         // FOLLOWING LINES COULD INVOLVE TRANSMITTING THE PO TO THE VENDOR WHICH WILL NOT BE REVERSED IN A TRANSACTION ROLLBACK
859 
860         // if the document is set in a Pending Transmission status then don't OPEN the PO just leave it as is
861         if (!po.getRequisitionSourceCode().equalsIgnoreCase(PurapConstants.RequisitionSources.MANUAL_INGEST)) {
862             if (!(po.getOrderType().getPurchaseOrderType()).equals(OLEConstants.APPROVAL)) {
863                 List<PurApItem> items = po.getItems();
864                 fileNameList = new ArrayList();
865                 for (PurApItem item : items) {
866                     initiateTransmission(po, item);
867                 }
868                 sendEmail();
869             }
870         }
871         //initiateTransmission(po);
872 
873 
874         if (!PurchaseOrderStatuses.STATUSES_BY_TRANSMISSION_TYPE.values().contains(po.getApplicationDocumentStatus())) {
875             attemptSetupOfInitialOpenOfDocument(po);
876         } else if (PurchaseOrderStatuses.APPDOC_PENDING_CXML.equals(po.getApplicationDocumentStatus())) {
877             completeB2BPurchaseOrder(po);
878         } else if (PurchaseOrderStatuses.APPDOC_PENDING_PRINT.equals(po.getApplicationDocumentStatus())) {
879             // default to using user that routed PO
880             String userToRouteFyi = po.getDocumentHeader().getWorkflowDocument().getRoutedByPrincipalId();
881             if (po.getPurchaseOrderAutomaticIndicator()) {
882                 // if APO, use the user that initiated the requisition
883                 RequisitionDocument req = requisitionService.getRequisitionById(po.getRequisitionIdentifier());
884                 userToRouteFyi = req.getDocumentHeader().getWorkflowDocument().getInitiatorPrincipalId();
885             }
886 
887             po.getDocumentHeader().getWorkflowDocument().adHocToPrincipal(ActionRequestType.FYI, po.getDocumentHeader().getWorkflowDocument().getCurrentNodeNames().iterator().next(), "This PO is ready for printing and distribution.", userToRouteFyi, "", true, "PRINT");
888         }
889 
890     }
891 
892 
893     protected boolean completeB2BPurchaseOrder(PurchaseOrderDocument po) {
894         String errors = b2bPurchaseOrderService.sendPurchaseOrder(po);
895         if (StringUtils.isEmpty(errors)) {
896             // PO sent successfully; change status to OPEN
897             attemptSetupOfInitialOpenOfDocument(po);
898             po.setPurchaseOrderLastTransmitTimestamp(dateTimeService.getCurrentTimestamp());
899             return true;
900         } else {
901             // PO transmission failed; record errors and change status to "cxml failed"
902             try {
903                 String noteText = "Unable to transmit the PO for the following reasons:\n" + errors;
904                 int noteMaxSize = dataDictionaryService.getAttributeMaxLength("Note", "noteText");
905 
906                 // Break up the note into multiple pieces if the note is too large to fit in the database field.
907                 while (noteText.length() > noteMaxSize) {
908                     int fromIndex = 0;
909                     String noteText1 = noteText.substring(0, noteMaxSize);
910                     Note note1 = documentService.createNoteFromDocument(po, noteText1);
911                     po.addNote(note1);
912                     documentService.saveDocumentNotes(po);
913                     noteText = noteText.substring(noteMaxSize);
914                 }
915 
916                 Note note = documentService.createNoteFromDocument(po, noteText);
917                 po.addNote(note);
918                 documentService.saveDocumentNotes(po);
919             } catch (Exception e) {
920                 throw new RuntimeException(e);
921             }
922 
923             try {
924                 po.updateAndSaveAppDocStatus(PurchaseOrderStatuses.APPDOC_CXML_ERROR);
925             } catch (WorkflowException e) {
926                 throw new RuntimeException("Error saving routing data while saving document with id " + po.getDocumentNumber(), e);
927             }
928 
929             return false;
930         }
931     }
932 
933     @Override
934     public void retransmitB2BPurchaseOrder(PurchaseOrderDocument po) {
935         if (completeB2BPurchaseOrder(po)) {
936             KNSGlobalVariables.getMessageList().add(PurapKeyConstants.B2B_PO_RETRANSMIT_SUCCESS);
937         } else {
938             GlobalVariables.getMessageMap().putError(OLEConstants.GLOBAL_ERRORS, PurapKeyConstants.B2B_PO_RETRANSMIT_FAILED);
939         }
940         purapService.saveDocumentNoValidation(po);
941     }
942 
943     @Override
944     public void completePurchaseOrderAmendment(PurchaseOrderDocument poa) {
945         LOG.debug("completePurchaseOrderAmendment() started");
946 
947         setCurrentAndPendingIndicatorsForApprovedPODocuments(poa);
948 
949         if (SpringContext.getBean(PaymentRequestService.class).hasActivePaymentRequestsForPurchaseOrder(poa.getPurapDocumentIdentifier())) {
950             poa.setPaymentRequestPositiveApprovalIndicator(true);
951             poa.setReceivingDocumentRequiredIndicator(false);
952         }
953         // check thresholds to see if receiving is required for purchase order amendment
954         if (!poa.isReceivingDocumentRequiredIndicator() &&
955                 !SpringContext.getBean(PaymentRequestService.class).hasActivePaymentRequestsForPurchaseOrder(poa.getPurapDocumentIdentifier())) {
956             setReceivingRequiredIndicatorForPurchaseOrder(poa);
957         }
958 
959         // if unordered items have been added to the PO then send an FYI to all fiscal officers
960         if (hasNewUnorderedItem(poa)) {
961             sendFyiForNewUnorderedItems(poa);
962         }
963         DocumentRouteHeaderValue routeHeader = ((RouteHeaderService) KEWServiceLocator.getService(KEWServiceLocator.DOC_ROUTE_HEADER_SRV)).getRouteHeader(poa.getDocumentNumber());
964         String status = routeHeader.getDocRouteStatus();
965         if (status.equals(KewApiConstants.ROUTE_HEADER_PROCESSED_CD)) {
966             List<PurApItem> items = poa.getItems();
967             for (PurApItem item : items) {
968                 initiateTransmission(poa, item);
969             }
970             //initiateTransmission(poa);
971         }
972 
973     }
974 
975     /**
976      * If there are commodity codes on the items on the PurchaseOrderDocument that haven't existed yet on the vendor that the
977      * PurchaseOrderDocument is using, then we will spawn a new VendorDetailMaintenanceDocument automatically to update the vendor
978      * with the commodity codes that aren't already existing on the vendor.
979      *
980      * @param po The PurchaseOrderDocument containing the vendor that we want to update.
981      */
982     @Override
983     public void updateVendorCommodityCode(PurchaseOrderDocument po) {
984         String noteText = "";
985         VendorDetail oldVendorDetail = po.getVendorDetail();
986         VendorDetail newVendorDetail = updateVendorWithMissingCommodityCodesIfNecessary(po);
987         if (newVendorDetail != null) {
988             try {
989                 // spawn a new vendor maintenance document to add the note
990                 MaintenanceDocument vendorMaintDoc = null;
991                 try {
992                     vendorMaintDoc = (MaintenanceDocument) documentService.getNewDocument("PVEN");
993                     vendorMaintDoc.getDocumentHeader().setDocumentDescription("Automatically spawned from PO");
994                     vendorMaintDoc.getOldMaintainableObject().setBusinessObject(oldVendorDetail);
995                     vendorMaintDoc.getNewMaintainableObject().setBusinessObject(newVendorDetail);
996                     vendorMaintDoc.getNewMaintainableObject().setMaintenanceAction(OLEConstants.MAINTENANCE_EDIT_ACTION);
997                     vendorMaintDoc.getNewMaintainableObject().setDocumentNumber(vendorMaintDoc.getDocumentNumber());
998                     boolean isVendorLocked = checkForLockingDocument(vendorMaintDoc);
999                     if (!isVendorLocked) {
1000                         // validating vendor doc to capture exception before trying to route which if exception happens in
1001                         // docService, then PO will fail too
1002                         vendorMaintDoc.validateBusinessRules(new RouteDocumentEvent(vendorMaintDoc));
1003                         addNoteForCommodityCodeToVendor(vendorMaintDoc.getNewMaintainableObject(), vendorMaintDoc.getDocumentNumber(), po.getPurapDocumentIdentifier());
1004                         documentService.routeDocument(vendorMaintDoc, null, null);
1005                     } else {
1006                         // Add a note to the PO to tell the users that we can't automatically update the vendor because it's locked.
1007                         noteText = "Unable to automatically update vendor because it is locked";
1008                     }
1009                 } catch (Exception e) {
1010                     if (ObjectUtils.isNull(vendorMaintDoc)) {
1011                         noteText = "Unable to create a new VendorDetailMaintenanceDocument to update the vendor with new commodity codes";
1012                     } else {
1013                         noteText = "Unable to route a new VendorDetailMaintenanceDocument to update the vendor with new commodity codes";
1014                     }
1015                 } finally {
1016                     if (StringUtils.isNotBlank(noteText)) {
1017                         // update on purchase order notes
1018                         Note note = documentService.createNoteFromDocument(po, noteText);
1019                         po.addNote(note);
1020                         noteService.save(note);
1021                     }
1022                 }
1023             } catch (Exception e) {
1024                 LOG.error("updateVendorCommodityCode() unable to add a note(" + noteText + ") to PO document " + po.getDocumentNumber());
1025                 throw new RuntimeException(e);
1026             }
1027         }
1028     }
1029 
1030     /**
1031      * Creates a note to be added to the Vendor Maintenance Document which is spawned from the PurchaseOrderDocument.
1032      *
1033      * @param maintainable
1034      * @param documentNumber
1035      * @param poID
1036      */
1037     protected void addNoteForCommodityCodeToVendor(Maintainable maintainable, String documentNumber, Integer poID) {
1038         Note newBONote = new Note();
1039         newBONote.setNoteText("Change vendor document ID <" + documentNumber + ">. Document was automatically created from PO <" + poID + "> to add commodity codes used on this PO that were not yet assigned to this vendor.");
1040         try {
1041 
1042             newBONote = noteService.createNote(newBONote, maintainable.getBusinessObject(), GlobalVariables.getUserSession().getPrincipalId());
1043             newBONote.setNotePostedTimestampToCurrent();
1044         } catch (Exception e) {
1045             throw new RuntimeException("Caught Exception While Trying To Add Note to Vendor", e);
1046         }
1047         List<Note> noteList = noteService.getByRemoteObjectId(maintainable.getBusinessObject().getObjectId());
1048         noteList.add(newBONote);
1049         noteService.saveNoteList(noteList);
1050     }
1051 
1052     /**
1053      * Checks whether the vendor is currently locked.
1054      *
1055      * @param document The MaintenanceDocument containing the vendor.
1056      * @return boolean true if the vendor is currently locked and false otherwise.
1057      */
1058     protected boolean checkForLockingDocument(MaintenanceDocument document) {
1059         String blockingDocId = maintenanceDocumentService.getLockingDocumentId(document);
1060         if (StringUtils.isBlank(blockingDocId)) {
1061             return false;
1062         } else {
1063             return true;
1064         }
1065     }
1066 
1067     /**
1068      * @see org.kuali.ole.module.purap.document.service.PurchaseOrderService#updateVendorWithMissingCommodityCodesIfNecessary(org.kuali.ole.module.purap.document.PurchaseOrderDocument)
1069      */
1070     @Override
1071     public VendorDetail updateVendorWithMissingCommodityCodesIfNecessary(PurchaseOrderDocument po) {
1072         List<CommodityCode> result = new ArrayList<CommodityCode>();
1073         boolean foundDefault = false;
1074         VendorDetail vendor = (VendorDetail) ObjectUtils.deepCopy(po.getVendorDetail());
1075         for (PurchaseOrderItem item : (List<PurchaseOrderItem>) po.getItems()) {
1076             // Only check on commodity codes if the item is active and is above the line item type.
1077             if (item.getItemType().isLineItemIndicator() && item.isItemActiveIndicator()) {
1078                 CommodityCode cc = item.getCommodityCode();
1079                 if (cc != null && !result.contains(cc)) {
1080                     List<VendorCommodityCode> vendorCommodityCodes = po.getVendorDetail().getVendorCommodities();
1081                     boolean foundMatching = false;
1082                     for (VendorCommodityCode vcc : vendorCommodityCodes) {
1083                         if (vcc.getCommodityCode().getPurchasingCommodityCode().equals(cc.getPurchasingCommodityCode())) {
1084                             foundMatching = true;
1085                         }
1086                         if (!foundDefault && vcc.isCommodityDefaultIndicator()) {
1087                             foundDefault = true;
1088                         }
1089                     }
1090                     if (!foundMatching) {
1091                         result.add(cc);
1092                         VendorCommodityCode vcc = new VendorCommodityCode(vendor.getVendorHeaderGeneratedIdentifier(), vendor.getVendorDetailAssignedIdentifier(), cc, true);
1093                         vcc.setActive(true);
1094                         if (!foundDefault) {
1095                             vcc.setCommodityDefaultIndicator(true);
1096                             foundDefault = true;
1097                         }
1098                         vendor.getVendorCommodities().add(vcc);
1099                     }
1100                 }
1101             }
1102         }
1103         if (result.size() > 0) {
1104             // We also have to add to the old vendor detail's vendorCommodities if we're adding to the new
1105             // vendor detail's vendorCommodities.
1106             for (int i = 0; i < result.size(); i++) {
1107                 po.getVendorDetail().getVendorCommodities().add(new VendorCommodityCode());
1108             }
1109             return vendor;
1110         } else {
1111             return null;
1112         }
1113     }
1114 
1115     /**
1116      * Update the purchase order document with the appropriate status for pending first transmission based on the transmission type.
1117      *
1118      * @param po The purchase order document whose status to be updated.
1119      */
1120     protected void setupDocumentForPendingFirstTransmission(PurchaseOrderDocument po) {
1121         if (POTransmissionMethods.PRINT.equals(po.getPurchaseOrderTransmissionMethodCode()) || POTransmissionMethods.FAX.equals(po.getPurchaseOrderTransmissionMethodCode()) || POTransmissionMethods.ELECTRONIC.equals(po.getPurchaseOrderTransmissionMethodCode())) {
1122             String newStatusCode = PurchaseOrderStatuses.STATUSES_BY_TRANSMISSION_TYPE.get(po.getPurchaseOrderTransmissionMethodCode());
1123             if (LOG.isDebugEnabled()) {
1124                 LOG.debug("setupDocumentForPendingFirstTransmission() Purchase Order Transmission Type is '" + po.getPurchaseOrderTransmissionMethodCode() + "' setting status to '" + newStatusCode + "'");
1125             }
1126             try {
1127                 po.updateAndSaveAppDocStatus(newStatusCode);
1128             } catch (WorkflowException e) {
1129                 throw new RuntimeException("Error saving routing data while saving document with id " + po.getDocumentNumber(), e);
1130             }
1131         }
1132     }
1133 
1134     /**
1135      * If the status of the purchase order is not OPEN and the initial open date is null, sets the initial open date to current date
1136      * and update the status to OPEN, then save the purchase order.
1137      *
1138      * @param po The purchase order document whose initial open date and status we want to update.
1139      */
1140     protected void attemptSetupOfInitialOpenOfDocument(PurchaseOrderDocument po) {
1141         if (LOG.isInfoEnabled()) {
1142             LOG.info("attemptSetupOfInitialOpenOfDocument() started using document with doc id " + po.getDocumentNumber());
1143         }
1144 
1145         if (!PurchaseOrderStatuses.APPDOC_OPEN.equals(po.getApplicationDocumentStatus())) {
1146             if (ObjectUtils.isNull(po.getPurchaseOrderInitialOpenTimestamp())) {
1147                 LOG.debug("attemptSetupOfInitialOpenOfDocument() setting initial open date on document");
1148                 po.setPurchaseOrderInitialOpenTimestamp(dateTimeService.getCurrentTimestamp());
1149             } else {
1150                 throw new RuntimeException("Document does not have status code '" + PurchaseOrderStatuses.APPDOC_OPEN + "' on it but value of initial open date is " + po.getPurchaseOrderInitialOpenTimestamp());
1151             }
1152             LOG.info("attemptSetupOfInitialOpenOfDocument() Setting po document id " + po.getDocumentNumber() + " status from '" + po.getApplicationDocumentStatus() + "' to '" + PurchaseOrderStatuses.APPDOC_OPEN + "'");
1153             try {
1154                 po.updateAndSaveAppDocStatus(PurchaseOrderStatuses.APPDOC_OPEN);
1155             } catch (WorkflowException we) {
1156                 throw new RuntimeException("Unable to load a WorkflowDocument object for " + po.getDocumentNumber(), we);
1157             }
1158             po.setApplicationDocumentStatus(PurchaseOrderStatuses.APPDOC_OPEN);
1159             // no need to save here because calling class should handle the save if needed
1160         }
1161 
1162         // Modified for jira OLE-2478
1163         /*else {
1164             LOG.error("attemptSetupOfInitialOpenOfDocument() Found document already in '" + PurchaseOrderStatuses.OPEN + "' status for PO#" + po.getPurapDocumentIdentifier() + "; will not change or update");
1165         }*/
1166     }
1167 
1168     /**
1169      * @see org.kuali.ole.module.purap.document.service.PurchaseOrderService#getCurrentPurchaseOrder(java.lang.Integer)
1170      */
1171     @Override
1172     public PurchaseOrderDocument getCurrentPurchaseOrder(Integer id) {
1173         return getPurchaseOrderByDocumentNumber(purchaseOrderDao.getDocumentNumberForCurrentPurchaseOrder(id));
1174         // TODO hjs: code review (why is this DB call so complicated? wouldn't this method be cleaner and less db calls?)
1175         // return purchaseOrderDao.getCurrentPurchaseOrder(id);
1176     }
1177 
1178     /**
1179      * @see org.kuali.ole.module.purap.document.service.PurchaseOrderService#getPurchaseOrderByDocumentNumber(java.lang.String)
1180      */
1181     @Override
1182     public PurchaseOrderDocument getPurchaseOrderByDocumentNumber(String documentNumber) {
1183         if (ObjectUtils.isNotNull(documentNumber)) {
1184             try {
1185                 PurchaseOrderDocument doc = (PurchaseOrderDocument) documentService.getByDocumentHeaderId(documentNumber);
1186                 if (ObjectUtils.isNotNull(doc)) {
1187                     WorkflowDocument workflowDocument = doc.getDocumentHeader().getWorkflowDocument();
1188                     doc.refreshReferenceObject(OLEPropertyConstants.DOCUMENT_HEADER);
1189                     doc.getDocumentHeader().setWorkflowDocument(workflowDocument);
1190                 }
1191                 return doc;
1192             } catch (WorkflowException e) {
1193                 String errorMessage = "Error getting purchase order document from document service";
1194                 LOG.error("getPurchaseOrderByDocumentNumber() " + errorMessage, e);
1195                 throw new RuntimeException(errorMessage, e);
1196             }
1197         }
1198         return null;
1199     }
1200 
1201     /**
1202      * @see org.kuali.ole.module.purap.document.service.PurchaseOrderService#getOldestPurchaseOrder(org.kuali.ole.module.purap.document.PurchaseOrderDocument,
1203      * org.kuali.ole.module.purap.document.PurchaseOrderDocument)
1204      */
1205     @Override
1206     public PurchaseOrderDocument getOldestPurchaseOrder(PurchaseOrderDocument po, PurchaseOrderDocument documentBusinessObject) {
1207         LOG.debug("entering getOldestPO(PurchaseOrderDocument)");
1208         if (ObjectUtils.isNotNull(po)) {
1209             String oldestDocumentNumber = purchaseOrderDao.getOldestPurchaseOrderDocumentNumber(po.getPurapDocumentIdentifier());
1210             // OLEMI-9746 -- See Harsha's comments...
1211             if (StringUtils.isBlank(oldestDocumentNumber)) {
1212                 return null;
1213             }
1214             if (StringUtils.equals(oldestDocumentNumber, po.getDocumentNumber())) {
1215                 // manually set bo notes - this is mainly done for performance reasons (preferably we could call
1216                 // retrieve doc notes in PersistableBusinessObjectBase but that is protected)
1217                 updateNotes(po, documentBusinessObject);
1218                 LOG.debug("exiting getOldestPO(PurchaseOrderDocument)");
1219                 return po;
1220             } else {
1221                 PurchaseOrderDocument oldestPurchaseOrder = getPurchaseOrderByDocumentNumber(oldestDocumentNumber);
1222                 updateNotes(oldestPurchaseOrder, documentBusinessObject);
1223                 LOG.debug("exiting getOldestPO(PurchaseOrderDocument)");
1224                 return oldestPurchaseOrder;
1225             }
1226         }
1227         return null;
1228     }
1229 
1230     /**
1231      * If the purchase order's object id is not null (I think this means if it's an existing purchase order that had already been
1232      * saved to the db previously), get the notes of the purchase order from the database, fix the notes' fields by calling the
1233      * fixDbNoteFields, then set the notes to the purchase order. Otherwise (I think this means if it's a new purchase order), set
1234      * the notes of this purchase order to be the notes of the documentBusinessObject.
1235      *
1236      * @param po                     The current purchase order.
1237      * @param documentBusinessObject The oldest purchase order whose purapDocumentIdentifier is the same as the po's
1238      *                               purapDocumentIdentifier.
1239      */
1240     protected void updateNotes(PurchaseOrderDocument po, PurchaseOrderDocument documentBusinessObject) {
1241         if (ObjectUtils.isNotNull(documentBusinessObject)) {
1242             if (ObjectUtils.isNotNull(po.getObjectId())) {
1243                 List<Note> dbNotes = noteService.getByRemoteObjectId(po.getObjectId());
1244                 // need to set fields that are not ojb managed (i.e. the notes on the documentBusinessObject may have been modified
1245                 // independently of the ones in the db)
1246                 fixDbNoteFields(documentBusinessObject, dbNotes);
1247                 po.setNotes(dbNotes);
1248             } else {
1249                 po.setNotes(documentBusinessObject.getNotes());
1250             }
1251         }
1252     }
1253 
1254     /**
1255      * This method fixes non ojb managed missing fields from the db
1256      *
1257      * @param documentBusinessObject The oldest purchase order whose purapDocumentIdentifier is the same as the po's
1258      *                               purapDocumentIdentifier.
1259      * @param dbNotes                The notes of the purchase order obtained from the database.
1260      */
1261     protected void fixDbNoteFields(PurchaseOrderDocument documentBusinessObject, List<Note> dbNotes) {
1262         for (int i = 0; i < dbNotes.size(); i++) {
1263             Note dbNote = dbNotes.get(i);
1264             List<Note> currentNotes = documentBusinessObject.getNotes();
1265             if (i < currentNotes.size()) {
1266                 Note currentNote = (currentNotes).get(i);
1267                 // set the fyi from the current note if not empty
1268                 AdHocRouteRecipient fyiNoteRecipient = currentNote.getAdHocRouteRecipient();
1269                 if (ObjectUtils.isNotNull(fyiNoteRecipient)) {
1270                     dbNote.setAdHocRouteRecipient(fyiNoteRecipient);
1271                 }
1272             }
1273         }
1274     }
1275 
1276     /**
1277      * @see org.kuali.ole.module.purap.document.service.PurchaseOrderService#getPurchaseOrderNotes(java.lang.Integer)
1278      */
1279     @Override
1280     public List<Note> getPurchaseOrderNotes(Integer id) {
1281         List<Note> notes = new ArrayList<Note>();
1282 
1283         PurchaseOrderDocument po = getPurchaseOrderByDocumentNumber(purchaseOrderDao.getOldestPurchaseOrderDocumentNumber(id));
1284         if (ObjectUtils.isNotNull(po)) {
1285             notes = noteService.getByRemoteObjectId(po.getObjectId());
1286         }
1287         return notes;
1288     }
1289 
1290     /**
1291      * @see org.kuali.ole.module.purap.document.service.PurchaseOrderService#setCurrentAndPendingIndicatorsForApprovedPODocuments(org.kuali.ole.module.purap.document.PurchaseOrderDocument)
1292      */
1293     @Override
1294     public void setCurrentAndPendingIndicatorsForApprovedPODocuments(PurchaseOrderDocument newPO) {
1295 
1296         // if(newPO.getPurapDocumentIdentifier() == null)
1297         // return;
1298 
1299         // Get the "current PO" that's in the database, i.e. the PO row that contains current indicator = Y
1300         PurchaseOrderDocument oldPO = getCurrentPurchaseOrder(newPO.getPurapDocumentIdentifier());
1301 
1302         if (oldPO == null) {
1303             return;
1304         }
1305 
1306         // If the document numbers between the oldPO and the newPO are different, then this is a PO change document.
1307         if (!oldPO.getDocumentNumber().equals(newPO.getDocumentNumber())) {
1308             // First, we set the indicators for the oldPO to : Current = N and Pending = N
1309             oldPO.setPurchaseOrderCurrentIndicator(false);
1310             oldPO.setPendingActionIndicator(false);
1311 
1312             // set the status and status history of the oldPO to retired version
1313             try {
1314                 oldPO.updateAndSaveAppDocStatus(PurapConstants.PurchaseOrderStatuses.APPDOC_RETIRED_VERSION);
1315             } catch (WorkflowException e) {
1316                 throw new RuntimeException("Error saving routing data while saving document with id " + oldPO.getDocumentNumber(), e);
1317             }
1318 
1319             saveDocumentNoValidationUsingClearMessageMap(oldPO);
1320         }
1321         if (!oldPO.getVendorDetail().getVendorHeaderGeneratedIdentifier().equals(newPO.getVendorDetail().getVendorHeaderGeneratedIdentifier())) {
1322             List<PurApItem> items = oldPO.getItems();
1323             for (PurApItem item : items) {
1324                 initiateTransmission(oldPO, item);
1325             }
1326             // initiateTransmission(oldPO);
1327         }
1328         // Now, we set the "new PO" indicators so that Current = Y and Pending = N
1329         newPO.setPurchaseOrderCurrentIndicator(true);
1330         newPO.setPendingActionIndicator(false);
1331 
1332         // this was never being saved. Should call the method to save the newPO.
1333         // saveDocumentNoValidationUsingClearMessageMap(newPO);
1334     }
1335 
1336     /**
1337      * @see org.kuali.ole.module.purap.document.service.PurchaseOrderService#setCurrentAndPendingIndicatorsForDisapprovedChangePODocuments(org.kuali.ole.module.purap.document.PurchaseOrderDocument)
1338      */
1339     @Override
1340     public void setCurrentAndPendingIndicatorsForDisapprovedChangePODocuments(PurchaseOrderDocument newPO) {
1341         updateCurrentDocumentForNoPendingAction(newPO, PurapConstants.PurchaseOrderStatuses.APPDOC_DISAPPROVED_CHANGE, PurapConstants.PurchaseOrderStatuses.APPDOC_OPEN);
1342     }
1343 
1344     /**
1345      * @see org.kuali.ole.module.purap.document.service.PurchaseOrderService#setCurrentAndPendingIndicatorsForCancelledChangePODocuments(org.kuali.ole.module.purap.document.PurchaseOrderDocument)
1346      */
1347     @Override
1348     public void setCurrentAndPendingIndicatorsForCancelledChangePODocuments(PurchaseOrderDocument newPO) {
1349         updateCurrentDocumentForNoPendingAction(newPO, PurapConstants.PurchaseOrderStatuses.APPDOC_CANCELLED_CHANGE, PurapConstants.PurchaseOrderStatuses.APPDOC_OPEN);
1350     }
1351 
1352     /**
1353      * @see org.kuali.ole.module.purap.document.service.PurchaseOrderService#setCurrentAndPendingIndicatorsForCancelledReopenPODocuments(org.kuali.ole.module.purap.document.PurchaseOrderDocument)
1354      */
1355     @Override
1356     public void setCurrentAndPendingIndicatorsForCancelledReopenPODocuments(PurchaseOrderDocument newPO) {
1357         updateCurrentDocumentForNoPendingAction(newPO, PurapConstants.PurchaseOrderStatuses.APPDOC_CANCELLED_CHANGE, PurapConstants.PurchaseOrderStatuses.APPDOC_CLOSED);
1358     }
1359 
1360     /**
1361      * @see org.kuali.ole.module.purap.document.service.PurchaseOrderService#setCurrentAndPendingIndicatorsForDisapprovedReopenPODocuments(org.kuali.ole.module.purap.document.PurchaseOrderDocument)
1362      */
1363     @Override
1364     public void setCurrentAndPendingIndicatorsForDisapprovedReopenPODocuments(PurchaseOrderDocument newPO) {
1365         updateCurrentDocumentForNoPendingAction(newPO, PurapConstants.PurchaseOrderStatuses.APPDOC_DISAPPROVED_CHANGE, PurapConstants.PurchaseOrderStatuses.APPDOC_CLOSED);
1366     }
1367 
1368     /**
1369      * @see org.kuali.ole.module.purap.document.service.PurchaseOrderService#setCurrentAndPendingIndicatorsForCancelledRemoveHoldPODocuments(org.kuali.ole.module.purap.document.PurchaseOrderDocument)
1370      */
1371     @Override
1372     public void setCurrentAndPendingIndicatorsForCancelledRemoveHoldPODocuments(PurchaseOrderDocument newPO) {
1373         updateCurrentDocumentForNoPendingAction(newPO, PurapConstants.PurchaseOrderStatuses.APPDOC_CANCELLED_CHANGE, PurapConstants.PurchaseOrderStatuses.APPDOC_PAYMENT_HOLD);
1374     }
1375 
1376     /**
1377      * @see org.kuali.ole.module.purap.document.service.PurchaseOrderService#setCurrentAndPendingIndicatorsForDisapprovedRemoveHoldPODocuments(org.kuali.ole.module.purap.document.PurchaseOrderDocument)
1378      */
1379     @Override
1380     public void setCurrentAndPendingIndicatorsForDisapprovedRemoveHoldPODocuments(PurchaseOrderDocument newPO) {
1381         updateCurrentDocumentForNoPendingAction(newPO, PurapConstants.PurchaseOrderStatuses.APPDOC_DISAPPROVED_CHANGE, PurapConstants.PurchaseOrderStatuses.APPDOC_PAYMENT_HOLD);
1382     }
1383 
1384     /**
1385      * Update the statuses of both the old purchase order and the new purchase orders, then save the old and the new purchase
1386      * orders.
1387      *
1388      * @param newPO       The new change purchase order document (e.g. the PurchaseOrderAmendmentDocument that was resulted from the user
1389      *                    clicking on the amend button).
1390      * @param newPOStatus The status to be set on the new change purchase order document.
1391      * @param oldPOStatus The status to be set on the existing (old) purchase order document.
1392      */
1393     protected void updateCurrentDocumentForNoPendingAction(PurchaseOrderDocument newPO, String newPOStatus, String oldPOStatus) {
1394         // Get the "current PO" that's in the database, i.e. the PO row that contains current indicator = Y
1395         PurchaseOrderDocument oldPO = getCurrentPurchaseOrder(newPO.getPurapDocumentIdentifier());
1396         // Set the Pending indicator for the oldPO to N
1397         oldPO.setPendingActionIndicator(false);
1398         try {
1399             oldPO.updateAndSaveAppDocStatus(oldPOStatus);
1400             newPO.updateAndSaveAppDocStatus(newPOStatus);
1401         } catch (WorkflowException e) {
1402             throw new RuntimeException("Error saving routing data while saving document", e);
1403         }
1404 
1405         // savePurchaseOrderData(oldPO);
1406         saveDocumentNoValidationUsingClearMessageMap(oldPO);
1407         saveDocumentNoValidationUsingClearMessageMap(newPO);
1408     }
1409 
1410     @Override
1411     public List<PurchaseOrderQuoteStatus> getPurchaseOrderQuoteStatusCodes() {
1412         List<PurchaseOrderQuoteStatus> poQuoteStatuses = new ArrayList<PurchaseOrderQuoteStatus>();
1413         poQuoteStatuses = (List<PurchaseOrderQuoteStatus>) businessObjectService.findAll(PurchaseOrderQuoteStatus.class);
1414         return poQuoteStatuses;
1415     }
1416 
1417     @Override
1418     public void setReceivingRequiredIndicatorForPurchaseOrder(PurchaseOrderDocument po) {
1419         ThresholdHelper thresholdHelper = new ThresholdHelper(po);
1420         boolean result = thresholdHelper.isReceivingDocumentRequired();
1421         if (result) {
1422             ThresholdSummary thresholdSummary = thresholdHelper.getThresholdSummary();
1423             ReceivingThreshold receivingThreshold = thresholdHelper.getReceivingThreshold();
1424             po.setReceivingDocumentRequiredIndicator(true);
1425 
1426             String notetxt = "Receiving is set to be required because the threshold summary with a total amount of " + thresholdSummary.getTotalAmount();
1427             notetxt += " exceeds the receiving threshold of " + receivingThreshold.getThresholdAmount();
1428             notetxt += " with respect to the threshold criteria ";
1429 
1430             if (thresholdSummary.getThresholdCriteria() == ThresholdHelper.CHART) {
1431                 notetxt += " Chart " + receivingThreshold.getChartOfAccountsCode();
1432             } else if (thresholdSummary.getThresholdCriteria() == ThresholdHelper.CHART_AND_ACCOUNTTYPE) {
1433                 notetxt += " Chart " + receivingThreshold.getChartOfAccountsCode();
1434                 notetxt += " - Account Type " + receivingThreshold.getAccountTypeCode();
1435             } else if (thresholdSummary.getThresholdCriteria() == ThresholdHelper.CHART_AND_SUBFUND) {
1436                 notetxt += " Chart " + receivingThreshold.getChartOfAccountsCode();
1437                 notetxt += " - Sub-Fund " + receivingThreshold.getSubFundGroupCode();
1438             } else if (thresholdSummary.getThresholdCriteria() == ThresholdHelper.CHART_AND_COMMODITYCODE) {
1439                 notetxt += " Chart " + receivingThreshold.getChartOfAccountsCode();
1440                 notetxt += " - Commodity Code " + receivingThreshold.getPurchasingCommodityCode();
1441             } else if (thresholdSummary.getThresholdCriteria() == ThresholdHelper.CHART_AND_OBJECTCODE) {
1442                 notetxt += " Chart " + receivingThreshold.getChartOfAccountsCode();
1443                 notetxt += " - Object code " + receivingThreshold.getFinancialObjectCode();
1444             } else if (thresholdSummary.getThresholdCriteria() == ThresholdHelper.CHART_AND_ORGANIZATIONCODE) {
1445                 notetxt += " Chart " + receivingThreshold.getChartOfAccountsCode();
1446                 notetxt += " - Organization " + receivingThreshold.getOrganizationCode();
1447             } else if (thresholdSummary.getThresholdCriteria() == ThresholdHelper.CHART_AND_VENDOR) {
1448                 notetxt += " Chart " + receivingThreshold.getChartOfAccountsCode();
1449                 notetxt += " - Vendor " + receivingThreshold.getVendorNumber();
1450             }
1451 
1452             try {
1453                 Note note = documentService.createNoteFromDocument(po, notetxt);
1454 //                documentService.addNoteToDocument(po, note);
1455                 noteService.save(note);
1456             } catch (Exception e) {
1457                 throw new RuntimeException(e);
1458             }
1459         }
1460     }
1461 
1462     /**
1463      * @see org.kuali.ole.module.purap.document.service.PurchaseOrderService#hasNewUnorderedItem(org.kuali.ole.module.purap.document.PurchaseOrderDocument)
1464      */
1465     @Override
1466     public boolean hasNewUnorderedItem(PurchaseOrderDocument po) {
1467 
1468         boolean itemAdded = false;
1469 
1470         for (PurchaseOrderItem poItem : (List<PurchaseOrderItem>) po.getItems()) {
1471             // only check, active, above the line, unordered items
1472             if (poItem.isItemActiveIndicator() && poItem.getItemType().isLineItemIndicator() && PurapConstants.ItemTypeCodes.ITEM_TYPE_UNORDERED_ITEM_CODE.equals(poItem.getItemTypeCode())) {
1473 
1474                 // if the item identifier is null its new, or if the item doesn't exist on the current purchase order it's new
1475                 if (poItem.getItemIdentifier() == null || !purchaseOrderDao.itemExistsOnPurchaseOrder(poItem.getItemLineNumber(), purchaseOrderDao.getDocumentNumberForCurrentPurchaseOrder(po.getPurapDocumentIdentifier()))) {
1476                     itemAdded = true;
1477                     break;
1478                 }
1479             }
1480         }
1481 
1482         return itemAdded;
1483     }
1484 
1485     @Override
1486     public boolean isNewUnorderedItem(PurchaseOrderItem poItem) {
1487 
1488         boolean itemAdded = false;
1489 
1490         // only check, active, above the line, unordered items
1491         if (poItem.isItemActiveIndicator() && poItem.getItemType().isLineItemIndicator() && PurapConstants.ItemTypeCodes.ITEM_TYPE_UNORDERED_ITEM_CODE.equals(poItem.getItemTypeCode())) {
1492 
1493             // if the item identifier is null its new, or if the item doesn't exist on the current purchase order it's new
1494             if (poItem.getItemIdentifier() == null || !purchaseOrderDao.itemExistsOnPurchaseOrder(poItem.getItemLineNumber(), purchaseOrderDao.getDocumentNumberForCurrentPurchaseOrder(poItem.getPurchaseOrder().getPurapDocumentIdentifier()))) {
1495                 itemAdded = true;
1496             }
1497         }
1498 
1499         return itemAdded;
1500     }
1501 
1502     @Override
1503     public boolean isNewItemForAmendment(PurchaseOrderItem poItem) {
1504 
1505         boolean itemAdded = false;
1506 
1507         // only check, active, above the line, unordered items
1508         if (poItem.isItemActiveIndicator() && poItem.getItemType().isLineItemIndicator()) {
1509 
1510             // if the item identifier is null its new, or if the item doesn't exist on the current purchase order it's new
1511             if (poItem.getItemIdentifier() == null || !purchaseOrderDao.itemExistsOnPurchaseOrder(poItem.getItemLineNumber(), purchaseOrderDao.getDocumentNumberForCurrentPurchaseOrder(poItem.getPurchaseOrder().getPurapDocumentIdentifier()))) {
1512                 itemAdded = true;
1513             }
1514         }
1515 
1516         return itemAdded;
1517     }
1518 
1519     /**
1520      * Sends an FYI to fiscal officers for new unordered items.
1521      *
1522      * @param po
1523      */
1524     protected void sendFyiForNewUnorderedItems(PurchaseOrderDocument po) {
1525 
1526         List<AdHocRoutePerson> fyiList = createFyiFiscalOfficerListForNewUnorderedItems(po);
1527         String annotation = "Notification of New Unordered Items for Purchase Order" + po.getPurapDocumentIdentifier() + "(document id " + po.getDocumentNumber() + ")";
1528         String responsibilityNote = "Purchase Order Amendment Routed By User";
1529 
1530         for (AdHocRoutePerson adHocPerson : fyiList) {
1531             try {
1532                 po.appSpecificRouteDocumentToUser(
1533                         po.getDocumentHeader().getWorkflowDocument(),
1534                         adHocPerson.getPerson().getPrincipalId(),
1535                         annotation,
1536                         responsibilityNote);
1537             } catch (WorkflowException e) {
1538                 throw new RuntimeException("Error routing fyi for document with id " + po.getDocumentNumber(), e);
1539             }
1540 
1541         }
1542     }
1543 
1544     /**
1545      * Creates a list of fiscal officers for new unordered items added to a purchase order.
1546      *
1547      * @param po
1548      * @return
1549      */
1550     protected List<AdHocRoutePerson> createFyiFiscalOfficerListForNewUnorderedItems(PurchaseOrderDocument po) {
1551 
1552         List<AdHocRoutePerson> adHocRoutePersons = new ArrayList<AdHocRoutePerson>();
1553         Map fiscalOfficers = new HashMap();
1554         AdHocRoutePerson adHocRoutePerson = null;
1555 
1556         for (PurchaseOrderItem poItem : (List<PurchaseOrderItem>) po.getItems()) {
1557             // only check, active, above the line, unordered items
1558             if (poItem.isItemActiveIndicator() && poItem.getItemType().isLineItemIndicator() && PurapConstants.ItemTypeCodes.ITEM_TYPE_UNORDERED_ITEM_CODE.equals(poItem.getItemTypeCode())) {
1559 
1560                 // if the item identifier is null its new, or if the item doesn't exist on the current purchase order it's new
1561                 if (poItem.getItemIdentifier() == null || !purchaseOrderDao.itemExistsOnPurchaseOrder(poItem.getItemLineNumber(), purchaseOrderDao.getDocumentNumberForCurrentPurchaseOrder(po.getPurapDocumentIdentifier()))) {
1562 
1563                     // loop through accounts and pull off fiscal officer
1564                     for (PurApAccountingLine account : poItem.getSourceAccountingLines()) {
1565 
1566                         // check for dupes of fiscal officer
1567                         if (fiscalOfficers.containsKey(account.getAccount().getAccountFiscalOfficerUser().getPrincipalName()) == false) {
1568 
1569                             // add fiscal officer to list
1570                             fiscalOfficers.put(account.getAccount().getAccountFiscalOfficerUser().getPrincipalName(), account.getAccount().getAccountFiscalOfficerUser().getPrincipalName());
1571 
1572                             // create AdHocRoutePerson object and add to list
1573                             adHocRoutePerson = new AdHocRoutePerson();
1574                             adHocRoutePerson.setId(account.getAccount().getAccountFiscalOfficerUser().getPrincipalName());
1575                             adHocRoutePerson.setActionRequested(OLEConstants.WORKFLOW_FYI_REQUEST);
1576                             adHocRoutePersons.add(adHocRoutePerson);
1577                         }
1578                     }
1579                 }
1580             }
1581         }
1582 
1583         return adHocRoutePersons;
1584     }
1585 
1586     /**
1587      * Sends an FYI to fiscal officers for general ledger entries created for amend purchase order
1588      *
1589      * @param po
1590      */
1591     @Override
1592     public void sendFyiForGLEntries(PurchaseOrderDocument po) {
1593 
1594         List<AdHocRoutePerson> fyiList = createFyiFiscalOfficerListForAmendGlEntries(po);
1595         String annotation = "Amendment to Purchase Order " + po.getPurapDocumentIdentifier() + "( Document id " + po.getDocumentNumber() + ")" +
1596                 " resulted in the generation of Pending General Ledger Entries.";
1597         String responsibilityNote = "Purchase Order Amendment Routed By User";
1598 
1599         for (AdHocRoutePerson adHocPerson : fyiList) {
1600             try {
1601                 po.appSpecificRouteDocumentToUser(
1602                         po.getDocumentHeader().getWorkflowDocument(),
1603                         adHocPerson.getPerson().getPrincipalId(),
1604                         annotation,
1605                         responsibilityNote);
1606             } catch (WorkflowException e) {
1607                 throw new RuntimeException("Error routing fyi for document with id " + po.getDocumentNumber(), e);
1608             }
1609 
1610         }
1611     }
1612 
1613     /**
1614      * Creates a list of fiscal officers for amend genera
1615      *
1616      * @param po
1617      * @return
1618      */
1619     protected List<AdHocRoutePerson> createFyiFiscalOfficerListForAmendGlEntries(PurchaseOrderDocument po) {
1620 
1621         List<AdHocRoutePerson> adHocRoutePersons = new ArrayList<AdHocRoutePerson>();
1622         Map fiscalOfficers = new HashMap();
1623         AdHocRoutePerson adHocRoutePerson = null;
1624 
1625         for (SourceAccountingLine account : po.getGlOnlySourceAccountingLines()) {
1626             // loop through accounts and pull off fiscal officer
1627             // for(PurApAccountingLine account : poItem.getSourceAccountingLines()){
1628             // check for dupes of fiscal officer
1629             Account acct = SpringContext.getBean(AccountService.class).getByPrimaryId(account.getChartOfAccountsCode(),
1630                     account.getAccountNumber());
1631             String principalName = acct.getAccountFiscalOfficerUser().getPrincipalName();
1632             // String principalName = account.getAccount().getAccountFiscalOfficerUser().getPrincipalName();
1633             if (fiscalOfficers.containsKey(principalName) == false) {
1634                 // add fiscal officer to list
1635                 fiscalOfficers.put(principalName, principalName);
1636                 // create AdHocRoutePerson object and add to list
1637                 adHocRoutePerson = new AdHocRoutePerson();
1638                 adHocRoutePerson.setId(principalName);
1639                 adHocRoutePerson.setActionRequested(KewApiConstants.ACTION_REQUEST_FYI_REQ);
1640                 adHocRoutePersons.add(adHocRoutePerson);
1641             }
1642             // }
1643         }
1644 
1645         return adHocRoutePersons;
1646     }
1647 
1648     /**
1649      * @see org.kuali.ole.module.purap.document.service.PurchaseOrderService#categorizeItemsForSplit(java.util.List)
1650      */
1651     @Override
1652     public HashMap<String, List<PurchaseOrderItem>> categorizeItemsForSplit(List<PurchaseOrderItem> items) {
1653         HashMap<String, List<PurchaseOrderItem>> movingOrNot = new HashMap<String, List<PurchaseOrderItem>>(3);
1654         List<PurchaseOrderItem> movingPOItems = new ArrayList<PurchaseOrderItem>();
1655         List<PurchaseOrderItem> remainingPOItems = new ArrayList<PurchaseOrderItem>();
1656         List<PurchaseOrderItem> remainingPOLineItems = new ArrayList<PurchaseOrderItem>();
1657         for (PurchaseOrderItem item : items) {
1658             if (item.isMovingToSplit()) {
1659                 movingPOItems.add(item);
1660             } else {
1661                 remainingPOItems.add(item);
1662                 if (item.getItemType().isLineItemIndicator()) {
1663                     remainingPOLineItems.add(item);
1664                 }
1665             }
1666         }
1667         movingOrNot.put(PODocumentsStrings.ITEMS_MOVING_TO_SPLIT, movingPOItems);
1668         movingOrNot.put(PODocumentsStrings.ITEMS_REMAINING, remainingPOItems);
1669         movingOrNot.put(PODocumentsStrings.LINE_ITEMS_REMAINING, remainingPOLineItems);
1670         return movingOrNot;
1671     }
1672 
1673     /**
1674      * @see org.kuali.module.purap.service.PurchaseOrderService#populateQuoteWithVendor(java.lang.Integer, java.lang.Integer,
1675      * java.lang.String)
1676      */
1677     @Override
1678     public PurchaseOrderVendorQuote populateQuoteWithVendor(Integer headerId, Integer detailId, String documentNumber) {
1679         VendorDetail vendor = vendorService.getVendorDetail(headerId, detailId);
1680         updateDefaultVendorAddress(vendor);
1681         PurchaseOrderVendorQuote newPOVendorQuote = populateAddressForPOVendorQuote(vendor, documentNumber);
1682 
1683         // Set the vendorPhoneNumber on the quote to be the first "phone number" type phone
1684         // found on the list. If there's no "phone number" type found, the quote's
1685         // vendorPhoneNumber will be blank regardless of any other types of phone found on the list.
1686         for (VendorPhoneNumber phone : vendor.getVendorPhoneNumbers()) {
1687             if (VendorConstants.PhoneTypes.PHONE.equals(phone.getVendorPhoneTypeCode())) {
1688                 newPOVendorQuote.setVendorPhoneNumber(phone.getVendorPhoneNumber());
1689                 break;
1690             }
1691         }
1692 
1693         return newPOVendorQuote;
1694     }
1695 
1696     /**
1697      * Creates the new PurchaseOrderVendorQuote and populate the address fields for it.
1698      *
1699      * @param newVendor      The VendorDetail object from which we obtain the values for the address fields.
1700      * @param documentNumber The documentNumber of the PurchaseOrderDocument containing the PurchaseOrderVendorQuote.
1701      * @return
1702      */
1703     protected PurchaseOrderVendorQuote populateAddressForPOVendorQuote(VendorDetail newVendor, String documentNumber) {
1704         PurchaseOrderVendorQuote newPOVendorQuote = new PurchaseOrderVendorQuote();
1705         newPOVendorQuote.setVendorName(newVendor.getVendorName());
1706         newPOVendorQuote.setVendorHeaderGeneratedIdentifier(newVendor.getVendorHeaderGeneratedIdentifier());
1707         newPOVendorQuote.setVendorDetailAssignedIdentifier(newVendor.getVendorDetailAssignedIdentifier());
1708         newPOVendorQuote.setDocumentNumber(documentNumber);
1709         boolean foundAddress = false;
1710         for (VendorAddress address : newVendor.getVendorAddresses()) {
1711             if (AddressTypes.QUOTE.equals(address.getVendorAddressTypeCode())) {
1712                 newPOVendorQuote.setVendorCityName(address.getVendorCityName());
1713                 newPOVendorQuote.setVendorCountryCode(address.getVendorCountryCode());
1714                 newPOVendorQuote.setVendorLine1Address(address.getVendorLine1Address());
1715                 newPOVendorQuote.setVendorLine2Address(address.getVendorLine2Address());
1716                 newPOVendorQuote.setVendorPostalCode(address.getVendorZipCode());
1717                 newPOVendorQuote.setVendorStateCode(address.getVendorStateCode());
1718                 newPOVendorQuote.setVendorFaxNumber(address.getVendorFaxNumber());
1719                 foundAddress = true;
1720                 break;
1721             }
1722         }
1723         if (!foundAddress) {
1724             newPOVendorQuote.setVendorCityName(newVendor.getDefaultAddressCity());
1725             newPOVendorQuote.setVendorCountryCode(newVendor.getDefaultAddressCountryCode());
1726             newPOVendorQuote.setVendorLine1Address(newVendor.getDefaultAddressLine1());
1727             newPOVendorQuote.setVendorLine2Address(newVendor.getDefaultAddressLine2());
1728             newPOVendorQuote.setVendorPostalCode(newVendor.getDefaultAddressPostalCode());
1729             newPOVendorQuote.setVendorStateCode(newVendor.getDefaultAddressStateCode());
1730             newPOVendorQuote.setVendorFaxNumber(newVendor.getDefaultFaxNumber());
1731         }
1732         return newPOVendorQuote;
1733     }
1734 
1735     /**
1736      * Obtains the defaultAddress of the vendor and setting the default address fields on the vendor.
1737      *
1738      * @param vendor The VendorDetail object whose default address we'll obtain and set the fields.
1739      */
1740     protected void updateDefaultVendorAddress(VendorDetail vendor) {
1741         VendorAddress defaultAddress = vendorService.getVendorDefaultAddress(vendor.getVendorAddresses(), vendor.getVendorHeader().getVendorType().getAddressType().getVendorAddressTypeCode(), "");
1742         if (defaultAddress != null) {
1743             if (defaultAddress.getVendorState() != null) {
1744                 vendor.setVendorStateForLookup(defaultAddress.getVendorState().getName());
1745             }
1746             vendor.setDefaultAddressLine1(defaultAddress.getVendorLine1Address());
1747             vendor.setDefaultAddressLine2(defaultAddress.getVendorLine2Address());
1748             vendor.setDefaultAddressCity(defaultAddress.getVendorCityName());
1749             vendor.setDefaultAddressPostalCode(defaultAddress.getVendorZipCode());
1750             vendor.setDefaultAddressStateCode(defaultAddress.getVendorStateCode());
1751             vendor.setDefaultAddressInternationalProvince(defaultAddress.getVendorAddressInternationalProvinceName());
1752             vendor.setDefaultAddressCountryCode(defaultAddress.getVendorCountryCode());
1753             vendor.setDefaultFaxNumber(defaultAddress.getVendorFaxNumber());
1754         }
1755     }
1756 
1757     /**
1758      * @see org.kuali.ole.module.purap.document.service.PurchaseOrderService#processACMReq(org.kuali.ole.module.purap.document.ContractManagerAssignmentDocument)
1759      */
1760     @Override
1761     public void processACMReq(ContractManagerAssignmentDocument acmDoc) {
1762         List<ContractManagerAssignmentDetail> acmDetails = acmDoc.getContractManagerAssignmentDetails();
1763         for (Object element : acmDetails) {
1764             ContractManagerAssignmentDetail detail = (ContractManagerAssignmentDetail) element;
1765 
1766             if (ObjectUtils.isNotNull(detail.getContractManagerCode())) {
1767                 // Get the requisition for this ContractManagerAssignmentDetail.
1768                 RequisitionDocument req = requisitionService.getRequisitionById(detail.getRequisitionIdentifier());
1769 
1770                 if (PurapConstants.RequisitionStatuses.APPDOC_AWAIT_CONTRACT_MANAGER_ASSGN.equals(req.getApplicationDocumentStatus())) {
1771                     // only update REQ if code is empty and status is correct
1772                     try {
1773                         req.updateAndSaveAppDocStatus(PurapConstants.RequisitionStatuses.APPDOC_CLOSED);
1774                     } catch (WorkflowException e) {
1775                         throw new RuntimeException("Error saving routing data while saving document with id " + req.getDocumentNumber(), e);
1776                     }
1777 
1778                     purapService.saveDocumentNoValidation(req);
1779                     createPurchaseOrderDocument(req, getOleSelectDocumentService().getSelectParameterValue(OLEConstants.SYSTEM_USER), detail.getContractManagerCode());
1780                 }
1781             }
1782 
1783         }// endfor
1784     }
1785 
1786     /**
1787      * @see org.kuali.ole.module.purap.document.service.PurchaseOrderService#autoCloseFullyDisencumberedOrders()
1788      */
1789     @Override
1790     public boolean autoCloseFullyDisencumberedOrders() {
1791         LOG.debug("autoCloseFullyDisencumberedOrders() started");
1792         List<AutoClosePurchaseOrderView> autoCloseList = new ArrayList<AutoClosePurchaseOrderView>();
1793 
1794         String autoCloseOrderFromDateString = parameterService.getParameterValueAsString(AutoClosePurchaseOrdersStep.class, PurapParameterConstants.AUTO_CLOSE_PO_FROM_DATE);
1795         String autoCloseOrderToDateString = parameterService.getParameterValueAsString(AutoClosePurchaseOrdersStep.class, PurapParameterConstants.AUTO_CLOSE_PO_TO_DATE);
1796 
1797         boolean validFromDate = true;
1798         java.sql.Date autoCloseOrderFromDate = null;
1799         try {
1800             autoCloseOrderFromDate = dateTimeService.convertToSqlDate(autoCloseOrderFromDateString);
1801         } catch (Exception e) {
1802             autoCloseOrderFromDate = null;
1803         }
1804 
1805         boolean validToDate = true;
1806         java.sql.Date autoCloseOrderToDate = null;
1807         try {
1808             autoCloseOrderToDate = dateTimeService.convertToSqlDate(autoCloseOrderToDateString);
1809 
1810         } catch (Exception e) {
1811             autoCloseOrderToDate = null;
1812         }
1813 
1814 
1815         autoCloseList = purchaseOrderDao.getAllOpenPurchaseOrders(getExcludedVendorChoiceCodes(), autoCloseOrderFromDate, autoCloseOrderToDate);
1816 
1817         //we need to eliminate the AutoClosePurchaseOrderView whose workflowdocument status is not OPEN..
1818         //KFSMI-7533
1819          List<AutoClosePurchaseOrderView> purchaseOrderAutoCloseList = filterDocumentsForAppDocStatusOpen
1820          (autoCloseList);
1821 
1822         for (AutoClosePurchaseOrderView poAutoClose : purchaseOrderAutoCloseList) {
1823             if ((poAutoClose.getTotalAmount() != null) && ((KualiDecimal.ZERO.compareTo(poAutoClose.getTotalAmount())) != 0)) {
1824             if (LOG.isDebugEnabled()) {
1825                 LOG.debug("autoCloseFullyDisencumberedOrders() PO ID " + poAutoClose.getPurapDocumentIdentifier() + " with total " + poAutoClose.getTotalAmount().doubleValue() + " will be closed");
1826             }
1827             String newStatus = PurapConstants.PurchaseOrderStatuses.APPDOC_PENDING_CLOSE;
1828             String annotation = "This PO was automatically closed in batch.";
1829             String documentType = PurapConstants.PurchaseOrderDocTypes.PURCHASE_ORDER_CLOSE_DOCUMENT;
1830             PurchaseOrderDocument document = getPurchaseOrderByDocumentNumber(poAutoClose.getDocumentNumber());
1831             createNoteForAutoCloseOrders(document, annotation);
1832             createAndRoutePotentialChangeDocument(poAutoClose.getDocumentNumber(), documentType, annotation, null, newStatus);
1833             }
1834 
1835         }
1836         LOG.debug("autoCloseFullyDisencumberedOrders() ended");
1837 
1838         resetAutoClosePurchaseOrderDateParameter();
1839         return true;
1840     }
1841 
1842     /**
1843      * @see org.kuali.ole.module.purap.document.service.PurchaseOrderService#autoCloseRecurringOrders()
1844      */
1845     @Override
1846     public boolean autoCloseRecurringOrders() {
1847         LOG.debug("autoCloseRecurringOrders() started");
1848         boolean shouldSendEmail = true;
1849         MailMessage message = new MailMessage();
1850         String parameterEmail = parameterService.getParameterValueAsString(AutoCloseRecurringOrdersStep.class, PurapParameterConstants.AUTO_CLOSE_RECURRING_PO_TO_EMAIL_ADDRESSES);
1851 
1852         if (StringUtils.isEmpty(parameterEmail)) {
1853             // Don't stop the show if the email address is wrong, log it and continue.
1854             LOG.warn("autoCloseRecurringOrders(): parameterEmail is missing, we'll not send out any emails for this job.");
1855             shouldSendEmail = false;
1856         }
1857         if (shouldSendEmail) {
1858             message = setMessageAddressesAndSubject(message, parameterEmail);
1859         }
1860         StringBuffer emailBody = new StringBuffer();
1861         // There should always be a "AUTO_CLOSE_RECURRING_ORDER_DT"
1862         // row in the table, this method sets it to "mm/dd/yyyy" after processing.
1863         String recurringOrderDateString = parameterService.getParameterValueAsString(AutoCloseRecurringOrdersStep.class, PurapParameterConstants.AUTO_CLOSE_RECURRING_PO_DATE);
1864         boolean validDate = true;
1865         java.util.Date recurringOrderDate = null;
1866         try {
1867             recurringOrderDate = dateTimeService.convertToDate(recurringOrderDateString);
1868         } catch (ParseException pe) {
1869             validDate = false;
1870         }
1871         if (StringUtils.isEmpty(recurringOrderDateString) || recurringOrderDateString.equalsIgnoreCase("mm/dd/yyyy") || (!validDate)) {
1872             if (recurringOrderDateString.equalsIgnoreCase("mm/dd/yyyy")) {
1873                 LOG.debug("autoCloseRecurringOrders(): mm/dd/yyyy " + "was found in the Application Settings table. No orders will be closed, method will end.");
1874                 if (shouldSendEmail) {
1875                     emailBody.append("The AUTO_CLOSE_RECURRING_ORDER_DT found in the Application Settings table " + "was mm/dd/yyyy. No recurring PO's were closed.");
1876                 }
1877             } else {
1878                 if (LOG.isDebugEnabled()) {
1879                     LOG.debug("autoCloseRecurringOrders(): An invalid autoCloseRecurringOrdersDate " + "was found in the Application Settings table: " + recurringOrderDateString + ". Method will end.");
1880                 }
1881                 if (shouldSendEmail) {
1882                     emailBody.append("An invalid AUTO_CLOSE_RECURRING_ORDER_DT was found in the Application Settings table: " + recurringOrderDateString + ". No recurring PO's were closed.");
1883                 }
1884             }
1885             if (shouldSendEmail) {
1886                 sendMessage(message, emailBody.toString());
1887             }
1888             LOG.debug("autoCloseRecurringOrders() ended");
1889 
1890             return false;
1891         }
1892         if (LOG.isDebugEnabled()) {
1893             LOG.debug("autoCloseRecurringOrders() The autoCloseRecurringOrdersDate found in the Application Settings table was " + recurringOrderDateString);
1894         }
1895         if (shouldSendEmail) {
1896             emailBody.append("The autoCloseRecurringOrdersDate found in the Application Settings table was " + recurringOrderDateString + ".");
1897         }
1898         Calendar appSettingsDate = dateTimeService.getCalendar(recurringOrderDate);
1899         Timestamp appSettingsDay = new Timestamp(appSettingsDate.getTime().getTime());
1900 
1901         Calendar todayMinusThreeMonths = getTodayMinusThreeMonths();
1902         Timestamp threeMonthsAgo = new Timestamp(todayMinusThreeMonths.getTime().getTime());
1903 
1904         if (appSettingsDate.after(todayMinusThreeMonths)) {
1905             if (LOG.isDebugEnabled()) {
1906                 LOG.debug("autoCloseRecurringOrders() The appSettingsDate: " + appSettingsDay + " is after todayMinusThreeMonths: " + threeMonthsAgo + ". The program will end.");
1907             }
1908             if (shouldSendEmail) {
1909                 emailBody.append("\n\nThe autoCloseRecurringOrdersDate: " + appSettingsDay + " is after todayMinusThreeMonths: " + threeMonthsAgo + ". The program will end.");
1910                 sendMessage(message, emailBody.toString());
1911             }
1912             LOG.debug("autoCloseRecurringOrders() ended");
1913 
1914             return false;
1915         }
1916 
1917         List<AutoClosePurchaseOrderView> closeList = purchaseOrderDao.getAutoCloseRecurringPurchaseOrders(getExcludedVendorChoiceCodes());
1918 
1919         //we need to eliminate the AutoClosePurchaseOrderView whose workflowdocument status is not OPEN..
1920         //KFSMI-7533
1921         List<AutoClosePurchaseOrderView> purchaseOrderAutoCloseList = filterDocumentsForAppDocStatusOpen(closeList);
1922 
1923         LOG.info("autoCloseRecurringOrders(): " + purchaseOrderAutoCloseList.size() + " PO's were returned for processing.");
1924         int counter = 0;
1925         for (AutoClosePurchaseOrderView poAutoClose : purchaseOrderAutoCloseList) {
1926             if (LOG.isDebugEnabled()) {
1927                 LOG.debug("autoCloseRecurringOrders(): Testing PO ID " + poAutoClose.getPurapDocumentIdentifier() + ". recurringPaymentEndDate: " + poAutoClose.getRecurringPaymentEndDate());
1928             }
1929             if (poAutoClose.getRecurringPaymentEndDate().before(threeMonthsAgo)) {
1930                 String newStatus = PurapConstants.PurchaseOrderStatuses.APPDOC_PENDING_CLOSE;
1931                 String annotation = "This recurring PO was automatically closed in batch.";
1932                 String documentType = PurapConstants.PurchaseOrderDocTypes.PURCHASE_ORDER_CLOSE_DOCUMENT;
1933                 PurchaseOrderDocument document = getPurchaseOrderByDocumentNumber(poAutoClose.getDocumentNumber());
1934                 boolean rulePassed = kualiRuleService.applyRules(new AttributedRouteDocumentEvent("", document));
1935 
1936                 boolean success = true;
1937                 if (success) {
1938                     ++counter;
1939                     if (counter == 1) {
1940                         emailBody.append("\n\nThe following recurring Purchase Orders will be closed by auto close recurring batch job \n");
1941                     }
1942                     if (LOG.isDebugEnabled()) {
1943                         LOG.debug("autoCloseRecurringOrders() PO ID " + poAutoClose.getPurapDocumentIdentifier() + " will be closed.");
1944                     }
1945                     createNoteForAutoCloseOrders(document, annotation);
1946                     createAndRoutePotentialChangeDocument(poAutoClose.getDocumentNumber(), documentType, annotation, null, newStatus);
1947                     if (shouldSendEmail) {
1948                         emailBody.append("\n\n" + counter + " PO ID: " + poAutoClose.getPurapDocumentIdentifier() + ", End Date: " + poAutoClose.getRecurringPaymentEndDate() + ", Status: " + poAutoClose.getApplicationDocumentStatus() + ", VendorChoice: " + poAutoClose.getVendorChoiceCode() + ", RecurringPaymentType: " + poAutoClose.getRecurringPaymentTypeCode());
1949                     }
1950                 } else {
1951                     // If it was unsuccessful, we have to clear the error map in the GlobalVariables so that the previous
1952                     // error would not still be lingering around and the next PO in the list can be validated.
1953                     GlobalVariables.getMessageMap().clearErrorMessages();
1954                 }
1955             }
1956         }
1957         if (counter == 0) {
1958             LOG.debug("\n\nNo recurring PO's fit the conditions for closing.");
1959             if (shouldSendEmail) {
1960                 emailBody.append("\n\nNo recurring PO's fit the conditions for closing.");
1961             }
1962         }
1963         if (shouldSendEmail) {
1964             sendMessage(message, emailBody.toString());
1965         }
1966         resetAutoCloseRecurringOrderDateParameter();
1967         LOG.debug("autoCloseRecurringOrders() ended");
1968 
1969         return true;
1970     }
1971 
1972     /**
1973      * Filter out the auto close purchase order view documents for the appDocStatus with status open
1974      * For each document in the list, check if there is workflowdocument whose appdocstatus is open
1975      * add add to the return list.
1976      *
1977      * @param List<AutoClosePurchaseOrderView>
1978      * @param appDocStatus
1979      * @return filteredAutoClosePOView filtered auto close po view documents where appdocstatus is open
1980      */
1981     protected List<AutoClosePurchaseOrderView> filterDocumentsForAppDocStatusOpen(List<AutoClosePurchaseOrderView> autoClosePurchaseOrderViews) {
1982         List<AutoClosePurchaseOrderView> filteredAutoClosePOView = new ArrayList<AutoClosePurchaseOrderView>();
1983 
1984         for (AutoClosePurchaseOrderView autoClosePurchaseOrderView : autoClosePurchaseOrderViews) {
1985             Document document = findDocument(autoClosePurchaseOrderView.getDocumentNumber());
1986 
1987             if (document != null) {
1988                 if (PurapConstants.PurchaseOrderStatuses.APPDOC_OPEN.equalsIgnoreCase(
1989                         document.getDocumentHeader().getWorkflowDocument().getApplicationDocumentStatus())) {
1990                     //found the matched Awaiting Contract Manager Assignment status, retrieve the routeHeaderId and add to the list
1991                     filteredAutoClosePOView.add(autoClosePurchaseOrderView);
1992                 }
1993             }
1994         }
1995 
1996         return filteredAutoClosePOView;
1997     }
1998 
1999     /**
2000      * This method finds the document for the given document header id
2001      *
2002      * @param documentHeaderId
2003      * @return document The document in the workflow that matches the document header id.
2004      */
2005     protected Document findDocument(String documentHeaderId) {
2006         Document document = null;
2007 
2008         try {
2009             document = documentService.getByDocumentHeaderId(documentHeaderId);
2010         } catch (WorkflowException ex) {
2011             LOG.error("Exception encountered on finding the document: " + documentHeaderId, ex);
2012         } catch (UnknownDocumentTypeException ex) {
2013             // don't blow up just because a document type is not installed (but don't return it either)
2014             LOG.error("Exception encountered on finding the document: " + documentHeaderId, ex);
2015         }
2016 
2017         return document;
2018     }
2019 
2020 
2021     /**
2022      * Creates and returns a Calendar object of today minus three months.
2023      *
2024      * @return Calendar object of today minus three months.
2025      */
2026     protected Calendar getTodayMinusThreeMonths() {
2027         Calendar todayMinusThreeMonths = Calendar.getInstance(); // Set to today.
2028         todayMinusThreeMonths.add(Calendar.MONTH, -3); // Back up 3 months.
2029         todayMinusThreeMonths.set(Calendar.HOUR, 12);
2030         todayMinusThreeMonths.set(Calendar.MINUTE, 0);
2031         todayMinusThreeMonths.set(Calendar.SECOND, 0);
2032         todayMinusThreeMonths.set(Calendar.MILLISECOND, 0);
2033         todayMinusThreeMonths.set(Calendar.AM_PM, Calendar.AM);
2034         return todayMinusThreeMonths;
2035     }
2036 
2037     /**
2038      * Sets the to addresses, from address and the subject of the email.
2039      *
2040      * @param message        The MailMessage object of the email to be sent.
2041      * @param parameterEmail The String of email addresses with delimiters of ";" obtained from the system parameter.
2042      * @return The MailMessage object after the to addresses, from address and the subject have been set.
2043      */
2044     protected MailMessage setMessageAddressesAndSubject(MailMessage message, String parameterEmail) {
2045         String toAddressList[] = parameterEmail.split(";");
2046 
2047         if (toAddressList.length > 0) {
2048             for (String element : toAddressList) {
2049                 if (element != null) {
2050                     message.addToAddress(element.trim());
2051                 }
2052             }
2053         }
2054 
2055         message.setFromAddress(toAddressList[0]);
2056         message.setSubject("Auto Close Recurring Purchase Orders");
2057         return message;
2058     }
2059 
2060     /**
2061      * Sends the email by calling the sendMessage method in mailService and log error if exception occurs during the attempt to send
2062      * the message.
2063      *
2064      * @param message   The MailMessage object containing information to be sent.
2065      * @param emailBody The String containing the body of the email to be sent.
2066      */
2067     protected void sendMessage(MailMessage message, String emailBody) {
2068         message.setMessage(emailBody);
2069         try {
2070             mailService.sendMessage(message);
2071         } catch (Exception e) {
2072             // Don't stop the show if the email has problem, log it and continue.
2073             LOG.error("autoCloseRecurringOrders(): email problem. Message not sent.", e);
2074             throw new RuntimeException(e);
2075         }
2076     }
2077 
2078     /**
2079      * Resets the AUTO_CLOSE_RECURRING_ORDER_DT system parameter to "mm/dd/yyyy".
2080      */
2081     protected void resetAutoCloseRecurringOrderDateParameter() {
2082         Parameter autoCloseRecurringPODate = parameterService.getParameter(AutoCloseRecurringOrdersStep.class, PurapParameterConstants.AUTO_CLOSE_RECURRING_PO_DATE);
2083         if (autoCloseRecurringPODate != null) {
2084             Parameter.Builder updatedParameter = Parameter.Builder.create(autoCloseRecurringPODate);
2085             updatedParameter.setValue("mm/dd/yyyy");
2086             parameterService.updateParameter(updatedParameter.build());
2087         }
2088     }
2089 
2090     /**
2091      * Resets the AUTO_CLOSE_PO_FROM_DATE and AUTO_CLOSE_PO_TO_DATE system parameters to "mm/dd/yyyy".
2092      */
2093     protected void resetAutoClosePurchaseOrderDateParameter() {
2094         Parameter autoClosePOFromDate = parameterService.getParameter(AutoClosePurchaseOrdersStep.class, PurapParameterConstants.AUTO_CLOSE_PO_FROM_DATE);
2095         Parameter autoClosePOToDate = parameterService.getParameter(AutoClosePurchaseOrdersStep.class, PurapParameterConstants.AUTO_CLOSE_PO_TO_DATE);
2096 
2097         if (autoClosePOFromDate != null) {
2098             Parameter.Builder updatedParameter = Parameter.Builder.create(autoClosePOFromDate);
2099             updatedParameter.setValue("mm/dd/yyyy");
2100             parameterService.updateParameter(updatedParameter.build());
2101         }
2102         if (autoClosePOToDate != null) {
2103             Parameter.Builder updatedParameter = Parameter.Builder.create(autoClosePOToDate);
2104             updatedParameter.setValue("mm/dd/yyyy");
2105             parameterService.updateParameter(updatedParameter.build());
2106         }
2107     }
2108 
2109     /**
2110      * Gets a List of excluded vendor choice codes from PurapConstants.
2111      *
2112      * @return a List of excluded vendor choice codes
2113      */
2114     protected List<String> getExcludedVendorChoiceCodes() {
2115         List<String> excludedVendorChoiceCodes = new ArrayList<String>();
2116         for (String excludedCode : PurapConstants.AUTO_CLOSE_EXCLUSION_VNDR_CHOICE_CODES) {
2117             excludedVendorChoiceCodes.add(excludedCode);
2118         }
2119         return excludedVendorChoiceCodes;
2120     }
2121 
2122     /**
2123      * Creates and add a note to the purchase order document using the annotation String in the input parameter. This method is used
2124      * by the autoCloseRecurringOrders() and autoCloseFullyDisencumberedOrders to add a note to the purchase order to indicate that
2125      * the purchase order was closed by the batch job.
2126      *
2127      * @param purchaseOrderDocument The purchase order document that is being closed by the batch job.
2128      * @param annotation            The string to appear on the note to be attached to the purchase order.
2129      */
2130     protected void createNoteForAutoCloseOrders(PurchaseOrderDocument purchaseOrderDocument, String annotation) {
2131         try {
2132             Note noteObj = documentService.createNoteFromDocument(purchaseOrderDocument, annotation);
2133 //            documentService.addNoteToDocument(purchaseOrderDocument, noteObj);
2134             noteService.save(noteObj);
2135         } catch (Exception e) {
2136             String errorMessage = "Error creating and saving close note for purchase order with document service";
2137             LOG.error("createNoteForAutoCloseRecurringOrders " + errorMessage, e);
2138             throw new RuntimeException(errorMessage, e);
2139         }
2140     }
2141 
2142     /**
2143      * @see org.kuali.ole.module.purap.document.service.PurchaseOrderService#retrieveCapitalAssetItemsForIndividual(java.lang.Integer)
2144      */
2145     @Override
2146     public List<PurchasingCapitalAssetItem> retrieveCapitalAssetItemsForIndividual(Integer poId) {
2147         PurchaseOrderDocument po = getCurrentPurchaseOrder(poId);
2148         if (ObjectUtils.isNotNull(po)) {
2149             return po.getPurchasingCapitalAssetItems();
2150         }
2151         return null;
2152     }
2153 
2154     /**
2155      * @see org.kuali.ole.module.purap.document.service.PurchaseOrderService#retrieveCapitalAssetSystemForOneSystem(java.lang.Integer)
2156      */
2157     @Override
2158     public CapitalAssetSystem retrieveCapitalAssetSystemForOneSystem(Integer poId) {
2159         PurchaseOrderDocument po = getCurrentPurchaseOrder(poId);
2160         if (ObjectUtils.isNotNull(po)) {
2161             List<CapitalAssetSystem> systems = po.getPurchasingCapitalAssetSystems();
2162             if (ObjectUtils.isNotNull(systems)) {
2163                 // for one system, there should only ever be one system
2164                 return systems.get(0);
2165             }
2166         }
2167         return null;
2168     }
2169 
2170     /**
2171      * @see org.kuali.ole.module.purap.document.service.PurchaseOrderService#retrieveCapitalAssetSystemsForMultipleSystem(java.lang.Integer)
2172      */
2173     @Override
2174     public List<CapitalAssetSystem> retrieveCapitalAssetSystemsForMultipleSystem(Integer poId) {
2175         PurchaseOrderDocument po = getCurrentPurchaseOrder(poId);
2176         if (ObjectUtils.isNotNull(po)) {
2177             return po.getPurchasingCapitalAssetSystems();
2178         }
2179         return null;
2180     }
2181 
2182     /**
2183      * This method fixes the item references in this document
2184      */
2185     protected void fixItemReferences(PurchaseOrderDocument po) {
2186         // fix item and account references in case this is a new doc (since they will be lost)
2187         for (PurApItem item : (List<PurApItem>) po.getItems()) {
2188             item.setPurapDocument(po);
2189             item.fixAccountReferences();
2190         }
2191     }
2192 
2193     @Override
2194     public List getPendingPurchaseOrderFaxes() {
2195         List<PurchaseOrderDocument> purchaseOrderList = purchaseOrderDao.getPendingPurchaseOrdersForFaxing();
2196         return filterPurchaseOrderDocumentByAppDocStatus(purchaseOrderList,
2197                 PurapConstants.PurchaseOrderStatuses.APPDOC_PENDING_FAX);
2198     }
2199 
2200     /**
2201      * Wrapper class to the filterPaymentRequestByAppDocStatus
2202      * <p/>
2203      * This class first extract the payment request document numbers from the Payment Request Collections,
2204      * then perform the filterPaymentRequestByAppDocStatus function.  Base on the filtered payment request
2205      * doc number, reconstruct the filtered Payment Request Collection
2206      *
2207      * @param paymentRequestDocuments
2208      * @param appDocStatus
2209      * @return
2210      */
2211     protected List<PurchaseOrderDocument> filterPurchaseOrderDocumentByAppDocStatus(Collection<PurchaseOrderDocument> purchaseOrderDocuments, String... appDocStatus) {
2212         List<String> purchaseOrderDocNumbers = new ArrayList<String>();
2213         for (PurchaseOrderDocument purchaseOrder : purchaseOrderDocuments) {
2214             purchaseOrderDocNumbers.add(purchaseOrder.getDocumentNumber());
2215         }
2216 
2217         List<String> filteredPurchaseOrderDocNumbers = filterPurchaseOrderDocumentNumbersByAppDocStatus(purchaseOrderDocNumbers, appDocStatus);
2218 
2219         List<PurchaseOrderDocument> filteredPaymentRequestDocuments = new ArrayList<PurchaseOrderDocument>();
2220         //add to filtered collection if it is in the filtered payment request doc number list
2221         for (PurchaseOrderDocument po : purchaseOrderDocuments) {
2222             if (filteredPurchaseOrderDocNumbers.contains(po.getDocumentNumber())) {
2223                 filteredPaymentRequestDocuments.add(po);
2224             }
2225         }
2226         return filteredPaymentRequestDocuments;
2227     }
2228 
2229     /**
2230      * Since PaymentRequest does not have the app doc status, perform an additional lookup
2231      * through doc search by using list of PaymentRequest Doc numbers.  Query appDocStatus
2232      * from workflow document and filter against the provided status
2233      * <p/>
2234      * DocumentSearch allows for multiple docNumber lookup by docId|docId|docId conversion
2235      *
2236      * @param lookupDocNumbers
2237      * @param appDocStatus
2238      * @return List<String> purchaseOrderDocumentNumbers
2239      */
2240     protected List<String> filterPurchaseOrderDocumentNumbersByAppDocStatus(List<String> lookupDocNumbers, String... appDocStatus) {
2241         boolean valid = false;
2242 
2243         final String DOC_NUM_DELIM = "|";
2244         StrBuilder routerHeaderIdBuilder = new StrBuilder().appendWithSeparators(lookupDocNumbers, DOC_NUM_DELIM);
2245 
2246         List<String> purchaseOrderDocNumbers = new ArrayList<String>();
2247 
2248         DocumentSearchCriteria.Builder documentSearchCriteriaDTO = DocumentSearchCriteria.Builder.create();
2249         documentSearchCriteriaDTO.setDocumentId(routerHeaderIdBuilder.toString());
2250         documentSearchCriteriaDTO.setDocumentTypeName(PurapConstants.PurapDocTypeCodes.PO_DOCUMENT);
2251 
2252         DocumentSearchResults poDocumentsList = KewApiServiceLocator.getWorkflowDocumentService().documentSearch(
2253                 GlobalVariables.getUserSession().getPrincipalId(), documentSearchCriteriaDTO.build());
2254 
2255         for (DocumentSearchResult poDocument : poDocumentsList.getSearchResults()) {
2256             ///use the appDocStatus from the KeyValueDTO result to look up custom status
2257             if (Arrays.asList(appDocStatus).contains(poDocument.getDocument().getApplicationDocumentStatus())) {
2258                 //found the matching status, retrieve the routeHeaderId and add to the list
2259                 purchaseOrderDocNumbers.add(poDocument.getDocument().getDocumentId());
2260             }
2261         }
2262 
2263         return purchaseOrderDocNumbers;
2264     }
2265 
2266     @Override
2267     public String getPurchaseOrderAppDocStatus(Integer poId) {
2268         //TODO: This could be kind of expensive for one field
2269         PurchaseOrderDocument po = getCurrentPurchaseOrder(poId);
2270         if (ObjectUtils.isNotNull(po)) {
2271             return po.getApplicationDocumentStatus();
2272         }
2273 
2274         return null;
2275     }
2276 
2277     /**
2278      * helper method to take the po and save it using businessObjectService so that only the po related data is saved since most
2279      * often we are only updating the flags on the document. It will then reindex the document.
2280      *
2281      * @param po
2282      */
2283     protected void savePurchaseOrderData(PurchaseOrderDocument po) {
2284         // saving old PO using the business object service because the documentService saveDocument
2285         // will try to save the notes again and will cause ojb lock exception.
2286         // since only values that is changed on PO is pendingActionIndicator, save on businessObjectService is used
2287         // OLEMI-9741
2288 
2289         businessObjectService.save(po);
2290 
2291         // reindex the document so that the app doc status gets updated in the results for the PO lookups.
2292         final DocumentAttributeIndexingQueue documentAttributeIndexingQueue = KewApiServiceLocator
2293                 .getDocumentAttributeIndexingQueue();
2294         documentAttributeIndexingQueue.indexDocument(po.getDocumentNumber());
2295 
2296     }
2297 
2298     /**
2299      * @return Returns the personService.
2300      */
2301     protected PersonService getPersonService() {
2302         if (personService == null) {
2303             personService = SpringContext.getBean(PersonService.class);
2304         }
2305         return personService;
2306     }
2307 
2308     public void setB2bPurchaseOrderService(B2BPurchaseOrderService purchaseOrderService) {
2309         this.b2bPurchaseOrderService = purchaseOrderService;
2310     }
2311 
2312     public void setBusinessObjectService(BusinessObjectService boService) {
2313         this.businessObjectService = boService;
2314     }
2315 
2316     public void setDateTimeService(DateTimeService dateTimeService) {
2317         this.dateTimeService = dateTimeService;
2318     }
2319 
2320     public void setDocumentService(DocumentService documentService) {
2321         this.documentService = documentService;
2322     }
2323 
2324     public void setNoteService(NoteService noteService) {
2325         this.noteService = noteService;
2326     }
2327 
2328     public void setPurapService(PurapService purapService) {
2329         this.purapService = purapService;
2330     }
2331 
2332     public void setPrintService(PrintService printService) {
2333         this.printService = printService;
2334     }
2335 
2336     public void setPurchaseOrderDao(PurchaseOrderDao purchaseOrderDao) {
2337         this.purchaseOrderDao = purchaseOrderDao;
2338     }
2339 
2340     public void setWorkflowDocumentService(WorkflowDocumentService workflowDocumentService) {
2341         this.workflowDocumentService = workflowDocumentService;
2342     }
2343 
2344     public void setConfigurationService(ConfigurationService kualiConfigurationService) {
2345         this.kualiConfigurationService = kualiConfigurationService;
2346     }
2347 
2348     public void setKualiRuleService(KualiRuleService kualiRuleService) {
2349         this.kualiRuleService = kualiRuleService;
2350     }
2351 
2352     public void setVendorService(VendorService vendorService) {
2353         this.vendorService = vendorService;
2354     }
2355 
2356     public void setRequisitionService(RequisitionService requisitionService) {
2357         this.requisitionService = requisitionService;
2358     }
2359 
2360     public void setPurapWorkflowIntegrationService(PurApWorkflowIntegrationService purapWorkflowIntegrationService) {
2361         this.purapWorkflowIntegrationService = purapWorkflowIntegrationService;
2362     }
2363 
2364     public void setMaintenanceDocumentService(MaintenanceDocumentService maintenanceDocumentService) {
2365         this.maintenanceDocumentService = maintenanceDocumentService;
2366     }
2367 
2368     public void setParameterService(ParameterService parameterService) {
2369         this.parameterService = parameterService;
2370     }
2371 
2372     public void setMailService(MailService mailService) {
2373         this.mailService = mailService;
2374     }
2375 
2376     public void setDataDictionaryService(DataDictionaryService dataDictionaryService) {
2377         this.dataDictionaryService = dataDictionaryService;
2378     }
2379 
2380 
2381     /**
2382      * This method is the starting point of EDI File creation and transmission for a PO
2383      *
2384      * @param po
2385      */
2386     @Override
2387     public void initiateTransmission(PurchaseOrderDocument po, PurApItem item) {
2388         if (po.getRequestorPersonEmailAddress() != null) {
2389             fromEmailAddress = po.getRequestorPersonEmailAddress();
2390         }
2391 
2392         if (po != null && po.getVendorDetail() != null) {
2393             List<VendorTransmissionFormatDetail> vendorTxFormat = po.getVendorDetail().getVendorTransmissionFormat();
2394             boolean isSuccess = false;
2395             if (vendorTxFormat.size() > 0) {
2396                 for (int i = 0; i < vendorTxFormat.size(); i++) {
2397                     VendorTransmissionFormatDetail vendorTransmissionFormatDetail = vendorTxFormat.get(i);
2398                     boolean isPrefferedTransmissionFormat = vendorTransmissionFormatDetail.isVendorPreferredTransmissionFormat();
2399                     if (isPrefferedTransmissionFormat && item.getItemLineNumber() != null) {
2400                         String documentType = po.getDocumentHeader().getWorkflowDocument().getDocumentTypeName();
2401                         String ediFileName = documentType + "_" + po.getPurapDocumentIdentifier().toString() + "_" + PurapConstants.ItemTypeCodes.ITEM_TYPE_ITEM_CODE + item.getItemLineNumber() + "_" + System.currentTimeMillis() + ".edi";
2402                         String pdfFileName = documentType + "_" + po.getPurapDocumentIdentifier().toString() + "_" + System.currentTimeMillis() + ".pdf";
2403                         String directory = kualiConfigurationService.getPropertyValueAsString(OLEConstants.STAGING_DIRECTORY_KEY);
2404                         String fileLocation = directory + getOlePurapService().getParameter(OLEConstants.VENDOR_TRANSMISSION_FILE);
2405                         File fileLocationDir = new File(fileLocation);
2406                         if (!(fileLocationDir.exists())) {
2407                             fileLocationDir.mkdir();
2408                         }
2409                         String file = fileLocation.trim() + ediFileName.trim();
2410 
2411                         if (vendorTransmissionFormatDetail.getVendorTransmissionFormat() != null &&
2412                                 vendorTransmissionFormatDetail.getVendorTransmissionFormat().getVendorTransmissionFormat() != null &&
2413                                 vendorTransmissionFormatDetail.getVendorTransmissionFormat().getVendorTransmissionFormat().equalsIgnoreCase("EDI")) {
2414                             if (LOG.isDebugEnabled()) {
2415                                 LOG.debug("EDI File======================>" + file);
2416                             }
2417                             isSuccess = savePurchaseOrderEdi(po, file, item);
2418                         }
2419                         if (vendorTransmissionFormatDetail.getVendorTransmissionFormat() != null &&
2420                                 vendorTransmissionFormatDetail.getVendorTransmissionFormat().getVendorTransmissionFormat() != null &&
2421                                 vendorTransmissionFormatDetail.getVendorTransmissionFormat().getVendorTransmissionFormat().equalsIgnoreCase(OLEConstants.OLE_VENDOR_PDF_OPTION)) {
2422                             if (!(po.getDocumentNumber().equals(documentNumber))) {
2423                                 savePurchaseOrderPdf(po, pdfFileName, item);
2424                                 processFTPTransmission(vendorTransmissionFormatDetail, file, pdfFileName.trim());
2425                                 documentNumber = po.getDocumentNumber();
2426                             }
2427                         }
2428                         if (isSuccess && vendorTransmissionFormatDetail.getVendorTransmissionTypes().getVendorTransmissionType() != null) {
2429                             processFTPTransmission(vendorTransmissionFormatDetail, file, ediFileName.trim());
2430                         }
2431                     }
2432                 }
2433             }
2434 
2435         }
2436     }
2437 
2438     public String getEmailAddress(VendorTransmissionFormatDetail vendorTransmissionFormatDetail) {
2439         String toAddress = "";
2440         Integer vendorId = vendorTransmissionFormatDetail.getVendorHeaderGeneratedIdentifier();
2441         if (vendorId != null) {
2442             Map searchMap = new HashMap();
2443             searchMap.put("vendorHeaderGeneratedIdentifier", vendorId);
2444             List<VendorAddress> address = (List<VendorAddress>) SpringContext.getBean(BusinessObjectService.class).findMatching(VendorAddress.class, searchMap);
2445             if (address.size() > 0) {
2446                 for (VendorAddress vendorAddress : address) {
2447                     if (toAddress == null || (toAddress.isEmpty())) {
2448                         toAddress = vendorAddress.getVendorAddressEmailAddress();
2449                     }
2450                 }
2451 
2452             }
2453             if (toAddress == null || toAddress.isEmpty()) {
2454                 List<VendorContact> vendorContactList = (List<VendorContact>) SpringContext.getBean(BusinessObjectService.class).findMatching(VendorContact.class, searchMap);
2455                 if (vendorContactList.size() > 0) {
2456                     for (VendorContact vendorContact : vendorContactList) {
2457                         if (toAddress == null || (toAddress.isEmpty())) {
2458                             toAddress = vendorContact.getVendorContactEmailAddress();
2459                         }
2460                     }
2461                 }
2462             }
2463         }
2464 
2465         return toAddress;
2466     }
2467 
2468 
2469     /**
2470      * This method is to create and save the EDI file into the staging directory.
2471      *
2472      * @param po
2473      * @param file
2474      * @param isRetransmit
2475      * @param environment
2476      */
2477     public boolean savePurchaseOrderEdi(PurchaseOrderDocument po, String file, PurApItem item) {
2478         LOG.debug("****************savePurchaseOrderEdi() started*******************");
2479         boolean isSuccess = false;
2480         PurchaseOrderEdi purchaseOrderEdi = SpringContext.getBean(PurchaseOrderEdi.class, "purchaseOrderEdi");
2481         try {
2482             isSuccess = purchaseOrderEdi.saveEdi(po, item, file);
2483             LOG.debug("savePurchaseOrderEdi() completed");
2484         } catch (Exception e) {
2485             LOG.error("Caught exception ", e);
2486             throw new RuntimeException(e);
2487         }
2488         return isSuccess;
2489     }
2490 
2491     public boolean savePurchaseOrderPdf(PurchaseOrderDocument po, String file, PurApItem item) {
2492         LOG.debug("****************savePurchaseOrderPDF() started*******************");
2493         boolean isSuccess = false;
2494         PurchaseOrderParameters purchaseOrderParameters = getPurchaseOrderParameters();
2495         purchaseOrderParameters.setPurchaseOrderPdfAndFaxParameters(po);
2496         PurchaseOrderTransmitParameters purchaseOrderTransmitParameter = (PurchaseOrderTransmitParameters) purchaseOrderParameters;
2497         String pdfFileLocation = ConfigContext.getCurrentContextConfig().getProperty(org.kuali.ole.OLEConstants.VENDOR_TRANSMISSION_FILE_PATH);
2498         if (pdfFileLocation != null) {
2499             purchaseOrderTransmitParameter.setPdfFileLocation(pdfFileLocation);
2500             purchaseOrderTransmitParameter.setPdfFileName(file);
2501         }
2502         String environment = kualiConfigurationService.getPropertyValueAsString(OLEConstants.ENVIRONMENT_KEY);
2503         PurchaseOrderPdf purchaseOrderPdf = SpringContext.getBean(PurchaseOrderPdf.class, "purchaseOrderPdf");
2504         try {
2505             purchaseOrderPdf.savePdf(po, purchaseOrderParameters, isSuccess, environment);
2506             isSuccess = true;
2507             LOG.debug("savePurchaseOrderPdf() completed");
2508         } catch (Exception e) {
2509             LOG.error("Caught exception ", e);
2510             throw new RuntimeException(e);
2511         }
2512         return isSuccess;
2513     }
2514 
2515     public PurchaseOrderParameters getPurchaseOrderParameters() {
2516         return SpringContext.getBean(PurchaseOrderParameters.class);
2517     }
2518 
2519 
2520     /**
2521      * This method is for transmitting EDI file into FTP Server
2522      *
2523      * @param vendorTransmissionFormatDetail
2524      * @param file
2525      * @param fileName
2526      */
2527     @Override
2528     public boolean processFTPTransmission(VendorTransmissionFormatDetail vendorTransmissionFormatDetail, String file, String fileName) {
2529 
2530         if (vendorTransmissionFormatDetail != null &&
2531                 vendorTransmissionFormatDetail.getVendorEDIConnectionAddress() != null &&
2532                 vendorTransmissionFormatDetail.getVendorEDIConnectionUserName() != null &&
2533                 vendorTransmissionFormatDetail.getVendorEDIConnectionPassword() != null) {
2534             if (LOG.isDebugEnabled()) {
2535                 LOG.debug("Transmission Format==================>" + vendorTransmissionFormatDetail.getVendorTransmissionFormat().getVendorTransmissionFormat());
2536                 LOG.debug("Connection Type======================>" + vendorTransmissionFormatDetail.getVendorTransmissionTypes().getVendorTransmissionType());
2537                 LOG.debug("Connection Address===================>" + vendorTransmissionFormatDetail.getVendorEDIConnectionAddress());
2538                 LOG.debug("Connection User Name=================>" + vendorTransmissionFormatDetail.getVendorEDIConnectionUserName());
2539                 //            LOG.info("Connection Password==================>"+vendorTransmissionFormatDetail.getVendorEDIConnectionPassword());
2540                 LOG.debug("file=================================>" + file);
2541                 LOG.debug("ediFileName==========================>" + fileName);
2542             }
2543 
2544             OleTransmissionService transmissionService = (OleTransmissionService) SpringContext.getService("transmissionService");
2545             if (vendorTransmissionFormatDetail.getVendorTransmissionTypes().getVendorTransmissionType().equalsIgnoreCase("SFTP")) {
2546                 transmissionService.doSFTPUpload(vendorTransmissionFormatDetail, file, fileName);
2547             } else if (vendorTransmissionFormatDetail.getVendorTransmissionTypes().getVendorTransmissionType().equalsIgnoreCase("FTP")) {
2548                 transmissionService.doFTPUpload(vendorTransmissionFormatDetail, file, fileName);
2549             } else if (vendorTransmissionFormatDetail.getVendorTransmissionTypes().getVendorTransmissionType().equalsIgnoreCase(OLEConstants.OLE_VENDOR_EMAIL_OPTION)) {
2550                 String fileLocation = ConfigContext.getCurrentContextConfig().getProperty(org.kuali.ole.OLEConstants.VENDOR_TRANSMISSION_FILE_PATH) + fileName;
2551                 fileNameList.add(fileLocation);
2552                 toEmailAddress = getEmailAddress(vendorTransmissionFormatDetail);
2553             }
2554         } else if (vendorTransmissionFormatDetail != null &&
2555                 getVendorFormatType(vendorTransmissionFormatDetail.getVendorTransmissionFormatId()).equalsIgnoreCase(OLEConstants.OLE_VENDOR_PDF_OPTION) &&
2556                 vendorTransmissionFormatDetail.isVendorPreferredTransmissionFormat() &&
2557                 getVendorTransmissionType(vendorTransmissionFormatDetail.getVendorTransmissionTypeId()).equalsIgnoreCase(OLEConstants.OLE_VENDOR_EMAIL_OPTION)) {
2558             if (vendorTransmissionFormatDetail.getVendorTransmissionTypes().getVendorTransmissionType().equalsIgnoreCase(OLEConstants.OLE_VENDOR_EMAIL_OPTION) &&
2559                     vendorTransmissionFormatDetail.getVendorTransmissionFormat().getVendorTransmissionFormat().equalsIgnoreCase(OLEConstants.OLE_VENDOR_PDF_OPTION)) {
2560                 String fileLocation = ConfigContext.getCurrentContextConfig().getProperty(org.kuali.ole.OLEConstants.VENDOR_TRANSMISSION_FILE_PATH) + fileName;
2561                 fileNameList.add(fileLocation);
2562                 toEmailAddress = getEmailAddress(vendorTransmissionFormatDetail);
2563             }
2564         } else if (vendorTransmissionFormatDetail != null &&
2565                 getVendorFormatType(vendorTransmissionFormatDetail.getVendorTransmissionFormatId()).equalsIgnoreCase(OLEConstants.OLE_VENDOR_EDI_OPTION) &&
2566                 vendorTransmissionFormatDetail.isVendorPreferredTransmissionFormat() &&
2567                 getVendorTransmissionType(vendorTransmissionFormatDetail.getVendorTransmissionTypeId()).equalsIgnoreCase(OLEConstants.OLE_VENDOR_EMAIL_OPTION)) {
2568             String fileLocation = ConfigContext.getCurrentContextConfig().getProperty(org.kuali.ole.OLEConstants.VENDOR_TRANSMISSION_FILE_PATH) + fileName;
2569             fileNameList.add(fileLocation);
2570             toEmailAddress = getEmailAddress(vendorTransmissionFormatDetail);
2571         }
2572         return true;
2573     }
2574 
2575     public String getVendorFormatType(Long id) {
2576         String vendorFormatType = "";
2577         Map searchMap = new HashMap();
2578         searchMap.put(OLEConstants.VENDOR_TRANS_FORMAT_ID, id);
2579         OleVendorTransmissionFormat vendorTransmissionFormat = SpringContext.getBean(BusinessObjectService.class).findByPrimaryKey(OleVendorTransmissionFormat.class, searchMap);
2580         if (vendorTransmissionFormat != null) {
2581             vendorFormatType = vendorTransmissionFormat.getVendorTransmissionFormat();
2582         }
2583         return vendorFormatType;
2584     }
2585 
2586     public String getVendorTransmissionType(Integer id) {
2587         String vendorTransmissionType = "";
2588         Map searchMap = new HashMap();
2589         searchMap.put(OLEConstants.VENDOR_TRANS_TYPE_ID, id);
2590         OleVendorTransmissionType transmissionType = SpringContext.getBean(BusinessObjectService.class).findByPrimaryKey(OleVendorTransmissionType.class, searchMap);
2591         if (transmissionType != null) {
2592             vendorTransmissionType = transmissionType.getVendorTransmissionType();
2593         }
2594         return vendorTransmissionType;
2595     }
2596 
2597     public void sendEmail() {
2598         try {
2599             if (toEmailAddress != null && fromEmailAddress != null && fileNameList.size() > 0) {
2600                 String subject = OLEConstants.MAIL_SUBJECT;
2601                 String messageBody = OLEConstants.MAIL_MESSAGE_BODY;
2602                 OleMailer oleMail = GlobalResourceLoader.getService(OLEConstants.OLE_MAILER);
2603                 oleMail.SendEMail(toEmailAddress, fromEmailAddress, fileNameList, subject, messageBody);
2604             }
2605         } catch (Exception e) {
2606             LOG.info("Email Sending Failed for fromAddress [" + fromEmailAddress + "] and toAddress [ " + toEmailAddress + " ]");
2607             e.printStackTrace();
2608         }
2609     }
2610 
2611     public OleSelectDocumentService getOleSelectDocumentService() {
2612         if (oleSelectDocumentService == null) {
2613             oleSelectDocumentService = SpringContext.getBean(OleSelectDocumentService.class);
2614         }
2615         return oleSelectDocumentService;
2616     }
2617 
2618     public void setOleSelectDocumentService(OleSelectDocumentService oleSelectDocumentService) {
2619         this.oleSelectDocumentService = oleSelectDocumentService;
2620     }
2621 }