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.math.BigDecimal; 019 import java.sql.Timestamp; 020 import java.text.MessageFormat; 021 import java.util.ArrayList; 022 import java.util.HashMap; 023 import java.util.List; 024 import java.util.Map; 025 026 import org.apache.commons.lang.StringUtils; 027 import org.kuali.kfs.module.purap.PurapConstants; 028 import org.kuali.kfs.module.purap.PurapKeyConstants; 029 import org.kuali.kfs.module.purap.PurapConstants.PurchaseOrderDocTypes; 030 import org.kuali.kfs.module.purap.PurapConstants.PurchaseOrderStatuses; 031 import org.kuali.kfs.module.purap.businessobject.CorrectionReceivingItem; 032 import org.kuali.kfs.module.purap.businessobject.ItemType; 033 import org.kuali.kfs.module.purap.businessobject.LineItemReceivingItem; 034 import org.kuali.kfs.module.purap.businessobject.LineItemReceivingView; 035 import org.kuali.kfs.module.purap.businessobject.PurApAccountingLine; 036 import org.kuali.kfs.module.purap.businessobject.PurchaseOrderItem; 037 import org.kuali.kfs.module.purap.businessobject.ReceivingItem; 038 import org.kuali.kfs.module.purap.document.CorrectionReceivingDocument; 039 import org.kuali.kfs.module.purap.document.LineItemReceivingDocument; 040 import org.kuali.kfs.module.purap.document.PurchaseOrderAmendmentDocument; 041 import org.kuali.kfs.module.purap.document.PurchaseOrderDocument; 042 import org.kuali.kfs.module.purap.document.ReceivingDocument; 043 import org.kuali.kfs.module.purap.document.dataaccess.ReceivingDao; 044 import org.kuali.kfs.module.purap.document.service.LogicContainer; 045 import org.kuali.kfs.module.purap.document.service.PurapService; 046 import org.kuali.kfs.module.purap.document.service.PurchaseOrderService; 047 import org.kuali.kfs.module.purap.document.service.ReceivingService; 048 import org.kuali.kfs.module.purap.document.validation.event.AttributedContinuePurapEvent; 049 import org.kuali.kfs.sys.KFSConstants; 050 import org.kuali.kfs.sys.context.SpringContext; 051 import org.kuali.rice.kew.exception.WorkflowException; 052 import org.kuali.rice.kns.bo.AdHocRoutePerson; 053 import org.kuali.rice.kns.bo.Note; 054 import org.kuali.rice.kns.exception.InfrastructureException; 055 import org.kuali.rice.kns.service.DocumentService; 056 import org.kuali.rice.kns.service.KualiConfigurationService; 057 import org.kuali.rice.kns.service.NoteService; 058 import org.kuali.rice.kns.util.GlobalVariables; 059 import org.kuali.rice.kns.util.KualiDecimal; 060 import org.kuali.rice.kns.util.ObjectUtils; 061 import org.kuali.rice.kns.workflow.service.KualiWorkflowDocument; 062 import org.kuali.rice.kns.workflow.service.WorkflowDocumentService; 063 import org.springframework.transaction.annotation.Transactional; 064 065 @Transactional 066 public class ReceivingServiceImpl implements ReceivingService { 067 private static org.apache.log4j.Logger LOG = org.apache.log4j.Logger.getLogger(ReceivingServiceImpl.class); 068 069 private PurchaseOrderService purchaseOrderService; 070 private ReceivingDao receivingDao; 071 private DocumentService documentService; 072 private WorkflowDocumentService workflowDocumentService; 073 private KualiConfigurationService configurationService; 074 private PurapService purapService; 075 private NoteService noteService; 076 077 public void setPurchaseOrderService(PurchaseOrderService purchaseOrderService) { 078 this.purchaseOrderService = purchaseOrderService; 079 } 080 081 public void setReceivingDao(ReceivingDao receivingDao) { 082 this.receivingDao = receivingDao; 083 } 084 085 public void setDocumentService(DocumentService documentService){ 086 this.documentService = documentService; 087 } 088 089 public void setWorkflowDocumentService(WorkflowDocumentService workflowDocumentService){ 090 this.workflowDocumentService = workflowDocumentService; 091 } 092 093 public void setConfigurationService(KualiConfigurationService configurationService) { 094 this.configurationService = configurationService; 095 } 096 097 public void setPurapService(PurapService purapService) { 098 this.purapService = purapService; 099 } 100 101 public void setNoteService(NoteService noteService) { 102 this.noteService = noteService; 103 } 104 105 /** 106 * 107 * @see org.kuali.kfs.module.purap.document.service.ReceivingService#populateReceivingLineFromPurchaseOrder(org.kuali.kfs.module.purap.document.LineItemReceivingDocument) 108 */ 109 public void populateReceivingLineFromPurchaseOrder(LineItemReceivingDocument rlDoc) { 110 111 if(rlDoc == null){ 112 rlDoc = new LineItemReceivingDocument(); 113 } 114 115 //retrieve po by doc id 116 PurchaseOrderDocument poDoc = null; 117 poDoc = purchaseOrderService.getCurrentPurchaseOrder(rlDoc.getPurchaseOrderIdentifier()); 118 119 if(poDoc != null){ 120 rlDoc.populateReceivingLineFromPurchaseOrder(poDoc); 121 } 122 123 } 124 125 public void populateCorrectionReceivingFromReceivingLine(CorrectionReceivingDocument rcDoc) { 126 127 if(rcDoc == null){ 128 rcDoc = new CorrectionReceivingDocument(); 129 } 130 131 //retrieve receiving line by doc id 132 LineItemReceivingDocument rlDoc = rcDoc.getLineItemReceivingDocument(); 133 134 if(rlDoc != null){ 135 rcDoc.populateCorrectionReceivingFromReceivingLine(rlDoc); 136 } 137 138 } 139 140 /** 141 * 142 * @see org.kuali.kfs.module.purap.document.service.ReceivingService#populateAndSaveLineItemReceivingDocument(org.kuali.kfs.module.purap.document.LineItemReceivingDocument) 143 */ 144 public void populateAndSaveLineItemReceivingDocument(LineItemReceivingDocument rlDoc) throws WorkflowException { 145 try { 146 documentService.saveDocument(rlDoc, AttributedContinuePurapEvent.class); 147 } 148 catch (WorkflowException we) { 149 String errorMsg = "Error saving document # " + rlDoc.getDocumentHeader().getDocumentNumber() + " " + we.getMessage(); 150 //LOG.error(errorMsg, we); 151 throw new RuntimeException(errorMsg, we); 152 } 153 } 154 155 /** 156 * @see org.kuali.kfs.module.purap.document.service.ReceivingService#populateCorrectionReceivingDocument(org.kuali.kfs.module.purap.document.CorrectionReceivingDocument) 157 */ 158 public void populateCorrectionReceivingDocument(CorrectionReceivingDocument rcDoc) { 159 populateCorrectionReceivingFromReceivingLine(rcDoc); 160 } 161 162 /** 163 * 164 * @see org.kuali.kfs.module.purap.document.service.ReceivingService#canCreateLineItemReceivingDocument(java.lang.Integer, java.lang.String) 165 */ 166 public boolean canCreateLineItemReceivingDocument(Integer poId, String receivingDocumentNumber) throws RuntimeException { 167 168 PurchaseOrderDocument po = purchaseOrderService.getCurrentPurchaseOrder(poId); 169 170 return canCreateLineItemReceivingDocument(po, receivingDocumentNumber); 171 } 172 173 /** 174 * 175 * @see org.kuali.kfs.module.purap.document.service.ReceivingService#canCreateLineItemReceivingDocument(org.kuali.kfs.module.purap.document.PurchaseOrderDocument) 176 */ 177 public boolean canCreateLineItemReceivingDocument(PurchaseOrderDocument po) throws RuntimeException { 178 return canCreateLineItemReceivingDocument(po, null); 179 } 180 181 protected boolean canCreateLineItemReceivingDocument(PurchaseOrderDocument po, String receivingDocumentNumber) { 182 boolean canCreate = false; 183 184 if (isPurchaseOrderValidForLineItemReceivingDocumentCreation(po) && 185 !isLineItemReceivingDocumentInProcessForPurchaseOrder(po.getPurapDocumentIdentifier(), receivingDocumentNumber) && 186 !isCorrectionReceivingDocumentInProcessForPurchaseOrder(po.getPurapDocumentIdentifier(), null)) { 187 canCreate = true; 188 } 189 190 return canCreate; 191 } 192 193 public boolean isPurchaseOrderActiveForLineItemReceivingDocumentCreation(Integer poId){ 194 PurchaseOrderDocument po = purchaseOrderService.getCurrentPurchaseOrder(poId); 195 return isPurchaseOrderValidForLineItemReceivingDocumentCreation(po); 196 } 197 198 protected boolean isPurchaseOrderValidForLineItemReceivingDocumentCreation(PurchaseOrderDocument po){ 199 return po != null && 200 ObjectUtils.isNotNull(po.getPurapDocumentIdentifier()) && 201 po.isPurchaseOrderCurrentIndicator() && 202 (PurchaseOrderStatuses.OPEN.equals(po.getStatusCode()) || 203 PurchaseOrderStatuses.CLOSED.equals(po.getStatusCode()) || 204 PurchaseOrderStatuses.PAYMENT_HOLD.equals(po.getStatusCode())); 205 } 206 207 public boolean canCreateCorrectionReceivingDocument(LineItemReceivingDocument rl) throws RuntimeException { 208 return canCreateCorrectionReceivingDocument(rl, null); 209 } 210 211 public boolean canCreateCorrectionReceivingDocument(LineItemReceivingDocument rl, String receivingCorrectionDocNumber) throws RuntimeException { 212 213 boolean canCreate = false; 214 KualiWorkflowDocument workflowDocument = null; 215 216 try{ 217 workflowDocument = workflowDocumentService.createWorkflowDocument(Long.valueOf(rl.getDocumentNumber()), GlobalVariables.getUserSession().getPerson()); 218 }catch(WorkflowException we){ 219 throw new RuntimeException(we); 220 } 221 222 if( workflowDocument.stateIsFinal() && 223 !isCorrectionReceivingDocumentInProcessForReceivingLine(rl.getDocumentNumber(), receivingCorrectionDocNumber)){ 224 canCreate = true; 225 } 226 227 return canCreate; 228 } 229 230 protected boolean isLineItemReceivingDocumentInProcessForPurchaseOrder(Integer poId, String receivingDocumentNumber) throws RuntimeException{ 231 return !getLineItemReceivingDocumentNumbersInProcessForPurchaseOrder(poId, receivingDocumentNumber).isEmpty(); 232 } 233 234 public List<String> getLineItemReceivingDocumentNumbersInProcessForPurchaseOrder(Integer poId, 235 String receivingDocumentNumber){ 236 237 List<String> inProcessDocNumbers = new ArrayList<String>(); 238 List<String> docNumbers = receivingDao.getDocumentNumbersByPurchaseOrderId(poId); 239 KualiWorkflowDocument workflowDocument = null; 240 241 for (String docNumber : docNumbers) { 242 243 try{ 244 workflowDocument = workflowDocumentService.createWorkflowDocument(Long.valueOf(docNumber), GlobalVariables.getUserSession().getPerson()); 245 }catch(WorkflowException we){ 246 throw new RuntimeException(we); 247 } 248 249 if(!(workflowDocument.stateIsCanceled() || 250 workflowDocument.stateIsException() || 251 workflowDocument.stateIsFinal()) && 252 docNumber.equals(receivingDocumentNumber) == false ){ 253 inProcessDocNumbers.add(docNumber); 254 } 255 } 256 257 return inProcessDocNumbers; 258 } 259 260 public List<LineItemReceivingDocument> getLineItemReceivingDocumentsInFinalForPurchaseOrder(Integer poId) { 261 262 List<String> finalDocNumbers = new ArrayList<String>(); 263 List<String> docNumbers = receivingDao.getDocumentNumbersByPurchaseOrderId(poId); 264 KualiWorkflowDocument workflowDocument = null; 265 266 for (String docNumber : docNumbers) { 267 268 try { 269 workflowDocument = workflowDocumentService.createWorkflowDocument(Long.valueOf(docNumber), GlobalVariables.getUserSession().getPerson()); 270 } 271 catch (WorkflowException we) { 272 throw new RuntimeException(we); 273 } 274 275 if (workflowDocument.stateIsFinal()) { 276 finalDocNumbers.add(docNumber); 277 } 278 } 279 280 if (finalDocNumbers.size() > 0) { 281 try { 282 return SpringContext.getBean(DocumentService.class).getDocumentsByListOfDocumentHeaderIds(LineItemReceivingDocument.class, finalDocNumbers); 283 } 284 catch (WorkflowException e) { 285 throw new InfrastructureException("unable to retrieve LineItemReceivingDocuments", e); 286 } 287 } 288 else { 289 return null; 290 } 291 292 } 293 294 protected boolean isCorrectionReceivingDocumentInProcessForPurchaseOrder(Integer poId, String receivingDocumentNumber) throws RuntimeException{ 295 return !getCorrectionReceivingDocumentNumbersInProcessForPurchaseOrder(poId, receivingDocumentNumber).isEmpty(); 296 } 297 298 public List<String> getCorrectionReceivingDocumentNumbersInProcessForPurchaseOrder(Integer poId, 299 String receivingDocumentNumber){ 300 301 boolean isInProcess = false; 302 303 List<String> inProcessDocNumbers = new ArrayList<String>(); 304 List<String> docNumbers = receivingDao.getCorrectionReceivingDocumentNumbersByPurchaseOrderId(poId); 305 KualiWorkflowDocument workflowDocument = null; 306 307 for (String docNumber : docNumbers) { 308 309 try{ 310 workflowDocument = workflowDocumentService.createWorkflowDocument(Long.valueOf(docNumber), GlobalVariables.getUserSession().getPerson()); 311 }catch(WorkflowException we){ 312 throw new RuntimeException(we); 313 } 314 315 if(!(workflowDocument.stateIsCanceled() || 316 workflowDocument.stateIsException() || 317 workflowDocument.stateIsFinal()) && 318 docNumber.equals(receivingDocumentNumber) == false ){ 319 inProcessDocNumbers.add(docNumber); 320 } 321 } 322 323 return inProcessDocNumbers; 324 } 325 326 327 protected boolean isCorrectionReceivingDocumentInProcessForReceivingLine(String receivingDocumentNumber, String receivingCorrectionDocNumber) throws RuntimeException{ 328 329 boolean isInProcess = false; 330 331 List<String> docNumbers = receivingDao.getCorrectionReceivingDocumentNumbersByReceivingLineNumber(receivingDocumentNumber); 332 KualiWorkflowDocument workflowDocument = null; 333 334 for (String docNumber : docNumbers) { 335 336 try{ 337 workflowDocument = workflowDocumentService.createWorkflowDocument(Long.valueOf(docNumber), GlobalVariables.getUserSession().getPerson()); 338 }catch(WorkflowException we){ 339 throw new RuntimeException(we); 340 } 341 342 if(!(workflowDocument.stateIsCanceled() || 343 workflowDocument.stateIsException() || 344 workflowDocument.stateIsFinal()) && 345 docNumber.equals(receivingCorrectionDocNumber) == false ){ 346 347 isInProcess = true; 348 break; 349 } 350 } 351 352 return isInProcess; 353 } 354 355 /** 356 * 357 * @see org.kuali.kfs.module.purap.document.service.ReceivingService#receivingLineDuplicateMessages(org.kuali.kfs.module.purap.document.LineItemReceivingDocument) 358 */ 359 public HashMap<String, String> receivingLineDuplicateMessages(LineItemReceivingDocument rlDoc) { 360 HashMap<String, String> msgs; 361 msgs = new HashMap<String, String>(); 362 Integer poId = rlDoc.getPurchaseOrderIdentifier(); 363 StringBuffer currentMessage = new StringBuffer(""); 364 List<String> docNumbers = null; 365 366 //check vendor date for duplicates 367 if( rlDoc.getShipmentReceivedDate() != null ){ 368 docNumbers = receivingDao.duplicateVendorDate(poId, rlDoc.getShipmentReceivedDate()); 369 if( hasDuplicateEntry(docNumbers) ){ 370 appendDuplicateMessage(currentMessage, PurapKeyConstants.MESSAGE_DUPLICATE_RECEIVING_LINE_VENDOR_DATE, rlDoc.getPurchaseOrderIdentifier()); 371 } 372 } 373 374 //check packing slip number for duplicates 375 if( !StringUtils.isEmpty(rlDoc.getShipmentPackingSlipNumber()) ){ 376 docNumbers = receivingDao.duplicatePackingSlipNumber(poId, rlDoc.getShipmentPackingSlipNumber()); 377 if( hasDuplicateEntry(docNumbers) ){ 378 appendDuplicateMessage(currentMessage, PurapKeyConstants.MESSAGE_DUPLICATE_RECEIVING_LINE_PACKING_SLIP_NUMBER, rlDoc.getPurchaseOrderIdentifier()); 379 } 380 } 381 382 //check bill of lading number for duplicates 383 if( !StringUtils.isEmpty(rlDoc.getShipmentBillOfLadingNumber()) ){ 384 docNumbers = receivingDao.duplicateBillOfLadingNumber(poId, rlDoc.getShipmentBillOfLadingNumber()); 385 if( hasDuplicateEntry(docNumbers) ){ 386 appendDuplicateMessage(currentMessage, PurapKeyConstants.MESSAGE_DUPLICATE_RECEIVING_LINE_BILL_OF_LADING_NUMBER, rlDoc.getPurchaseOrderIdentifier()); 387 } 388 } 389 390 //add message if one exists 391 if(currentMessage.length() > 0){ 392 //add suffix 393 appendDuplicateMessage(currentMessage, PurapKeyConstants.MESSAGE_DUPLICATE_RECEIVING_LINE_SUFFIX, rlDoc.getPurchaseOrderIdentifier() ); 394 395 //add msg to map 396 msgs.put(PurapConstants.LineItemReceivingDocumentStrings.DUPLICATE_RECEIVING_LINE_QUESTION, currentMessage.toString()); 397 } 398 399 return msgs; 400 } 401 402 /** 403 * Looks at a list of doc numbers, but only considers an entry duplicate 404 * if the document is in a Final status. 405 * 406 * @param docNumbers 407 * @return 408 */ 409 protected boolean hasDuplicateEntry(List<String> docNumbers){ 410 411 boolean isDuplicate = false; 412 KualiWorkflowDocument workflowDocument = null; 413 414 for (String docNumber : docNumbers) { 415 416 try{ 417 workflowDocument = workflowDocumentService.createWorkflowDocument(Long.valueOf(docNumber), GlobalVariables.getUserSession().getPerson()); 418 }catch(WorkflowException we){ 419 throw new RuntimeException(we); 420 } 421 422 //if the doc number exists, and is in final status, consider this a dupe and return 423 if(workflowDocument.stateIsFinal()){ 424 isDuplicate = true; 425 break; 426 } 427 } 428 429 return isDuplicate; 430 431 } 432 protected void appendDuplicateMessage(StringBuffer currentMessage, String duplicateMessageKey, Integer poId){ 433 434 //append prefix if this is first call 435 if(currentMessage.length() == 0){ 436 String messageText = configurationService.getPropertyString(PurapKeyConstants.MESSAGE_DUPLICATE_RECEIVING_LINE_PREFIX); 437 String prefix = MessageFormat.format(messageText, poId.toString() ); 438 439 currentMessage.append(prefix); 440 } 441 442 //append message 443 currentMessage.append( configurationService.getPropertyString(duplicateMessageKey) ); 444 } 445 446 public void completeCorrectionReceivingDocument(ReceivingDocument correctionDocument){ 447 448 ReceivingDocument receivingDoc = ((CorrectionReceivingDocument)correctionDocument).getLineItemReceivingDocument(); 449 450 for (CorrectionReceivingItem correctionItem : (List<CorrectionReceivingItem>)correctionDocument.getItems()) { 451 if(!StringUtils.equalsIgnoreCase(correctionItem.getItemType().getItemTypeCode(),PurapConstants.ItemTypeCodes.ITEM_TYPE_UNORDERED_ITEM_CODE)) { 452 453 LineItemReceivingItem recItem = (LineItemReceivingItem) receivingDoc.getItem(correctionItem.getItemLineNumber().intValue() - 1); 454 PurchaseOrderItem poItem = (PurchaseOrderItem) receivingDoc.getPurchaseOrderDocument().getItem(correctionItem.getItemLineNumber().intValue() - 1); 455 456 if(ObjectUtils.isNotNull(recItem)) { 457 recItem.setItemReceivedTotalQuantity(correctionItem.getItemReceivedTotalQuantity()); 458 recItem.setItemReturnedTotalQuantity(correctionItem.getItemReturnedTotalQuantity()); 459 recItem.setItemDamagedTotalQuantity(correctionItem.getItemDamagedTotalQuantity()); 460 } 461 } 462 } 463 464 } 465 466 /** 467 * 468 * This method deletes unneeded items and updates the totals on the po and does any additional processing based on items i.e. FYI etc 469 * @param receivingDocument receiving document 470 */ 471 public void completeReceivingDocument(ReceivingDocument receivingDocument) { 472 473 PurchaseOrderDocument poDoc = null; 474 475 if (receivingDocument instanceof LineItemReceivingDocument){ 476 // delete unentered items 477 purapService.deleteUnenteredItems(receivingDocument); 478 poDoc = receivingDocument.getPurchaseOrderDocument(); 479 }else if (receivingDocument instanceof CorrectionReceivingDocument){ 480 CorrectionReceivingDocument correctionDocument = (CorrectionReceivingDocument)receivingDocument; 481 poDoc = purchaseOrderService.getCurrentPurchaseOrder(correctionDocument.getLineItemReceivingDocument().getPurchaseOrderIdentifier()); 482 } 483 484 updateReceivingTotalsOnPurchaseOrder(receivingDocument, poDoc); 485 486 //TODO: custom doc specific service hook here for correction to do it's receiving doc update 487 488 purapService.saveDocumentNoValidation(poDoc); 489 490 sendFyiForItems(receivingDocument); 491 492 spawnPoAmendmentForUnorderedItems(receivingDocument, poDoc); 493 494 purapService.saveDocumentNoValidation(receivingDocument); 495 } 496 497 public void createNoteForReturnedAndDamagedItems(ReceivingDocument recDoc){ 498 499 for (ReceivingItem item : (List<ReceivingItem>)recDoc.getItems()){ 500 if(!StringUtils.equalsIgnoreCase(item.getItemType().getItemTypeCode(),PurapConstants.ItemTypeCodes.ITEM_TYPE_UNORDERED_ITEM_CODE)) { 501 if (item.getItemReturnedTotalQuantity() != null && item.getItemReturnedTotalQuantity().isGreaterThan(KualiDecimal.ZERO)){ 502 try{ 503 String noteString = SpringContext.getBean(KualiConfigurationService.class).getPropertyString(PurapKeyConstants.MESSAGE_RECEIVING_LINEITEM_RETURN_NOTE_TEXT); 504 noteString = item.getItemReturnedTotalQuantity().intValue() + " " + noteString + " " + item.getItemLineNumber(); 505 addNoteToReceivingDocument(recDoc, noteString); 506 }catch (Exception e){ 507 String errorMsg = "Note Service Exception caught: " + e.getLocalizedMessage(); 508 throw new RuntimeException(errorMsg, e); 509 } 510 } 511 512 if (item.getItemDamagedTotalQuantity() != null && item.getItemDamagedTotalQuantity().isGreaterThan(KualiDecimal.ZERO)){ 513 try{ 514 String noteString = SpringContext.getBean(KualiConfigurationService.class).getPropertyString(PurapKeyConstants.MESSAGE_RECEIVING_LINEITEM_DAMAGE_NOTE_TEXT); 515 noteString = item.getItemDamagedTotalQuantity().intValue() + " " + noteString + " " + item.getItemLineNumber(); 516 addNoteToReceivingDocument(recDoc, noteString); 517 }catch (Exception e){ 518 String errorMsg = "Note Service Exception caught: " + e.getLocalizedMessage(); 519 throw new RuntimeException(errorMsg, e); 520 } 521 } 522 } 523 } 524 } 525 526 protected void updateReceivingTotalsOnPurchaseOrder(ReceivingDocument receivingDocument, PurchaseOrderDocument poDoc) { 527 for (ReceivingItem receivingItem : (List<ReceivingItem>)receivingDocument.getItems()) { 528 ItemType itemType = receivingItem.getItemType(); 529 if(!StringUtils.equalsIgnoreCase(itemType.getItemTypeCode(),PurapConstants.ItemTypeCodes.ITEM_TYPE_UNORDERED_ITEM_CODE)) { 530 //TODO: Chris - this method of getting the line out of po should be turned into a method that can get an item based on a combo or itemType and line 531 PurchaseOrderItem poItem = (PurchaseOrderItem)poDoc.getItemByLineNumber(receivingItem.getItemLineNumber()); 532 533 if(ObjectUtils.isNotNull(poItem)) { 534 535 KualiDecimal poItemReceivedTotal = poItem.getItemReceivedTotalQuantity(); 536 537 KualiDecimal receivingItemReceivedOriginal = receivingItem.getItemOriginalReceivedTotalQuantity(); 538 /** 539 * FIXME: It's coming as null although we set the default value in the LineItemReceivingItem constructor - mpv 540 */ 541 if (ObjectUtils.isNull(receivingItemReceivedOriginal)){ 542 receivingItemReceivedOriginal = KualiDecimal.ZERO; 543 } 544 KualiDecimal receivingItemReceived = receivingItem.getItemReceivedTotalQuantity(); 545 KualiDecimal receivingItemTotalReceivedAdjested = receivingItemReceived.subtract(receivingItemReceivedOriginal); 546 547 if (ObjectUtils.isNull(poItemReceivedTotal)){ 548 poItemReceivedTotal = KualiDecimal.ZERO; 549 } 550 KualiDecimal poItemReceivedTotalAdjusted = poItemReceivedTotal.add(receivingItemTotalReceivedAdjested); 551 552 KualiDecimal receivingItemReturnedOriginal = receivingItem.getItemOriginalReturnedTotalQuantity(); 553 if (ObjectUtils.isNull(receivingItemReturnedOriginal)){ 554 receivingItemReturnedOriginal = KualiDecimal.ZERO; 555 } 556 557 KualiDecimal receivingItemReturned = receivingItem.getItemReturnedTotalQuantity(); 558 if (ObjectUtils.isNull(receivingItemReturned)){ 559 receivingItemReturned = KualiDecimal.ZERO; 560 } 561 562 KualiDecimal receivingItemTotalReturnedAdjusted = receivingItemReturned.subtract(receivingItemReturnedOriginal); 563 564 poItemReceivedTotalAdjusted = poItemReceivedTotalAdjusted.subtract(receivingItemTotalReturnedAdjusted); 565 566 poItem.setItemReceivedTotalQuantity(poItemReceivedTotalAdjusted); 567 568 KualiDecimal poTotalDamaged = poItem.getItemDamagedTotalQuantity(); 569 if (ObjectUtils.isNull(poTotalDamaged)){ 570 poTotalDamaged = KualiDecimal.ZERO; 571 } 572 573 KualiDecimal receivingItemTotalDamagedOriginal = receivingItem.getItemOriginalDamagedTotalQuantity(); 574 if (ObjectUtils.isNull(receivingItemTotalDamagedOriginal)){ 575 receivingItemTotalDamagedOriginal = KualiDecimal.ZERO; 576 } 577 578 KualiDecimal receivingItemTotalDamaged = receivingItem.getItemDamagedTotalQuantity(); 579 if (ObjectUtils.isNull(receivingItemTotalDamaged)){ 580 receivingItemTotalDamaged = KualiDecimal.ZERO; 581 } 582 583 KualiDecimal receivingItemTotalDamagedAdjusted = receivingItemTotalDamaged.subtract(receivingItemTotalDamagedOriginal); 584 585 poItem.setItemDamagedTotalQuantity(poTotalDamaged.add(receivingItemTotalDamagedAdjusted)); 586 587 } 588 } 589 } 590 } 591 592 /** 593 * Spawns PO amendments for new unordered items on a receiving document. 594 * 595 * @param receivingDocument 596 * @param po 597 */ 598 protected void spawnPoAmendmentForUnorderedItems(ReceivingDocument receivingDocument, PurchaseOrderDocument po){ 599 600 //if receiving line document 601 if (receivingDocument instanceof LineItemReceivingDocument) { 602 LineItemReceivingDocument rlDoc = (LineItemReceivingDocument)receivingDocument; 603 604 //if a new item has been added spawn a purchase order amendment 605 if( hasNewUnorderedItem((LineItemReceivingDocument)receivingDocument) ){ 606 String newSessionUserId = KFSConstants.SYSTEM_USER; 607 try { 608 609 LogicContainer logicToRun = new LogicContainer() { 610 public Object runLogic(Object[] objects) throws Exception { 611 LineItemReceivingDocument rlDoc = (LineItemReceivingDocument)objects[0]; 612 String poDocNumber = (String)objects[1]; 613 614 //create a PO amendment 615 PurchaseOrderAmendmentDocument amendmentPo = (PurchaseOrderAmendmentDocument) purchaseOrderService.createAndSavePotentialChangeDocument(poDocNumber, PurchaseOrderDocTypes.PURCHASE_ORDER_AMENDMENT_DOCUMENT, PurchaseOrderStatuses.AMENDMENT); 616 617 //add new lines to amendement 618 addUnorderedItemsToAmendment(amendmentPo, rlDoc); 619 620 //route amendment 621 documentService.routeDocument(amendmentPo, null, null); 622 623 //add note to amendment po document 624 String note = "Purchase Order Amendment " + amendmentPo.getPurapDocumentIdentifier() + " (document id " + amendmentPo.getDocumentNumber() + ") created for new unordered line items due to Receiving (document id " + rlDoc.getDocumentNumber() + ")"; 625 626 Note noteObj = documentService.createNoteFromDocument(amendmentPo, note); 627 documentService.addNoteToDocument(amendmentPo, noteObj); 628 noteService.save(noteObj); 629 630 return null; 631 } 632 }; 633 634 purapService.performLogicWithFakedUserSession(newSessionUserId, logicToRun, new Object[] { rlDoc, po.getDocumentNumber() }); 635 } 636 catch (WorkflowException e) { 637 String errorMsg = "Workflow Exception caught: " + e.getLocalizedMessage(); 638 throw new RuntimeException(errorMsg, e); 639 } 640 catch (Exception e) { 641 throw new RuntimeException(e); 642 } 643 } 644 } 645 } 646 647 /** 648 * Checks the item list for newly added items. 649 * 650 * @param rlDoc 651 * @return 652 */ 653 protected boolean hasNewUnorderedItem(LineItemReceivingDocument rlDoc){ 654 655 boolean itemAdded = false; 656 657 for(LineItemReceivingItem rlItem: (List<LineItemReceivingItem>)rlDoc.getItems()){ 658 if( PurapConstants.ItemTypeCodes.ITEM_TYPE_UNORDERED_ITEM_CODE.equals(rlItem.getItemTypeCode()) && 659 !StringUtils.isEmpty(rlItem.getItemReasonAddedCode()) ){ 660 itemAdded = true; 661 break; 662 } 663 } 664 665 return itemAdded; 666 } 667 668 /** 669 * Adds an unordered item to a po amendment document. 670 * 671 * @param amendment 672 * @param rlDoc 673 */ 674 protected void addUnorderedItemsToAmendment(PurchaseOrderAmendmentDocument amendment, LineItemReceivingDocument rlDoc){ 675 676 PurchaseOrderItem poi = null; 677 678 for(LineItemReceivingItem rlItem: (List<LineItemReceivingItem>)rlDoc.getItems()){ 679 if( PurapConstants.ItemTypeCodes.ITEM_TYPE_UNORDERED_ITEM_CODE.equals(rlItem.getItemTypeCode()) && 680 !StringUtils.isEmpty(rlItem.getItemReasonAddedCode()) ){ 681 682 poi = createPoItemFromReceivingLine(rlItem); 683 poi.setDocumentNumber( amendment.getDocumentNumber() ); 684 poi.refreshNonUpdateableReferences(); 685 amendment.addItem(poi); 686 } 687 } 688 689 } 690 691 /** 692 * Creates a PO item from a receiving line item. 693 * 694 * @param rlItem 695 * @return 696 */ 697 protected PurchaseOrderItem createPoItemFromReceivingLine(LineItemReceivingItem rlItem){ 698 699 PurchaseOrderItem poi = new PurchaseOrderItem(); 700 701 poi.setItemActiveIndicator(true); 702 poi.setItemTypeCode(rlItem.getItemTypeCode()); 703 poi.setItemLineNumber(rlItem.getItemLineNumber()); 704 poi.setItemCatalogNumber( rlItem.getItemCatalogNumber() ); 705 poi.setItemDescription( rlItem.getItemDescription() ); 706 707 if( rlItem.getItemReturnedTotalQuantity() == null){ 708 poi.setItemQuantity( rlItem.getItemReceivedTotalQuantity()); 709 }else{ 710 poi.setItemQuantity( rlItem.getItemReceivedTotalQuantity().subtract(rlItem.getItemReturnedTotalQuantity()) ); 711 } 712 713 poi.setItemUnitOfMeasureCode( rlItem.getItemUnitOfMeasureCode() ); 714 poi.setItemUnitPrice(new BigDecimal(0)); 715 716 poi.setItemDamagedTotalQuantity( rlItem.getItemDamagedTotalQuantity() ); 717 poi.setItemReceivedTotalQuantity( rlItem.getItemReceivedTotalQuantity() ); 718 719 return poi; 720 } 721 722 /** 723 * Creates a list of fiscal officers for new unordered items added to a purchase order. 724 * 725 * @param po 726 * @return 727 */ 728 protected List<AdHocRoutePerson> createFyiFiscalOfficerList(ReceivingDocument recDoc){ 729 730 PurchaseOrderDocument po = recDoc.getPurchaseOrderDocument(); 731 List<AdHocRoutePerson> adHocRoutePersons = new ArrayList<AdHocRoutePerson>(); 732 Map fiscalOfficers = new HashMap(); 733 AdHocRoutePerson adHocRoutePerson = null; 734 735 for(ReceivingItem recItem: (List<ReceivingItem>)recDoc.getItems()){ 736 //if this item has an item line number then it is coming from the po 737 if (ObjectUtils.isNotNull(recItem.getItemLineNumber())) { 738 PurchaseOrderItem poItem = (PurchaseOrderItem)po.getItemByLineNumber(recItem.getItemLineNumber()); 739 740 if(poItem.getItemQuantity().isLessThan(poItem.getItemReceivedTotalQuantity())|| 741 recItem.getItemDamagedTotalQuantity().isGreaterThan(KualiDecimal.ZERO)) { 742 743 // loop through accounts and pull off fiscal officer 744 for(PurApAccountingLine account : poItem.getSourceAccountingLines()){ 745 746 //check for dupes of fiscal officer 747 if( fiscalOfficers.containsKey(account.getAccount().getAccountFiscalOfficerUser().getPrincipalName()) == false ){ 748 749 //add fiscal officer to list 750 fiscalOfficers.put(account.getAccount().getAccountFiscalOfficerUser().getPrincipalName(), account.getAccount().getAccountFiscalOfficerUser().getPrincipalName()); 751 752 //create AdHocRoutePerson object and add to list 753 adHocRoutePerson = new AdHocRoutePerson(); 754 adHocRoutePerson.setId(account.getAccount().getAccountFiscalOfficerUser().getPrincipalName()); 755 adHocRoutePerson.setActionRequested(KFSConstants.WORKFLOW_FYI_REQUEST); 756 adHocRoutePersons.add(adHocRoutePerson); 757 } 758 } 759 760 } 761 762 } 763 } 764 765 return adHocRoutePersons; 766 } 767 /** 768 * Sends an FYI to fiscal officers for new unordered items. 769 * 770 * @param po 771 */ 772 protected void sendFyiForItems(ReceivingDocument recDoc){ 773 774 List<AdHocRoutePerson> fyiList = createFyiFiscalOfficerList(recDoc); 775 String annotation = "Notification of Item exceeded Quantity or Damaged" + "(document id " + recDoc.getDocumentNumber() + ")"; 776 String responsibilityNote = "Please Review"; 777 778 for(AdHocRoutePerson adHocPerson: fyiList){ 779 try{ 780 recDoc.appSpecificRouteDocumentToUser( 781 recDoc.getDocumentHeader().getWorkflowDocument(), 782 adHocPerson.getId(), 783 annotation, 784 responsibilityNote); 785 }catch (WorkflowException e) { 786 throw new RuntimeException("Error routing fyi for document with id " + recDoc.getDocumentNumber(), e); 787 } 788 789 } 790 } 791 792 public void addNoteToReceivingDocument(ReceivingDocument receivingDocument, String note) throws Exception{ 793 Note noteObj = documentService.createNoteFromDocument(receivingDocument, note); 794 documentService.addNoteToDocument(receivingDocument, noteObj); 795 noteService.save(noteObj); 796 } 797 798 public String getReceivingDeliveryCampusCode(PurchaseOrderDocument po){ 799 String deliveryCampusCode = ""; 800 String latestDocumentNumber = ""; 801 802 List<LineItemReceivingView> rViews = null; 803 KualiWorkflowDocument workflowDocument = null; 804 Timestamp latestCreateDate = null; 805 806 //get related views 807 if(ObjectUtils.isNotNull(po.getRelatedViews()) ){ 808 rViews = po.getRelatedViews().getRelatedLineItemReceivingViews(); 809 } 810 811 //if not empty, then grab the latest receiving view 812 if(ObjectUtils.isNotNull(rViews) && rViews.isEmpty() == false){ 813 814 for(LineItemReceivingView rView : rViews){ 815 try{ 816 workflowDocument = workflowDocumentService.createWorkflowDocument(Long.valueOf(rView.getDocumentNumber()), GlobalVariables.getUserSession().getPerson()); 817 818 //if latest create date is null or the latest is before the current, current is newer 819 if( ObjectUtils.isNull(latestCreateDate) || latestCreateDate.before(workflowDocument.getCreateDate()) ){ 820 latestCreateDate = workflowDocument.getCreateDate(); 821 latestDocumentNumber = workflowDocument.getRouteHeaderId().toString(); 822 } 823 }catch(WorkflowException we){ 824 throw new RuntimeException(we); 825 } 826 } 827 828 //if there is a create date, a latest workflow doc was found 829 if( ObjectUtils.isNotNull(latestCreateDate)){ 830 try{ 831 LineItemReceivingDocument rlDoc = (LineItemReceivingDocument)documentService.getByDocumentHeaderId(latestDocumentNumber); 832 deliveryCampusCode = rlDoc.getDeliveryCampusCode(); 833 }catch(WorkflowException we){ 834 throw new RuntimeException(we); 835 } 836 } 837 } 838 839 return deliveryCampusCode; 840 } 841 842 /** 843 * @see org.kuali.kfs.module.purap.document.service.ReceivingService#isLineItemReceivingDocumentGeneratedForPurchaseOrder(java.lang.Integer) 844 */ 845 public boolean isLineItemReceivingDocumentGeneratedForPurchaseOrder(Integer poId) throws RuntimeException{ 846 847 boolean isGenerated = false; 848 849 List<String> docNumbers = receivingDao.getDocumentNumbersByPurchaseOrderId(poId); 850 KualiWorkflowDocument workflowDocument = null; 851 852 for (String docNumber : docNumbers) { 853 854 try{ 855 workflowDocument = workflowDocumentService.createWorkflowDocument(Long.valueOf(docNumber), GlobalVariables.getUserSession().getPerson()); 856 }catch(WorkflowException we){ 857 throw new RuntimeException(we); 858 } 859 860 if(workflowDocument.stateIsFinal()){ 861 isGenerated = true; 862 break; 863 } 864 } 865 866 return isGenerated; 867 } 868 869 public void approveReceivingDocsForPOAmendment(){ 870 List<LineItemReceivingDocument> docs = receivingDao.getReceivingDocumentsForPOAmendment(); 871 if (docs != null){ 872 for (LineItemReceivingDocument receivingDoc: docs) { 873 if (StringUtils.equals(receivingDoc.getDocumentHeader().getWorkflowDocument().getRouteHeader().getCurrentRouteNodeNames(), 874 PurapConstants.LineItemReceivingDocumentStrings.AWAITING_PO_OPEN_STATUS)){ 875 approveReceivingDoc(receivingDoc); 876 } 877 } 878 } 879 880 } 881 882 protected void approveReceivingDoc(LineItemReceivingDocument receivingDoc){ 883 PurchaseOrderDocument poDoc = receivingDoc.getPurchaseOrderDocument(); 884 if (purchaseOrderService.canAmendPurchaseOrder(poDoc)){ 885 try{ 886 SpringContext.getBean(DocumentService.class).approveDocument(receivingDoc, "Approved by the batch job", null); 887 } 888 catch (WorkflowException e) { 889 LOG.error("approveReceivingDoc() Error approving receiving document from awaiting PO open", e); 890 throw new RuntimeException("Error approving receiving document from awaiting PO open", e); 891 } 892 } 893 } 894 } 895