1   
2   
3   
4   
5   
6   
7   
8   
9   
10  
11  
12  
13  
14  
15  
16  package org.kuali.ole.module.purap.document.service.impl;
17  
18  import org.apache.commons.lang.StringUtils;
19  import org.kuali.ole.module.purap.*;
20  import org.kuali.ole.module.purap.PurapConstants.PurchaseOrderStatuses;
21  import org.kuali.ole.module.purap.PurapParameterConstants.TaxParameters;
22  import org.kuali.ole.module.purap.businessobject.*;
23  import org.kuali.ole.module.purap.document.*;
24  import org.kuali.ole.module.purap.document.service.LogicContainer;
25  import org.kuali.ole.module.purap.document.service.PurapService;
26  import org.kuali.ole.module.purap.document.service.PurchaseOrderService;
27  import org.kuali.ole.module.purap.service.PurapAccountingService;
28  import org.kuali.ole.module.purap.util.PurApItemUtils;
29  import org.kuali.ole.select.businessobject.OleInvoiceItem;
30  import org.kuali.ole.select.document.OleInvoiceDocument;
31  import org.kuali.ole.sys.OLEConstants;
32  import org.kuali.ole.sys.OLEPropertyConstants;
33  import org.kuali.ole.sys.businessobject.SourceAccountingLine;
34  import org.kuali.ole.sys.businessobject.TaxDetail;
35  import org.kuali.ole.sys.context.SpringContext;
36  import org.kuali.ole.sys.document.validation.event.DocumentSystemSaveEvent;
37  import org.kuali.ole.sys.service.NonTransactional;
38  import org.kuali.ole.sys.service.TaxService;
39  import org.kuali.ole.sys.service.UniversityDateService;
40  import org.kuali.ole.sys.service.impl.OleParameterConstants;
41  import org.kuali.ole.vnd.businessobject.CommodityCode;
42  import org.kuali.ole.vnd.document.service.VendorService;
43  import org.kuali.rice.core.api.datetime.DateTimeService;
44  import org.kuali.rice.core.api.parameter.ParameterEvaluator;
45  import org.kuali.rice.core.api.parameter.ParameterEvaluatorService;
46  import org.kuali.rice.core.api.util.type.KualiDecimal;
47  import org.kuali.rice.coreservice.framework.parameter.ParameterService;
48  import org.kuali.rice.kew.api.KewApiServiceLocator;
49  import org.kuali.rice.kew.api.WorkflowDocument;
50  import org.kuali.rice.kew.api.document.attribute.DocumentAttributeIndexingQueue;
51  import org.kuali.rice.kew.api.exception.WorkflowException;
52  import org.kuali.rice.kns.service.DataDictionaryService;
53  import org.kuali.rice.kns.util.KNSGlobalVariables;
54  import org.kuali.rice.krad.UserSession;
55  import org.kuali.rice.krad.bo.Note;
56  import org.kuali.rice.krad.document.Document;
57  import org.kuali.rice.krad.service.BusinessObjectService;
58  import org.kuali.rice.krad.service.DocumentService;
59  import org.kuali.rice.krad.service.NoteService;
60  import org.kuali.rice.krad.service.PersistenceService;
61  import org.kuali.rice.krad.util.GlobalVariables;
62  import org.kuali.rice.krad.util.KRADPropertyConstants;
63  import org.kuali.rice.krad.util.ObjectUtils;
64  import org.springframework.transaction.annotation.Transactional;
65  
66  import java.math.BigDecimal;
67  import java.sql.Date;
68  import java.sql.Timestamp;
69  import java.util.*;
70  
71  @NonTransactional
72  public class PurapServiceImpl implements PurapService {
73      private static org.apache.log4j.Logger LOG = org.apache.log4j.Logger.getLogger(PurapServiceImpl.class);
74  
75      private BusinessObjectService businessObjectService;
76      private DataDictionaryService dataDictionaryService;
77      private DateTimeService dateTimeService;
78      private DocumentService documentService;
79      private NoteService noteService;
80      private ParameterService parameterService;
81      private PersistenceService persistenceService;
82      private PurchaseOrderService purchaseOrderService;
83      private UniversityDateService universityDateService;
84      private VendorService vendorService;
85      private TaxService taxService;
86      private PurapAccountingService purapAccountingService;
87  
88      public void setBusinessObjectService(BusinessObjectService boService) {
89          this.businessObjectService = boService;
90      }
91  
92      public void setDateTimeService(DateTimeService dateTimeService) {
93          this.dateTimeService = dateTimeService;
94      }
95  
96      public void setParameterService(ParameterService parameterService) {
97          this.parameterService = parameterService;
98      }
99  
100     public void setDocumentService(DocumentService documentService) {
101         this.documentService = documentService;
102     }
103 
104     public void setDataDictionaryService(DataDictionaryService dataDictionaryService) {
105         this.dataDictionaryService = dataDictionaryService;
106     }
107 
108     public void setVendorService(VendorService vendorService) {
109         this.vendorService = vendorService;
110     }
111 
112     public void setPersistenceService(PersistenceService persistenceService) {
113         this.persistenceService = persistenceService;
114     }
115 
116     public void setPurchaseOrderService(PurchaseOrderService purchaseOrderService) {
117         this.purchaseOrderService = purchaseOrderService;
118     }
119 
120     public void setNoteService(NoteService noteService) {
121         this.noteService = noteService;
122     }
123 
124     public void setUniversityDateService(UniversityDateService universityDateService) {
125         this.universityDateService = universityDateService;
126     }
127 
128     public void setTaxService(TaxService taxService) {
129         this.taxService = taxService;
130     }
131 
132     
133 
134 
135     
136   
137 
138 
139 
140 
141 
142 
143 
144 
145 
146 
147 
148 
149 
150 
151 
152     @Override
153     public void saveRoutingDataForRelatedDocuments(Integer accountsPayablePurchasingDocumentLinkIdentifier) {
154 
155         try {
156             
157             List<RequisitionView> reqViews = getRelatedViews(RequisitionView.class, accountsPayablePurchasingDocumentLinkIdentifier);
158             for (RequisitionView view : reqViews) {
159                 Document doc = documentService.getByDocumentHeaderId(view.getDocumentNumber());
160                 doc.getDocumentHeader().getWorkflowDocument().saveDocumentData();
161             }
162 
163             
164             List<PurchaseOrderView> poViews = getRelatedViews(PurchaseOrderView.class, accountsPayablePurchasingDocumentLinkIdentifier);
165             for (PurchaseOrderView view : poViews) {
166                 Document doc = documentService.getByDocumentHeaderId(view.getDocumentNumber());
167                 doc.getDocumentHeader().getWorkflowDocument().saveDocumentData();
168             }
169 
170             
171             List<PaymentRequestView> preqViews = getRelatedViews(PaymentRequestView.class, accountsPayablePurchasingDocumentLinkIdentifier);
172             for (PaymentRequestView view : preqViews) {
173                 Document doc = documentService.getByDocumentHeaderId(view.getDocumentNumber());
174                 doc.getDocumentHeader().getWorkflowDocument().saveDocumentData();
175             }
176 
177             
178             List<InvoiceView> invViews = getRelatedViews(InvoiceView.class, accountsPayablePurchasingDocumentLinkIdentifier);
179             for (InvoiceView invView : invViews) {
180                 Document doc = documentService.getByDocumentHeaderId(invView.getDocumentNumber());
181                 doc.getDocumentHeader().getWorkflowDocument().saveDocumentData();
182             }
183 
184             
185             List<CreditMemoView> cmViews = getRelatedViews(CreditMemoView.class, accountsPayablePurchasingDocumentLinkIdentifier);
186             for (CreditMemoView view : cmViews) {
187                 Document doc = documentService.getByDocumentHeaderId(view.getDocumentNumber());
188                 doc.getDocumentHeader().getWorkflowDocument().saveDocumentData();
189             }
190 
191             
192             List<LineItemReceivingView> lineViews = getRelatedViews(LineItemReceivingView.class, accountsPayablePurchasingDocumentLinkIdentifier);
193             for (LineItemReceivingView view : lineViews) {
194                 Document doc = documentService.getByDocumentHeaderId(view.getDocumentNumber());
195                 doc.getDocumentHeader().getWorkflowDocument().saveDocumentData();
196             }
197 
198             
199             List<CorrectionReceivingView> corrViews = getRelatedViews(CorrectionReceivingView.class, accountsPayablePurchasingDocumentLinkIdentifier);
200             for (CorrectionReceivingView view : corrViews) {
201                 Document doc = documentService.getByDocumentHeaderId(view.getDocumentNumber());
202                 doc.getDocumentHeader().getWorkflowDocument().saveDocumentData();
203             }
204 
205             
206             List<BulkReceivingView> bulkViews = getRelatedViews(BulkReceivingView.class, accountsPayablePurchasingDocumentLinkIdentifier);
207             for (BulkReceivingView view : bulkViews) {
208                 Document doc = documentService.getByDocumentHeaderId(view.getDocumentNumber());
209                 doc.getDocumentHeader().getWorkflowDocument().saveDocumentData();
210             }
211         } catch (WorkflowException e) {
212             throw new IllegalArgumentException("unable to save routing data for related docs", e);
213         }
214 
215     }
216 
217     
218 
219 
220     @Override
221     public List<String> getRelatedDocumentIds(Integer accountsPayablePurchasingDocumentLinkIdentifier) {
222         LOG.debug("getRelatedDocumentIds() started");
223         List<String> documentIdList = new ArrayList<String>();
224 
225         
226         List<RequisitionView> reqViews = getRelatedViews(RequisitionView.class, accountsPayablePurchasingDocumentLinkIdentifier);
227         for (RequisitionView view : reqViews) {
228             documentIdList.add(view.getDocumentNumber());
229         }
230 
231         
232         List<PurchaseOrderView> poViews = getRelatedViews(PurchaseOrderView.class, accountsPayablePurchasingDocumentLinkIdentifier);
233         for (PurchaseOrderView view : poViews) {
234             documentIdList.add(view.getDocumentNumber());
235         }
236 
237         
238         List<PaymentRequestView> preqViews = getRelatedViews(PaymentRequestView.class, accountsPayablePurchasingDocumentLinkIdentifier);
239         for (PaymentRequestView view : preqViews) {
240             documentIdList.add(view.getDocumentNumber());
241         }
242 
243         
244         List<InvoiceView> invViews = getRelatedViews(InvoiceView.class, accountsPayablePurchasingDocumentLinkIdentifier);
245         for (InvoiceView invView : invViews) {
246             documentIdList.add(invView.getDocumentNumber());
247         }
248 
249         
250         List<CreditMemoView> cmViews = getRelatedViews(CreditMemoView.class, accountsPayablePurchasingDocumentLinkIdentifier);
251         for (CreditMemoView view : cmViews) {
252             documentIdList.add(view.getDocumentNumber());
253         }
254 
255         
256         List<LineItemReceivingView> lineViews = getRelatedViews(LineItemReceivingView.class, accountsPayablePurchasingDocumentLinkIdentifier);
257         for (LineItemReceivingView view : lineViews) {
258             documentIdList.add(view.getDocumentNumber());
259         }
260 
261         
262         List<CorrectionReceivingView> corrViews = getRelatedViews(CorrectionReceivingView.class, accountsPayablePurchasingDocumentLinkIdentifier);
263         for (CorrectionReceivingView view : corrViews) {
264             documentIdList.add(view.getDocumentNumber());
265         }
266 
267         
268         List<BulkReceivingView> bulkViews = getRelatedViews(BulkReceivingView.class, accountsPayablePurchasingDocumentLinkIdentifier);
269         for (BulkReceivingView view : bulkViews) {
270             documentIdList.add(view.getDocumentNumber());
271         }
272 
273         
274 
275         return documentIdList;
276     }
277 
278     
279 
280 
281     @Override
282     @SuppressWarnings("unchecked")
283     @Transactional
284     public List getRelatedViews(Class clazz, Integer accountsPayablePurchasingDocumentLinkIdentifier) {
285         LOG.debug("getRelatedViews() started");
286 
287         Map criteria = new HashMap();
288         String st = clazz.getSimpleName();
289 
290         
291         if (st.equals("InvoiceView")) {
292             Map criteriaMap = new HashMap();
293             criteriaMap.put("accountsPayablePurchasingDocumentLinkIdentifier", accountsPayablePurchasingDocumentLinkIdentifier);
294             List<OleInvoiceItem> boInvoiceItemList = (List<OleInvoiceItem>) businessObjectService.findMatching(OleInvoiceItem.class, criteriaMap);
295             List boList = new ArrayList();
296             if (boInvoiceItemList.size() > 0) {
297                 for (OleInvoiceItem item : boInvoiceItemList) {
298                     accountsPayablePurchasingDocumentLinkIdentifier =item.getInvoiceDocument().getAccountsPayablePurchasingDocumentLinkIdentifier();
299                     criteria.put("accountsPayablePurchasingDocumentLinkIdentifier", accountsPayablePurchasingDocumentLinkIdentifier);
300                     boList.addAll((List) businessObjectService.findMatchingOrderBy(clazz, criteria, OLEPropertyConstants.DOCUMENT_NUMBER, false));
301 
302                 }
303                 return boList;
304             }
305         }
306         criteria.put("accountsPayablePurchasingDocumentLinkIdentifier", accountsPayablePurchasingDocumentLinkIdentifier);
307         
308         List boList = (List) businessObjectService.findMatchingOrderBy(clazz, criteria, OLEPropertyConstants.DOCUMENT_NUMBER, false);
309         return boList;
310     }
311 
312     
313 
314 
315     @Override
316     @SuppressWarnings("unchecked")
317     public void addBelowLineItems(PurchasingAccountsPayableDocument document) {
318         LOG.debug("addBelowLineItems() started");
319 
320         String[] itemTypes = getBelowTheLineForDocument(document);
321 
322         List<PurApItem> existingItems = document.getItems();
323 
324         List<PurApItem> belowTheLine = new ArrayList<PurApItem>();
325         
326         sortBelowTheLine(itemTypes, existingItems, belowTheLine);
327 
328         List<String> existingItemTypes = new ArrayList<String>();
329         for (PurApItem existingItem : existingItems) {
330             existingItemTypes.add(existingItem.getItemTypeCode());
331         }
332 
333         Class itemClass = document.getItemClass();
334 
335         for (int i = 0; i < itemTypes.length; i++) {
336             int lastFound;
337             if (!existingItemTypes.contains(itemTypes[i])) {
338                 try {
339                     if (i > 0) {
340                         lastFound = existingItemTypes.lastIndexOf(itemTypes[i - 1]) + 1;
341                     } else {
342                         lastFound = existingItemTypes.size();
343                     }
344                     PurApItem newItem = (PurApItem) itemClass.newInstance();
345                     newItem.setItemTypeCode(itemTypes[i]);
346                     newItem.setPurapDocument(document);
347                     existingItems.add(lastFound, newItem);
348                     existingItemTypes.add(itemTypes[i]);
349                 } catch (Exception e) {
350                     
351                 }
352             }
353         }
354 
355         document.fixItemReferences();
356     }
357 
358     
359 
360 
361 
362 
363 
364 
365     protected void sortBelowTheLine(String[] itemTypes, List<PurApItem> existingItems, List<PurApItem> belowTheLine) {
366         LOG.debug("sortBelowTheLine() started");
367 
368         
369         for (int i = 0; i < existingItems.size(); i++) {
370             PurApItem purApItem = existingItems.get(i);
371             if (purApItem.getItemType().isAdditionalChargeIndicator()) {
372                 belowTheLine.add(existingItems.get(i));
373             }
374         }
375         existingItems.removeAll(belowTheLine);
376         for (String itemType : itemTypes) {
377             for (PurApItem purApItem : belowTheLine) {
378                 if (StringUtils.equalsIgnoreCase(purApItem.getItemTypeCode(), itemType)) {
379                     existingItems.add(purApItem);
380                     break;
381                 }
382             }
383         }
384         belowTheLine.removeAll(existingItems);
385         if (belowTheLine.size() != 0) {
386             throw new RuntimeException("below the line item sort didn't work: trying to remove an item without adding it back");
387         }
388     }
389 
390     
391 
392 
393     @Override
394     public void sortBelowTheLine(PurchasingAccountsPayableDocument document) {
395         LOG.debug("sortBelowTheLine() started");
396 
397         String[] itemTypes = getBelowTheLineForDocument(document);
398 
399         List<PurApItem> existingItems = document.getItems();
400 
401         List<PurApItem> belowTheLine = new ArrayList<PurApItem>();
402         
403         sortBelowTheLine(itemTypes, existingItems, belowTheLine);
404     }
405 
406     
407 
408 
409     @Override
410     public String[] getBelowTheLineForDocument(PurchasingAccountsPayableDocument document) {
411         LOG.debug("getBelowTheLineForDocument() started");
412 
413         
414 
415 
416 
417 
418         
419         
420         
421 
422 
423 
424 
425         String documentType = dataDictionaryService.getDocumentTypeNameByClass(document.getClass());
426 
427         try {
428             return parameterService.getParameterValuesAsString(Class.forName(PurapConstants.PURAP_DETAIL_TYPE_CODE_MAP.get(documentType)), PurapConstants.BELOW_THE_LINES_PARAMETER).toArray(new String[]{});
429         } catch (ClassNotFoundException e) {
430             throw new RuntimeException("The getBelowTheLineForDocument method of PurapServiceImpl was unable to resolve the document class for type: " + PurapConstants.PURAP_DETAIL_TYPE_CODE_MAP.get(documentType), e);
431         }
432     }
433 
434     
435 
436 
437 
438     @Override
439     public PurApItem getBelowTheLineByType(PurchasingAccountsPayableDocument document, ItemType iT) {
440         LOG.debug("getBelowTheLineByType() started");
441 
442         String[] itemTypes = getBelowTheLineForDocument(document);
443         boolean foundItemType = false;
444         for (String itemType : itemTypes) {
445             if (StringUtils.equals(iT.getItemTypeCode(), itemType)) {
446                 foundItemType = true;
447                 break;
448             }
449         }
450         if (!foundItemType) {
451             return null;
452         }
453 
454         PurApItem belowTheLineItem = null;
455         for (PurApItem item : document.getItems()) {
456             if (item.getItemType().isAdditionalChargeIndicator()) {
457                 if (StringUtils.equals(iT.getItemTypeCode(), item.getItemType().getItemTypeCode())) {
458                     belowTheLineItem = item;
459                     break;
460                 }
461             }
462         }
463         return belowTheLineItem;
464     }
465 
466     
467 
468 
469     @Override
470     public Date getDateFromOffsetFromToday(int offsetDays) {
471         Calendar calendar = dateTimeService.getCurrentCalendar();
472         calendar.add(Calendar.DATE, offsetDays);
473         return new Date(calendar.getTimeInMillis());
474     }
475 
476     
477 
478 
479     @Override
480     public boolean isDateInPast(Date compareDate) {
481         LOG.debug("isDateInPast() started");
482 
483         Date today = dateTimeService.getCurrentSqlDate();
484         int diffFromToday = dateTimeService.dateDiff(today, compareDate, false);
485         return (diffFromToday < 0);
486     }
487 
488     
489 
490 
491     @Override
492     public boolean isDateMoreThanANumberOfDaysAway(Date compareDate, int daysAway) {
493         LOG.debug("isDateMoreThanANumberOfDaysAway() started");
494 
495         Date todayAtMidnight = dateTimeService.getCurrentSqlDateMidnight();
496         Calendar daysAwayCalendar = dateTimeService.getCalendar(todayAtMidnight);
497         daysAwayCalendar.add(Calendar.DATE, daysAway);
498         Timestamp daysAwayTime = new Timestamp(daysAwayCalendar.getTime().getTime());
499         Calendar compareCalendar = dateTimeService.getCalendar(compareDate);
500         compareCalendar.set(Calendar.HOUR, 0);
501         compareCalendar.set(Calendar.MINUTE, 0);
502         compareCalendar.set(Calendar.SECOND, 0);
503         compareCalendar.set(Calendar.MILLISECOND, 0);
504         compareCalendar.set(Calendar.AM_PM, Calendar.AM);
505         Timestamp compareTime = new Timestamp(compareCalendar.getTime().getTime());
506         return (compareTime.compareTo(daysAwayTime) > 0);
507     }
508 
509     
510 
511 
512     @Override
513     public boolean isDateAYearBeforeToday(Date compareDate) {
514         LOG.debug("isDateAYearBeforeToday() started");
515 
516         Calendar calendar = dateTimeService.getCurrentCalendar();
517         calendar.add(Calendar.YEAR, -1);
518         Date yearAgo = new Date(calendar.getTimeInMillis());
519         int diffFromYearAgo = dateTimeService.dateDiff(compareDate, yearAgo, false);
520         return (diffFromYearAgo > 0);
521     }
522 
523     
524 
525 
526     @Override
527     @SuppressWarnings("unchecked")
528     public KualiDecimal getApoLimit(Integer vendorContractGeneratedIdentifier, String chart, String org) {
529         LOG.debug("getApoLimit() started");
530 
531         KualiDecimal purchaseOrderTotalLimit = vendorService.getApoLimitFromContract(vendorContractGeneratedIdentifier, chart, org);
532 
533         
534         if (ObjectUtils.isNull(purchaseOrderTotalLimit) && !ObjectUtils.isNull(chart) && !ObjectUtils.isNull(org)) {
535             OrganizationParameter organizationParameter = new OrganizationParameter();
536             organizationParameter.setChartOfAccountsCode(chart);
537             organizationParameter.setOrganizationCode(org);
538             Map orgParamKeys = persistenceService.getPrimaryKeyFieldValues(organizationParameter);
539             orgParamKeys.put(KRADPropertyConstants.ACTIVE_INDICATOR, true);
540             organizationParameter = businessObjectService.findByPrimaryKey(OrganizationParameter.class, orgParamKeys);
541             purchaseOrderTotalLimit = (organizationParameter == null) ? null : organizationParameter.getOrganizationAutomaticPurchaseOrderLimit();
542         }
543 
544         if (ObjectUtils.isNull(purchaseOrderTotalLimit)) {
545             String defaultLimit = parameterService.getParameterValueAsString(RequisitionDocument.class, PurapParameterConstants.AUTOMATIC_PURCHASE_ORDER_DEFAULT_LIMIT_AMOUNT);
546             purchaseOrderTotalLimit = new KualiDecimal(defaultLimit);
547         }
548 
549         return purchaseOrderTotalLimit;
550     }
551 
552     
553 
554 
555     @Override
556     public boolean isFullDocumentEntryCompleted(PurchasingAccountsPayableDocument purapDocument) {
557         LOG.debug("isFullDocumentEntryCompleted() started");
558 
559         
560         boolean value = false;
561         if (purapDocument instanceof PaymentRequestDocument) {
562             value = PurapConstants.PaymentRequestStatuses.STATUS_ORDER.isFullDocumentEntryCompleted(purapDocument.getApplicationDocumentStatus());
563         } else if (purapDocument instanceof VendorCreditMemoDocument) {
564             value = PurapConstants.CreditMemoStatuses.STATUS_ORDER.isFullDocumentEntryCompleted(purapDocument.getApplicationDocumentStatus());
565         } else if (purapDocument instanceof OleInvoiceDocument) {
566             value = PurapConstants.InvoiceStatuses.STATUS_ORDER.isFullDocumentEntryCompleted(purapDocument.getApplicationDocumentStatus());
567         }
568         return value;
569     }
570 
571     
572 
573 
574     @Override
575     public boolean isPaymentRequestFullDocumentEntryCompleted(String purapDocumentStatus) {
576         LOG.debug("isPaymentRequestFullDocumentEntryCompleted() started");
577         return PurapConstants.PaymentRequestStatuses.STATUS_ORDER.isFullDocumentEntryCompleted(purapDocumentStatus);
578     }
579 
580     
581 
582 
583     @Override
584     public boolean isVendorCreditMemoFullDocumentEntryCompleted(String purapDocumentStatus) {
585         LOG.debug("isVendorCreditMemoFullDocumentEntryCompleted() started");
586         return PurapConstants.CreditMemoStatuses.STATUS_ORDER.isFullDocumentEntryCompleted(purapDocumentStatus);
587     }
588 
589     
590 
591 
592     @Override
593     public boolean isInvoiceFullDocumentEntryCompleted(String purapDocumentStatus) {
594         LOG.debug("isInvoiceFullDocumentEntryCompleted() started");
595         return PurapConstants.InvoiceStatuses.STATUS_ORDER.isFullDocumentEntryCompleted(purapDocumentStatus);
596     }
597 
598     
599 
600 
601 
602 
603     @Override
604     public void performLogicForCloseReopenPO(PurchasingAccountsPayableDocument purapDocument) {
605         LOG.debug("performLogicForCloseReopenPO() started");
606 
607         if (purapDocument instanceof PaymentRequestDocument) {
608             PaymentRequestDocument paymentRequest = (PaymentRequestDocument) purapDocument;
609 
610             if (paymentRequest.isClosePurchaseOrderIndicator() && PurchaseOrderStatuses.APPDOC_OPEN.equals(paymentRequest.getPurchaseOrderDocument().getApplicationDocumentStatus())) {
611                 
612                 
613                 processCloseReopenPo((AccountsPayableDocumentBase) purapDocument, PurapConstants.PurchaseOrderDocTypes.PURCHASE_ORDER_CLOSE_DOCUMENT);
614             }
615 
616         } else if (purapDocument instanceof VendorCreditMemoDocument) {
617             VendorCreditMemoDocument creditMemo = (VendorCreditMemoDocument) purapDocument;
618 
619             if (creditMemo.isReopenPurchaseOrderIndicator() && PurchaseOrderStatuses.APPDOC_CLOSED.equals(creditMemo.getPurchaseOrderDocument().getApplicationDocumentStatus())) {
620                 
621                 
622                 processCloseReopenPo((AccountsPayableDocumentBase) purapDocument, PurapConstants.PurchaseOrderDocTypes.PURCHASE_ORDER_REOPEN_DOCUMENT);
623             }
624 
625         } else {
626             throw new RuntimeException("Attempted to perform full entry logic for unhandled document type '" + purapDocument.getClass().getName() + "'");
627         }
628 
629     }
630 
631     
632 
633 
634 
635 
636 
637     @Override
638     public void deleteUnenteredItems(PurapItemOperations document) {
639         LOG.debug("deleteUnenteredItems() started");
640 
641         List<PurapEnterableItem> deletionList = new ArrayList<PurapEnterableItem>();
642         for (PurapEnterableItem item : (List<PurapEnterableItem>) document.getItems()) {
643             if (!item.isConsideredEntered()) {
644                 deletionList.add(item);
645             }
646         }
647         document.getItems().removeAll(deletionList);
648     }
649 
650     
651 
652 
653 
654 
655 
656     @SuppressWarnings("unchecked")
657     public void processCloseReopenPo(AccountsPayableDocumentBase apDocument, String docType) {
658         LOG.debug("processCloseReopenPo() started");
659 
660         String action = null;
661         String newStatus = null;
662         
663         if (PurapConstants.PurchaseOrderDocTypes.PURCHASE_ORDER_CLOSE_DOCUMENT.equals(docType)) {
664             action = "closed";
665             newStatus = PurchaseOrderStatuses.APPDOC_PENDING_CLOSE;
666         } else if (PurapConstants.PurchaseOrderDocTypes.PURCHASE_ORDER_REOPEN_DOCUMENT.equals(docType)) {
667             action = "reopened";
668             newStatus = PurchaseOrderStatuses.APPDOC_PENDING_REOPEN;
669         } else {
670             String errorMessage = "Method processCloseReopenPo called using ID + '" + apDocument.getPurapDocumentIdentifier() + "' and invalid doc type '" + docType + "'";
671             LOG.error(errorMessage);
672             throw new RuntimeException(errorMessage);
673         }
674 
675 
676         Integer poId = apDocument.getPurchaseOrderIdentifier();
677         PurchaseOrderDocument purchaseOrderDocument = purchaseOrderService.getCurrentPurchaseOrder(poId);
678         if (!StringUtils.equalsIgnoreCase(purchaseOrderDocument.getDocumentHeader().getWorkflowDocument().getDocumentTypeName(), docType)) {
679             
680             
681             purchaseOrderService.createAndRoutePotentialChangeDocument(purchaseOrderDocument.getDocumentNumber(), docType, assemblePurchaseOrderNote(apDocument, docType, action), new ArrayList(), newStatus);
682         }
683 
684         
685 
686 
687 
688         if (PurapConstants.PurchaseOrderDocTypes.PURCHASE_ORDER_CLOSE_DOCUMENT.equals(docType)) {
689             apDocument.setClosePurchaseOrderIndicator(false);
690 
691             
692             String userName = apDocument.getLastActionPerformedByPersonName();
693             StringBuffer poNote = new StringBuffer("");
694             poNote.append("PO was closed manually by ");
695             poNote.append(userName);
696             poNote.append(" in approving PREQ with ID ");
697             poNote.append(apDocument.getDocumentNumber());
698 
699             
700             try {
701                 Note noteObj = documentService.createNoteFromDocument(apDocument.getPurchaseOrderDocument(), poNote.toString());
702                 noteObj.setNoteTypeCode(apDocument.getPurchaseOrderDocument().getNoteType().getCode());
703                 apDocument.getPurchaseOrderDocument().addNote(noteObj);
704                 noteService.save(noteObj);
705             } catch (Exception e) {
706                 String errorMessage = "Error creating and saving close note for purchase order with document service";
707                 LOG.error("processCloseReopenPo() " + errorMessage, e);
708                 throw new RuntimeException(errorMessage, e);
709             }
710         } else if (PurapConstants.PurchaseOrderDocTypes.PURCHASE_ORDER_REOPEN_DOCUMENT.equals(docType)) {
711             apDocument.setReopenPurchaseOrderIndicator(false);
712         }
713 
714     }
715 
716     
717 
718 
719 
720 
721 
722 
723     protected String assemblePurchaseOrderNote(AccountsPayableDocumentBase apDocument, String docType, String action) {
724         LOG.debug("assemblePurchaseOrderNote() started");
725 
726         String documentLabel = dataDictionaryService.getDocumentLabelByClass(apDocument.getClass());
727         StringBuffer closeReopenNote = new StringBuffer("");
728         String userName = GlobalVariables.getUserSession().getPerson().getName();
729         closeReopenNote.append(dataDictionaryService.getDocumentLabelByTypeName(OLEConstants.FinancialDocumentTypeCodes.PURCHASE_ORDER));
730         closeReopenNote.append(" will be manually ");
731         closeReopenNote.append(action);
732         closeReopenNote.append(" by ");
733         closeReopenNote.append(userName);
734         closeReopenNote.append(" when approving ");
735         closeReopenNote.append(documentLabel);
736         closeReopenNote.append(" with ");
737         closeReopenNote.append(dataDictionaryService.getAttributeLabel(apDocument.getClass(), PurapPropertyConstants.PURAP_DOC_ID));
738         closeReopenNote.append(" ");
739         closeReopenNote.append(apDocument.getPurapDocumentIdentifier());
740 
741         return closeReopenNote.toString();
742     }
743 
744     
745 
746 
747     @Override
748     public Object performLogicWithFakedUserSession(String requiredPersonPersonUserId, LogicContainer logicToRun, Object... objects) throws WorkflowException, Exception {
749         LOG.debug("performLogicWithFakedUserSession() started");
750 
751         if (StringUtils.isBlank(requiredPersonPersonUserId)) {
752             throw new RuntimeException("Attempted to perform logic with a fake user session with a blank user person id: '" + requiredPersonPersonUserId + "'");
753         }
754         if (ObjectUtils.isNull(logicToRun)) {
755             throw new RuntimeException("Attempted to perform logic with a fake user session with no logic to run");
756         }
757         UserSession actualUserSession = GlobalVariables.getUserSession();
758         try {
759             GlobalVariables.setUserSession(new UserSession(requiredPersonPersonUserId));
760             return logicToRun.runLogic(objects);
761         } finally {
762             GlobalVariables.setUserSession(actualUserSession);
763         }
764     }
765 
766     
767 
768 
769     @Override
770     public void saveDocumentNoValidation(Document document) {
771         try {
772             
773             
774             
775             
776             if (document.getDocumentHeader() != null && document.getDocumentHeader().getDocumentNumber() == null) {
777                 WorkflowDocument workflowDocument = document.getDocumentHeader().getWorkflowDocument();
778                 document.refreshReferenceObject("documentHeader");
779                 document.getDocumentHeader().setWorkflowDocument(workflowDocument);
780             }
781             documentService.saveDocument(document, DocumentSystemSaveEvent.class);
782 
783             
784             
785             
786             final DocumentAttributeIndexingQueue documentAttributeIndexingQueue = KewApiServiceLocator.getDocumentAttributeIndexingQueue();
787 
788             documentAttributeIndexingQueue.indexDocument(document.getDocumentNumber());
789         } catch (WorkflowException we) {
790             String errorMsg = "Workflow error saving document # " + document.getDocumentHeader().getDocumentNumber() + " " + we.getMessage();
791             LOG.error(errorMsg, we);
792             throw new RuntimeException(errorMsg, we);
793         } catch (NumberFormatException ne) {
794             String errorMsg = "Invalid document number format for document # " + document.getDocumentHeader().getDocumentNumber() + " " + ne.getMessage();
795             LOG.error(errorMsg, ne);
796             throw new RuntimeException(errorMsg, ne);
797         }
798     }
799 
800     
801 
802 
803     @Override
804     public boolean isDocumentStoppedInRouteNode(PurchasingAccountsPayableDocument document, String nodeName) {
805         WorkflowDocument workflowDoc = document.getDocumentHeader().getWorkflowDocument();
806         Set<String> currentRouteLevels = workflowDoc.getCurrentNodeNames();
807         if (currentRouteLevels.contains(nodeName) && workflowDoc.isApprovalRequested()) {
808             return true;
809         }
810         return false;
811     }
812 
813     
814 
815 
816     @Override
817     public boolean allowEncumberNextFiscalYear() {
818         LOG.debug("allowEncumberNextFiscalYear() started");
819 
820         java.util.Date today = dateTimeService.getCurrentDate();
821         java.util.Date closingDate = universityDateService.getLastDateOfFiscalYear(universityDateService.getCurrentFiscalYear());
822         int allowEncumberNext = (Integer.parseInt(parameterService.getParameterValueAsString(RequisitionDocument.class, PurapRuleConstants.ALLOW_ENCUMBER_NEXT_YEAR_DAYS)));
823         int diffTodayClosing = dateTimeService.dateDiff(today, closingDate, false);
824 
825         if (ObjectUtils.isNotNull(closingDate) && ObjectUtils.isNotNull(today) && ObjectUtils.isNotNull(allowEncumberNext)) {
826             if (LOG.isDebugEnabled()) {
827                 LOG.debug("allowEncumberNextFiscalYear() today = " + dateTimeService.toDateString(today) + "; encumber next FY range = " + allowEncumberNext + " - " + dateTimeService.toDateTimeString(today));
828             }
829 
830             if (allowEncumberNext >= diffTodayClosing && diffTodayClosing >= KualiDecimal.ZERO.intValue()) {
831                 LOG.debug("allowEncumberNextFiscalYear() encumber next FY allowed; return true.");
832                 return true;
833             }
834         }
835         LOG.debug("allowEncumberNextFiscalYear() encumber next FY not allowed; return false.");
836         return false;
837     }
838 
839     
840 
841 
842     @Override
843     public List<Integer> getAllowedFiscalYears() {
844         List<Integer> allowedYears = new ArrayList<Integer>();
845         Integer currentFY = universityDateService.getCurrentFiscalYear();
846         allowedYears.add(currentFY);
847         if (allowEncumberNextFiscalYear()) {
848             allowedYears.add(currentFY + 1);
849         }
850         return allowedYears;
851     }
852 
853     
854 
855 
856     @Override
857     public boolean isTodayWithinApoAllowedRange() {
858         java.util.Date today = dateTimeService.getCurrentDate();
859         Integer currentFY = universityDateService.getCurrentFiscalYear();
860         java.util.Date closingDate = universityDateService.getLastDateOfFiscalYear(currentFY);
861         int allowApoDate = (Integer.parseInt(parameterService.getParameterValueAsString(RequisitionDocument.class, PurapRuleConstants.ALLOW_APO_NEXT_FY_DAYS)));
862         int diffTodayClosing = dateTimeService.dateDiff(today, closingDate, true);
863 
864         return diffTodayClosing <= allowApoDate;
865     }
866 
867     
868 
869 
870     @Override
871     public void clearTax(PurchasingAccountsPayableDocument purapDocument, boolean useTax) {
872         for (PurApItem item : purapDocument.getItems()) {
873             if (useTax) {
874                 item.getUseTaxItems().clear();
875             } else {
876                 item.setItemTaxAmount(null);
877             }
878         }
879     }
880 
881     @Override
882     public void updateUseTaxIndicator(PurchasingAccountsPayableDocument purapDocument, boolean newUseTaxIndicatorValue) {
883         boolean currentUseTaxIndicator = purapDocument.isUseTaxIndicator();
884         if (currentUseTaxIndicator != newUseTaxIndicatorValue) {
885             
886             clearTax(purapDocument, currentUseTaxIndicator);
887         }
888         purapDocument.setUseTaxIndicator(newUseTaxIndicatorValue);
889     }
890 
891     
892 
893 
894     @Override
895     public void calculateTax(PurchasingAccountsPayableDocument purapDocument) {
896 
897         boolean salesTaxInd = SpringContext.getBean(ParameterService.class).getParameterValueAsBoolean(OleParameterConstants.PURCHASING_DOCUMENT.class, PurapParameterConstants.ENABLE_SALES_TAX_IND);
898         boolean useTaxIndicator = purapDocument.isUseTaxIndicator();
899         String deliveryState = getDeliveryState(purapDocument);
900         String deliveryPostalCode = getDeliveryPostalCode(purapDocument);
901         Date transactionTaxDate = purapDocument.getTransactionTaxDate();
902 
903         
904         if (salesTaxInd || useTaxIndicator) {
905             
906             for (PurApItem item : purapDocument.getItems()) {
907                 if (isTaxable(useTaxIndicator, deliveryState, item)) {
908                     calculateItemTax(useTaxIndicator, deliveryPostalCode, transactionTaxDate, item, item.getUseTaxClass(), purapDocument);
909                 }
910             }
911         }
912     }
913 
914     @Override
915     public String getDeliveryState(PurchasingAccountsPayableDocument purapDocument) {
916         if (purapDocument instanceof PurchasingDocument) {
917             PurchasingDocument document = (PurchasingDocument) purapDocument;
918             return document.getDeliveryStateCode();
919         } else if (purapDocument instanceof AccountsPayableDocument) {
920             AccountsPayableDocument document = (AccountsPayableDocument) purapDocument;
921             if (document.getPurchaseOrderDocument() == null) {
922                 throw new RuntimeException("PurchaseOrder document does not exists");
923             }
924             return document.getPurchaseOrderDocument().getDeliveryStateCode();
925         }
926         return null;
927     }
928 
929     protected String getDeliveryPostalCode(PurchasingAccountsPayableDocument purapDocument) {
930         if (purapDocument instanceof PurchasingDocument) {
931             PurchasingDocument document = (PurchasingDocument) purapDocument;
932             return document.getDeliveryPostalCode();
933         } else if (purapDocument instanceof AccountsPayableDocument) {
934             AccountsPayableDocument docBase = (AccountsPayableDocument) purapDocument;
935             if (docBase.getPurchaseOrderDocument() == null) {
936                 throw new RuntimeException("PurchaseOrder document does not exists");
937             }
938             return docBase.getPurchaseOrderDocument().getDeliveryPostalCode();
939         }
940         return null;
941     }
942 
943     
944 
945 
946 
947 
948 
949 
950 
951     @Override
952     public boolean isTaxable(boolean useTaxIndicator, String deliveryState, PurApItem item) {
953 
954         boolean taxable = false;
955 
956         if (item.getItemType().isTaxableIndicator() &&
957                 ((ObjectUtils.isNull(item.getItemTaxAmount()) && useTaxIndicator == false) || useTaxIndicator) &&
958                 (doesCommodityAllowCallToTaxService(item)) &&
959                 (doesAccountAllowCallToTaxService(deliveryState, item))) {
960 
961             taxable = true;
962         }
963         return taxable;
964     }
965 
966     
967 
968 
969     @Override
970     public boolean isTaxableForSummary(boolean useTaxIndicator, String deliveryState, PurApItem item) {
971 
972         boolean taxable = false;
973 
974         if (item.getItemType().isTaxableIndicator() &&
975                 (doesCommodityAllowCallToTaxService(item)) &&
976                 (doesAccountAllowCallToTaxService(deliveryState, item))) {
977 
978             taxable = true;
979         }
980         return taxable;
981     }
982 
983     
984 
985 
986 
987 
988 
989     protected boolean doesCommodityAllowCallToTaxService(PurApItem item) {
990         boolean callService = true;
991 
992         
993         if (item.getItemType().isLineItemIndicator()) {
994             if (item instanceof PurchasingItem) {
995                 PurchasingItemBase purItem = (PurchasingItemBase) item;
996                 callService = isCommodityCodeTaxable(purItem.getCommodityCode());
997             }
998             else if (item instanceof AccountsPayableItem) {
999                 AccountsPayableItem apItem = (AccountsPayableItem) item;
1000                 PurchaseOrderItem poItem = apItem.getPurchaseOrderItem();
1001                 if (ObjectUtils.isNotNull(poItem)) {
1002                     callService = isCommodityCodeTaxable(poItem.getCommodityCode());
1003                 }
1004             }
1005         }
1006 
1007         return callService;
1008     }
1009 
1010     protected boolean isCommodityCodeTaxable(CommodityCode commodityCode) {
1011         boolean isTaxable = true;
1012 
1013         if (ObjectUtils.isNotNull(commodityCode)) {
1014 
1015             if (commodityCode.isSalesTaxIndicator() == false) {
1016                 
1017                 isTaxable = false;
1018             }
1019 
1020         }
1021 
1022         return isTaxable;
1023     }
1024 
1025     
1026 
1027 
1028     @Override
1029     public boolean isDeliveryStateTaxable(String deliveryState) {
1030         ParameterEvaluator parmEval = SpringContext.getBean(ParameterEvaluatorService.class).getParameterEvaluator(OleParameterConstants.PURCHASING_DOCUMENT.class, TaxParameters.TAXABLE_DELIVERY_STATES, deliveryState);
1031         return parmEval.evaluationSucceeds();
1032     }
1033 
1034     
1035 
1036 
1037 
1038 
1039 
1040 
1041     protected boolean doesAccountAllowCallToTaxService(String deliveryState, PurApItem item) {
1042         boolean callService = false;
1043         boolean deliveryStateTaxable = isDeliveryStateTaxable(deliveryState);
1044 
1045         for (PurApAccountingLine acctLine : item.getSourceAccountingLines()) {
1046             if (isAccountingLineTaxable(acctLine, deliveryStateTaxable)) {
1047                 callService = true;
1048                 break;
1049             }
1050         }
1051 
1052         return callService;
1053     }
1054 
1055     
1056 
1057 
1058     @Override
1059     public boolean isAccountingLineTaxable(PurApAccountingLine acctLine, boolean deliveryStateTaxable) {
1060         boolean isTaxable = false;
1061         String parameterSuffix = null;
1062 
1063         if (deliveryStateTaxable) {
1064             parameterSuffix = TaxParameters.FOR_TAXABLE_STATES_SUFFIX;
1065         } else {
1066             parameterSuffix = TaxParameters.FOR_NON_TAXABLE_STATES_SUFFIX;
1067         }
1068 
1069         
1070         if (isAccountTaxable(parameterSuffix, acctLine) && isObjectCodeTaxable(parameterSuffix, acctLine)) {
1071             isTaxable = true;
1072         }
1073 
1074         return isTaxable;
1075     }
1076 
1077     
1078 
1079 
1080 
1081 
1082 
1083 
1084 
1085     protected boolean isAccountTaxable(String parameterSuffix, PurApAccountingLine acctLine) {
1086 
1087         boolean isAccountTaxable = false;
1088         String fundParam = TaxParameters.TAXABLE_FUND_GROUPS_PREFIX + parameterSuffix;
1089         String subFundParam = TaxParameters.TAXABLE_SUB_FUND_GROUPS_PREFIX + parameterSuffix;
1090         ParameterEvaluator fundParamEval = null;
1091         ParameterEvaluator subFundParamEval = null;
1092 
1093         if (ObjectUtils.isNull(acctLine.getAccount().getSubFundGroup())) {
1094             acctLine.refreshNonUpdateableReferences();
1095         }
1096 
1097         fundParamEval = SpringContext.getBean(ParameterEvaluatorService.class).getParameterEvaluator(OleParameterConstants.PURCHASING_DOCUMENT.class, fundParam, acctLine.getAccount().getSubFundGroup().getFundGroupCode());
1098         subFundParamEval = SpringContext.getBean(ParameterEvaluatorService.class).getParameterEvaluator(OleParameterConstants.PURCHASING_DOCUMENT.class, subFundParam, acctLine.getAccount().getSubFundGroupCode());
1099 
1100         if ((isAllowedFound(fundParamEval) && (isAllowedFound(subFundParamEval) || isAllowedNotFound(subFundParamEval) || isDeniedNotFound(subFundParamEval))) ||
1101                 (isAllowedNotFound(fundParamEval) && isAllowedFound(subFundParamEval)) ||
1102                 (isDeniedFound(fundParamEval) && isAllowedFound(subFundParamEval)) ||
1103                 (isDeniedNotFound(fundParamEval) && (isAllowedFound(subFundParamEval) || isAllowedNotFound(subFundParamEval) || isDeniedNotFound(subFundParamEval)))) {
1104 
1105             isAccountTaxable = true;
1106         }
1107 
1108         return isAccountTaxable;
1109     }
1110 
1111     
1112 
1113 
1114 
1115 
1116 
1117 
1118 
1119     protected boolean isObjectCodeTaxable(String parameterSuffix, PurApAccountingLine acctLine) {
1120 
1121         boolean isObjectCodeTaxable = false;
1122         String levelParam = TaxParameters.TAXABLE_OBJECT_LEVELS_PREFIX + parameterSuffix;
1123         String consolidationParam = TaxParameters.TAXABLE_OBJECT_CONSOLIDATIONS_PREFIX + parameterSuffix;
1124         ParameterEvaluator levelParamEval = null;
1125         ParameterEvaluator consolidationParamEval = null;
1126 
1127         
1128         acctLine.getObjectCode().refreshReferenceObject("financialObjectLevel");
1129 
1130         levelParamEval = SpringContext.getBean(ParameterEvaluatorService.class).getParameterEvaluator(OleParameterConstants.PURCHASING_DOCUMENT.class, levelParam, acctLine.getObjectCode().getFinancialObjectLevelCode());
1131         consolidationParamEval = SpringContext.getBean(ParameterEvaluatorService.class).getParameterEvaluator(OleParameterConstants.PURCHASING_DOCUMENT.class, consolidationParam, acctLine.getObjectCode().getFinancialObjectLevel().getFinancialConsolidationObjectCode());
1132 
1133         if ((isAllowedFound(levelParamEval) && (isAllowedFound(consolidationParamEval) || isAllowedNotFound(consolidationParamEval) || isDeniedNotFound(consolidationParamEval))) ||
1134                 (isAllowedNotFound(levelParamEval) && isAllowedFound(consolidationParamEval)) ||
1135                 (isDeniedFound(levelParamEval) && isAllowedFound(consolidationParamEval)) ||
1136                 (isDeniedNotFound(levelParamEval) && (isAllowedFound(consolidationParamEval) || isAllowedNotFound(consolidationParamEval) || isDeniedNotFound(consolidationParamEval)))) {
1137 
1138             isObjectCodeTaxable = true;
1139         }
1140 
1141         return isObjectCodeTaxable;
1142     }
1143 
1144     
1145 
1146 
1147 
1148 
1149 
1150     protected boolean isAllowedFound(ParameterEvaluator eval) {
1151         boolean exists = false;
1152 
1153         if (eval.evaluationSucceeds() && eval.constraintIsAllow()) {
1154             exists = true;
1155         }
1156 
1157         return exists;
1158     }
1159 
1160     
1161 
1162 
1163 
1164 
1165 
1166     protected boolean isAllowedNotFound(ParameterEvaluator eval) {
1167         boolean exists = false;
1168 
1169         if (eval.evaluationSucceeds() == false && eval.constraintIsAllow()) {
1170             exists = true;
1171         }
1172 
1173         return exists;
1174     }
1175 
1176     
1177 
1178 
1179 
1180 
1181 
1182     protected boolean isDeniedFound(ParameterEvaluator eval) {
1183         boolean exists = false;
1184 
1185         if (eval.evaluationSucceeds() == false && eval.constraintIsAllow() == false) {
1186             exists = true;
1187         }
1188 
1189         return exists;
1190     }
1191 
1192     
1193 
1194 
1195 
1196 
1197 
1198     protected boolean isDeniedNotFound(ParameterEvaluator eval) {
1199         boolean exists = false;
1200 
1201         if (eval.evaluationSucceeds() && eval.constraintIsAllow() == false) {
1202             exists = true;
1203         }
1204 
1205         return exists;
1206     }
1207 
1208     
1209 
1210 
1211 
1212 
1213 
1214 
1215     @SuppressWarnings("unchecked")
1216     protected void calculateItemTax(boolean useTaxIndicator,
1217                                     String deliveryPostalCode,
1218                                     Date transactionTaxDate,
1219                                     PurApItem item,
1220                                     Class itemUseTaxClass,
1221                                     PurchasingAccountsPayableDocument purapDocument) {
1222 
1223         if (!useTaxIndicator) {
1224             if (!StringUtils.equals(item.getItemTypeCode(), PurapConstants.ItemTypeCodes.ITEM_TYPE_PMT_TERMS_DISCOUNT_CODE) &&
1225                     !StringUtils.equals(item.getItemTypeCode(), PurapConstants.ItemTypeCodes.ITEM_TYPE_ORDER_DISCOUNT_CODE)) {
1226                 KualiDecimal taxAmount = taxService.getTotalSalesTaxAmount(transactionTaxDate, deliveryPostalCode, item.getExtendedPrice());
1227                 item.setItemTaxAmount(taxAmount);
1228             }
1229         } else {
1230             KualiDecimal extendedPrice = item.getExtendedPrice();
1231 
1232             if (StringUtils.equals(item.getItemTypeCode(), PurapConstants.ItemTypeCodes.ITEM_TYPE_ORDER_DISCOUNT_CODE)) {
1233                 KualiDecimal taxablePrice = getFullDiscountTaxablePrice(extendedPrice, purapDocument);
1234                 extendedPrice = taxablePrice;
1235             }
1236             List<TaxDetail> taxDetails = taxService.getUseTaxDetails(transactionTaxDate, deliveryPostalCode, extendedPrice);
1237             List<PurApItemUseTax> newUseTaxItems = new ArrayList<PurApItemUseTax>();
1238             if (taxDetails != null) {
1239                 for (TaxDetail taxDetail : taxDetails) {
1240                     try {
1241                         PurApItemUseTax useTaxitem = (PurApItemUseTax) itemUseTaxClass.newInstance();
1242                         useTaxitem.setChartOfAccountsCode(taxDetail.getChartOfAccountsCode());
1243                         useTaxitem.setFinancialObjectCode(taxDetail.getFinancialObjectCode());
1244                         useTaxitem.setAccountNumber(taxDetail.getAccountNumber());
1245                         useTaxitem.setItemIdentifier(item.getItemIdentifier());
1246                         useTaxitem.setRateCode(taxDetail.getRateCode());
1247                         useTaxitem.setTaxAmount(taxDetail.getTaxAmount());
1248                         newUseTaxItems.add(useTaxitem);
1249                     } catch (Exception e) {
1250                         
1251 
1252 
1253 
1254                         throw new RuntimeException(e);
1255                     }
1256                 }
1257             }
1258             item.setUseTaxItems(newUseTaxItems);
1259         }
1260     }
1261 
1262     public KualiDecimal getFullDiscountTaxablePrice(KualiDecimal extendedPrice, PurchasingAccountsPayableDocument purapDocument) {
1263         KualiDecimal taxablePrice = KualiDecimal.ZERO;
1264         KualiDecimal taxableLineItemPrice = KualiDecimal.ZERO;
1265         KualiDecimal totalLineItemPrice = KualiDecimal.ZERO;
1266         boolean useTaxIndicator = purapDocument.isUseTaxIndicator();
1267         String deliveryState = getDeliveryState(purapDocument);
1268 
1269         
1270         for (PurApItem item : purapDocument.getItems()) {
1271             if (item.getItemType().isLineItemIndicator()) {
1272                 
1273                 if (ObjectUtils.isNotNull(item.getExtendedPrice())) {
1274                     if (isTaxable(useTaxIndicator, deliveryState, item)) {
1275                         taxableLineItemPrice = taxableLineItemPrice.add(item.getExtendedPrice());
1276                         totalLineItemPrice = totalLineItemPrice.add(item.getExtendedPrice());
1277                     } else {
1278                         totalLineItemPrice = totalLineItemPrice.add(item.getExtendedPrice());
1279                     }
1280                 }
1281             }
1282         }
1283 
1284         
1285         if (totalLineItemPrice.isNonZero() && ObjectUtils.isNotNull(extendedPrice)) {
1286             taxablePrice = taxableLineItemPrice.divide(totalLineItemPrice).multiply(extendedPrice);
1287         }
1288 
1289         return taxablePrice;
1290     }
1291 
1292     @Override
1293     public void prorateForTradeInAndFullOrderDiscount(PurchasingAccountsPayableDocument purDoc) {
1294 
1295         if (purDoc instanceof VendorCreditMemoDocument) {
1296             throw new RuntimeException("This method not applicable for VCM documents");
1297         }
1298 
1299         
1300         PurApItem fullOrderDiscount = null;
1301         PurApItem tradeIn = null;
1302         KualiDecimal totalAmount = KualiDecimal.ZERO;
1303         KualiDecimal totalTaxAmount = KualiDecimal.ZERO;
1304 
1305         List<PurApAccountingLine> distributedAccounts = null;
1306         List<SourceAccountingLine> summaryAccounts = null;
1307 
1308         
1309         for (PurApItem item : purDoc.getItems()) {
1310             if (item.getItemTypeCode().equals(PurapConstants.ItemTypeCodes.ITEM_TYPE_ORDER_DISCOUNT_CODE)) {
1311                 fullOrderDiscount = item;
1312             } else if (item.getItemTypeCode().equals(PurapConstants.ItemTypeCodes.ITEM_TYPE_TRADE_IN_CODE)) {
1313                 tradeIn = item;
1314             }
1315         }
1316         
1317         if (fullOrderDiscount != null &&
1318                 fullOrderDiscount.getExtendedPrice() != null &&
1319                 fullOrderDiscount.getExtendedPrice().isNonZero()) {
1320 
1321             
1322             KNSGlobalVariables.getMessageList().add("Full order discount accounts cleared and regenerated");
1323             fullOrderDiscount.getSourceAccountingLines().clear();
1324             
1325             totalAmount = purDoc.getTotalDollarAmountAboveLineItems().subtract(purDoc.getTotalTaxAmountAboveLineItems());
1326             totalTaxAmount = purDoc.getTotalTaxAmountAboveLineItems();
1327 
1328             
1329             purapAccountingService.updateAccountAmounts(purDoc);
1330 
1331             
1332             boolean salesTaxInd = SpringContext.getBean(ParameterService.class).getParameterValueAsBoolean(OleParameterConstants.PURCHASING_DOCUMENT.class, PurapParameterConstants.ENABLE_SALES_TAX_IND);
1333             boolean useTaxIndicator = purDoc.isUseTaxIndicator();
1334 
1335             if (salesTaxInd == true && (ObjectUtils.isNull(fullOrderDiscount.getItemTaxAmount()) && useTaxIndicator == false)) {
1336                 KualiDecimal discountAmount = fullOrderDiscount.getExtendedPrice();
1337                 KualiDecimal discountTaxAmount = discountAmount.divide(totalAmount).multiply(totalTaxAmount);
1338 
1339                 fullOrderDiscount.setItemTaxAmount(discountTaxAmount);
1340             }
1341 
1342             
1343             summaryAccounts = purapAccountingService.generateSummary(PurApItemUtils.getAboveTheLineOnly(purDoc.getItems()));
1344 
1345             if (summaryAccounts.size() == 0) {
1346                 if (purDoc.shouldGiveErrorForEmptyAccountsProration()) {
1347                     GlobalVariables.getMessageMap().putError(PurapConstants.ITEM_TAB_ERROR_PROPERTY, PurapKeyConstants.ERROR_SUMMARY_ACCOUNTS_LIST_EMPTY, "full order discount");
1348                 }
1349             } else {
1350                 
1351                 distributedAccounts = purapAccountingService.generateAccountDistributionForProration(summaryAccounts, totalAmount.add(totalTaxAmount), 2, fullOrderDiscount.getAccountingLineClass());
1352 
1353                 for (PurApAccountingLine distributedAccount : distributedAccounts) {
1354                     BigDecimal percent = distributedAccount.getAccountLinePercent();
1355                     BigDecimal roundedPercent = new BigDecimal(Math.round(percent.doubleValue()));
1356                     distributedAccount.setAccountLinePercent(roundedPercent);
1357                 }
1358 
1359                 
1360                 purapAccountingService.updateAccountAmountsWithTotal(distributedAccounts, totalAmount,
1361                         fullOrderDiscount.getTotalAmount());
1362 
1363                 fullOrderDiscount.setSourceAccountingLines(distributedAccounts);
1364             }
1365         } else if (fullOrderDiscount != null &&
1366                 (fullOrderDiscount.getExtendedPrice() == null || fullOrderDiscount.getExtendedPrice().isZero())) {
1367             fullOrderDiscount.getSourceAccountingLines().clear();
1368         }
1369 
1370         
1371         if (tradeIn != null && tradeIn.getExtendedPrice() != null && tradeIn.getExtendedPrice().isNonZero()) {
1372 
1373             tradeIn.getSourceAccountingLines().clear();
1374 
1375             totalAmount = purDoc.getTotalDollarAmountForTradeIn();
1376             KualiDecimal tradeInTotalAmount = tradeIn.getTotalAmount();
1377             
1378             purapAccountingService.updateAccountAmounts(purDoc);
1379 
1380             
1381             
1382             List<PurApItem> clonedTradeInItems = new ArrayList();
1383             Collection<String> objectSubTypesRequiringQty = SpringContext.getBean(ParameterService.class).getParameterValuesAsString(OleParameterConstants.PURCHASING_DOCUMENT.class, PurapParameterConstants.OBJECT_SUB_TYPES_REQUIRING_QUANTITY);
1384             Collection<String> purchasingObjectSubTypes = SpringContext.getBean(ParameterService.class).getParameterValuesAsString(OleParameterConstants.CAPITAL_ASSET_BUILDER_DOCUMENT.class, PurapParameterConstants.PURCHASING_OBJECT_SUB_TYPES);
1385 
1386             String tradeInCapitalObjectCode = SpringContext.getBean(ParameterService.class).getParameterValueAsString(PurapConstants.PURAP_NAMESPACE, "Document", "TRADE_IN_OBJECT_CODE_FOR_CAPITAL_ASSET");
1387             String tradeInCapitalLeaseObjCd = SpringContext.getBean(ParameterService.class).getParameterValueAsString(PurapConstants.PURAP_NAMESPACE, "Document", "TRADE_IN_OBJECT_CODE_FOR_CAPITAL_LEASE");
1388 
1389             for (PurApItem item : purDoc.getTradeInItems()) {
1390                 PurApItem cloneItem = (PurApItem) ObjectUtils.deepCopy(item);
1391                 List<PurApAccountingLine> sourceAccountingLines = cloneItem.getSourceAccountingLines();
1392                 for (PurApAccountingLine accountingLine : sourceAccountingLines) {
1393                     if (objectSubTypesRequiringQty.contains(accountingLine.getObjectCode().getFinancialObjectSubTypeCode())) {
1394                         accountingLine.setFinancialObjectCode(tradeInCapitalObjectCode);
1395                     } else if (purchasingObjectSubTypes.contains(accountingLine.getObjectCode().getFinancialObjectSubTypeCode())) {
1396                         accountingLine.setFinancialObjectCode(tradeInCapitalLeaseObjCd);
1397                     }
1398                 }
1399                 clonedTradeInItems.add(cloneItem);
1400             }
1401 
1402 
1403             summaryAccounts = purapAccountingService.generateSummary(clonedTradeInItems);
1404             if (summaryAccounts.size() == 0) {
1405                 if (purDoc.shouldGiveErrorForEmptyAccountsProration()) {
1406                     GlobalVariables.getMessageMap().putError(PurapConstants.ITEM_TAB_ERROR_PROPERTY, PurapKeyConstants.ERROR_SUMMARY_ACCOUNTS_LIST_EMPTY, "trade in");
1407                 }
1408             } else {
1409                 distributedAccounts = purapAccountingService.generateAccountDistributionForProration(summaryAccounts, totalAmount, 2, tradeIn.getAccountingLineClass());
1410                 for (PurApAccountingLine distributedAccount : distributedAccounts) {
1411                     BigDecimal percent = distributedAccount.getAccountLinePercent();
1412                     BigDecimal roundedPercent = new BigDecimal(Math.round(percent.doubleValue()));
1413                     distributedAccount.setAccountLinePercent(roundedPercent);
1414                     
1415                     resetAccountAmount(distributedAccount, tradeInTotalAmount);
1416                 }
1417                 tradeIn.setSourceAccountingLines(distributedAccounts);
1418             }
1419         }
1420     }
1421 
1422     private void resetAccountAmount(PurApAccountingLine distributedAccount, KualiDecimal tradeInTotalAmount) {
1423         BigDecimal pct = distributedAccount.getAccountLinePercent();
1424         BigDecimal amount = tradeInTotalAmount.bigDecimalValue().multiply(pct).divide(new BigDecimal(100));
1425         distributedAccount.setAmount(new KualiDecimal(amount));
1426     }
1427 
1428     @Override
1429     public void clearAllTaxes(PurchasingAccountsPayableDocument purapDoc) {
1430         if (purapDoc.getItems() != null) {
1431             for (int i = 0; i < purapDoc.getItems().size(); i++) {
1432                 PurApItem item = purapDoc.getItems().get(i);
1433                 if (purapDoc.isUseTaxIndicator()) {
1434                     item.setUseTaxItems(new ArrayList<PurApItemUseTax>());
1435                 } else {
1436                     item.setItemTaxAmount(null);
1437                 }
1438             }
1439         }
1440     }
1441 
1442     
1443 
1444 
1445 
1446 
1447 
1448 
1449     @Override
1450     public boolean isItemTypeConflictWithTaxPolicy(PurchasingDocument purchasingDocument, PurApItem item) {
1451         boolean conflict = false;
1452 
1453         String deliveryState = getDeliveryState(purchasingDocument);
1454         if (item.getItemType().isLineItemIndicator()) {
1455             if (item.getItemType().isTaxableIndicator()) {
1456                 if (isTaxDisabledForVendor(purchasingDocument)) {
1457                     conflict = true;
1458                 }
1459             }
1460             
1461             if (!item.getSourceAccountingLines().isEmpty()) {
1462                 if (!doesAccountAllowCallToTaxService(deliveryState, item)) {
1463                     conflict = true;
1464                 }
1465             }
1466         }
1467         return conflict;
1468     }
1469 
1470     
1471 
1472 
1473 
1474 
1475 
1476     protected boolean isTaxDisabledForVendor(PurchasingDocument purapDocument) {
1477         return false;
1478     }
1479 
1480     public PurapAccountingService getPurapAccountingService() {
1481         return purapAccountingService;
1482     }
1483 
1484     public void setPurapAccountingService(PurapAccountingService purapAccountingService) {
1485         this.purapAccountingService = purapAccountingService;
1486     }
1487 }
1488