View Javadoc
1   /*
2    * Copyright 2006 The Kuali Foundation
3    *
4    * Licensed under the Educational Community License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    * http://www.opensource.org/licenses/ecl2.php
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  package org.kuali.ole.module.purap.document.service.impl;
17  
18  import org.apache.commons.lang.StringUtils;
19  import org.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      * @see org.kuali.ole.module.purap.document.service.PurapService# updateStatus(org.kuali.ole.module.purap.document.PurchasingAccountsPayableDocument, String)
134      */
135     //TODO hjs: is this method really needed now that we don't have status history tables?
136   /*  @Override
137     public boolean updateStatus(PurchasingAccountsPayableDocument document, String newStatus) {
138         LOG.debug("updateStatus() started");
139 
140         if (ObjectUtils.isNotNull(document) || ObjectUtils.isNotNull(newStatus)) {
141             String oldStatus = document.getApplicationDocumentStatus();
142             document.setApplicationDocumentStatus(newStatus);
143             if ( LOG.isDebugEnabled() ) {
144                 LOG.debug("Status of document #" + document.getDocumentNumber() + " has been changed from " + oldStatus + " to " + newStatus);
145             }
146             return true;
147         }
148         else {
149             return false;
150         }
151     }*/
152     @Override
153     public void saveRoutingDataForRelatedDocuments(Integer accountsPayablePurchasingDocumentLinkIdentifier) {
154 
155         try {
156             //save requisition routing data
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             //save purchase order routing data
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             //save payment request routing data
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             //save Invoice routing data
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             //save credit memo routing data
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             //save line item receiving routing data
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             //save correction receiving routing data
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             //save bulk receiving routing data
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      * @see org.kuali.ole.module.purap.document.service.PurapService#getRelatedDocumentIds(Integer)
219      */
220     @Override
221     public List<String> getRelatedDocumentIds(Integer accountsPayablePurchasingDocumentLinkIdentifier) {
222         LOG.debug("getRelatedDocumentIds() started");
223         List<String> documentIdList = new ArrayList<String>();
224 
225         //get requisition views
226         List<RequisitionView> reqViews = getRelatedViews(RequisitionView.class, accountsPayablePurchasingDocumentLinkIdentifier);
227         for (RequisitionView view : reqViews) {
228             documentIdList.add(view.getDocumentNumber());
229         }
230 
231         //get purchase order views
232         List<PurchaseOrderView> poViews = getRelatedViews(PurchaseOrderView.class, accountsPayablePurchasingDocumentLinkIdentifier);
233         for (PurchaseOrderView view : poViews) {
234             documentIdList.add(view.getDocumentNumber());
235         }
236 
237         //get payment request views
238         List<PaymentRequestView> preqViews = getRelatedViews(PaymentRequestView.class, accountsPayablePurchasingDocumentLinkIdentifier);
239         for (PaymentRequestView view : preqViews) {
240             documentIdList.add(view.getDocumentNumber());
241         }
242 
243         //get Invoice views
244         List<InvoiceView> invViews = getRelatedViews(InvoiceView.class, accountsPayablePurchasingDocumentLinkIdentifier);
245         for (InvoiceView invView : invViews) {
246             documentIdList.add(invView.getDocumentNumber());
247         }
248 
249         //get credit memo views
250         List<CreditMemoView> cmViews = getRelatedViews(CreditMemoView.class, accountsPayablePurchasingDocumentLinkIdentifier);
251         for (CreditMemoView view : cmViews) {
252             documentIdList.add(view.getDocumentNumber());
253         }
254 
255         //get line item receiving views
256         List<LineItemReceivingView> lineViews = getRelatedViews(LineItemReceivingView.class, accountsPayablePurchasingDocumentLinkIdentifier);
257         for (LineItemReceivingView view : lineViews) {
258             documentIdList.add(view.getDocumentNumber());
259         }
260 
261         //get correction receiving views
262         List<CorrectionReceivingView> corrViews = getRelatedViews(CorrectionReceivingView.class, accountsPayablePurchasingDocumentLinkIdentifier);
263         for (CorrectionReceivingView view : corrViews) {
264             documentIdList.add(view.getDocumentNumber());
265         }
266 
267         //get bulk receiving views
268         List<BulkReceivingView> bulkViews = getRelatedViews(BulkReceivingView.class, accountsPayablePurchasingDocumentLinkIdentifier);
269         for (BulkReceivingView view : bulkViews) {
270             documentIdList.add(view.getDocumentNumber());
271         }
272 
273         //TODO (hjs)get electronic invoice reject views???
274 
275         return documentIdList;
276     }
277 
278     /**
279      * @see org.kuali.ole.module.purap.document.service.PurapService#getRelatedViews(Class, Integer)
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         //if (st.equals("InvoiceView") || st.equals("CreditMemoView") || st.equals("PaymentRequestView")) {
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         // retrieve in descending order of document number so that newer documents are in the front
308         List boList = (List) businessObjectService.findMatchingOrderBy(clazz, criteria, OLEPropertyConstants.DOCUMENT_NUMBER, false);
309         return boList;
310     }
311 
312     /**
313      * @see org.kuali.ole.module.purap.document.service.PurapService#addBelowLineItems(org.kuali.ole.module.purap.document.PurchasingAccountsPayableDocument)
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         // needed in case they get out of sync below won't work
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                     // do something
351                 }
352             }
353         }
354 
355         document.fixItemReferences();
356     }
357 
358     /**
359      * Sorts the below the line elements
360      *
361      * @param itemTypes
362      * @param existingItems
363      * @param belowTheLine
364      */
365     protected void sortBelowTheLine(String[] itemTypes, List<PurApItem> existingItems, List<PurApItem> belowTheLine) {
366         LOG.debug("sortBelowTheLine() started");
367 
368         // sort existing below the line if any
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      * @see org.kuali.ole.module.purap.document.service.PurapService#sortBelowTheLine(String[], java.util.List, java.util.List)
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         // needed in case they get out of sync below won't work
403         sortBelowTheLine(itemTypes, existingItems, belowTheLine);
404     }
405 
406     /**
407      * @see org.kuali.ole.module.purap.document.service.PurapService#getBelowTheLineForDocument(org.kuali.ole.module.purap.document.PurchasingAccountsPayableDocument)
408      */
409     @Override
410     public String[] getBelowTheLineForDocument(PurchasingAccountsPayableDocument document) {
411         LOG.debug("getBelowTheLineForDocument() started");
412 
413         // Obtain a list of below the line items from system parameter
414 //        String documentTypeClassName = document.getClass().getName();
415 //        String[] documentTypeArray = StringUtils.split(documentTypeClassName, ".");
416 //        String documentType = documentTypeArray[documentTypeArray.length - 1];
417 
418         //FIXME RELEASE 3 (hjs) why is this "if" here with no code in it?  is it supposed to be doing somethign?
419         // If it's a credit memo, we'll have to append the source of the credit memo
420         // whether it's created from a Vendor, a PO or a PREQ.
421 //        if (documentType.equals("CreditMemoDocument")) {
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      * @see org.kuali.ole.module.purap.document.service.PurapService#getBelowTheLineByType(org.kuali.ole.module.purap.document.PurchasingAccountsPayableDocument,
436      *      org.kuali.ole.module.purap.businessobject.ItemType)
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      * @see org.kuali.ole.module.purap.document.service.PurapService#getDateFromOffsetFromToday(int)
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      * @see org.kuali.ole.module.purap.document.service.PurapService#isDateInPast(java.sql.Date)
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      * @see org.kuali.ole.module.purap.document.service.PurapService#isDateMoreThanANumberOfDaysAway(java.sql.Date, int)
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      * @see org.kuali.ole.module.purap.document.service.PurapService#isDateAYearAfterToday(java.sql.Date)
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      * @see org.kuali.ole.module.purap.document.service.PurapService#getApoLimit(Integer, String, String)
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         // We didn't find the limit on the vendor contract, get it from the org parameter table.
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      * @see org.kuali.ole.module.purap.document.service.PurapService#isFullDocumentEntryCompleted(org.kuali.ole.module.purap.document.PurchasingAccountsPayableDocument)
554      */
555     @Override
556     public boolean isFullDocumentEntryCompleted(PurchasingAccountsPayableDocument purapDocument) {
557         LOG.debug("isFullDocumentEntryCompleted() started");
558 
559         // for now just return true if not in one of the first few states
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      * @see org.kuali.ole.module.purap.document.service.PurapService#isPaymentRequestFullDocumentEntryCompleted(String)
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      * @see org.kuali.ole.module.purap.document.service.PurapService#isVendorCreditMemoFullDocumentEntryCompleted(String)
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      * @see org.kuali.ole.module.purap.document.service.PurapService#isInvoiceFullDocumentEntryCompleted(String)
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      * Main hook point for close/Reopen PO.
600      *
601      * @see org.kuali.ole.module.purap.document.service.PurapService#performLogicForCloseReopenPO(org.kuali.ole.module.purap.document.PurchasingAccountsPayableDocument)
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                 // get the po id and get the current po
612                 // check the current po: if status is not closed and there is no pending action... route close po as system user
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                 // get the po id and get the current PO
621                 // route 'Re-Open PO Document' if PO criteria meets requirements from business rules
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      * Remove items that have not been "entered" which means no data has been added to them so no more processing needs to continue
633      * on these items.
634      *
635      * @param apDocument AccountsPayableDocument which contains list of items to be reviewed
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      * Actual method that will close or reopen a po.
652      *
653      * @param apDocument AccountsPayableDocument
654      * @param docType
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         // setup text for note that will be created, will either be closed or reopened
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             // we are skipping the validation above because it would be too late to correct any errors (i.e. because in
680             // post-processing)
681             purchaseOrderService.createAndRoutePotentialChangeDocument(purchaseOrderDocument.getDocumentNumber(), docType, assemblePurchaseOrderNote(apDocument, docType, action), new ArrayList(), newStatus);
682         }
683 
684         /*
685          * if we made it here, route document has not errored out, so set appropriate indicator depending on what is being
686          * requested.
687          */
688         if (PurapConstants.PurchaseOrderDocTypes.PURCHASE_ORDER_CLOSE_DOCUMENT.equals(docType)) {
689             apDocument.setClosePurchaseOrderIndicator(false);
690 
691             //add a note to the purchase order indicating it has been closed by a payment request document
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             //save the note to the purchase order
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      * Generate a note for the close/reopen po method.
718      *
719      * @param docType
720      * @param preqId
721      * @return Note to be saved
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      * @see org.kuali.ole.module.purap.document.service.PurapService#performLogicWithFakedUserSession(String, org.kuali.ole.module.purap.document.service.LogicContainer, Object[])
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      * @see org.kuali.ole.module.purap.document.service.PurchaseOrderService#saveDocumentNoValidation(org.kuali.ole.module.purap.document.PurchaseOrderDocument)
768      */
769     @Override
770     public void saveDocumentNoValidation(Document document) {
771         try {
772             // FIXME The following code of refreshing document header is a temporary fix for the issue that
773             // in some cases (seem random) the doc header fields are null; and if doc header is refreshed, the workflow doc becomes null.
774             // The root cause of this is that when some docs are retrieved manually using OJB criteria, ref objs such as doc header or workflow doc
775             // aren't retrieved; the solution would be to add these refreshing when documents are retrieved in those OJB methods.
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             // At this point, the work-flow status will not change for the current document, but the document status will.
784             // This causes the search indices for the document to become out of synch, and will show a different status type
785             // in the RICE lookup results screen.
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      * @see org.kuali.ole.module.purap.document.service.PurapService#isDocumentStoppedInRouteNode(org.kuali.ole.module.purap.document.PurchasingAccountsPayableDocument, String)
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      * @see org.kuali.ole.module.purap.document.service.PurapService#allowEncumberNextFiscalYear()
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      * @see org.kuali.ole.module.purap.document.service.PurapService#getAllowedFiscalYears()
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      * @see org.kuali.ole.module.purap.document.service.PurapService#isTodayWithinApoAllowedRange()
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      * @see org.kuali.ole.module.purap.document.service.PurapService#clearTax(org.kuali.ole.module.purap.document.PurchasingAccountsPayableDocument)
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             //i.e. if the indicator changed clear out the tax
886             clearTax(purapDocument, currentUseTaxIndicator);
887         }
888         purapDocument.setUseTaxIndicator(newUseTaxIndicatorValue);
889     }
890 
891     /**
892      * @see org.kuali.ole.module.purap.document.service.PurapService#calculateTax(org.kuali.ole.module.purap.document.PurchasingAccountsPayableDocument)
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         //calculate if sales tax enabled for purap
904         if (salesTaxInd || useTaxIndicator) {
905             //iterate over items and calculate tax if taxable
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      * Determines if the item is taxable based on a decision tree.
945      *
946      * @param useTaxIndicator
947      * @param deliveryState
948      * @param item
949      * @return
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      * @see org.kuali.ole.module.purap.document.service.PurapService#isTaxableForSummary(boolean, String, org.kuali.ole.module.purap.businessobject.PurApItem)
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      * Determines if the the tax service should be called due to the commodity code.
985      *
986      * @param item
987      * @return
988      */
989     protected boolean doesCommodityAllowCallToTaxService(PurApItem item) {
990         boolean callService = true;
991 
992         // only check for commodity code on above the line times (additional charges don't allow commodity code)
993         if (item.getItemType().isLineItemIndicator()) {
994             if (item instanceof PurchasingItem) {
995                 PurchasingItemBase purItem = (PurchasingItemBase) item;
996                 callService = isCommodityCodeTaxable(purItem.getCommodityCode());
997             }// if not a purchasing item, then pull item from PO
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                 //not taxable, so don't call service
1017                 isTaxable = false;
1018             }//if true we want to call service
1019 
1020         }//if null, return true
1021 
1022         return isTaxable;
1023     }
1024 
1025     /**
1026      * @see org.kuali.ole.module.purap.document.service.PurapService#isDeliveryStateTaxable(String)
1027      */
1028     @Override
1029     public boolean isDeliveryStateTaxable(String deliveryState) {
1030         ParameterEvaluator parmEval = /*REFACTORME*/SpringContext.getBean(ParameterEvaluatorService.class).getParameterEvaluator(OleParameterConstants.PURCHASING_DOCUMENT.class, TaxParameters.TAXABLE_DELIVERY_STATES, deliveryState);
1031         return parmEval.evaluationSucceeds();
1032     }
1033 
1034     /**
1035      * Checks if the account is taxable, based on the delivery state, fund/subfund groups, and object code level/consolidations.
1036      *
1037      * @param deliveryState
1038      * @param item
1039      * @return
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      * @see org.kuali.ole.module.purap.document.service.PurapService#isAccountingLineTaxable(org.kuali.ole.module.purap.businessobject.PurApAccountingLine, boolean)
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         // is account (fund/subfund) and object code (level/consolidation) taxable?
1070         if (isAccountTaxable(parameterSuffix, acctLine) && isObjectCodeTaxable(parameterSuffix, acctLine)) {
1071             isTaxable = true;
1072         }
1073 
1074         return isTaxable;
1075     }
1076 
1077     /**
1078      * Checks if the account fund/subfund groups are in a set of parameters taking into account allowed/denied constraints and
1079      * ultimately determines if taxable.
1080      *
1081      * @param parameterSuffix
1082      * @param acctLine
1083      * @return
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      * Checks if the object code level/consolidation groups are in a set of parameters taking into account allowed/denied constraints and
1113      * ultimately determines if taxable.
1114      *
1115      * @param parameterSuffix
1116      * @param acctLine
1117      * @return
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         //refresh financial object level
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      * Helper method to work with parameter evaluator to find, allowed and found in parameter value.
1146      *
1147      * @param eval
1148      * @return
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      * Helper method to work with parameter evaluator to find, allowed and not found in parameter value.
1162      *
1163      * @param eval
1164      * @return
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      * Helper method to work with parameter evaluator to find, denied and found in parameter value.
1178      *
1179      * @param eval
1180      * @return
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      * Helper method to work with parameter evaluator to find, denied and not found in parameter value.
1194      *
1195      * @param eval
1196      * @return
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      * @param useTaxIndicator
1210      * @param deliveryPostalCode
1211      * @param transactionTaxDate
1212      * @param item
1213      * @param itemUseTaxClass
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                          * Shallow.. This never happen - InstantiationException/IllegalAccessException
1252                          * To be safe, throw a runtime exception
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         // iterate over items and calculate tax if taxable
1270         for (PurApItem item : purapDocument.getItems()) {
1271             if (item.getItemType().isLineItemIndicator()) {
1272                 //only when extended price exists
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         //check nonzero so no divide by zero errors, and make sure extended price is not null
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         //TODO: are we throwing sufficient errors in this method?
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         // iterate through below the line and grab FoD and TrdIn.
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         // If Discount is not null or zero get proration list for all non misc items and set (if not empty?)
1317         if (fullOrderDiscount != null &&
1318                 fullOrderDiscount.getExtendedPrice() != null &&
1319                 fullOrderDiscount.getExtendedPrice().isNonZero()) {
1320 
1321             // empty
1322             KNSGlobalVariables.getMessageList().add("Full order discount accounts cleared and regenerated");
1323             fullOrderDiscount.getSourceAccountingLines().clear();
1324             //total amount is pretax dollars
1325             totalAmount = purDoc.getTotalDollarAmountAboveLineItems().subtract(purDoc.getTotalTaxAmountAboveLineItems());
1326             totalTaxAmount = purDoc.getTotalTaxAmountAboveLineItems();
1327 
1328             //Before we generate account summary, we should update the account amounts first.
1329             purapAccountingService.updateAccountAmounts(purDoc);
1330 
1331             //calculate tax
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             //generate summary
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                 //prorate accounts
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                 //update amounts on distributed accounts
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         // If tradeIn is not null or zero get proration list for all non misc items and set (if not empty?)
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             //Before we generate account summary, we should update the account amounts first.
1378             purapAccountingService.updateAccountAmounts(purDoc);
1379 
1380             //Before generating the summary, lets replace the object code in a cloned accounts collection sothat we can
1381             //consolidate all the modified object codes during summary generation.
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                     // set the accountAmount same as tradeIn amount not line item's amount
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      * Determines if the item type specified conflict with the Account tax policy.
1444      *
1445      * @param purchasingDocument purchasing document to check
1446      * @param item               item to check if in conflict with tax policy
1447      * @return true if item is in conflict, false otherwise
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             // only check account tax policy if accounting line exists
1461             if (!item.getSourceAccountingLines().isEmpty()) {
1462                 if (!doesAccountAllowCallToTaxService(deliveryState, item)) {
1463                     conflict = true;
1464                 }
1465             }
1466         }
1467         return conflict;
1468     }
1469 
1470     /**
1471      * Determines if tax is disabled for vendor, in default always returns false
1472      *
1473      * @param purapDocument the PurchasingDocument with a vendor to check
1474      * @return true if tax is disabled, false if it is not - in foundation OLE, tax is never disabled
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