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