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(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 boolean foundCanceledPreApprove = false;
601 for (InvoiceDocument testPRQS : prqss) {
602 if (StringUtils.equals(testPRQS.getApplicationDocumentStatus(), InvoiceStatuses.APPDOC_CANCELLED_POST_AP_APPROVE)) {
603 foundCanceledPostApprove |= true;
604 } else if (StringUtils.equals(testPRQS.getApplicationDocumentStatus(), InvoiceStatuses.APPDOC_CANCELLED_IN_PROCESS)) {
605 foundCanceledPreApprove |= true;
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 if (foundCanceledPostApprove && foundCanceledPreApprove) {
615 msgs.put(PRQSDocumentsStrings.DUPLICATE_INVOICE_QUESTION, configurationService.getPropertyValueAsString(PurapKeyConstants.MESSAGE_DUPLICATE_INVOICE_CANCELLEDORVOIDED));
616 } else if (foundCanceledPreApprove) {
617 msgs.put(PRQSDocumentsStrings.DUPLICATE_INVOICE_QUESTION, configurationService.getPropertyValueAsString(PurapKeyConstants.MESSAGE_DUPLICATE_INVOICE_VOIDED));
618 } else if (foundCanceledPostApprove) {
619
620 msgs.put(PRQSDocumentsStrings.DUPLICATE_INVOICE_QUESTION, configurationService.getPropertyValueAsString(PurapKeyConstants.MESSAGE_DUPLICATE_INVOICE_CANCELLED));
621 }
622 }
623 }
624
625
626 prqss = getInvoicesByPOIdInvoiceAmountInvoiceDate(purchaseOrderId, document.getVendorInvoiceAmount(), document.getInvoiceDate());
627 if (prqss.size() > 0) {
628 boolean addedMessage = false;
629 boolean foundCanceledPostApprove = false;
630 boolean foundCanceledPreApprove = false;
631 msgs.put(PRQSDocumentsStrings.DUPLICATE_INVOICE_QUESTION, configurationService.getPropertyValueAsString(PurapKeyConstants.MESSAGE_DUPLICATE_INVOICE_DATE_AMOUNT));
632 for (InvoiceDocument testPRQS : prqss) {
633 if (StringUtils.equalsIgnoreCase(testPRQS.getApplicationDocumentStatus(), InvoiceStatuses.APPDOC_CANCELLED_POST_AP_APPROVE)) {
634 foundCanceledPostApprove |= true;
635 } else if (StringUtils.equalsIgnoreCase(testPRQS.getApplicationDocumentStatus(), InvoiceStatuses.APPDOC_CANCELLED_IN_PROCESS)) {
636 foundCanceledPreApprove |= true;
637 } else {
638 msgs.put(PRQSDocumentsStrings.DUPLICATE_INVOICE_QUESTION, configurationService.getPropertyValueAsString(PurapKeyConstants.MESSAGE_DUPLICATE_INVOICE_DATE_AMOUNT));
639 addedMessage = true;
640 break;
641 }
642 }
643
644
645 if (!addedMessage) {
646 if (foundCanceledPostApprove && foundCanceledPreApprove) {
647 msgs.put(PRQSDocumentsStrings.DUPLICATE_INVOICE_QUESTION, configurationService.getPropertyValueAsString(PurapKeyConstants.MESSAGE_DUPLICATE_INVOICE_DATE_AMOUNT_CANCELLEDORVOIDED));
648 } else if (foundCanceledPreApprove) {
649 msgs.put(PRQSDocumentsStrings.DUPLICATE_INVOICE_QUESTION, configurationService.getPropertyValueAsString(PurapKeyConstants.MESSAGE_DUPLICATE_INVOICE_DATE_AMOUNT_VOIDED));
650 addedMessage = true;
651 } else if (foundCanceledPostApprove) {
652 msgs.put(PRQSDocumentsStrings.DUPLICATE_INVOICE_QUESTION, configurationService.getPropertyValueAsString(PurapKeyConstants.MESSAGE_DUPLICATE_INVOICE_DATE_AMOUNT_CANCELLED));
653 addedMessage = true;
654 }
655
656 }
657 }
658 }
659 return msgs;
660 }
661
662
663
664
665 @Override
666 public InvoiceDocument getInvoiceByDocumentNumber(String documentNumber) {
667 LOG.debug("getInvoiceByDocumentNumber() started");
668
669 if (ObjectUtils.isNotNull(documentNumber)) {
670 try {
671 InvoiceDocument doc = (InvoiceDocument) documentService.getByDocumentHeaderId(documentNumber);
672 return doc;
673 } catch (WorkflowException e) {
674 String errorMessage = "Error getting payment request document from document service";
675 LOG.error("getInvoiceByDocumentNumber() " + errorMessage, e);
676 throw new RuntimeException(errorMessage, e);
677 }
678 }
679 return null;
680 }
681
682
683
684
685 @Override
686 public InvoiceDocument getInvoiceById(Integer poDocId) {
687 return getInvoiceByDocumentNumber(invoiceDao.getDocumentNumberByInvoiceId(poDocId));
688 }
689
690
691
692
693 @Override
694 public List<InvoiceDocument> getInvoicesByPurchaseOrderId(Integer poDocId) {
695 List<InvoiceDocument> prqss = new ArrayList<InvoiceDocument>();
696 List<String> docNumbers = invoiceDao.getDocumentNumbersByPurchaseOrderId(poDocId);
697 for (String docNumber : docNumbers) {
698 InvoiceDocument prqs = getInvoiceByDocumentNumber(docNumber);
699 if (ObjectUtils.isNotNull(prqs)) {
700 prqss.add(prqs);
701 }
702 }
703 return prqss;
704 }
705
706
707
708
709
710 @Override
711 public List<InvoiceDocument> getInvoicesByPOIdInvoiceAmountInvoiceDate(Integer poId, KualiDecimal invoiceAmount, Date invoiceDate) {
712 LOG.debug("getInvoicesByPOIdInvoiceAmountInvoiceDate() started");
713 return invoiceDao.getActiveInvoicesByPOIdInvoiceAmountInvoiceDate(poId, invoiceAmount, invoiceDate);
714 }
715
716
717
718
719 @Override
720 public boolean isInvoiceDateAfterToday(Date invoiceDate) {
721
722 Calendar now = Calendar.getInstance();
723 now.set(Calendar.HOUR, 11);
724 now.set(Calendar.MINUTE, 59);
725 now.set(Calendar.SECOND, 59);
726 now.set(Calendar.MILLISECOND, 59);
727 Timestamp nowTime = new Timestamp(now.getTimeInMillis());
728 Calendar invoiceDateC = Calendar.getInstance();
729 invoiceDateC.setTime(invoiceDate);
730
731 invoiceDateC.set(Calendar.HOUR, 0);
732 invoiceDateC.set(Calendar.MINUTE, 0);
733 invoiceDateC.set(Calendar.SECOND, 0);
734 invoiceDateC.set(Calendar.MILLISECOND, 0);
735 Timestamp invoiceDateTime = new Timestamp(invoiceDateC.getTimeInMillis());
736 return ((invoiceDateTime.compareTo(nowTime)) > 0);
737 }
738
739
740
741
742
743 @Override
744 public Date calculatePayDate(Date invoiceDate, PaymentTermType terms) {
745 LOG.debug("calculatePayDate() started");
746
747
748 if (invoiceDate == null) {
749 return null;
750 }
751 Calendar invoicedDateCalendar = dateTimeService.getCalendar(invoiceDate);
752 Calendar processedDateCalendar = dateTimeService.getCurrentCalendar();
753
754
755
756 String defaultDays = getParameter(OLEConstants.OptionalModuleNamespaces.PURCHASING_ACCOUNTS_PAYABLE, OLEConstants.InvoiceDocument.CMPNT_CD,PurapParameterConstants.PURAP_PRQS_PAY_DATE_DEFAULT_NUMBER_OF_DAYS);
757 processedDateCalendar.add(Calendar.DAY_OF_MONTH, Integer.parseInt(defaultDays));
758
759 if (ObjectUtils.isNull(terms) || StringUtils.isEmpty(terms.getVendorPaymentTermsCode())) {
760 invoicedDateCalendar.add(Calendar.DAY_OF_MONTH, PurapConstants.PRQS_PAY_DATE_EMPTY_TERMS_DEFAULT_DAYS);
761 return returnLaterDate(invoicedDateCalendar, processedDateCalendar);
762 }
763
764 Integer discountDueNumber = terms.getVendorDiscountDueNumber();
765 Integer netDueNumber = terms.getVendorNetDueNumber();
766 if (ObjectUtils.isNotNull(discountDueNumber)) {
767 String discountDueTypeDescription = terms.getVendorDiscountDueTypeDescription();
768 paymentTermsDateCalculation(discountDueTypeDescription, invoicedDateCalendar, discountDueNumber);
769 } else if (ObjectUtils.isNotNull(netDueNumber)) {
770 String netDueTypeDescription = terms.getVendorNetDueTypeDescription();
771 paymentTermsDateCalculation(netDueTypeDescription, invoicedDateCalendar, netDueNumber);
772 } else {
773 throw new RuntimeException("Neither discount or net number were specified for this payment terms type");
774 }
775
776
777 return returnLaterDate(invoicedDateCalendar, processedDateCalendar);
778 }
779
780
781
782
783
784
785
786
787 protected Date returnLaterDate(Calendar invoicedDateCalendar, Calendar processedDateCalendar) {
788 if (invoicedDateCalendar.after(processedDateCalendar)) {
789 return new Date(invoicedDateCalendar.getTimeInMillis());
790 } else {
791 return new Date(processedDateCalendar.getTimeInMillis());
792 }
793 }
794
795
796
797
798
799
800
801
802 protected void paymentTermsDateCalculation(String dueTypeDescription, Calendar invoicedDateCalendar, Integer dueNumber) {
803
804 if (StringUtils.equals(dueTypeDescription, PurapConstants.PRQS_PAY_DATE_DATE)) {
805
806 invoicedDateCalendar.add(Calendar.MONTH, 1);
807 invoicedDateCalendar.set(Calendar.DAY_OF_MONTH, dueNumber.intValue());
808 } else if (StringUtils.equals(PurapConstants.PRQS_PAY_DATE_DAYS, dueTypeDescription)) {
809
810 invoicedDateCalendar.add(Calendar.DAY_OF_MONTH, dueNumber.intValue());
811 } else {
812
813 throw new RuntimeException("missing payment terms description or not properly enterred on payment term maintenance doc");
814 }
815 }
816
817
818
819
820
821 @Override
822 public void calculateInvoice(InvoiceDocument invoice, boolean updateDiscount) {
823 LOG.debug("calculateInvoice() started");
824
825
826 if (ObjectUtils.isNull(invoice.getInvoicePayDate())) {
827 invoice.setInvoicePayDate(calculatePayDate(invoice.getInvoiceDate(), invoice.getVendorPaymentTerms()));
828 }
829
830 distributeAccounting(invoice);
831
832 purapService.calculateTax(invoice);
833
834
835 purapService.prorateForTradeInAndFullOrderDiscount(invoice);
836
837
838 if (updateDiscount) {
839 calculateDiscount(invoice);
840 }
841
842 distributeAccounting(invoice);
843 }
844
845
846
847
848
849
850
851 protected void calculateDiscount(InvoiceDocument invoiceDocument) {
852 InvoiceItem discountItem = findDiscountItem(invoiceDocument);
853
854 PaymentTermType pt = invoiceDocument.getVendorPaymentTerms();
855 if ((pt != null) && (pt.getVendorPaymentTermsPercent() != null) && (BigDecimal.ZERO.compareTo(pt.getVendorPaymentTermsPercent()) != 0)) {
856 if (discountItem == null) {
857
858
859
860 purapService.addBelowLineItems(invoiceDocument);
861
862
863 removeIneligibleAdditionalCharges(invoiceDocument);
864
865 discountItem = findDiscountItem(invoiceDocument);
866 }
867
868
869 InvoiceItem fullOrderItem = findFullOrderDiscountItem(invoiceDocument);
870 KualiDecimal fullOrderAmount = KualiDecimal.ZERO;
871 KualiDecimal fullOrderTaxAmount = KualiDecimal.ZERO;
872
873 if (fullOrderItem != null) {
874 fullOrderAmount = (ObjectUtils.isNotNull(fullOrderItem.getExtendedPrice())) ? fullOrderItem.getExtendedPrice() : KualiDecimal.ZERO;
875 fullOrderTaxAmount = (ObjectUtils.isNotNull(fullOrderItem.getItemTaxAmount())) ? fullOrderItem.getItemTaxAmount() : KualiDecimal.ZERO;
876 }
877 KualiDecimal totalCost = invoiceDocument.getTotalPreTaxDollarAmountAboveLineItems().add(fullOrderAmount);
878 PurApItem tradeInItem = invoiceDocument.getTradeInItem();
879 if (ObjectUtils.isNotNull(tradeInItem)) {
880 totalCost = totalCost.subtract(tradeInItem.getTotalAmount());
881 }
882 BigDecimal discountAmount = pt.getVendorPaymentTermsPercent().multiply(totalCost.bigDecimalValue()).multiply(new BigDecimal(PurapConstants.PRQS_DISCOUNT_MULT));
883
884
885 discountItem.setItemUnitPrice(discountAmount.setScale(4, KualiDecimal.ROUND_BEHAVIOR));
886 discountItem.setExtendedPrice(new KualiDecimal(discountAmount));
887
888
889 boolean salesTaxInd = parameterService.getParameterValueAsBoolean(OleParameterConstants.PURCHASING_DOCUMENT.class, PurapParameterConstants.ENABLE_SALES_TAX_IND);
890 boolean useTaxIndicator = invoiceDocument.isUseTaxIndicator();
891
892 if (salesTaxInd == true && useTaxIndicator == false) {
893 KualiDecimal totalTax = invoiceDocument.getTotalTaxAmountAboveLineItems().add(fullOrderTaxAmount);
894 BigDecimal discountTaxAmount = null;
895 if (totalCost.isNonZero()) {
896 discountTaxAmount = discountAmount.divide(totalCost.bigDecimalValue()).multiply(totalTax.bigDecimalValue());
897 } else {
898 discountTaxAmount = BigDecimal.ZERO;
899 }
900
901 discountItem.setItemTaxAmount(new KualiDecimal(discountTaxAmount.setScale(KualiDecimal.SCALE, KualiDecimal.ROUND_BEHAVIOR)));
902 }
903
904
905 discountItem.setPurapDocument(invoiceDocument);
906 } else {
907 if (discountItem != null) {
908 invoiceDocument.getItems().remove(discountItem);
909 }
910 }
911
912 }
913
914
915 @Override
916 public void clearTax(InvoiceDocument document) {
917
918 removeTaxItems(document);
919
920 document.setTaxClassificationCode(null);
921 document.setTaxFederalPercent(null);
922 document.setTaxStatePercent(null);
923 document.setTaxCountryCode(null);
924 document.setTaxNQIId(null);
925
926 document.setTaxForeignSourceIndicator(false);
927 document.setTaxExemptTreatyIndicator(false);
928 document.setTaxOtherExemptIndicator(false);
929 document.setTaxGrossUpIndicator(false);
930 document.setTaxUSAIDPerDiemIndicator(false);
931 document.setTaxSpecialW4Amount(null);
932
933 }
934
935
936
937
938 @Override
939 public void calculateTaxArea(InvoiceDocument prqs) {
940 LOG.debug("calculateTaxArea() started");
941
942
943 removeTaxItems(prqs);
944
945
946 if (StringUtils.equalsIgnoreCase(prqs.getTaxClassificationCode(), "N")) {
947 return;
948 }
949
950
951
952 BigDecimal taxableAmount = prqs.getGrandPreTaxTotal().bigDecimalValue();
953
954
955
956 if (prqs.getTaxGrossUpIndicator() && prqs.getTaxStatePercent().compareTo(new BigDecimal(0)) != 0) {
957 PurApItem stateGrossItem = addTaxItem(prqs, ItemTypeCodes.ITEM_TYPE_STATE_GROSS_CODE, taxableAmount);
958 }
959
960
961 if (prqs.getTaxStatePercent().compareTo(new BigDecimal(0)) != 0) {
962 PurApItem stateTaxItem = addTaxItem(prqs, ItemTypeCodes.ITEM_TYPE_STATE_TAX_CODE, taxableAmount);
963 }
964
965
966
967 if (prqs.getTaxGrossUpIndicator() && prqs.getTaxFederalPercent().compareTo(new BigDecimal(0)) != 0) {
968 PurApItem federalGrossItem = addTaxItem(prqs, ItemTypeCodes.ITEM_TYPE_FEDERAL_GROSS_CODE, taxableAmount);
969 }
970
971
972 if (prqs.getTaxFederalPercent().compareTo(new BigDecimal(0)) != 0) {
973 PurApItem federalTaxItem = addTaxItem(prqs, ItemTypeCodes.ITEM_TYPE_FEDERAL_TAX_CODE, taxableAmount);
974 }
975
976
977
978
979 }
980
981
982
983
984
985
986 protected void removeTaxItems(InvoiceDocument prqs) {
987 List<PurApItem> items = prqs.getItems();
988 for (int i = 0; i < items.size(); i++) {
989 PurApItem item = items.get(i);
990 String code = item.getItemTypeCode();
991 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)) {
992 items.remove(i--);
993 }
994 }
995 }
996
997
998
999
1000
1001
1002
1003
1004
1005 protected PurApItem addTaxItem(InvoiceDocument prqs, String itemTypeCode, BigDecimal taxableAmount) {
1006 PurApItem taxItem = null;
1007
1008 try {
1009 taxItem = (PurApItem) prqs.getItemClass().newInstance();
1010 } catch (IllegalAccessException e) {
1011 throw new IllegalArgumentException("Unable to access itemClass", e);
1012 } catch (InstantiationException e) {
1013 throw new IllegalArgumentException("Unable to instantiate itemClass", e);
1014 }
1015
1016
1017 taxItem.setItemTypeCode(itemTypeCode);
1018 prqs.addItem(taxItem);
1019
1020
1021 PurApAccountingLine taxLine = addTaxAccountingLine(taxItem, taxableAmount);
1022
1023
1024 taxItem.setItemUnitPrice(taxLine.getAmount().bigDecimalValue());
1025 taxItem.setExtendedPrice(taxLine.getAmount());
1026
1027
1028 ItemType itemType = new ItemType();
1029 itemType.setItemTypeCode(itemTypeCode);
1030 itemType = (ItemType) businessObjectService.retrieve(itemType);
1031 taxItem.setItemType(itemType);
1032 taxItem.setItemDescription(itemType.getItemTypeDescription());
1033
1034 return taxItem;
1035 }
1036
1037
1038
1039
1040
1041
1042
1043
1044 protected PurApAccountingLine addTaxAccountingLine(PurApItem taxItem, BigDecimal taxableAmount) {
1045 InvoiceDocument prqs = taxItem.getPurapDocument();
1046 PurApAccountingLine taxLine = null;
1047
1048 try {
1049 taxLine = (PurApAccountingLine) taxItem.getAccountingLineClass().newInstance();
1050 } catch (IllegalAccessException e) {
1051 throw new IllegalArgumentException("Unable to access sourceAccountingLineClass", e);
1052 } catch (InstantiationException e) {
1053 throw new IllegalArgumentException("Unable to instantiate sourceAccountingLineClass", e);
1054 }
1055
1056
1057 boolean isFederalTax = ItemTypeCodes.ITEM_TYPE_FEDERAL_TAX_CODE.equals(taxItem.getItemTypeCode());
1058 boolean isFederalGross = ItemTypeCodes.ITEM_TYPE_FEDERAL_GROSS_CODE.equals(taxItem.getItemTypeCode());
1059 boolean isStateTax = ItemTypeCodes.ITEM_TYPE_STATE_TAX_CODE.equals(taxItem.getItemTypeCode());
1060 boolean isStateGross = ItemTypeCodes.ITEM_TYPE_STATE_GROSS_CODE.equals(taxItem.getItemTypeCode());
1061 boolean isFederal = isFederalTax || isFederalGross;
1062 boolean isGross = isFederalGross || isStateGross;
1063
1064
1065 String taxChart = null;
1066 String taxAccount = null;
1067 String taxObjectCode = null;
1068
1069 if (isGross) {
1070
1071 AccountingLine line1 = prqs.getFirstAccount();
1072 taxChart = line1.getChartOfAccountsCode();
1073 taxAccount = line1.getAccountNumber();
1074 taxObjectCode = line1.getFinancialObjectCode();
1075 } else if (isFederalTax) {
1076
1077 taxChart = getParameter(OLEConstants.OptionalModuleNamespaces.PURCHASING_ACCOUNTS_PAYABLE, OLEConstants.InvoiceDocument.CMPNT_CD,NRATaxParameters.FEDERAL_TAX_PARM_PREFIX + NRATaxParameters.TAX_PARM_CHART_SUFFIX);
1078 taxAccount = getParameter(OLEConstants.OptionalModuleNamespaces.PURCHASING_ACCOUNTS_PAYABLE, OLEConstants.InvoiceDocument.CMPNT_CD,NRATaxParameters.FEDERAL_TAX_PARM_PREFIX + NRATaxParameters.TAX_PARM_ACCOUNT_SUFFIX);
1079 taxObjectCode = parameterService.getSubParameterValueAsString(InvoiceDocument.class, NRATaxParameters.FEDERAL_TAX_PARM_PREFIX + NRATaxParameters.TAX_PARM_OBJECT_BY_INCOME_CLASS_SUFFIX, prqs.getTaxClassificationCode());
1080 if (StringUtils.isBlank(taxChart) || StringUtils.isBlank(taxAccount) || StringUtils.isBlank(taxObjectCode)) {
1081 LOG.error("Unable to retrieve federal tax parameters.");
1082 throw new RuntimeException("Unable to retrieve federal tax parameters.");
1083 }
1084 } else if (isStateTax) {
1085
1086 taxChart = getParameter(OLEConstants.OptionalModuleNamespaces.PURCHASING_ACCOUNTS_PAYABLE, OLEConstants.InvoiceDocument.CMPNT_CD,NRATaxParameters.STATE_TAX_PARM_PREFIX + NRATaxParameters.TAX_PARM_CHART_SUFFIX);
1087 taxAccount = getParameter(OLEConstants.OptionalModuleNamespaces.PURCHASING_ACCOUNTS_PAYABLE, OLEConstants.InvoiceDocument.CMPNT_CD,NRATaxParameters.STATE_TAX_PARM_PREFIX + NRATaxParameters.TAX_PARM_ACCOUNT_SUFFIX);
1088 taxObjectCode = parameterService.getSubParameterValueAsString(InvoiceDocument.class, NRATaxParameters.STATE_TAX_PARM_PREFIX + NRATaxParameters.TAX_PARM_OBJECT_BY_INCOME_CLASS_SUFFIX, prqs.getTaxClassificationCode());
1089 if (StringUtils.isBlank(taxChart) || StringUtils.isBlank(taxAccount) || StringUtils.isBlank(taxObjectCode)) {
1090 LOG.error("Unable to retrieve state tax parameters.");
1091 throw new RuntimeException("Unable to retrieve state tax parameters.");
1092 }
1093 }
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103 BigDecimal taxPercentFederal = prqs.getTaxFederalPercent();
1104 BigDecimal taxPercentState = prqs.getTaxStatePercent();
1105 BigDecimal taxPercent = isFederal ? taxPercentFederal : taxPercentState;
1106
1107
1108 BigDecimal taxDivider = new BigDecimal(100);
1109 if (prqs.getTaxGrossUpIndicator()) {
1110 taxDivider = taxDivider.subtract(taxPercentFederal.add(taxPercentState));
1111 }
1112
1113
1114 BigDecimal taxAmount = taxableAmount.multiply(taxPercent);
1115 taxAmount = taxAmount.divide(taxDivider, 5, BigDecimal.ROUND_HALF_UP);
1116
1117
1118 if (!isGross) {
1119 taxAmount = taxAmount.negate();
1120 }
1121
1122
1123 taxLine.setDocumentNumber(prqs.getDocumentNumber());
1124 taxLine.setSequenceNumber(prqs.getNextSourceLineNumber());
1125 taxLine.setChartOfAccountsCode(taxChart);
1126 taxLine.setAccountNumber(taxAccount);
1127 taxLine.setFinancialObjectCode(taxObjectCode);
1128 taxLine.setAmount(new KualiDecimal(taxAmount));
1129
1130
1131 taxLine.setItemIdentifier(taxItem.getItemIdentifier());
1132 taxLine.setPurapItem(taxItem);
1133 taxItem.getSourceAccountingLines().add(taxLine);
1134
1135 return taxLine;
1136 }
1137
1138
1139
1140
1141
1142
1143
1144 protected InvoiceItem findDiscountItem(InvoiceDocument invoiceDocument) {
1145 InvoiceItem discountItem = null;
1146 for (InvoiceItem prqsItem : (List<InvoiceItem>) invoiceDocument.getItems()) {
1147 if (StringUtils.equals(prqsItem.getItemTypeCode(), ItemTypeCodes.ITEM_TYPE_PMT_TERMS_DISCOUNT_CODE)) {
1148 discountItem = prqsItem;
1149 break;
1150 }
1151 }
1152 return discountItem;
1153 }
1154
1155
1156
1157
1158
1159
1160
1161 protected InvoiceItem findFullOrderDiscountItem(InvoiceDocument invoiceDocument) {
1162 InvoiceItem discountItem = null;
1163 for (InvoiceItem prqsItem : (List<InvoiceItem>) invoiceDocument.getItems()) {
1164 if (StringUtils.equals(prqsItem.getItemTypeCode(), ItemTypeCodes.ITEM_TYPE_ORDER_DISCOUNT_CODE)) {
1165 discountItem = prqsItem;
1166 break;
1167 }
1168 }
1169 return discountItem;
1170 }
1171
1172
1173
1174
1175
1176
1177 protected void distributeAccounting(InvoiceDocument invoiceDocument) {
1178
1179 purapAccountingService.updateAccountAmounts(invoiceDocument);
1180
1181 String accountDistributionMethod = invoiceDocument.getAccountDistributionMethod();
1182
1183 for (InvoiceItem item : (List<InvoiceItem>) invoiceDocument.getItems()) {
1184 KualiDecimal totalAmount = KualiDecimal.ZERO;
1185 List<PurApAccountingLine> distributedAccounts = null;
1186 List<SourceAccountingLine> summaryAccounts = null;
1187 Set excludedItemTypeCodes = new HashSet();
1188 excludedItemTypeCodes.add(ItemTypeCodes.ITEM_TYPE_PMT_TERMS_DISCOUNT_CODE);
1189
1190
1191 if (item.getItemType().isLineItemIndicator()) {
1192 continue;
1193 }
1194
1195 if ((item.getSourceAccountingLines().isEmpty()) && (ObjectUtils.isNotNull(item.getExtendedPrice())) && (KualiDecimal.ZERO.compareTo(item.getExtendedPrice()) != 0)) {
1196 if ((StringUtils.equals(ItemTypeCodes.ITEM_TYPE_PMT_TERMS_DISCOUNT_CODE, item.getItemType().getItemTypeCode())) && (invoiceDocument.getGrandTotal() != null) && ((KualiDecimal.ZERO.compareTo(invoiceDocument.getGrandTotal()) != 0))) {
1197
1198
1199
1200
1201
1202 totalAmount = invoiceDocument.getLineItemTotal();
1203
1204
1205 Set includedItemTypeCodes = new HashSet();
1206 includedItemTypeCodes.add(ItemTypeCodes.ITEM_TYPE_ITEM_CODE);
1207 includedItemTypeCodes.add(ItemTypeCodes.ITEM_TYPE_SERVICE_CODE);
1208
1209 summaryAccounts = purapAccountingService.generateSummaryIncludeItemTypesAndNoZeroTotals(invoiceDocument.getItems(), includedItemTypeCodes);
1210
1211
1212
1213 if (summaryAccounts != null) {
1214 distributedAccounts = purapAccountingService.generateAccountDistributionForProration(summaryAccounts, totalAmount, PurapConstants.PRORATION_SCALE, InvoiceAccount.class);
1215 }
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230 } else {
1231 PurchaseOrderItem poi = item.getPurchaseOrderItem();
1232 if ((poi != null) && (poi.getSourceAccountingLines() != null) && (!(poi.getSourceAccountingLines().isEmpty())) && (poi.getExtendedPrice() != null) && ((KualiDecimal.ZERO.compareTo(poi.getExtendedPrice())) != 0)) {
1233
1234
1235 item.generateAccountListFromPoItemAccounts(poi.getSourceAccountingLines());
1236 } else {
1237 totalAmount = invoiceDocument.getPurchaseOrderDocument().getTotalDollarAmountAboveLineItems();
1238 purapAccountingService.updateAccountAmounts(invoiceDocument.getPurchaseOrderDocument());
1239 summaryAccounts = purapAccountingService.generateSummary(PurApItemUtils.getAboveTheLineOnly(invoiceDocument.getPurchaseOrderDocument().getItems()));
1240 distributedAccounts = purapAccountingService.generateAccountDistributionForProration(summaryAccounts, totalAmount, new Integer("6"), InvoiceAccount.class);
1241 }
1242 }
1243 if (CollectionUtils.isNotEmpty(distributedAccounts) && CollectionUtils.isEmpty(item.getSourceAccountingLines())) {
1244 item.setSourceAccountingLines(distributedAccounts);
1245 }
1246 }
1247
1248 purapAccountingService.updateItemAccountAmounts(item);
1249 }
1250
1251
1252
1253 purapAccountingService.updateAccountAmounts(invoiceDocument);
1254 }
1255
1256
1257
1258
1259
1260 @Override
1261 public InvoiceDocument addHoldOnInvoice(InvoiceDocument document, String note) throws Exception {
1262
1263 Note noteObj = documentService.createNoteFromDocument(document, note);
1264 document.addNote(noteObj);
1265 noteService.save(noteObj);
1266
1267
1268
1269 document.setHoldIndicator(true);
1270 document.setLastActionPerformedByPersonId(GlobalVariables.getUserSession().getPerson().getPrincipalId());
1271 purapService.saveDocumentNoValidation(document);
1272
1273 return document;
1274 }
1275
1276
1277
1278
1279 @Override
1280 public InvoiceDocument removeHoldOnInvoice(InvoiceDocument document, String note) throws Exception {
1281
1282 Note noteObj = documentService.createNoteFromDocument(document, note);
1283 document.addNote(noteObj);
1284 noteService.save(noteObj);
1285
1286
1287
1288 document.setHoldIndicator(false);
1289 document.setLastActionPerformedByPersonId(null);
1290 purapService.saveDocumentNoValidation(document);
1291
1292 return document;
1293 }
1294
1295
1296
1297
1298
1299 @Override
1300 public void requestCancelOnInvoice(InvoiceDocument document, String note) throws Exception {
1301
1302 Note noteObj = documentService.createNoteFromDocument(document, note);
1303 document.addNote(noteObj);
1304 noteService.save(noteObj);
1305
1306
1307 document.setInvoiceCancelIndicator(true);
1308 document.setLastActionPerformedByPersonId(GlobalVariables.getUserSession().getPerson().getPrincipalId());
1309 document.setAccountsPayableRequestCancelIdentifier(GlobalVariables.getUserSession().getPerson().getPrincipalId());
1310 purapService.saveDocumentNoValidation(document);
1311 }
1312
1313
1314
1315
1316 @Override
1317 public void removeRequestCancelOnInvoice(InvoiceDocument document, String note) throws Exception {
1318
1319 Note noteObj = documentService.createNoteFromDocument(document, note);
1320 document.addNote(noteObj);
1321 noteService.save(noteObj);
1322
1323 clearRequestCancelFields(document);
1324
1325 purapService.saveDocumentNoValidation(document);
1326
1327 }
1328
1329
1330
1331
1332
1333
1334 protected void clearRequestCancelFields(InvoiceDocument document) {
1335 document.setInvoiceCancelIndicator(false);
1336 document.setLastActionPerformedByPersonId(null);
1337 document.setAccountsPayableRequestCancelIdentifier(null);
1338 }
1339
1340
1341
1342
1343 @Override
1344 public boolean isExtracted(InvoiceDocument document) {
1345 return (ObjectUtils.isNull(document.getExtractedTimestamp()) ? false : true);
1346 }
1347
1348 protected boolean isBeingAdHocRouted(InvoiceDocument document) {
1349 return financialSystemWorkflowHelperService.isAdhocApprovalRequestedForPrincipal(document.getDocumentHeader().getWorkflowDocument(), GlobalVariables.getUserSession().getPrincipalId());
1350 }
1351
1352
1353
1354
1355
1356 @Override
1357 public void cancelExtractedInvoice(InvoiceDocument invoice, String note) {
1358 LOG.debug("cancelExtractedInvoice() started");
1359 if (InvoiceStatuses.CANCELLED_STATUSES.contains(invoice.getApplicationDocumentStatus())) {
1360 LOG.debug("cancelExtractedInvoice() ended");
1361 return;
1362 }
1363
1364 try {
1365 Note cancelNote = documentService.createNoteFromDocument(invoice, note);
1366 invoice.addNote(cancelNote);
1367 noteService.save(cancelNote);
1368 } catch (Exception e) {
1369 throw new RuntimeException(PurapConstants.REQ_UNABLE_TO_CREATE_NOTE, e);
1370 }
1371
1372
1373 invoice.setReopenPurchaseOrderIndicator(false);
1374
1375 getAccountsPayableService().cancelAccountsPayableDocument(invoice, "");
1376
1377
1378 if (LOG.isDebugEnabled()) {
1379 LOG.debug("cancelExtractedInvoice() PRQS " + invoice.getPurapDocumentIdentifier() + " Cancelled Without Workflow");
1380 LOG.debug("cancelExtractedInvoice() ended");
1381 }
1382 }
1383
1384
1385
1386
1387
1388 @Override
1389 public void resetExtractedInvoice(InvoiceDocument invoice, String note) {
1390 LOG.debug("resetExtractedInvoice() started");
1391 if (InvoiceStatuses.CANCELLED_STATUSES.contains(invoice.getApplicationDocumentStatus())) {
1392 LOG.debug("resetExtractedInvoice() ended");
1393 return;
1394 }
1395 invoice.setExtractedTimestamp(null);
1396 invoice.setPaymentPaidTimestamp(null);
1397 String noteText = "This Invoice is being reset for extraction by PDP " + note;
1398 try {
1399 Note resetNote = documentService.createNoteFromDocument(invoice, noteText);
1400 invoice.addNote(resetNote);
1401 noteService.save(resetNote);
1402 } catch (Exception e) {
1403 throw new RuntimeException(PurapConstants.REQ_UNABLE_TO_CREATE_NOTE + " " + e);
1404 }
1405 purapService.saveDocumentNoValidation(invoice);
1406 if (LOG.isDebugEnabled()) {
1407 LOG.debug("resetExtractedInvoice() PRQS " + invoice.getPurapDocumentIdentifier() + " Reset from Extracted status");
1408 }
1409 }
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 @Override
1453 public String createPreqDocumentDescription(Integer purchaseOrderIdentifier, String vendorName) {
1454 StringBuffer descr = new StringBuffer("");
1455 descr.append("PO: ");
1456 descr.append(purchaseOrderIdentifier);
1457 descr.append(" Vendor: ");
1458 descr.append(StringUtils.trimToEmpty(vendorName));
1459
1460 int noteTextMaxLength = dataDictionaryService.getAttributeMaxLength(DocumentHeader.class, KRADPropertyConstants.DOCUMENT_DESCRIPTION).intValue();
1461 if (noteTextMaxLength >= descr.length()) {
1462 return descr.toString();
1463 } else {
1464 return descr.toString().substring(0, noteTextMaxLength);
1465 }
1466 }
1467
1468
1469
1470
1471 @Override
1472 public void populateAndSaveInvoice(InvoiceDocument prqs) throws WorkflowException {
1473 try {
1474 prqs.updateAndSaveAppDocStatus(InvoiceStatuses.APPDOC_IN_PROCESS);
1475 documentService.saveDocument(prqs, AttributedContinuePurapEvent.class);
1476 } catch (ValidationException ve) {
1477 prqs.updateAndSaveAppDocStatus(InvoiceStatuses.APPDOC_INITIATE);
1478 } catch (WorkflowException we) {
1479 prqs.updateAndSaveAppDocStatus(InvoiceStatuses.APPDOC_INITIATE);
1480
1481 String errorMsg = "Error saving document # " + prqs.getDocumentHeader().getDocumentNumber() + " " + we.getMessage();
1482 LOG.error(errorMsg, we);
1483 throw new RuntimeException(errorMsg, we);
1484 }
1485 }
1486
1487
1488
1489
1490
1491
1492
1493
1494
1495
1496 @Override
1497 public boolean shouldPurchaseOrderBeReversed(AccountsPayableDocument apDoc) {
1498 PurchaseOrderDocument po = apDoc.getPurchaseOrderDocument();
1499 if (ObjectUtils.isNull(po)) {
1500 throw new RuntimeException("po should never be null on PRQS");
1501 }
1502
1503 if (purapService.isFullDocumentEntryCompleted(apDoc) && StringUtils.equalsIgnoreCase(PurapConstants.PurchaseOrderStatuses.APPDOC_CLOSED, po.getApplicationDocumentStatus())) {
1504 return true;
1505 }
1506 return false;
1507 }
1508
1509
1510
1511
1512 @Override
1513 public Person getPersonForCancel(AccountsPayableDocument apDoc) {
1514 InvoiceDocument prqsDoc = (InvoiceDocument) apDoc;
1515 Person user = null;
1516 if (prqsDoc.isInvoiceCancelIndicator()) {
1517 user = prqsDoc.getLastActionPerformedByUser();
1518 }
1519 return user;
1520 }
1521
1522
1523
1524
1525 @Override
1526 public void takePurchaseOrderCancelAction(AccountsPayableDocument apDoc) {
1527 InvoiceDocument prqsDocument = (InvoiceDocument) apDoc;
1528 if (prqsDocument.isReopenPurchaseOrderIndicator()) {
1529 String docType = PurapConstants.PurchaseOrderDocTypes.PURCHASE_ORDER_REOPEN_DOCUMENT;
1530 purchaseOrderService.createAndRoutePotentialChangeDocument(prqsDocument.getPurchaseOrderDocument().getDocumentNumber(), docType, "reopened by Credit Memo " + apDoc.getPurapDocumentIdentifier() + "cancel", new ArrayList(), PurapConstants.PurchaseOrderStatuses.APPDOC_PENDING_REOPEN);
1531 }
1532 }
1533
1534
1535
1536
1537
1538 @Override
1539 public String updateStatusByNode(String currentNodeName, AccountsPayableDocument apDoc) {
1540 return updateStatusByNode(currentNodeName, (InvoiceDocument) apDoc);
1541 }
1542
1543
1544
1545
1546
1547
1548
1549
1550 protected String updateStatusByNode(String currentNodeName, InvoiceDocument prqsDoc) {
1551
1552 clearRequestCancelFields(prqsDoc);
1553
1554
1555
1556 String cancelledStatus = "";
1557 if (StringUtils.isEmpty(currentNodeName)) {
1558
1559 cancelledStatus = InvoiceStatuses.APPDOC_CANCELLED_POST_AP_APPROVE;
1560 } else {
1561 cancelledStatus = InvoiceStatuses.getInvoiceAppDocDisapproveStatuses().get(currentNodeName);
1562 }
1563
1564 if (StringUtils.isNotBlank(cancelledStatus)) {
1565 try {
1566 prqsDoc.updateAndSaveAppDocStatus(cancelledStatus);
1567 } catch (WorkflowException we) {
1568 throw new RuntimeException("Unable to save the route status data for document: " + prqsDoc.getDocumentNumber(), we);
1569 }
1570 purapService.saveDocumentNoValidation(prqsDoc);
1571 } else {
1572 logAndThrowRuntimeException("No status found to set for document being disapproved in node '" + currentNodeName + "'");
1573 }
1574 return cancelledStatus;
1575 }
1576
1577
1578
1579
1580
1581 @Override
1582 public void markPaid(InvoiceDocument pr, Date processDate) {
1583 LOG.debug("markPaid() started");
1584
1585 pr.setPaymentPaidTimestamp(new Timestamp(processDate.getTime()));
1586 purapService.saveDocumentNoValidation(pr);
1587 }
1588
1589
1590
1591
1592 @Override
1593 public boolean hasDiscountItem(InvoiceDocument prqs) {
1594 return ObjectUtils.isNotNull(findDiscountItem(prqs));
1595 }
1596
1597
1598
1599
1600
1601 @Override
1602 public boolean poItemEligibleForAp(AccountsPayableDocument apDoc, PurchaseOrderItem poi) {
1603 if (ObjectUtils.isNull(poi)) {
1604 throw new RuntimeException("item null in purchaseOrderItemEligibleForPayment ... this should never happen");
1605 }
1606
1607 if (!poi.isItemActiveIndicator()) {
1608 return false;
1609 }
1610
1611 ItemType poiType = poi.getItemType();
1612 if (ObjectUtils.isNull(poiType)) {
1613 return false;
1614 }
1615
1616 if (poiType.isQuantityBasedGeneralLedgerIndicator()) {
1617 if (poi.getItemQuantity().isGreaterThan(poi.getItemInvoicedTotalQuantity())) {
1618 return true;
1619 }
1620 return false;
1621 } else {
1622
1623
1624
1625 if (poi.getItemOutstandingEncumberedAmount() != null) {
1626 return true;
1627 }
1628 return false;
1629 }
1630 }
1631
1632 @Override
1633 public void removeIneligibleAdditionalCharges(InvoiceDocument document) {
1634
1635 List<InvoiceItem> itemsToRemove = new ArrayList<InvoiceItem>();
1636
1637 for (InvoiceItem item : (List<InvoiceItem>) document.getItems()) {
1638
1639
1640 if (ObjectUtils.isNull(item.getPurchaseOrderItemUnitPrice()) && (ItemTypeCodes.ITEM_TYPE_ORDER_DISCOUNT_CODE.equals(item.getItemTypeCode()) || ItemTypeCodes.ITEM_TYPE_TRADE_IN_CODE.equals(item.getItemTypeCode()))) {
1641 itemsToRemove.add(item);
1642 continue;
1643 }
1644
1645
1646 if (StringUtils.equals(item.getItemTypeCode(), ItemTypeCodes.ITEM_TYPE_PMT_TERMS_DISCOUNT_CODE)) {
1647 PaymentTermType pt = document.getVendorPaymentTerms();
1648 if ((pt != null) && (pt.getVendorPaymentTermsPercent() != null) && (BigDecimal.ZERO.compareTo(pt.getVendorPaymentTermsPercent()) != 0)) {
1649
1650 } else {
1651
1652 itemsToRemove.add(item);
1653 }
1654 continue;
1655 }
1656
1657 }
1658
1659
1660 for (InvoiceItem item : itemsToRemove) {
1661 document.getItems().remove(item);
1662 }
1663 }
1664
1665 @Override
1666 public void changeVendor(InvoiceDocument prqs, Integer headerId, Integer detailId) {
1667
1668 VendorDetail primaryVendor = vendorService.getVendorDetail(prqs.getOriginalVendorHeaderGeneratedIdentifier(), prqs.getOriginalVendorDetailAssignedIdentifier());
1669
1670 if (primaryVendor == null) {
1671 LOG.error("useAlternateVendor() primaryVendorDetail from database for header id " + headerId + " and detail id " + detailId + "is null");
1672 throw new PurError("AlternateVendor: VendorDetail from database for header id " + headerId + " and detail id " + detailId + "is null");
1673 }
1674
1675
1676 VendorDetail vd = vendorService.getVendorDetail(headerId, detailId);
1677 if (vd == null) {
1678 LOG.error("changeVendor() VendorDetail from database for header id " + headerId + " and detail id " + detailId + "is null");
1679 throw new PurError("changeVendor: VendorDetail from database for header id " + headerId + " and detail id " + detailId + "is null");
1680 }
1681 prqs.setVendorDetail(vd);
1682 prqs.setVendorName(vd.getVendorName());
1683 prqs.setVendorNumber(vd.getVendorNumber());
1684 prqs.setVendorHeaderGeneratedIdentifier(vd.getVendorHeaderGeneratedIdentifier());
1685 prqs.setVendorDetailAssignedIdentifier(vd.getVendorDetailAssignedIdentifier());
1686 prqs.setVendorPaymentTermsCode(vd.getVendorPaymentTermsCode());
1687 prqs.setVendorShippingPaymentTermsCode(vd.getVendorShippingPaymentTermsCode());
1688 prqs.setVendorShippingTitleCode(vd.getVendorShippingTitleCode());
1689 prqs.refreshReferenceObject("vendorPaymentTerms");
1690 prqs.refreshReferenceObject("vendorShippingPaymentTerms");
1691
1692
1693 String deliveryCampus = prqs.getPurchaseOrderDocument().getDeliveryCampusCode();
1694 VendorAddress va = vendorService.getVendorDefaultAddress(headerId, detailId, VendorConstants.AddressTypes.REMIT, deliveryCampus);
1695 if (va == null) {
1696 va = vendorService.getVendorDefaultAddress(headerId, detailId, VendorConstants.AddressTypes.PURCHASE_ORDER, deliveryCampus);
1697 }
1698 if (va == null) {
1699 LOG.error("changeVendor() VendorAddress from database for header id " + headerId + " and detail id " + detailId + "is null");
1700 throw new PurError("changeVendor VendorAddress from database for header id " + headerId + " and detail id " + detailId + "is null");
1701 }
1702
1703 if (prqs != null) {
1704 setVendorAddress(va, prqs);
1705 } else {
1706 LOG.error("changeVendor(): Null link back to the Purchase Order.");
1707 throw new PurError("Null link back to the Purchase Order.");
1708 }
1709
1710
1711
1712 }
1713
1714
1715
1716
1717
1718
1719
1720
1721 protected void setVendorAddress(VendorAddress va, InvoiceDocument inv) {
1722
1723 if (va != null) {
1724 inv.setVendorAddressGeneratedIdentifier(va.getVendorAddressGeneratedIdentifier());
1725 inv.setVendorAddressInternationalProvinceName(va.getVendorAddressInternationalProvinceName());
1726 inv.setVendorLine1Address(va.getVendorLine1Address());
1727 inv.setVendorLine2Address(va.getVendorLine2Address());
1728 inv.setVendorCityName(va.getVendorCityName());
1729 inv.setVendorStateCode(va.getVendorStateCode());
1730 inv.setVendorPostalCode(va.getVendorZipCode());
1731 inv.setVendorCountryCode(va.getVendorCountryCode());
1732 }
1733
1734 }
1735
1736
1737
1738
1739
1740
1741 protected void logAndThrowRuntimeException(String errorMessage) {
1742 this.logAndThrowRuntimeException(errorMessage, null);
1743 }
1744
1745
1746
1747
1748
1749
1750
1751 protected void logAndThrowRuntimeException(String errorMessage, Exception e) {
1752 if (ObjectUtils.isNotNull(e)) {
1753 LOG.error(errorMessage, e);
1754 throw new RuntimeException(errorMessage, e);
1755 } else {
1756 LOG.error(errorMessage);
1757 throw new RuntimeException(errorMessage);
1758 }
1759 }
1760
1761
1762
1763
1764
1765
1766 @Override
1767 public void generateGLEntriesCreateAccountsPayableDocument(AccountsPayableDocument apDocument) {
1768
1769
1770 InvoiceDocument invoice = (InvoiceDocument) apDocument;
1771
1772
1773 SpringContext.getBean(PurapGeneralLedgerService.class).generateEntriesCreateInvoice(invoice);
1774 }
1775
1776
1777
1778
1779 @Override
1780 public boolean hasActiveInvoicesForPurchaseOrder(Integer purchaseOrderIdentifier) {
1781
1782 boolean hasActivePreqs = false;
1783 List<String> docNumbers = null;
1784 WorkflowDocument workflowDocument = null;
1785
1786 docNumbers = invoiceDao.getActiveInvoiceDocumentNumbersForPurchaseOrder(purchaseOrderIdentifier);
1787 docNumbers = filterInvoiceByAppDocStatus(docNumbers, InvoiceStatuses.STATUSES_POTENTIALLY_ACTIVE);
1788
1789 for (String docNumber : docNumbers) {
1790 try {
1791 workflowDocument = workflowDocumentService.loadWorkflowDocument(docNumber, GlobalVariables.getUserSession().getPerson());
1792 } catch (WorkflowException we) {
1793 throw new RuntimeException(we);
1794 }
1795
1796 if (!(workflowDocument.isCanceled() || workflowDocument.isException())) {
1797 hasActivePreqs = true;
1798 break;
1799 }
1800 }
1801 return hasActivePreqs;
1802 }
1803
1804
1805
1806
1807
1808
1809
1810
1811
1812
1813
1814
1815 protected List<String> filterInvoiceByAppDocStatus(List<String> lookupDocNumbers, String... appDocStatus) {
1816 boolean valid = false;
1817
1818 final String DOC_NUM_DELIM = "|";
1819 StrBuilder routerHeaderIdBuilder = new StrBuilder().appendWithSeparators(lookupDocNumbers, DOC_NUM_DELIM);
1820
1821 List<String> invoiceDocNumbers = new ArrayList<String>();
1822
1823 DocumentSearchCriteria.Builder documentSearchCriteriaDTO = DocumentSearchCriteria.Builder.create();
1824 documentSearchCriteriaDTO.setDocumentId(routerHeaderIdBuilder.toString());
1825 documentSearchCriteriaDTO.setDocumentTypeName(PurapConstants.PurapDocTypeCodes.PAYMENT_REQUEST_DOCUMENT);
1826 documentSearchCriteriaDTO.setApplicationDocumentStatuses(Arrays.asList(appDocStatus));
1827
1828 DocumentSearchResults reqDocumentsList = KewApiServiceLocator.getWorkflowDocumentService().documentSearch(
1829 "", documentSearchCriteriaDTO.build());
1830
1831 for (DocumentSearchResult reqDocument : reqDocumentsList.getSearchResults()) {
1832
1833 if (Arrays.asList(appDocStatus).contains(reqDocument.getDocument().getApplicationDocumentStatus())) {
1834
1835 invoiceDocNumbers.add(reqDocument.getDocument().getDocumentId());
1836 }
1837
1838 }
1839
1840 return invoiceDocNumbers;
1841 }
1842
1843
1844
1845
1846
1847
1848
1849
1850
1851
1852
1853
1854 protected Collection<InvoiceDocument> filterInvoiceByAppDocStatus(Collection<InvoiceDocument> invoiceDocuments, String... appDocStatus) {
1855 List<String> invoiceDocNumbers = new ArrayList<String>();
1856 for (InvoiceDocument invoice : invoiceDocuments) {
1857 invoiceDocNumbers.add(invoice.getDocumentNumber());
1858 }
1859
1860 List<String> filteredInvoiceDocNumbers = filterInvoiceByAppDocStatus(invoiceDocNumbers, appDocStatus);
1861
1862 Collection<InvoiceDocument> filteredInvoiceDocuments = new ArrayList<InvoiceDocument>();
1863
1864 for (InvoiceDocument invoice : invoiceDocuments) {
1865 if (filteredInvoiceDocNumbers.contains(invoice.getDocumentNumber())) {
1866 filteredInvoiceDocuments.add(invoice);
1867 }
1868 }
1869 return filteredInvoiceDocuments;
1870 }
1871
1872
1873
1874
1875
1876
1877
1878
1879
1880
1881
1882
1883 protected Iterator<InvoiceDocument> filterInvoiceByAppDocStatus(Iterator<InvoiceDocument> invoiceIterator, String... appDocStatus) {
1884 Collection<InvoiceDocument> invoiceDocuments = new ArrayList<InvoiceDocument>();
1885 for (; invoiceIterator.hasNext(); ) {
1886 invoiceDocuments.add(invoiceIterator.next());
1887 }
1888
1889 return filterInvoiceByAppDocStatus(invoiceDocuments, appDocStatus).iterator();
1890 }
1891
1892
1893
1894
1895 @Override
1896 public void processInvoiceInReceivingStatus() {
1897 List<String> docNumbers = invoiceDao.getInvoiceInReceivingStatus();
1898 docNumbers = filterInvoiceByAppDocStatus(docNumbers, InvoiceStatuses.APPDOC_AWAITING_RECEIVING_REVIEW);
1899
1900 List<InvoiceDocument> prqssAwaitingReceiving = new ArrayList<InvoiceDocument>();
1901 for (String docNumber : docNumbers) {
1902 InvoiceDocument prqs = getInvoiceByDocumentNumber(docNumber);
1903 if (ObjectUtils.isNotNull(prqs)) {
1904 prqssAwaitingReceiving.add(prqs);
1905 }
1906 }
1907 if (ObjectUtils.isNotNull(prqssAwaitingReceiving)) {
1908 for (InvoiceDocument prqsDoc : prqssAwaitingReceiving) {
1909 if (prqsDoc.isReceivingRequirementMet()) {
1910 try {
1911 documentService.approveDocument(prqsDoc, "Approved by Receiving Required PRQS job", null);
1912 } catch (WorkflowException e) {
1913 LOG.error("processInvoiceInReceivingStatus() Error approving payment request document from awaiting receiving", e);
1914 throw new RuntimeException("Error approving payment request document from awaiting receiving", e);
1915 }
1916 }
1917 }
1918 }
1919 }
1920
1921
1922
1923
1924 @Override
1925 public boolean allowBackpost(InvoiceDocument invoiceDocument) {
1926 int allowBackpost = (Integer.parseInt(getParameter(OLEConstants.OptionalModuleNamespaces.PURCHASING_ACCOUNTS_PAYABLE, OLEConstants.InvoiceDocument.CMPNT_CD,PurapRuleConstants.ALLOW_BACKPOST_DAYS)));
1927
1928 Calendar today = dateTimeService.getCurrentCalendar();
1929 Integer currentFY = universityDateService.getCurrentUniversityDate().getUniversityFiscalYear();
1930 java.util.Date priorClosingDateTemp = universityDateService.getLastDateOfFiscalYear(currentFY - 1);
1931 Calendar priorClosingDate = Calendar.getInstance();
1932 priorClosingDate.setTime(priorClosingDateTemp);
1933
1934
1935 Calendar allowBackpostDate = Calendar.getInstance();
1936 allowBackpostDate.setTime(priorClosingDate.getTime());
1937 allowBackpostDate.add(Calendar.DATE, allowBackpost + 1);
1938
1939 Calendar prqsInvoiceDate = Calendar.getInstance();
1940 prqsInvoiceDate.setTime(invoiceDocument.getInvoiceDate() != null ? invoiceDocument.getInvoiceDate() : new java.util.Date());
1941
1942
1943
1944 if ((today.compareTo(priorClosingDate) > 0) && (today.compareTo(allowBackpostDate) <= 0) && (prqsInvoiceDate.compareTo(priorClosingDate) <= 0)) {
1945 LOG.debug("allowBackpost() within range to allow backpost; posting entry to period 12 of previous FY");
1946 return true;
1947 }
1948
1949 LOG.debug("allowBackpost() not within range to allow backpost; posting entry to current FY");
1950 return false;
1951 }
1952
1953 @Override
1954 public boolean isPurchaseOrderValidForInvoiceDocumentCreation(InvoiceDocument invoiceDocument, PurchaseOrderDocument po) {
1955 Integer POID = invoiceDocument.getPurchaseOrderIdentifier();
1956
1957 boolean valid = true;
1958
1959 PurchaseOrderDocument purchaseOrderDocument = invoiceDocument.getPurchaseOrderDocument();
1960 if (ObjectUtils.isNull(purchaseOrderDocument)) {
1961 GlobalVariables.getMessageMap().putError(PurapPropertyConstants.PURCHASE_ORDER_IDENTIFIER, PurapKeyConstants.ERROR_PURCHASE_ORDER_NOT_EXIST);
1962 valid &= false;
1963 } else if (purchaseOrderDocument.isPendingActionIndicator()) {
1964 GlobalVariables.getMessageMap().putError(PurapPropertyConstants.PURCHASE_ORDER_IDENTIFIER, PurapKeyConstants.ERROR_PURCHASE_PENDING_ACTION);
1965 valid &= false;
1966 } else if (!StringUtils.equals(purchaseOrderDocument.getApplicationDocumentStatus(), PurapConstants.PurchaseOrderStatuses.APPDOC_OPEN)) {
1967 GlobalVariables.getMessageMap().putError(PurapPropertyConstants.PURCHASE_ORDER_IDENTIFIER, PurapKeyConstants.ERROR_PURCHASE_ORDER_NOT_OPEN);
1968 valid &= false;
1969
1970 } else {
1971
1972
1973 }
1974
1975 return valid;
1976 }
1977
1978 @Override
1979 public boolean encumberedItemExistsForInvoicing(PurchaseOrderDocument document) {
1980 boolean zeroDollar = true;
1981 GlobalVariables.getMessageMap().clearErrorPath();
1982 GlobalVariables.getMessageMap().addToErrorPath(OLEPropertyConstants.DOCUMENT);
1983 for (PurchaseOrderItem poi : (List<PurchaseOrderItem>) document.getItems()) {
1984
1985 if (poi.getItemType().isLineItemIndicator() && poi.getItemType().isQuantityBasedGeneralLedgerIndicator()) {
1986 KualiDecimal encumberedQuantity = poi.getItemOutstandingEncumberedQuantity() == null ? KualiDecimal.ZERO : poi.getItemOutstandingEncumberedQuantity();
1987 if (encumberedQuantity.compareTo(KualiDecimal.ZERO) == 1) {
1988 zeroDollar = false;
1989 break;
1990 }
1991 }
1992
1993 else if (poi.getItemType().isAmountBasedGeneralLedgerIndicator() || poi.getItemType().isAdditionalChargeIndicator()) {
1994 KualiDecimal encumberedAmount = poi.getItemOutstandingEncumberedAmount() == null ? KualiDecimal.ZERO : poi.getItemOutstandingEncumberedAmount();
1995 if (encumberedAmount.compareTo(KualiDecimal.ZERO) == 1) {
1996 zeroDollar = false;
1997 break;
1998 }
1999 }
2000 }
2001
2002 return !zeroDollar;
2003 }
2004
2005 public void setDateTimeService(DateTimeService dateTimeService) {
2006 this.dateTimeService = dateTimeService;
2007 }
2008
2009 public void setParameterService(ParameterService parameterService) {
2010 this.parameterService = parameterService;
2011 }
2012
2013 public void setConfigurationService(ConfigurationService configurationService) {
2014 this.configurationService = configurationService;
2015 }
2016
2017 public void setDocumentService(DocumentService documentService) {
2018 this.documentService = documentService;
2019 }
2020
2021 public void setNoteService(NoteService noteService) {
2022 this.noteService = noteService;
2023 }
2024
2025 public void setPurapService(PurapService purapService) {
2026 this.purapService = purapService;
2027 }
2028
2029 public void setInvoiceDao(InvoiceDao invoiceDao) {
2030 this.invoiceDao = invoiceDao;
2031 }
2032
2033 public void setNegativeInvoiceApprovalLimitService(NegativeInvoiceApprovalLimitService negativeInvoiceApprovalLimitService) {
2034 this.negativeInvoiceApprovalLimitService = negativeInvoiceApprovalLimitService;
2035 }
2036
2037 public void setPurapAccountingService(PurapAccountingService purapAccountingService) {
2038 this.purapAccountingService = purapAccountingService;
2039 }
2040
2041 public void setBusinessObjectService(BusinessObjectService businessObjectService) {
2042 this.businessObjectService = businessObjectService;
2043 }
2044
2045 public void setPurapWorkflowIntegrationService(PurApWorkflowIntegrationService purapWorkflowIntegrationService) {
2046 this.purapWorkflowIntegrationService = purapWorkflowIntegrationService;
2047 }
2048
2049 public void setWorkflowDocumentService(WorkflowDocumentService workflowDocumentService) {
2050 this.workflowDocumentService = workflowDocumentService;
2051 }
2052
2053 public void setAccountsPayableService(AccountsPayableService accountsPayableService) {
2054 this.accountsPayableService = accountsPayableService;
2055 }
2056
2057 public void setVendorService(VendorService vendorService) {
2058 this.vendorService = vendorService;
2059 }
2060
2061 public void setDataDictionaryService(DataDictionaryService dataDictionaryService) {
2062 this.dataDictionaryService = dataDictionaryService;
2063 }
2064
2065 public void setUniversityDateService(UniversityDateService universityDateService) {
2066 this.universityDateService = universityDateService;
2067 }
2068
2069
2070 public void setBankService(BankService bankService) {
2071 this.bankService = bankService;
2072 }
2073
2074
2075 public void setPurchaseOrderService(PurchaseOrderService purchaseOrderService) {
2076 this.purchaseOrderService = purchaseOrderService;
2077 }
2078
2079 public void setFinancialSystemWorkflowHelperService(FinancialSystemWorkflowHelperService financialSystemWorkflowHelperService) {
2080 this.financialSystemWorkflowHelperService = financialSystemWorkflowHelperService;
2081 }
2082
2083
2084 public void setKualiRuleService(KualiRuleService kualiRuleService) {
2085 this.kualiRuleService = kualiRuleService;
2086 }
2087
2088
2089
2090
2091
2092
2093
2094 public AccountsPayableService getAccountsPayableService() {
2095 return SpringContext.getBean(AccountsPayableService.class);
2096 }
2097
2098 public String getParameter(String namespaceCode, String componentCode, String parameterName) {
2099 ParameterKey parameterKey = ParameterKey.create(OLEConstants.OLE_CMPNT,
2100 namespaceCode, componentCode, parameterName);
2101 Parameter parameter = CoreServiceApiServiceLocator.getParameterRepositoryService().getParameter(parameterKey);
2102 return parameter != null ? parameter.getValue() : null;
2103 }
2104
2105 public boolean getParameterBoolean(String namespaceCode, String componentCode, String parameterName) {
2106 ParameterKey parameterKey = ParameterKey.create(OLEConstants.OLE_CMPNT,
2107 namespaceCode, componentCode, parameterName);
2108 Parameter parameter = CoreServiceApiServiceLocator.getParameterRepositoryService().getParameter(parameterKey);
2109 if (parameter != null && parameter.getValue().equalsIgnoreCase("y")) {
2110 return true;
2111 }
2112 return false;
2113 }
2114 }