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 }