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.collections.CollectionUtils;
19  import org.apache.commons.lang.StringUtils;
20  import org.apache.commons.lang.text.StrBuilder;
21  import org.kuali.ole.module.purap.*;
22  import org.kuali.ole.module.purap.PurapConstants.ItemTypeCodes;
23  import org.kuali.ole.module.purap.PurapConstants.PREQDocumentsStrings;
24  import org.kuali.ole.module.purap.PurapConstants.PaymentRequestStatuses;
25  import org.kuali.ole.module.purap.PurapParameterConstants.NRATaxParameters;
26  import org.kuali.ole.module.purap.businessobject.*;
27  import org.kuali.ole.module.purap.document.AccountsPayableDocument;
28  import org.kuali.ole.module.purap.document.PaymentRequestDocument;
29  import org.kuali.ole.module.purap.document.PurchaseOrderDocument;
30  import org.kuali.ole.module.purap.document.VendorCreditMemoDocument;
31  import org.kuali.ole.module.purap.document.dataaccess.PaymentRequestDao;
32  import org.kuali.ole.module.purap.document.service.*;
33  import org.kuali.ole.module.purap.document.validation.event.AttributedContinuePurapEvent;
34  import org.kuali.ole.module.purap.exception.PurError;
35  import org.kuali.ole.module.purap.service.PurapAccountingService;
36  import org.kuali.ole.module.purap.service.PurapGeneralLedgerService;
37  import org.kuali.ole.module.purap.util.ExpiredOrClosedAccountEntry;
38  import org.kuali.ole.module.purap.util.PurApItemUtils;
39  import org.kuali.ole.module.purap.util.VendorGroupingHelper;
40  import org.kuali.ole.select.OleSelectConstant;
41  import org.kuali.ole.sys.OLEConstants;
42  import org.kuali.ole.sys.OLEPropertyConstants;
43  import org.kuali.ole.sys.businessobject.AccountingLine;
44  import org.kuali.ole.sys.businessobject.Bank;
45  import org.kuali.ole.sys.businessobject.SourceAccountingLine;
46  import org.kuali.ole.sys.context.SpringContext;
47  import org.kuali.ole.sys.service.BankService;
48  import org.kuali.ole.sys.service.FinancialSystemWorkflowHelperService;
49  import org.kuali.ole.sys.service.UniversityDateService;
50  import org.kuali.ole.sys.service.impl.OleParameterConstants;
51  import org.kuali.ole.vnd.VendorConstants;
52  import org.kuali.ole.vnd.businessobject.PaymentTermType;
53  import org.kuali.ole.vnd.businessobject.VendorAddress;
54  import org.kuali.ole.vnd.businessobject.VendorDetail;
55  import org.kuali.ole.vnd.document.service.VendorService;
56  import org.kuali.rice.core.api.config.property.ConfigurationService;
57  import org.kuali.rice.core.api.datetime.DateTimeService;
58  import org.kuali.rice.core.api.util.type.KualiDecimal;
59  import org.kuali.rice.coreservice.framework.parameter.ParameterService;
60  import org.kuali.rice.kew.api.KewApiServiceLocator;
61  import org.kuali.rice.kew.api.WorkflowDocument;
62  import org.kuali.rice.kew.api.document.search.DocumentSearchCriteria;
63  import org.kuali.rice.kew.api.document.search.DocumentSearchResult;
64  import org.kuali.rice.kew.api.document.search.DocumentSearchResults;
65  import org.kuali.rice.kew.api.exception.WorkflowException;
66  import org.kuali.rice.kim.api.identity.Person;
67  import org.kuali.rice.kns.service.DataDictionaryService;
68  import org.kuali.rice.krad.bo.DocumentHeader;
69  import org.kuali.rice.krad.bo.Note;
70  import org.kuali.rice.krad.exception.ValidationException;
71  import org.kuali.rice.krad.service.BusinessObjectService;
72  import org.kuali.rice.krad.service.DocumentService;
73  import org.kuali.rice.krad.service.KualiRuleService;
74  import org.kuali.rice.krad.service.NoteService;
75  import org.kuali.rice.krad.util.GlobalVariables;
76  import org.kuali.rice.krad.util.KRADPropertyConstants;
77  import org.kuali.rice.krad.util.ObjectUtils;
78  import org.kuali.rice.krad.workflow.service.WorkflowDocumentService;
79  import org.springframework.transaction.annotation.Transactional;
80  
81  import java.math.BigDecimal;
82  import java.sql.Date;
83  import java.sql.Timestamp;
84  import java.util.*;
85  
86  
87  
88  
89  @Transactional
90  public class PaymentRequestServiceImpl implements PaymentRequestService {
91      private static final org.apache.log4j.Logger LOG = org.apache.log4j.Logger.getLogger(PaymentRequestServiceImpl.class);
92  
93      protected DateTimeService dateTimeService;
94      protected DocumentService documentService;
95      protected NoteService noteService;
96      protected PurapService purapService;
97      protected PaymentRequestDao paymentRequestDao;
98      protected ParameterService parameterService;
99      protected ConfigurationService configurationService;
100     protected NegativePaymentRequestApprovalLimitService negativePaymentRequestApprovalLimitService;
101     protected PurapAccountingService purapAccountingService;
102     protected BusinessObjectService businessObjectService;
103     protected PurApWorkflowIntegrationService purapWorkflowIntegrationService;
104     protected WorkflowDocumentService workflowDocumentService;
105     protected AccountsPayableService accountsPayableService;
106     protected VendorService vendorService;
107     protected DataDictionaryService dataDictionaryService;
108     protected UniversityDateService universityDateService;
109     protected BankService bankService;
110     protected PurchaseOrderService purchaseOrderService;
111     protected FinancialSystemWorkflowHelperService financialSystemWorkflowHelperService;
112     protected KualiRuleService kualiRuleService;
113     protected boolean currencyTypeIndicator = true;
114     private static transient OlePurapService olePurapService;
115 
116     public static OlePurapService getOlePurapService() {
117         if (olePurapService == null) {
118             olePurapService = SpringContext.getBean(OlePurapService.class);
119         }
120         return olePurapService;
121     }
122 
123     
124 
125 
126 
127 
128     @Override
129     @Deprecated
130     public Collection<PaymentRequestDocument> getPaymentRequestsToExtractByCM(String campusCode, VendorCreditMemoDocument cmd) {
131         LOG.debug("getPaymentRequestsByCM() started");
132         Date currentSqlDateMidnight = dateTimeService.getCurrentSqlDateMidnight();
133         List<PaymentRequestDocument> paymentRequestIterator = paymentRequestDao.getPaymentRequestsToExtract(campusCode, null, null, cmd.getVendorHeaderGeneratedIdentifier(), cmd.getVendorDetailAssignedIdentifier(), currentSqlDateMidnight);
134 
135         return filterPaymentRequestByAppDocStatus(paymentRequestIterator,
136                 PurapConstants.PaymentRequestStatuses.APPDOC_AUTO_APPROVED,
137                 PurapConstants.PaymentRequestStatuses.APPDOC_DEPARTMENT_APPROVED);
138     }
139 
140 
141     
142 
143 
144 
145     @Override
146     public Collection<PaymentRequestDocument> getPaymentRequestsToExtractByVendor(String campusCode, VendorGroupingHelper vendor, Date onOrBeforePaymentRequestPayDate) {
147         LOG.debug("getPaymentRequestsByVendor() started");
148         Collection<PaymentRequestDocument> paymentRequestDocuments = paymentRequestDao.getPaymentRequestsToExtractForVendor(campusCode, vendor, onOrBeforePaymentRequestPayDate);
149 
150         return filterPaymentRequestByAppDocStatus(paymentRequestDocuments,
151                 PurapConstants.PaymentRequestStatuses.APPDOC_AUTO_APPROVED,
152                 PurapConstants.PaymentRequestStatuses.APPDOC_DEPARTMENT_APPROVED);
153     }
154 
155     
156 
157 
158     @Override
159     public Collection<PaymentRequestDocument> getPaymentRequestsToExtract(Date onOrBeforePaymentRequestPayDate) {
160         LOG.debug("getPaymentRequestsToExtract() started");
161 
162         Collection<PaymentRequestDocument> paymentRequestIterator = paymentRequestDao.getPaymentRequestsToExtract(false, null, onOrBeforePaymentRequestPayDate);
163         return filterPaymentRequestByAppDocStatus(paymentRequestIterator,
164                 PaymentRequestStatuses.STATUSES_ALLOWED_FOR_EXTRACTION);
165     }
166 
167     
168 
169 
170 
171     @Override
172     public Collection<PaymentRequestDocument> getPaymentRequestsToExtractSpecialPayments(String chartCode, Date onOrBeforePaymentRequestPayDate) {
173         LOG.debug("getPaymentRequestsToExtractSpecialPayments() started");
174 
175         Collection<PaymentRequestDocument> paymentRequestIterator = paymentRequestDao.getPaymentRequestsToExtract(true, chartCode, onOrBeforePaymentRequestPayDate);
176         return filterPaymentRequestByAppDocStatus(paymentRequestIterator,
177                 PaymentRequestStatuses.STATUSES_ALLOWED_FOR_EXTRACTION);
178     }
179 
180     
181 
182 
183     @Override
184     public Collection<PaymentRequestDocument> getImmediatePaymentRequestsToExtract(String chartCode) {
185         LOG.debug("getImmediatePaymentRequestsToExtract() started");
186 
187         Collection<PaymentRequestDocument> paymentRequestIterator = paymentRequestDao.getImmediatePaymentRequestsToExtract(chartCode);
188         return filterPaymentRequestByAppDocStatus(paymentRequestIterator,
189                 PaymentRequestStatuses.STATUSES_ALLOWED_FOR_EXTRACTION);
190     }
191 
192     
193 
194 
195 
196     @Override
197     public Collection<PaymentRequestDocument> getPaymentRequestToExtractByChart(String chartCode, Date onOrBeforePaymentRequestPayDate) {
198         LOG.debug("getPaymentRequestToExtractByChart() started");
199 
200         Collection<PaymentRequestDocument> paymentRequestIterator = paymentRequestDao.getPaymentRequestsToExtract(false, chartCode, onOrBeforePaymentRequestPayDate);
201         return filterPaymentRequestByAppDocStatus(paymentRequestIterator,
202                 PaymentRequestStatuses.STATUSES_ALLOWED_FOR_EXTRACTION);
203     }
204 
205     
206 
207 
208     @Override
209     public boolean autoApprovePaymentRequests() {
210         if (LOG.isInfoEnabled()) {
211             LOG.info("Starting autoApprovePaymentRequests.");
212         }
213         boolean hadErrorAtLeastOneError = true;
214         
215         Date todayAtMidnight = dateTimeService.getCurrentSqlDateMidnight();
216 
217         List<String> docNumbers = paymentRequestDao.getEligibleForAutoApproval(todayAtMidnight);
218         docNumbers = filterPaymentRequestByAppDocStatus(docNumbers, PurapConstants.PaymentRequestStatuses.PREQ_STATUSES_FOR_AUTO_APPROVE);
219         if (LOG.isInfoEnabled()) {
220             LOG.info(" -- Initial filtering complete, returned " + new Integer(docNumbers.size()).toString() + " docs.");
221         }
222 
223         List<PaymentRequestDocument> docs = new ArrayList<PaymentRequestDocument>();
224         for (String docNumber : docNumbers) {
225             PaymentRequestDocument preq = getPaymentRequestByDocumentNumber(docNumber);
226             if (ObjectUtils.isNotNull(preq)) {
227                 docs.add(preq);
228             }
229         }
230         if (LOG.isInfoEnabled()) {
231             LOG.info(" -- Initial filtering complete, returned " + new Integer((docs == null ? 0 : docs.size())).toString() + " docs.");
232         }
233         if (docs != null) {
234             String samt = parameterService.getParameterValueAsString(PaymentRequestDocument.class, PurapParameterConstants.PURAP_DEFAULT_NEGATIVE_PAYMENT_REQUEST_APPROVAL_LIMIT);
235             KualiDecimal defaultMinimumLimit = new KualiDecimal(samt);
236             if (LOG.isInfoEnabled()) {
237                 LOG.info(" -- Using default limit value of " + defaultMinimumLimit.toString() + ".");
238             }
239             for (PaymentRequestDocument paymentRequestDocument : docs) {
240                 hadErrorAtLeastOneError |= !autoApprovePaymentRequest(paymentRequestDocument, defaultMinimumLimit);
241             }
242         }
243         return hadErrorAtLeastOneError;
244     }
245 
246     
247 
248 
249 
250 
251 
252 
253     @Override
254     public boolean autoApprovePaymentRequest(String docNumber, KualiDecimal defaultMinimumLimit) {
255         PaymentRequestDocument paymentRequestDocument = null;
256         try {
257             paymentRequestDocument = (PaymentRequestDocument) documentService.getByDocumentHeaderId(docNumber);
258             if (paymentRequestDocument.isHoldIndicator() || paymentRequestDocument.isPaymentRequestedCancelIndicator() || !Arrays.asList(PurapConstants.PaymentRequestStatuses.PREQ_STATUSES_FOR_AUTO_APPROVE).contains(paymentRequestDocument.getApplicationDocumentStatus())) {
259                 
260                 
261                 
262                 
263                 
264                 
265 
266                 
267                 
268                 LOG.warn("Payment Request Document " + paymentRequestDocument.getDocumentNumber() + " could not be auto-approved because it has either been placed on hold, " + " requested cancel, or does not have one of the PREQ statuses for auto-approve.");
269                 return true;
270             }
271             if (autoApprovePaymentRequest(paymentRequestDocument, defaultMinimumLimit)) {
272                 if (LOG.isInfoEnabled()) {
273                     LOG.info("Auto-approval for payment request successful.  Doc number: " + docNumber);
274                 }
275                 return true;
276             } else {
277                 LOG.error("Payment Request Document " + docNumber + " could not be auto-approved.");
278                 return false;
279             }
280         } catch (WorkflowException we) {
281             LOG.error("Exception encountered when retrieving document number " + docNumber + ".", we);
282             
283             throw new RuntimeException("Exception encountered when retrieving document number " + docNumber + ".", we);
284         }
285     }
286 
287     
288 
289 
290 
291 
292 
293 
294     @Override
295     public boolean autoApprovePaymentRequest(PaymentRequestDocument doc, KualiDecimal defaultMinimumLimit) {
296         if (isEligibleForAutoApproval(doc, defaultMinimumLimit)) {
297             try {
298                 
299                 
300                 
301                 
302                 
303                 
304 
305                 
306                 
307                 
308                 try {
309                     ObjectUtils.materializeUpdateableCollections(doc);
310                     for (PaymentRequestItem item : (List<PaymentRequestItem>) doc.getItems()) {
311                         ObjectUtils.materializeUpdateableCollections(item);
312                     }
313                 } catch (Exception ex) {
314                     throw new RuntimeException(ex);
315                 }
316                 doc = (PaymentRequestDocument) ObjectUtils.deepCopy(doc);
317                 
318                 doc.updateAndSaveAppDocStatus(PaymentRequestStatuses.APPDOC_AUTO_APPROVED);
319 
320                 documentService.blanketApproveDocument(doc, "auto-approving: Total is below threshold.", null);
321             } catch (WorkflowException we) {
322                 LOG.error("Exception encountered when approving document number " + doc.getDocumentNumber() + ".", we);
323                 
324                 throw new RuntimeException("Exception encountered when approving document number " + doc.getDocumentNumber() + ".", we);
325             }
326         }
327         return true;
328     }
329 
330     public boolean autoApprovePaymentRequest(PaymentRequestDocument doc) {
331         try {
332             
333             
334             
335             
336             
337             
338 
339             
340             
341             
342             try {
343                 ObjectUtils.materializeUpdateableCollections(doc);
344                 for (PaymentRequestItem item : (List<PaymentRequestItem>) doc.getItems()) {
345                     ObjectUtils.materializeUpdateableCollections(item);
346                 }
347             } catch (Exception ex) {
348                 throw new RuntimeException(ex);
349             }
350             doc = (PaymentRequestDocument) ObjectUtils.deepCopy(doc);
351             
352             doc.updateAndSaveAppDocStatus(PaymentRequestStatuses.APPDOC_AUTO_APPROVED);
353 
354             documentService.blanketApproveDocument(doc, "auto-approving: Total is below threshold.", null);
355         } catch (WorkflowException we) {
356             LOG.error("Exception encountered when approving document number " + doc.getDocumentNumber() + ".", we);
357             
358             throw new RuntimeException("Exception encountered when approving document number " + doc.getDocumentNumber() + ".", we);
359         }
360         return true;
361     }
362 
363     
364 
365 
366 
367 
368 
369 
370 
371 
372 
373     protected boolean isEligibleForAutoApproval(PaymentRequestDocument document, KualiDecimal defaultMinimumLimit) {
374         
375         if (document.getVendorDetail().getCurrencyType()!=null){
376             if(document.getVendorDetail().getCurrencyType().getCurrencyType().equalsIgnoreCase(OleSelectConstant.CURRENCY_TYPE_NAME)){
377                 currencyTypeIndicator=true;
378             }
379             else{
380                 currencyTypeIndicator=false;
381             }
382         }
383         if (!currencyTypeIndicator) {
384             if (LOG.isInfoEnabled()) {
385                 LOG.info(" -- PayReq [" + document.getDocumentNumber() + "] skipped due to a Foreign Vendor.");
386             }
387             return false;
388         }
389 
390         
391         if (purapWorkflowIntegrationService.willDocumentStopAtGivenFutureRouteNode(document, PaymentRequestStatuses.NODE_VENDOR_TAX_REVIEW)) {
392             if (LOG.isInfoEnabled()) {
393                 LOG.info(" -- PayReq [" + document.getDocumentNumber() + "] skipped due to requiring Tax Review.");
394             }
395             return false;
396         }
397 
398         
399         if (document.isPaymentRequestPositiveApprovalIndicator()) {
400             if (LOG.isInfoEnabled()) {
401                 LOG.info(" -- PayReq [" + document.getDocumentNumber()
402                         + "] skipped due to a Positive Approval Required Indicator set to Yes.");
403             }
404             return false;
405         }
406 
407         
408         
409         
410         KualiDecimal minimumAmount = null;
411 
412         
413         
414         
415         for (SourceAccountingLine line : purapAccountingService.generateSummary(document.getItems())) {
416             
417             Map<String, Object> autoApproveMap = new HashMap<String, Object>();
418             autoApproveMap.put("chartOfAccountsCode", line.getChartOfAccountsCode());
419             autoApproveMap.put("accountNumber", line.getAccountNumber());
420             autoApproveMap.put("active", true);
421             AutoApproveExclude autoApproveExclude = businessObjectService.findByPrimaryKey(AutoApproveExclude.class, autoApproveMap);
422             if (autoApproveExclude != null) {
423                 if (LOG.isInfoEnabled()) {
424                     LOG.info(" -- PayReq [" + document.getDocumentNumber() + "] skipped due to source accounting line "
425                             + line.getSequenceNumber() + " using Chart/Account [" + line.getChartOfAccountsCode() + "-"
426                             + line.getAccountNumber() + "], which is excluded in the Auto Approve Exclusions table.");
427                 }
428                 return false;
429             }
430 
431             minimumAmount = getMinimumLimitAmount(negativePaymentRequestApprovalLimitService.findByChart(line.getChartOfAccountsCode()), minimumAmount);
432             minimumAmount = getMinimumLimitAmount(negativePaymentRequestApprovalLimitService.findByChartAndAccount(line.getChartOfAccountsCode(), line.getAccountNumber()), minimumAmount);
433             minimumAmount = getMinimumLimitAmount(negativePaymentRequestApprovalLimitService.findByChartAndOrganization(line.getChartOfAccountsCode(), line.getOrganizationReferenceId()), minimumAmount);
434         }
435 
436         
437         if (document.isReceivingDocumentRequiredIndicator()) {
438             if (LOG.isInfoEnabled()) {
439                 LOG.info(" -- PayReq ["
440                         + document.getDocumentNumber()
441                         + "] auto-approved (ignored dollar limit) due to Receiving Document Required Indicator set to Yes.");
442             }
443             return true;
444         }
445 
446         
447         if (ObjectUtils.isNull(minimumAmount) || defaultMinimumLimit.compareTo(minimumAmount) < 0) {
448             minimumAmount = defaultMinimumLimit;
449         }
450 
451         
452         if (document.getFinancialSystemDocumentHeader().getFinancialDocumentTotalAmount().isLessThan(minimumAmount)) {
453             if (LOG.isInfoEnabled()) {
454                 LOG.info(" -- PayReq ["
455                         + document.getDocumentNumber()
456                         + "] auto-approved due to document Total ["
457                         + document.getFinancialSystemDocumentHeader().getFinancialDocumentTotalAmount()
458                         + "] being less than "
459                         + (minimumAmount == defaultMinimumLimit ? "Default Auto-Approval Limit "
460                         : "Configured Auto-Approval Limit ") + "of "
461                         + (minimumAmount == null ? "null" : minimumAmount.toString()) + ".");
462             }
463             return true;
464         }
465 
466         if (LOG.isInfoEnabled()) {
467             LOG.info(" -- PayReq ["
468                     + document.getDocumentNumber()
469                     + "] skipped due to document Total ["
470                     + document.getFinancialSystemDocumentHeader().getFinancialDocumentTotalAmount()
471                     + "] being greater than "
472                     + (minimumAmount == defaultMinimumLimit ? "Default Auto-Approval Limit "
473                     : "Configured Auto-Approval Limit ") + "of "
474                     + (minimumAmount == null ? "null" : minimumAmount.toString()) + ".");
475         }
476 
477         return false;
478     }
479 
480     
481 
482 
483 
484 
485 
486 
487 
488 
489     protected KualiDecimal getMinimumLimitAmount(Collection<NegativePaymentRequestApprovalLimit> limits, KualiDecimal minimumAmount) {
490         for (NegativePaymentRequestApprovalLimit limit : limits) {
491             KualiDecimal amount = limit.getNegativePaymentRequestApprovalLimitAmount();
492             if (null == minimumAmount) {
493                 minimumAmount = amount;
494             } else if (minimumAmount.isGreaterThan(amount)) {
495                 minimumAmount = amount;
496             }
497         }
498         return minimumAmount;
499     }
500 
501     
502 
503 
504 
505 
506 
507 
508 
509     @Override
510     public List getPaymentRequestsByVendorNumber(Integer vendorHeaderGeneratedId, Integer vendorDetailAssignedId) {
511         LOG.debug("getActivePaymentRequestsByVendorNumber() started");
512         return paymentRequestDao.getActivePaymentRequestsByVendorNumber(vendorHeaderGeneratedId, vendorDetailAssignedId);
513     }
514 
515     
516 
517 
518 
519 
520 
521 
522 
523     @Override
524     public List getPaymentRequestsByVendorNumberInvoiceNumber(Integer vendorHeaderGeneratedId, Integer vendorDetailAssignedId, String invoiceNumber) {
525         LOG.debug("getActivePaymentRequestsByVendorNumberInvoiceNumber() started");
526         return paymentRequestDao.getActivePaymentRequestsByVendorNumberInvoiceNumber(vendorHeaderGeneratedId, vendorDetailAssignedId, invoiceNumber);
527     }
528 
529     
530 
531 
532     @Override
533     public HashMap<String, String> paymentRequestDuplicateMessages(PaymentRequestDocument document) {
534         HashMap<String, String> msgs;
535         msgs = new HashMap<String, String>();
536 
537         Integer purchaseOrderId = document.getPurchaseOrderIdentifier();
538 
539         if (ObjectUtils.isNotNull(document.getInvoiceDate())) {
540             if (purapService.isDateAYearBeforeToday(document.getInvoiceDate())) {
541                 msgs.put(PREQDocumentsStrings.DUPLICATE_INVOICE_QUESTION, configurationService.getPropertyValueAsString(PurapKeyConstants.MESSAGE_INVOICE_DATE_A_YEAR_OR_MORE_PAST));
542             }
543         }
544         PurchaseOrderDocument po = document.getPurchaseOrderDocument();
545 
546         if (po != null) {
547             Integer vendorDetailAssignedId = po.getVendorDetailAssignedIdentifier();
548             Integer vendorHeaderGeneratedId = po.getVendorHeaderGeneratedIdentifier();
549 
550             List<PaymentRequestDocument> preqs = new ArrayList();
551 
552             List<PaymentRequestDocument> preqsDuplicates = getPaymentRequestsByVendorNumber(vendorHeaderGeneratedId, vendorDetailAssignedId);
553             for (PaymentRequestDocument duplicatePREQ : preqsDuplicates) {
554                 if (duplicatePREQ!=null && duplicatePREQ.getInvoiceNumber()!=null &&
555                         document!=null && document.getInvoiceNumber()!=null &&
556                         duplicatePREQ.getInvoiceNumber().toUpperCase().equals(document.getInvoiceNumber().toUpperCase())) {
557                     
558                     preqs.add(duplicatePREQ);
559                 }
560             }
561 
562             if (preqs.size() > 0) {
563                 boolean addedMessage = false;
564                 boolean foundCanceledPostApprove = false; 
565                 boolean foundCanceledPreApprove = false; 
566                 for (PaymentRequestDocument testPREQ : preqs) {
567                     if (StringUtils.equals(testPREQ.getApplicationDocumentStatus(), PaymentRequestStatuses.APPDOC_CANCELLED_POST_AP_APPROVE)) {
568                         foundCanceledPostApprove |= true;
569                     } else if (StringUtils.equals(testPREQ.getApplicationDocumentStatus(), PaymentRequestStatuses.APPDOC_CANCELLED_IN_PROCESS)) {
570                         foundCanceledPreApprove |= true;
571                     } else {
572                         msgs.put(PREQDocumentsStrings.DUPLICATE_INVOICE_QUESTION, configurationService.getPropertyValueAsString(PurapKeyConstants.MESSAGE_DUPLICATE_INVOICE));
573                         addedMessage = true;
574                         break;
575                     }
576                 }
577                 
578                 if (!addedMessage) {
579                     if (foundCanceledPostApprove && foundCanceledPreApprove) {
580                         msgs.put(PREQDocumentsStrings.DUPLICATE_INVOICE_QUESTION, configurationService.getPropertyValueAsString(PurapKeyConstants.MESSAGE_DUPLICATE_INVOICE_CANCELLEDORVOIDED));
581                     } else if (foundCanceledPreApprove) {
582                         msgs.put(PREQDocumentsStrings.DUPLICATE_INVOICE_QUESTION, configurationService.getPropertyValueAsString(PurapKeyConstants.MESSAGE_DUPLICATE_INVOICE_VOIDED));
583                     } else if (foundCanceledPostApprove) {
584                         
585                         msgs.put(PREQDocumentsStrings.DUPLICATE_INVOICE_QUESTION, configurationService.getPropertyValueAsString(PurapKeyConstants.MESSAGE_DUPLICATE_INVOICE_CANCELLED));
586                     }
587                 }
588             }
589 
590             
591             preqs = getPaymentRequestsByPOIdInvoiceAmountInvoiceDate(purchaseOrderId, document.getVendorInvoiceAmount(), document.getInvoiceDate());
592             if (preqs.size() > 0) {
593                 boolean addedMessage = false;
594                 boolean foundCanceledPostApprove = false; 
595                 boolean foundCanceledPreApprove = false; 
596                 msgs.put(PREQDocumentsStrings.DUPLICATE_INVOICE_QUESTION, configurationService.getPropertyValueAsString(PurapKeyConstants.MESSAGE_DUPLICATE_INVOICE_DATE_AMOUNT));
597                 for (PaymentRequestDocument testPREQ : preqs) {
598                     if (StringUtils.equalsIgnoreCase(testPREQ.getApplicationDocumentStatus(), PaymentRequestStatuses.APPDOC_CANCELLED_POST_AP_APPROVE)) {
599                         foundCanceledPostApprove |= true;
600                     } else if (StringUtils.equalsIgnoreCase(testPREQ.getApplicationDocumentStatus(), PaymentRequestStatuses.APPDOC_CANCELLED_IN_PROCESS)) {
601                         foundCanceledPreApprove |= true;
602                     } else {
603                         msgs.put(PREQDocumentsStrings.DUPLICATE_INVOICE_QUESTION, configurationService.getPropertyValueAsString(PurapKeyConstants.MESSAGE_DUPLICATE_INVOICE_DATE_AMOUNT));
604                         addedMessage = true;
605                         break;
606                     }
607                 }
608 
609                 
610                 if (!addedMessage) {
611                     if (foundCanceledPostApprove && foundCanceledPreApprove) {
612                         msgs.put(PREQDocumentsStrings.DUPLICATE_INVOICE_QUESTION, configurationService.getPropertyValueAsString(PurapKeyConstants.MESSAGE_DUPLICATE_INVOICE_DATE_AMOUNT_CANCELLEDORVOIDED));
613                     } else if (foundCanceledPreApprove) {
614                         msgs.put(PREQDocumentsStrings.DUPLICATE_INVOICE_QUESTION, configurationService.getPropertyValueAsString(PurapKeyConstants.MESSAGE_DUPLICATE_INVOICE_DATE_AMOUNT_VOIDED));
615                         addedMessage = true;
616                     } else if (foundCanceledPostApprove) {
617                         msgs.put(PREQDocumentsStrings.DUPLICATE_INVOICE_QUESTION, configurationService.getPropertyValueAsString(PurapKeyConstants.MESSAGE_DUPLICATE_INVOICE_DATE_AMOUNT_CANCELLED));
618                         addedMessage = true;
619                     }
620 
621                 }
622             }
623         }
624         return msgs;
625     }
626 
627     
628 
629 
630     @Override
631     public PaymentRequestDocument getPaymentRequestByDocumentNumber(String documentNumber) {
632         LOG.debug("getPaymentRequestByDocumentNumber() started");
633 
634         if (ObjectUtils.isNotNull(documentNumber)) {
635             try {
636                 PaymentRequestDocument doc = (PaymentRequestDocument) documentService.getByDocumentHeaderId(documentNumber);
637                 return doc;
638             } catch (WorkflowException e) {
639                 String errorMessage = "Error getting payment request document from document service";
640                 LOG.error("getPaymentRequestByDocumentNumber() " + errorMessage, e);
641                 throw new RuntimeException(errorMessage, e);
642             }
643         }
644         return null;
645     }
646 
647     
648 
649 
650     @Override
651     public PaymentRequestDocument getPaymentRequestById(Integer poDocId) {
652         return getPaymentRequestByDocumentNumber(paymentRequestDao.getDocumentNumberByPaymentRequestId(poDocId));
653     }
654 
655     
656 
657 
658     @Override
659     public List<PaymentRequestDocument> getPaymentRequestsByPurchaseOrderId(Integer poDocId) {
660         List<PaymentRequestDocument> preqs = new ArrayList<PaymentRequestDocument>();
661         List<String> docNumbers = paymentRequestDao.getDocumentNumbersByPurchaseOrderId(poDocId);
662         for (String docNumber : docNumbers) {
663             PaymentRequestDocument preq = getPaymentRequestByDocumentNumber(docNumber);
664             if (ObjectUtils.isNotNull(preq)) {
665                 preqs.add(preq);
666             }
667         }
668         return preqs;
669     }
670 
671     
672 
673 
674 
675     @Override
676     public List<PaymentRequestDocument> getPaymentRequestsByPOIdInvoiceAmountInvoiceDate(Integer poId, KualiDecimal invoiceAmount, Date invoiceDate) {
677         LOG.debug("getPaymentRequestsByPOIdInvoiceAmountInvoiceDate() started");
678         return paymentRequestDao.getActivePaymentRequestsByPOIdInvoiceAmountInvoiceDate(poId, invoiceAmount, invoiceDate);
679     }
680 
681     
682 
683 
684     @Override
685     public boolean isInvoiceDateAfterToday(Date invoiceDate) {
686         
687         Calendar now = Calendar.getInstance();
688         now.set(Calendar.HOUR, 11);
689         now.set(Calendar.MINUTE, 59);
690         now.set(Calendar.SECOND, 59);
691         now.set(Calendar.MILLISECOND, 59);
692         Timestamp nowTime = new Timestamp(now.getTimeInMillis());
693         Calendar invoiceDateC = Calendar.getInstance();
694         invoiceDateC.setTime(invoiceDate);
695         
696         invoiceDateC.set(Calendar.HOUR, 0);
697         invoiceDateC.set(Calendar.MINUTE, 0);
698         invoiceDateC.set(Calendar.SECOND, 0);
699         invoiceDateC.set(Calendar.MILLISECOND, 0);
700         Timestamp invoiceDateTime = new Timestamp(invoiceDateC.getTimeInMillis());
701         return ((invoiceDateTime.compareTo(nowTime)) > 0);
702     }
703 
704     
705 
706 
707 
708     @Override
709     public java.sql.Date calculatePayDate(Date invoiceDate, PaymentTermType terms) {
710         LOG.debug("calculatePayDate() started");
711         
712         Calendar invoicedDateCalendar = dateTimeService.getCalendar(invoiceDate);
713         Calendar processedDateCalendar = dateTimeService.getCurrentCalendar();
714 
715         
716         String defaultDays = parameterService.getParameterValueAsString(PaymentRequestDocument.class, PurapParameterConstants.PURAP_PREQ_PAY_DATE_DEFAULT_NUMBER_OF_DAYS);
717         processedDateCalendar.add(Calendar.DAY_OF_MONTH, Integer.parseInt(defaultDays));
718 
719         if (ObjectUtils.isNull(terms) || StringUtils.isEmpty(terms.getVendorPaymentTermsCode())) {
720             invoicedDateCalendar.add(Calendar.DAY_OF_MONTH, PurapConstants.PREQ_PAY_DATE_EMPTY_TERMS_DEFAULT_DAYS);
721             return returnLaterDate(invoicedDateCalendar, processedDateCalendar);
722         }
723 
724         Integer discountDueNumber = terms.getVendorDiscountDueNumber();
725         Integer netDueNumber = terms.getVendorNetDueNumber();
726         if (ObjectUtils.isNotNull(discountDueNumber)) {
727             String discountDueTypeDescription = terms.getVendorDiscountDueTypeDescription();
728             paymentTermsDateCalculation(discountDueTypeDescription, invoicedDateCalendar, discountDueNumber);
729         } else if (ObjectUtils.isNotNull(netDueNumber)) {
730             String netDueTypeDescription = terms.getVendorNetDueTypeDescription();
731             paymentTermsDateCalculation(netDueTypeDescription, invoicedDateCalendar, netDueNumber);
732         } else {
733             throw new RuntimeException("Neither discount or net number were specified for this payment terms type");
734         }
735 
736         
737         return returnLaterDate(invoicedDateCalendar, processedDateCalendar);
738     }
739 
740     
741 
742 
743 
744 
745 
746 
747     protected java.sql.Date returnLaterDate(Calendar invoicedDateCalendar, Calendar processedDateCalendar) {
748         if (invoicedDateCalendar.after(processedDateCalendar)) {
749             return new java.sql.Date(invoicedDateCalendar.getTimeInMillis());
750         } else {
751             return new java.sql.Date(processedDateCalendar.getTimeInMillis());
752         }
753     }
754 
755     
756 
757 
758 
759 
760 
761 
762     protected void paymentTermsDateCalculation(String dueTypeDescription, Calendar invoicedDateCalendar, Integer dueNumber) {
763 
764         if (StringUtils.equals(dueTypeDescription, PurapConstants.PREQ_PAY_DATE_DATE)) {
765             
766             invoicedDateCalendar.add(Calendar.MONTH, 1);
767             invoicedDateCalendar.set(Calendar.DAY_OF_MONTH, dueNumber.intValue());
768         } else if (StringUtils.equals(PurapConstants.PREQ_PAY_DATE_DAYS, dueTypeDescription)) {
769             
770             invoicedDateCalendar.add(Calendar.DAY_OF_MONTH, dueNumber.intValue());
771         } else {
772             
773             throw new RuntimeException("missing payment terms description or not properly enterred on payment term maintenance doc");
774         }
775     }
776 
777     
778 
779 
780 
781     @Override
782     public void calculatePaymentRequest(PaymentRequestDocument paymentRequest, boolean updateDiscount) {
783         LOG.debug("calculatePaymentRequest() started");
784 
785         
786         if (ObjectUtils.isNull(paymentRequest.getPaymentRequestPayDate())) {
787             paymentRequest.setPaymentRequestPayDate(calculatePayDate(paymentRequest.getInvoiceDate(), paymentRequest.getVendorPaymentTerms()));
788         }
789 
790         distributeAccounting(paymentRequest);
791 
792         purapService.calculateTax(paymentRequest);
793 
794         
795         purapService.prorateForTradeInAndFullOrderDiscount(paymentRequest);
796 
797         
798         if (updateDiscount) {
799             calculateDiscount(paymentRequest);
800         }
801 
802         distributeAccounting(paymentRequest);
803     }
804 
805 
806     
807 
808 
809 
810 
811     protected void calculateDiscount(PaymentRequestDocument paymentRequestDocument) {
812         PaymentRequestItem discountItem = findDiscountItem(paymentRequestDocument);
813         
814         PaymentTermType pt = paymentRequestDocument.getVendorPaymentTerms();
815         if ((pt != null) && (pt.getVendorPaymentTermsPercent() != null) && (BigDecimal.ZERO.compareTo(pt.getVendorPaymentTermsPercent()) != 0)) {
816             if (discountItem == null) {
817                 
818                 
819                 
820                 purapService.addBelowLineItems(paymentRequestDocument);
821 
822                 
823                 removeIneligibleAdditionalCharges(paymentRequestDocument);
824 
825                 discountItem = findDiscountItem(paymentRequestDocument);
826             }
827 
828             
829             PaymentRequestItem fullOrderItem = findFullOrderDiscountItem(paymentRequestDocument);
830             KualiDecimal fullOrderAmount = KualiDecimal.ZERO;
831             KualiDecimal fullOrderTaxAmount = KualiDecimal.ZERO;
832 
833             if (fullOrderItem != null) {
834                 fullOrderAmount = (ObjectUtils.isNotNull(fullOrderItem.getExtendedPrice())) ? fullOrderItem.getExtendedPrice() : KualiDecimal.ZERO;
835                 fullOrderTaxAmount = (ObjectUtils.isNotNull(fullOrderItem.getItemTaxAmount())) ? fullOrderItem.getItemTaxAmount() : KualiDecimal.ZERO;
836             }
837             KualiDecimal totalCost = paymentRequestDocument.getTotalPreTaxDollarAmountAboveLineItems().add(fullOrderAmount);
838             PurApItem tradeInItem = paymentRequestDocument.getTradeInItem();
839             if (ObjectUtils.isNotNull(tradeInItem)) {
840                 totalCost = totalCost.subtract(tradeInItem.getTotalAmount());
841             }
842             BigDecimal discountAmount = pt.getVendorPaymentTermsPercent().multiply(totalCost.bigDecimalValue()).multiply(new BigDecimal(PurapConstants.PREQ_DISCOUNT_MULT));
843 
844             
845             discountItem.setItemUnitPrice(discountAmount.setScale(2, KualiDecimal.ROUND_BEHAVIOR));
846             discountItem.setExtendedPrice(new KualiDecimal(discountAmount));
847 
848             
849             boolean salesTaxInd = parameterService.getParameterValueAsBoolean(OleParameterConstants.PURCHASING_DOCUMENT.class, PurapParameterConstants.ENABLE_SALES_TAX_IND);
850             boolean useTaxIndicator = paymentRequestDocument.isUseTaxIndicator();
851 
852             if (salesTaxInd == true && useTaxIndicator == false) {
853                 KualiDecimal totalTax = paymentRequestDocument.getTotalTaxAmountAboveLineItems().add(fullOrderTaxAmount);
854                 BigDecimal discountTaxAmount = null;
855                 if (totalCost.isNonZero()) {
856                     discountTaxAmount = discountAmount.divide(totalCost.bigDecimalValue()).multiply(totalTax.bigDecimalValue());
857                 } else {
858                     discountTaxAmount = BigDecimal.ZERO;
859                 }
860 
861                 discountItem.setItemTaxAmount(new KualiDecimal(discountTaxAmount.setScale(KualiDecimal.SCALE, KualiDecimal.ROUND_BEHAVIOR)));
862             }
863 
864             
865             discountItem.setPurapDocument(paymentRequestDocument);
866         } else { 
867             if (discountItem != null) {
868                 paymentRequestDocument.getItems().remove(discountItem);
869             }
870         }
871 
872     }
873 
874 
875     @Override
876     public void clearTax(PaymentRequestDocument document) {
877         
878         removeTaxItems(document);
879         
880         document.setTaxClassificationCode(null);
881         document.setTaxFederalPercent(null);
882         document.setTaxStatePercent(null);
883         document.setTaxCountryCode(null);
884         document.setTaxNQIId(null);
885 
886         document.setTaxForeignSourceIndicator(false);
887         document.setTaxExemptTreatyIndicator(false);
888         document.setTaxOtherExemptIndicator(false);
889         document.setTaxGrossUpIndicator(false);
890         document.setTaxUSAIDPerDiemIndicator(false);
891         document.setTaxSpecialW4Amount(null);
892 
893     }
894 
895     
896 
897 
898     @Override
899     public void calculateTaxArea(PaymentRequestDocument preq) {
900         LOG.debug("calculateTaxArea() started");
901 
902         
903         removeTaxItems(preq);
904 
905         
906         if (StringUtils.equalsIgnoreCase(preq.getTaxClassificationCode(), "N")) {
907             return;
908         }
909 
910         
911         
912         BigDecimal taxableAmount = preq.getGrandPreTaxTotal().bigDecimalValue();
913 
914         
915         
916         if (preq.getTaxGrossUpIndicator() && preq.getTaxStatePercent().compareTo(new BigDecimal(0)) != 0) {
917             PurApItem stateGrossItem = addTaxItem(preq, ItemTypeCodes.ITEM_TYPE_STATE_GROSS_CODE, taxableAmount);
918         }
919 
920         
921         if (preq.getTaxStatePercent().compareTo(new BigDecimal(0)) != 0) {
922             PurApItem stateTaxItem = addTaxItem(preq, ItemTypeCodes.ITEM_TYPE_STATE_TAX_CODE, taxableAmount);
923         }
924 
925         
926         
927         if (preq.getTaxGrossUpIndicator() && preq.getTaxFederalPercent().compareTo(new BigDecimal(0)) != 0) {
928             PurApItem federalGrossItem = addTaxItem(preq, ItemTypeCodes.ITEM_TYPE_FEDERAL_GROSS_CODE, taxableAmount);
929         }
930 
931         
932         if (preq.getTaxFederalPercent().compareTo(new BigDecimal(0)) != 0) {
933             PurApItem federalTaxItem = addTaxItem(preq, ItemTypeCodes.ITEM_TYPE_FEDERAL_TAX_CODE, taxableAmount);
934         }
935 
936         
937         
938         
939     }
940 
941     
942 
943 
944 
945 
946     protected void removeTaxItems(PaymentRequestDocument preq) {
947         List<PurApItem> items = preq.getItems();
948         for (int i = 0; i < items.size(); i++) {
949             PurApItem item = items.get(i);
950             String code = item.getItemTypeCode();
951             if (ItemTypeCodes.ITEM_TYPE_FEDERAL_TAX_CODE.equals(code) || ItemTypeCodes.ITEM_TYPE_STATE_TAX_CODE.equals(code) || ItemTypeCodes.ITEM_TYPE_FEDERAL_GROSS_CODE.equals(code) || ItemTypeCodes.ITEM_TYPE_STATE_GROSS_CODE.equals(code)) {
952                 items.remove(i--);
953             }
954         }
955     }
956 
957     
958 
959 
960 
961 
962 
963 
964 
965     protected PurApItem addTaxItem(PaymentRequestDocument preq, String itemTypeCode, BigDecimal taxableAmount) {
966         PurApItem taxItem = null;
967 
968         try {
969             taxItem = (PurApItem) preq.getItemClass().newInstance();
970         } catch (IllegalAccessException e) {
971             throw new IllegalArgumentException("Unable to access itemClass", e);
972         } catch (InstantiationException e) {
973             throw new IllegalArgumentException("Unable to instantiate itemClass", e);
974         }
975 
976         
977         taxItem.setItemTypeCode(itemTypeCode);
978         preq.addItem(taxItem);
979 
980         
981         PurApAccountingLine taxLine = addTaxAccountingLine(taxItem, taxableAmount);
982 
983         
984         taxItem.setItemUnitPrice(taxLine.getAmount().bigDecimalValue());
985         taxItem.setExtendedPrice(taxLine.getAmount());
986 
987         
988         ItemType itemType = new ItemType();
989         itemType.setItemTypeCode(itemTypeCode);
990         itemType = (ItemType) businessObjectService.retrieve(itemType);
991         taxItem.setItemType(itemType);
992         taxItem.setItemDescription(itemType.getItemTypeDescription());
993 
994         return taxItem;
995     }
996 
997     
998 
999 
1000 
1001 
1002 
1003 
1004     protected PurApAccountingLine addTaxAccountingLine(PurApItem taxItem, BigDecimal taxableAmount) {
1005         PaymentRequestDocument preq = taxItem.getPurapDocument();
1006         PurApAccountingLine taxLine = null;
1007 
1008         try {
1009             taxLine = (PurApAccountingLine) taxItem.getAccountingLineClass().newInstance();
1010         } catch (IllegalAccessException e) {
1011             throw new IllegalArgumentException("Unable to access sourceAccountingLineClass", e);
1012         } catch (InstantiationException e) {
1013             throw new IllegalArgumentException("Unable to instantiate sourceAccountingLineClass", e);
1014         }
1015 
1016         
1017         boolean isFederalTax = ItemTypeCodes.ITEM_TYPE_FEDERAL_TAX_CODE.equals(taxItem.getItemTypeCode());
1018         boolean isFederalGross = ItemTypeCodes.ITEM_TYPE_FEDERAL_GROSS_CODE.equals(taxItem.getItemTypeCode());
1019         boolean isStateTax = ItemTypeCodes.ITEM_TYPE_STATE_TAX_CODE.equals(taxItem.getItemTypeCode());
1020         boolean isStateGross = ItemTypeCodes.ITEM_TYPE_STATE_GROSS_CODE.equals(taxItem.getItemTypeCode());
1021         boolean isFederal = isFederalTax || isFederalGross; 
1022         boolean isGross = isFederalGross || isStateGross; 
1023 
1024         
1025         String taxChart = null;
1026         String taxAccount = null;
1027         String taxObjectCode = null;
1028 
1029         if (isGross) {
1030             
1031             AccountingLine line1 = preq.getFirstAccount();
1032             taxChart = line1.getChartOfAccountsCode();
1033             taxAccount = line1.getAccountNumber();
1034             taxObjectCode = line1.getFinancialObjectCode();
1035         } else if (isFederalTax) {
1036             
1037             taxChart = parameterService.getParameterValueAsString(PaymentRequestDocument.class, NRATaxParameters.FEDERAL_TAX_PARM_PREFIX + NRATaxParameters.TAX_PARM_CHART_SUFFIX);
1038             taxAccount = parameterService.getParameterValueAsString(PaymentRequestDocument.class, NRATaxParameters.FEDERAL_TAX_PARM_PREFIX + NRATaxParameters.TAX_PARM_ACCOUNT_SUFFIX);
1039             taxObjectCode = parameterService.getSubParameterValueAsString(PaymentRequestDocument.class, NRATaxParameters.FEDERAL_TAX_PARM_PREFIX + NRATaxParameters.TAX_PARM_OBJECT_BY_INCOME_CLASS_SUFFIX, preq.getTaxClassificationCode());
1040             if (StringUtils.isBlank(taxChart) || StringUtils.isBlank(taxAccount) || StringUtils.isBlank(taxObjectCode)) {
1041                 LOG.error("Unable to retrieve federal tax parameters.");
1042                 throw new RuntimeException("Unable to retrieve federal tax parameters.");
1043             }
1044         } else if (isStateTax) {
1045             
1046             taxChart = parameterService.getParameterValueAsString(PaymentRequestDocument.class, NRATaxParameters.STATE_TAX_PARM_PREFIX + NRATaxParameters.TAX_PARM_CHART_SUFFIX);
1047             taxAccount = parameterService.getParameterValueAsString(PaymentRequestDocument.class, NRATaxParameters.STATE_TAX_PARM_PREFIX + NRATaxParameters.TAX_PARM_ACCOUNT_SUFFIX);
1048             taxObjectCode = parameterService.getSubParameterValueAsString(PaymentRequestDocument.class, NRATaxParameters.STATE_TAX_PARM_PREFIX + NRATaxParameters.TAX_PARM_OBJECT_BY_INCOME_CLASS_SUFFIX, preq.getTaxClassificationCode());
1049             if (StringUtils.isBlank(taxChart) || StringUtils.isBlank(taxAccount) || StringUtils.isBlank(taxObjectCode)) {
1050                 LOG.error("Unable to retrieve state tax parameters.");
1051                 throw new RuntimeException("Unable to retrieve state tax parameters.");
1052             }
1053         }
1054 
1055         
1056         
1057 
1058 
1059 
1060 
1061 
1062         
1063         BigDecimal taxPercentFederal = preq.getTaxFederalPercent();
1064         BigDecimal taxPercentState = preq.getTaxStatePercent();
1065         BigDecimal taxPercent = isFederal ? taxPercentFederal : taxPercentState;
1066 
1067         
1068         BigDecimal taxDivider = new BigDecimal(100);
1069         if (preq.getTaxGrossUpIndicator()) {
1070             taxDivider = taxDivider.subtract(taxPercentFederal.add(taxPercentState));
1071         }
1072 
1073         
1074         BigDecimal taxAmount = taxableAmount.multiply(taxPercent);
1075         taxAmount = taxAmount.divide(taxDivider, 5, BigDecimal.ROUND_HALF_UP);
1076 
1077         
1078         if (!isGross) {
1079             taxAmount = taxAmount.negate();
1080         }
1081 
1082         
1083         taxLine.setDocumentNumber(preq.getDocumentNumber());
1084         taxLine.setSequenceNumber(preq.getNextSourceLineNumber());
1085         taxLine.setChartOfAccountsCode(taxChart);
1086         taxLine.setAccountNumber(taxAccount);
1087         taxLine.setFinancialObjectCode(taxObjectCode);
1088         taxLine.setAmount(new KualiDecimal(taxAmount));
1089 
1090         
1091         taxLine.setItemIdentifier(taxItem.getItemIdentifier());
1092         taxLine.setPurapItem(taxItem);
1093         taxItem.getSourceAccountingLines().add(taxLine);
1094 
1095         return taxLine;
1096     }
1097 
1098     
1099 
1100 
1101 
1102 
1103 
1104     protected PaymentRequestItem findDiscountItem(PaymentRequestDocument paymentRequestDocument) {
1105         PaymentRequestItem discountItem = null;
1106         for (PaymentRequestItem preqItem : (List<PaymentRequestItem>) paymentRequestDocument.getItems()) {
1107             if (StringUtils.equals(preqItem.getItemTypeCode(), PurapConstants.ItemTypeCodes.ITEM_TYPE_PMT_TERMS_DISCOUNT_CODE)) {
1108                 discountItem = preqItem;
1109                 break;
1110             }
1111         }
1112         return discountItem;
1113     }
1114 
1115     
1116 
1117 
1118 
1119 
1120 
1121     protected PaymentRequestItem findFullOrderDiscountItem(PaymentRequestDocument paymentRequestDocument) {
1122         PaymentRequestItem discountItem = null;
1123         for (PaymentRequestItem preqItem : (List<PaymentRequestItem>) paymentRequestDocument.getItems()) {
1124             if (StringUtils.equals(preqItem.getItemTypeCode(), PurapConstants.ItemTypeCodes.ITEM_TYPE_ORDER_DISCOUNT_CODE)) {
1125                 discountItem = preqItem;
1126                 break;
1127             }
1128         }
1129         return discountItem;
1130     }
1131 
1132     
1133 
1134 
1135 
1136 
1137     protected void distributeAccounting(PaymentRequestDocument paymentRequestDocument) {
1138         
1139         purapAccountingService.updateAccountAmounts(paymentRequestDocument);
1140 
1141         String accountDistributionMethod = paymentRequestDocument.getAccountDistributionMethod();
1142 
1143         for (PaymentRequestItem item : (List<PaymentRequestItem>) paymentRequestDocument.getItems()) {
1144             KualiDecimal totalAmount = KualiDecimal.ZERO;
1145             List<PurApAccountingLine> distributedAccounts = null;
1146             List<SourceAccountingLine> summaryAccounts = null;
1147             Set excludedItemTypeCodes = new HashSet();
1148             excludedItemTypeCodes.add(PurapConstants.ItemTypeCodes.ITEM_TYPE_PMT_TERMS_DISCOUNT_CODE);
1149 
1150             
1151             if (item.getItemType().isLineItemIndicator()) {
1152                 continue;
1153             }
1154 
1155             if ((item.getSourceAccountingLines().isEmpty()) && (ObjectUtils.isNotNull(item.getExtendedPrice())) && (KualiDecimal.ZERO.compareTo(item.getExtendedPrice()) != 0)) {
1156                 if ((StringUtils.equals(PurapConstants.ItemTypeCodes.ITEM_TYPE_PMT_TERMS_DISCOUNT_CODE, item.getItemType().getItemTypeCode())) && (paymentRequestDocument.getGrandTotal() != null) && ((KualiDecimal.ZERO.compareTo(paymentRequestDocument.getGrandTotal()) != 0))) {
1157 
1158                     
1159                     
1160 
1161                     
1162                     totalAmount = paymentRequestDocument.getLineItemTotal();
1163 
1164                     
1165                     Set includedItemTypeCodes = new HashSet();
1166                     includedItemTypeCodes.add(PurapConstants.ItemTypeCodes.ITEM_TYPE_ITEM_CODE);
1167                     includedItemTypeCodes.add(PurapConstants.ItemTypeCodes.ITEM_TYPE_SERVICE_CODE);
1168 
1169                     summaryAccounts = purapAccountingService.generateSummaryIncludeItemTypesAndNoZeroTotals(paymentRequestDocument.getItems(), includedItemTypeCodes);
1170                     
1171                     
1172                     
1173                     if (summaryAccounts != null) {
1174                         distributedAccounts = purapAccountingService.generateAccountDistributionForProration(summaryAccounts, totalAmount, PurapConstants.PRORATION_SCALE, PaymentRequestAccount.class);
1175                     }
1176                     
1177 
1178 
1179 
1180 
1181 
1182 
1183 
1184 
1185 
1186 
1187 
1188 
1189 
1190                 } else {
1191                     PurchaseOrderItem poi = item.getPurchaseOrderItem();
1192                     if ((poi != null) && (poi.getSourceAccountingLines() != null) && (!(poi.getSourceAccountingLines().isEmpty())) && (poi.getExtendedPrice() != null) && ((KualiDecimal.ZERO.compareTo(poi.getExtendedPrice())) != 0)) {
1193                         
1194                         
1195                         item.generateAccountListFromPoItemAccounts(poi.getSourceAccountingLines());
1196                     } else {
1197                         totalAmount = paymentRequestDocument.getPurchaseOrderDocument().getTotalDollarAmountAboveLineItems();
1198                         purapAccountingService.updateAccountAmounts(paymentRequestDocument.getPurchaseOrderDocument());
1199                         summaryAccounts = purapAccountingService.generateSummary(PurApItemUtils.getAboveTheLineOnly(paymentRequestDocument.getPurchaseOrderDocument().getItems()));
1200                         distributedAccounts = purapAccountingService.generateAccountDistributionForProration(summaryAccounts, totalAmount, new Integer("6"), PaymentRequestAccount.class);
1201                     }
1202                 }
1203                 if (CollectionUtils.isNotEmpty(distributedAccounts) && CollectionUtils.isEmpty(item.getSourceAccountingLines())) {
1204                     item.setSourceAccountingLines(distributedAccounts);
1205                 }
1206             }
1207             
1208             purapAccountingService.updateItemAccountAmounts(item);
1209         }
1210 
1211         
1212         
1213         purapAccountingService.updateAccountAmounts(paymentRequestDocument);
1214     }
1215 
1216     
1217 
1218 
1219 
1220     @Override
1221     public PaymentRequestDocument addHoldOnPaymentRequest(PaymentRequestDocument document, String note) throws Exception {
1222         
1223         Note noteObj = documentService.createNoteFromDocument(document, note);
1224         document.addNote(noteObj);
1225         noteService.save(noteObj);
1226 
1227         
1228 
1229         document.setHoldIndicator(true);
1230         document.setLastActionPerformedByPersonId(GlobalVariables.getUserSession().getPerson().getPrincipalId());
1231         purapService.saveDocumentNoValidation(document);
1232 
1233         return document;
1234     }
1235 
1236     
1237 
1238 
1239     @Override
1240     public PaymentRequestDocument removeHoldOnPaymentRequest(PaymentRequestDocument document, String note) throws Exception {
1241         
1242         Note noteObj = documentService.createNoteFromDocument(document, note);
1243         document.addNote(noteObj);
1244         noteService.save(noteObj);
1245 
1246         
1247 
1248         document.setHoldIndicator(false);
1249         document.setLastActionPerformedByPersonId(null);
1250         purapService.saveDocumentNoValidation(document);
1251 
1252         return document;
1253     }
1254 
1255     
1256 
1257 
1258 
1259     @Override
1260     public void requestCancelOnPaymentRequest(PaymentRequestDocument document, String note) throws Exception {
1261         
1262         Note noteObj = documentService.createNoteFromDocument(document, note);
1263         document.addNote(noteObj);
1264         noteService.save(noteObj);
1265 
1266         
1267         document.setPaymentRequestedCancelIndicator(true);
1268         document.setLastActionPerformedByPersonId(GlobalVariables.getUserSession().getPerson().getPrincipalId());
1269         document.setAccountsPayableRequestCancelIdentifier(GlobalVariables.getUserSession().getPerson().getPrincipalId());
1270         purapService.saveDocumentNoValidation(document);
1271     }
1272 
1273     
1274 
1275 
1276     @Override
1277     public void removeRequestCancelOnPaymentRequest(PaymentRequestDocument document, String note) throws Exception {
1278         
1279         Note noteObj = documentService.createNoteFromDocument(document, note);
1280         document.addNote(noteObj);
1281         noteService.save(noteObj);
1282 
1283         clearRequestCancelFields(document);
1284 
1285         purapService.saveDocumentNoValidation(document);
1286 
1287     }
1288 
1289     
1290 
1291 
1292 
1293 
1294     protected void clearRequestCancelFields(PaymentRequestDocument document) {
1295         document.setPaymentRequestedCancelIndicator(false);
1296         document.setLastActionPerformedByPersonId(null);
1297         document.setAccountsPayableRequestCancelIdentifier(null);
1298     }
1299 
1300     
1301 
1302 
1303     @Override
1304     public boolean isExtracted(PaymentRequestDocument document) {
1305         return (ObjectUtils.isNull(document.getExtractedTimestamp()) ? false : true);
1306     }
1307 
1308     protected boolean isBeingAdHocRouted(PaymentRequestDocument document) {
1309         return financialSystemWorkflowHelperService.isAdhocApprovalRequestedForPrincipal(document.getDocumentHeader().getWorkflowDocument(), GlobalVariables.getUserSession().getPrincipalId());
1310     }
1311 
1312     
1313 
1314 
1315 
1316     @Override
1317     public void cancelExtractedPaymentRequest(PaymentRequestDocument paymentRequest, String note) {
1318         LOG.debug("cancelExtractedPaymentRequest() started");
1319         if (PaymentRequestStatuses.CANCELLED_STATUSES.contains(paymentRequest.getApplicationDocumentStatus())) {
1320             LOG.debug("cancelExtractedPaymentRequest() ended");
1321             return;
1322         }
1323 
1324         try {
1325             Note cancelNote = documentService.createNoteFromDocument(paymentRequest, note);
1326             paymentRequest.addNote(cancelNote);
1327             noteService.save(cancelNote);
1328         } catch (Exception e) {
1329             throw new RuntimeException(PurapConstants.REQ_UNABLE_TO_CREATE_NOTE, e);
1330         }
1331 
1332         
1333         paymentRequest.setReopenPurchaseOrderIndicator(false);
1334 
1335         getAccountsPayableService().cancelAccountsPayableDocument(paymentRequest, ""); 
1336         
1337         
1338         if (LOG.isDebugEnabled()) {
1339             LOG.debug("cancelExtractedPaymentRequest() PREQ " + paymentRequest.getPurapDocumentIdentifier() + " Cancelled Without Workflow");
1340             LOG.debug("cancelExtractedPaymentRequest() ended");
1341         }
1342     }
1343 
1344     
1345 
1346 
1347 
1348     @Override
1349     public void resetExtractedPaymentRequest(PaymentRequestDocument paymentRequest, String note) {
1350         LOG.debug("resetExtractedPaymentRequest() started");
1351         if (PaymentRequestStatuses.CANCELLED_STATUSES.contains(paymentRequest.getApplicationDocumentStatus())) {
1352             LOG.debug("resetExtractedPaymentRequest() ended");
1353             return;
1354         }
1355         paymentRequest.setExtractedTimestamp(null);
1356         paymentRequest.setPaymentPaidTimestamp(null);
1357         String noteText = "This Payment Request is being reset for extraction by PDP " + note;
1358         try {
1359             Note resetNote = documentService.createNoteFromDocument(paymentRequest, noteText);
1360             paymentRequest.addNote(resetNote);
1361             noteService.save(resetNote);
1362         } catch (Exception e) {
1363             throw new RuntimeException(PurapConstants.REQ_UNABLE_TO_CREATE_NOTE + " " + e);
1364         }
1365         purapService.saveDocumentNoValidation(paymentRequest);
1366         if (LOG.isDebugEnabled()) {
1367             LOG.debug("resetExtractedPaymentRequest() PREQ " + paymentRequest.getPurapDocumentIdentifier() + " Reset from Extracted status");
1368         }
1369     }
1370 
1371     
1372 
1373 
1374     @Override
1375     public void populatePaymentRequest(PaymentRequestDocument paymentRequestDocument) {
1376 
1377         PurchaseOrderDocument purchaseOrderDocument = paymentRequestDocument.getPurchaseOrderDocument();
1378 
1379         
1380         HashMap<String, ExpiredOrClosedAccountEntry> expiredOrClosedAccountList = getAccountsPayableService().getExpiredOrClosedAccountList(paymentRequestDocument);
1381         if (paymentRequestDocument.getInvoiceIdentifier() == null) {
1382             paymentRequestDocument.populatePaymentRequestFromPurchaseOrder(purchaseOrderDocument, expiredOrClosedAccountList);
1383         }
1384 
1385 
1386         paymentRequestDocument.getDocumentHeader().setDocumentDescription(createPreqDocumentDescription(paymentRequestDocument.getPurchaseOrderIdentifier(), paymentRequestDocument.getVendorName()));
1387 
1388         
1389         
1390         getAccountsPayableService().generateExpiredOrClosedAccountNote(paymentRequestDocument, expiredOrClosedAccountList);
1391 
1392         
1393         if (!expiredOrClosedAccountList.isEmpty()) {
1394             paymentRequestDocument.setContinuationAccountIndicator(true);
1395         }
1396 
1397         
1398         calculateDiscount(paymentRequestDocument);
1399         
1400         distributeAccounting(paymentRequestDocument);
1401 
1402         
1403         Bank defaultBank = bankService.getDefaultBankByDocType(paymentRequestDocument.getClass());
1404         if (defaultBank != null) {
1405             paymentRequestDocument.setBankCode(defaultBank.getBankCode());
1406             paymentRequestDocument.setBank(defaultBank);
1407         }
1408     }
1409 
1410     
1411 
1412 
1413 
1414     @Override
1415     public String createPreqDocumentDescription(Integer purchaseOrderIdentifier, String vendorName) {
1416         LOG.debug("Inside createPreqDocumentDescription()");
1417         String description =  getOlePurapService().getParameter(OLEConstants.PREQ_DESC);
1418         Map<String,String> descMap = new HashMap<>();
1419         descMap.put(OLEConstants.PO_DOC_ID,purchaseOrderIdentifier.toString());
1420         descMap.put(OLEConstants.VENDOR_NAME,StringUtils.trimToEmpty(vendorName));
1421         description = getOlePurapService().setDocumentDescription(description,descMap);
1422         int noteTextMaxLength = dataDictionaryService.getAttributeMaxLength(DocumentHeader.class, KRADPropertyConstants.DOCUMENT_DESCRIPTION).intValue();
1423         if (noteTextMaxLength >= description.length()) {
1424             return description;
1425         } else {
1426             return description.substring(0, noteTextMaxLength);
1427         }
1428     }
1429 
1430     
1431 
1432 
1433     @Override
1434     public void populateAndSavePaymentRequest(PaymentRequestDocument preq) throws WorkflowException {
1435         try {
1436             preq.updateAndSaveAppDocStatus(PurapConstants.PaymentRequestStatuses.APPDOC_IN_PROCESS);
1437             documentService.saveDocument(preq, AttributedContinuePurapEvent.class);
1438             LOG.info("Payment request saved successfully" + preq.getDocumentNumber());
1439         } catch (ValidationException ve) {
1440             preq.updateAndSaveAppDocStatus(PurapConstants.PaymentRequestStatuses.APPDOC_INITIATE);
1441         } catch (WorkflowException we) {
1442             preq.updateAndSaveAppDocStatus(PurapConstants.PaymentRequestStatuses.APPDOC_INITIATE);
1443 
1444             String errorMsg = "Error saving document # " + preq.getDocumentHeader().getDocumentNumber() + " " + we.getMessage();
1445             LOG.error(errorMsg, we);
1446             throw new RuntimeException(errorMsg, we);
1447         }
1448     }
1449 
1450     
1451 
1452 
1453 
1454 
1455 
1456 
1457 
1458 
1459     @Override
1460     public boolean shouldPurchaseOrderBeReversed(AccountsPayableDocument apDoc) {
1461         PurchaseOrderDocument po = apDoc.getPurchaseOrderDocument();
1462         if (ObjectUtils.isNull(po)) {
1463             throw new RuntimeException("po should never be null on PREQ");
1464         }
1465         
1466         if (purapService.isFullDocumentEntryCompleted(apDoc) && StringUtils.equalsIgnoreCase(PurapConstants.PurchaseOrderStatuses.APPDOC_CLOSED, po.getApplicationDocumentStatus())) {
1467             return true;
1468         }
1469         return false;
1470     }
1471 
1472     
1473 
1474 
1475     @Override
1476     public Person getPersonForCancel(AccountsPayableDocument apDoc) {
1477         PaymentRequestDocument preqDoc = (PaymentRequestDocument) apDoc;
1478         Person user = null;
1479         if (preqDoc.isPaymentRequestedCancelIndicator()) {
1480             user = preqDoc.getLastActionPerformedByUser();
1481         }
1482         return user;
1483     }
1484 
1485     
1486 
1487 
1488     @Override
1489     public void takePurchaseOrderCancelAction(AccountsPayableDocument apDoc) {
1490         PaymentRequestDocument preqDocument = (PaymentRequestDocument) apDoc;
1491         if (preqDocument.isReopenPurchaseOrderIndicator()) {
1492             String docType = PurapConstants.PurchaseOrderDocTypes.PURCHASE_ORDER_REOPEN_DOCUMENT;
1493             purchaseOrderService.createAndRoutePotentialChangeDocument(preqDocument.getPurchaseOrderDocument().getDocumentNumber(), docType, "reopened by Credit Memo " + apDoc.getPurapDocumentIdentifier() + "cancel", new ArrayList(), PurapConstants.PurchaseOrderStatuses.APPDOC_PENDING_REOPEN);
1494         }
1495     }
1496 
1497     
1498 
1499 
1500 
1501     @Override
1502     public String updateStatusByNode(String currentNodeName, AccountsPayableDocument apDoc) {
1503         return updateStatusByNode(currentNodeName, (PaymentRequestDocument) apDoc);
1504     }
1505 
1506     
1507 
1508 
1509 
1510 
1511 
1512 
1513     protected String updateStatusByNode(String currentNodeName, PaymentRequestDocument preqDoc) {
1514         
1515         clearRequestCancelFields(preqDoc);
1516 
1517         
1518 
1519         String cancelledStatus = "";
1520         if (StringUtils.isEmpty(currentNodeName)) {
1521             
1522             cancelledStatus = PurapConstants.PaymentRequestStatuses.APPDOC_CANCELLED_POST_AP_APPROVE;
1523         } else {
1524             cancelledStatus = PurapConstants.PaymentRequestStatuses.getPaymentRequestAppDocDisapproveStatuses().get(currentNodeName);
1525         }
1526 
1527         if (StringUtils.isNotBlank(cancelledStatus)) {
1528             try {
1529                 preqDoc.updateAndSaveAppDocStatus(cancelledStatus);
1530             } catch (WorkflowException we) {
1531                 throw new RuntimeException("Unable to save the route status data for document: " + preqDoc.getDocumentNumber(), we);
1532             }
1533             purapService.saveDocumentNoValidation(preqDoc);
1534         } else {
1535             logAndThrowRuntimeException("No status found to set for document being disapproved in node '" + currentNodeName + "'");
1536         }
1537         return cancelledStatus;
1538     }
1539 
1540     
1541 
1542 
1543 
1544     @Override
1545     public void markPaid(PaymentRequestDocument pr, Date processDate) {
1546         LOG.debug("markPaid() started");
1547 
1548         pr.setPaymentPaidTimestamp(new Timestamp(processDate.getTime()));
1549         purapService.saveDocumentNoValidation(pr);
1550     }
1551 
1552     
1553 
1554 
1555     @Override
1556     public boolean hasDiscountItem(PaymentRequestDocument preq) {
1557         return ObjectUtils.isNotNull(findDiscountItem(preq));
1558     }
1559 
1560     
1561 
1562 
1563 
1564     @Override
1565     public boolean poItemEligibleForAp(AccountsPayableDocument apDoc, PurchaseOrderItem poi) {
1566         if (ObjectUtils.isNull(poi)) {
1567             throw new RuntimeException("item null in purchaseOrderItemEligibleForPayment ... this should never happen");
1568         }
1569         
1570         if (!poi.isItemActiveIndicator()) {
1571             return false;
1572         }
1573 
1574         ItemType poiType = poi.getItemType();
1575         if (ObjectUtils.isNull(poiType)) {
1576             return false;
1577         }
1578 
1579         if (poiType.isQuantityBasedGeneralLedgerIndicator()) {
1580             if (poi.getItemQuantity().isGreaterThan(poi.getItemInvoicedTotalQuantity())) {
1581                 return true;
1582             }
1583             return false;
1584         } else { 
1585             
1586             
1587             
1588             if (poi.getItemOutstandingEncumberedAmount() != null) {
1589                 return true;
1590             }
1591             return false;
1592         }
1593     }
1594 
1595     @Override
1596     public void removeIneligibleAdditionalCharges(PaymentRequestDocument document) {
1597 
1598         List<PaymentRequestItem> itemsToRemove = new ArrayList<PaymentRequestItem>();
1599 
1600         for (PaymentRequestItem item : (List<PaymentRequestItem>) document.getItems()) {
1601 
1602             
1603             if (ObjectUtils.isNull(item.getPurchaseOrderItemUnitPrice()) && (ItemTypeCodes.ITEM_TYPE_ORDER_DISCOUNT_CODE.equals(item.getItemTypeCode()) || ItemTypeCodes.ITEM_TYPE_TRADE_IN_CODE.equals(item.getItemTypeCode()))) {
1604                 itemsToRemove.add(item);
1605                 continue;
1606             }
1607 
1608             
1609             if (StringUtils.equals(item.getItemTypeCode(), PurapConstants.ItemTypeCodes.ITEM_TYPE_PMT_TERMS_DISCOUNT_CODE)) {
1610                 PaymentTermType pt = document.getVendorPaymentTerms();
1611                 if ((pt != null) && (pt.getVendorPaymentTermsPercent() != null) && (BigDecimal.ZERO.compareTo(pt.getVendorPaymentTermsPercent()) != 0)) {
1612                     
1613                 } else {
1614                     
1615                     itemsToRemove.add(item);
1616                 }
1617                 continue;
1618             }
1619 
1620         }
1621 
1622         
1623         for (PaymentRequestItem item : itemsToRemove) {
1624             document.getItems().remove(item);
1625         }
1626     }
1627 
1628     @Override
1629     public void changeVendor(PaymentRequestDocument preq, Integer headerId, Integer detailId) {
1630 
1631         VendorDetail primaryVendor = vendorService.getVendorDetail(preq.getOriginalVendorHeaderGeneratedIdentifier(), preq.getOriginalVendorDetailAssignedIdentifier());
1632 
1633         if (primaryVendor == null) {
1634             LOG.error("useAlternateVendor() primaryVendorDetail from database for header id " + headerId + " and detail id " + detailId + "is null");
1635             throw new PurError("AlternateVendor: VendorDetail from database for header id " + headerId + " and detail id " + detailId + "is null");
1636         }
1637 
1638         
1639         VendorDetail vd = vendorService.getVendorDetail(headerId, detailId);
1640         if (vd == null) {
1641             LOG.error("changeVendor() VendorDetail from database for header id " + headerId + " and detail id " + detailId + "is null");
1642             throw new PurError("changeVendor: VendorDetail from database for header id " + headerId + " and detail id " + detailId + "is null");
1643         }
1644         preq.setVendorDetail(vd);
1645         preq.setVendorName(vd.getVendorName());
1646         preq.setVendorNumber(vd.getVendorNumber());
1647         preq.setVendorHeaderGeneratedIdentifier(vd.getVendorHeaderGeneratedIdentifier());
1648         preq.setVendorDetailAssignedIdentifier(vd.getVendorDetailAssignedIdentifier());
1649         preq.setVendorPaymentTermsCode(vd.getVendorPaymentTermsCode());
1650         preq.setVendorShippingPaymentTermsCode(vd.getVendorShippingPaymentTermsCode());
1651         preq.setVendorShippingTitleCode(vd.getVendorShippingTitleCode());
1652         preq.refreshReferenceObject("vendorPaymentTerms");
1653         preq.refreshReferenceObject("vendorShippingPaymentTerms");
1654 
1655         
1656         String deliveryCampus = preq.getPurchaseOrderDocument().getDeliveryCampusCode();
1657         VendorAddress va = vendorService.getVendorDefaultAddress(headerId, detailId, VendorConstants.AddressTypes.REMIT, deliveryCampus);
1658         if (va == null) {
1659             va = vendorService.getVendorDefaultAddress(headerId, detailId, VendorConstants.AddressTypes.PURCHASE_ORDER, deliveryCampus);
1660         }
1661         if (va == null) {
1662             LOG.error("changeVendor() VendorAddress from database for header id " + headerId + " and detail id " + detailId + "is null");
1663             throw new PurError("changeVendor  VendorAddress from database for header id " + headerId + " and detail id " + detailId + "is null");
1664         }
1665 
1666         if (preq != null) {
1667             setVendorAddress(va, preq);
1668         } else {
1669             LOG.error("changeVendor(): Null link back to the Purchase Order.");
1670             throw new PurError("Null link back to the Purchase Order.");
1671         }
1672 
1673         
1674         preq.getDocumentHeader().setDocumentDescription(createPreqDocumentDescription(preq.getPurchaseOrderIdentifier(), preq.getVendorName()));
1675     }
1676 
1677     
1678 
1679 
1680 
1681 
1682 
1683 
1684     protected void setVendorAddress(VendorAddress va, PaymentRequestDocument preq) {
1685 
1686         if (va != null) {
1687             preq.setVendorAddressGeneratedIdentifier(va.getVendorAddressGeneratedIdentifier());
1688             preq.setVendorAddressInternationalProvinceName(va.getVendorAddressInternationalProvinceName());
1689             preq.setVendorLine1Address(va.getVendorLine1Address());
1690             preq.setVendorLine2Address(va.getVendorLine2Address());
1691             preq.setVendorCityName(va.getVendorCityName());
1692             preq.setVendorStateCode(va.getVendorStateCode());
1693             preq.setVendorPostalCode(va.getVendorZipCode());
1694             preq.setVendorCountryCode(va.getVendorCountryCode());
1695         }
1696 
1697     }
1698 
1699     
1700 
1701 
1702 
1703 
1704     protected void logAndThrowRuntimeException(String errorMessage) {
1705         this.logAndThrowRuntimeException(errorMessage, null);
1706     }
1707 
1708     
1709 
1710 
1711 
1712 
1713 
1714     protected void logAndThrowRuntimeException(String errorMessage, Exception e) {
1715         if (ObjectUtils.isNotNull(e)) {
1716             LOG.error(errorMessage, e);
1717             throw new RuntimeException(errorMessage, e);
1718         } else {
1719             LOG.error(errorMessage);
1720             throw new RuntimeException(errorMessage);
1721         }
1722     }
1723 
1724     
1725 
1726 
1727 
1728 
1729     @Override
1730     public void generateGLEntriesCreateAccountsPayableDocument(AccountsPayableDocument apDocument) {
1731         PaymentRequestDocument paymentRequest = (PaymentRequestDocument) apDocument;
1732         
1733         SpringContext.getBean(PurapGeneralLedgerService.class).generateEntriesCreatePaymentRequest(paymentRequest);
1734     }
1735 
1736     
1737 
1738 
1739     @Override
1740     public boolean hasActivePaymentRequestsForPurchaseOrder(Integer purchaseOrderIdentifier) {
1741 
1742         boolean hasActivePreqs = false;
1743         List<String> docNumbers = null;
1744         WorkflowDocument workflowDocument = null;
1745 
1746         docNumbers = paymentRequestDao.getActivePaymentRequestDocumentNumbersForPurchaseOrder(purchaseOrderIdentifier);
1747         docNumbers = filterPaymentRequestByAppDocStatus(docNumbers, PaymentRequestStatuses.STATUSES_POTENTIALLY_ACTIVE);
1748 
1749         for (String docNumber : docNumbers) {
1750             try {
1751                 workflowDocument = workflowDocumentService.loadWorkflowDocument(docNumber, GlobalVariables.getUserSession().getPerson());
1752             } catch (WorkflowException we) {
1753                 throw new RuntimeException(we);
1754             }
1755             
1756             if (!(workflowDocument.isCanceled() || workflowDocument.isException())) {
1757                 hasActivePreqs = true;
1758                 break;
1759             }
1760         }
1761         return hasActivePreqs;
1762     }
1763 
1764     
1765 
1766 
1767 
1768 
1769 
1770 
1771 
1772 
1773 
1774 
1775     protected List<String> filterPaymentRequestByAppDocStatus(List<String> lookupDocNumbers, String... appDocStatus) {
1776         boolean valid = false;
1777 
1778         final String DOC_NUM_DELIM = "|";
1779         StrBuilder routerHeaderIdBuilder = new StrBuilder().appendWithSeparators(lookupDocNumbers, DOC_NUM_DELIM);
1780 
1781         List<String> paymentRequestDocNumbers = new ArrayList<String>();
1782 
1783         DocumentSearchCriteria.Builder documentSearchCriteriaDTO = DocumentSearchCriteria.Builder.create();
1784         documentSearchCriteriaDTO.setDocumentId(routerHeaderIdBuilder.toString());
1785         documentSearchCriteriaDTO.setDocumentTypeName(PurapConstants.PurapDocTypeCodes.PAYMENT_REQUEST_DOCUMENT);
1786         documentSearchCriteriaDTO.setApplicationDocumentStatuses(Arrays.asList(appDocStatus));
1787 
1788         DocumentSearchResults reqDocumentsList = KewApiServiceLocator.getWorkflowDocumentService().documentSearch(
1789                 "", documentSearchCriteriaDTO.build());
1790 
1791         for (DocumentSearchResult reqDocument : reqDocumentsList.getSearchResults()) {
1792             
1793             if (Arrays.asList(appDocStatus).contains(reqDocument.getDocument().getApplicationDocumentStatus())) {
1794                 
1795                 paymentRequestDocNumbers.add(reqDocument.getDocument().getDocumentId());
1796             }
1797 
1798         }
1799 
1800         return paymentRequestDocNumbers;
1801     }
1802 
1803     
1804 
1805 
1806 
1807 
1808 
1809 
1810 
1811 
1812 
1813 
1814     protected Collection<PaymentRequestDocument> filterPaymentRequestByAppDocStatus(Collection<PaymentRequestDocument> paymentRequestDocuments, String... appDocStatus) {
1815         List<String> paymentRequestDocNumbers = new ArrayList<String>();
1816         for (PaymentRequestDocument paymentRequest : paymentRequestDocuments) {
1817             paymentRequestDocNumbers.add(paymentRequest.getDocumentNumber());
1818         }
1819 
1820         List<String> filteredPaymentRequestDocNumbers = filterPaymentRequestByAppDocStatus(paymentRequestDocNumbers, appDocStatus);
1821 
1822         Collection<PaymentRequestDocument> filteredPaymentRequestDocuments = new ArrayList<PaymentRequestDocument>();
1823         
1824         for (PaymentRequestDocument paymentRequest : paymentRequestDocuments) {
1825             if (filteredPaymentRequestDocNumbers.contains(paymentRequest.getDocumentNumber())) {
1826                 filteredPaymentRequestDocuments.add(paymentRequest);
1827             }
1828         }
1829         return filteredPaymentRequestDocuments;
1830     }
1831 
1832 
1833     
1834 
1835 
1836 
1837 
1838 
1839 
1840 
1841 
1842 
1843     protected Iterator<PaymentRequestDocument> filterPaymentRequestByAppDocStatus(Iterator<PaymentRequestDocument> paymentRequestIterator, String... appDocStatus) {
1844         Collection<PaymentRequestDocument> paymentRequestDocuments = new ArrayList<PaymentRequestDocument>();
1845         for (; paymentRequestIterator.hasNext(); ) {
1846             paymentRequestDocuments.add(paymentRequestIterator.next());
1847         }
1848 
1849         return filterPaymentRequestByAppDocStatus(paymentRequestDocuments, appDocStatus).iterator();
1850     }
1851 
1852     
1853 
1854 
1855     @Override
1856     public void processPaymentRequestInReceivingStatus() {
1857         List<String> docNumbers = paymentRequestDao.getPaymentRequestInReceivingStatus();
1858         docNumbers = filterPaymentRequestByAppDocStatus(docNumbers, PurapConstants.PaymentRequestStatuses.APPDOC_AWAITING_RECEIVING_REVIEW);
1859 
1860         List<PaymentRequestDocument> preqsAwaitingReceiving = new ArrayList<PaymentRequestDocument>();
1861         for (String docNumber : docNumbers) {
1862             PaymentRequestDocument preq = getPaymentRequestByDocumentNumber(docNumber);
1863             if (ObjectUtils.isNotNull(preq)) {
1864                 preqsAwaitingReceiving.add(preq);
1865             }
1866         }
1867         if (ObjectUtils.isNotNull(preqsAwaitingReceiving)) {
1868             for (PaymentRequestDocument preqDoc : preqsAwaitingReceiving) {
1869                 if (preqDoc.isReceivingRequirementMet()) {
1870                     try {
1871                         documentService.approveDocument(preqDoc, "Approved by Receiving Required PREQ job", null);
1872                     } catch (WorkflowException e) {
1873                         LOG.error("processPaymentRequestInReceivingStatus() Error approving payment request document from awaiting receiving", e);
1874                         throw new RuntimeException("Error approving payment request document from awaiting receiving", e);
1875                     }
1876                 }
1877             }
1878         }
1879     }
1880 
1881     
1882 
1883 
1884     @Override
1885     public boolean allowBackpost(PaymentRequestDocument paymentRequestDocument) {
1886         int allowBackpost = (Integer.parseInt(parameterService.getParameterValueAsString(PaymentRequestDocument.class, PurapRuleConstants.ALLOW_BACKPOST_DAYS)));
1887 
1888         Calendar today = dateTimeService.getCurrentCalendar();
1889         Integer currentFY = universityDateService.getCurrentUniversityDate().getUniversityFiscalYear();
1890         java.util.Date priorClosingDateTemp = universityDateService.getLastDateOfFiscalYear(currentFY - 1);
1891         Calendar priorClosingDate = Calendar.getInstance();
1892         priorClosingDate.setTime(priorClosingDateTemp);
1893 
1894         
1895         Calendar allowBackpostDate = Calendar.getInstance();
1896         allowBackpostDate.setTime(priorClosingDate.getTime());
1897         allowBackpostDate.add(Calendar.DATE, allowBackpost + 1);
1898 
1899         Calendar preqInvoiceDate = Calendar.getInstance();
1900         preqInvoiceDate.setTime(paymentRequestDocument.getInvoiceDate());
1901 
1902         
1903         
1904         if ((today.compareTo(priorClosingDate) > 0) && (today.compareTo(allowBackpostDate) <= 0) && (preqInvoiceDate.compareTo(priorClosingDate) <= 0)) {
1905             LOG.debug("allowBackpost() within range to allow backpost; posting entry to period 12 of previous FY");
1906             return true;
1907         }
1908 
1909         LOG.debug("allowBackpost() not within range to allow backpost; posting entry to current FY");
1910         return false;
1911     }
1912 
1913     @Override
1914     public boolean isPurchaseOrderValidForPaymentRequestDocumentCreation(PaymentRequestDocument paymentRequestDocument, PurchaseOrderDocument po) {
1915         Integer POID = paymentRequestDocument.getPurchaseOrderIdentifier();
1916         boolean valid = true;
1917 
1918         PurchaseOrderDocument purchaseOrderDocument = paymentRequestDocument.getPurchaseOrderDocument();
1919         if (ObjectUtils.isNull(purchaseOrderDocument)) {
1920             GlobalVariables.getMessageMap().putError(PurapPropertyConstants.PURCHASE_ORDER_IDENTIFIER, PurapKeyConstants.ERROR_PURCHASE_ORDER_NOT_EXIST);
1921             valid &= false;
1922         } else if (purchaseOrderDocument.isPendingActionIndicator()) {
1923             GlobalVariables.getMessageMap().putError(PurapPropertyConstants.PURCHASE_ORDER_IDENTIFIER, PurapKeyConstants.ERROR_PURCHASE_PENDING_ACTION);
1924             valid &= false;
1925         } else if (!StringUtils.equals(purchaseOrderDocument.getApplicationDocumentStatus(), PurapConstants.PurchaseOrderStatuses.APPDOC_OPEN)) {
1926             GlobalVariables.getMessageMap().putError(PurapPropertyConstants.PURCHASE_ORDER_IDENTIFIER, PurapKeyConstants.ERROR_PURCHASE_ORDER_NOT_OPEN);
1927             valid &= false;
1928             
1929         } else {
1930             
1931             
1932         }
1933 
1934         return valid;
1935     }
1936 
1937     @Override
1938     public boolean encumberedItemExistsForInvoicing(PurchaseOrderDocument document) {
1939         boolean zeroDollar = true;
1940         GlobalVariables.getMessageMap().clearErrorPath();
1941         GlobalVariables.getMessageMap().addToErrorPath(OLEPropertyConstants.DOCUMENT);
1942         for (PurchaseOrderItem poi : (List<PurchaseOrderItem>) document.getItems()) {
1943             
1944             if (poi.getItemType().isLineItemIndicator() && poi.getItemType().isQuantityBasedGeneralLedgerIndicator()) {
1945                 KualiDecimal encumberedQuantity = poi.getItemOutstandingEncumberedQuantity() == null ? KualiDecimal.ZERO : poi.getItemOutstandingEncumberedQuantity();
1946                 if (encumberedQuantity.compareTo(KualiDecimal.ZERO) == 1) {
1947                     zeroDollar = false;
1948                     break;
1949                 }
1950             }
1951             
1952             else if (poi.getItemType().isAmountBasedGeneralLedgerIndicator() || poi.getItemType().isAdditionalChargeIndicator()) {
1953                 KualiDecimal encumberedAmount = poi.getItemOutstandingEncumberedAmount() == null ? KualiDecimal.ZERO : poi.getItemOutstandingEncumberedAmount();
1954                 if (encumberedAmount.compareTo(KualiDecimal.ZERO) == 1) {
1955                     zeroDollar = false;
1956                     break;
1957                 }
1958             }
1959         }
1960 
1961         return !zeroDollar;
1962     }
1963 
1964     public void setDateTimeService(DateTimeService dateTimeService) {
1965         this.dateTimeService = dateTimeService;
1966     }
1967 
1968     public void setParameterService(ParameterService parameterService) {
1969         this.parameterService = parameterService;
1970     }
1971 
1972     public void setConfigurationService(ConfigurationService configurationService) {
1973         this.configurationService = configurationService;
1974     }
1975 
1976     public void setDocumentService(DocumentService documentService) {
1977         this.documentService = documentService;
1978     }
1979 
1980     public void setNoteService(NoteService noteService) {
1981         this.noteService = noteService;
1982     }
1983 
1984     public void setPurapService(PurapService purapService) {
1985         this.purapService = purapService;
1986     }
1987 
1988     public void setPaymentRequestDao(PaymentRequestDao paymentRequestDao) {
1989         this.paymentRequestDao = paymentRequestDao;
1990     }
1991 
1992     public void setNegativePaymentRequestApprovalLimitService(NegativePaymentRequestApprovalLimitService negativePaymentRequestApprovalLimitService) {
1993         this.negativePaymentRequestApprovalLimitService = negativePaymentRequestApprovalLimitService;
1994     }
1995 
1996     public void setPurapAccountingService(PurapAccountingService purapAccountingService) {
1997         this.purapAccountingService = purapAccountingService;
1998     }
1999 
2000     public void setBusinessObjectService(BusinessObjectService businessObjectService) {
2001         this.businessObjectService = businessObjectService;
2002     }
2003 
2004     public void setPurapWorkflowIntegrationService(PurApWorkflowIntegrationService purapWorkflowIntegrationService) {
2005         this.purapWorkflowIntegrationService = purapWorkflowIntegrationService;
2006     }
2007 
2008     public void setWorkflowDocumentService(WorkflowDocumentService workflowDocumentService) {
2009         this.workflowDocumentService = workflowDocumentService;
2010     }
2011 
2012     public void setAccountsPayableService(AccountsPayableService accountsPayableService) {
2013         this.accountsPayableService = accountsPayableService;
2014     }
2015 
2016     public void setVendorService(VendorService vendorService) {
2017         this.vendorService = vendorService;
2018     }
2019 
2020     public void setDataDictionaryService(DataDictionaryService dataDictionaryService) {
2021         this.dataDictionaryService = dataDictionaryService;
2022     }
2023 
2024     public void setUniversityDateService(UniversityDateService universityDateService) {
2025         this.universityDateService = universityDateService;
2026     }
2027 
2028 
2029     public void setBankService(BankService bankService) {
2030         this.bankService = bankService;
2031     }
2032 
2033 
2034     public void setPurchaseOrderService(PurchaseOrderService purchaseOrderService) {
2035         this.purchaseOrderService = purchaseOrderService;
2036     }
2037 
2038     public void setFinancialSystemWorkflowHelperService(FinancialSystemWorkflowHelperService financialSystemWorkflowHelperService) {
2039         this.financialSystemWorkflowHelperService = financialSystemWorkflowHelperService;
2040     }
2041 
2042 
2043     public void setKualiRuleService(KualiRuleService kualiRuleService) {
2044         this.kualiRuleService = kualiRuleService;
2045     }
2046 
2047     
2048 
2049 
2050 
2051 
2052 
2053     public AccountsPayableService getAccountsPayableService() {
2054         return SpringContext.getBean(AccountsPayableService.class);
2055     }
2056 }