001 /* 002 * Copyright 2011 The Kuali Foundation. 003 * 004 * Licensed under the Educational Community License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.opensource.org/licenses/ecl2.php 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016 package org.kuali.kfs.module.purap.document.service.impl; 017 018 import java.io.ByteArrayOutputStream; 019 import java.sql.Timestamp; 020 import java.text.ParseException; 021 import java.util.ArrayList; 022 import java.util.Calendar; 023 import java.util.Collection; 024 import java.util.Date; 025 import java.util.HashMap; 026 import java.util.HashSet; 027 import java.util.Iterator; 028 import java.util.List; 029 import java.util.Map; 030 import java.util.Set; 031 032 import org.apache.commons.lang.StringUtils; 033 import org.kuali.kfs.integration.purap.CapitalAssetSystem; 034 import org.kuali.kfs.module.purap.PurapConstants; 035 import org.kuali.kfs.module.purap.PurapConstants.CreditMemoStatuses; 036 import org.kuali.kfs.module.purap.PurapConstants.PODocumentsStrings; 037 import org.kuali.kfs.module.purap.PurapConstants.POTransmissionMethods; 038 import org.kuali.kfs.module.purap.PurapConstants.PaymentRequestStatuses; 039 import org.kuali.kfs.module.purap.PurapConstants.PurchaseOrderDocTypes; 040 import org.kuali.kfs.module.purap.PurapConstants.PurchaseOrderStatuses; 041 import org.kuali.kfs.module.purap.PurapConstants.RequisitionSources; 042 import org.kuali.kfs.module.purap.PurapKeyConstants; 043 import org.kuali.kfs.module.purap.PurapParameterConstants; 044 import org.kuali.kfs.module.purap.PurapPropertyConstants; 045 import org.kuali.kfs.module.purap.PurapWorkflowConstants.PurchaseOrderDocument.NodeDetailEnum; 046 import org.kuali.kfs.module.purap.batch.AutoCloseRecurringOrdersStep; 047 import org.kuali.kfs.module.purap.businessobject.AutoClosePurchaseOrderView; 048 import org.kuali.kfs.module.purap.businessobject.ContractManagerAssignmentDetail; 049 import org.kuali.kfs.module.purap.businessobject.CreditMemoView; 050 import org.kuali.kfs.module.purap.businessobject.PaymentRequestView; 051 import org.kuali.kfs.module.purap.businessobject.PurApAccountingLine; 052 import org.kuali.kfs.module.purap.businessobject.PurApItem; 053 import org.kuali.kfs.module.purap.businessobject.PurchaseOrderCapitalAssetItem; 054 import org.kuali.kfs.module.purap.businessobject.PurchaseOrderCapitalAssetSystem; 055 import org.kuali.kfs.module.purap.businessobject.PurchaseOrderItem; 056 import org.kuali.kfs.module.purap.businessobject.PurchaseOrderQuoteStatus; 057 import org.kuali.kfs.module.purap.businessobject.PurchaseOrderVendorQuote; 058 import org.kuali.kfs.module.purap.businessobject.PurchasingCapitalAssetItem; 059 import org.kuali.kfs.module.purap.businessobject.ReceivingThreshold; 060 import org.kuali.kfs.module.purap.document.ContractManagerAssignmentDocument; 061 import org.kuali.kfs.module.purap.document.PurchaseOrderDocument; 062 import org.kuali.kfs.module.purap.document.PurchaseOrderSplitDocument; 063 import org.kuali.kfs.module.purap.document.PurchasingDocument; 064 import org.kuali.kfs.module.purap.document.RequisitionDocument; 065 import org.kuali.kfs.module.purap.document.dataaccess.PurchaseOrderDao; 066 import org.kuali.kfs.module.purap.document.service.B2BPurchaseOrderService; 067 import org.kuali.kfs.module.purap.document.service.LogicContainer; 068 import org.kuali.kfs.module.purap.document.service.PaymentRequestService; 069 import org.kuali.kfs.module.purap.document.service.PrintService; 070 import org.kuali.kfs.module.purap.document.service.PurApWorkflowIntegrationService; 071 import org.kuali.kfs.module.purap.document.service.PurapService; 072 import org.kuali.kfs.module.purap.document.service.PurchaseOrderService; 073 import org.kuali.kfs.module.purap.document.service.RequisitionService; 074 import org.kuali.kfs.module.purap.util.PurApObjectUtils; 075 import org.kuali.kfs.module.purap.util.ThresholdHelper; 076 import org.kuali.kfs.module.purap.util.ThresholdHelper.ThresholdSummary; 077 import org.kuali.kfs.sys.KFSConstants; 078 import org.kuali.kfs.sys.KFSPropertyConstants; 079 import org.kuali.kfs.sys.context.SpringContext; 080 import org.kuali.kfs.sys.document.FinancialSystemTransactionalDocumentBase; 081 import org.kuali.kfs.sys.document.validation.event.AttributedRouteDocumentEvent; 082 import org.kuali.kfs.sys.document.validation.event.DocumentSystemSaveEvent; 083 import org.kuali.kfs.vnd.VendorConstants; 084 import org.kuali.kfs.vnd.VendorConstants.AddressTypes; 085 import org.kuali.kfs.vnd.businessobject.CommodityCode; 086 import org.kuali.kfs.vnd.businessobject.VendorAddress; 087 import org.kuali.kfs.vnd.businessobject.VendorCommodityCode; 088 import org.kuali.kfs.vnd.businessobject.VendorDetail; 089 import org.kuali.kfs.vnd.businessobject.VendorPhoneNumber; 090 import org.kuali.kfs.vnd.document.service.VendorService; 091 import org.kuali.rice.kew.exception.WorkflowException; 092 import org.kuali.rice.kew.service.WorkflowDocumentActions; 093 import org.kuali.rice.kew.util.KEWConstants; 094 import org.kuali.rice.kim.bo.Person; 095 import org.kuali.rice.kim.service.PersonService; 096 import org.kuali.rice.kns.bo.AdHocRoutePerson; 097 import org.kuali.rice.kns.bo.AdHocRouteRecipient; 098 import org.kuali.rice.kns.bo.Note; 099 import org.kuali.rice.kns.bo.Parameter; 100 import org.kuali.rice.kns.document.DocumentBase; 101 import org.kuali.rice.kns.document.MaintenanceDocument; 102 import org.kuali.rice.kns.exception.ValidationException; 103 import org.kuali.rice.kns.mail.MailMessage; 104 import org.kuali.rice.kns.maintenance.Maintainable; 105 import org.kuali.rice.kns.rule.event.RouteDocumentEvent; 106 import org.kuali.rice.kns.service.BusinessObjectService; 107 import org.kuali.rice.kns.service.DataDictionaryService; 108 import org.kuali.rice.kns.service.DateTimeService; 109 import org.kuali.rice.kns.service.DocumentService; 110 import org.kuali.rice.kns.service.KualiConfigurationService; 111 import org.kuali.rice.kns.service.KualiRuleService; 112 import org.kuali.rice.kns.service.MailService; 113 import org.kuali.rice.kns.service.MaintenanceDocumentService; 114 import org.kuali.rice.kns.service.NoteService; 115 import org.kuali.rice.kns.service.ParameterService; 116 import org.kuali.rice.kns.service.SequenceAccessorService; 117 import org.kuali.rice.kns.util.GlobalVariables; 118 import org.kuali.rice.kns.util.KualiDecimal; 119 import org.kuali.rice.kns.util.MessageMap; 120 import org.kuali.rice.kns.util.ObjectUtils; 121 import org.kuali.rice.kns.util.TypedArrayList; 122 import org.kuali.rice.kns.workflow.service.KualiWorkflowDocument; 123 import org.kuali.rice.kns.workflow.service.KualiWorkflowInfo; 124 import org.kuali.rice.kns.workflow.service.WorkflowDocumentService; 125 import org.springframework.transaction.annotation.Transactional; 126 127 128 @Transactional 129 public class PurchaseOrderServiceImpl implements PurchaseOrderService { 130 private static org.apache.log4j.Logger LOG = org.apache.log4j.Logger.getLogger(PurchaseOrderServiceImpl.class); 131 132 private BusinessObjectService businessObjectService; 133 protected DateTimeService dateTimeService; 134 private DocumentService documentService; 135 private NoteService noteService; 136 protected PurapService purapService; 137 private PrintService printService; 138 private PurchaseOrderDao purchaseOrderDao; 139 private WorkflowDocumentService workflowDocumentService; 140 private KualiConfigurationService kualiConfigurationService; 141 private KualiRuleService kualiRuleService; 142 private VendorService vendorService; 143 private RequisitionService requisitionService; 144 private PurApWorkflowIntegrationService purapWorkflowIntegrationService; 145 private KualiWorkflowInfo workflowInfoService; 146 private MaintenanceDocumentService maintenanceDocumentService; 147 private ParameterService parameterService; 148 private PersonService<Person> personService; 149 private MailService mailService; 150 private B2BPurchaseOrderService b2bPurchaseOrderService; 151 private DataDictionaryService dataDictionaryService; 152 153 public void setB2bPurchaseOrderService(B2BPurchaseOrderService purchaseOrderService) { 154 b2bPurchaseOrderService = purchaseOrderService; 155 } 156 157 public void setBusinessObjectService(BusinessObjectService boService) { 158 this.businessObjectService = boService; 159 } 160 161 public void setDateTimeService(DateTimeService dateTimeService) { 162 this.dateTimeService = dateTimeService; 163 } 164 165 public void setDocumentService(DocumentService documentService) { 166 this.documentService = documentService; 167 } 168 169 public void setNoteService(NoteService noteService) { 170 this.noteService = noteService; 171 } 172 173 public void setPurapService(PurapService purapService) { 174 this.purapService = purapService; 175 } 176 177 public void setPrintService(PrintService printService) { 178 this.printService = printService; 179 } 180 181 public void setPurchaseOrderDao(PurchaseOrderDao purchaseOrderDao) { 182 this.purchaseOrderDao = purchaseOrderDao; 183 } 184 185 public void setWorkflowDocumentService(WorkflowDocumentService workflowDocumentService) { 186 this.workflowDocumentService = workflowDocumentService; 187 } 188 189 public void setKualiConfigurationService(KualiConfigurationService kualiConfigurationService) { 190 this.kualiConfigurationService = kualiConfigurationService; 191 } 192 193 public void setKualiRuleService(KualiRuleService kualiRuleService) { 194 this.kualiRuleService = kualiRuleService; 195 } 196 197 public void setVendorService(VendorService vendorService) { 198 this.vendorService = vendorService; 199 } 200 201 public void setRequisitionService(RequisitionService requisitionService) { 202 this.requisitionService = requisitionService; 203 } 204 205 public void setPurapWorkflowIntegrationService(PurApWorkflowIntegrationService purapWorkflowIntegrationService) { 206 this.purapWorkflowIntegrationService = purapWorkflowIntegrationService; 207 } 208 209 public void setWorkflowInfoService(KualiWorkflowInfo workflowInfoService) { 210 this.workflowInfoService = workflowInfoService; 211 } 212 213 public void setMaintenanceDocumentService(MaintenanceDocumentService maintenanceDocumentService) { 214 this.maintenanceDocumentService = maintenanceDocumentService; 215 } 216 217 public void setParameterService(ParameterService parameterService) { 218 this.parameterService = parameterService; 219 } 220 221 public void setMailService(MailService mailService) { 222 this.mailService = mailService; 223 } 224 225 226 public boolean isPurchaseOrderOpenForProcessing(Integer poId) { 227 return isPurchaseOrderOpenForProcessing(getCurrentPurchaseOrder(poId)); 228 } 229 230 public boolean isPurchaseOrderOpenForProcessing(PurchaseOrderDocument purchaseOrderDocument) { 231 boolean can = PurchaseOrderStatuses.OPEN.equals(purchaseOrderDocument.getStatusCode()); 232 can = can && purchaseOrderDocument.isPurchaseOrderCurrentIndicator() && !purchaseOrderDocument.isPendingActionIndicator(); 233 234 // in addition, check conditions about No In Process PREQ and CM) 235 if (can) { 236 List<PaymentRequestView> preqViews = purchaseOrderDocument.getRelatedViews().getRelatedPaymentRequestViews(); 237 if ( preqViews != null ) { 238 for (PaymentRequestView preqView : preqViews) { 239 if (StringUtils.equalsIgnoreCase(preqView.getStatusCode(), PaymentRequestStatuses.IN_PROCESS)) { 240 return false; 241 } 242 } 243 } 244 List<CreditMemoView> cmViews = purchaseOrderDocument.getRelatedViews().getRelatedCreditMemoViews(); 245 if ( cmViews != null ) { 246 for (CreditMemoView cmView : cmViews) { 247 if (StringUtils.equalsIgnoreCase(cmView.getCreditMemoStatusCode(), CreditMemoStatuses.IN_PROCESS)) { 248 return false; 249 } 250 } 251 } 252 } 253 254 return can; 255 } 256 257 /** 258 * Sets the error map to a new, empty error map before calling saveDocumentNoValidation to save the document. 259 * 260 * @param document The purchase order document to be saved. 261 */ 262 protected void saveDocumentNoValidationUsingClearErrorMap(PurchaseOrderDocument document) { 263 MessageMap errorHolder = GlobalVariables.getMessageMap(); 264 GlobalVariables.setMessageMap(new MessageMap()); 265 try { 266 purapService.saveDocumentNoValidation(document); 267 } 268 finally { 269 GlobalVariables.setMessageMap(errorHolder); 270 } 271 } 272 273 /** 274 * Calls the saveDocument method of documentService to save the document. 275 * 276 * @param document The document to be saved. 277 */ 278 protected void saveDocumentStandardSave(PurchaseOrderDocument document) { 279 try { 280 documentService.saveDocument(document); 281 } 282 catch (WorkflowException we) { 283 String errorMsg = "Workflow Error saving document # " + document.getDocumentHeader().getDocumentNumber() + " " + we.getMessage(); 284 LOG.error(errorMsg, we); 285 throw new RuntimeException(errorMsg, we); 286 } 287 } 288 289 public PurchasingCapitalAssetItem createCamsItem(PurchasingDocument purDoc, PurApItem purapItem) { 290 PurchasingCapitalAssetItem camsItem = new PurchaseOrderCapitalAssetItem(); 291 camsItem.setItemIdentifier(purapItem.getItemIdentifier()); 292 // If the system type is INDIVIDUAL then for each of the capital asset items, we need a system attached to it. 293 if (purDoc.getCapitalAssetSystemTypeCode().equals(PurapConstants.CapitalAssetTabStrings.INDIVIDUAL_ASSETS)) { 294 CapitalAssetSystem resultSystem = new PurchaseOrderCapitalAssetSystem(); 295 camsItem.setPurchasingCapitalAssetSystem(resultSystem); 296 } 297 camsItem.setPurchasingDocument(purDoc); 298 299 return camsItem; 300 } 301 302 public CapitalAssetSystem createCapitalAssetSystem() { 303 CapitalAssetSystem resultSystem = new PurchaseOrderCapitalAssetSystem(); 304 return resultSystem; 305 } 306 307 /** 308 * @see org.kuali.kfs.module.purap.document.service.PurchaseOrderService#createAutomaticPurchaseOrderDocument(org.kuali.kfs.module.purap.document.RequisitionDocument) 309 */ 310 public void createAutomaticPurchaseOrderDocument(RequisitionDocument reqDocument) { 311 String newSessionUserId = KFSConstants.SYSTEM_USER; 312 try { 313 LogicContainer logicToRun = new LogicContainer() { 314 public Object runLogic(Object[] objects) throws Exception { 315 RequisitionDocument doc = (RequisitionDocument) objects[0]; 316 // update REQ data 317 doc.setPurchaseOrderAutomaticIndicator(Boolean.TRUE); 318 // create PO and populate with default data 319 PurchaseOrderDocument po = generatePurchaseOrderFromRequisition(doc); 320 po.setDefaultValuesForAPO(); 321 po.setContractManagerCode(PurapConstants.APO_CONTRACT_MANAGER); 322 documentService.routeDocument(po, null, null); 323 324 final WorkflowDocumentActions workflowDocumentActions = SpringContext.getBean(WorkflowDocumentActions.class); 325 workflowDocumentActions.indexDocument(new Long(po.getDocumentNumber())); 326 return null; 327 } 328 }; 329 purapService.performLogicWithFakedUserSession(newSessionUserId, logicToRun, new Object[] { reqDocument }); 330 } 331 catch (WorkflowException e) { 332 String errorMsg = "Workflow Exception caught: " + e.getLocalizedMessage(); 333 LOG.error(errorMsg, e); 334 throw new RuntimeException(errorMsg, e); 335 } 336 catch (Exception e) { 337 throw new RuntimeException(e); 338 } 339 } 340 341 /** 342 * @see org.kuali.kfs.module.purap.document.service.PurchaseOrderService#createPurchaseOrderDocument(org.kuali.kfs.module.purap.document.RequisitionDocument, 343 * java.lang.String, java.lang.Integer) 344 */ 345 public PurchaseOrderDocument createPurchaseOrderDocument(RequisitionDocument reqDocument, String newSessionUserId, Integer contractManagerCode) { 346 try { 347 LogicContainer logicToRun = new LogicContainer() { 348 public Object runLogic(Object[] objects) throws Exception { 349 RequisitionDocument doc = (RequisitionDocument) objects[0]; 350 PurchaseOrderDocument po = generatePurchaseOrderFromRequisition(doc); 351 Integer cmCode = (Integer) objects[1]; 352 po.setContractManagerCode(cmCode); 353 purapService.saveDocumentNoValidation(po); 354 return po; 355 } 356 }; 357 return (PurchaseOrderDocument) purapService.performLogicWithFakedUserSession(newSessionUserId, logicToRun, new Object[] { reqDocument, contractManagerCode }); 358 } 359 catch (WorkflowException e) { 360 String errorMsg = "Workflow Exception caught: " + e.getLocalizedMessage(); 361 LOG.error(errorMsg, e); 362 throw new RuntimeException(errorMsg, e); 363 } 364 catch (Exception e) { 365 throw new RuntimeException(e); 366 } 367 } 368 369 /** 370 * Create Purchase Order and populate with data from Requisition and other default data 371 * 372 * @param reqDocument The requisition document from which we create the purchase order document. 373 * @return The purchase order document created by this method. 374 * @throws WorkflowException 375 */ 376 protected PurchaseOrderDocument generatePurchaseOrderFromRequisition(RequisitionDocument reqDocument) throws WorkflowException { 377 PurchaseOrderDocument poDocument = null; 378 poDocument = (PurchaseOrderDocument) documentService.getNewDocument(PurchaseOrderDocTypes.PURCHASE_ORDER_DOCUMENT); 379 poDocument.populatePurchaseOrderFromRequisition(reqDocument); 380 poDocument.setStatusCode(PurchaseOrderStatuses.IN_PROCESS); 381 poDocument.setPurchaseOrderCurrentIndicator(true); 382 poDocument.setPendingActionIndicator(false); 383 384 if (RequisitionSources.B2B.equals(poDocument.getRequisitionSourceCode())) { 385 String paramName = PurapParameterConstants.DEFAULT_B2B_VENDOR_CHOICE; 386 String paramValue = parameterService.getParameterValue(PurchaseOrderDocument.class, paramName); 387 poDocument.setPurchaseOrderVendorChoiceCode(paramValue); 388 } 389 390 if (ObjectUtils.isNotNull(poDocument.getVendorContract())) { 391 poDocument.setVendorPaymentTermsCode(poDocument.getVendorContract().getVendorPaymentTermsCode()); 392 poDocument.setVendorShippingPaymentTermsCode(poDocument.getVendorContract().getVendorShippingPaymentTermsCode()); 393 poDocument.setVendorShippingTitleCode(poDocument.getVendorContract().getVendorShippingTitleCode()); 394 } 395 else { 396 VendorDetail vendor = vendorService.getVendorDetail(poDocument.getVendorHeaderGeneratedIdentifier(), poDocument.getVendorDetailAssignedIdentifier()); 397 if (ObjectUtils.isNotNull(vendor)) { 398 poDocument.setVendorPaymentTermsCode(vendor.getVendorPaymentTermsCode()); 399 poDocument.setVendorShippingPaymentTermsCode(vendor.getVendorShippingPaymentTermsCode()); 400 poDocument.setVendorShippingTitleCode(vendor.getVendorShippingTitleCode()); 401 } 402 } 403 404 if (!PurapConstants.RequisitionSources.B2B.equals(poDocument.getRequisitionSourceCode())) { 405 purapService.addBelowLineItems(poDocument); 406 } 407 poDocument.fixItemReferences(); 408 409 return poDocument; 410 } 411 412 /** 413 * @see org.kuali.kfs.module.purap.document.service.PurchaseOrderService#getInternalPurchasingDollarLimit(org.kuali.kfs.module.purap.document.PurchaseOrderDocument) 414 */ 415 public KualiDecimal getInternalPurchasingDollarLimit(PurchaseOrderDocument document) { 416 if ((document.getVendorContract() != null) && (document.getContractManager() != null)) { 417 KualiDecimal contractDollarLimit = vendorService.getApoLimitFromContract(document.getVendorContract().getVendorContractGeneratedIdentifier(), document.getChartOfAccountsCode(), document.getOrganizationCode()); 418 //FIXME somehow data fields such as contractManagerDelegationDollarLimit in reference object contractManager didn't get retrieved 419 // (are null) as supposed to be (this happens whether or not proxy is set to true), even though contractManager is not null; 420 // so here we have to manually refresh the contractManager to retrieve the fields 421 if (document.getContractManager().getContractManagerDelegationDollarLimit() == null) { 422 document.refreshReferenceObject(PurapPropertyConstants.CONTRACT_MANAGER); 423 } 424 KualiDecimal contractManagerLimit = document.getContractManager().getContractManagerDelegationDollarLimit(); 425 if ((contractDollarLimit != null) && (contractManagerLimit != null)) { 426 if (contractDollarLimit.compareTo(contractManagerLimit) > 0) { 427 return contractDollarLimit; 428 } 429 else { 430 return contractManagerLimit; 431 } 432 } 433 else if (contractDollarLimit != null) { 434 return contractDollarLimit; 435 } 436 else { 437 return contractManagerLimit; 438 } 439 } 440 else if ((document.getVendorContract() == null) && (document.getContractManager() != null)) { 441 //FIXME As above, here we have to manually refresh the contractManager to retrieve its field 442 if (document.getContractManager().getContractManagerDelegationDollarLimit() == null) { 443 document.refreshReferenceObject(PurapPropertyConstants.CONTRACT_MANAGER); 444 } 445 return document.getContractManager().getContractManagerDelegationDollarLimit(); 446 } 447 else if ((document.getVendorContract() != null) && (document.getContractManager() == null)) { 448 return purapService.getApoLimit(document.getVendorContract().getVendorContractGeneratedIdentifier(), document.getChartOfAccountsCode(), document.getOrganizationCode()); 449 } 450 else { 451 String errorMsg = "No internal purchase order dollar limit found for purchase order '" + document.getPurapDocumentIdentifier() + "'."; 452 LOG.warn(errorMsg); 453 return null; 454 } 455 } 456 457 /** 458 * Loops through the collection of error messages and adding each of them to the error map. 459 * 460 * @param errorKey The resource key used to retrieve the error text from the error message resource bundle. 461 * @param errors The collection of error messages. 462 */ 463 protected void addStringErrorMessagesToErrorMap(String errorKey, Collection<String> errors) { 464 if (ObjectUtils.isNotNull(errors)) { 465 for (String error : errors) { 466 LOG.error("Adding error message using error key '" + errorKey + "' with text '" + error + "'"); 467 GlobalVariables.getMessageMap().putError(KFSConstants.GLOBAL_ERRORS, errorKey, error); 468 } 469 } 470 } 471 472 473 /** 474 * TODO RELEASE 3 - QUOTE 475 * 476 * @see org.kuali.kfs.module.purap.document.service.PurchaseOrderService#printPurchaseOrderQuoteRequestsListPDF(org.kuali.kfs.module.purap.document.PurchaseOrderDocument, 477 * java.io.ByteArrayOutputStream) 478 */ 479 public boolean printPurchaseOrderQuoteRequestsListPDF(String documentNumber, ByteArrayOutputStream baosPDF) { 480 PurchaseOrderDocument po = getPurchaseOrderByDocumentNumber(documentNumber); 481 String environment = kualiConfigurationService.getPropertyString(KFSConstants.ENVIRONMENT_KEY); 482 Collection<String> generatePDFErrors = printService.generatePurchaseOrderQuoteRequestsListPdf(po, baosPDF); 483 484 if (generatePDFErrors.size() > 0) { 485 addStringErrorMessagesToErrorMap(PurapKeyConstants.ERROR_PURCHASE_ORDER_PDF, generatePDFErrors); 486 return false; 487 } else { 488 return true; 489 } 490 } 491 492 /** 493 * TODO RELEASE 3 - QUOTE 494 * 495 * @see org.kuali.kfs.module.purap.document.service.PurchaseOrderService#printPurchaseOrderQuotePDF(org.kuali.kfs.module.purap.document.PurchaseOrderDocument, 496 * org.kuali.kfs.module.purap.businessobject.PurchaseOrderVendorQuote, java.io.ByteArrayOutputStream) 497 */ 498 public boolean printPurchaseOrderQuotePDF(PurchaseOrderDocument po, PurchaseOrderVendorQuote povq, ByteArrayOutputStream baosPDF) { 499 String environment = kualiConfigurationService.getPropertyString(KFSConstants.ENVIRONMENT_KEY); 500 Collection<String> generatePDFErrors = printService.generatePurchaseOrderQuotePdf(po, povq, baosPDF, environment); 501 502 if (generatePDFErrors.size() > 0) { 503 addStringErrorMessagesToErrorMap(PurapKeyConstants.ERROR_PURCHASE_ORDER_PDF, generatePDFErrors); 504 return false; 505 } 506 else { 507 return true; 508 } 509 } 510 511 /** 512 * @see org.kuali.kfs.module.purap.document.service.PurchaseOrderService#performPurchaseOrderFirstTransmitViaPrinting(java.lang.String, 513 * java.io.ByteArrayOutputStream) 514 */ 515 public void performPurchaseOrderFirstTransmitViaPrinting(String documentNumber, ByteArrayOutputStream baosPDF) { 516 PurchaseOrderDocument po = getPurchaseOrderByDocumentNumber(documentNumber); 517 String environment = kualiConfigurationService.getPropertyString(KFSConstants.ENVIRONMENT_KEY); 518 Collection<String> generatePDFErrors = printService.generatePurchaseOrderPdf(po, baosPDF, environment, null); 519 if (!generatePDFErrors.isEmpty()) { 520 addStringErrorMessagesToErrorMap(PurapKeyConstants.ERROR_PURCHASE_ORDER_PDF, generatePDFErrors); 521 throw new ValidationException("printing purchase order for first transmission failed"); 522 } 523 if (ObjectUtils.isNotNull(po.getPurchaseOrderFirstTransmissionTimestamp())) { 524 // should not call this method for first transmission if document has already been transmitted 525 String errorMsg = "Method to perform first transmit was called on document (doc id " + documentNumber + ") with already filled in 'first transmit date'"; 526 LOG.error(errorMsg); 527 throw new RuntimeException(errorMsg); 528 } 529 Timestamp currentDate = dateTimeService.getCurrentTimestamp(); 530 po.setPurchaseOrderFirstTransmissionTimestamp(currentDate); 531 po.setPurchaseOrderLastTransmitTimestamp(currentDate); 532 po.setOverrideWorkflowButtons(Boolean.FALSE); 533 boolean performedAction = purapWorkflowIntegrationService.takeAllActionsForGivenCriteria(po, "Action taken automatically as part of document initial print transmission", NodeDetailEnum.DOCUMENT_TRANSMISSION.getName(), GlobalVariables.getUserSession().getPerson(), null); 534 if (!performedAction) { 535 Person systemUserPerson = getPersonService().getPersonByPrincipalName(KFSConstants.SYSTEM_USER); 536 purapWorkflowIntegrationService.takeAllActionsForGivenCriteria(po, "Action taken automatically as part of document initial print transmission by user " + GlobalVariables.getUserSession().getPerson().getName(), NodeDetailEnum.DOCUMENT_TRANSMISSION.getName(), systemUserPerson, KFSConstants.SYSTEM_USER); 537 } 538 po.setOverrideWorkflowButtons(Boolean.TRUE); 539 if (!po.getStatusCode().equals(PurapConstants.PurchaseOrderStatuses.OPEN)) { 540 attemptSetupOfInitialOpenOfDocument(po); 541 } 542 purapService.saveDocumentNoValidation(po); 543 } 544 545 /** 546 * @see org.kuali.kfs.module.purap.document.service.PurchaseOrderService#performPurchaseOrderPreviewPrinting(java.lang.String, 547 * java.io.ByteArrayOutputStream) 548 */ 549 public void performPurchaseOrderPreviewPrinting(String documentNumber, ByteArrayOutputStream baosPDF) { 550 performPrintPurchaseOrderPDFOnly(documentNumber, baosPDF); 551 } 552 553 /** 554 * @see org.kuali.kfs.module.purap.document.service.PurchaseOrderService#performPrintPurchaseOrderPDFOnly(java.lang.String, 555 * java.io.ByteArrayOutputStream) 556 */ 557 public void performPrintPurchaseOrderPDFOnly(String documentNumber, ByteArrayOutputStream baosPDF) { 558 PurchaseOrderDocument po = getPurchaseOrderByDocumentNumber(documentNumber); 559 String environment = kualiConfigurationService.getPropertyString(KFSConstants.ENVIRONMENT_KEY); 560 Collection<String> generatePDFErrors = printService.generatePurchaseOrderPdf(po, baosPDF, environment, null); 561 if (!generatePDFErrors.isEmpty()) { 562 addStringErrorMessagesToErrorMap(PurapKeyConstants.ERROR_PURCHASE_ORDER_PDF, generatePDFErrors); 563 throw new ValidationException("printing purchase order for first transmission failed"); 564 } 565 } 566 567 /** 568 * @see org.kuali.kfs.module.purap.document.service.PurchaseOrderService#retransmitPurchaseOrderPDF(org.kuali.kfs.module.purap.document.PurchaseOrderDocument, 569 * java.io.ByteArrayOutputStream) 570 */ 571 public void retransmitPurchaseOrderPDF(PurchaseOrderDocument po, ByteArrayOutputStream baosPDF) { 572 573 String environment = kualiConfigurationService.getPropertyString(KFSConstants.ENVIRONMENT_KEY); 574 List<PurchaseOrderItem> items = po.getItems(); 575 List<PurchaseOrderItem> retransmitItems = new ArrayList<PurchaseOrderItem>(); 576 for (PurchaseOrderItem item : items) { 577 if (item.isItemSelectedForRetransmitIndicator()) { 578 retransmitItems.add(item); 579 } 580 } 581 Collection<String> generatePDFErrors = printService.generatePurchaseOrderPdfForRetransmission(po, baosPDF, environment, retransmitItems); 582 583 if (generatePDFErrors.size() > 0) { 584 addStringErrorMessagesToErrorMap(PurapKeyConstants.ERROR_PURCHASE_ORDER_PDF, generatePDFErrors); 585 throw new ValidationException("found errors while trying to print po with doc id " + po.getDocumentNumber()); 586 } 587 po.setPurchaseOrderLastTransmitTimestamp(dateTimeService.getCurrentTimestamp()); 588 purapService.saveDocumentNoValidation(po); 589 } 590 591 /** 592 * This method creates a new Purchase Order Document using the given document type based off the given source document. This 593 * method will return null if the source document given is null.<br> 594 * <br> ** THIS METHOD DOES NOT SAVE EITHER THE GIVEN SOURCE DOCUMENT OR THE NEW DOCUMENT CREATED 595 * 596 * @param sourceDocument - document the new Purchase Order Document should be based off of in terms of data 597 * @param docType - document type of the potential new Purchase Order Document 598 * @return the new Purchase Order Document of the given document type or null if the given source document is null 599 * @throws WorkflowException if a new document cannot be created using the given type 600 */ 601 protected PurchaseOrderDocument createPurchaseOrderDocumentFromSourceDocument(PurchaseOrderDocument sourceDocument, String docType) throws WorkflowException { 602 if (ObjectUtils.isNull(sourceDocument)) { 603 String errorMsg = "Attempting to create new PO of type '" + docType + "' from source PO doc that is null"; 604 LOG.error(errorMsg); 605 throw new RuntimeException(errorMsg); 606 } 607 608 PurchaseOrderDocument newPurchaseOrderChangeDocument = (PurchaseOrderDocument) documentService.getNewDocument(docType); 609 610 Set classesToExclude = new HashSet(); 611 Class sourceObjectClass = FinancialSystemTransactionalDocumentBase.class; 612 classesToExclude.add(sourceObjectClass); 613 while (sourceObjectClass.getSuperclass() != null) { 614 sourceObjectClass = sourceObjectClass.getSuperclass(); 615 classesToExclude.add(sourceObjectClass); 616 } 617 PurApObjectUtils.populateFromBaseWithSuper(sourceDocument, newPurchaseOrderChangeDocument, PurapConstants.uncopyableFieldsForPurchaseOrder(), classesToExclude); 618 newPurchaseOrderChangeDocument.getDocumentHeader().setDocumentDescription(sourceDocument.getDocumentHeader().getDocumentDescription()); 619 newPurchaseOrderChangeDocument.getDocumentHeader().setOrganizationDocumentNumber(sourceDocument.getDocumentHeader().getOrganizationDocumentNumber()); 620 newPurchaseOrderChangeDocument.getDocumentHeader().setExplanation(sourceDocument.getDocumentHeader().getExplanation()); 621 newPurchaseOrderChangeDocument.setPurchaseOrderCurrentIndicator(false); 622 newPurchaseOrderChangeDocument.setPendingActionIndicator(false); 623 624 // TODO f2f: what is this doing? 625 // Need to find a way to make the ManageableArrayList to expand and populating the items and 626 // accounts, otherwise it will complain about the account on item 1 is missing. 627 for (PurApItem item : (List<PurApItem>) newPurchaseOrderChangeDocument.getItems()) { 628 item.getSourceAccountingLines().iterator(); 629 // we only need to do this once to apply to all items, so we can break out of the loop now 630 SequenceAccessorService sas = SpringContext.getBean(SequenceAccessorService.class); 631 Integer itemIdentifier = sas.getNextAvailableSequenceNumber("PO_ITM_ID", PurApItem.class).intValue(); 632 item.setItemIdentifier(itemIdentifier); 633 } 634 635 updateCapitalAssetRelatedCollections(newPurchaseOrderChangeDocument); 636 newPurchaseOrderChangeDocument.refreshNonUpdateableReferences(); 637 638 return newPurchaseOrderChangeDocument; 639 } 640 641 protected void updateCapitalAssetRelatedCollections(PurchaseOrderDocument newDocument) { 642 643 for (PurchasingCapitalAssetItem capitalAssetItem : newDocument.getPurchasingCapitalAssetItems()) { 644 Integer lineNumber = capitalAssetItem.getPurchasingItem().getItemLineNumber(); 645 PurApItem newItem = newDocument.getItemByLineNumber(lineNumber.intValue()); 646 capitalAssetItem.setItemIdentifier(newItem.getItemIdentifier()); 647 capitalAssetItem.setPurchasingDocument(newDocument); 648 capitalAssetItem.setCapitalAssetSystemIdentifier(null); 649 CapitalAssetSystem oldSystem = capitalAssetItem.getPurchasingCapitalAssetSystem(); 650 capitalAssetItem.setPurchasingCapitalAssetSystem(new PurchaseOrderCapitalAssetSystem(oldSystem)); 651 652 } 653 } 654 655 /** 656 * @see org.kuali.kfs.module.purap.document.service.PurchaseOrderService#createAndSavePotentialChangeDocument(java.lang.String, 657 * java.lang.String, java.lang.String) 658 */ 659 public PurchaseOrderDocument createAndSavePotentialChangeDocument(String documentNumber, String docType, String currentDocumentStatusCode) { 660 PurchaseOrderDocument currentDocument = getPurchaseOrderByDocumentNumber(documentNumber); 661 662 try { 663 PurchaseOrderDocument newDocument = createPurchaseOrderDocumentFromSourceDocument(currentDocument, docType); 664 665 if (ObjectUtils.isNotNull(newDocument)) { 666 newDocument.setStatusCode(PurchaseOrderStatuses.CHANGE_IN_PROCESS); 667 // set status if needed 668 if (StringUtils.isNotBlank(currentDocumentStatusCode)) { 669 purapService.updateStatus(currentDocument, currentDocumentStatusCode); 670 } 671 try { 672 documentService.saveDocument(newDocument, DocumentSystemSaveEvent.class); 673 } 674 // if we catch a ValidationException it means the new PO doc found errors 675 catch (ValidationException ve) { 676 throw ve; 677 } 678 // if no validation exception was thrown then rules have passed and we are ok to edit the current PO 679 currentDocument.setPendingActionIndicator(true); 680 681 saveDocumentNoValidationUsingClearErrorMap(currentDocument); 682 683 return newDocument; 684 } 685 else { 686 String errorMsg = "Attempting to create new PO of type '" + docType + "' from source PO doc id " + documentNumber + " returned null for new document"; 687 LOG.error(errorMsg); 688 throw new RuntimeException(errorMsg); 689 } 690 } 691 catch (WorkflowException we) { 692 String errorMsg = "Workflow Exception caught trying to create and save PO document of type '" + docType + "' using source document with doc id '" + documentNumber + "'"; 693 LOG.error(errorMsg, we); 694 throw new RuntimeException(errorMsg, we); 695 } 696 } 697 698 /** 699 * @see org.kuali.kfs.module.purap.document.service.PurchaseOrderService#createAndRoutePotentialChangeDocument(java.lang.String, 700 * java.lang.String, java.lang.String, java.util.List, java.lang.String) 701 */ 702 public PurchaseOrderDocument createAndRoutePotentialChangeDocument(String documentNumber, String docType, String annotation, List adhocRoutingRecipients, String currentDocumentStatusCode) { 703 PurchaseOrderDocument currentDocument = getPurchaseOrderByDocumentNumber(documentNumber); 704 purapService.updateStatus(currentDocument, currentDocumentStatusCode); 705 706 try { 707 PurchaseOrderDocument newDocument = createPurchaseOrderDocumentFromSourceDocument(currentDocument, docType); 708 newDocument.setStatusCode(PurchaseOrderStatuses.CHANGE_IN_PROCESS); 709 if (ObjectUtils.isNotNull(newDocument)) { 710 try { 711 // set the pending indictor before routing, so that when routing is done in synch mode, the pending indicator won't be set again after route finishes and cause inconsistency 712 currentDocument.setPendingActionIndicator(true); 713 documentService.routeDocument(newDocument, annotation, adhocRoutingRecipients); 714 } 715 // if we catch a ValidationException it means the new PO doc found errors 716 catch (ValidationException ve) { 717 //clear the pending indictor if an exception occurs, to leave the existing PO intact 718 currentDocument.setPendingActionIndicator(false); 719 saveDocumentNoValidationUsingClearErrorMap(currentDocument); 720 throw ve; 721 } 722 return newDocument; 723 } 724 else { 725 String errorMsg = "Attempting to create new PO of type '" + docType + "' from source PO doc id " + documentNumber + " returned null for new document"; 726 LOG.error(errorMsg); 727 throw new RuntimeException(errorMsg); 728 } 729 } 730 catch (WorkflowException we) { 731 String errorMsg = "Workflow Exception caught trying to create and route PO document of type '" + docType + "' using source document with doc id '" + documentNumber + "'"; 732 LOG.error(errorMsg, we); 733 throw new RuntimeException(errorMsg, we); 734 } 735 } 736 737 /** 738 * @see org.kuali.kfs.module.purap.document.service.PurchaseOrderService#createAndSavePurchaseOrderSplitDocument(java.util.List, java.lang.String, boolean) 739 */ 740 public PurchaseOrderSplitDocument createAndSavePurchaseOrderSplitDocument(List<PurchaseOrderItem> newPOItems, PurchaseOrderDocument currentDocument, boolean copyNotes, String splitNoteText) { 741 742 if (ObjectUtils.isNull(currentDocument)) { 743 String errorMsg = "Attempting to create new PO of type PurchaseOrderSplitDocument from source PO doc that is null"; 744 LOG.error(errorMsg); 745 throw new RuntimeException(errorMsg); 746 } 747 String documentNumber = currentDocument.getDocumentNumber(); 748 749 try { 750 // Create the new Split PO document (throws WorkflowException) 751 PurchaseOrderSplitDocument newDocument = (PurchaseOrderSplitDocument)documentService.getNewDocument(PurchaseOrderDocTypes.PURCHASE_ORDER_SPLIT_DOCUMENT); 752 753 if (ObjectUtils.isNotNull(newDocument)) { 754 755 // Prepare for copying fields over from the current document. 756 Set<Class> classesToExclude = getClassesToExcludeFromCopy(); 757 Map<String, Class> uncopyableFields = PurapConstants.UNCOPYABLE_FIELDS_FOR_PO; 758 uncopyableFields.putAll(PurapConstants.uncopyableFieldsForSplitPurchaseOrder()); 759 760 // Copy all fields over from the current document except the items and the above-specified fields. 761 PurApObjectUtils.populateFromBaseWithSuper(currentDocument, newDocument, uncopyableFields, classesToExclude); 762 newDocument.getDocumentHeader().setDocumentDescription(currentDocument.getDocumentHeader().getDocumentDescription()); 763 newDocument.getDocumentHeader().setOrganizationDocumentNumber(currentDocument.getDocumentHeader().getOrganizationDocumentNumber()); 764 newDocument.setPurchaseOrderCurrentIndicator(true); 765 newDocument.setPendingActionIndicator(false); 766 767 // Add in and renumber the items that the new document should have. 768 newDocument.setItems(newPOItems); 769 SpringContext.getBean(PurapService.class).addBelowLineItems(newDocument); 770 newDocument.renumberItems(0); 771 772 newDocument.setPostingYear(currentDocument.getPostingYear()); 773 774 if (copyNotes) { 775 // Copy the old notes, except for the one that contains the split note text. 776 List<Note> notes = (List<Note>)currentDocument.getBoNotes(); 777 int noteLength = notes.size(); 778 if (noteLength > 0) { 779 notes.subList(noteLength - 1, noteLength).clear(); 780 for(Note note : notes) { 781 try { 782 Note copyingNote = documentService.createNoteFromDocument(newDocument, note.getNoteText()); 783 newDocument.addNote(copyingNote); 784 } 785 catch (Exception e) { 786 throw new RuntimeException(e); 787 } 788 } 789 } 790 } 791 // Modify the split note text and add the note. 792 splitNoteText = splitNoteText.substring(splitNoteText.indexOf(":") + 1); 793 splitNoteText = PurapConstants.PODocumentsStrings.SPLIT_NOTE_PREFIX_NEW_DOC + currentDocument.getPurapDocumentIdentifier() + " : " + splitNoteText; 794 try { 795 Note splitNote = documentService.createNoteFromDocument(newDocument, splitNoteText); 796 newDocument.addNote(splitNote); 797 } 798 catch (Exception e) { 799 throw new RuntimeException(e); 800 } 801 newDocument.setStatusCode(PurchaseOrderStatuses.IN_PROCESS); 802 803 //fix references before saving 804 fixItemReferences(newDocument); 805 806 purapService.saveDocumentNoValidation(newDocument); 807 808 return newDocument; 809 } 810 else { 811 String errorMsg = "Attempting to create new PO of type 'PurchaseOrderSplitDocument' from source PO doc id " + documentNumber + " returned null for new document"; 812 LOG.error(errorMsg); 813 throw new RuntimeException(errorMsg); 814 } 815 } 816 catch (WorkflowException we) { 817 String errorMsg = "Workflow Exception caught trying to create and save PO document of type PurchaseOrderSplitDocument using source document with doc id '" + documentNumber + "'"; 818 LOG.error(errorMsg, we); 819 throw new RuntimeException(errorMsg, we); 820 } 821 } 822 823 /** 824 * Gets a set of classes to exclude from those whose fields will be copied during a copy operation from one Document to 825 * another. 826 * 827 * @return A Set<Class> 828 */ 829 protected Set<Class> getClassesToExcludeFromCopy() { 830 Set<Class> classesToExclude = new HashSet<Class>(); 831 Class sourceObjectClass = DocumentBase.class; 832 classesToExclude.add(sourceObjectClass); 833 while (sourceObjectClass.getSuperclass() != null) { 834 sourceObjectClass = sourceObjectClass.getSuperclass(); 835 classesToExclude.add(sourceObjectClass); 836 } 837 return classesToExclude; 838 } 839 840 /** 841 * Returns the current route node name. 842 * 843 * @param wd The KualiWorkflowDocument object whose current route node we're trying to get. 844 * @return The current route node name. 845 * @throws WorkflowException 846 */ 847 protected String getCurrentRouteNodeName(KualiWorkflowDocument wd) throws WorkflowException { 848 String[] nodeNames = wd.getNodeNames(); 849 if ((nodeNames == null) || (nodeNames.length == 0)) { 850 return null; 851 } 852 else { 853 return nodeNames[0]; 854 } 855 } 856 857 /** 858 * @see org.kuali.kfs.module.purap.document.service.PurchaseOrderService#completePurchaseOrder(org.kuali.kfs.module.purap.document.PurchaseOrderDocument) 859 */ 860 public void completePurchaseOrder(PurchaseOrderDocument po) { 861 LOG.debug("completePurchaseOrder() started"); 862 setCurrentAndPendingIndicatorsForApprovedPODocuments(po); 863 setupDocumentForPendingFirstTransmission(po); 864 865 // check thresholds to see if receiving is required for purchase order 866 if (!po.isReceivingDocumentRequiredIndicator()) { 867 setReceivingRequiredIndicatorForPurchaseOrder(po); 868 } 869 870 // update the vendor record if the commodity code used on the PO is not already associated with the vendor. 871 updateVendorCommodityCode(po); 872 873 // PERFORM ANY LOGIC THAT COULD POTENTIALLY CAUSE THE DOCUMENT TO FAIL BEFORE THIS LINE 874 // FOLLOWING LINES COULD INVOLVE TRANSMITTING THE PO TO THE VENDOR WHICH WILL NOT BE REVERSED IN A TRANSACTION ROLLBACK 875 876 // if the document is set in a Pending Transmission status then don't OPEN the PO just leave it as is 877 if (!PurchaseOrderStatuses.STATUSES_BY_TRANSMISSION_TYPE.values().contains(po.getStatusCode())) { 878 attemptSetupOfInitialOpenOfDocument(po); 879 } 880 else if (PurchaseOrderStatuses.PENDING_CXML.equals(po.getStatusCode())) { 881 completeB2BPurchaseOrder(po); 882 } 883 else if (PurchaseOrderStatuses.PENDING_PRINT.equals(po.getStatusCode())) { 884 //default to using user that routed PO 885 String userToRouteFyi = po.getDocumentHeader().getWorkflowDocument().getRoutedByPrincipalId(); 886 if (po.getPurchaseOrderAutomaticIndicator()) { 887 //if APO, use the user that initiated the requisition 888 RequisitionDocument req = SpringContext.getBean(RequisitionService.class).getRequisitionById(po.getRequisitionIdentifier()); 889 userToRouteFyi = req.getDocumentHeader().getWorkflowDocument().getInitiatorPrincipalId(); 890 } 891 892 try { 893 //send FYI to user for printing 894 po.getDocumentHeader().getWorkflowDocument().adHocRouteDocumentToPrincipal(KEWConstants.ACTION_REQUEST_FYI_REQ, po.getDocumentHeader().getWorkflowDocument().getCurrentRouteNodeNames(), "This PO is ready for printing and distribution.", userToRouteFyi, "", true, "PRINT"); 895 } 896 catch (WorkflowException e) { 897 LOG.error("Error sending FYI to user to print PO.", e); 898 throw new RuntimeException("Error sending FYI to user to print PO.", e); 899 } 900 } 901 902 } 903 904 protected boolean completeB2BPurchaseOrder(PurchaseOrderDocument po) { 905 String errors = b2bPurchaseOrderService.sendPurchaseOrder(po); 906 if (StringUtils.isEmpty(errors)) { 907 //PO sent successfully; change status to OPEN 908 attemptSetupOfInitialOpenOfDocument(po); 909 po.setPurchaseOrderLastTransmitTimestamp(dateTimeService.getCurrentTimestamp()); 910 return true; 911 } 912 else { 913 //PO transmission failed; record errors and change status to "cxml failed" 914 try { 915 String noteText = "Unable to transmit the PO for the following reasons:\n" + errors; 916 int noteMaxSize = dataDictionaryService.getAttributeMaxLength("Note", "noteText"); 917 918 // Break up the note into multiple pieces if the note is too large to fit in the database field. 919 while (noteText.length() > noteMaxSize) { 920 int fromIndex = 0; 921 String noteText1 = noteText.substring(0, noteMaxSize); 922 Note note1 = documentService.createNoteFromDocument(po, noteText1); 923 documentService.addNoteToDocument(po, note1); 924 noteText = noteText.substring(noteMaxSize); 925 } 926 927 Note note = documentService.createNoteFromDocument(po, noteText); 928 documentService.addNoteToDocument(po, note); 929 } 930 catch (Exception e) { 931 throw new RuntimeException(e); 932 } 933 934 purapService.updateStatus(po, PurchaseOrderStatuses.CXML_ERROR); 935 return false; 936 } 937 } 938 939 public void retransmitB2BPurchaseOrder(PurchaseOrderDocument po) { 940 if (completeB2BPurchaseOrder(po)) { 941 GlobalVariables.getMessageList().add(PurapKeyConstants.B2B_PO_RETRANSMIT_SUCCESS); 942 } 943 else { 944 GlobalVariables.getMessageMap().putError(KFSConstants.GLOBAL_ERRORS, PurapKeyConstants.B2B_PO_RETRANSMIT_FAILED); 945 } 946 purapService.saveDocumentNoValidation(po); 947 } 948 949 public boolean canHoldPayment(PurchaseOrderDocument purchaseOrder){ 950 951 if (purchaseOrder.getStatusCode().equals(PurapConstants.PurchaseOrderStatuses.OPEN) && 952 purchaseOrder.isPurchaseOrderCurrentIndicator() && 953 !purchaseOrder.isPendingActionIndicator()) { 954 return true; 955 } 956 957 return false; 958 } 959 960 public boolean canAmendPurchaseOrder(PurchaseOrderDocument purchaseOrder){ 961 boolean canAmend = false; 962 963 //The other conditions for displaying amend button (apart from the condition about No In Process PREQ and CM) 964 //are the same as the conditions for displaying the Payment Hold button, so we're reusing that method here. 965 if (canHoldPayment(purchaseOrder)) { 966 967 canAmend = true; 968 969 if (purchaseOrder.getRelatedViews().getRelatedPaymentRequestViews() != null && 970 purchaseOrder.getRelatedViews().getRelatedPaymentRequestViews().size() > 0) { 971 972 for (PaymentRequestView preq : purchaseOrder.getRelatedViews().getRelatedPaymentRequestViews()) { 973 if (StringUtils.equalsIgnoreCase(preq.getStatusCode(), PaymentRequestStatuses.IN_PROCESS)) { 974 return false; 975 } 976 } 977 978 } 979 980 if (purchaseOrder.getRelatedViews().getRelatedCreditMemoViews() != null && 981 purchaseOrder.getRelatedViews().getRelatedCreditMemoViews().size() > 0) { 982 983 for (CreditMemoView cm : purchaseOrder.getRelatedViews().getRelatedCreditMemoViews()) { 984 if (StringUtils.equalsIgnoreCase(cm.getCreditMemoStatusCode(), CreditMemoStatuses.IN_PROCESS)) { 985 return false; 986 } 987 } 988 } 989 } 990 991 return canAmend; 992 } 993 994 public void completePurchaseOrderAmendment(PurchaseOrderDocument poa) { 995 LOG.debug("completePurchaseOrderAmendment() started"); 996 997 setCurrentAndPendingIndicatorsForApprovedPODocuments(poa); 998 999 if (SpringContext.getBean(PaymentRequestService.class).hasActivePaymentRequestsForPurchaseOrder(poa.getPurapDocumentIdentifier())) { 1000 poa.setPaymentRequestPositiveApprovalIndicator(true); 1001 poa.setReceivingDocumentRequiredIndicator(false); 1002 } 1003 // check thresholds to see if receiving is required for purchase order amendment 1004 else if (!poa.isReceivingDocumentRequiredIndicator()) { 1005 setReceivingRequiredIndicatorForPurchaseOrder(poa); 1006 } 1007 1008 // if unordered items have been added to the PO then send an FYI to all fiscal officers 1009 if (hasNewUnorderedItem(poa)) { 1010 sendFyiForNewUnorderedItems(poa); 1011 } 1012 1013 } 1014 1015 /** 1016 * If there are commodity codes on the items on the PurchaseOrderDocument that 1017 * haven't existed yet on the vendor that the PurchaseOrderDocument is using, 1018 * then we will spawn a new VendorDetailMaintenanceDocument automatically to 1019 * update the vendor with the commodity codes that aren't already existing on 1020 * the vendor. 1021 * 1022 * @param po The PurchaseOrderDocument containing the vendor that we want to update. 1023 */ 1024 public void updateVendorCommodityCode(PurchaseOrderDocument po) { 1025 String noteText = ""; 1026 VendorDetail oldVendorDetail = po.getVendorDetail(); 1027 VendorDetail newVendorDetail = updateVendorWithMissingCommodityCodesIfNecessary(po); 1028 if (newVendorDetail != null) { 1029 try { 1030 // spawn a new vendor maintenance document to add the note 1031 MaintenanceDocument vendorMaintDoc = null; 1032 try { 1033 vendorMaintDoc = (MaintenanceDocument) documentService.getNewDocument("PVEN"); 1034 vendorMaintDoc.getDocumentHeader().setDocumentDescription("Automatically spawned from PO"); 1035 vendorMaintDoc.getOldMaintainableObject().setBusinessObject(oldVendorDetail); 1036 vendorMaintDoc.getNewMaintainableObject().setBusinessObject(newVendorDetail); 1037 vendorMaintDoc.getNewMaintainableObject().setMaintenanceAction(KFSConstants.MAINTENANCE_EDIT_ACTION); 1038 vendorMaintDoc.getNewMaintainableObject().setDocumentNumber(vendorMaintDoc.getDocumentNumber()); 1039 boolean isVendorLocked = checkForLockingDocument(vendorMaintDoc); 1040 if (!isVendorLocked) { 1041 //validating vendor doc to capture exception before trying to route which if exception happens in docService, then PO will fail too 1042 vendorMaintDoc.validateBusinessRules(new RouteDocumentEvent(vendorMaintDoc)); 1043 addNoteForCommodityCodeToVendor(vendorMaintDoc.getNewMaintainableObject(), vendorMaintDoc.getDocumentNumber(), po.getPurapDocumentIdentifier()); 1044 documentService.routeDocument(vendorMaintDoc, null, null); 1045 } 1046 else { 1047 // Add a note to the PO to tell the users that we can't automatically update the vendor because it's locked. 1048 noteText = "Unable to automatically update vendor because it is locked"; 1049 } 1050 } 1051 catch (Exception e) { 1052 if (ObjectUtils.isNull(vendorMaintDoc)) { 1053 noteText = "Unable to create a new VendorDetailMaintenanceDocument to update the vendor with new commodity codes"; 1054 } 1055 else { 1056 noteText = "Unable to route a new VendorDetailMaintenanceDocument to update the vendor with new commodity codes"; 1057 } 1058 } 1059 finally { 1060 if (StringUtils.isNotBlank(noteText)) { 1061 // update on purchase order notes 1062 Note note = documentService.createNoteFromDocument(po, noteText); 1063 documentService.addNoteToDocument(po, note); 1064 noteService.save(note); 1065 } 1066 } 1067 } 1068 catch (Exception e) { 1069 LOG.error("updateVendorCommodityCode() unable to add a note(" + noteText + ") to PO document " + po.getDocumentNumber()); 1070 } 1071 } 1072 } 1073 1074 /** 1075 * Creates a note to be added to the Vendor Maintenance Document which is spawned 1076 * from the PurchaseOrderDocument. 1077 * 1078 * @param maintainable 1079 * @param documentNumber 1080 * @param poID 1081 */ 1082 protected void addNoteForCommodityCodeToVendor(Maintainable maintainable, String documentNumber, Integer poID) { 1083 Note newBONote = new Note(); 1084 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."); 1085 try { 1086 newBONote = noteService.createNote(newBONote, maintainable.getBusinessObject()); 1087 } 1088 catch (Exception e) { 1089 throw new RuntimeException("Caught Exception While Trying To Add Note to Vendor", e); 1090 } 1091 maintainable.getBusinessObject().getBoNotes().add(newBONote); 1092 } 1093 1094 /** 1095 * Checks whether the vendor is currently locked. 1096 * 1097 * @param document The MaintenanceDocument containing the vendor. 1098 * @return boolean true if the vendor is currently locked and false otherwise. 1099 */ 1100 protected boolean checkForLockingDocument(MaintenanceDocument document) { 1101 String blockingDocId = maintenanceDocumentService.getLockingDocumentId(document); 1102 if (StringUtils.isBlank(blockingDocId)) { 1103 return false; 1104 } 1105 else { 1106 return true; 1107 } 1108 } 1109 1110 /** 1111 * @see org.kuali.kfs.module.purap.document.service.PurchaseOrderService#updateVendorWithMissingCommodityCodesIfNecessary(org.kuali.kfs.module.purap.document.PurchaseOrderDocument) 1112 */ 1113 public VendorDetail updateVendorWithMissingCommodityCodesIfNecessary(PurchaseOrderDocument po) { 1114 List<CommodityCode> result = new ArrayList<CommodityCode>(); 1115 boolean foundDefault = false; 1116 VendorDetail vendor = (VendorDetail)ObjectUtils.deepCopy(po.getVendorDetail()); 1117 for (PurchaseOrderItem item : (List<PurchaseOrderItem>)po.getItems()) { 1118 //Only check on commodity codes if the item is active and is above the line item type. 1119 if (item.getItemType().isLineItemIndicator() && item.isItemActiveIndicator()) { 1120 CommodityCode cc = item.getCommodityCode(); 1121 if (cc != null && !result.contains(cc)) { 1122 List<VendorCommodityCode> vendorCommodityCodes = po.getVendorDetail().getVendorCommodities(); 1123 boolean foundMatching = false; 1124 for (VendorCommodityCode vcc : vendorCommodityCodes) { 1125 if (vcc.getCommodityCode().getPurchasingCommodityCode().equals(cc.getPurchasingCommodityCode())) { 1126 foundMatching = true; 1127 } 1128 if (!foundDefault && vcc.isCommodityDefaultIndicator()) { 1129 foundDefault = true; 1130 } 1131 } 1132 if (!foundMatching) { 1133 result.add(cc); 1134 VendorCommodityCode vcc = new VendorCommodityCode(vendor.getVendorHeaderGeneratedIdentifier(), vendor.getVendorDetailAssignedIdentifier(), cc, true); 1135 vcc.setActive(true); 1136 if (!foundDefault) { 1137 vcc.setCommodityDefaultIndicator(true); 1138 foundDefault = true; 1139 } 1140 vendor.getVendorCommodities().add(vcc); 1141 } 1142 } 1143 } 1144 } 1145 if (result.size() > 0) { 1146 //We also have to add to the old vendor detail's vendorCommodities if we're adding to the new 1147 //vendor detail's vendorCommodities. 1148 for (int i=0; i<result.size(); i++) { 1149 po.getVendorDetail().getVendorCommodities().add(new VendorCommodityCode()); 1150 } 1151 return vendor; 1152 } 1153 else { 1154 return null; 1155 } 1156 } 1157 1158 /** 1159 * Update the purchase order document with the appropriate status for pending first transmission based on the transmission type. 1160 * 1161 * @param po The purchase order document whose status to be updated. 1162 */ 1163 protected void setupDocumentForPendingFirstTransmission(PurchaseOrderDocument po) { 1164 if (POTransmissionMethods.PRINT.equals(po.getPurchaseOrderTransmissionMethodCode()) || POTransmissionMethods.FAX.equals(po.getPurchaseOrderTransmissionMethodCode()) || POTransmissionMethods.ELECTRONIC.equals(po.getPurchaseOrderTransmissionMethodCode())) { 1165 String newStatusCode = PurchaseOrderStatuses.STATUSES_BY_TRANSMISSION_TYPE.get(po.getPurchaseOrderTransmissionMethodCode()); 1166 LOG.debug("setupDocumentForPendingFirstTransmission() Purchase Order Transmission Type is '" + po.getPurchaseOrderTransmissionMethodCode() + "' setting status to '" + newStatusCode + "'"); 1167 purapService.updateStatus(po, newStatusCode); 1168 } 1169 } 1170 1171 /** 1172 * 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 1173 * and update the status to OPEN, then save the purchase order. 1174 * 1175 * @param po The purchase order document whose initial open date and status we want to update. 1176 */ 1177 protected void attemptSetupOfInitialOpenOfDocument(PurchaseOrderDocument po) { 1178 LOG.debug("attemptSetupOfInitialOpenOfDocument() started using document with doc id " + po.getDocumentNumber()); 1179 1180 if (!PurchaseOrderStatuses.OPEN.equals(po.getStatusCode())) { 1181 if (ObjectUtils.isNull(po.getPurchaseOrderInitialOpenTimestamp())) { 1182 LOG.debug("attemptSetupOfInitialOpenOfDocument() setting initial open date on document"); 1183 po.setPurchaseOrderInitialOpenTimestamp(dateTimeService.getCurrentTimestamp()); 1184 } 1185 else { 1186 throw new RuntimeException("Document does not have status code '" + PurchaseOrderStatuses.OPEN + "' on it but value of initial open date is " + po.getPurchaseOrderInitialOpenTimestamp()); 1187 } 1188 LOG.info("attemptSetupOfInitialOpenOfDocument() Setting po document id " + po.getDocumentNumber() + " status from '" + po.getStatusCode() + "' to '" + PurchaseOrderStatuses.OPEN + "'"); 1189 purapService.updateStatus(po, PurchaseOrderStatuses.OPEN); 1190 //no need to save here because calling class should handle the save if needed 1191 } 1192 else { 1193 LOG.error("attemptSetupOfInitialOpenOfDocument() Found document already in '" + PurchaseOrderStatuses.OPEN + "' status for PO#" + po.getPurapDocumentIdentifier() + "; will not change or update"); 1194 } 1195 } 1196 1197 /** 1198 * @see org.kuali.kfs.module.purap.document.service.PurchaseOrderService#getCurrentPurchaseOrder(java.lang.Integer) 1199 */ 1200 public PurchaseOrderDocument getCurrentPurchaseOrder(Integer id) { 1201 return getPurchaseOrderByDocumentNumber(purchaseOrderDao.getDocumentNumberForCurrentPurchaseOrder(id)); 1202 //TODO hjs: code review (why is this DB call so complicated? wouldn't this method be cleaner and less db calls?) 1203 // return purchaseOrderDao.getCurrentPurchaseOrder(id); 1204 } 1205 1206 /** 1207 * @see org.kuali.kfs.module.purap.document.service.PurchaseOrderService#getPurchaseOrderByDocumentNumber(java.lang.String) 1208 */ 1209 public PurchaseOrderDocument getPurchaseOrderByDocumentNumber(String documentNumber) { 1210 if (ObjectUtils.isNotNull(documentNumber)) { 1211 try { 1212 PurchaseOrderDocument doc = (PurchaseOrderDocument) documentService.getByDocumentHeaderId(documentNumber); 1213 if (ObjectUtils.isNotNull(doc)) { 1214 KualiWorkflowDocument workflowDocument = doc.getDocumentHeader().getWorkflowDocument(); 1215 doc.refreshReferenceObject(KFSPropertyConstants.DOCUMENT_HEADER); 1216 doc.getDocumentHeader().setWorkflowDocument(workflowDocument); 1217 } 1218 return doc; 1219 } 1220 catch (WorkflowException e) { 1221 String errorMessage = "Error getting purchase order document from document service"; 1222 LOG.error("getPurchaseOrderByDocumentNumber() " + errorMessage, e); 1223 throw new RuntimeException(errorMessage, e); 1224 } 1225 } 1226 return null; 1227 } 1228 1229 /** 1230 * @see org.kuali.kfs.module.purap.document.service.PurchaseOrderService#getOldestPurchaseOrder(org.kuali.kfs.module.purap.document.PurchaseOrderDocument, 1231 * org.kuali.kfs.module.purap.document.PurchaseOrderDocument) 1232 */ 1233 public PurchaseOrderDocument getOldestPurchaseOrder(PurchaseOrderDocument po, PurchaseOrderDocument documentBusinessObject) { 1234 LOG.debug("entering getOldestPO(PurchaseOrderDocument)"); 1235 if (ObjectUtils.isNotNull(po)) { 1236 String oldestDocumentNumber = purchaseOrderDao.getOldestPurchaseOrderDocumentNumber(po.getPurapDocumentIdentifier()); 1237 if (StringUtils.equals(oldestDocumentNumber, po.getDocumentNumber())) { 1238 // manually set bo notes - this is mainly done for performance reasons (preferably we could call 1239 // retrieve doc notes in PersistableBusinessObjectBase but that is private) 1240 updateNotes(po, documentBusinessObject); 1241 LOG.debug("exiting getOldestPO(PurchaseOrderDocument)"); 1242 return po; 1243 } 1244 else { 1245 PurchaseOrderDocument oldestPurchaseOrder = getPurchaseOrderByDocumentNumber(oldestDocumentNumber); 1246 updateNotes(oldestPurchaseOrder, documentBusinessObject); 1247 LOG.debug("exiting getOldestPO(PurchaseOrderDocument)"); 1248 return oldestPurchaseOrder; 1249 } 1250 } 1251 return null; 1252 } 1253 1254 /** 1255 * 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 1256 * saved to the db previously), get the notes of the purchase order from the database, fix the notes' fields by calling the 1257 * fixDbNoteFields, then set the notes to the purchase order. Otherwise (I think this means if it's a new purchase order), set 1258 * the notes of this purchase order to be the notes of the documentBusinessObject. 1259 * 1260 * @param po The current purchase order. 1261 * @param documentBusinessObject The oldest purchase order whose purapDocumentIdentifier is the same as the po's 1262 * purapDocumentIdentifier. 1263 */ 1264 protected void updateNotes(PurchaseOrderDocument po, PurchaseOrderDocument documentBusinessObject) { 1265 if (ObjectUtils.isNotNull(documentBusinessObject)) { 1266 if (ObjectUtils.isNotNull(po.getObjectId())) { 1267 List<Note> dbNotes = noteService.getByRemoteObjectId(po.getObjectId()); 1268 // need to set fields that are not ojb managed (i.e. the notes on the documentBusinessObject may have been modified 1269 // independently of the ones in the db) 1270 fixDbNoteFields(documentBusinessObject, dbNotes); 1271 po.setBoNotes(dbNotes); 1272 } 1273 else { 1274 po.setBoNotes(documentBusinessObject.getBoNotes()); 1275 } 1276 } 1277 } 1278 1279 /** 1280 * This method fixes non ojb managed missing fields from the db 1281 * 1282 * @param documentBusinessObject The oldest purchase order whose purapDocumentIdentifier is the same as the po's 1283 * purapDocumentIdentifier. 1284 * @param dbNotes The notes of the purchase order obtained from the database. 1285 */ 1286 protected void fixDbNoteFields(PurchaseOrderDocument documentBusinessObject, List<Note> dbNotes) { 1287 for (int i = 0; i < dbNotes.size(); i++) { 1288 Note dbNote = dbNotes.get(i); 1289 List<Note> currentNotes = (List<Note>) documentBusinessObject.getBoNotes(); 1290 if (i < currentNotes.size()) { 1291 Note currentNote = (currentNotes).get(i); 1292 // set the fyi from the current note if not empty 1293 AdHocRouteRecipient fyiNoteRecipient = currentNote.getAdHocRouteRecipient(); 1294 if (ObjectUtils.isNotNull(fyiNoteRecipient)) { 1295 dbNote.setAdHocRouteRecipient(fyiNoteRecipient); 1296 } 1297 } 1298 } 1299 } 1300 1301 /** 1302 * @see org.kuali.kfs.module.purap.document.service.PurchaseOrderService#getPurchaseOrderNotes(java.lang.Integer) 1303 */ 1304 public ArrayList<Note> getPurchaseOrderNotes(Integer id) { 1305 ArrayList notes = new TypedArrayList(Note.class); 1306 PurchaseOrderDocument po = getPurchaseOrderByDocumentNumber(purchaseOrderDao.getOldestPurchaseOrderDocumentNumber(id)); 1307 if (ObjectUtils.isNotNull(po)) { 1308 notes = noteService.getByRemoteObjectId(po.getObjectId()); 1309 } 1310 return notes; 1311 } 1312 1313 /** 1314 * @see org.kuali.kfs.module.purap.document.service.PurchaseOrderService#setCurrentAndPendingIndicatorsForApprovedPODocuments(org.kuali.kfs.module.purap.document.PurchaseOrderDocument) 1315 */ 1316 public void setCurrentAndPendingIndicatorsForApprovedPODocuments(PurchaseOrderDocument newPO) { 1317 // Get the "current PO" that's in the database, i.e. the PO row that contains current indicator = Y 1318 PurchaseOrderDocument oldPO = getCurrentPurchaseOrder(newPO.getPurapDocumentIdentifier()); 1319 1320 // If the document numbers between the oldPO and the newPO are different, then this is a PO change document. 1321 if (!oldPO.getDocumentNumber().equals(newPO.getDocumentNumber())) { 1322 // First, we set the indicators for the oldPO to : Current = N and Pending = N 1323 oldPO.setPurchaseOrderCurrentIndicator(false); 1324 oldPO.setPendingActionIndicator(false); 1325 1326 // set the status and status history of the oldPO to retired version 1327 purapService.updateStatus(oldPO, PurapConstants.PurchaseOrderStatuses.RETIRED_VERSION); 1328 1329 saveDocumentNoValidationUsingClearErrorMap(oldPO); 1330 } 1331 1332 // Now, we set the "new PO" indicators so that Current = Y and Pending = N 1333 newPO.setPurchaseOrderCurrentIndicator(true); 1334 newPO.setPendingActionIndicator(false); 1335 } 1336 1337 /** 1338 * @see org.kuali.kfs.module.purap.document.service.PurchaseOrderService#setCurrentAndPendingIndicatorsForDisapprovedChangePODocuments(org.kuali.kfs.module.purap.document.PurchaseOrderDocument) 1339 */ 1340 public void setCurrentAndPendingIndicatorsForDisapprovedChangePODocuments(PurchaseOrderDocument newPO) { 1341 updateCurrentDocumentForNoPendingAction(newPO, PurapConstants.PurchaseOrderStatuses.DISAPPROVED_CHANGE, PurapConstants.PurchaseOrderStatuses.OPEN); 1342 } 1343 1344 /** 1345 * @see org.kuali.kfs.module.purap.document.service.PurchaseOrderService#setCurrentAndPendingIndicatorsForCancelledChangePODocuments(org.kuali.kfs.module.purap.document.PurchaseOrderDocument) 1346 */ 1347 public void setCurrentAndPendingIndicatorsForCancelledChangePODocuments(PurchaseOrderDocument newPO) { 1348 updateCurrentDocumentForNoPendingAction(newPO, PurapConstants.PurchaseOrderStatuses.CANCELLED_CHANGE, PurapConstants.PurchaseOrderStatuses.OPEN); 1349 } 1350 1351 /** 1352 * @see org.kuali.kfs.module.purap.document.service.PurchaseOrderService#setCurrentAndPendingIndicatorsForCancelledReopenPODocuments(org.kuali.kfs.module.purap.document.PurchaseOrderDocument) 1353 */ 1354 public void setCurrentAndPendingIndicatorsForCancelledReopenPODocuments(PurchaseOrderDocument newPO) { 1355 updateCurrentDocumentForNoPendingAction(newPO, PurapConstants.PurchaseOrderStatuses.CANCELLED_CHANGE, PurapConstants.PurchaseOrderStatuses.CLOSED); 1356 } 1357 1358 /** 1359 * @see org.kuali.kfs.module.purap.document.service.PurchaseOrderService#setCurrentAndPendingIndicatorsForDisapprovedReopenPODocuments(org.kuali.kfs.module.purap.document.PurchaseOrderDocument) 1360 */ 1361 public void setCurrentAndPendingIndicatorsForDisapprovedReopenPODocuments(PurchaseOrderDocument newPO) { 1362 updateCurrentDocumentForNoPendingAction(newPO, PurapConstants.PurchaseOrderStatuses.DISAPPROVED_CHANGE, PurapConstants.PurchaseOrderStatuses.CLOSED); 1363 } 1364 1365 /** 1366 * @see org.kuali.kfs.module.purap.document.service.PurchaseOrderService#setCurrentAndPendingIndicatorsForCancelledRemoveHoldPODocuments(org.kuali.kfs.module.purap.document.PurchaseOrderDocument) 1367 */ 1368 public void setCurrentAndPendingIndicatorsForCancelledRemoveHoldPODocuments(PurchaseOrderDocument newPO) { 1369 updateCurrentDocumentForNoPendingAction(newPO, PurapConstants.PurchaseOrderStatuses.CANCELLED_CHANGE, PurapConstants.PurchaseOrderStatuses.PAYMENT_HOLD); 1370 } 1371 1372 /** 1373 * @see org.kuali.kfs.module.purap.document.service.PurchaseOrderService#setCurrentAndPendingIndicatorsForDisapprovedRemoveHoldPODocuments(org.kuali.kfs.module.purap.document.PurchaseOrderDocument) 1374 */ 1375 public void setCurrentAndPendingIndicatorsForDisapprovedRemoveHoldPODocuments(PurchaseOrderDocument newPO) { 1376 updateCurrentDocumentForNoPendingAction(newPO, PurapConstants.PurchaseOrderStatuses.DISAPPROVED_CHANGE, PurapConstants.PurchaseOrderStatuses.PAYMENT_HOLD); 1377 } 1378 1379 /** 1380 * Update the statuses of both the old purchase order and the new purchase orders, then save the old and the new purchase 1381 * orders. 1382 * 1383 * @param newPO The new change purchase order document (e.g. the PurchaseOrderAmendmentDocument that was resulted from the user 1384 * clicking on the amend button). 1385 * @param newPOStatus The status to be set on the new change purchase order document. 1386 * @param oldPOStatus The status to be set on the existing (old) purchase order document. 1387 */ 1388 protected void updateCurrentDocumentForNoPendingAction(PurchaseOrderDocument newPO, String newPOStatus, String oldPOStatus) { 1389 // Get the "current PO" that's in the database, i.e. the PO row that contains current indicator = Y 1390 PurchaseOrderDocument oldPO = getCurrentPurchaseOrder(newPO.getPurapDocumentIdentifier()); 1391 // Set the Pending indicator for the oldPO to N 1392 oldPO.setPendingActionIndicator(false); 1393 purapService.updateStatus(oldPO, oldPOStatus); 1394 purapService.updateStatus(newPO, newPOStatus); 1395 saveDocumentNoValidationUsingClearErrorMap(oldPO); 1396 saveDocumentNoValidationUsingClearErrorMap(newPO); 1397 } 1398 1399 public ArrayList<PurchaseOrderQuoteStatus> getPurchaseOrderQuoteStatusCodes() { 1400 ArrayList poQuoteStatuses = new TypedArrayList(PurchaseOrderQuoteStatus.class); 1401 poQuoteStatuses = (ArrayList) businessObjectService.findAll(PurchaseOrderQuoteStatus.class); 1402 return poQuoteStatuses; 1403 } 1404 1405 public void setReceivingRequiredIndicatorForPurchaseOrder(PurchaseOrderDocument po) { 1406 ThresholdHelper thresholdHelper = new ThresholdHelper(po); 1407 boolean result = thresholdHelper.isReceivingDocumentRequired(); 1408 if (result) { 1409 ThresholdSummary thresholdSummary = thresholdHelper.getThresholdSummary(); 1410 ReceivingThreshold receivingThreshold = thresholdHelper.getReceivingThreshold(); 1411 po.setReceivingDocumentRequiredIndicator(true); 1412 1413 String notetxt = "Receiving is set to be required because the threshold summary with a total amount of " + thresholdSummary.getTotalAmount(); 1414 notetxt += " exceeds the receiving threshold of " + receivingThreshold.getThresholdAmount(); 1415 notetxt += " with respect to the threshold criteria "; 1416 1417 if (thresholdSummary.getThresholdCriteria() == ThresholdHelper.CHART){ 1418 notetxt += " Chart " + receivingThreshold.getChartOfAccountsCode(); 1419 } else if (thresholdSummary.getThresholdCriteria() == ThresholdHelper.CHART_AND_ACCOUNTTYPE){ 1420 notetxt += " Chart " + receivingThreshold.getChartOfAccountsCode(); 1421 notetxt += " - Account Type " + receivingThreshold.getAccountTypeCode(); 1422 } else if (thresholdSummary.getThresholdCriteria() == ThresholdHelper.CHART_AND_SUBFUND){ 1423 notetxt += " Chart " + receivingThreshold.getChartOfAccountsCode(); 1424 notetxt += " - Sub-Fund " + receivingThreshold.getSubFundGroupCode(); 1425 } else if (thresholdSummary.getThresholdCriteria() == ThresholdHelper.CHART_AND_COMMODITYCODE){ 1426 notetxt += " Chart " + receivingThreshold.getChartOfAccountsCode(); 1427 notetxt += " - Commodity Code " + receivingThreshold.getPurchasingCommodityCode(); 1428 } else if (thresholdSummary.getThresholdCriteria() == ThresholdHelper.CHART_AND_OBJECTCODE){ 1429 notetxt += " Chart " + receivingThreshold.getChartOfAccountsCode(); 1430 notetxt += " - Object code " + receivingThreshold.getFinancialObjectCode(); 1431 } else if (thresholdSummary.getThresholdCriteria() == ThresholdHelper.CHART_AND_ORGANIZATIONCODE){ 1432 notetxt += " Chart " + receivingThreshold.getChartOfAccountsCode(); 1433 notetxt += " - Organization " + receivingThreshold.getOrganizationCode(); 1434 } else if (thresholdSummary.getThresholdCriteria() == ThresholdHelper.CHART_AND_VENDOR){ 1435 notetxt += " Chart " + receivingThreshold.getChartOfAccountsCode(); 1436 notetxt += " - Vendor " + receivingThreshold.getVendorNumber(); 1437 } 1438 1439 try { 1440 Note note = documentService.createNoteFromDocument(po, notetxt); 1441 documentService.addNoteToDocument(po, note); 1442 noteService.save(note); 1443 } 1444 catch (Exception e) { 1445 throw new RuntimeException(e); 1446 } 1447 } 1448 } 1449 1450 /** 1451 * @see org.kuali.kfs.module.purap.document.service.PurchaseOrderService#hasNewUnorderedItem(org.kuali.kfs.module.purap.document.PurchaseOrderDocument) 1452 */ 1453 public boolean hasNewUnorderedItem(PurchaseOrderDocument po){ 1454 1455 boolean itemAdded = false; 1456 1457 for(PurchaseOrderItem poItem: (List<PurchaseOrderItem>)po.getItems()){ 1458 //only check, active, above the line, unordered items 1459 if (poItem.isItemActiveIndicator() && poItem.getItemType().isLineItemIndicator() && PurapConstants.ItemTypeCodes.ITEM_TYPE_UNORDERED_ITEM_CODE.equals(poItem.getItemTypeCode()) ) { 1460 1461 //if the item identifier is null its new, or if the item doesn't exist on the current purchase order it's new 1462 if( poItem.getItemIdentifier() == null || !purchaseOrderDao.itemExistsOnPurchaseOrder(poItem.getItemLineNumber(), purchaseOrderDao.getDocumentNumberForCurrentPurchaseOrder(po.getPurapDocumentIdentifier()) )){ 1463 itemAdded = true; 1464 break; 1465 } 1466 } 1467 } 1468 1469 return itemAdded; 1470 } 1471 1472 public boolean isNewUnorderedItem(PurchaseOrderItem poItem){ 1473 1474 boolean itemAdded = false; 1475 1476 //only check, active, above the line, unordered items 1477 if (poItem.isItemActiveIndicator() && poItem.getItemType().isLineItemIndicator() && PurapConstants.ItemTypeCodes.ITEM_TYPE_UNORDERED_ITEM_CODE.equals(poItem.getItemTypeCode()) ) { 1478 1479 //if the item identifier is null its new, or if the item doesn't exist on the current purchase order it's new 1480 if( poItem.getItemIdentifier() == null || !purchaseOrderDao.itemExistsOnPurchaseOrder(poItem.getItemLineNumber(), purchaseOrderDao.getDocumentNumberForCurrentPurchaseOrder(poItem.getPurchaseOrder().getPurapDocumentIdentifier()) )){ 1481 itemAdded = true; 1482 } 1483 } 1484 1485 return itemAdded; 1486 } 1487 1488 public boolean isNewItemForAmendment(PurchaseOrderItem poItem){ 1489 1490 boolean itemAdded = false; 1491 1492 //only check, active, above the line, unordered items 1493 if (poItem.isItemActiveIndicator() && poItem.getItemType().isLineItemIndicator()) { 1494 1495 //if the item identifier is null its new, or if the item doesn't exist on the current purchase order it's new 1496 if( poItem.getItemIdentifier() == null || !purchaseOrderDao.itemExistsOnPurchaseOrder(poItem.getItemLineNumber(), purchaseOrderDao.getDocumentNumberForCurrentPurchaseOrder(poItem.getPurchaseOrder().getPurapDocumentIdentifier()) )){ 1497 itemAdded = true; 1498 } 1499 } 1500 1501 return itemAdded; 1502 } 1503 1504 /** 1505 * Sends an FYI to fiscal officers for new unordered items. 1506 * 1507 * @param po 1508 */ 1509 protected void sendFyiForNewUnorderedItems(PurchaseOrderDocument po){ 1510 1511 List<AdHocRoutePerson> fyiList = createFyiFiscalOfficerListForNewUnorderedItems(po); 1512 String annotation = "Notification of New Unordered Items for Purchase Order" + po.getPurapDocumentIdentifier() + "(document id " + po.getDocumentNumber() + ")"; 1513 String responsibilityNote = "Purchase Order Amendment Routed By User"; 1514 1515 for(AdHocRoutePerson adHocPerson: fyiList){ 1516 try{ 1517 po.appSpecificRouteDocumentToUser( 1518 po.getDocumentHeader().getWorkflowDocument(), 1519 adHocPerson.getId(), 1520 annotation, 1521 responsibilityNote); 1522 }catch (WorkflowException e) { 1523 throw new RuntimeException("Error routing fyi for document with id " + po.getDocumentNumber(), e); 1524 } 1525 1526 } 1527 } 1528 1529 /** 1530 * Creates a list of fiscal officers for new unordered items added to a purchase order. 1531 * 1532 * @param po 1533 * @return 1534 */ 1535 protected List<AdHocRoutePerson> createFyiFiscalOfficerListForNewUnorderedItems(PurchaseOrderDocument po){ 1536 1537 List<AdHocRoutePerson> adHocRoutePersons = new ArrayList<AdHocRoutePerson>(); 1538 Map fiscalOfficers = new HashMap(); 1539 AdHocRoutePerson adHocRoutePerson = null; 1540 1541 for(PurchaseOrderItem poItem: (List<PurchaseOrderItem>)po.getItems()){ 1542 //only check, active, above the line, unordered items 1543 if (poItem.isItemActiveIndicator() && poItem.getItemType().isLineItemIndicator() && PurapConstants.ItemTypeCodes.ITEM_TYPE_UNORDERED_ITEM_CODE.equals(poItem.getItemTypeCode()) ) { 1544 1545 //if the item identifier is null its new, or if the item doesn't exist on the current purchase order it's new 1546 if( poItem.getItemIdentifier() == null || !purchaseOrderDao.itemExistsOnPurchaseOrder(poItem.getItemLineNumber(), purchaseOrderDao.getDocumentNumberForCurrentPurchaseOrder(po.getPurapDocumentIdentifier()) )){ 1547 1548 // loop through accounts and pull off fiscal officer 1549 for(PurApAccountingLine account : poItem.getSourceAccountingLines()){ 1550 1551 //check for dupes of fiscal officer 1552 if( fiscalOfficers.containsKey(account.getAccount().getAccountFiscalOfficerUser().getPrincipalName()) == false ){ 1553 1554 //add fiscal officer to list 1555 fiscalOfficers.put(account.getAccount().getAccountFiscalOfficerUser().getPrincipalName(), account.getAccount().getAccountFiscalOfficerUser().getPrincipalName()); 1556 1557 //create AdHocRoutePerson object and add to list 1558 adHocRoutePerson = new AdHocRoutePerson(); 1559 adHocRoutePerson.setId(account.getAccount().getAccountFiscalOfficerUser().getPrincipalName()); 1560 adHocRoutePerson.setActionRequested(KFSConstants.WORKFLOW_FYI_REQUEST); 1561 adHocRoutePersons.add(adHocRoutePerson); 1562 } 1563 } 1564 } 1565 } 1566 } 1567 1568 return adHocRoutePersons; 1569 } 1570 1571 /** 1572 * @see org.kuali.kfs.module.purap.document.service.PurchaseOrderService#categorizeItemsForSplit(java.util.List) 1573 */ 1574 public HashMap<String, List<PurchaseOrderItem>> categorizeItemsForSplit(List<PurchaseOrderItem> items) { 1575 HashMap<String, List<PurchaseOrderItem>> movingOrNot = new HashMap<String, List<PurchaseOrderItem>>(3); 1576 List<PurchaseOrderItem> movingPOItems = new TypedArrayList(PurchaseOrderItem.class); 1577 List<PurchaseOrderItem> remainingPOItems = new TypedArrayList(PurchaseOrderItem.class); 1578 List<PurchaseOrderItem> remainingPOLineItems = new TypedArrayList(PurchaseOrderItem.class); 1579 for (PurchaseOrderItem item : items) { 1580 if(item.isMovingToSplit()) { 1581 movingPOItems.add(item); 1582 } 1583 else { 1584 remainingPOItems.add(item); 1585 if (item.getItemType().isLineItemIndicator()) { 1586 remainingPOLineItems.add(item); 1587 } 1588 } 1589 } 1590 movingOrNot.put(PODocumentsStrings.ITEMS_MOVING_TO_SPLIT, movingPOItems); 1591 movingOrNot.put(PODocumentsStrings.ITEMS_REMAINING, remainingPOItems); 1592 movingOrNot.put(PODocumentsStrings.LINE_ITEMS_REMAINING, remainingPOLineItems); 1593 return movingOrNot; 1594 } 1595 1596 /** 1597 * @see org.kuali.module.purap.service.PurchaseOrderService#populateQuoteWithVendor(java.lang.Integer, java.lang.Integer, java.lang.String) 1598 */ 1599 public PurchaseOrderVendorQuote populateQuoteWithVendor(Integer headerId, Integer detailId, String documentNumber) { 1600 VendorDetail vendor = vendorService.getVendorDetail(headerId, detailId); 1601 updateDefaultVendorAddress(vendor); 1602 PurchaseOrderVendorQuote newPOVendorQuote = populateAddressForPOVendorQuote(vendor, documentNumber); 1603 1604 //Set the vendorPhoneNumber on the quote to be the first "phone number" type phone 1605 //found on the list. If there's no "phone number" type found, the quote's 1606 //vendorPhoneNumber will be blank regardless of any other types of phone found on the list. 1607 for (VendorPhoneNumber phone : vendor.getVendorPhoneNumbers()) { 1608 if (VendorConstants.PhoneTypes.PHONE.equals(phone.getVendorPhoneTypeCode())) { 1609 newPOVendorQuote.setVendorPhoneNumber(phone.getVendorPhoneNumber()); 1610 break; 1611 } 1612 } 1613 1614 return newPOVendorQuote; 1615 } 1616 1617 /** 1618 * Creates the new PurchaseOrderVendorQuote and populate the address fields for it. 1619 * 1620 * @param newVendor The VendorDetail object from which we obtain the values for the address fields. 1621 * @param documentNumber The documentNumber of the PurchaseOrderDocument containing the PurchaseOrderVendorQuote. 1622 * @return 1623 */ 1624 protected PurchaseOrderVendorQuote populateAddressForPOVendorQuote(VendorDetail newVendor, String documentNumber) { 1625 PurchaseOrderVendorQuote newPOVendorQuote = new PurchaseOrderVendorQuote(); 1626 newPOVendorQuote.setVendorName(newVendor.getVendorName()); 1627 newPOVendorQuote.setVendorHeaderGeneratedIdentifier(newVendor.getVendorHeaderGeneratedIdentifier()); 1628 newPOVendorQuote.setVendorDetailAssignedIdentifier(newVendor.getVendorDetailAssignedIdentifier()); 1629 newPOVendorQuote.setDocumentNumber(documentNumber); 1630 boolean foundAddress = false; 1631 for (VendorAddress address : newVendor.getVendorAddresses()) { 1632 if (AddressTypes.QUOTE.equals(address.getVendorAddressTypeCode())) { 1633 newPOVendorQuote.setVendorCityName(address.getVendorCityName()); 1634 newPOVendorQuote.setVendorCountryCode(address.getVendorCountryCode()); 1635 newPOVendorQuote.setVendorLine1Address(address.getVendorLine1Address()); 1636 newPOVendorQuote.setVendorLine2Address(address.getVendorLine2Address()); 1637 newPOVendorQuote.setVendorPostalCode(address.getVendorZipCode()); 1638 newPOVendorQuote.setVendorStateCode(address.getVendorStateCode()); 1639 newPOVendorQuote.setVendorFaxNumber(address.getVendorFaxNumber()); 1640 foundAddress = true; 1641 break; 1642 } 1643 } 1644 if (!foundAddress) { 1645 newPOVendorQuote.setVendorCityName(newVendor.getDefaultAddressCity()); 1646 newPOVendorQuote.setVendorCountryCode(newVendor.getDefaultAddressCountryCode()); 1647 newPOVendorQuote.setVendorLine1Address(newVendor.getDefaultAddressLine1()); 1648 newPOVendorQuote.setVendorLine2Address(newVendor.getDefaultAddressLine2()); 1649 newPOVendorQuote.setVendorPostalCode(newVendor.getDefaultAddressPostalCode()); 1650 newPOVendorQuote.setVendorStateCode(newVendor.getDefaultAddressStateCode()); 1651 newPOVendorQuote.setVendorFaxNumber(newVendor.getDefaultFaxNumber()); 1652 } 1653 return newPOVendorQuote; 1654 } 1655 1656 /** 1657 * Obtains the defaultAddress of the vendor and setting the default address fields on 1658 * the vendor. 1659 * 1660 * @param vendor The VendorDetail object whose default address we'll obtain and set the fields. 1661 */ 1662 protected void updateDefaultVendorAddress(VendorDetail vendor) { 1663 VendorAddress defaultAddress = SpringContext.getBean(VendorService.class).getVendorDefaultAddress(vendor.getVendorAddresses(), vendor.getVendorHeader().getVendorType().getAddressType().getVendorAddressTypeCode(), ""); 1664 if (defaultAddress != null ) { 1665 if (defaultAddress.getVendorState() != null) { 1666 vendor.setVendorStateForLookup(defaultAddress.getVendorState().getPostalStateName()); 1667 } 1668 vendor.setDefaultAddressLine1(defaultAddress.getVendorLine1Address()); 1669 vendor.setDefaultAddressLine2(defaultAddress.getVendorLine2Address()); 1670 vendor.setDefaultAddressCity(defaultAddress.getVendorCityName()); 1671 vendor.setDefaultAddressPostalCode(defaultAddress.getVendorZipCode()); 1672 vendor.setDefaultAddressStateCode(defaultAddress.getVendorStateCode()); 1673 vendor.setDefaultAddressInternationalProvince(defaultAddress.getVendorAddressInternationalProvinceName()); 1674 vendor.setDefaultAddressCountryCode(defaultAddress.getVendorCountryCode()); 1675 vendor.setDefaultFaxNumber(defaultAddress.getVendorFaxNumber()); 1676 } 1677 } 1678 1679 /** 1680 * 1681 * @see org.kuali.kfs.module.purap.document.service.PurchaseOrderService#processACMReq(org.kuali.kfs.module.purap.document.ContractManagerAssignmentDocument) 1682 */ 1683 public void processACMReq(ContractManagerAssignmentDocument acmDoc) { 1684 List<ContractManagerAssignmentDetail> acmDetails = acmDoc.getContractManagerAssignmentDetails(); 1685 for (Iterator iter = acmDetails.iterator(); iter.hasNext();) { 1686 ContractManagerAssignmentDetail detail = (ContractManagerAssignmentDetail) iter.next(); 1687 1688 if (ObjectUtils.isNotNull(detail.getContractManagerCode())) { 1689 // Get the requisition for this ContractManagerAssignmentDetail. 1690 RequisitionDocument req = SpringContext.getBean(RequisitionService.class).getRequisitionById(detail.getRequisitionIdentifier()); 1691 1692 if (req.getStatusCode().equals(PurapConstants.RequisitionStatuses.AWAIT_CONTRACT_MANAGER_ASSGN)) { 1693 // only update REQ if code is empty and status is correct 1694 purapService.updateStatus(req, PurapConstants.RequisitionStatuses.CLOSED); 1695 purapService.saveDocumentNoValidation(req); 1696 createPurchaseOrderDocument(req, KFSConstants.SYSTEM_USER, detail.getContractManagerCode()); 1697 } 1698 } 1699 1700 }// endfor 1701 } 1702 1703 /** 1704 * 1705 * @see org.kuali.kfs.module.purap.document.service.PurchaseOrderService#autoCloseFullyDisencumberedOrders() 1706 */ 1707 public boolean autoCloseFullyDisencumberedOrders() { 1708 LOG.info("autoCloseFullyDisencumberedOrders() started"); 1709 1710 List<AutoClosePurchaseOrderView> purchaseOrderAutoCloseList = purchaseOrderDao.getAllOpenPurchaseOrders(getExcludedVendorChoiceCodes()); 1711 1712 for (AutoClosePurchaseOrderView poAutoClose : purchaseOrderAutoCloseList) { 1713 if ((poAutoClose.getTotalAmount() != null) && ((KualiDecimal.ZERO.compareTo(poAutoClose.getTotalAmount())) != 0)) { 1714 LOG.info("autoCloseFullyDisencumberedOrders() PO ID " + poAutoClose.getPurapDocumentIdentifier() + " with total " + poAutoClose.getTotalAmount().doubleValue() + " will be closed"); 1715 String newStatus = PurapConstants.PurchaseOrderStatuses.PENDING_CLOSE; 1716 String annotation = "This PO was automatically closed in batch."; 1717 String documentType = PurapConstants.PurchaseOrderDocTypes.PURCHASE_ORDER_CLOSE_DOCUMENT; 1718 PurchaseOrderDocument document = getPurchaseOrderByDocumentNumber(poAutoClose.getDocumentNumber()); 1719 createNoteForAutoCloseOrders(document, annotation); 1720 createAndRoutePotentialChangeDocument(poAutoClose.getDocumentNumber(), documentType, annotation, null, newStatus); 1721 1722 } 1723 } 1724 LOG.info("autoCloseFullyDisencumberedOrders() ended"); 1725 1726 return true; 1727 } 1728 1729 /** 1730 * @see org.kuali.kfs.module.purap.document.service.PurchaseOrderService#autoCloseRecurringOrders() 1731 */ 1732 public boolean autoCloseRecurringOrders() { 1733 LOG.info("autoCloseRecurringOrders() started"); 1734 boolean shouldSendEmail = true; 1735 MailMessage message = new MailMessage(); 1736 String parameterEmail = parameterService.getParameterValue(AutoCloseRecurringOrdersStep.class, PurapParameterConstants.AUTO_CLOSE_RECURRING_PO_TO_EMAIL_ADDRESSES); 1737 1738 if (StringUtils.isEmpty(parameterEmail)) { 1739 // Don't stop the show if the email address is wrong, log it and continue. 1740 LOG.error("autoCloseRecurringOrders(): parameterEmail is missing, we'll not send out any emails for this job."); 1741 shouldSendEmail = false; 1742 } 1743 if (shouldSendEmail) { 1744 message = setMessageAddressesAndSubject(message, parameterEmail); 1745 } 1746 StringBuffer emailBody = new StringBuffer(); 1747 // There should always be a "AUTO_CLOSE_RECURRING_ORDER_DT" 1748 // row in the table, this method sets it to "mm/dd/yyyy" after processing. 1749 String recurringOrderDateString = parameterService.getParameterValue(AutoCloseRecurringOrdersStep.class, PurapParameterConstants.AUTO_CLOSE_RECURRING_PO_DATE); 1750 boolean validDate = true; 1751 java.util.Date recurringOrderDate = null; 1752 try { 1753 recurringOrderDate = dateTimeService.convertToDate(recurringOrderDateString); 1754 } 1755 catch (ParseException pe) { 1756 validDate = false; 1757 } 1758 if (StringUtils.isEmpty(recurringOrderDateString) || recurringOrderDateString.equalsIgnoreCase("mm/dd/yyyy") || (!validDate)) { 1759 if (recurringOrderDateString.equalsIgnoreCase("mm/dd/yyyy")) { 1760 LOG.debug("autoCloseRecurringOrders(): mm/dd/yyyy " + "was found in the Application Settings table. No orders will be closed, method will end."); 1761 if (shouldSendEmail) { 1762 emailBody.append("The AUTO_CLOSE_RECURRING_ORDER_DT found in the Application Settings table " + "was mm/dd/yyyy. No recurring PO's were closed."); 1763 } 1764 } 1765 else { 1766 LOG.debug("autoCloseRecurringOrders(): An invalid autoCloseRecurringOrdersDate " + "was found in the Application Settings table: " + recurringOrderDateString + ". Method will end."); 1767 if (shouldSendEmail) { 1768 emailBody.append("An invalid AUTO_CLOSE_RECURRING_ORDER_DT was found in the Application Settings table: " + recurringOrderDateString + ". No recurring PO's were closed."); 1769 } 1770 } 1771 if (shouldSendEmail) { 1772 sendMessage(message, emailBody.toString()); 1773 } 1774 LOG.info("autoCloseRecurringOrders() ended"); 1775 1776 return false; 1777 } 1778 LOG.info("autoCloseRecurringOrders() The autoCloseRecurringOrdersDate found in the Application Settings table was " + recurringOrderDateString); 1779 if (shouldSendEmail) { 1780 emailBody.append("The autoCloseRecurringOrdersDate found in the Application Settings table was " + recurringOrderDateString + "."); 1781 } 1782 Calendar appSettingsDate = dateTimeService.getCalendar(recurringOrderDate); 1783 Timestamp appSettingsDay = new Timestamp(appSettingsDate.getTime().getTime()); 1784 1785 Calendar todayMinusThreeMonths = getTodayMinusThreeMonths(); 1786 Timestamp threeMonthsAgo = new Timestamp(todayMinusThreeMonths.getTime().getTime()); 1787 1788 if (appSettingsDate.after(todayMinusThreeMonths)) { 1789 LOG.info("autoCloseRecurringOrders() The appSettingsDate: " + appSettingsDay + " is after todayMinusThreeMonths: " + threeMonthsAgo + ". The program will end."); 1790 if (shouldSendEmail) { 1791 emailBody.append("\n\nThe autoCloseRecurringOrdersDate: " + appSettingsDay + " is after todayMinusThreeMonths: " + threeMonthsAgo + ". The program will end."); 1792 sendMessage(message, emailBody.toString()); 1793 } 1794 LOG.info("autoCloseRecurringOrders() ended"); 1795 1796 return false; 1797 } 1798 1799 List<AutoClosePurchaseOrderView> purchaseOrderAutoCloseList = purchaseOrderDao.getAutoCloseRecurringPurchaseOrders(getExcludedVendorChoiceCodes()); 1800 LOG.info("autoCloseRecurringOrders(): " + purchaseOrderAutoCloseList.size() + " PO's were returned for processing."); 1801 int counter = 0; 1802 for (AutoClosePurchaseOrderView poAutoClose : purchaseOrderAutoCloseList) { 1803 LOG.info("autoCloseRecurringOrders(): Testing PO ID " + poAutoClose.getPurapDocumentIdentifier() + ". recurringPaymentEndDate: " + poAutoClose.getRecurringPaymentEndDate()); 1804 if (poAutoClose.getRecurringPaymentEndDate().before(appSettingsDay)) { 1805 String newStatus = PurapConstants.PurchaseOrderStatuses.PENDING_CLOSE; 1806 String annotation = "This recurring PO was automatically closed in batch."; 1807 String documentType = PurapConstants.PurchaseOrderDocTypes.PURCHASE_ORDER_CLOSE_DOCUMENT; 1808 PurchaseOrderDocument document = getPurchaseOrderByDocumentNumber(poAutoClose.getDocumentNumber()); 1809 boolean rulePassed = SpringContext.getBean(KualiRuleService.class).applyRules(new AttributedRouteDocumentEvent("", document)); 1810 1811 boolean success = true; 1812 if (success) { 1813 ++counter; 1814 if (counter == 1) { 1815 emailBody.append("\n\nThe following recurring Purchase Orders will be closed by auto close recurring batch job \n"); 1816 } 1817 LOG.info("autoCloseRecurringOrders() PO ID " + poAutoClose.getPurapDocumentIdentifier() + " will be closed."); 1818 createNoteForAutoCloseOrders(document, annotation); 1819 createAndRoutePotentialChangeDocument(poAutoClose.getDocumentNumber(), documentType, annotation, null, newStatus); 1820 if (shouldSendEmail) { 1821 emailBody.append("\n\n" + counter + " PO ID: " + poAutoClose.getPurapDocumentIdentifier() + ", End Date: " + poAutoClose.getRecurringPaymentEndDate() + ", Status: " + poAutoClose.getPurchaseOrderStatusCode() + ", VendorChoice: " + poAutoClose.getVendorChoiceCode() + ", RecurringPaymentType: " + poAutoClose.getRecurringPaymentTypeCode()); 1822 } 1823 } 1824 else { 1825 //If it was unsuccessful, we have to clear the error map in the GlobalVariables so that the previous 1826 //error would not still be lingering around and the next PO in the list can be validated. 1827 GlobalVariables.getMessageMap().clear(); 1828 } 1829 } 1830 } 1831 if (counter == 0) { 1832 LOG.info("\n\nNo recurring PO's fit the conditions for closing."); 1833 if (shouldSendEmail) { 1834 emailBody.append("\n\nNo recurring PO's fit the conditions for closing."); 1835 } 1836 } 1837 if (shouldSendEmail) { 1838 sendMessage(message, emailBody.toString()); 1839 } 1840 resetAutoCloseRecurringOrderDateParameter(); 1841 LOG.info("autoCloseRecurringOrders() ended"); 1842 1843 return true; 1844 } 1845 1846 /** 1847 * Creates and returns a Calendar object of today minus three months. 1848 * 1849 * @return Calendar object of today minus three months. 1850 */ 1851 protected Calendar getTodayMinusThreeMonths() { 1852 Calendar todayMinusThreeMonths = Calendar.getInstance(); // Set to today. 1853 todayMinusThreeMonths.add(Calendar.MONTH, -3); // Back up 3 months. 1854 todayMinusThreeMonths.set(Calendar.HOUR, 12); 1855 todayMinusThreeMonths.set(Calendar.MINUTE, 0); 1856 todayMinusThreeMonths.set(Calendar.SECOND, 0); 1857 todayMinusThreeMonths.set(Calendar.MILLISECOND, 0); 1858 todayMinusThreeMonths.set(Calendar.AM_PM, Calendar.AM); 1859 return todayMinusThreeMonths; 1860 } 1861 1862 /** 1863 * Sets the to addresses, from address and the subject of the email. 1864 * 1865 * @param message The MailMessage object of the email to be sent. 1866 * @param parameterEmail The String of email addresses with delimiters of ";" 1867 * obtained from the system parameter. 1868 * @return The MailMessage object after the to addresses, from 1869 * address and the subject have been set. 1870 */ 1871 protected MailMessage setMessageAddressesAndSubject(MailMessage message, String parameterEmail) { 1872 String toAddressList[] = parameterEmail.split(";"); 1873 1874 if (toAddressList.length > 0) { 1875 for (int i = 0; i < toAddressList.length; i++) { 1876 if (toAddressList[i] != null) { 1877 message.addToAddress(toAddressList[i].trim()); 1878 } 1879 } 1880 } 1881 1882 message.setFromAddress(toAddressList[0]); 1883 1884 if (kualiConfigurationService.isProductionEnvironment()) { 1885 message.setSubject("Auto Close Recurring Purchase Orders"); 1886 } 1887 else { 1888 message.setSubject(kualiConfigurationService.getPropertyString(KFSConstants.ENVIRONMENT_KEY) + " - Auto Close Recurring Purchase Orders"); 1889 } 1890 return message; 1891 } 1892 1893 /** 1894 * Sends the email by calling the sendMessage method in mailService and log error if exception occurs 1895 * during the attempt to send the message. 1896 * 1897 * @param message The MailMessage object containing information to be sent. 1898 * @param emailBody The String containing the body of the email to be sent. 1899 */ 1900 protected void sendMessage(MailMessage message, String emailBody) { 1901 message.setMessage(emailBody); 1902 try { 1903 mailService.sendMessage(message); 1904 } 1905 catch (Exception e) { 1906 // Don't stop the show if the email has problem, log it and continue. 1907 LOG.error("autoCloseRecurringOrders(): email problem. Message not sent.", e); 1908 } 1909 } 1910 1911 /** 1912 * Resets the AUTO_CLOSE_RECURRING_ORDER_DT system parameter to "mm/dd/yyyy". 1913 * 1914 */ 1915 protected void resetAutoCloseRecurringOrderDateParameter() { 1916 Map<String, String> fieldValues = new HashMap<String, String>(); 1917 fieldValues.put("parameterName", PurapParameterConstants.AUTO_CLOSE_RECURRING_PO_DATE); 1918 1919 Collection result = businessObjectService.findMatching(Parameter.class, fieldValues); 1920 Parameter autoCloseRecurringPODate = (Parameter)result.iterator().next(); 1921 autoCloseRecurringPODate.setParameterValue("mm/dd/yyyy"); 1922 businessObjectService.save(autoCloseRecurringPODate); 1923 } 1924 1925 /** 1926 * Gets a List of excluded vendor choice codes from PurapConstants. 1927 * 1928 * @return a List of excluded vendor choice codes 1929 */ 1930 protected List<String> getExcludedVendorChoiceCodes() { 1931 List<String> excludedVendorChoiceCodes = new ArrayList<String>(); 1932 for (int i = 0; i < PurapConstants.AUTO_CLOSE_EXCLUSION_VNDR_CHOICE_CODES.length; i++) { 1933 String excludedCode = PurapConstants.AUTO_CLOSE_EXCLUSION_VNDR_CHOICE_CODES[i]; 1934 excludedVendorChoiceCodes.add(excludedCode); 1935 } 1936 return excludedVendorChoiceCodes; 1937 } 1938 1939 /** 1940 * Creates and add a note to the purchase order document using the annotation String 1941 * in the input parameter. This method is used by the autoCloseRecurringOrders() and 1942 * autoCloseFullyDisencumberedOrders to add a note to the purchase order to 1943 * indicate that the purchase order was closed by the batch job. 1944 * 1945 * @param purchaseOrderDocument The purchase order document that is being closed by the batch job. 1946 * @param annotation The string to appear on the note to be attached to the purchase order. 1947 */ 1948 protected void createNoteForAutoCloseOrders(PurchaseOrderDocument purchaseOrderDocument, String annotation) { 1949 try { 1950 Note noteObj = documentService.createNoteFromDocument(purchaseOrderDocument, annotation); 1951 documentService.addNoteToDocument(purchaseOrderDocument, noteObj); 1952 noteService.save(noteObj); 1953 } 1954 catch(Exception e){ 1955 String errorMessage = "Error creating and saving close note for purchase order with document service"; 1956 LOG.error("createNoteForAutoCloseRecurringOrders " + errorMessage, e); 1957 throw new RuntimeException(errorMessage, e); 1958 } 1959 } 1960 1961 /** 1962 * @see org.kuali.kfs.module.purap.document.service.PurchaseOrderService#retrieveCapitalAssetItemsForIndividual(java.lang.Integer) 1963 */ 1964 public List<PurchasingCapitalAssetItem> retrieveCapitalAssetItemsForIndividual(Integer poId) { 1965 PurchaseOrderDocument po = getCurrentPurchaseOrder(poId); 1966 if (ObjectUtils.isNotNull(po)) { 1967 return po.getPurchasingCapitalAssetItems(); 1968 } 1969 return null; 1970 } 1971 1972 /** 1973 * @see org.kuali.kfs.module.purap.document.service.PurchaseOrderService#retrieveCapitalAssetSystemForOneSystem(java.lang.Integer) 1974 */ 1975 public CapitalAssetSystem retrieveCapitalAssetSystemForOneSystem(Integer poId) { 1976 PurchaseOrderDocument po = getCurrentPurchaseOrder(poId); 1977 if (ObjectUtils.isNotNull(po)) { 1978 List<CapitalAssetSystem> systems = po.getPurchasingCapitalAssetSystems(); 1979 if (ObjectUtils.isNotNull(systems)) { 1980 //for one system, there should only ever be one system 1981 return systems.get(0); 1982 } 1983 } 1984 return null; 1985 } 1986 1987 /** 1988 * @see org.kuali.kfs.module.purap.document.service.PurchaseOrderService#retrieveCapitalAssetSystemsForMultipleSystem(java.lang.Integer) 1989 */ 1990 public List<CapitalAssetSystem> retrieveCapitalAssetSystemsForMultipleSystem(Integer poId) { 1991 PurchaseOrderDocument po = getCurrentPurchaseOrder(poId); 1992 if (ObjectUtils.isNotNull(po)) { 1993 return po.getPurchasingCapitalAssetSystems(); 1994 } 1995 return null; 1996 } 1997 1998 /** 1999 * This method fixes the item references in this document 2000 */ 2001 protected void fixItemReferences(PurchaseOrderDocument po) { 2002 //fix item and account references in case this is a new doc (since they will be lost) 2003 for (PurApItem item : (List<PurApItem>)po.getItems()) { 2004 item.setPurapDocument(po); 2005 item.fixAccountReferences(); 2006 } 2007 } 2008 2009 public List getPendingPurchaseOrderFaxes() { 2010 return purchaseOrderDao.getPendingPurchaseOrdersForFaxing(); 2011 } 2012 2013 /** 2014 * @return Returns the personService. 2015 */ 2016 protected PersonService<Person> getPersonService() { 2017 if(personService==null) 2018 personService = SpringContext.getBean(PersonService.class); 2019 return personService; 2020 } 2021 2022 public void setDataDictionaryService(DataDictionaryService dataDictionaryService) { 2023 this.dataDictionaryService = dataDictionaryService; 2024 } 2025 2026 }