From 8d9a0bba53220c2cfedc2e45a01147ea9384bde4 Mon Sep 17 00:00:00 2001 From: Ralph Soika Date: Wed, 15 May 2024 16:21:26 +0200 Subject: [PATCH] update invoice xml import --- ...argosoftXMLInvoiceImportImportService.java | 292 ++++++++++++------ 1 file changed, 198 insertions(+), 94 deletions(-) diff --git a/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/xml/CargosoftXMLInvoiceImportImportService.java b/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/xml/CargosoftXMLInvoiceImportImportService.java index ca2d6bf..9801d28 100644 --- a/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/xml/CargosoftXMLInvoiceImportImportService.java +++ b/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/xml/CargosoftXMLInvoiceImportImportService.java @@ -32,7 +32,11 @@ import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.util.Date; import java.util.Properties; +import java.util.TimeZone; import java.util.logging.Logger; import javax.ejb.EJB; @@ -41,6 +45,11 @@ import javax.enterprise.event.Observes; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.ParserConfigurationException; +import javax.xml.xpath.XPath; +import javax.xml.xpath.XPathConstants; +import javax.xml.xpath.XPathExpression; +import javax.xml.xpath.XPathExpressionException; +import javax.xml.xpath.XPathFactory; import org.apache.commons.net.ftp.FTP; import org.apache.commons.net.ftp.FTPClient; @@ -48,6 +57,7 @@ import org.apache.commons.net.ftp.FTPFile; import org.apache.commons.net.ftp.FTPSClient; import org.imixs.archive.importer.DocumentImportEvent; import org.imixs.archive.importer.DocumentImportService; +import org.imixs.workflow.FileData; import org.imixs.workflow.ItemCollection; import org.imixs.workflow.engine.ModelService; import org.imixs.workflow.engine.WorkflowService; @@ -151,105 +161,117 @@ public class CargosoftXMLInvoiceImportImportService { // set default port ftpPort = "21"; } - - FTPClient ftpClient = null; try { - logger.finest("......read directories ..."); - documentImportService.logMessage("...connecting to FTP server: " + ftpServer, event); + documentImportService.logMessage("...working directory: " + ftpPath, event); + FTPClient ftpClient = connectServer(ftpServer, Integer.parseInt(ftpPort), ftpUser, ftpPassword); + scannFTPDirectory(ftpClient, ftpPath, mandantID, event); + } catch (PluginException pe) { + documentImportService.logMessage(pe.getMessage(), event); + event.setResult(DocumentImportEvent.PROCESSING_ERROR); + return; + } + + // completed + event.setResult(DocumentImportEvent.PROCESSING_COMPLETED); + + } + + /** + * Connects to the FTP Server and returns a ftpClient instance. + */ + private FTPClient connectServer(String ftpServer, int ftpPort, String ftpUser, String ftpPassword) + throws PluginException { + FTPClient ftpClient = null; + // TLS + ftpClient = new FTPSClient("TLS", false); + ftpClient.setControlEncoding("UTF-8"); + try { + ftpClient.connect(ftpServer, ftpPort); - // TLS - ftpClient = new FTPSClient("TLS", false); - ftpClient.setControlEncoding("UTF-8"); - ftpClient.connect(ftpServer, Integer.parseInt(ftpPort)); if (ftpClient.login(ftpUser, ftpPassword) == false) { - documentImportService.logMessage("FTP file transfer failed: login failed!", event); - event.setResult(DocumentImportEvent.PROCESSING_ERROR); - return; + throw new PluginException(CargosoftXMLInvoiceImportImportService.class.getName(), + "FTP_ERROR", "FTP file transfer failed: login failed!"); } - ftpClient.enterLocalPassiveMode(); logger.finest("...... FileType=" + FTP.BINARY_FILE_TYPE); ftpClient.setFileType(FTP.BINARY_FILE_TYPE); ftpClient.setControlEncoding("UTF-8"); - // try to enter the working directory.... + } catch (IOException e) { + throw new PluginException(CargosoftXMLInvoiceImportImportService.class.getName(), + "FTP_ERROR", e.getMessage()); + } + + return ftpClient; + } + + /** + * Helper method reads the working FTP directory and starts importing the files. + * + * @param ftpClient + * @param ftpPath + * @throws PluginException + */ + private void scannFTPDirectory(FTPClient ftpClient, String ftpPath, String mandantID, DocumentImportEvent event) + throws PluginException { + try { + logger.finest("......read directory " + ftpPath); + // try to enter the working directory and read all files... boolean bWorkingDir = ftpClient.changeWorkingDirectory(ftpPath); - if (bWorkingDir == true) { + if (bWorkingDir == false) { + throw new PluginException(CargosoftXMLInvoiceImportImportService.class.getName(), + "FTP_ERROR", "...failed to change into working directory: "); + } - documentImportService.logMessage("...working directory: " + ftpClient.printWorkingDirectory(), event); + FTPFile[] allFiles = ftpClient.listFiles(ftpPath); - // scann directories... + // FTPFile[] allFiles = ftpClient.listFiles(); + int count = 0; + if (allFiles.length > 0) { + documentImportService.logMessage("..." + allFiles.length + " files found ", event); + for (FTPFile file : allFiles) { + // if this is a directory or symlink then we do ignore this entry + if (!file.isFile()) { + logger.warning("...'" + file.getName() + "' is not a valid file, object will be ignored!"); + continue; + } + ItemCollection invoice = null; + logger.info("import file " + file.getName() + "..."); + // String fullFileName = ftpPath + "/" + file.getName(); + try (ByteArrayOutputStream is = new ByteArrayOutputStream();) { + ftpClient.retrieveFile(file.getName(), is); + byte[] rawData = is.toByteArray(); + if (rawData != null && rawData.length > 0) { + logger.finest("......file '" + file.getName() + "' successful read - bytes size = " + + rawData.length); + // create new workitem + invoice = createWorkitem(event.getSource(), file.getName(), rawData); - FTPFile[] subDirectories = ftpClient.listDirectories(ftpPath); - - for (FTPFile ftpSubDirectory : subDirectories) { - ItemCollection space = null; - - String ftpSubDirectoryName = ftpSubDirectory.getName(); - - logger.info("......scan files from path=" + ftpPath - + ftpSubDirectory.getName()); - - /* - * Nun importieren wir alle Files - */ - - FTPFile[] allFiles = ftpClient.listFiles(ftpPath + ftpSubDirectory.getName()); - - /* - * create a new workitem for each document - */ - - int count = 0; - if (allFiles.length > 0) { - documentImportService.logMessage("..." + allFiles.length + " files found ", event); - ftpClient.changeWorkingDirectory(ftpPath + ftpSubDirectory.getName()); - - for (FTPFile file : allFiles) { - // if this is a directory or symlink then we do ignore this entry - if (!file.isFile()) { - documentImportService.logMessage( - "...'" + file.getName() + "' is not a valid file, object will be ignored!", - event); - continue; + // persist workitem only if not exists and with a matching mandant.id + if (!invoice.getItemValueString("mandant.id").equals(mandantID)) { + throw new PluginException(CargosoftXMLInvoiceImportImportService.class.getName(), + "FTP_ERROR", "Invoice does not match mandantID " + mandantID); } - logger.info("import file " + file.getName() + "..."); - // String fullFileName = ftpPath + "/" + file.getName(); - try (ByteArrayOutputStream is = new ByteArrayOutputStream();) { - ftpClient.retrieveFile(file.getName(), is); - byte[] rawData = is.toByteArray(); - if (rawData != null && rawData.length > 0) { - logger.finest("......file '" + file.getName() + "' successfull read - bytes size = " - + rawData.length); - // create new workitem - createWorkitem(event.getSource(), file.getName(), rawData, space); - // documentImportService.logMessage("....imported '" + file.getName() + "'", - // event); - count++; - } else { - documentImportService.logMessage("...Warning - invalid file content '" - + file.getName() + "' - file will be deleted!", event); - } - // finally delete the file.... - ftpClient.deleteFile(file.getName()); - } catch (AccessDeniedException | ProcessingErrorException | PluginException - | ModelException e) { - documentImportService.logMessage("...FTP import failed: " + e.getMessage(), event); - event.setResult(DocumentImportEvent.PROCESSING_ERROR); - return; - } + count++; + } else { + logger.warning("...Warning - invalid file content '" + + file.getName() + "' - file will be deleted!"); } - documentImportService.logMessage("..." + count + " new files imported.", event); - } else { - documentImportService.logMessage("...no files found, directory '" + ftpPath + "' is empty", - event); + if (invoice != null) { + workflowService.processWorkItem(invoice); + } + // finally delete the file.... + // ftpClient.deleteFile(file.getName()); + } catch (AccessDeniedException | ProcessingErrorException | PluginException + | ModelException e) { + throw new PluginException(CargosoftXMLInvoiceImportImportService.class.getName(), + "FTP_ERROR", e.getMessage()); } } - } else { - documentImportService.logMessage("...failed to change into working directory: " + ftpPath, event); + documentImportService.logMessage("..." + count + " new files imported.", event); } } catch (IOException e) { @@ -257,10 +279,8 @@ public class CargosoftXMLInvoiceImportImportService { int r = ftpClient.getReplyCode(); logger.severe("FTP ReplyCode=" + r); - documentImportService.logMessage("...FTP file transfer failed (replyCode=" + r + ") : " + e.getMessage(), - event); - event.setResult(DocumentImportEvent.PROCESSING_ERROR); - return; + throw new PluginException(CargosoftXMLInvoiceImportImportService.class.getName(), + "FTP_ERROR", e.getMessage()); } finally { // do logout.... @@ -270,14 +290,10 @@ public class CargosoftXMLInvoiceImportImportService { ftpClient.disconnect(); } catch (IOException e) { documentImportService.logMessage("...FTP file transfer failed: " + e.getMessage(), event); - event.setResult(DocumentImportEvent.PROCESSING_ERROR); - return; + throw new PluginException(CargosoftXMLInvoiceImportImportService.class.getName(), + "FTP_ERROR", e.getMessage()); } } - - // completed - event.setResult(DocumentImportEvent.PROCESSING_COMPLETED); - } /** @@ -291,7 +307,7 @@ public class CargosoftXMLInvoiceImportImportService { * @throws ProcessingErrorException * @throws AccessDeniedException */ - public ItemCollection createWorkitem(ItemCollection source, String fileName, byte[] rawData, ItemCollection space) + public ItemCollection createWorkitem(ItemCollection source, String fileName, byte[] rawData) throws AccessDeniedException, ProcessingErrorException, PluginException, ModelException { ItemCollection workitem = new ItemCollection(); workitem.model(source.getItemValueString(DocumentImportService.SOURCE_ITEM_MODELVERSION)); @@ -307,12 +323,100 @@ public class CargosoftXMLInvoiceImportImportService { try { documentBuilder = DocumentBuilderFactory.newInstance().newDocumentBuilder(); Document doc = documentBuilder.parse(inputSource); - Node node = doc.importNode(doc.getDocumentElement(), true); - } catch (ParserConfigurationException | SAXException | IOException e) { - // TODO Auto-generated catch block - e.printStackTrace(); + + // Now we parse the following item values + // invoice.number + // invoice.currency + // invoice.text + // invoice.date + // dbtr.number + // invoice.duedate + // invoice.reminder + // payment.term + // invoice.rate + // invoice.base.amount + + readXMLValue(doc, "/Invoices/Invoice/InvoiceHeader/Client/Codes/Code", workitem, + "mandant.id", String.class); + readXMLValue(doc, "/Invoices/Invoice/InvoiceHeader/InvoiceAmount/NetAmount/Amount/Value", + workitem, "invoice.total", String.class); + + readXMLValue(doc, "/Invoices/Invoice/InvoiceHeader/InvoiceCurrency/Codes/Code", + workitem, "invoice.currency", String.class); + + // invoice.date 2024-05-13T00:00:00+02:00 + readXMLValue(doc, "/Invoices/Invoice/InvoiceHeader/InvoiceDate", + workitem, "invoice.date", Date.class); + + readXMLValue(doc, "/Invoices/Invoice/InvoiceHeader/InvoiceAddress/Codes/Code", + workitem, "dbtr.number", String.class); + String dbtrNumber = workitem.getItemValueString("dbtr.number"); + if (dbtrNumber.startsWith("K") || dbtrNumber.startsWith("D")) { + workitem.setItemValue("dbtr.number", dbtrNumber.substring(1)); + } + + // read filename + readXMLValue(doc, "/Invoices/Invoice/InvoiceHeader/Attachments/Attachment/Filename", + workitem, "import.filename", String.class); + readXMLValue(doc, "/Invoices/Invoice/InvoiceHeader/Attachments/Attachment/Content", + workitem, "import.filedata", String.class); + String fileDataString = workitem.getItemValueString("import.filedata"); + byte[] decodedData = java.util.Base64.getDecoder().decode(fileDataString); + FileData fileData = new FileData(workitem.getItemValueString("import.filename"), + decodedData, "application/pdf", null); + workitem.addFileData(fileData); + workitem.removeItem("import.filename"); + workitem.removeItem("import.filedata"); + + } catch (ParserConfigurationException | SAXException | IOException | XPathExpressionException e) { + throw new PluginException(CargosoftXMLInvoiceImportImportService.class.getName(), + "XML_ERROR", e.getMessage()); } return workitem; } + public void getMist(String itemName, Class itemType) { + } + + /** + * Reads a tag value from the xml tree and set the value into the given + * workitem. + * + * /Invoices/Invoice/InvoiceHeader/Client/Code + * + * @param doc - xml doc + * @param expression - xpath expression + * @param workitem + * @param itemName + * @param itemType + * @throws XPathExpressionException + * @throws PluginException + */ + private void readXMLValue(Document doc, String expression, ItemCollection workitem, String itemName, + Class itemType) throws XPathExpressionException { + // create XPath... + XPathFactory xpathFactory = XPathFactory.newInstance(); + XPath xpath = xpathFactory.newXPath(); + XPathExpression xPathExpression = xpath.compile(expression); + // extract node value + Node valueNode = (Node) xPathExpression.evaluate(doc, XPathConstants.NODE); + if (valueNode != null) { + String value = valueNode.getTextContent(); + + if (itemType == Date.class) { + // 2024-05-13T00:00:00+02:00 + SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssXXX"); + formatter.setTimeZone(TimeZone.getTimeZone("CET")); + try { + workitem.setItemValue(itemName, formatter.parse(value)); + } catch (ParseException e) { + logger.warning("Invalid Date Format"); + } + + } else { + workitem.setItemValue(itemName, value); + } + + } + } }