diff --git a/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/einvoice/KSeFAdapter.java b/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/einvoice/KSeFAdapter.java index 45d17d0..91b33e9 100644 --- a/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/einvoice/KSeFAdapter.java +++ b/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/einvoice/KSeFAdapter.java @@ -2,6 +2,10 @@ package com.alexanderlogistics.einvoice; import java.io.ByteArrayInputStream; import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.InputStream; +import java.math.BigDecimal; +import java.math.RoundingMode; import java.text.NumberFormat; import java.text.SimpleDateFormat; import java.util.Collection; @@ -10,6 +14,9 @@ import java.util.List; import java.util.Locale; import java.util.logging.Logger; +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.parsers.ParserConfigurationException; import javax.xml.transform.TransformerException; import org.imixs.archive.core.SnapshotService; @@ -29,11 +36,15 @@ import org.imixs.workflow.exceptions.AdapterException; import org.imixs.workflow.exceptions.PluginException; import org.imixs.workflow.exceptions.QueryException; import org.imixs.workflow.util.XMLParser; +import org.w3c.dom.Document; import org.w3c.dom.Element; +import org.xml.sax.InputSource; +import org.xml.sax.SAXException; import com.alexanderlogistics.BusinessPartnerService; import com.alexanderlogistics.InvoiceService; import com.alexanderlogistics.InvoiceUtil; +import com.alexanderlogistics.xml.CargosoftXMLInvoiceImportService; import jakarta.inject.Inject; @@ -155,13 +166,20 @@ public class KSeFAdapter implements SignalAdapter { // date model.setIssueDateTime(workitem.getItemValueLocalDate("invoice.date")); - model.setDueDateTime(workitem.getItemValueLocalDate("invoice.duedate")); + + // Set Performance Date + if (workitem.getItemValueDate("invoice.performancedate") == null) { + // hilfs code um das invoice.performancedate nachträglich zu parsen + syncPerformanceDate(workitem); + } + ((EInvoiceModelKSeF) model) + .setPerformanceDateTime(workitem.getItemValueLocalDate("invoice.performancedate")); // Update Addresses TradeParty billingAddress = buildAddress(workitem, "buyer", model); model.setTradeParty(billingAddress); - // set Tax Type based on vatID + // set Tax Type based on vatID - as a result taxType is 1=Poland 2=EU 3=Other ((EInvoiceModelKSeF) model).setTaxType(workitem.getItemValueString("partner.vat")); workitem.setItemValue("invoice.tax.type", ((EInvoiceModelKSeF) model).getTaxType()); @@ -227,16 +245,56 @@ public class KSeFAdapter implements SignalAdapter { } // Summenbildung - - // Netto + /* + * Field mapping: + * + */ model.setNetTotalAmount(workitem.getItemValueDouble("invoice.total.net")); + + // ?? has no function + model.setTaxRate(workitem.getItemValueDouble("invoice.total.tax")); + // Tax + /* + * P_14_1 + * + * This call also generate P_14_1W bei fremdwährung + */ model.setTaxTotalAmount(InvoiceUtil.round(workitem.getItemValueDouble("invoice.total") - workitem.getItemValueDouble("invoice.total.net"))); - model.setTaxRate(workitem.getItemValueDouble("invoice.total.tax")); + /* + * If we have Foreign Currency we need to set P_14_1W in case we are tax type 1 + */ + if (!"PLN".equals(workitem.getItemValueString("invoice.currency"))) { + if ("1".equals(((EInvoiceModelKSeF) model).getTaxType())) { + // compute rate + double rate = workitem.getItemValueDouble("invoice.rate"); + logger.info("│ ├── rate=" + rate); + double totalTax = InvoiceUtil.round(workitem.getItemValueDouble("invoice.total.tax")); + logger.info("│ ├── total.tax=" + totalTax); + BigDecimal value = BigDecimal.valueOf(totalTax) + .divide(BigDecimal.valueOf(rate), 10, RoundingMode.HALF_UP); + // P_14_1W must come directly after P_14_1 + logger.info("│ ├── P_14_1W=" + value); + Element element = model.findOrCreateChildNodeAfter(elementFa, EInvoiceNS.KSEF, "P_14_1W", "P_14_1"); + element.setTextContent(value.setScale(2, RoundingMode.HALF_UP).toPlainString()); + } + + } + // Brutto + /* + * P_15 + */ model.setGrandTotalAmount(workitem.getItemValueDouble("invoice.total")); + // finally set the due date at the end of the XML tree + model.setDueDateTime(workitem.getItemValueLocalDate("invoice.duedate")); + // finally update the template file fileDataXMLTemplate.setContent(model.getContent()); @@ -246,6 +304,42 @@ public class KSeFAdapter implements SignalAdapter { } + /** + * Dies ist eine Hilfsmethode die nachträglich das invoice.performancedate aus + * dem Cargosoft XML ausliest + * + * @param workitem + * @throws PluginException + */ + private void syncPerformanceDate(ItemCollection workitem) throws PluginException { + try { + logger.info("----Resync cargosoft performancedate...."); + DocumentBuilder documentBuilder; + // hole die XML Datei + FileData cargoXML = snapshotService.getWorkItemFile(workitem.getUniqueID(), + workitem.getItemValueString("cargosoft.import.filename")); + if (cargoXML != null) { + InputStream inputStream = new ByteArrayInputStream(cargoXML.getContent()); + InputSource inputSource = new InputSource(inputStream); + + documentBuilder = DocumentBuilderFactory.newInstance().newDocumentBuilder(); + Document doc = documentBuilder.parse(inputSource); + + // Performance Date + CargosoftXMLInvoiceImportService.readXMLValue(doc, "/Invoices/Invoice/InvoiceHeader/PerformanceDate", + workitem, "invoice.performancedate", + Date.class); + + // falls keines gefunden wurde nehmen wir das invoice date + if (workitem.getItemValueDate("invoice.performancedate") == null) { + workitem.setItemValue("invoice.performancedate", workitem.getItemValueDate("invoice.date")); + } + } + } catch (ParserConfigurationException | SAXException | IOException e) { + throw new PluginException(CargosoftXMLInvoiceImportService.class.getName(), "XML_ERROR", e.getMessage()); + } + } + /** * Gibt True zurück wenn es eine korrektur rechnung ist * diff --git a/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/xml/CargosoftXMLInvoiceImportService.java b/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/xml/CargosoftXMLInvoiceImportService.java index 0577495..fbecb63 100644 --- a/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/xml/CargosoftXMLInvoiceImportService.java +++ b/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/xml/CargosoftXMLInvoiceImportService.java @@ -445,6 +445,10 @@ public class CargosoftXMLInvoiceImportService { readXMLValue(doc, "/Invoices/Invoice/InvoiceHeader/PaymentConditions/DueDate", workitem, "invoice.reminder", Date.class); + // Performance Date + readXMLValue(doc, "/Invoices/Invoice/InvoiceHeader/PerformanceDate", workitem, "invoice.performancedate", + Date.class); + readXMLValue(doc, "/Invoices/Invoice/InvoiceHeader/InvoiceAddress/Codes/Code", workitem, "dbtr.number", String.class); String dbtrNumber = workitem.getItemValueString("dbtr.number"); @@ -522,7 +526,7 @@ public class CargosoftXMLInvoiceImportService { * @param itemName * @param itemType */ - private void readXMLValue(Document doc, String expression, ItemCollection workitem, String itemName, + public static void readXMLValue(Document doc, String expression, ItemCollection workitem, String itemName, Class itemType) { // create XPath... XPathFactory xpathFactory = XPathFactory.newInstance(); diff --git a/office-alexander-logistics-app/src/test/resources/ksef/output/invoice-simple.xml b/office-alexander-logistics-app/src/test/resources/ksef/output/invoice-simple.xml new file mode 100644 index 0000000..6e79abc --- /dev/null +++ b/office-alexander-logistics-app/src/test/resources/ksef/output/invoice-simple.xml @@ -0,0 +1,118 @@ + + + + FA + 3 + 2026-04-16T06:22:32.455873037Z + Imixs eInvoice + + + + + 9552521552 + Alexander Global Logistics + + + PL + Gdanska 36 + 70-660 Szczecin + + + MBudas@alexander-logistics.com + + + + + + + + 1234567890 + Test Sp. z o.o. + + + PL + ul. Testowa 1 + 00-001 Warszawa + + + D-12345 + 2 + + 2 + + + + + PLN + 2025-02-10 + + FV/2025/001 + + 2025-03-10 + + + + 10000.00 + 2300.00 + 12300.00 + + + 2 + + 2 + + 2 + + 2 + + + 1 + + + + 1 + + + 2 + + + 1 + + + + VAT + + + 1 + a743f373-b75c-44c5-a077-ee628295b5d3 + szt. + 1 + 6000.00 + 6000.00 + 0 + + + 2 + 9eb2d946-a3cb-4adb-8ed1-069e2f48d67d + szt. + 1 + 4000.00 + 4000.00 + 0 + + + diff --git a/office-alexander-logistics-app/src/test/resources/ksef/output/test-korrekturrechnung.xml b/office-alexander-logistics-app/src/test/resources/ksef/output/test-korrekturrechnung.xml index 675ab1f..883b811 100644 --- a/office-alexander-logistics-app/src/test/resources/ksef/output/test-korrekturrechnung.xml +++ b/office-alexander-logistics-app/src/test/resources/ksef/output/test-korrekturrechnung.xml @@ -6,7 +6,7 @@ FA 3 - 2026-02-24T16:11:02.707702Z + 2026-04-16T06:22:32.198172335Z Imixs eInvoice @@ -103,12 +103,13 @@ 1 - 781e1f16-a86e-452d-b959-01c76743a792 - Transport Berlin - Warsaw + d437ac36-9439-4bd6-96fb-0fe0b91fd32d + usługa spedycyjna / transport w relacji, PL63 - CZ43 szt. 1 -900.00 -900.00 + 0 diff --git a/workflow/pl/rechnungsausgang-pl-1.1.0.bpmn b/workflow/pl/rechnungsausgang-pl-1.1.0.bpmn index 04cc6da..0f9b688 100644 --- a/workflow/pl/rechnungsausgang-pl-1.1.0.bpmn +++ b/workflow/pl/rechnungsausgang-pl-1.1.0.bpmn @@ -1569,7 +1569,7 @@ Wiedervorlage wird um 7 bzw. 5 Tage erhöht - + ksef.xml created]]> false @@ -2301,7 +2301,7 @@ AGL Poland Team - + ksef.xml tranfered successful. ]]> false