1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package org.kuali.ole.module.purap.service.impl;
17
18 import org.apache.commons.io.FileUtils;
19 import org.apache.commons.io.FilenameUtils;
20 import org.apache.commons.lang.ArrayUtils;
21 import org.apache.commons.lang.StringUtils;
22 import org.apache.commons.lang.math.NumberUtils;
23 import org.apache.xml.serialize.OutputFormat;
24 import org.apache.xml.serialize.XMLSerializer;
25 import org.kuali.ole.gl.service.impl.StringHelper;
26 import org.kuali.ole.module.purap.PurapConstants;
27 import org.kuali.ole.module.purap.PurapConstants.PurchaseOrderStatuses;
28 import org.kuali.ole.module.purap.PurapKeyConstants;
29 import org.kuali.ole.module.purap.PurapParameterConstants;
30 import org.kuali.ole.module.purap.batch.ElectronicInvoiceInputFileType;
31 import org.kuali.ole.module.purap.batch.ElectronicInvoiceStep;
32 import org.kuali.ole.module.purap.businessobject.*;
33 import org.kuali.ole.module.purap.dataaccess.ElectronicInvoicingDao;
34 import org.kuali.ole.module.purap.document.*;
35 import org.kuali.ole.module.purap.document.service.AccountsPayableService;
36 import org.kuali.ole.module.purap.document.service.PaymentRequestService;
37 import org.kuali.ole.module.purap.document.service.PurchaseOrderService;
38 import org.kuali.ole.module.purap.document.service.RequisitionService;
39 import org.kuali.ole.module.purap.document.validation.event.AttributedCalculateAccountsPayableEvent;
40 import org.kuali.ole.module.purap.document.validation.event.AttributedPaymentRequestForEInvoiceEvent;
41 import org.kuali.ole.module.purap.exception.CxmlParseException;
42 import org.kuali.ole.module.purap.exception.PurError;
43 import org.kuali.ole.module.purap.service.ElectronicInvoiceHelperService;
44 import org.kuali.ole.module.purap.service.ElectronicInvoiceMatchingService;
45 import org.kuali.ole.module.purap.util.ElectronicInvoiceUtils;
46 import org.kuali.ole.module.purap.util.ExpiredOrClosedAccountEntry;
47 import org.kuali.ole.sys.batch.InitiateDirectoryBase;
48 import org.kuali.ole.sys.batch.service.BatchInputFileService;
49 import org.kuali.ole.sys.businessobject.Bank;
50 import org.kuali.ole.sys.context.SpringContext;
51 import org.kuali.ole.sys.exception.ParseException;
52 import org.kuali.ole.sys.service.BankService;
53 import org.kuali.ole.vnd.businessobject.VendorDetail;
54 import org.kuali.ole.vnd.document.service.VendorService;
55 import org.kuali.rice.core.api.config.property.ConfigurationService;
56 import org.kuali.rice.core.api.datetime.DateTimeService;
57 import org.kuali.rice.core.api.mail.MailMessage;
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.exception.WorkflowException;
61 import org.kuali.rice.kim.api.identity.Person;
62 import org.kuali.rice.kim.api.services.KimApiServiceLocator;
63 import org.kuali.rice.kns.service.DataDictionaryService;
64 import org.kuali.rice.kns.util.KNSGlobalVariables;
65 import org.kuali.rice.krad.bo.Attachment;
66 import org.kuali.rice.krad.bo.DocumentHeader;
67 import org.kuali.rice.krad.bo.Note;
68 import org.kuali.rice.krad.bo.PersistableBusinessObject;
69 import org.kuali.rice.krad.exception.ValidationException;
70 import org.kuali.rice.krad.service.*;
71 import org.kuali.rice.krad.util.ErrorMessage;
72 import org.kuali.rice.krad.util.GlobalVariables;
73 import org.kuali.rice.krad.util.KRADPropertyConstants;
74 import org.kuali.rice.krad.util.ObjectUtils;
75 import org.springframework.transaction.annotation.Transactional;
76 import org.springframework.util.AutoPopulatingList;
77 import org.w3c.dom.Document;
78 import org.w3c.dom.Element;
79 import org.w3c.dom.Node;
80
81 import javax.xml.parsers.DocumentBuilder;
82 import javax.xml.parsers.DocumentBuilderFactory;
83 import javax.xml.parsers.ParserConfigurationException;
84 import java.io.*;
85 import java.math.BigDecimal;
86 import java.text.MessageFormat;
87 import java.util.*;
88
89
90
91
92
93
94 @Transactional
95 public class ElectronicInvoiceHelperServiceImpl extends InitiateDirectoryBase implements ElectronicInvoiceHelperService {
96 private static org.apache.log4j.Logger LOG = org.apache.log4j.Logger.getLogger(ElectronicInvoiceHelperServiceImpl.class);
97
98 protected final String UNKNOWN_DUNS_IDENTIFIER = "Unknown";
99 protected final String INVOICE_FILE_MIME_TYPE = "text/xml";
100
101 private StringBuffer emailTextErrorList;
102
103 protected ElectronicInvoiceInputFileType electronicInvoiceInputFileType;
104 protected MailService mailService;
105 protected ElectronicInvoiceMatchingService matchingService;
106 protected ElectronicInvoicingDao electronicInvoicingDao;
107 protected BatchInputFileService batchInputFileService;
108 protected VendorService vendorService;
109 protected PurchaseOrderService purchaseOrderService;
110 protected PaymentRequestService paymentRequestService;
111 protected ConfigurationService kualiConfigurationService;
112 protected DateTimeService dateTimeService;
113 protected ParameterService parameterService;
114
115 @Override
116 public ElectronicInvoiceLoad loadElectronicInvoices() {
117
118
119 prepareDirectories(getRequiredDirectoryNames());
120
121 String baseDirName = getBaseDirName();
122 String rejectDirName = getRejectDirName();
123 String acceptDirName = getAcceptDirName();
124 emailTextErrorList = new StringBuffer();
125
126 boolean moveFiles = SpringContext.getBean(ParameterService.class).getParameterValueAsBoolean(ElectronicInvoiceStep.class, PurapParameterConstants.ElectronicInvoiceParameters.FILE_MOVE_AFTER_LOAD_IND);
127
128 int failedCnt = 0;
129
130 if (LOG.isInfoEnabled()) {
131 LOG.info("Invoice Base Directory - " + electronicInvoiceInputFileType.getDirectoryPath());
132 LOG.info("Invoice Accept Directory - " + acceptDirName);
133 LOG.info("Invoice Reject Directory - " + rejectDirName);
134 LOG.info("Is moving files allowed - " + moveFiles);
135 }
136
137 if (StringUtils.isBlank(rejectDirName)) {
138 throw new RuntimeException("Reject directory name should not be empty");
139 }
140
141 if (StringUtils.isBlank(acceptDirName)) {
142 throw new RuntimeException("Accept directory name should not be empty");
143 }
144
145 File baseDir = new File(baseDirName);
146 if (!baseDir.exists()) {
147 throw new RuntimeException("Base dir [" + baseDirName + "] doesn't exists in the system");
148 }
149
150 File[] filesToBeProcessed = baseDir.listFiles(new FileFilter() {
151 @Override
152 public boolean accept(File file) {
153 String fullPath = FilenameUtils.getFullPath(file.getAbsolutePath());
154 String fileName = FilenameUtils.getBaseName(file.getAbsolutePath());
155 File processedFile = new File(fullPath + File.separator + fileName + ".processed");
156 return (!file.isDirectory() &&
157 file.getName().endsWith(".xml") &&
158 !processedFile.exists());
159 }
160 });
161
162
163 ElectronicInvoiceLoad eInvoiceLoad = new ElectronicInvoiceLoad();
164
165 if (filesToBeProcessed == null ||
166 filesToBeProcessed.length == 0) {
167
168 StringBuffer mailText = new StringBuffer();
169
170 mailText.append("\n\n");
171 mailText.append(PurapConstants.ElectronicInvoice.NO_FILES_PROCESSED_EMAIL_MESSAGE);
172 mailText.append("\n\n");
173
174 sendSummary(mailText);
175 return eInvoiceLoad;
176 }
177
178 try {
179
180
181
182 FileUtils.forceMkdir(new File(acceptDirName));
183 FileUtils.forceMkdir(new File(rejectDirName));
184 } catch (IOException e) {
185 throw new RuntimeException(e);
186 }
187
188 if (LOG.isInfoEnabled()) {
189 LOG.info(filesToBeProcessed.length + " file(s) available for processing");
190 }
191
192 StringBuilder emailMsg = new StringBuilder();
193
194 for (File element2 : filesToBeProcessed) {
195
196
197 File xmlFile = element2;
198 LOG.info("Processing " + xmlFile.getName() + "....");
199
200 byte[] modifiedXML = null;
201
202 if (xmlFile.length() != 0L) {
203 modifiedXML = addNamespaceDefinition(eInvoiceLoad, xmlFile);
204 }
205
206 boolean isRejected = false;
207
208 if (modifiedXML == null) {
209 isRejected = true;
210 } else {
211 try {
212 isRejected = processElectronicInvoice(eInvoiceLoad, xmlFile, modifiedXML);
213 } catch (Exception e) {
214 String msg = xmlFile.getName() + "\n";
215 LOG.error(msg);
216
217
218 StackTraceElement[] elements = e.getStackTrace();
219 StringBuffer trace = new StringBuffer();
220 trace.append(e.getClass().getName());
221 if (e.getMessage() != null) {
222 trace.append(": ");
223 trace.append(e.getMessage());
224 }
225 trace.append("\n");
226 for (StackTraceElement element : elements) {
227 trace.append(" at ");
228 trace.append(describeStackTraceElement(element));
229 trace.append("\n");
230 }
231
232 LOG.error(trace);
233 emailMsg.append(msg);
234 msg += "\n--------------------------------------------------------------------------------------\n" + trace;
235 logProcessElectronicInvoiceError(msg);
236 failedCnt++;
237
238
239
240
241
242 GlobalVariables.getMessageMap().clearErrorMessages();
243
244
245 continue;
246 }
247 }
248
249
250
251
252
253 if (isRejected) {
254 if (LOG.isInfoEnabled()) {
255 LOG.info(xmlFile.getName() + " has been rejected");
256 }
257 if (moveFiles) {
258 if (LOG.isInfoEnabled()) {
259 LOG.info(xmlFile.getName() + " has been marked to move to " + rejectDirName);
260 }
261 eInvoiceLoad.addRejectFileToMove(xmlFile, rejectDirName);
262 }
263 } else {
264 if (LOG.isInfoEnabled()) {
265 LOG.info(xmlFile.getName() + " has been accepted");
266 }
267 if (moveFiles) {
268 if (!moveFile(xmlFile, acceptDirName)) {
269 String msg = xmlFile.getName() + " unable to move";
270 LOG.error(msg);
271 throw new PurError(msg);
272 }
273 }
274 }
275
276 if (!moveFiles) {
277 String fullPath = FilenameUtils.getFullPath(xmlFile.getAbsolutePath());
278 String fileName = FilenameUtils.getBaseName(xmlFile.getAbsolutePath());
279 File processedFile = new File(fullPath + File.separator + fileName + ".processed");
280 try {
281 FileUtils.touch(processedFile);
282 } catch (IOException e) {
283 throw new RuntimeException(e);
284 }
285 }
286
287
288 deleteDoneFile(xmlFile);
289 }
290
291 emailTextErrorList.append("\nFAILED FILES\n");
292 emailTextErrorList.append("-----------------------------------------------------------\n\n");
293 emailTextErrorList.append(emailMsg);
294 emailTextErrorList.append("\nTOTAL COUNT\n");
295 emailTextErrorList.append("===========================\n");
296 emailTextErrorList.append(" " + failedCnt + " FAILED\n");
297 emailTextErrorList.append("===========================\n");
298
299 StringBuffer summaryText = saveLoadSummary(eInvoiceLoad);
300
301 StringBuffer finalText = new StringBuffer();
302 finalText.append(summaryText);
303 finalText.append("\n");
304 finalText.append(emailTextErrorList);
305 sendSummary(finalText);
306
307 LOG.info("Processing completed");
308
309 return eInvoiceLoad;
310
311 }
312
313 protected void logProcessElectronicInvoiceError(String msg) {
314 File file = new File(electronicInvoiceInputFileType.getReportPath() + "/" +
315 electronicInvoiceInputFileType.getReportPrefix() + "_" +
316 dateTimeService.toDateTimeStringForFilename(dateTimeService.getCurrentDate()) + "." +
317 electronicInvoiceInputFileType.getReportExtension());
318 BufferedWriter writer = null;
319
320 try {
321 writer = new BufferedWriter(new PrintWriter(file));
322 writer.write(msg);
323 writer.newLine();
324 } catch (FileNotFoundException e) {
325 LOG.error(file + " not found " + " " + e.getMessage());
326 throw new RuntimeException(file + " not found " + e.getMessage(), e);
327 } catch (IOException e) {
328 LOG.error("Error writing to BufferedWriter " + e.getMessage());
329 throw new RuntimeException("Error writing to BufferedWriter " + e.getMessage(), e);
330 } finally {
331 try {
332 writer.flush();
333 writer.close();
334 } catch (Exception e) {
335 }
336 }
337 }
338
339
340
341
342
343 private static String describeStackTraceElement(StackTraceElement element) {
344 StringBuffer description = new StringBuffer();
345 if (element == null) {
346 description.append("invalid (null) element");
347 }
348 description.append(element.getClassName());
349 description.append(".");
350 description.append(element.getMethodName());
351 description.append("(");
352 description.append(element.getFileName());
353 description.append(":");
354 description.append(element.getLineNumber());
355 description.append(")");
356
357 return description.toString();
358 }
359
360 protected byte[] addNamespaceDefinition(ElectronicInvoiceLoad eInvoiceLoad,
361 File invoiceFile) {
362
363
364 boolean result = true;
365
366 if (LOG.isInfoEnabled()) {
367 LOG.info("Adding namespace definition");
368 }
369
370 DocumentBuilderFactory builderFactory = DocumentBuilderFactory.newInstance();
371 builderFactory.setValidating(false);
372 builderFactory.setIgnoringElementContentWhitespace(true);
373
374 DocumentBuilder builder = null;
375 try {
376 builder = builderFactory.newDocumentBuilder();
377 } catch (ParserConfigurationException e) {
378 LOG.error("Error getting document builder - " + e.getMessage());
379 throw new RuntimeException(e);
380 }
381
382 Document xmlDoc = null;
383
384 try {
385 xmlDoc = builder.parse(invoiceFile);
386 } catch (Exception e) {
387 if (LOG.isInfoEnabled()) {
388 LOG.info("Error parsing the file - " + e.getMessage());
389 }
390 rejectElectronicInvoiceFile(eInvoiceLoad, UNKNOWN_DUNS_IDENTIFIER, invoiceFile, e.getMessage(), PurapConstants.ElectronicInvoice.FILE_FORMAT_INVALID);
391 return null;
392 }
393
394 Node node = xmlDoc.getDocumentElement();
395 Element element = (Element) node;
396
397 String xmlnsValue = element.getAttribute("xmlns");
398 String xmlnsXsiValue = element.getAttribute("xmlns:xsi");
399
400 File namespaceAddedFile = getInvoiceFile(invoiceFile.getName());
401
402 if (StringUtils.equals(xmlnsValue, "http://www.kuali.org/ole/purap/electronicInvoice") &&
403 StringUtils.equals(xmlnsXsiValue, "http://www.w3.org/2001/XMLSchema-instance")) {
404 if (LOG.isInfoEnabled()) {
405 LOG.info("xmlns and xmlns:xsi attributes already exists in the invoice xml");
406 }
407 } else {
408 element.setAttribute("xmlns", "http://www.kuali.org/ole/purap/electronicInvoice");
409 element.setAttribute("xmlns:xsi", "http://www.w3.org/2001/XMLSchema-instance");
410 }
411
412 OutputFormat outputFormat = new OutputFormat(xmlDoc);
413 outputFormat.setOmitDocumentType(true);
414
415 ByteArrayOutputStream out = new ByteArrayOutputStream();
416 XMLSerializer serializer = new XMLSerializer(out, outputFormat);
417 try {
418 serializer.asDOMSerializer();
419 serializer.serialize(xmlDoc.getDocumentElement());
420 } catch (IOException e) {
421 throw new RuntimeException(e);
422 }
423
424 if (LOG.isInfoEnabled()) {
425 LOG.info("Namespace validation completed");
426 }
427
428 return out.toByteArray();
429
430 }
431
432
433
434
435
436
437
438 protected boolean processElectronicInvoice(ElectronicInvoiceLoad eInvoiceLoad,
439 File invoiceFile,
440 byte[] xmlAsBytes) {
441
442 ElectronicInvoice eInvoice = null;
443
444 try {
445 eInvoice = loadElectronicInvoice(xmlAsBytes);
446 } catch (CxmlParseException e) {
447 LOG.info("Error loading file - " + e.getMessage());
448 rejectElectronicInvoiceFile(eInvoiceLoad, UNKNOWN_DUNS_IDENTIFIER, invoiceFile, e.getMessage(), PurapConstants.ElectronicInvoice.FILE_FORMAT_INVALID);
449 return true;
450 }
451
452 eInvoice.setFileName(invoiceFile.getName());
453
454 boolean isCompleteFailure = checkForCompleteFailure(eInvoiceLoad, eInvoice, invoiceFile);
455
456 if (isCompleteFailure) {
457 return true;
458 }
459
460 setVendorDUNSNumber(eInvoice);
461 setVendorDetails(eInvoice);
462
463 Map itemTypeMappings = getItemTypeMappings(eInvoice.getVendorHeaderID(), eInvoice.getVendorDetailID());
464 Map kualiItemTypes = getKualiItemTypes();
465
466 if (LOG.isInfoEnabled()) {
467 if (itemTypeMappings != null && itemTypeMappings.size() > 0) {
468 LOG.info("Item mappings found");
469 }
470 }
471
472 boolean validateHeader = true;
473
474 for (ElectronicInvoiceOrder order : eInvoice.getInvoiceDetailOrders()) {
475
476 String poID = order.getOrderReferenceOrderID();
477 PurchaseOrderDocument po = null;
478
479 if (NumberUtils.isDigits(StringUtils.defaultString(poID))) {
480 po = purchaseOrderService.getCurrentPurchaseOrder(new Integer(poID));
481 if (po != null) {
482 order.setInvoicePurchaseOrderID(poID);
483 order.setPurchaseOrderID(po.getPurapDocumentIdentifier());
484 order.setPurchaseOrderCampusCode(po.getDeliveryCampusCode());
485
486 if (LOG.isInfoEnabled()) {
487 LOG.info("PO matching Document found");
488 }
489 }
490 }
491
492 ElectronicInvoiceOrderHolder orderHolder = new ElectronicInvoiceOrderHolder(eInvoice, order, po, itemTypeMappings, kualiItemTypes, validateHeader);
493 matchingService.doMatchingProcess(orderHolder);
494
495 if (orderHolder.isInvoiceRejected()) {
496
497 ElectronicInvoiceRejectDocument rejectDocument = createRejectDocument(eInvoice, order, eInvoiceLoad);
498
499 if (orderHolder.getAccountsPayablePurchasingDocumentLinkIdentifier() != null) {
500 rejectDocument.setAccountsPayablePurchasingDocumentLinkIdentifier(orderHolder.getAccountsPayablePurchasingDocumentLinkIdentifier());
501 }
502
503 String dunsNumber = StringUtils.isEmpty(eInvoice.getDunsNumber()) ?
504 UNKNOWN_DUNS_IDENTIFIER :
505 eInvoice.getDunsNumber();
506
507 ElectronicInvoiceLoadSummary loadSummary = getOrCreateLoadSummary(eInvoiceLoad, dunsNumber);
508 loadSummary.addFailedInvoiceOrder(rejectDocument.getTotalAmount(), eInvoice);
509 eInvoiceLoad.insertInvoiceLoadSummary(loadSummary);
510
511 } else {
512
513 PaymentRequestDocument preqDoc = createPaymentRequest(orderHolder);
514
515 if (orderHolder.isInvoiceRejected()) {
516
517
518
519
520 GlobalVariables.getMessageMap().clearErrorMessages();
521
522 ElectronicInvoiceRejectDocument rejectDocument = createRejectDocument(eInvoice, order, eInvoiceLoad);
523
524 if (orderHolder.getAccountsPayablePurchasingDocumentLinkIdentifier() != null) {
525 rejectDocument.setAccountsPayablePurchasingDocumentLinkIdentifier(orderHolder.getAccountsPayablePurchasingDocumentLinkIdentifier());
526 }
527
528 ElectronicInvoiceLoadSummary loadSummary = getOrCreateLoadSummary(eInvoiceLoad, eInvoice.getDunsNumber());
529 loadSummary.addFailedInvoiceOrder(rejectDocument.getTotalAmount(), eInvoice);
530 eInvoiceLoad.insertInvoiceLoadSummary(loadSummary);
531
532 } else {
533 ElectronicInvoiceLoadSummary loadSummary = getOrCreateLoadSummary(eInvoiceLoad, eInvoice.getDunsNumber());
534 loadSummary.addSuccessfulInvoiceOrder(preqDoc.getTotalDollarAmount(), eInvoice);
535 eInvoiceLoad.insertInvoiceLoadSummary(loadSummary);
536 }
537
538 }
539
540 validateHeader = false;
541 }
542
543 return eInvoice.isFileRejected();
544 }
545
546 protected void setVendorDUNSNumber(ElectronicInvoice eInvoice) {
547
548 String dunsNumber = null;
549
550 if (StringUtils.equals(eInvoice.getCxmlHeader().getFromDomain(), "DUNS")) {
551 dunsNumber = eInvoice.getCxmlHeader().getFromIdentity();
552 } else if (StringUtils.equals(eInvoice.getCxmlHeader().getSenderDomain(), "DUNS")) {
553 dunsNumber = eInvoice.getCxmlHeader().getSenderIdentity();
554 }
555
556 if (StringUtils.isNotEmpty((dunsNumber))) {
557 if (LOG.isInfoEnabled()) {
558 LOG.info("Setting Vendor DUNS number - " + dunsNumber);
559 }
560 eInvoice.setDunsNumber(dunsNumber);
561 }
562
563 }
564
565 protected void setVendorDetails(ElectronicInvoice eInvoice) {
566
567 if (StringUtils.isNotEmpty(eInvoice.getDunsNumber())) {
568
569 VendorDetail vendorDetail = vendorService.getVendorByDunsNumber(eInvoice.getDunsNumber());
570
571 if (vendorDetail != null) {
572 if (LOG.isInfoEnabled()) {
573 LOG.info("Vendor match found - " + vendorDetail.getVendorNumber());
574 }
575 eInvoice.setVendorHeaderID(vendorDetail.getVendorHeaderGeneratedIdentifier());
576 eInvoice.setVendorDetailID(vendorDetail.getVendorDetailAssignedIdentifier());
577 eInvoice.setVendorName(vendorDetail.getVendorName());
578 } else {
579 eInvoice.setVendorHeaderID(null);
580 eInvoice.setVendorDetailID(null);
581 eInvoice.setVendorName(null);
582 }
583 }
584 }
585
586 protected void validateVendorDetails(ElectronicInvoiceRejectDocument rejectDocument) {
587
588 boolean vendorFound = false;
589
590 if (StringUtils.isNotEmpty(rejectDocument.getVendorDunsNumber())) {
591
592 VendorDetail vendorDetail = vendorService.getVendorByDunsNumber(rejectDocument.getVendorDunsNumber());
593
594 if (vendorDetail != null) {
595 if (LOG.isInfoEnabled()) {
596 LOG.info("Vendor [" + vendorDetail.getVendorNumber() + "] match found for the DUNS - " + rejectDocument.getVendorDunsNumber());
597 }
598 rejectDocument.setVendorHeaderGeneratedIdentifier(vendorDetail.getVendorHeaderGeneratedIdentifier());
599 rejectDocument.setVendorDetailAssignedIdentifier(vendorDetail.getVendorDetailAssignedIdentifier());
600 rejectDocument.setVendorDetail(vendorDetail);
601 vendorFound = true;
602 }
603 }
604
605 if (!vendorFound) {
606 rejectDocument.setVendorHeaderGeneratedIdentifier(null);
607 rejectDocument.setVendorDetailAssignedIdentifier(null);
608 rejectDocument.setVendorDetail(null);
609 }
610
611 String newDocumentDesc = generateRejectDocumentDescription(rejectDocument);
612 rejectDocument.getDocumentHeader().setDocumentDescription(newDocumentDesc);
613 }
614
615 protected Map getItemTypeMappings(Integer vendorHeaderId,
616 Integer vendorDetailId) {
617
618 Map itemTypeMappings = null;
619
620 if (vendorHeaderId != null && vendorDetailId != null) {
621 String vendorNumber = getVendorNumber(vendorHeaderId, vendorDetailId);
622 itemTypeMappings = electronicInvoicingDao.getItemMappingMap(vendorHeaderId, vendorDetailId);
623 }
624
625 if (itemTypeMappings == null || itemTypeMappings.isEmpty()) {
626 itemTypeMappings = electronicInvoicingDao.getDefaultItemMappingMap();
627 }
628
629 return itemTypeMappings;
630 }
631
632 protected String getVendorNumber(Integer vendorHeaderId,
633 Integer vendorDetailId) {
634
635 if (vendorHeaderId != null && vendorDetailId != null) {
636 VendorDetail forVendorNo = new VendorDetail();
637 forVendorNo.setVendorHeaderGeneratedIdentifier(vendorHeaderId);
638 forVendorNo.setVendorDetailAssignedIdentifier(vendorDetailId);
639 return forVendorNo.getVendorNumber();
640 } else {
641 return null;
642 }
643 }
644
645 protected Map<String, ItemType> getKualiItemTypes() {
646
647 Collection<ItemType> collection = SpringContext.getBean(BusinessObjectService.class).findAll(ItemType.class);
648 Map kualiItemTypes = new HashMap<String, ItemType>();
649
650 if (collection == null || collection.size() == 0) {
651 throw new RuntimeException("Kauli Item types not available");
652 } else {
653 if (collection != null) {
654 ItemType[] itemTypes = new ItemType[collection.size()];
655 collection.toArray(itemTypes);
656 for (ItemType itemType : itemTypes) {
657 kualiItemTypes.put(itemType.getItemTypeCode(), itemType);
658 }
659 }
660 }
661
662 return kualiItemTypes;
663 }
664
665 protected boolean checkForCompleteFailure(ElectronicInvoiceLoad electronicInvoiceLoad,
666 ElectronicInvoice electronicInvoice,
667 File invoiceFile) {
668
669 if (LOG.isInfoEnabled()) {
670 LOG.info("Checking for complete failure...");
671 }
672
673 if (electronicInvoice.getInvoiceDetailRequestHeader().isHeaderInvoiceIndicator()) {
674 rejectElectronicInvoiceFile(electronicInvoiceLoad, UNKNOWN_DUNS_IDENTIFIER, invoiceFile, PurapConstants.ElectronicInvoice.HEADER_INVOICE_IND_ON);
675 return true;
676 }
677
678 if (electronicInvoice.getInvoiceDetailOrders().size() < 1) {
679 rejectElectronicInvoiceFile(electronicInvoiceLoad, UNKNOWN_DUNS_IDENTIFIER, invoiceFile, PurapConstants.ElectronicInvoice.INVOICE_ORDERS_NOT_FOUND);
680 return true;
681 }
682
683
684
685
686
687 for (Object element : electronicInvoice.getInvoiceDetailOrders()) {
688 ElectronicInvoiceOrder invoiceOrder = (ElectronicInvoiceOrder) element;
689 for (Object element2 : invoiceOrder.getInvoiceItems()) {
690 ElectronicInvoiceItem invoiceItem = (ElectronicInvoiceItem) element2;
691 if (invoiceItem != null) {
692 invoiceItem.setCatalogNumber(invoiceItem.getReferenceItemIDSupplierPartID());
693 }
694 }
695 }
696
697 if (LOG.isInfoEnabled()) {
698 LOG.info("No Complete failure");
699 }
700
701 return false;
702
703 }
704
705 protected ElectronicInvoiceRejectReasonType getRejectReasonType(String rejectReasonTypeCode) {
706 return matchingService.getElectronicInvoiceRejectReasonType(rejectReasonTypeCode);
707 }
708
709 protected void rejectElectronicInvoiceFile(ElectronicInvoiceLoad eInvoiceLoad,
710 String fileDunsNumber,
711 File filename,
712 String rejectReasonTypeCode) {
713
714 rejectElectronicInvoiceFile(eInvoiceLoad, fileDunsNumber, filename, null, rejectReasonTypeCode);
715 }
716
717 protected void rejectElectronicInvoiceFile(ElectronicInvoiceLoad eInvoiceLoad,
718 String fileDunsNumber,
719 File invoiceFile,
720 String extraDescription,
721 String rejectReasonTypeCode) {
722 if (LOG.isInfoEnabled()) {
723 LOG.info("Rejecting the entire invoice file - " + invoiceFile.getName());
724 }
725
726 ElectronicInvoiceLoadSummary eInvoiceLoadSummary = getOrCreateLoadSummary(eInvoiceLoad, fileDunsNumber);
727 eInvoiceLoadSummary.addFailedInvoiceOrder();
728 eInvoiceLoad.insertInvoiceLoadSummary(eInvoiceLoadSummary);
729
730 ElectronicInvoiceRejectDocument eInvoiceRejectDocument = null;
731 try {
732 eInvoiceRejectDocument = (ElectronicInvoiceRejectDocument) SpringContext.getBean(DocumentService.class).getNewDocument("EIRT");
733
734 eInvoiceRejectDocument.setInvoiceProcessTimestamp(SpringContext.getBean(DateTimeService.class).getCurrentTimestamp());
735 eInvoiceRejectDocument.setVendorDunsNumber(fileDunsNumber);
736 eInvoiceRejectDocument.setDocumentCreationInProgress(true);
737
738 if (invoiceFile != null) {
739 eInvoiceRejectDocument.setInvoiceFileName(invoiceFile.getName());
740 }
741
742 List<ElectronicInvoiceRejectReason> list = new ArrayList<ElectronicInvoiceRejectReason>(1);
743
744 String message = "Complete failure document has been created for the Invoice with Filename '" + invoiceFile.getName() + "' due to the following error:\n";
745 emailTextErrorList.append(message);
746
747 ElectronicInvoiceRejectReason rejectReason = matchingService.createRejectReason(rejectReasonTypeCode, extraDescription, invoiceFile.getName());
748 list.add(rejectReason);
749
750 emailTextErrorList.append(" - " + rejectReason.getInvoiceRejectReasonDescription());
751 emailTextErrorList.append("\n\n");
752
753 eInvoiceRejectDocument.setInvoiceRejectReasons(list);
754 eInvoiceRejectDocument.getDocumentHeader().setDocumentDescription("Complete failure");
755
756 String noteText = "Invoice file";
757 attachInvoiceXMLWithRejectDoc(eInvoiceRejectDocument, invoiceFile, noteText);
758
759 eInvoiceLoad.addInvoiceReject(eInvoiceRejectDocument);
760
761 } catch (WorkflowException e) {
762 throw new RuntimeException(e);
763 }
764
765 if (LOG.isInfoEnabled()) {
766 LOG.info("Complete failure document has been created (DocNo:" + eInvoiceRejectDocument.getDocumentNumber() + ")");
767 }
768 }
769
770 protected void attachInvoiceXMLWithRejectDoc(ElectronicInvoiceRejectDocument eInvoiceRejectDocument,
771 File attachmentFile,
772 String noteText) {
773
774 Note note = null;
775 try {
776 note = SpringContext.getBean(DocumentService.class).createNoteFromDocument(eInvoiceRejectDocument, noteText);
777 } catch (Exception e1) {
778 throw new RuntimeException("Unable to create note from document: ", e1);
779 }
780
781 String attachmentType = null;
782 BufferedInputStream fileStream = null;
783 try {
784 fileStream = new BufferedInputStream(new FileInputStream(attachmentFile));
785 } catch (FileNotFoundException e) {
786 LOG.error("Exception opening attachment file", e);
787 }
788
789 Attachment attachment = null;
790 try {
791 attachment = SpringContext.getBean(AttachmentService.class).createAttachment(eInvoiceRejectDocument, attachmentFile.getName(), INVOICE_FILE_MIME_TYPE, (int) attachmentFile.length(), fileStream, attachmentType);
792 } catch (IOException e) {
793 throw new RuntimeException("Unable to create attachment", e);
794 } finally {
795 if (fileStream != null) {
796 try {
797 fileStream.close();
798 } catch (IOException e) {
799 LOG.error("Exception closing file", e);
800 }
801 }
802 }
803
804 note.setAttachment(attachment);
805 attachment.setNote(note);
806 SpringContext.getBean(NoteService.class).save(note);
807 }
808
809 public ElectronicInvoiceRejectDocument createRejectDocument(ElectronicInvoice eInvoice,
810 ElectronicInvoiceOrder electronicInvoiceOrder,
811 ElectronicInvoiceLoad eInvoiceLoad) {
812
813 if (LOG.isInfoEnabled()) {
814 LOG.info("Creating reject document [DUNS=" + eInvoice.getDunsNumber() + ",POID=" + electronicInvoiceOrder.getInvoicePurchaseOrderID() + "]");
815 }
816
817 ElectronicInvoiceRejectDocument eInvoiceRejectDocument;
818
819 try {
820
821 eInvoiceRejectDocument = (ElectronicInvoiceRejectDocument) SpringContext.getBean(DocumentService.class).getNewDocument("EIRT");
822
823 eInvoiceRejectDocument.setInvoiceProcessTimestamp(SpringContext.getBean(DateTimeService.class).getCurrentTimestamp());
824 String rejectdocDesc = generateRejectDocumentDescription(eInvoice, electronicInvoiceOrder);
825 eInvoiceRejectDocument.getDocumentHeader().setDocumentDescription(rejectdocDesc);
826 eInvoiceRejectDocument.setDocumentCreationInProgress(true);
827
828 eInvoiceRejectDocument.setFileLevelData(eInvoice);
829 eInvoiceRejectDocument.setInvoiceOrderLevelData(eInvoice, electronicInvoiceOrder);
830
831
832 SpringContext.getBean(DocumentService.class).saveDocument(eInvoiceRejectDocument);
833
834 String noteText = "Invoice file";
835 attachInvoiceXMLWithRejectDoc(eInvoiceRejectDocument, getInvoiceFile(eInvoice.getFileName()), noteText);
836
837 eInvoiceLoad.addInvoiceReject(eInvoiceRejectDocument);
838
839 } catch (WorkflowException e) {
840 throw new RuntimeException(e);
841 }
842
843 if (LOG.isInfoEnabled()) {
844 LOG.info("Reject document has been created (DocNo=" + eInvoiceRejectDocument.getDocumentNumber() + ")");
845 }
846
847 emailTextErrorList.append("DUNS Number - " + eInvoice.getDunsNumber() + " " + eInvoice.getVendorName() + ":\n");
848 emailTextErrorList.append("An Invoice from file '" + eInvoice.getFileName() + "' has been rejected due to the following error(s):\n");
849
850 int index = 1;
851 for (ElectronicInvoiceRejectReason reason : eInvoiceRejectDocument.getInvoiceRejectReasons()) {
852 emailTextErrorList.append(" - " + reason.getInvoiceRejectReasonDescription() + "\n");
853 addRejectReasonsToNote("Reject Reason " + index + ". " + reason.getInvoiceRejectReasonDescription(), eInvoiceRejectDocument);
854 index++;
855 }
856
857 emailTextErrorList.append("\n");
858
859 return eInvoiceRejectDocument;
860 }
861
862 protected void addRejectReasonsToNote(String rejectReasons, ElectronicInvoiceRejectDocument eInvoiceRejectDocument) {
863
864 try {
865 Note note = SpringContext.getBean(DocumentService.class).createNoteFromDocument(eInvoiceRejectDocument, rejectReasons);
866 PersistableBusinessObject noteParent = eInvoiceRejectDocument.getNoteTarget();
867 SpringContext.getBean(NoteService.class).save(note);
868 } catch (Exception e) {
869 LOG.error("Error creating reject reason note - " + e.getMessage());
870 }
871 }
872
873
874 protected String generateRejectDocumentDescription(ElectronicInvoice eInvoice,
875 ElectronicInvoiceOrder electronicInvoiceOrder) {
876
877 String poID = StringUtils.isEmpty(electronicInvoiceOrder.getInvoicePurchaseOrderID()) ?
878 "UNKNOWN" :
879 electronicInvoiceOrder.getInvoicePurchaseOrderID();
880
881 String vendorName = StringUtils.isEmpty(eInvoice.getVendorName()) ?
882 "UNKNOWN" :
883 eInvoice.getVendorName();
884
885 String description = "PO: " + poID + " Vendor: " + vendorName;
886
887 return checkDescriptionLengthAndStripIfNeeded(description);
888 }
889
890 protected String generateRejectDocumentDescription(ElectronicInvoiceRejectDocument rejectDoc) {
891
892 String poID = StringUtils.isEmpty(rejectDoc.getInvoicePurchaseOrderNumber()) ?
893 "UNKNOWN" :
894 rejectDoc.getInvoicePurchaseOrderNumber();
895
896 String vendorName = "UNKNOWN";
897 if (rejectDoc.getVendorDetail() != null) {
898 vendorName = rejectDoc.getVendorDetail().getVendorName();
899 }
900
901 String description = "PO: " + poID + " Vendor: " + vendorName;
902
903 return checkDescriptionLengthAndStripIfNeeded(description);
904 }
905
906 protected String checkDescriptionLengthAndStripIfNeeded(String description) {
907
908 int noteTextMaxLength = SpringContext.getBean(DataDictionaryService.class).getAttributeMaxLength(DocumentHeader.class, KRADPropertyConstants.DOCUMENT_DESCRIPTION).intValue();
909
910 if (noteTextMaxLength < description.length()) {
911 description = description.substring(0, noteTextMaxLength);
912 }
913
914 return description;
915 }
916
917 public ElectronicInvoiceLoadSummary getOrCreateLoadSummary(ElectronicInvoiceLoad eInvoiceLoad,
918 String fileDunsNumber) {
919 ElectronicInvoiceLoadSummary eInvoiceLoadSummary;
920
921 if (eInvoiceLoad.getInvoiceLoadSummaries().containsKey(fileDunsNumber)) {
922 eInvoiceLoadSummary = (ElectronicInvoiceLoadSummary) eInvoiceLoad.getInvoiceLoadSummaries().get(fileDunsNumber);
923 } else {
924 eInvoiceLoadSummary = new ElectronicInvoiceLoadSummary(fileDunsNumber);
925 }
926
927 return eInvoiceLoadSummary;
928
929 }
930
931 public ElectronicInvoice loadElectronicInvoice(byte[] xmlAsBytes)
932 throws CxmlParseException {
933
934 if (LOG.isInfoEnabled()) {
935 LOG.info("Loading Invoice File");
936 }
937
938 ElectronicInvoice electronicInvoice = null;
939
940 try {
941 electronicInvoice = (ElectronicInvoice) batchInputFileService.parse(electronicInvoiceInputFileType, xmlAsBytes);
942 } catch (ParseException e) {
943 throw new CxmlParseException(e.getMessage());
944 }
945
946 if (LOG.isInfoEnabled()) {
947 LOG.info("Successfully loaded the Invoice File");
948 }
949
950 return electronicInvoice;
951
952 }
953
954 protected StringBuffer saveLoadSummary(ElectronicInvoiceLoad eInvoiceLoad) {
955
956 Map savedLoadSummariesMap = new HashMap();
957 StringBuffer summaryMessage = new StringBuffer();
958
959 for (Iterator iter = eInvoiceLoad.getInvoiceLoadSummaries().keySet().iterator(); iter.hasNext(); ) {
960
961 String dunsNumber = (String) iter.next();
962 ElectronicInvoiceLoadSummary eInvoiceLoadSummary = (ElectronicInvoiceLoadSummary) eInvoiceLoad.getInvoiceLoadSummaries().get(dunsNumber);
963
964 if (!eInvoiceLoadSummary.isEmpty().booleanValue()) {
965 LOG.info("Saving Load Summary for DUNS '" + dunsNumber + "'");
966
967 ElectronicInvoiceLoadSummary currentLoadSummary = saveElectronicInvoiceLoadSummary(eInvoiceLoadSummary);
968
969 summaryMessage.append("DUNS Number - " + eInvoiceLoadSummary.getVendorDescriptor() + ":\n");
970 summaryMessage.append(" " + eInvoiceLoadSummary.getInvoiceLoadSuccessCount() + " successfully processed invoices for a total of $ " + eInvoiceLoadSummary.getInvoiceLoadSuccessAmount().doubleValue() + "\n");
971 summaryMessage.append(" " + eInvoiceLoadSummary.getInvoiceLoadFailCount() + " rejected invoices for an approximate total of $ " + eInvoiceLoadSummary.getInvoiceLoadFailAmount().doubleValue() + "\n");
972 summaryMessage.append("\n\n");
973
974 savedLoadSummariesMap.put(currentLoadSummary.getVendorDunsNumber(), eInvoiceLoadSummary);
975
976 } else {
977 LOG.info("Not saving Load Summary for DUNS '" + dunsNumber + "' because empty indicator is '" + eInvoiceLoadSummary.isEmpty().booleanValue() + "'");
978 }
979 }
980
981 summaryMessage.append("\n\n");
982
983 for (Iterator rejectIter = eInvoiceLoad.getRejectDocuments().iterator(); rejectIter.hasNext(); ) {
984 ElectronicInvoiceRejectDocument rejectDoc = (ElectronicInvoiceRejectDocument) rejectIter.next();
985 routeRejectDocument(rejectDoc, savedLoadSummariesMap);
986 }
987
988
989
990
991
992 moveFileList(eInvoiceLoad.getRejectFilesToMove());
993
994 return summaryMessage;
995 }
996
997 protected void routeRejectDocument(ElectronicInvoiceRejectDocument rejectDoc,
998 Map savedLoadSummariesMap) {
999
1000 LOG.info("Saving Invoice Reject for DUNS '" + rejectDoc.getVendorDunsNumber() + "'");
1001
1002 if (savedLoadSummariesMap.containsKey(rejectDoc.getVendorDunsNumber())) {
1003 rejectDoc.setInvoiceLoadSummary((ElectronicInvoiceLoadSummary) savedLoadSummariesMap.get(rejectDoc.getVendorDunsNumber()));
1004 } else {
1005 rejectDoc.setInvoiceLoadSummary((ElectronicInvoiceLoadSummary) savedLoadSummariesMap.get(UNKNOWN_DUNS_IDENTIFIER));
1006 }
1007
1008 try {
1009 SpringContext.getBean(DocumentService.class).routeDocument(rejectDoc, "Routed by electronic invoice batch job", null);
1010 } catch (WorkflowException e) {
1011 e.printStackTrace();
1012 }
1013
1014 }
1015
1016 protected void sendSummary(StringBuffer message) {
1017
1018 String fromMailId = SpringContext.getBean(ParameterService.class).getParameterValueAsString(ElectronicInvoiceStep.class, PurapParameterConstants.ElectronicInvoiceParameters.DAILY_SUMMARY_REPORT_FROM_EMAIL_ADDRESS);
1019 List<String> toMailIds = new ArrayList<String>(SpringContext.getBean(ParameterService.class).getParameterValuesAsString(ElectronicInvoiceStep.class, PurapParameterConstants.ElectronicInvoiceParameters.DAILY_SUMMARY_REPORT_TO_EMAIL_ADDRESSES));
1020
1021 LOG.info("From email address parameter value:" + fromMailId);
1022 LOG.info("To email address parameter value:" + toMailIds);
1023
1024 if (StringUtils.isBlank(fromMailId) || toMailIds.isEmpty()) {
1025 LOG.error("From/To mail addresses are empty. Unable to send the message");
1026 } else {
1027
1028 MailMessage mailMessage = new MailMessage();
1029
1030 mailMessage.setFromAddress(fromMailId);
1031 setMessageToAddressesAndSubject(mailMessage, toMailIds);
1032 mailMessage.setMessage(message.toString());
1033
1034 try {
1035 mailService.sendMessage(mailMessage);
1036 } catch (Exception e) {
1037 LOG.error("Invalid email address. Message not sent", e);
1038 }
1039 }
1040
1041 }
1042
1043 protected MailMessage setMessageToAddressesAndSubject(MailMessage message, List<String> toAddressList) {
1044
1045 if (!toAddressList.isEmpty()) {
1046 for (int i = 0; i < toAddressList.size(); i++) {
1047 if (StringUtils.isNotEmpty(toAddressList.get(i))) {
1048 message.addToAddress(toAddressList.get(i).trim());
1049 }
1050 }
1051 }
1052
1053 String mailTitle = "E-Invoice Load Results for " + ElectronicInvoiceUtils.getDateDisplayText(SpringContext.getBean(DateTimeService.class).getCurrentDate());
1054
1055 message.setSubject(mailTitle);
1056 return message;
1057 }
1058
1059
1060
1061
1062
1063
1064 @Override
1065 public boolean doMatchingProcess(ElectronicInvoiceRejectDocument rejectDocument) {
1066
1067
1068
1069
1070 validateVendorDetails(rejectDocument);
1071
1072 Map itemTypeMappings = getItemTypeMappings(rejectDocument.getVendorHeaderGeneratedIdentifier(),
1073 rejectDocument.getVendorDetailAssignedIdentifier());
1074
1075 Map kualiItemTypes = getKualiItemTypes();
1076
1077 ElectronicInvoiceOrderHolder rejectDocHolder = new ElectronicInvoiceOrderHolder(rejectDocument, itemTypeMappings, kualiItemTypes);
1078 matchingService.doMatchingProcess(rejectDocHolder);
1079
1080
1081
1082
1083
1084 if (!rejectDocHolder.isInvoiceRejected()) {
1085 validateInvoiceOrderValidForPREQCreation(rejectDocHolder);
1086 }
1087
1088
1089 List<String> ignoreRejectTypes = new ArrayList<String>(parameterService.getParameterValuesAsString(PurapConstants.PURAP_NAMESPACE, "ElectronicInvoiceReject", "SUPPRESS_REJECT_REASON_CODES_ON_EIRT_APPROVAL"));
1090 List<ElectronicInvoiceRejectReason> rejectReasonsToDelete = new ArrayList<ElectronicInvoiceRejectReason>();
1091 for (ElectronicInvoiceRejectReason rejectReason : rejectDocument.getInvoiceRejectReasons()) {
1092 String rejectedReasonTypeCode = rejectReason.getInvoiceRejectReasonTypeCode();
1093 if (StringUtils.isNotBlank(rejectedReasonTypeCode)) {
1094 if (ignoreRejectTypes.contains(rejectedReasonTypeCode)) {
1095 rejectReasonsToDelete.add(rejectReason);
1096 }
1097 }
1098 }
1099
1100
1101 if (!rejectReasonsToDelete.isEmpty()) {
1102 rejectDocument.getInvoiceRejectReasons().removeAll(rejectReasonsToDelete);
1103 }
1104
1105
1106 if (rejectDocument.getInvoiceRejectReasons().isEmpty()) {
1107 GlobalVariables.getMessageMap().clearErrorMessages();
1108 }
1109
1110
1111 return !rejectDocHolder.isInvoiceRejected();
1112 }
1113
1114 @Override
1115 public boolean createPaymentRequest(ElectronicInvoiceRejectDocument rejectDocument) {
1116
1117 if (rejectDocument.getInvoiceRejectReasons().size() > 0) {
1118 throw new RuntimeException("Not possible to create payment request since the reject document contains " + rejectDocument.getInvoiceRejectReasons().size() + " rejects");
1119 }
1120
1121 Map itemTypeMappings = getItemTypeMappings(rejectDocument.getVendorHeaderGeneratedIdentifier(),
1122 rejectDocument.getVendorDetailAssignedIdentifier());
1123
1124 Map kualiItemTypes = getKualiItemTypes();
1125
1126 ElectronicInvoiceOrderHolder rejectDocHolder = new ElectronicInvoiceOrderHolder(rejectDocument, itemTypeMappings, kualiItemTypes);
1127
1128
1129
1130
1131
1132 PaymentRequestDocument preqDocument = createPaymentRequest(rejectDocHolder);
1133 rejectDocument.setPaymentRequestIdentifier(preqDocument.getPurapDocumentIdentifier());
1134
1135 return !rejectDocHolder.isInvoiceRejected();
1136
1137 }
1138
1139 protected PaymentRequestDocument createPaymentRequest(ElectronicInvoiceOrderHolder orderHolder) {
1140
1141 if (LOG.isInfoEnabled()) {
1142 LOG.info("Creating Payment Request document");
1143 }
1144
1145 KNSGlobalVariables.getMessageList().clear();
1146
1147 validateInvoiceOrderValidForPREQCreation(orderHolder);
1148
1149 if (LOG.isInfoEnabled()) {
1150 if (orderHolder.isInvoiceRejected()) {
1151 LOG.info("Not possible to convert einvoice details into payment request");
1152 } else {
1153 LOG.info("Payment request document creation validation succeeded");
1154 }
1155 }
1156
1157 if (orderHolder.isInvoiceRejected()) {
1158 return null;
1159 }
1160
1161 PaymentRequestDocument preqDoc = null;
1162 try {
1163 preqDoc = (PaymentRequestDocument) SpringContext.getBean(DocumentService.class).getNewDocument("PREQ");
1164 } catch (WorkflowException e) {
1165 String extraDescription = "Error=" + e.getMessage();
1166 ElectronicInvoiceRejectReason rejectReason = matchingService.createRejectReason(PurapConstants.ElectronicInvoice.PREQ_WORKLOW_EXCEPTION,
1167 extraDescription,
1168 orderHolder.getFileName());
1169 orderHolder.addInvoiceOrderRejectReason(rejectReason);
1170 LOG.error("Error creating Payment request document - " + e.getMessage());
1171 return null;
1172 }
1173
1174 PurchaseOrderDocument poDoc = orderHolder.getPurchaseOrderDocument();
1175 if (poDoc == null) {
1176 throw new RuntimeException("Purchase Order document (POId=" + poDoc.getPurapDocumentIdentifier() + ") does not exist in the system");
1177 }
1178
1179 preqDoc.getDocumentHeader().setDocumentDescription(generatePREQDocumentDescription(poDoc));
1180 try {
1181 preqDoc.updateAndSaveAppDocStatus(PurapConstants.PaymentRequestStatuses.APPDOC_IN_PROCESS);
1182 } catch (WorkflowException we) {
1183 throw new RuntimeException("Unable to save route status data for document: " + preqDoc.getDocumentNumber(), we);
1184 }
1185
1186 preqDoc.setInvoiceDate(orderHolder.getInvoiceDate());
1187 preqDoc.setInvoiceNumber(orderHolder.getInvoiceNumber());
1188 preqDoc.setVendorInvoiceAmount(new KualiDecimal(orderHolder.getInvoiceNetAmount()));
1189 preqDoc.setAccountsPayableProcessorIdentifier("E-Invoice");
1190 preqDoc.setVendorCustomerNumber(orderHolder.getCustomerNumber());
1191 preqDoc.setPaymentRequestElectronicInvoiceIndicator(true);
1192
1193 if (orderHolder.getAccountsPayablePurchasingDocumentLinkIdentifier() != null) {
1194 preqDoc.setAccountsPayablePurchasingDocumentLinkIdentifier(orderHolder.getAccountsPayablePurchasingDocumentLinkIdentifier());
1195 }
1196
1197
1198
1199 Bank defaultBank = SpringContext.getBean(BankService.class).getDefaultBankByDocType(preqDoc.getClass());
1200 if (defaultBank != null) {
1201 preqDoc.setBankCode(defaultBank.getBankCode());
1202 preqDoc.setBank(defaultBank);
1203 }
1204
1205 RequisitionDocument reqDoc = SpringContext.getBean(RequisitionService.class).getRequisitionById(poDoc.getRequisitionIdentifier());
1206 String reqDocInitiator = reqDoc.getDocumentHeader().getWorkflowDocument().getInitiatorPrincipalId();
1207 try {
1208 Person user = KimApiServiceLocator.getPersonService().getPerson(reqDocInitiator);
1209
1210 setProcessingCampus(preqDoc, user.getCampusCode());
1211
1212 } catch (Exception e) {
1213 String extraDescription = "Error setting processing campus code - " + e.getMessage();
1214 ElectronicInvoiceRejectReason rejectReason = matchingService.createRejectReason(PurapConstants.ElectronicInvoice.PREQ_ROUTING_VALIDATION_ERROR, extraDescription, orderHolder.getFileName());
1215 orderHolder.addInvoiceOrderRejectReason(rejectReason);
1216 return null;
1217 }
1218
1219 HashMap<String, ExpiredOrClosedAccountEntry> expiredOrClosedAccountList = SpringContext.getBean(AccountsPayableService.class).expiredOrClosedAccountsList(poDoc);
1220 if (expiredOrClosedAccountList == null) {
1221 expiredOrClosedAccountList = new HashMap();
1222 }
1223
1224 if (LOG.isInfoEnabled()) {
1225 LOG.info(expiredOrClosedAccountList.size() + " accounts has been found as Expired or Closed");
1226 }
1227
1228 preqDoc.populatePaymentRequestFromPurchaseOrder(orderHolder.getPurchaseOrderDocument(), expiredOrClosedAccountList);
1229
1230 populateItemDetails(preqDoc, orderHolder);
1231
1232
1233
1234
1235
1236 SpringContext.getBean(KualiRuleService.class).applyRules(new AttributedCalculateAccountsPayableEvent(preqDoc));
1237
1238 SpringContext.getBean(PaymentRequestService.class).calculatePaymentRequest(preqDoc, true);
1239
1240 processItemsForDiscount(preqDoc, orderHolder);
1241
1242 if (orderHolder.isInvoiceRejected()) {
1243 return null;
1244 }
1245
1246 SpringContext.getBean(PaymentRequestService.class).calculatePaymentRequest(preqDoc, false);
1247
1248
1249
1250
1251 SpringContext.getBean(KualiRuleService.class).applyRules(new AttributedPaymentRequestForEInvoiceEvent(preqDoc));
1252
1253 if (GlobalVariables.getMessageMap().hasErrors()) {
1254 if (LOG.isInfoEnabled()) {
1255 LOG.info("***************Error in rules processing - " + GlobalVariables.getMessageMap());
1256 }
1257 Map<String, AutoPopulatingList<ErrorMessage>> errorMessages = GlobalVariables.getMessageMap().getErrorMessages();
1258
1259 String errors = errorMessages.toString();
1260 ElectronicInvoiceRejectReason rejectReason = matchingService.createRejectReason(PurapConstants.ElectronicInvoice.PREQ_ROUTING_VALIDATION_ERROR, errors, orderHolder.getFileName());
1261 orderHolder.addInvoiceOrderRejectReason(rejectReason);
1262 return null;
1263 }
1264
1265 if (KNSGlobalVariables.getMessageList().size() > 0) {
1266 if (LOG.isInfoEnabled()) {
1267 LOG.info("Payment request contains " + KNSGlobalVariables.getMessageList().size() + " warning message(s)");
1268 for (int i = 0; i < KNSGlobalVariables.getMessageList().size(); i++) {
1269 LOG.info("Warning " + i + " - " + KNSGlobalVariables.getMessageList().get(i));
1270 }
1271 }
1272 }
1273
1274 addShipToNotes(preqDoc, orderHolder);
1275
1276 String routingAnnotation = null;
1277 if (!orderHolder.isRejectDocumentHolder()) {
1278 routingAnnotation = "Routed by electronic invoice batch job";
1279 }
1280
1281 try {
1282 SpringContext.getBean(DocumentService.class).routeDocument(preqDoc, routingAnnotation, null);
1283 } catch (WorkflowException e) {
1284 e.printStackTrace();
1285 ElectronicInvoiceRejectReason rejectReason = matchingService.createRejectReason(PurapConstants.ElectronicInvoice.PREQ_ROUTING_FAILURE, e.getMessage(), orderHolder.getFileName());
1286 orderHolder.addInvoiceOrderRejectReason(rejectReason);
1287 return null;
1288 } catch (ValidationException e) {
1289 String extraDescription = GlobalVariables.getMessageMap().toString();
1290 ElectronicInvoiceRejectReason rejectReason = matchingService.createRejectReason(PurapConstants.ElectronicInvoice.PREQ_ROUTING_VALIDATION_ERROR, extraDescription, orderHolder.getFileName());
1291 orderHolder.addInvoiceOrderRejectReason(rejectReason);
1292 return null;
1293 }
1294
1295 return preqDoc;
1296 }
1297
1298
1299
1300
1301
1302
1303
1304
1305 protected void setProcessingCampus(PaymentRequestDocument preqDoc, String initiatorCampusCode) {
1306 String campusCode = parameterService.getParameterValueAsString(ElectronicInvoiceStep.class, PurapParameterConstants.ElectronicInvoiceParameters.OVERRIDE_PROCESSING_CAMPUS);
1307 if (!StringHelper.isNullOrEmpty(campusCode)) {
1308 preqDoc.setProcessingCampusCode(campusCode);
1309 } else {
1310 preqDoc.setProcessingCampusCode(initiatorCampusCode);
1311 }
1312
1313 }
1314
1315 protected void addShipToNotes(PaymentRequestDocument preqDoc,
1316 ElectronicInvoiceOrderHolder orderHolder) {
1317
1318 String shipToAddress = orderHolder.getInvoiceShipToAddressAsString();
1319
1320 try {
1321 Note noteObj = SpringContext.getBean(DocumentService.class).createNoteFromDocument(preqDoc, shipToAddress);
1322 preqDoc.addNote(noteObj);
1323 } catch (Exception e) {
1324 LOG.error("Error creating ShipTo notes - " + e.getMessage());
1325 }
1326 }
1327
1328 protected void processItemsForDiscount(PaymentRequestDocument preqDocument,
1329 ElectronicInvoiceOrderHolder orderHolder) {
1330
1331 if (LOG.isInfoEnabled()) {
1332 LOG.info("Processing payment request items for discount");
1333 }
1334
1335 if (!orderHolder.isItemTypeAvailableInItemMapping(ElectronicInvoice.INVOICE_AMOUNT_TYPE_CODE_DISCOUNT)) {
1336 if (LOG.isInfoEnabled()) {
1337 LOG.info("Skipping discount processing since there is no mapping of discount type for this vendor");
1338 }
1339 return;
1340 }
1341
1342 if (orderHolder.getInvoiceDiscountAmount() == null ||
1343 orderHolder.getInvoiceDiscountAmount() == BigDecimal.ZERO) {
1344 if (LOG.isInfoEnabled()) {
1345 LOG.info("Skipping discount processing since there is no discount amount found in the invoice file");
1346 }
1347 return;
1348 }
1349
1350 KualiDecimal discountValueToUse = new KualiDecimal(orderHolder.getInvoiceDiscountAmount().negate());
1351 List<PaymentRequestItem> preqItems = preqDocument.getItems();
1352
1353 boolean alreadyProcessedInvoiceDiscount = false;
1354 boolean hasKualiPaymentTermsDiscountItem = false;
1355
1356
1357 for (int i = 0; i < preqItems.size(); i++) {
1358
1359 PaymentRequestItem preqItem = preqItems.get(i);
1360
1361 hasKualiPaymentTermsDiscountItem = hasKualiPaymentTermsDiscountItem || (StringUtils.equals(PurapConstants.ItemTypeCodes.ITEM_TYPE_PMT_TERMS_DISCOUNT_CODE, preqItem.getItemTypeCode()));
1362
1363 if (isItemValidForUpdation(preqItem.getItemTypeCode(), ElectronicInvoice.INVOICE_AMOUNT_TYPE_CODE_DISCOUNT, orderHolder)) {
1364
1365 alreadyProcessedInvoiceDiscount = true;
1366
1367 if (StringUtils.equals(preqItem.getItemTypeCode(), PurapConstants.ItemTypeCodes.ITEM_TYPE_PMT_TERMS_DISCOUNT_CODE)) {
1368
1369
1370 if (LOG.isInfoEnabled()) {
1371 LOG.info("Discount Check - E-Invoice matches PREQ item type '" + preqItem.getItemTypeCode() + "'... now checking for amount");
1372 }
1373
1374 KualiDecimal preqExtendedPrice = preqItem.getExtendedPrice() == null ? KualiDecimal.ZERO : preqItem.getExtendedPrice();
1375 if ((discountValueToUse.compareTo(preqExtendedPrice)) < 0) {
1376 if (LOG.isInfoEnabled()) {
1377 LOG.info("Discount Check - Using E-Invoice amount (" + discountValueToUse + ") as it is more discount than current payment terms amount " + preqExtendedPrice);
1378 }
1379 preqItem.setItemUnitPrice(discountValueToUse.bigDecimalValue());
1380 preqItem.setExtendedPrice(discountValueToUse);
1381 }
1382 } else {
1383
1384
1385 if (LOG.isInfoEnabled()) {
1386 LOG.info("Discount Check - E-Invoice matches PREQ item type '" + preqItem.getItemTypeCode() + "'");
1387 LOG.info("Discount Check - Using E-Invoice amount (" + discountValueToUse + ") as it is greater than payment terms amount");
1388 }
1389 preqItem.addToUnitPrice(discountValueToUse.bigDecimalValue());
1390 preqItem.addToExtendedPrice(discountValueToUse);
1391 }
1392 }
1393 }
1394
1395
1396
1397
1398
1399
1400
1401
1402 if (!alreadyProcessedInvoiceDiscount) {
1403 String itemTypeRequired = PurapConstants.ItemTypeCodes.ITEM_TYPE_PMT_TERMS_DISCOUNT_CODE;
1404
1405
1406
1407 if (hasKualiPaymentTermsDiscountItem ||
1408 !orderHolder.isItemTypeAvailableInItemMapping(ElectronicInvoice.INVOICE_AMOUNT_TYPE_CODE_DISCOUNT)) {
1409 ElectronicInvoiceRejectReason rejectReason = matchingService.createRejectReason(PurapConstants.ElectronicInvoice.PREQ_DISCOUNT_ERROR, null, orderHolder.getFileName());
1410 orderHolder.addInvoiceOrderRejectReason(rejectReason);
1411 return;
1412 } else {
1413 PaymentRequestItem newItem = new PaymentRequestItem();
1414 newItem.setItemUnitPrice(discountValueToUse.bigDecimalValue());
1415 newItem.setItemTypeCode(PurapConstants.ItemTypeCodes.ITEM_TYPE_PMT_TERMS_DISCOUNT_CODE);
1416 newItem.setExtendedPrice(discountValueToUse);
1417 newItem.setPurapDocument(preqDocument);
1418 preqDocument.addItem(newItem);
1419 }
1420 }
1421
1422 if (LOG.isInfoEnabled()) {
1423 LOG.info("Completed processing payment request items for discount");
1424 }
1425
1426 }
1427
1428 protected void populateItemDetails(PaymentRequestDocument preqDocument, ElectronicInvoiceOrderHolder orderHolder) {
1429
1430 if (LOG.isInfoEnabled()) {
1431 LOG.info("Populating invoice order items into the payment request document");
1432 }
1433
1434 List<PurApItem> preqItems = preqDocument.getItems();
1435
1436
1437 for (int i = 0; i < preqItems.size(); i++) {
1438
1439 PaymentRequestItem preqItem = (PaymentRequestItem) preqItems.get(i);
1440 processInvoiceItem(preqItem, orderHolder);
1441
1442
1443
1444
1445
1446
1447
1448 }
1449
1450
1451
1452 addMissingMappedItems(preqItems, orderHolder);
1453
1454
1455 removeEmptyItems(preqItems);
1456
1457 if (LOG.isInfoEnabled()) {
1458 LOG.info("Successfully populated the invoice order items");
1459 }
1460
1461 }
1462
1463
1464
1465
1466
1467
1468 protected void removeEmptyItems(List<PurApItem> preqItems) {
1469
1470 for (int i = preqItems.size() - 1; i >= 0; i--) {
1471 PurApItem item = preqItems.get(i);
1472
1473
1474 if (isNullOrZero(item.getItemUnitPrice()) && isNullOrZero(item.getExtendedPrice())) {
1475 preqItems.remove(i);
1476 }
1477 }
1478 }
1479
1480
1481
1482
1483
1484
1485
1486
1487 protected void addMissingMappedItems(List<PurApItem> preqItems, ElectronicInvoiceOrderHolder orderHolder) {
1488
1489 PurchasingAccountsPayableDocument purapDoc = null;
1490 Integer purapDocIdentifier = null;
1491
1492
1493 List requiredItemTypeCodeList = createInvoiceRequiresItemTypeCodeList(orderHolder);
1494
1495 if (ObjectUtils.isNotNull(requiredItemTypeCodeList) && !requiredItemTypeCodeList.isEmpty()) {
1496
1497
1498 for (int i = 0; i < preqItems.size(); i++) {
1499
1500 if (requiredItemTypeCodeList.contains(preqItems.get(i).getItemTypeCode())) {
1501 requiredItemTypeCodeList.remove(preqItems.get(i).getItemTypeCode());
1502 }
1503
1504
1505 purapDoc = preqItems.get(i).getPurapDocument();
1506 purapDocIdentifier = preqItems.get(i).getPurapDocumentIdentifier();
1507 }
1508
1509 if (ObjectUtils.isNotNull(requiredItemTypeCodeList) && !requiredItemTypeCodeList.isEmpty()) {
1510
1511
1512 for (int i = 0; i < requiredItemTypeCodeList.size(); i++) {
1513 PaymentRequestItem preqItem = new PaymentRequestItem();
1514 preqItem.resetAccount();
1515 preqItem.setPurapDocumentIdentifier(purapDocIdentifier);
1516 preqItem.setPurapDocument(purapDoc);
1517 preqItem.setItemTypeCode((String) requiredItemTypeCodeList.get(i));
1518
1519
1520 processInvoiceItem(preqItem, orderHolder);
1521
1522
1523 if ((ObjectUtils.isNotNull(preqItem.getItemUnitPrice()) && preqItem.getItemUnitPrice() != BigDecimal.ZERO) &&
1524 (ObjectUtils.isNotNull(preqItem.getExtendedPrice()) && preqItem.getExtendedPrice() != KualiDecimal.ZERO)) {
1525
1526 preqItems.add(preqItem);
1527 }
1528
1529
1530 }
1531 }
1532
1533 }
1534 }
1535
1536
1537
1538
1539
1540 protected List createInvoiceRequiresItemTypeCodeList(ElectronicInvoiceOrderHolder orderHolder) {
1541 List itemTypeCodeList = new ArrayList();
1542
1543 addToListIfExists(itemTypeCodeList, ElectronicInvoice.INVOICE_AMOUNT_TYPE_CODE_TAX, orderHolder);
1544 addToListIfExists(itemTypeCodeList, ElectronicInvoice.INVOICE_AMOUNT_TYPE_CODE_SHIPPING, orderHolder);
1545 addToListIfExists(itemTypeCodeList, ElectronicInvoice.INVOICE_AMOUNT_TYPE_CODE_SPECIAL_HANDLING, orderHolder);
1546 addToListIfExists(itemTypeCodeList, ElectronicInvoice.INVOICE_AMOUNT_TYPE_CODE_DEPOSIT, orderHolder);
1547 addToListIfExists(itemTypeCodeList, ElectronicInvoice.INVOICE_AMOUNT_TYPE_CODE_DUE, orderHolder);
1548 addToListIfExists(itemTypeCodeList, ElectronicInvoice.INVOICE_AMOUNT_TYPE_CODE_DISCOUNT, orderHolder);
1549
1550 return itemTypeCodeList;
1551 }
1552
1553
1554
1555
1556
1557
1558
1559
1560 protected void addToListIfExists(List itemTypeCodeList, String invoiceItemTypeCode, ElectronicInvoiceOrderHolder orderHolder) {
1561
1562 String itemTypeCode = orderHolder.getKualiItemTypeCodeFromMappings(invoiceItemTypeCode);
1563
1564 if (ObjectUtils.isNotNull(itemTypeCode)) {
1565 itemTypeCodeList.add(itemTypeCode);
1566 }
1567 }
1568
1569
1570
1571
1572
1573
1574
1575
1576 protected void processInvoiceItem(PaymentRequestItem preqItem, ElectronicInvoiceOrderHolder orderHolder) {
1577
1578 if (isItemValidForUpdation(preqItem.getItemTypeCode(), ElectronicInvoice.INVOICE_AMOUNT_TYPE_CODE_ITEM, orderHolder)) {
1579 processAboveTheLineItem(preqItem, orderHolder);
1580 } else if (isItemValidForUpdation(preqItem.getItemTypeCode(), ElectronicInvoice.INVOICE_AMOUNT_TYPE_CODE_TAX, orderHolder)) {
1581 processTaxItem(preqItem, orderHolder);
1582 } else if (isItemValidForUpdation(preqItem.getItemTypeCode(), ElectronicInvoice.INVOICE_AMOUNT_TYPE_CODE_SHIPPING, orderHolder)) {
1583 processShippingItem(preqItem, orderHolder);
1584 } else if (isItemValidForUpdation(preqItem.getItemTypeCode(), ElectronicInvoice.INVOICE_AMOUNT_TYPE_CODE_SPECIAL_HANDLING, orderHolder)) {
1585 processSpecialHandlingItem(preqItem, orderHolder);
1586 } else if (isItemValidForUpdation(preqItem.getItemTypeCode(), ElectronicInvoice.INVOICE_AMOUNT_TYPE_CODE_DEPOSIT, orderHolder)) {
1587 processDepositItem(preqItem, orderHolder);
1588 } else if (isItemValidForUpdation(preqItem.getItemTypeCode(), ElectronicInvoice.INVOICE_AMOUNT_TYPE_CODE_DUE, orderHolder)) {
1589 processDueItem(preqItem, orderHolder);
1590 } else if (isItemValidForUpdation(preqItem.getItemTypeCode(), ElectronicInvoice.INVOICE_AMOUNT_TYPE_CODE_DISCOUNT, orderHolder)) {
1591 processDiscountItem(preqItem, orderHolder);
1592 } else if (isItemValidForUpdation(preqItem.getItemTypeCode(), ElectronicInvoice.INVOICE_AMOUNT_TYPE_CODE_EXMT, orderHolder)) {
1593 processAboveTheLineItem(preqItem, orderHolder);
1594 }
1595
1596 }
1597
1598 protected void processAboveTheLineItem(PaymentRequestItem purapItem,
1599 ElectronicInvoiceOrderHolder orderHolder) {
1600
1601 if (LOG.isInfoEnabled()) {
1602 LOG.info("Processing above the line item");
1603 }
1604
1605 ElectronicInvoiceItemHolder itemHolder = orderHolder.getItemByLineNumber(purapItem.getItemLineNumber().intValue());
1606 if (itemHolder == null) {
1607 LOG.info("Electronic Invoice does not have item with Ref Item Line number " + purapItem.getItemLineNumber());
1608 return;
1609 }
1610
1611 purapItem.setItemUnitPrice(itemHolder.getInvoiceItemUnitPrice());
1612 purapItem.setItemQuantity(new KualiDecimal(itemHolder.getInvoiceItemQuantity()));
1613 purapItem.setItemTaxAmount(new KualiDecimal(itemHolder.getTaxAmount()));
1614 purapItem.setItemCatalogNumber(itemHolder.getInvoiceItemCatalogNumber());
1615 purapItem.setItemDescription(itemHolder.getInvoiceItemDescription());
1616
1617 if (itemHolder.getSubTotalAmount() != null &&
1618 itemHolder.getSubTotalAmount().compareTo(KualiDecimal.ZERO) != 0) {
1619
1620 purapItem.setExtendedPrice(itemHolder.getSubTotalAmount());
1621
1622 } else {
1623
1624 if (purapItem.getItemQuantity() != null) {
1625 if (LOG.isInfoEnabled()) {
1626 LOG.info("Item number " + purapItem.getItemLineNumber() + " needs calculation of extended " +
1627 "price from quantity " + purapItem.getItemQuantity() + " and unit cost " + purapItem.getItemUnitPrice());
1628 }
1629 purapItem.setExtendedPrice(purapItem.getItemQuantity().multiply(new KualiDecimal(purapItem.getItemUnitPrice())));
1630 } else {
1631 if (LOG.isInfoEnabled()) {
1632 LOG.info("Item number " + purapItem.getItemLineNumber() + " has no quantity so extended price " +
1633 "equals unit price of " + purapItem.getItemUnitPrice());
1634 }
1635 purapItem.setExtendedPrice(new KualiDecimal(purapItem.getItemUnitPrice()));
1636 }
1637 }
1638
1639 }
1640
1641 protected void processSpecialHandlingItem(PaymentRequestItem purapItem,
1642 ElectronicInvoiceOrderHolder orderHolder) {
1643
1644 if (LOG.isInfoEnabled()) {
1645 LOG.info("Processing special handling item");
1646 }
1647
1648 purapItem.addToUnitPrice(orderHolder.getInvoiceSpecialHandlingAmount());
1649 purapItem.addToExtendedPrice(new KualiDecimal(orderHolder.getInvoiceSpecialHandlingAmount()));
1650
1651 String invoiceSpecialHandlingDescription = orderHolder.getInvoiceSpecialHandlingDescription();
1652
1653 if (invoiceSpecialHandlingDescription == null && (orderHolder.getInvoiceSpecialHandlingAmount() != null && BigDecimal.ZERO.compareTo(orderHolder.getInvoiceSpecialHandlingAmount()) != 0)) {
1654 invoiceSpecialHandlingDescription = PurapConstants.ElectronicInvoice.DEFAULT_SPECIAL_HANDLING_DESCRIPTION;
1655 }
1656 if (StringUtils.isNotEmpty(invoiceSpecialHandlingDescription)) {
1657 if (StringUtils.isEmpty(purapItem.getItemDescription())) {
1658 purapItem.setItemDescription(invoiceSpecialHandlingDescription);
1659 } else {
1660 purapItem.setItemDescription(purapItem.getItemDescription() + " - " + invoiceSpecialHandlingDescription);
1661 }
1662 }
1663
1664 }
1665
1666 protected void processTaxItem(PaymentRequestItem preqItem,
1667 ElectronicInvoiceOrderHolder orderHolder) {
1668
1669 if (LOG.isInfoEnabled()) {
1670 LOG.info("Processing Tax Item");
1671 }
1672
1673 preqItem.addToUnitPrice(orderHolder.getTaxAmount());
1674 preqItem.addToExtendedPrice(new KualiDecimal(orderHolder.getTaxAmount()));
1675
1676 if (StringUtils.isNotEmpty(orderHolder.getTaxDescription())) {
1677 if (StringUtils.isEmpty(preqItem.getItemDescription())) {
1678 preqItem.setItemDescription(orderHolder.getTaxDescription());
1679 } else {
1680 preqItem.setItemDescription(preqItem.getItemDescription() + " - " + orderHolder.getTaxDescription());
1681 }
1682 }
1683
1684 }
1685
1686 protected void processShippingItem(PaymentRequestItem preqItem,
1687 ElectronicInvoiceOrderHolder orderHolder) {
1688
1689 if (LOG.isInfoEnabled()) {
1690 LOG.info("Processing Shipping Item");
1691 }
1692
1693 preqItem.addToUnitPrice(orderHolder.getInvoiceShippingAmount());
1694 preqItem.addToExtendedPrice(new KualiDecimal(orderHolder.getInvoiceShippingAmount()));
1695
1696 if (StringUtils.isNotEmpty(orderHolder.getInvoiceShippingDescription())) {
1697 if (StringUtils.isEmpty(preqItem.getItemDescription())) {
1698 preqItem.setItemDescription(orderHolder.getInvoiceShippingDescription());
1699 } else {
1700 preqItem.setItemDescription(preqItem.getItemDescription() + " - " + orderHolder.getInvoiceShippingDescription());
1701 }
1702 }
1703 }
1704
1705 protected void processDiscountItem(PaymentRequestItem preqItem,
1706 ElectronicInvoiceOrderHolder orderHolder) {
1707
1708 if (LOG.isInfoEnabled()) {
1709 LOG.info("Processing Discount Item");
1710 }
1711
1712 preqItem.addToUnitPrice(orderHolder.getInvoiceDiscountAmount());
1713 preqItem.addToExtendedPrice(new KualiDecimal(orderHolder.getInvoiceDiscountAmount()));
1714 }
1715
1716
1717 protected void processDepositItem(PaymentRequestItem preqItem,
1718 ElectronicInvoiceOrderHolder orderHolder) {
1719
1720 LOG.info("Processing Deposit Item");
1721
1722 preqItem.addToUnitPrice(orderHolder.getInvoiceDepositAmount());
1723 preqItem.addToExtendedPrice(new KualiDecimal(orderHolder.getInvoiceDepositAmount()));
1724
1725 }
1726
1727 protected void processDueItem(PaymentRequestItem preqItem,
1728 ElectronicInvoiceOrderHolder orderHolder) {
1729
1730 LOG.info("Processing Deposit Item");
1731
1732 preqItem.addToUnitPrice(orderHolder.getInvoiceDueAmount());
1733 preqItem.addToExtendedPrice(new KualiDecimal(orderHolder.getInvoiceDueAmount()));
1734 }
1735
1736 protected boolean isNullOrZero(BigDecimal value) {
1737
1738 if (ObjectUtils.isNull(value) || value.compareTo(BigDecimal.ZERO) == 0) {
1739 return true;
1740 } else {
1741 return false;
1742 }
1743 }
1744
1745 protected boolean isNullOrZero(KualiDecimal value) {
1746
1747 if (ObjectUtils.isNull(value) || value.isZero()) {
1748 return true;
1749 } else {
1750 return false;
1751 }
1752 }
1753
1754 protected void setItemDefaultDescription(PaymentRequestItem preqItem) {
1755
1756
1757 if (StringUtils.isEmpty(preqItem.getItemDescription()) &&
1758 !StringUtils.equals(PurapConstants.ItemTypeCodes.ITEM_TYPE_ITEM_CODE, preqItem.getItemTypeCode())) {
1759 if (ArrayUtils.contains(PurapConstants.ElectronicInvoice.ITEM_TYPES_REQUIRES_DESCRIPTION, preqItem.getItemTypeCode())) {
1760 preqItem.setItemDescription(PurapConstants.ElectronicInvoice.DEFAULT_BELOW_LINE_ITEM_DESCRIPTION);
1761 }
1762 }
1763 }
1764
1765 protected boolean isItemValidForUpdation(String itemTypeCode,
1766 String invoiceItemTypeCode,
1767 ElectronicInvoiceOrderHolder orderHolder) {
1768
1769 boolean isItemTypeAvailableInItemMapping = orderHolder.isItemTypeAvailableInItemMapping(invoiceItemTypeCode);
1770 String itemTypeCodeFromMappings = orderHolder.getKualiItemTypeCodeFromMappings(invoiceItemTypeCode);
1771 return isItemTypeAvailableInItemMapping && StringUtils.equals(itemTypeCodeFromMappings, itemTypeCode);
1772 }
1773
1774
1775 protected String generatePREQDocumentDescription(PurchaseOrderDocument poDocument) {
1776 String description = "PO: " + poDocument.getPurapDocumentIdentifier() + " Vendor: " + poDocument.getVendorName() + " Electronic Invoice";
1777 return checkDescriptionLengthAndStripIfNeeded(description);
1778 }
1779
1780
1781
1782
1783 public void validateInvoiceOrderValidForPREQCreation(ElectronicInvoiceOrderHolder orderHolder) {
1784
1785 if (LOG.isInfoEnabled()) {
1786 LOG.info("Validiting ElectronicInvoice Order to make sure that it can be turned into a Payment Request document");
1787 }
1788
1789 PurchaseOrderDocument poDoc = orderHolder.getPurchaseOrderDocument();
1790
1791 if (poDoc == null) {
1792 throw new RuntimeException("PurchaseOrder not available");
1793 }
1794
1795 if (!poDoc.getApplicationDocumentStatus().equals(PurchaseOrderStatuses.APPDOC_OPEN)) {
1796 orderHolder.addInvoiceOrderRejectReason(matchingService.createRejectReason(PurapConstants.ElectronicInvoice.PO_NOT_OPEN, null, orderHolder.getFileName()));
1797 return;
1798 }
1799
1800 if (!orderHolder.isInvoiceNumberAcceptIndicatorEnabled()) {
1801 List preqs = paymentRequestService.getPaymentRequestsByVendorNumberInvoiceNumber(poDoc.getVendorHeaderGeneratedIdentifier(),
1802 poDoc.getVendorDetailAssignedIdentifier(),
1803 orderHolder.getInvoiceNumber());
1804
1805 if (preqs != null && preqs.size() > 0) {
1806 ElectronicInvoiceRejectReason rejectReason = matchingService.createRejectReason(PurapConstants.ElectronicInvoice.INVOICE_ORDER_DUPLICATE, null, orderHolder.getFileName());
1807 orderHolder.addInvoiceOrderRejectReason(rejectReason, PurapConstants.ElectronicInvoice.RejectDocumentFields.INVOICE_FILE_NUMBER, PurapKeyConstants.ERROR_REJECT_INVOICE_DUPLICATE);
1808 return;
1809 }
1810 }
1811
1812 if (orderHolder.getInvoiceDate() == null) {
1813 ElectronicInvoiceRejectReason rejectReason = matchingService.createRejectReason(PurapConstants.ElectronicInvoice.INVOICE_DATE_INVALID, null, orderHolder.getFileName());
1814 orderHolder.addInvoiceOrderRejectReason(rejectReason, PurapConstants.ElectronicInvoice.RejectDocumentFields.INVOICE_FILE_DATE, PurapKeyConstants.ERROR_REJECT_INVOICE_DATE_INVALID);
1815 return;
1816 } else if (orderHolder.getInvoiceDate().after(dateTimeService.getCurrentDate())) {
1817 ElectronicInvoiceRejectReason rejectReason = matchingService.createRejectReason(PurapConstants.ElectronicInvoice.INVOICE_DATE_GREATER, null, orderHolder.getFileName());
1818 orderHolder.addInvoiceOrderRejectReason(rejectReason, PurapConstants.ElectronicInvoice.RejectDocumentFields.INVOICE_FILE_DATE, PurapKeyConstants.ERROR_REJECT_INVOICE_DATE_GREATER);
1819 return;
1820 }
1821
1822 }
1823
1824 protected void moveFileList(Map filesToMove) {
1825 for (Iterator iter = filesToMove.keySet().iterator(); iter.hasNext(); ) {
1826 File fileToMove = (File) iter.next();
1827
1828 boolean success = this.moveFile(fileToMove, (String) filesToMove.get(fileToMove));
1829 if (!success) {
1830 String errorMessage = "File with name '" + fileToMove.getName() + "' could not be moved";
1831 throw new PurError(errorMessage);
1832 }
1833 }
1834 }
1835
1836 protected boolean moveFile(File fileForMove, String location) {
1837 File moveDir = new File(location);
1838 boolean success = fileForMove.renameTo(new File(moveDir, fileForMove.getName()));
1839 return success;
1840 }
1841
1842 protected void deleteDoneFile(File invoiceFile) {
1843 File doneFile = new File(invoiceFile.getAbsolutePath().replace(".xml", ".done"));
1844 if (doneFile.exists()) {
1845 doneFile.delete();
1846 }
1847 }
1848
1849
1850
1851
1852
1853
1854
1855 protected String getErrorMessages(Map<String, ArrayList> errorMap) {
1856
1857 ArrayList errorMessages = null;
1858 ErrorMessage errorMessage = null;
1859 StringBuffer errorList = new StringBuffer("");
1860 String errorText = null;
1861
1862 for (Map.Entry<String, ArrayList> errorEntry : errorMap.entrySet()) {
1863
1864 errorMessages = errorEntry.getValue();
1865
1866 for (int i = 0; i < errorMessages.size(); i++) {
1867
1868 errorMessage = (ErrorMessage) errorMessages.get(i);
1869
1870
1871 errorText = kualiConfigurationService
1872 .getPropertyValueAsString(errorMessage.getErrorKey());
1873
1874 errorText = MessageFormat.format(errorText,
1875 (Object[]) errorMessage.getMessageParameters());
1876
1877
1878 errorList.append(errorText + "\n");
1879 }
1880 }
1881
1882 return errorList.toString();
1883 }
1884
1885 protected String getBaseDirName() {
1886 return electronicInvoiceInputFileType.getDirectoryPath() + File.separator;
1887 }
1888
1889 public String getRejectDirName() {
1890 return getBaseDirName() + "reject" + File.separator;
1891 }
1892
1893 public String getAcceptDirName() {
1894 return getBaseDirName() + "accept" + File.separator;
1895 }
1896
1897 protected File getInvoiceFile(String fileName) {
1898 return new File(getBaseDirName() + fileName);
1899 }
1900
1901 protected ElectronicInvoiceLoadSummary saveElectronicInvoiceLoadSummary(ElectronicInvoiceLoadSummary eils) {
1902 SpringContext.getBean(BusinessObjectService.class).save(eils);
1903 eils.refreshNonUpdateableReferences();
1904 return eils;
1905 }
1906
1907 public void setElectronicInvoiceInputFileType(ElectronicInvoiceInputFileType electronicInvoiceInputFileType) {
1908 this.electronicInvoiceInputFileType = electronicInvoiceInputFileType;
1909 }
1910
1911 public void setMailService(MailService mailService) {
1912 this.mailService = mailService;
1913 }
1914
1915 public void setElectronicInvoicingDao(ElectronicInvoicingDao electronicInvoicingDao) {
1916 this.electronicInvoicingDao = electronicInvoicingDao;
1917 }
1918
1919 public void setBatchInputFileService(BatchInputFileService batchInputFileService) {
1920 this.batchInputFileService = batchInputFileService;
1921 }
1922
1923 public void setElectronicInvoiceMatchingService(ElectronicInvoiceMatchingService matchingService) {
1924 this.matchingService = matchingService;
1925 }
1926
1927 public void setVendorService(VendorService vendorService) {
1928 this.vendorService = vendorService;
1929 }
1930
1931 public void setPurchaseOrderService(PurchaseOrderService purchaseOrderService) {
1932 this.purchaseOrderService = purchaseOrderService;
1933 }
1934
1935 public void setPaymentRequestService(PaymentRequestService paymentRequestService) {
1936 this.paymentRequestService = paymentRequestService;
1937 }
1938
1939 public void setConfigurationService(ConfigurationService kualiConfigurationService) {
1940 this.kualiConfigurationService = kualiConfigurationService;
1941 }
1942
1943 public void setDateTimeService(DateTimeService dateTimeService) {
1944 this.dateTimeService = dateTimeService;
1945 }
1946
1947 public void setParameterService(ParameterService parameterService) {
1948 this.parameterService = parameterService;
1949 }
1950
1951
1952
1953
1954 @Override
1955 public List<String> getRequiredDirectoryNames() {
1956 return new ArrayList<String>() {{
1957 add(getBaseDirName());
1958 add(getAcceptDirName());
1959 add(getRejectDirName());
1960 }};
1961 }
1962
1963 }
1964