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