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:
+ *
+ * - taxType "1" (Poland): P_13_1 (domestic VAT)
+ * - taxType "2" (EU): P_13_6_2 (intra-community delivery, 0%)
+ * - taxType "3" (Non-EU): P_13_6_3 (export, 0%)
+ *
+ */
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