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