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
This commit is contained in:
parent
7d5cf15aa1
commit
219ee170ff
4 changed files with 159 additions and 33 deletions
|
|
@ -436,7 +436,7 @@ public class DatevCargosoftImportAdapter implements SignalAdapter {
|
||||||
// find the textblock...
|
// find the textblock...
|
||||||
List<ItemCollection> result = documentService.find(sQuery, 1, 0);
|
List<ItemCollection> result = documentService.find(sQuery, 1, 0);
|
||||||
if (result.size() > 0) {
|
if (result.size() > 0) {
|
||||||
throw new AdapterException(DatevCargosoftImportAdapter.class.getName(), "DUPPLICATE",
|
throw new AdapterException(DatevCargosoftImportAdapter.class.getName(), "DUPLICATE",
|
||||||
"Invoice already imported");
|
"Invoice already imported");
|
||||||
}
|
}
|
||||||
} catch (QueryException e) {
|
} catch (QueryException e) {
|
||||||
|
|
|
||||||
|
|
@ -34,7 +34,9 @@ import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.text.ParseException;
|
import java.text.ParseException;
|
||||||
import java.text.SimpleDateFormat;
|
import java.text.SimpleDateFormat;
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
|
import java.util.List;
|
||||||
import java.util.Properties;
|
import java.util.Properties;
|
||||||
import java.util.TimeZone;
|
import java.util.TimeZone;
|
||||||
import java.util.logging.Logger;
|
import java.util.logging.Logger;
|
||||||
|
|
@ -59,17 +61,22 @@ import org.imixs.archive.importer.DocumentImportEvent;
|
||||||
import org.imixs.archive.importer.DocumentImportService;
|
import org.imixs.archive.importer.DocumentImportService;
|
||||||
import org.imixs.workflow.FileData;
|
import org.imixs.workflow.FileData;
|
||||||
import org.imixs.workflow.ItemCollection;
|
import org.imixs.workflow.ItemCollection;
|
||||||
|
import org.imixs.workflow.engine.DocumentService;
|
||||||
import org.imixs.workflow.engine.ModelService;
|
import org.imixs.workflow.engine.ModelService;
|
||||||
import org.imixs.workflow.engine.WorkflowService;
|
import org.imixs.workflow.engine.WorkflowService;
|
||||||
import org.imixs.workflow.exceptions.AccessDeniedException;
|
import org.imixs.workflow.exceptions.AccessDeniedException;
|
||||||
import org.imixs.workflow.exceptions.ModelException;
|
import org.imixs.workflow.exceptions.ModelException;
|
||||||
import org.imixs.workflow.exceptions.PluginException;
|
import org.imixs.workflow.exceptions.PluginException;
|
||||||
import org.imixs.workflow.exceptions.ProcessingErrorException;
|
import org.imixs.workflow.exceptions.ProcessingErrorException;
|
||||||
|
import org.imixs.workflow.exceptions.QueryException;
|
||||||
import org.w3c.dom.Document;
|
import org.w3c.dom.Document;
|
||||||
import org.w3c.dom.Node;
|
import org.w3c.dom.Node;
|
||||||
|
import org.w3c.dom.NodeList;
|
||||||
import org.xml.sax.InputSource;
|
import org.xml.sax.InputSource;
|
||||||
import org.xml.sax.SAXException;
|
import org.xml.sax.SAXException;
|
||||||
|
|
||||||
|
import com.alexanderlogistics.InvoiceUtil;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Der CargosoftXMLInvoiceImportImportService erweitert den FTPImportService und
|
* Der CargosoftXMLInvoiceImportImportService erweitert den FTPImportService und
|
||||||
* kann XML Invoice Dokumente von Cargosoft importieren.
|
* kann XML Invoice Dokumente von Cargosoft importieren.
|
||||||
|
|
@ -96,10 +103,13 @@ public class CargosoftXMLInvoiceImportImportService {
|
||||||
|
|
||||||
private static Logger logger = Logger.getLogger(CargosoftXMLInvoiceImportImportService.class.getName());
|
private static Logger logger = Logger.getLogger(CargosoftXMLInvoiceImportImportService.class.getName());
|
||||||
public static final String OPTION_MANDANT_ID = "mandant.id";
|
public static final String OPTION_MANDANT_ID = "mandant.id";
|
||||||
|
public static final String DATE_FORMAT = "yyyy-MM-dd'T'HH:mm:ss";
|
||||||
|
|
||||||
@EJB
|
@EJB
|
||||||
WorkflowService workflowService;
|
WorkflowService workflowService;
|
||||||
|
|
||||||
|
@EJB
|
||||||
|
DocumentService documentService;
|
||||||
@EJB
|
@EJB
|
||||||
ModelService modelService;
|
ModelService modelService;
|
||||||
|
|
||||||
|
|
@ -247,23 +257,25 @@ public class CargosoftXMLInvoiceImportImportService {
|
||||||
+ rawData.length);
|
+ rawData.length);
|
||||||
// create new workitem
|
// create new workitem
|
||||||
invoice = createWorkitem(event.getSource(), file.getName(), rawData);
|
invoice = createWorkitem(event.getSource(), file.getName(), rawData);
|
||||||
|
if (invoice == null) {
|
||||||
// persist workitem only if not exists and with a matching mandant.id
|
// no new data found
|
||||||
if (!invoice.getItemValueString("mandant.id").equals(mandantID)) {
|
continue;
|
||||||
throw new PluginException(CargosoftXMLInvoiceImportImportService.class.getName(),
|
|
||||||
"FTP_ERROR", "Invoice does not match mandantID " + mandantID);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
count++;
|
|
||||||
} else {
|
} else {
|
||||||
logger.warning("...Warning - invalid file content '"
|
logger.warning("...Warning - invalid file content '"
|
||||||
+ file.getName() + "' - file will be deleted!");
|
+ file.getName() + "' - file will be deleted!");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (invoice != null) {
|
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);
|
workflowService.processWorkItem(invoice);
|
||||||
}
|
}
|
||||||
// finally delete the file....
|
// finally move the file....
|
||||||
// ftpClient.deleteFile(file.getName());
|
// ftpClient.deleteFile(file.getName());
|
||||||
} catch (AccessDeniedException | ProcessingErrorException | PluginException
|
} catch (AccessDeniedException | ProcessingErrorException | PluginException
|
||||||
| ModelException e) {
|
| ModelException e) {
|
||||||
|
|
@ -314,6 +326,7 @@ public class CargosoftXMLInvoiceImportImportService {
|
||||||
workitem.task(source.getItemValueInteger(DocumentImportService.SOURCE_ITEM_TASK));
|
workitem.task(source.getItemValueInteger(DocumentImportService.SOURCE_ITEM_TASK));
|
||||||
workitem.event(source.getItemValueInteger(DocumentImportService.SOURCE_ITEM_EVENT));
|
workitem.event(source.getItemValueInteger(DocumentImportService.SOURCE_ITEM_EVENT));
|
||||||
workitem.setWorkflowGroup(source.getItemValueString("workflowgroup"));
|
workitem.setWorkflowGroup(source.getItemValueString("workflowgroup"));
|
||||||
|
workitem.setItemValue("cargosoft.import.filename", fileName);
|
||||||
|
|
||||||
// Now we parse the rawData....
|
// Now we parse the rawData....
|
||||||
InputStream inputStream = new ByteArrayInputStream(rawData);
|
InputStream inputStream = new ByteArrayInputStream(rawData);
|
||||||
|
|
@ -338,16 +351,28 @@ public class CargosoftXMLInvoiceImportImportService {
|
||||||
|
|
||||||
readXMLValue(doc, "/Invoices/Invoice/InvoiceHeader/Client/Codes/Code", workitem,
|
readXMLValue(doc, "/Invoices/Invoice/InvoiceHeader/Client/Codes/Code", workitem,
|
||||||
"mandant.id", String.class);
|
"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",
|
readXMLValue(doc, "/Invoices/Invoice/InvoiceHeader/InvoiceAmount/NetAmount/Amount/Value",
|
||||||
workitem, "invoice.total", String.class);
|
workitem, "invoice.total", String.class);
|
||||||
|
|
||||||
readXMLValue(doc, "/Invoices/Invoice/InvoiceHeader/InvoiceCurrency/Codes/Code",
|
readXMLValue(doc, "/Invoices/Invoice/InvoiceHeader/InvoiceCurrency/Codes/Code",
|
||||||
workitem, "invoice.currency", String.class);
|
workitem, "invoice.currency", String.class);
|
||||||
|
|
||||||
// invoice.date <InvoiceDate>2024-05-13T00:00:00+02:00</InvoiceDate>
|
|
||||||
readXMLValue(doc, "/Invoices/Invoice/InvoiceHeader/InvoiceDate",
|
readXMLValue(doc, "/Invoices/Invoice/InvoiceHeader/InvoiceDate",
|
||||||
workitem, "invoice.date", Date.class);
|
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",
|
readXMLValue(doc, "/Invoices/Invoice/InvoiceHeader/InvoiceAddress/Codes/Code",
|
||||||
workitem, "dbtr.number", String.class);
|
workitem, "dbtr.number", String.class);
|
||||||
String dbtrNumber = workitem.getItemValueString("dbtr.number");
|
String dbtrNumber = workitem.getItemValueString("dbtr.number");
|
||||||
|
|
@ -355,6 +380,10 @@ public class CargosoftXMLInvoiceImportImportService {
|
||||||
workitem.setItemValue("dbtr.number", dbtrNumber.substring(1));
|
workitem.setItemValue("dbtr.number", dbtrNumber.substring(1));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Row Positions lesen
|
||||||
|
// InvoiceRows/InvoiceRow
|
||||||
|
readXMLRows(doc, workitem);
|
||||||
|
|
||||||
// read filename
|
// read filename
|
||||||
readXMLValue(doc, "/Invoices/Invoice/InvoiceHeader/Attachments/Attachment/Filename",
|
readXMLValue(doc, "/Invoices/Invoice/InvoiceHeader/Attachments/Attachment/Filename",
|
||||||
workitem, "import.filename", String.class);
|
workitem, "import.filename", String.class);
|
||||||
|
|
@ -368,7 +397,7 @@ public class CargosoftXMLInvoiceImportImportService {
|
||||||
workitem.removeItem("import.filename");
|
workitem.removeItem("import.filename");
|
||||||
workitem.removeItem("import.filedata");
|
workitem.removeItem("import.filedata");
|
||||||
|
|
||||||
} catch (ParserConfigurationException | SAXException | IOException | XPathExpressionException e) {
|
} catch (ParserConfigurationException | SAXException | IOException e) {
|
||||||
throw new PluginException(CargosoftXMLInvoiceImportImportService.class.getName(),
|
throw new PluginException(CargosoftXMLInvoiceImportImportService.class.getName(),
|
||||||
"XML_ERROR", e.getMessage());
|
"XML_ERROR", e.getMessage());
|
||||||
}
|
}
|
||||||
|
|
@ -384,39 +413,136 @@ public class CargosoftXMLInvoiceImportImportService {
|
||||||
*
|
*
|
||||||
* /Invoices/Invoice/InvoiceHeader/Client/Code
|
* /Invoices/Invoice/InvoiceHeader/Client/Code
|
||||||
*
|
*
|
||||||
|
* Beispiel Datum:
|
||||||
|
* <InvoiceDate>2024-05-13T00:00:00+02:00</InvoiceDate>
|
||||||
|
*
|
||||||
* @param doc - xml doc
|
* @param doc - xml doc
|
||||||
* @param expression - xpath expression
|
* @param expression - xpath expression
|
||||||
* @param workitem
|
* @param workitem
|
||||||
* @param itemName
|
* @param itemName
|
||||||
* @param itemType
|
* @param itemType
|
||||||
* @throws XPathExpressionException
|
|
||||||
* @throws PluginException
|
|
||||||
*/
|
*/
|
||||||
private <T> void readXMLValue(Document doc, String expression, ItemCollection workitem, String itemName,
|
private <T> void readXMLValue(Document doc, String expression, ItemCollection workitem, String itemName,
|
||||||
Class<T> itemType) throws XPathExpressionException {
|
Class<T> itemType) {
|
||||||
// create XPath...
|
// create XPath...
|
||||||
XPathFactory xpathFactory = XPathFactory.newInstance();
|
XPathFactory xpathFactory = XPathFactory.newInstance();
|
||||||
XPath xpath = xpathFactory.newXPath();
|
XPath xpath = xpathFactory.newXPath();
|
||||||
XPathExpression xPathExpression = xpath.compile(expression);
|
XPathExpression xPathExpression;
|
||||||
// extract node value
|
try {
|
||||||
Node valueNode = (Node) xPathExpression.evaluate(doc, XPathConstants.NODE);
|
xPathExpression = xpath.compile(expression);
|
||||||
if (valueNode != null) {
|
|
||||||
String value = valueNode.getTextContent();
|
|
||||||
|
|
||||||
if (itemType == Date.class) {
|
// extract node value
|
||||||
// 2024-05-13T00:00:00+02:00
|
Node valueNode = (Node) xPathExpression.evaluate(doc, XPathConstants.NODE);
|
||||||
SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssXXX");
|
if (valueNode != null) {
|
||||||
formatter.setTimeZone(TimeZone.getTimeZone("CET"));
|
String value = valueNode.getTextContent();
|
||||||
try {
|
|
||||||
workitem.setItemValue(itemName, formatter.parse(value));
|
if (itemType == Date.class) {
|
||||||
} catch (ParseException e) {
|
// 2024-05-13T00:00:00+02:00
|
||||||
logger.warning("Invalid Date Format");
|
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;
|
||||||
}
|
}
|
||||||
|
if (itemType == Double.class) {
|
||||||
} else {
|
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);
|
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<ItemCollection> 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<ItemCollection> 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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@
|
||||||
<CstmrCdtTrfInitn>
|
<CstmrCdtTrfInitn>
|
||||||
<GrpHdr>
|
<GrpHdr>
|
||||||
<MsgId>bf073e72asdf512c4d60af7740872036</MsgId>
|
<MsgId>bf073e72asdf512c4d60af7740872036</MsgId>
|
||||||
<CreDtTm>2024-04-12T09:10:53</CreDtTm>
|
<CreDtTm>2024-04-26T22:29:58</CreDtTm>
|
||||||
<NbOfTxs>3</NbOfTxs>
|
<NbOfTxs>3</NbOfTxs>
|
||||||
<CtrlSum>655.90</CtrlSum>
|
<CtrlSum>655.90</CtrlSum>
|
||||||
<InitgPty>
|
<InitgPty>
|
||||||
|
|
@ -23,7 +23,7 @@
|
||||||
<Cd>SEPA</Cd>
|
<Cd>SEPA</Cd>
|
||||||
</SvcLvl>
|
</SvcLvl>
|
||||||
</PmtTpInf>
|
</PmtTpInf>
|
||||||
<ReqdExctnDt>2024-04-12</ReqdExctnDt>
|
<ReqdExctnDt>2024-04-26</ReqdExctnDt>
|
||||||
<Dbtr>
|
<Dbtr>
|
||||||
<Nm>Targo Bank</Nm>
|
<Nm>Targo Bank</Nm>
|
||||||
</Dbtr>
|
</Dbtr>
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@
|
||||||
<CstmrCdtTrfInitn>
|
<CstmrCdtTrfInitn>
|
||||||
<GrpHdr>
|
<GrpHdr>
|
||||||
<MsgId>bf073e72asdf512c4d60af7740872036</MsgId>
|
<MsgId>bf073e72asdf512c4d60af7740872036</MsgId>
|
||||||
<CreDtTm>2024-04-12T09:10:53</CreDtTm>
|
<CreDtTm>2024-04-26T22:29:58</CreDtTm>
|
||||||
<NbOfTxs>3</NbOfTxs>
|
<NbOfTxs>3</NbOfTxs>
|
||||||
<CtrlSum>655.90</CtrlSum>
|
<CtrlSum>655.90</CtrlSum>
|
||||||
<InitgPty>
|
<InitgPty>
|
||||||
|
|
@ -23,7 +23,7 @@
|
||||||
<Cd>SEPA</Cd>
|
<Cd>SEPA</Cd>
|
||||||
</SvcLvl>
|
</SvcLvl>
|
||||||
</PmtTpInf>
|
</PmtTpInf>
|
||||||
<ReqdExctnDt>2024-04-12</ReqdExctnDt>
|
<ReqdExctnDt>2024-04-26</ReqdExctnDt>
|
||||||
<Dbtr>
|
<Dbtr>
|
||||||
<Nm>Targo Bank</Nm>
|
<Nm>Targo Bank</Nm>
|
||||||
</Dbtr>
|
</Dbtr>
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue