From 219ee170ff00c5f15ac4a2393ab4339d20d810e7 Mon Sep 17 00:00:00 2001 From: Ralph Soika Date: Mon, 27 May 2024 21:19:10 +0200 Subject: [PATCH] modified: office-alexander-logistics-app/src/main/java/com/alexanderlogistics/datev/DatevCargosoftImportAdapter.java modified: office-alexander-logistics-app/src/main/java/com/alexanderlogistics/xml/CargosoftXMLInvoiceImportImportService.java modified: reports/sepa/result_sepa01.xml modified: reports/sepa/result_sepa02.xml deleted: MAIL_OAUTH2.md deleted: OP_MODULE.md modified: README.md --- .../datev/DatevCargosoftImportAdapter.java | 2 +- ...argosoftXMLInvoiceImportImportService.java | 182 +++++++++++++++--- reports/sepa/result_sepa01.xml | 4 +- reports/sepa/result_sepa02.xml | 4 +- 4 files changed, 159 insertions(+), 33 deletions(-) diff --git a/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/datev/DatevCargosoftImportAdapter.java b/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/datev/DatevCargosoftImportAdapter.java index b39ab1d..6b50023 100644 --- a/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/datev/DatevCargosoftImportAdapter.java +++ b/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/datev/DatevCargosoftImportAdapter.java @@ -436,7 +436,7 @@ public class DatevCargosoftImportAdapter implements SignalAdapter { // find the textblock... List result = documentService.find(sQuery, 1, 0); if (result.size() > 0) { - throw new AdapterException(DatevCargosoftImportAdapter.class.getName(), "DUPPLICATE", + throw new AdapterException(DatevCargosoftImportAdapter.class.getName(), "DUPLICATE", "Invoice already imported"); } } catch (QueryException e) { 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 9801d28..0587c57 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 @@ -34,7 +34,9 @@ import java.io.IOException; import java.io.InputStream; import java.text.ParseException; import java.text.SimpleDateFormat; +import java.util.ArrayList; import java.util.Date; +import java.util.List; import java.util.Properties; import java.util.TimeZone; import java.util.logging.Logger; @@ -59,17 +61,22 @@ 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.DocumentService; import org.imixs.workflow.engine.ModelService; import org.imixs.workflow.engine.WorkflowService; import org.imixs.workflow.exceptions.AccessDeniedException; import org.imixs.workflow.exceptions.ModelException; import org.imixs.workflow.exceptions.PluginException; import org.imixs.workflow.exceptions.ProcessingErrorException; +import org.imixs.workflow.exceptions.QueryException; import org.w3c.dom.Document; import org.w3c.dom.Node; +import org.w3c.dom.NodeList; import org.xml.sax.InputSource; import org.xml.sax.SAXException; +import com.alexanderlogistics.InvoiceUtil; + /** * Der CargosoftXMLInvoiceImportImportService erweitert den FTPImportService und * kann XML Invoice Dokumente von Cargosoft importieren. @@ -96,10 +103,13 @@ public class CargosoftXMLInvoiceImportImportService { private static Logger logger = Logger.getLogger(CargosoftXMLInvoiceImportImportService.class.getName()); public static final String OPTION_MANDANT_ID = "mandant.id"; + public static final String DATE_FORMAT = "yyyy-MM-dd'T'HH:mm:ss"; @EJB WorkflowService workflowService; + @EJB + DocumentService documentService; @EJB ModelService modelService; @@ -247,23 +257,25 @@ public class CargosoftXMLInvoiceImportImportService { + rawData.length); // create new workitem invoice = createWorkitem(event.getSource(), file.getName(), rawData); - - // 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); + if (invoice == null) { + // no new data found + continue; } - - count++; } else { logger.warning("...Warning - invalid file content '" + file.getName() + "' - file will be deleted!"); } if (invoice != null) { + // 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); + } + count++; workflowService.processWorkItem(invoice); } - // finally delete the file.... + // finally move the file.... // ftpClient.deleteFile(file.getName()); } catch (AccessDeniedException | ProcessingErrorException | PluginException | ModelException e) { @@ -314,6 +326,7 @@ public class CargosoftXMLInvoiceImportImportService { workitem.task(source.getItemValueInteger(DocumentImportService.SOURCE_ITEM_TASK)); workitem.event(source.getItemValueInteger(DocumentImportService.SOURCE_ITEM_EVENT)); workitem.setWorkflowGroup(source.getItemValueString("workflowgroup")); + workitem.setItemValue("cargosoft.import.filename", fileName); // Now we parse the rawData.... InputStream inputStream = new ByteArrayInputStream(rawData); @@ -338,16 +351,28 @@ public class CargosoftXMLInvoiceImportImportService { readXMLValue(doc, "/Invoices/Invoice/InvoiceHeader/Client/Codes/Code", workitem, "mandant.id", String.class); + + readXMLValue(doc, "/Invoices/Invoice/InvoiceHeader/InvoiceNumber", + workitem, "invoice.number", String.class); + + // verify if invoice is already imported. + if (alreadyImported(workitem.getItemValueString("invoice.number"))) { + logger.warning("Invoice " + workitem.getItemValueString("invoice.number") + " already imported"); + return null; + } + 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/PaymentConditions/DueDate", + workitem, "invoice.duedate", Date.class); + readXMLValue(doc, "/Invoices/Invoice/InvoiceHeader/InvoiceAddress/Codes/Code", workitem, "dbtr.number", String.class); String dbtrNumber = workitem.getItemValueString("dbtr.number"); @@ -355,6 +380,10 @@ public class CargosoftXMLInvoiceImportImportService { workitem.setItemValue("dbtr.number", dbtrNumber.substring(1)); } + // Row Positions lesen + // InvoiceRows/InvoiceRow + readXMLRows(doc, workitem); + // read filename readXMLValue(doc, "/Invoices/Invoice/InvoiceHeader/Attachments/Attachment/Filename", workitem, "import.filename", String.class); @@ -368,7 +397,7 @@ public class CargosoftXMLInvoiceImportImportService { workitem.removeItem("import.filename"); workitem.removeItem("import.filedata"); - } catch (ParserConfigurationException | SAXException | IOException | XPathExpressionException e) { + } catch (ParserConfigurationException | SAXException | IOException e) { throw new PluginException(CargosoftXMLInvoiceImportImportService.class.getName(), "XML_ERROR", e.getMessage()); } @@ -384,39 +413,136 @@ public class CargosoftXMLInvoiceImportImportService { * * /Invoices/Invoice/InvoiceHeader/Client/Code * + * Beispiel Datum: + * 2024-05-13T00:00:00+02:00 + * * @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 { + Class itemType) { // 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(); + XPathExpression xPathExpression; + try { + xPathExpression = xpath.compile(expression); - 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"); + // 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(DATE_FORMAT); + formatter.setTimeZone(TimeZone.getTimeZone("CET")); + try { + workitem.setItemValue(itemName, formatter.parse(value)); + } catch (ParseException e) { + logger.warning("Invalid Date Format"); + } + return; } - - } else { + if (itemType == Double.class) { + workitem.setItemValue(itemName, Double.parseDouble(value)); + return; + } + if (itemType == Integer.class) { + workitem.setItemValue(itemName, Integer.parseInt(value)); + return; + } + // Default String format workitem.setItemValue(itemName, value); + } + } catch (XPathExpressionException e) { + logger.warning("Unable to read data field '" + expression + "' : " + e.getMessage()); } } + + /** + * Reads the positions rows + * + * InvoiceRows/InvoiceRow + * + * Each row has the following items: + * _childitems { + * datev.shzeichen=[H], + * datev.kurs=[0.0], + * datev.basisumsatz=[0.0], + * datev.text=[R LA-PAP-2405-051], + * datev.konto=[3851], + * datev.umsatz=[3307.81], + * datev.wkz=[EUR]}} + * + * @param doc - xml doc + * @param workitem + */ + private void readXMLRows(Document doc, ItemCollection workitem) { + // create XPath... + List childItems = new ArrayList<>(); + + XPathFactory xpathFactory = XPathFactory.newInstance(); + XPath xPath = xpathFactory.newXPath(); + try { + // Compile the XPath expressions + XPathExpression fileNumberExpr = xPath.compile("FileNumber/text()"); + XPathExpression netAmountCurrencyExpr = xPath + .compile("InvoiceAmount/NetAmount/Amount/Currency/Codes/Code[@Type='cs']/text()"); + XPathExpression netAmountValueExpr = xPath.compile("InvoiceAmount/NetAmount/Amount/Value/text()"); + + NodeList rowList = doc.getElementsByTagName("InvoiceRow"); + for (int i = 0; i < rowList.getLength(); i++) { + ItemCollection childItemCol = new ItemCollection(); + Node rowNode = rowList.item(i); + if (rowNode.getNodeType() == Node.ELEMENT_NODE) { + + // Get the FileNumber value + String fileNumber = (String) fileNumberExpr.evaluate(rowNode, XPathConstants.STRING); + childItemCol.setItemValue("datev.text", fileNumber); + String currency = (String) netAmountCurrencyExpr.evaluate(rowNode, XPathConstants.STRING); + childItemCol.setItemValue("datev.wkz", currency); + String umsatz = (String) netAmountValueExpr.evaluate(rowNode, XPathConstants.STRING); + childItemCol.setItemValue("datev.umsatz", Double.parseDouble(umsatz)); + + childItems.add(childItemCol); + } + } + + } catch (XPathExpressionException e) { + logger.warning("Unable to read row : " + e.getMessage()); + + } + + // Set child items + InvoiceUtil.implodeChildList(workitem, childItems); + + } + + /** + * Prüft ob die Belegnummer schon existiert (importiert wurde) + * + * @param belegNummer + * @return + */ + private boolean alreadyImported(String belegNummer) { + + String sQuery = "((type:workitem OR type:workitemarchive) AND $modelversion:rechnungsausgang* AND invoice.number:\"" + + belegNummer + "\")"; + try { + // find the textblock... + List result = documentService.find(sQuery, 1, 0); + if (result.size() > 0) { + return true; + } + } catch (QueryException e) { + logger.warning("failed to search invoices by query: " + e.getMessage()); + } + return false; + } } diff --git a/reports/sepa/result_sepa01.xml b/reports/sepa/result_sepa01.xml index d07d74d..faf1ec5 100644 --- a/reports/sepa/result_sepa01.xml +++ b/reports/sepa/result_sepa01.xml @@ -6,7 +6,7 @@ bf073e72asdf512c4d60af7740872036 - 2024-04-12T09:10:53 + 2024-04-26T22:29:58 3 655.90 @@ -23,7 +23,7 @@ SEPA - 2024-04-12 + 2024-04-26 Targo Bank diff --git a/reports/sepa/result_sepa02.xml b/reports/sepa/result_sepa02.xml index 0d6411c..f1189c2 100644 --- a/reports/sepa/result_sepa02.xml +++ b/reports/sepa/result_sepa02.xml @@ -6,7 +6,7 @@ bf073e72asdf512c4d60af7740872036 - 2024-04-12T09:10:53 + 2024-04-26T22:29:58 3 655.90 @@ -23,7 +23,7 @@ SEPA - 2024-04-12 + 2024-04-26 Targo Bank