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