View Javadoc
1   /*
2    * Copyright 2008 The Kuali Foundation
3    *
4    * Licensed under the Educational Community License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    * http://www.opensource.org/licenses/ecl2.php
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
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   * This is a helper service to parse electronic invoice file, match it with a PO and create PREQs based on the eInvoice. Also, it
91   * provides helper methods to the reject document to match it with a PO and create PREQ.
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         //add a step to check for directory paths
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              * Create, if not there
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             // MSU Contribution DTT-3014 OLEMI-8483 OLECNTRB-974
197             File xmlFile = element2;
198             LOG.info("Processing " + xmlFile.getName() + "....");
199 
200             byte[] modifiedXML = null;
201             // process only if file exists and not empty
202             if (xmlFile.length() != 0L) {
203                 modifiedXML = addNamespaceDefinition(eInvoiceLoad, xmlFile);
204             }
205 
206             boolean isRejected = false;
207 
208             if (modifiedXML == null) {//Not able to parse the xml
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                     //since getMessage() is empty we'll compose the stack trace and nicely format it.
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                      * Clear the error map, so that subsequent EIRT routing isn't prevented since rice is throwing a
240                      * ValidationException if the error map is not empty before routing the doc.
241                      */
242                     GlobalVariables.getMessageMap().clearErrorMessages();
243 
244                     //Do not execute rest of code below
245                     continue;
246                 }
247             }
248 
249             /**
250              * If there is a single order has rejects and the remainings are accepted in a invoice file,
251              * then the entire file has been moved to the reject dir.
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             //  delete the .done file
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      * @param element
341      * @return String describing the given StackTraceElement
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); // It's not needed to validate here
372         builderFactory.setIgnoringElementContentWhitespace(true);
373 
374         DocumentBuilder builder = null;
375         try {
376             builder = builderFactory.newDocumentBuilder();  // Create the parser
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      * This method processes a single electronic invoice file
434      *
435      * @param eInvoiceLoad the load summary to be modified
436      * @return boolean where true means there has been some type of reject
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                      * This is required. If there is anything in the error map, then it's not possible to route the doc since the rice
518                      * is throwing error if errormap is not empty before routing the doc.
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         //it says - Future Release - Enter valid location for Customer Number from E-Invoice
684         //mappingService.getInvoiceCustomerNumber() doesnt have any implementation
685 //        electronicInvoice.setCustomerNumber(mappingService.getInvoiceCustomerNumber(electronicInvoice));
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             // MSU fix
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          * Even if there is an exception in the reject doc routing, all the files marked as reject will
990          * be moved to the reject dir
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      * This method is responsible for the matching process for a reject document
1061      *
1062      * @return true if the matching process is succeed
1063      */
1064     @Override
1065     public boolean doMatchingProcess(ElectronicInvoiceRejectDocument rejectDocument) {
1066 
1067         /**
1068          * This is needed here since if the user changes the DUNS number.
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          * Once we're through with the matching process, it's needed to check whether it's possible
1082          * to create PREQ for the reject doc
1083          */
1084         if (!rejectDocHolder.isInvoiceRejected()) {
1085             validateInvoiceOrderValidForPREQCreation(rejectDocHolder);
1086         }
1087 
1088         //  determine which of the reject reasons we should suppress based on the parameter
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         //  remove the flagged reject reasons
1101         if (!rejectReasonsToDelete.isEmpty()) {
1102             rejectDocument.getInvoiceRejectReasons().removeAll(rejectReasonsToDelete);
1103         }
1104 
1105         //  if no reject reasons, then clear error messages
1106         if (rejectDocument.getInvoiceRejectReasons().isEmpty()) {
1107             GlobalVariables.getMessageMap().clearErrorMessages();
1108         }
1109 
1110         //  this automatically returns false if there are no reject reasons
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          * First, create a new payment request document.  Once this document is created, then update the reject document's PREQ_ID field
1130          * with the payment request document identifier.  This identifier is used to associate the reject document with the payment request.
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         //Copied from PaymentRequestServiceImpl.populatePaymentRequest()
1198         //set bank code to default bank code in the system parameter
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          * Validate totals,paydate
1234          */
1235         //PaymentRequestDocumentRule.processCalculateAccountsPayableBusinessRules
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          * PaymentRequestReview
1249          */
1250         //PaymentRequestDocumentRule.processRouteDocumentBusinessRules
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      * This method check OVERRIDE_PROCESSING_CAMPUS parameter to set processing campus code.
1300      * If parameter value is populated, it set the processing campus to the value in parameter, otherwise use requisition initiator's campus code.
1301      *
1302      * @param preqDoc
1303      * @param initiatorCampusCode
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         //if e-invoice amount is negative... it is a penalty and we must pay extra
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                     //Item is kuali payment terms discount item... must perform calculation
1369                     // if discount item exists on PREQ and discount dollar amount exists... use greater amount
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                     // item is not payment terms discount item... just add value
1384                     // if discount item exists on PREQ and discount dollar amount exists... use greater amount
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          *   If we have not already processed the discount amount then the mapping is pointed
1397          *   to an item that is not in the PREQ item list
1398          *
1399          *   FYI - FILE DISCOUNT AMOUNT CURRENTLY HARD CODED TO GO INTO PAYMENT TERMS DISCOUNT ITEM ONLY... ALL OTHERS WILL FAIL
1400          */
1401 
1402         if (!alreadyProcessedInvoiceDiscount) {
1403             String itemTypeRequired = PurapConstants.ItemTypeCodes.ITEM_TYPE_PMT_TERMS_DISCOUNT_CODE;
1404             // if we already have a PMT TERMS DISC item but the e-invoice discount wasn't processed... error out
1405             // if the item mapping for e-invoice discount item is not PMT TERMS DISC item and we haven't processed it... error out
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         //process all preq items and apply amounts from order holder
1437         for (int i = 0; i < preqItems.size(); i++) {
1438 
1439             PaymentRequestItem preqItem = (PaymentRequestItem) preqItems.get(i);
1440             processInvoiceItem(preqItem, orderHolder);
1441 
1442             /**
1443              * This is not needed since if we have default desc from misc item, then preq rules are expecting the account details for this items
1444              * AccountsPayableItemBase.isConsideredEntered() returns true if there is any item desc available.
1445              *
1446              */
1447 //            setItemDefaultDescription(preqItem);
1448         }
1449 
1450         //Now we'll add any missing mapping items that did not have
1451         // an existing payment request item
1452         addMissingMappedItems(preqItems, orderHolder);
1453 
1454         //as part of a clean up, remove any preq items that have zero or null unit/extended price
1455         removeEmptyItems(preqItems);
1456 
1457         if (LOG.isInfoEnabled()) {
1458             LOG.info("Successfully populated the invoice order items");
1459         }
1460 
1461     }
1462 
1463     /**
1464      * Removes preq items from the list that have null or zero unit and extended price
1465      *
1466      * @param preqItems
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             //if the unit and extended price have null or zero as a combo, remove item
1474             if (isNullOrZero(item.getItemUnitPrice()) && isNullOrZero(item.getExtendedPrice())) {
1475                 preqItems.remove(i);
1476             }
1477         }
1478     }
1479 
1480     /**
1481      * Ensures that the mapped items, item type code, exist as a payment request item so they're
1482      * process correctly within populateItemDetails
1483      *
1484      * @param preqItems
1485      * @param orderHolder
1486      */
1487     protected void addMissingMappedItems(List<PurApItem> preqItems, ElectronicInvoiceOrderHolder orderHolder) {
1488 
1489         PurchasingAccountsPayableDocument purapDoc = null;
1490         Integer purapDocIdentifier = null;
1491 
1492         //grab all the required item types that should be on the payment request
1493         List requiredItemTypeCodeList = createInvoiceRequiresItemTypeCodeList(orderHolder);
1494 
1495         if (ObjectUtils.isNotNull(requiredItemTypeCodeList) && !requiredItemTypeCodeList.isEmpty()) {
1496 
1497             //loop through existing payment request items and remove ones we already have
1498             for (int i = 0; i < preqItems.size(); i++) {
1499                 //if the preq item exists in the list already, remove
1500                 if (requiredItemTypeCodeList.contains(preqItems.get(i).getItemTypeCode())) {
1501                     requiredItemTypeCodeList.remove(preqItems.get(i).getItemTypeCode());
1502                 }
1503 
1504                 //utility grab the document identifier and document
1505                 purapDoc = preqItems.get(i).getPurapDocument();
1506                 purapDocIdentifier = preqItems.get(i).getPurapDocumentIdentifier();
1507             }
1508 
1509             if (ObjectUtils.isNotNull(requiredItemTypeCodeList) && !requiredItemTypeCodeList.isEmpty()) {
1510                 //if we have any left, it means they didn't exist on the payment request
1511                 // and we must add them.
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                     //process item
1520                     processInvoiceItem(preqItem, orderHolder);
1521 
1522                     //Add to preq Items if the value is not zero
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      * Creates a list of item types the eInvoice requirs on
1538      * the payment request due to valid amounts.
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      * Utility method to add a kuali item type code to a list from a invoice item type code
1555      *
1556      * @param itemTypeCodeList
1557      * @param invoiceItemTypeCode
1558      * @param orderHolder
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      * Finds the mapped item type code to invoice item type code and applies the appropriate values
1571      * to the correct payment request item.
1572      *
1573      * @param preqItem
1574      * @param orderHolder
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         //If description is empty and item is not type "ITEM"... use default description
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      * This validates an electronic invoice and makes sure it can be turned into a Payment Request
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      * returns a list of all error messages as a string
1851      *
1852      * @param errorMap
1853      * @return
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                 // get error text
1871                 errorText = kualiConfigurationService
1872                         .getPropertyValueAsString(errorMessage.getErrorKey());
1873                 // apply parameters
1874                 errorText = MessageFormat.format(errorText,
1875                         (Object[]) errorMessage.getMessageParameters());
1876 
1877                 // add key and error message together
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      * @see org.kuali.ole.sys.batch.service.impl.InitiateDirectoryImpl#getRequiredDirectoryNames()
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