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