From dd4feaa3347150a6d292a9d90deb946fe7592dce Mon Sep 17 00:00:00 2001 From: Kemal Taskin Date: Tue, 3 Dec 2024 14:13:55 +0100 Subject: [PATCH 01/22] Refactor and override toPDF method to accept xml string and return byte array --- .../ZUGFeRD/ZUGFeRDVisualizer.java | 58 +++++++++++++++++-- 1 file changed, 54 insertions(+), 4 deletions(-) diff --git a/library/src/main/java/org/mustangproject/ZUGFeRD/ZUGFeRDVisualizer.java b/library/src/main/java/org/mustangproject/ZUGFeRD/ZUGFeRDVisualizer.java index 64e14fc3..f9e456bf 100644 --- a/library/src/main/java/org/mustangproject/ZUGFeRD/ZUGFeRDVisualizer.java +++ b/library/src/main/java/org/mustangproject/ZUGFeRD/ZUGFeRDVisualizer.java @@ -35,6 +35,9 @@ import java.io.PipedOutputStream; import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.Paths; +import java.util.concurrent.atomic.AtomicReference; +import java.util.function.Consumer; +import java.util.function.Supplier; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; @@ -312,16 +315,61 @@ public class ZUGFeRDVisualizer { // the writing part File XMLinputFile = new File(xmlFilename); - String result = null; + String fopInput = null; /* remove file endings so that tests can also pass after checking out from git with arbitrary options (which may include CSRF changes) */ try { - result = this.toFOP(XMLinputFile.getAbsolutePath()); + fopInput = this.toFOP(XMLinputFile.getAbsolutePath()); } catch (FileNotFoundException | TransformerException e) { LOGGER.error("Failed to apply FOP", e); } + + toPDFfromFOP(fopInput, () -> { + try { + return new FileOutputStream(pdfFilename); + } catch (FileNotFoundException e) { + LOGGER.error("Failed to create PDF", e); + } + return null; + }, (OutputStream out) -> {}); + } + + public byte[] toPDF(String xmlContent) { + + String fopInput = null; + + /* remove file endings so that tests can also pass after checking + out from git with arbitrary options (which may include CSRF changes) + */ + try { + ByteArrayInputStream fis = new ByteArrayInputStream(xmlContent.getBytes(StandardCharsets.UTF_8)); + EStandard theStandard = findOutStandardFromRootNode(fis); + fis = new ByteArrayInputStream(xmlContent.getBytes(StandardCharsets.UTF_8));//rewind :-( + + fopInput = toFOP(fis, theStandard); + } catch (FileNotFoundException | TransformerException e) { + LOGGER.error("Failed to apply FOP", e); + } + + AtomicReference byteHolder = new AtomicReference<>(); + ByteArrayOutputStream os = new ByteArrayOutputStream(); + toPDFfromFOP(fopInput, () -> new BufferedOutputStream(os), (OutputStream out) -> { + + try { + out.flush(); + } catch (IOException e) { + LOGGER.error("Failed to create PDF", e); + } + byteHolder.set(os.toByteArray()); + }); + + return byteHolder.get(); + } + + private void toPDFfromFOP(String fopInput, Supplier outputStreamDelegate, Consumer consumerDelegate) { + DefaultConfigurationBuilder cfgBuilder = new DefaultConfigurationBuilder(); Configuration cfg = null; @@ -349,7 +397,7 @@ public class ZUGFeRDVisualizer { // Step 2: Set up output stream. // Note: Using BufferedOutputStream for performance reasons (helpful with FileOutputStreams). - try (OutputStream out = new BufferedOutputStream(new FileOutputStream(pdfFilename))) { + try (OutputStream out = new BufferedOutputStream(outputStreamDelegate.get())) { // Step 3: Construct fop with desired output format Fop fop = fopFactory.newFop(MimeConstants.MIME_PDF, userAgent, out); @@ -360,13 +408,15 @@ public class ZUGFeRDVisualizer { // Step 5: Setup input and output for XSLT transformation // Setup input stream - Source src = new StreamSource(new ByteArrayInputStream(result.getBytes(StandardCharsets.UTF_8))); + Source src = new StreamSource(new ByteArrayInputStream(fopInput.getBytes(StandardCharsets.UTF_8))); // Resulting SAX events (the generated FO) must be piped through to FOP Result res = new SAXResult(fop.getDefaultHandler()); // Step 6: Start XSLT transformation and FOP processing transformer.transform(src, res); + + consumerDelegate.accept(out); } catch (FOPException | IOException | TransformerException e) { LOGGER.error("Failed to create PDF", e); From 5732d23555ad0cd4e322960986f0fbfea406650a Mon Sep 17 00:00:00 2001 From: langfr Date: Sun, 8 Dec 2024 14:40:53 +0000 Subject: [PATCH 02/22] Enable flexible PaymentReference and a DocumentName. --- .../main/java/org/mustangproject/Invoice.java | 11 +++++++++++ .../ZUGFeRD/IExportableTransaction.java | 4 ++++ .../ZUGFeRD/ZUGFeRD2PullProvider.java | 17 ++++++++--------- .../ZUGFeRD/ZUGFeRDInvoiceImporter.java | 12 +++++++++++- .../org/mustangproject/ZUGFeRD/ZF2PushTest.java | 5 ++++- 5 files changed, 38 insertions(+), 11 deletions(-) diff --git a/library/src/main/java/org/mustangproject/Invoice.java b/library/src/main/java/org/mustangproject/Invoice.java index 10ba057f..667f8a6d 100644 --- a/library/src/main/java/org/mustangproject/Invoice.java +++ b/library/src/main/java/org/mustangproject/Invoice.java @@ -67,6 +67,7 @@ public class Invoice implements IExportableTransaction { protected String vatDueDateTypeCode = null; protected String creditorReferenceID; // required when direct debit is used. private BigDecimal roundingAmount=null; + private String paymentReference; // Remittance information / Verwendungszweck, BT-83 public Invoice() { ZFItems = new ArrayList<>(); @@ -617,6 +618,16 @@ public class Invoice implements IExportableTransaction { return this; } + @Override + public String getPaymentReference() { + return paymentReference; + } + + public Invoice setPaymentReference(String paymentReference) { + this.paymentReference = paymentReference; + return this; + } + @Override public TradeParty getDeliveryAddress() { return deliveryAddress; diff --git a/library/src/main/java/org/mustangproject/ZUGFeRD/IExportableTransaction.java b/library/src/main/java/org/mustangproject/ZUGFeRD/IExportableTransaction.java index 0a67a6be..a06952ae 100644 --- a/library/src/main/java/org/mustangproject/ZUGFeRD/IExportableTransaction.java +++ b/library/src/main/java/org/mustangproject/ZUGFeRD/IExportableTransaction.java @@ -312,6 +312,10 @@ public interface IExportableTransaction { return null; } + default String getPaymentReference() { + return null; + } + /** * returns if a rebate agreements exists * diff --git a/library/src/main/java/org/mustangproject/ZUGFeRD/ZUGFeRD2PullProvider.java b/library/src/main/java/org/mustangproject/ZUGFeRD/ZUGFeRD2PullProvider.java index ff5aa321..6316c31a 100644 --- a/library/src/main/java/org/mustangproject/ZUGFeRD/ZUGFeRD2PullProvider.java +++ b/library/src/main/java/org/mustangproject/ZUGFeRD/ZUGFeRD2PullProvider.java @@ -388,14 +388,13 @@ public class ZUGFeRD2PullProvider implements IXMLProvider { + "" + "" + "" - + "" + XMLTools.encodeXML(trans.getNumber()) + "" - // + "RECHNUNG" - // + "380" - + "" + typecode + "" - + "" - + DATE.udtFormat(trans.getIssueDate()) + "" // date + + "" + XMLTools.encodeXML(trans.getNumber()) + ""; + if (profile == Profiles.getByName("Extended") && trans.getDocumentName() != null) { + xml += "" + XMLTools.encodeXML(trans.getDocumentName()) + ""; + } + xml += "" + typecode + "" + + "" + DATE.udtFormat(trans.getIssueDate()) + "" // date + buildNotes(trans) - + "" + ""; int lineID = 0; @@ -662,8 +661,8 @@ public class ZUGFeRD2PullProvider implements IXMLProvider { if ((trans.getCreditorReferenceID() != null) && (getProfile() != Profiles.getByName("Minimum"))) { xml += "" + XMLTools.encodeXML(trans.getCreditorReferenceID()) + ""; } - if ((trans.getNumber() != null) && (getProfile() != Profiles.getByName("Minimum"))) { - xml += "" + XMLTools.encodeXML(trans.getNumber()) + ""; + if ((trans.getPaymentReference() != null) && (getProfile() != Profiles.getByName("Minimum"))) { + xml += "" + XMLTools.encodeXML(trans.getPaymentReference()) + ""; } xml += "" + trans.getCurrency() + ""; if (this.trans.getPayee() != null) { diff --git a/library/src/main/java/org/mustangproject/ZUGFeRD/ZUGFeRDInvoiceImporter.java b/library/src/main/java/org/mustangproject/ZUGFeRD/ZUGFeRDInvoiceImporter.java index ffcfaac1..e83ffea1 100644 --- a/library/src/main/java/org/mustangproject/ZUGFeRD/ZUGFeRDInvoiceImporter.java +++ b/library/src/main/java/org/mustangproject/ZUGFeRD/ZUGFeRDInvoiceImporter.java @@ -288,6 +288,7 @@ public class ZUGFeRDInvoiceImporter { public Invoice extractInto(Invoice zpp) throws XPathExpressionException, ParseException { String number = ""; + String documentName = null; String typeCode = null; String deliveryPeriodStart = null; String deliveryPeriodEnd = null; @@ -494,6 +495,9 @@ public class ZUGFeRDInvoiceImporter { if ((item.getLocalName() != null) && (item.getLocalName().equals("ID"))) { number = XMLTools.trimOrNull(item); } + if ((item.getLocalName() != null) && (item.getLocalName().equals("Name"))) { + documentName = XMLTools.trimOrNull(item); + } if ((item.getLocalName() != null) && (item.getLocalName().equals("TypeCode"))) { typeCode = XMLTools.trimOrNull(item); } @@ -659,6 +663,12 @@ public class ZUGFeRDInvoiceImporter { NodeList headerTradeSettlementChilds = headerTradeSettlementNode.getChildNodes(); for (int settlementChildIndex = 0; settlementChildIndex < headerTradeSettlementChilds.getLength(); settlementChildIndex++) { + if ((headerTradeSettlementChilds.item(settlementChildIndex).getLocalName() != null) + && (headerTradeSettlementChilds.item(settlementChildIndex).getLocalName().equals("PaymentReference"))) { + String paymentReference = headerTradeSettlementChilds.item(settlementChildIndex).getTextContent(); + zpp.setPaymentReference(paymentReference); + } + if ((headerTradeSettlementChilds.item(settlementChildIndex).getLocalName() != null) && (headerTradeSettlementChilds.item(settlementChildIndex).getLocalName().equals("SpecifiedTradePaymentTerms"))) { NodeList paymentTermChilds = headerTradeSettlementChilds.item(settlementChildIndex).getChildNodes(); @@ -780,7 +790,7 @@ public class ZUGFeRDInvoiceImporter { } - zpp.setIssueDate(issueDate).setDueDate(dueDate).setDeliveryDate(deliveryDate).setSender(new TradeParty(SellerNodes)).setRecipient(new TradeParty(BuyerNodes)).setNumber(number).setDocumentCode(typeCode); + zpp.setIssueDate(issueDate).setDueDate(dueDate).setDeliveryDate(deliveryDate).setSender(new TradeParty(SellerNodes)).setRecipient(new TradeParty(BuyerNodes)).setNumber(number).setDocumentName(documentName).setDocumentCode(typeCode); if ((directDebitMandateID != null) && (IBAN != null)) { DirectDebit d = new DirectDebit(IBAN, directDebitMandateID); diff --git a/library/src/test/java/org/mustangproject/ZUGFeRD/ZF2PushTest.java b/library/src/test/java/org/mustangproject/ZUGFeRD/ZF2PushTest.java index d435e58e..84968add 100644 --- a/library/src/test/java/org/mustangproject/ZUGFeRD/ZF2PushTest.java +++ b/library/src/test/java/org/mustangproject/ZUGFeRD/ZF2PushTest.java @@ -526,7 +526,7 @@ public class ZF2PushTest extends TestCase { try { SchemedID gtin = new SchemedID("0160", "2001015001325"); SchemedID gln = new SchemedID("0088", "4304171000002"); - ze.setTransaction(new Invoice().setCurrency("CHF").addNote("document level 1/2").addNote("document level 2/2").setDueDate(new Date()).setIssueDate(new Date()).setDeliveryDate(new Date()) + ze.setTransaction(new Invoice().setCurrency("CHF").addNote("document level 1/2").addNote("document level 2/2").setDueDate(new Date()).setIssueDate(new Date()).setDeliveryDate(new Date()).setPaymentReference("Verwendungszweck").setDocumentName("Rechnung") .setSellerOrderReferencedDocumentID("9384").setBuyerOrderReferencedDocumentID("28934") .setDetailedDeliveryPeriod(new SimpleDateFormat("yyyyMMdd").parse(occurrenceFrom), new SimpleDateFormat("yyyyMMdd").parse(occurrenceTo)) .setSender(new TradeParty(orgname, "teststr", "55232", "teststadt", "DE").addTaxID(taxID).setEmail("sender@test.org").setID(orgID).addVATID("DE0815")) @@ -578,6 +578,7 @@ public class ZF2PushTest extends TestCase { assertTrue(zi.getUTF8().contains("++49555123456")); assertTrue(zi.getUTF8().contains("Cash Discount")); // default description for cash discounts assertThat(zi.getUTF8()).valueByXPath("//*[local-name()='ApplicableTradeTax']/*[local-name()='DueDateTypeCode']").asString().isEqualTo(EventTimeCodeTypeConstants.PAYMENT_DATE); + assertTrue(zi.getUTF8().contains("Rechnung")); ZUGFeRDInvoiceImporter zii = new ZUGFeRDInvoiceImporter(TARGET_PUSHEDGE); try { @@ -587,6 +588,8 @@ public class ZF2PushTest extends TestCase { assertEquals("4304171000002", i.getRecipient().getGlobalID()); assertEquals("2001015001325", i.getZFItems()[0].getProduct().getGlobalID()); assertEquals(orgID, i.getSender().getID()); + assertEquals("Verwendungszweck", i.getPaymentReference()); + assertEquals("Rechnung", i.getDocumentName()); } catch (XPathExpressionException e) { fail("XPathExpressionException should not be raised"); From 4edeab56ee849d7a6f07aaf1730ba50e80dd4ade Mon Sep 17 00:00:00 2001 From: Adrian-Devries Date: Tue, 7 Jan 2025 16:24:03 +0100 Subject: [PATCH 03/22] Add net.sf.offo:fop-hyph Add hyphenation patterns to avoid the following issue: "WARN org.apache.fop.apps.FOUserAgent - Hyphenation pattern not found." --- library/pom.xml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/library/pom.xml b/library/pom.xml index 073661c1..8b5b3370 100644 --- a/library/pom.xml +++ b/library/pom.xml @@ -59,6 +59,12 @@ slf4j-api 2.0.9 + + net.sf.offo + fop-hyph + 2.0 + runtime + net.sf.saxon From d0d4346b71cdc4c0f1cd05f54c87f9a5ca698456 Mon Sep 17 00:00:00 2001 From: melo0187 <2528018+melo0187@users.noreply.github.com> Date: Mon, 13 Jan 2025 09:39:27 +0100 Subject: [PATCH 04/22] Fix #632: Return ubl_creditnote as Standard for CreditNotes This allows to distinguish between UBL's Invoice and UBL's CreditNote. I think this is usefule, since further processing with UBL libraries requires knowledge about the document type. --- .../java/org/mustangproject/ZUGFeRD/ZUGFeRDInvoiceImporter.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/src/main/java/org/mustangproject/ZUGFeRD/ZUGFeRDInvoiceImporter.java b/library/src/main/java/org/mustangproject/ZUGFeRD/ZUGFeRDInvoiceImporter.java index de66b35d..608a565e 100644 --- a/library/src/main/java/org/mustangproject/ZUGFeRD/ZUGFeRDInvoiceImporter.java +++ b/library/src/main/java/org/mustangproject/ZUGFeRD/ZUGFeRDInvoiceImporter.java @@ -1037,7 +1037,7 @@ public class ZUGFeRDInvoiceImporter { } else if (rootNode.equals("Invoice")) { return EStandard.ubl; } else if (rootNode.equals("CreditNote")) { - return EStandard.ubl; + return EStandard.ubl_creditnote; } else if (rootNode.equals("CrossIndustryInvoice")) { return EStandard.facturx; } else if (rootNode.equals("SCRDMCCBDACIDAMessageStructure")) { From b96a2c0438c3137993fe8bd904a7ec642a8b922e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fatih=20=C3=87atalkaya?= Date: Mon, 13 Jan 2025 14:17:09 +0100 Subject: [PATCH 05/22] Upgrades VeraPDF dependency to version 1.26.2 --- validator/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/validator/pom.xml b/validator/pom.xml index 67d7ca94..739163cb 100644 --- a/validator/pom.xml +++ b/validator/pom.xml @@ -73,7 +73,7 @@ org.verapdf validation-model-jakarta - 1.26.1 + 1.26.2 From a55bc529670d8ddc1a124028a6f4da1d0ecf7263 Mon Sep 17 00:00:00 2001 From: "mr.mister123" Date: Wed, 15 Jan 2025 10:37:25 +0100 Subject: [PATCH 06/22] optimized validation-report to pdf functionality --- .../main/resources/stylesheets/result-pdf.xsl | 47 ++++++++++++++----- 1 file changed, 36 insertions(+), 11 deletions(-) diff --git a/library/src/main/resources/stylesheets/result-pdf.xsl b/library/src/main/resources/stylesheets/result-pdf.xsl index 0dfa7c95..f3e2e0d4 100644 --- a/library/src/main/resources/stylesheets/result-pdf.xsl +++ b/library/src/main/resources/stylesheets/result-pdf.xsl @@ -28,6 +28,14 @@ red + + + Das XML ist valide. + + + Das XML ist nicht valide. + + green @@ -37,18 +45,26 @@ - + Das ZUGFeRD-PDF ist valide. - + Das ZUGFeRD-PDF ist nicht valide. + + + green + + + red + + - + Es wird empfohlen, das Dokument anzunehmen und es weiterzuverarbeiten. - + Es wird empfohlen, das Dokument zurückzuweisen. @@ -118,6 +134,7 @@ + From 11e36dac60be0253b533ac6f98623b20f29b2310 Mon Sep 17 00:00:00 2001 From: jstaerk Date: Fri, 17 Jan 2025 14:56:54 +0100 Subject: [PATCH 09/22] closes #689 --- History.md | 3 ++- .../mustangproject/ZUGFeRD/ZUGFeRD2PullProvider.java | 10 +++++----- .../java/org/mustangproject/ZUGFeRD/ZF2PushTest.java | 5 +++-- 3 files changed, 10 insertions(+), 8 deletions(-) diff --git a/History.md b/History.md index acf2b5ad..e9e401b1 100644 --- a/History.md +++ b/History.md @@ -1,6 +1,7 @@ - #678 - #679 -- #681 +- #681 +- #689 - be able to set detailedDeliveryPeriodFrom, detailedDeliveryPeriodTo MS188 - updated verapdf from 1.26.1 to 1.26.2 diff --git a/library/src/main/java/org/mustangproject/ZUGFeRD/ZUGFeRD2PullProvider.java b/library/src/main/java/org/mustangproject/ZUGFeRD/ZUGFeRD2PullProvider.java index 2903d81d..192582f4 100644 --- a/library/src/main/java/org/mustangproject/ZUGFeRD/ZUGFeRD2PullProvider.java +++ b/library/src/main/java/org/mustangproject/ZUGFeRD/ZUGFeRD2PullProvider.java @@ -288,8 +288,8 @@ public class ZUGFeRD2PullProvider implements IXMLProvider { final String allowanceChargeStr = "" + chargeIndicator + "" + percentage + "" + priceFormat(allowance.getTotalAmount(item)) + "" + - reason + reasonCode + + reason + ""; return allowanceChargeStr; } @@ -323,8 +323,8 @@ public class ZUGFeRD2PullProvider implements IXMLProvider { final String itemTotalAllowanceChargeStr = "" + chargeIndicator + "" + percentage + "" + currencyFormat(allowance.getTotalAmount(item)) + "" + - reason + reasonCode + + reason + ""; return itemTotalAllowanceChargeStr; } @@ -744,12 +744,12 @@ public class ZUGFeRD2PullProvider implements IXMLProvider { "true" + "" + "" + currencyFormat(charge.getTotalAmount(calc)) + ""; - if (charge.getReason() != null) { - xml += "" + XMLTools.encodeXML(charge.getReason()) + ""; - } if (charge.getReasonCode() != null) { xml += "" + charge.getReasonCode() + ""; } + if (charge.getReason() != null) { + xml += "" + XMLTools.encodeXML(charge.getReason()) + ""; + } xml += "" + "VAT" + "" + charge.getCategoryCode() + ""; diff --git a/library/src/test/java/org/mustangproject/ZUGFeRD/ZF2PushTest.java b/library/src/test/java/org/mustangproject/ZUGFeRD/ZF2PushTest.java index 56a64b16..412d334a 100644 --- a/library/src/test/java/org/mustangproject/ZUGFeRD/ZF2PushTest.java +++ b/library/src/test/java/org/mustangproject/ZUGFeRD/ZF2PushTest.java @@ -259,9 +259,10 @@ public class ZF2PushTest extends TestCase { .setRecipient(new TradeParty("Franz Müller", "teststr.12", "55232", "Entenhausen", "DE") .setContact(new Contact("contact testname", "123456", "contact.testemail@example.org").setFax("0911623562"))) .setNumber(number) + .addCharge(new Charge(new BigDecimal(1)).setReasonCode("ABK").setReason("AReason").setTaxPercent(new BigDecimal(19))) .addItem(new Item(new Product("Testprodukt", "", "C62", new BigDecimal(19)), amount, new BigDecimal(1.0)).addAllowance(new Allowance(new BigDecimal("0.1")))) .addItem(new Item(new Product("Testprodukt", "", "C62", new BigDecimal(19)), amount, new BigDecimal(1.0)).addAllowance(new Allowance().setPercent(new BigDecimal(50)))) - .addItem(new Item(new Product("Testprodukt", "", "C62", new BigDecimal(19)), amount, new BigDecimal(2.0)).addCharge(new Charge(new BigDecimal(1)).setReasonCode("ABK"))) + .addItem(new Item(new Product("Testprodukt", "", "C62", new BigDecimal(19)), amount, new BigDecimal(2.0)).addCharge(new Charge(new BigDecimal(1)).setReasonCode("ABK").setReason("AnotherReason"))) .addItem(new Item(new Product("Testprodukt", "", "C62", new BigDecimal(19)), amount, new BigDecimal(1.0)).addCharge(new Charge(new BigDecimal(1))).addAllowance(new Allowance(new BigDecimal("1")))) ); @@ -280,7 +281,7 @@ public class ZF2PushTest extends TestCase { assertTrue(zi.getUTF8().contains("ABK")); // Reading ZUGFeRD - assertEquals("18.33", zi.getAmount()); + assertEquals("19.52", zi.getAmount()); assertEquals(orgname, zi.getHolder()); assertEquals(number, zi.getForeignReference()); try { From 4d8494c84fa42d026e7ad8f76e99f499746cb896 Mon Sep 17 00:00:00 2001 From: langfr Date: Fri, 17 Jan 2025 19:38:48 +0000 Subject: [PATCH 10/22] Fix current check failures. --- .../ZUGFeRD/DeSerializationTest.java | 35 ++++++++++++------- .../ZUGFeRD/ZF2ZInvoiceImporterTest.java | 4 +-- 2 files changed, 24 insertions(+), 15 deletions(-) diff --git a/library/src/test/java/org/mustangproject/ZUGFeRD/DeSerializationTest.java b/library/src/test/java/org/mustangproject/ZUGFeRD/DeSerializationTest.java index 24f4044a..2b18655f 100644 --- a/library/src/test/java/org/mustangproject/ZUGFeRD/DeSerializationTest.java +++ b/library/src/test/java/org/mustangproject/ZUGFeRD/DeSerializationTest.java @@ -21,26 +21,35 @@ */ package org.mustangproject.ZUGFeRD; -import com.fasterxml.jackson.annotation.JsonInclude; -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.databind.ObjectMapper; -import junit.framework.TestCase; -import org.junit.Assert; -import org.junit.FixMethodOrder; -import org.junit.runners.MethodSorters; -import org.mustangproject.*; -import org.mustangproject.ZUGFeRD.model.EventTimeCodeTypeConstants; - -import javax.xml.xpath.XPathExpressionException; import java.io.File; import java.io.IOException; -import java.io.InputStream; import java.math.BigDecimal; import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.Date; +import java.util.TimeZone; + +import javax.xml.xpath.XPathExpressionException; + +import org.junit.FixMethodOrder; +import org.junit.runners.MethodSorters; +import org.mustangproject.Allowance; +import org.mustangproject.BankDetails; +import org.mustangproject.CalculatedInvoice; +import org.mustangproject.CashDiscount; +import org.mustangproject.Charge; +import org.mustangproject.Contact; +import org.mustangproject.Invoice; +import org.mustangproject.Item; +import org.mustangproject.Product; +import org.mustangproject.SchemedID; +import org.mustangproject.TradeParty; +import org.mustangproject.ZUGFeRD.model.EventTimeCodeTypeConstants; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; @FixMethodOrder(MethodSorters.NAME_ASCENDING) public class DeSerializationTest extends ResourceCase { @@ -223,7 +232,7 @@ public class DeSerializationTest extends ResourceCase { "\n" + " \"number\": \"471102\",\n" + " \"currency\": \"EUR\",\n" + - " \"issueDate\": \"2018-03-04T00:00:00.000+01:00\",\n" + + " \"issueDate\": \"2018-03-04T00:00:00.000\",\n" + " \"dueDate\": \"2018-03-04T00:00:00.000+01:00\",\n" + " \"deliveryDate\": \"2018-03-04T00:00:00.000+01:00\",\n" + " \"sender\": {\n" + diff --git a/library/src/test/java/org/mustangproject/ZUGFeRD/ZF2ZInvoiceImporterTest.java b/library/src/test/java/org/mustangproject/ZUGFeRD/ZF2ZInvoiceImporterTest.java index 02985e61..15e65646 100644 --- a/library/src/test/java/org/mustangproject/ZUGFeRD/ZF2ZInvoiceImporterTest.java +++ b/library/src/test/java/org/mustangproject/ZUGFeRD/ZF2ZInvoiceImporterTest.java @@ -245,7 +245,7 @@ public class ZF2ZInvoiceImporterTest extends ResourceCase { } assertFalse(hasExceptions); TransactionCalculator tc = new TransactionCalculator(invoice); - assertEquals(new BigDecimal("18.33"), tc.getGrandTotal()); + assertEquals(new BigDecimal("19.52"), tc.getGrandTotal()); } public void testBasisQuantityImport() { @@ -357,7 +357,7 @@ public class ZF2ZInvoiceImporterTest extends ResourceCase { ZUGFeRDInvoiceImporter zii = new ZUGFeRDInvoiceImporter(new FileInputStream(CIIinputFile)); Invoice i = zii.extractInvoice(); - assertEquals("DE21860000000086001055", i.getSender().getBankDetails().get(0).getIBAN()); + assertEquals("DE21860000000086001055", i.getRecipient().getBankDetails().get(0).getIBAN()); ObjectMapper mapper = new ObjectMapper(); String jsonArray = mapper.writeValueAsString(i); From deea7c03cd2ae59d0f5c4d590fb3352ec132b729 Mon Sep 17 00:00:00 2001 From: jstaerk Date: Sat, 18 Jan 2025 09:37:32 +0100 Subject: [PATCH 11/22] improve cashdiscount --- History.md | 1 + library/src/main/java/org/mustangproject/CashDiscount.java | 3 +++ 2 files changed, 4 insertions(+) diff --git a/History.md b/History.md index e9e401b1..c122d89b 100644 --- a/History.md +++ b/History.md @@ -4,6 +4,7 @@ - #689 - be able to set detailedDeliveryPeriodFrom, detailedDeliveryPeriodTo MS188 - updated verapdf from 1.26.1 to 1.26.2 +- cashDiscount JSON now corrently ignores values for cii and xr methods 2.16.0 ======= diff --git a/library/src/main/java/org/mustangproject/CashDiscount.java b/library/src/main/java/org/mustangproject/CashDiscount.java index 2151489a..b1993a02 100644 --- a/library/src/main/java/org/mustangproject/CashDiscount.java +++ b/library/src/main/java/org/mustangproject/CashDiscount.java @@ -1,5 +1,6 @@ package org.mustangproject; +import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonInclude; import org.mustangproject.ZUGFeRD.IZUGFeRDCashDiscount; @@ -63,6 +64,7 @@ public class CashDiscount implements IZUGFeRDCashDiscount { /*** * @return this particular cash discount as cross industry invoice XML */ + @JsonIgnore public String getAsCII() { return ""+ "Cash Discount"+ @@ -78,6 +80,7 @@ public class CashDiscount implements IZUGFeRDCashDiscount { * XRechnung CIUS defined it's own proprietary format for a freetext field * @return this particular cash discount in proprietary xrechnung format */ + @JsonIgnore public String getAsXRechnung() { return "#SKONTO#TAGE="+days+"#PROZENT="+XMLTools.nDigitFormat(percent,2)+"#\n"; } From f5a4cb3a2c7fd53671a503ca845d942db39c41ba Mon Sep 17 00:00:00 2001 From: jstaerk Date: Mon, 20 Jan 2025 13:51:52 +0100 Subject: [PATCH 12/22] corrected tests --- .../ZUGFeRD/ZF2ZInvoiceImporterTest.java | 44 +++++++++---------- 1 file changed, 22 insertions(+), 22 deletions(-) diff --git a/library/src/test/java/org/mustangproject/ZUGFeRD/ZF2ZInvoiceImporterTest.java b/library/src/test/java/org/mustangproject/ZUGFeRD/ZF2ZInvoiceImporterTest.java index 02985e61..6b50de4e 100644 --- a/library/src/test/java/org/mustangproject/ZUGFeRD/ZF2ZInvoiceImporterTest.java +++ b/library/src/test/java/org/mustangproject/ZUGFeRD/ZF2ZInvoiceImporterTest.java @@ -125,8 +125,8 @@ public class ZF2ZInvoiceImporterTest extends ResourceCase { // Reading ZUGFeRD assertEquals("Bei Spiel GmbH", invoice.getOwnOrganisationName()); assertEquals(3, invoice.getZFItems().length); - assertEquals(invoice.getZFItems()[0].getNotesWithSubjectCode().get(0).getContent(),"Something"); - assertEquals(invoice.getZFItems()[0].getNotesWithSubjectCode().size(),1); + assertEquals(invoice.getZFItems()[0].getNotesWithSubjectCode().get(0).getContent(), "Something"); + assertEquals(invoice.getZFItems()[0].getNotesWithSubjectCode().size(), 1); assertEquals("400", invoice.getZFItems()[1].getQuantity().toString()); assertEquals("Zahlbar ohne Abzug bis zum 30.05.2017", invoice.getPaymentTermDescription()); assertEquals("AB321", invoice.getReferenceNumber()); @@ -245,7 +245,7 @@ public class ZF2ZInvoiceImporterTest extends ResourceCase { } assertFalse(hasExceptions); TransactionCalculator tc = new TransactionCalculator(invoice); - assertEquals(new BigDecimal("18.33"), tc.getGrandTotal()); + assertEquals(new BigDecimal("19.52"), tc.getGrandTotal()); } public void testBasisQuantityImport() { @@ -264,6 +264,7 @@ public class ZF2ZInvoiceImporterTest extends ResourceCase { assertEquals(new BigDecimal("337.60"), tc.getGrandTotal()); } + public void testAllowancesChargesImport() { ZUGFeRDInvoiceImporter zii = new ZUGFeRDInvoiceImporter("./target/testout-ZF2PushChargesAllowances.pdf"); @@ -286,10 +287,10 @@ public class ZF2ZInvoiceImporterTest extends ResourceCase { ZUGFeRDImporter zii = new ZUGFeRDImporter(); - int version=-1; + int version = -1; try { zii.fromXML(new String(Files.readAllBytes(Paths.get("./target/testout-XR-Edge.xml")), StandardCharsets.UTF_8)); - version=zii.getVersion(); + version = zii.getVersion(); } catch (IOException e) { hasExceptions = true; } catch (Exception e) { @@ -305,13 +306,12 @@ public class ZF2ZInvoiceImporterTest extends ResourceCase { assertFalse(hasExceptions); - TransactionCalculator tc = new TransactionCalculator(invoice); assertEquals(new BigDecimal("1.00"), tc.getGrandTotal()); - assertEquals(version,2); + assertEquals(version, 2); assertTrue(new BigDecimal("1").compareTo(invoice.getZFItems()[0].getQuantity()) == 0); - LineCalculator lc=new LineCalculator(invoice.getZFItems()[0]); + LineCalculator lc = new LineCalculator(invoice.getZFItems()[0]); assertTrue(new BigDecimal("1").compareTo(lc.getItemTotalNetAmount()) == 0); assertTrue(invoice.getTradeSettlement().length == 1); @@ -357,7 +357,7 @@ public class ZF2ZInvoiceImporterTest extends ResourceCase { ZUGFeRDInvoiceImporter zii = new ZUGFeRDInvoiceImporter(new FileInputStream(CIIinputFile)); Invoice i = zii.extractInvoice(); - assertEquals("DE21860000000086001055", i.getSender().getBankDetails().get(0).getIBAN()); + assertEquals("DE21860000000086001055", i.getRecipient().getBankDetails().get(0).getIBAN()); ObjectMapper mapper = new ObjectMapper(); String jsonArray = mapper.writeValueAsString(i); @@ -430,15 +430,15 @@ public class ZF2ZInvoiceImporterTest extends ResourceCase { CalculatedInvoice invoice = new CalculatedInvoice(); importer.extractInto(invoice); - boolean isBD=invoice.getTotalPrepaidAmount() instanceof BigDecimal; + boolean isBD = invoice.getTotalPrepaidAmount() instanceof BigDecimal; assertTrue(isBD); - BigDecimal expectedPrepaid=new BigDecimal(50); - BigDecimal expectedLineTotal=new BigDecimal("180.76"); - BigDecimal expectedDue=new BigDecimal("147.65"); + BigDecimal expectedPrepaid = new BigDecimal(50); + BigDecimal expectedLineTotal = new BigDecimal("180.76"); + BigDecimal expectedDue = new BigDecimal("147.65"); if (isBD) { - BigDecimal amread=invoice.getTotalPrepaidAmount(); - BigDecimal importedLineTotal=invoice.getLineTotalAmount(); - BigDecimal importedDuePayable=invoice.getDuePayable(); + BigDecimal amread = invoice.getTotalPrepaidAmount(); + BigDecimal importedLineTotal = invoice.getLineTotalAmount(); + BigDecimal importedDuePayable = invoice.getDuePayable(); assertTrue(amread.compareTo(expectedPrepaid) == 0); assertTrue(importedLineTotal.compareTo(expectedLineTotal) == 0); assertTrue(importedDuePayable.compareTo(expectedDue) == 0); @@ -475,19 +475,19 @@ public class ZF2ZInvoiceImporterTest extends ResourceCase { ZUGFeRDInvoiceImporter importer = new ZUGFeRDInvoiceImporter(new FileInputStream(inputFile)); Invoice invoice = importer.extractInvoice(); - assertEquals(1,invoice.getRecipient().getBankDetails().size()); + assertEquals(1, invoice.getRecipient().getBankDetails().size()); // IBAN belongs to recipient in invoice with sepa debit - assertEquals("DE21860000000086001055",invoice.getRecipient().getBankDetails().get(0).getIBAN()); - assertEquals(0,invoice.getSender().getBankDetails().size()); + assertEquals("DE21860000000086001055", invoice.getRecipient().getBankDetails().get(0).getIBAN()); + assertEquals(0, invoice.getSender().getBankDetails().size()); inputFile = getResourceAsFile("factur-x.xml"); importer = new ZUGFeRDInvoiceImporter(new FileInputStream(inputFile)); invoice = importer.extractInvoice(); - assertEquals(1,invoice.getSender().getBankDetails().size()); + assertEquals(1, invoice.getSender().getBankDetails().size()); // IBAN belongs to sender in normal invoice - assertEquals("DE88200800000970375700",invoice.getSender().getBankDetails().get(0).getIBAN()); - assertEquals(0,invoice.getRecipient().getBankDetails().size()); + assertEquals("DE88200800000970375700", invoice.getSender().getBankDetails().get(0).getIBAN()); + assertEquals(0, invoice.getRecipient().getBankDetails().size()); } From ef97effec065739018fad7194cb320bdf65b88a0 Mon Sep 17 00:00:00 2001 From: jstaerk Date: Tue, 21 Jan 2025 13:08:02 +0100 Subject: [PATCH 13/22] updated history --- History.md | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/History.md b/History.md index c122d89b..c3afba03 100644 --- a/History.md +++ b/History.md @@ -1,7 +1,10 @@ -- #678 -- #679 -- #681 -- #689 +2.16.1 +======= +2025-01-21 +- #678 some ubl creditnote attributes are not parsed +- #679 validation of a XR does not ignore whitespace +- #681 IBAN assigned to invoice sender not recipient on direct debit +- #689 incorrect element order when both charge reason and reasoncode are specified - be able to set detailedDeliveryPeriodFrom, detailedDeliveryPeriodTo MS188 - updated verapdf from 1.26.1 to 1.26.2 - cashDiscount JSON now corrently ignores values for cii and xr methods From e4915c578cb72c7e88b2336e50a38cbaeb290def Mon Sep 17 00:00:00 2001 From: jstaerk Date: Tue, 21 Jan 2025 13:12:41 +0100 Subject: [PATCH 14/22] [maven-release-plugin] prepare release core-2.16.1 --- Mustang-CLI/pom.xml | 6 +++--- library/pom.xml | 6 +++--- pom.xml | 4 ++-- validator/pom.xml | 6 +++--- 4 files changed, 11 insertions(+), 11 deletions(-) diff --git a/Mustang-CLI/pom.xml b/Mustang-CLI/pom.xml index 9d83688f..0504de84 100644 --- a/Mustang-CLI/pom.xml +++ b/Mustang-CLI/pom.xml @@ -3,7 +3,7 @@ org.mustangproject core - 2.16.1-SNAPSHOT + 2.16.1 4.0.0 org.mustangproject @@ -12,7 +12,7 @@ should also work for XRechnung/CII. jar - 2.16.1-SNAPSHOT + 2.16.1 UTF-8 11 @@ -23,7 +23,7 @@ org.mustangproject validator - 2.16.1-SNAPSHOT + 2.16.1 diff --git a/library/pom.xml b/library/pom.xml index 81a1716f..33154746 100644 --- a/library/pom.xml +++ b/library/pom.xml @@ -3,13 +3,13 @@ org.mustangproject core - 2.16.1-SNAPSHOT + 2.16.1 4.0.0 org.mustangproject library - 2.16.1-SNAPSHOT + 2.16.1 jar Library to write, read and validate e-invoices (Factur-X, ZUGFeRD, Order-X, XRechnung/CII) FOSS Java library to read, write and validate european electronic invoices and orders in the UN/CEFACT @@ -20,7 +20,7 @@ scm:git:https://github.com/ZUGFeRD/mustangproject.git scm:git:https://github.com/ZUGFeRD/mustangproject.git https://github.com/ZUGFeRD/mustangproject - core-2.3.2 + core-2.16.1 diff --git a/pom.xml b/pom.xml index 03f326a5..9d089f57 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ 4.0.0 org.mustangproject core - 2.16.1-SNAPSHOT pom + 2.16.1 pom Mustang @@ -19,7 +19,7 @@ scm:git:git://github.com/dexecutor/dependent-tasks-executor.git scm:git:git@github.com:dexecutor/dexecutor.git https://github.com/dexecutor/dependent-tasks-executor - core-2.3.2 + core-2.16.1 diff --git a/validator/pom.xml b/validator/pom.xml index 6ceb0dd3..16498b6e 100644 --- a/validator/pom.xml +++ b/validator/pom.xml @@ -3,7 +3,7 @@ org.mustangproject core - 2.16.1-SNAPSHOT + 2.16.1 4.0.0 org.mustangproject @@ -11,7 +11,7 @@ Library to validate e-invoices (ZUGFeRD, Factur-X and Xrechnung) jar - 2.16.1-SNAPSHOT + 2.16.1 @@ -38,7 +38,7 @@ ${project.groupId} library - 2.16.1-SNAPSHOT + 2.16.1 org.dom4j From 331c05857b65b26efab44fb26afd2b7fdcd6a2e9 Mon Sep 17 00:00:00 2001 From: jstaerk Date: Tue, 21 Jan 2025 13:12:43 +0100 Subject: [PATCH 15/22] [maven-release-plugin] prepare for next development iteration --- Mustang-CLI/pom.xml | 6 +++--- library/pom.xml | 6 +++--- pom.xml | 4 ++-- validator/pom.xml | 6 +++--- 4 files changed, 11 insertions(+), 11 deletions(-) diff --git a/Mustang-CLI/pom.xml b/Mustang-CLI/pom.xml index 0504de84..d5b924e9 100644 --- a/Mustang-CLI/pom.xml +++ b/Mustang-CLI/pom.xml @@ -3,7 +3,7 @@ org.mustangproject core - 2.16.1 + 2.16.2-SNAPSHOT 4.0.0 org.mustangproject @@ -12,7 +12,7 @@ should also work for XRechnung/CII. jar - 2.16.1 + 2.16.2-SNAPSHOT UTF-8 11 @@ -23,7 +23,7 @@ org.mustangproject validator - 2.16.1 + 2.16.2-SNAPSHOT diff --git a/library/pom.xml b/library/pom.xml index 33154746..6045292a 100644 --- a/library/pom.xml +++ b/library/pom.xml @@ -3,13 +3,13 @@ org.mustangproject core - 2.16.1 + 2.16.2-SNAPSHOT 4.0.0 org.mustangproject library - 2.16.1 + 2.16.2-SNAPSHOT jar Library to write, read and validate e-invoices (Factur-X, ZUGFeRD, Order-X, XRechnung/CII) FOSS Java library to read, write and validate european electronic invoices and orders in the UN/CEFACT @@ -20,7 +20,7 @@ scm:git:https://github.com/ZUGFeRD/mustangproject.git scm:git:https://github.com/ZUGFeRD/mustangproject.git https://github.com/ZUGFeRD/mustangproject - core-2.16.1 + core-2.3.2 diff --git a/pom.xml b/pom.xml index 9d089f57..7346b012 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ 4.0.0 org.mustangproject core - 2.16.1 pom + 2.16.2-SNAPSHOT pom Mustang @@ -19,7 +19,7 @@ scm:git:git://github.com/dexecutor/dependent-tasks-executor.git scm:git:git@github.com:dexecutor/dexecutor.git https://github.com/dexecutor/dependent-tasks-executor - core-2.16.1 + core-2.3.2 diff --git a/validator/pom.xml b/validator/pom.xml index 16498b6e..6c94fd9e 100644 --- a/validator/pom.xml +++ b/validator/pom.xml @@ -3,7 +3,7 @@ org.mustangproject core - 2.16.1 + 2.16.2-SNAPSHOT 4.0.0 org.mustangproject @@ -11,7 +11,7 @@ Library to validate e-invoices (ZUGFeRD, Factur-X and Xrechnung) jar - 2.16.1 + 2.16.2-SNAPSHOT @@ -38,7 +38,7 @@ ${project.groupId} library - 2.16.1 + 2.16.2-SNAPSHOT org.dom4j From 1f1c0fc52a30773fbf95229ec5233f4456789146 Mon Sep 17 00:00:00 2001 From: Liam Costello Date: Wed, 22 Jan 2025 14:54:46 +0100 Subject: [PATCH 16/22] Ensure Base64 decoding can handle newlines when decoding a FileAttachment --- .../java/org/mustangproject/ZUGFeRD/ZUGFeRDInvoiceImporter.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/src/main/java/org/mustangproject/ZUGFeRD/ZUGFeRDInvoiceImporter.java b/library/src/main/java/org/mustangproject/ZUGFeRD/ZUGFeRDInvoiceImporter.java index 9115feb4..7a17bd11 100644 --- a/library/src/main/java/org/mustangproject/ZUGFeRD/ZUGFeRDInvoiceImporter.java +++ b/library/src/main/java/org/mustangproject/ZUGFeRD/ZUGFeRDInvoiceImporter.java @@ -890,7 +890,7 @@ public class ZUGFeRDInvoiceImporter { xpr = xpath.compile("//*[local-name()=\"AttachmentBinaryObject\"]|//*[local-name()=\"EmbeddedDocumentBinaryObject\"]"); NodeList attachmentNodes = (NodeList) xpr.evaluate(getDocument(), XPathConstants.NODESET); for (int i = 0; i < attachmentNodes.getLength(); i++) { - FileAttachment fa = new FileAttachment(attachmentNodes.item(i).getAttributes().getNamedItem("filename").getNodeValue(), attachmentNodes.item(i).getAttributes().getNamedItem("mimeCode").getNodeValue(), "Data", Base64.getDecoder().decode(XMLTools.trimOrNull(attachmentNodes.item(i)))); + FileAttachment fa = new FileAttachment(attachmentNodes.item(i).getAttributes().getNamedItem("filename").getNodeValue(), attachmentNodes.item(i).getAttributes().getNamedItem("mimeCode").getNodeValue(), "Data", Base64.getMimeDecoder().decode(XMLTools.trimOrNull(attachmentNodes.item(i)))); zpp.embedFileInXML(fa); // filename = "Aufmass.png" mimeCode = "image/png" //EmbeddedDocumentBinaryObject cbc:EmbeddedDocumentBinaryObject mimeCode="image/png" filename="Aufmass.png" From 354756ed2466e94baaf7f8778f228ff584f25cff Mon Sep 17 00:00:00 2001 From: langfr Date: Wed, 22 Jan 2025 21:19:24 +0000 Subject: [PATCH 17/22] Fill TaxExemptionReason during InvoiceImport. --- library/src/main/java/org/mustangproject/Item.java | 3 +++ library/src/test/java/org/mustangproject/ZUGFeRD/XRTest.java | 2 +- .../org/mustangproject/ZUGFeRD/ZF2ZInvoiceImporterTest.java | 3 +++ 3 files changed, 7 insertions(+), 1 deletion(-) diff --git a/library/src/main/java/org/mustangproject/Item.java b/library/src/main/java/org/mustangproject/Item.java index 8fefa87f..fc851930 100644 --- a/library/src/main/java/org/mustangproject/Item.java +++ b/library/src/main/java/org/mustangproject/Item.java @@ -172,6 +172,9 @@ public class Item implements IZUGFeRDExportableItem { icnm.getAsNodeMap("ApplicableTradeTax") .flatMap(cnm -> cnm.getAsBigDecimal("RateApplicablePercent", "ApplicablePercent")) .ifPresent(product::setVATPercent); + icnm.getAsNodeMap("ApplicableTradeTax") + .flatMap(cnm -> cnm.getAsString("ExemptionReason")) + .ifPresent(product::setTaxExemptionReason); icnm.getAsNodeMap("SpecifiedTradeAllowanceCharge").ifPresent(stac -> { stac.getAsNodeMap("ChargeIndicator").ifPresent(ci -> { String isChargeString=ci.getAsString("Indicator").get(); diff --git a/library/src/test/java/org/mustangproject/ZUGFeRD/XRTest.java b/library/src/test/java/org/mustangproject/ZUGFeRD/XRTest.java index 3efa4ccf..e860c9a2 100644 --- a/library/src/test/java/org/mustangproject/ZUGFeRD/XRTest.java +++ b/library/src/test/java/org/mustangproject/ZUGFeRD/XRTest.java @@ -94,7 +94,7 @@ public class XRTest extends TestCase { .addCashDiscount(new CashDiscount(new BigDecimal(3), 14)) .setReferenceNumber("991-01484-64")//leitweg-id // not using any VAT, this is also a test of zero-rated goods: - .setNumber(number).addItem(new Item(new Product("Testprodukt", "", "C62", BigDecimal.ZERO), amount, new BigDecimal(1.0))) + .setNumber(number).addItem(new Item(new Product("Testprodukt", "", "C62", BigDecimal.ZERO).setTaxExemptionReason("Kleinunternehmer"), amount, new BigDecimal(1.0))) .setPayee( new TradeParty().setName("VR Factoring GmbH").setID("DE813838785").setLegalOrganisation(new LegalOrganisation("391200LDDFJDMIPPMZ54", "0199"))) .embedFileInXML(fe1); diff --git a/library/src/test/java/org/mustangproject/ZUGFeRD/ZF2ZInvoiceImporterTest.java b/library/src/test/java/org/mustangproject/ZUGFeRD/ZF2ZInvoiceImporterTest.java index 6b50de4e..c3020e92 100644 --- a/library/src/test/java/org/mustangproject/ZUGFeRD/ZF2ZInvoiceImporterTest.java +++ b/library/src/test/java/org/mustangproject/ZUGFeRD/ZF2ZInvoiceImporterTest.java @@ -314,6 +314,9 @@ public class ZF2ZInvoiceImporterTest extends ResourceCase { LineCalculator lc = new LineCalculator(invoice.getZFItems()[0]); assertTrue(new BigDecimal("1").compareTo(lc.getItemTotalNetAmount()) == 0); + assertEquals("Z", invoice.getZFItems()[0].getProduct().getTaxCategoryCode()); + assertEquals("Kleinunternehmer", invoice.getZFItems()[0].getProduct().getTaxExemptionReason()); + assertTrue(invoice.getTradeSettlement().length == 1); assertTrue(invoice.getTradeSettlement()[0] instanceof IZUGFeRDTradeSettlementPayment); IZUGFeRDTradeSettlementPayment paym = (IZUGFeRDTradeSettlementPayment) invoice.getTradeSettlement()[0]; From aff57e5a511f6503c6d07ce6f1bd963db7074820 Mon Sep 17 00:00:00 2001 From: jstaerk Date: Fri, 24 Jan 2025 12:29:55 +0100 Subject: [PATCH 18/22] closes #705 --- History.md | 2 + .../ZUGFeRD/ZUGFeRDInvoiceImporter.java | 47 +- .../ZUGFeRD/ZF2ZInvoiceImporterTest.java | 18 + .../resources/cii/extended_warenrechnung.xml | 567 ++++++++++++++++++ 4 files changed, 626 insertions(+), 8 deletions(-) create mode 100644 library/src/test/resources/cii/extended_warenrechnung.xml diff --git a/History.md b/History.md index c3afba03..be39ae49 100644 --- a/History.md +++ b/History.md @@ -1,3 +1,5 @@ +#705 + 2.16.1 ======= 2025-01-21 diff --git a/library/src/main/java/org/mustangproject/ZUGFeRD/ZUGFeRDInvoiceImporter.java b/library/src/main/java/org/mustangproject/ZUGFeRD/ZUGFeRDInvoiceImporter.java index 9115feb4..a1cd2d8d 100644 --- a/library/src/main/java/org/mustangproject/ZUGFeRD/ZUGFeRDInvoiceImporter.java +++ b/library/src/main/java/org/mustangproject/ZUGFeRD/ZUGFeRDInvoiceImporter.java @@ -556,13 +556,13 @@ public class ZUGFeRDInvoiceImporter { } zpp.addNotes(includedNotes); String rootNode = extractString("local-name(/*)"); - if (rootNode.equals("Invoice")||rootNode.equals("CreditNote")) { + if (rootNode.equals("Invoice") || rootNode.equals("CreditNote")) { // UBL... // //*[local-name()="Invoice" or local-name()="CreditNote"] number = extractString("/*[local-name()=\"Invoice\" or local-name()=\"CreditNote\"]/*[local-name()=\"ID\"]").trim(); typeCode = extractString("/*[local-name()=\"Invoice\" or local-name()=\"CreditNote\"]/*[local-name()=\"InvoiceTypeCode\"]").trim(); String issueDateStr = extractString("/*[local-name()=\"Invoice\" or local-name()=\"CreditNote\"]/*[local-name()=\"IssueDate\"]").trim(); - if (issueDateStr.length()>0) { + if (issueDateStr.length() > 0) { issueDate = new SimpleDateFormat("yyyy-MM-dd").parse(issueDateStr); } String dueDt = extractString("/*[local-name()=\"Invoice\" or local-name()=\"CreditNote\"]/*[local-name()=\"DueDate\"]").trim(); @@ -651,7 +651,7 @@ public class ZUGFeRDInvoiceImporter { zpp.setCurrency(currency); String paymentTermsDescription = extractString("//*[local-name()=\"SpecifiedTradePaymentTerms\"]/*[local-name()=\"Description\"]|//*[local-name()=\"PaymentTerms\"]/*[local-name()=\"Note\"]"); - if ((paymentTermsDescription!=null)&&(!paymentTermsDescription.isEmpty())) { + if ((paymentTermsDescription != null) && (!paymentTermsDescription.isEmpty())) { zpp.setPaymentTermDescription(paymentTermsDescription); } @@ -848,15 +848,14 @@ public class ZUGFeRDInvoiceImporter { Node currentItemNode = nodes.item(i); ReferencedDocument doc = ReferencedDocument.fromNode(currentItemNode); - if (doc != null - && (!Objects.equals(zpp.getInvoiceReferencedDocumentID(), doc.getIssuerAssignedID()) - || !Objects.equals(zpp.getInvoiceReferencedIssueDate(), doc.getFormattedIssueDateTime()))) - { + if (doc != null + && (!Objects.equals(zpp.getInvoiceReferencedDocumentID(), doc.getIssuerAssignedID()) + || !Objects.equals(zpp.getInvoiceReferencedIssueDate(), doc.getFormattedIssueDateTime()))) { zpp.addInvoiceReferencedDocument(doc); } } } - + zpp.setOwnOrganisationName(extractString("//*[local-name()=\"SellerTradeParty\"]/*[local-name()=\"Name\"]|//*[local-name()=\"AccountingSupplierParty\"]/*[local-name()=\"Party\"]/*[local-name()=\"PartyName\"]").trim()); String rounding = extractString("//*[local-name()=\"SpecifiedTradeSettlementHeaderMonetarySummation\"]/*[local-name()=\"RoundingAmount\"]|//*[local-name()=\"LegalMonetaryTotal\"]/*[local-name()=\"Party\"]/*[local-name()=\"PayableRoundingAmount\"]"); @@ -976,6 +975,38 @@ public class ZUGFeRDInvoiceImporter { } } + xpr = xpath.compile("//*[local-name()=\"ApplicableHeaderTradeSettlement\"]/*[local-name()=\"SpecifiedLogisticsServiceCharge\"]");// UBL unknown + chargeNodes = (NodeList) xpr.evaluate(getDocument(), XPathConstants.NODESET); + for (int i = 0; i < chargeNodes.getLength(); i++) { + NodeList chargeNodeChilds = chargeNodes.item(i).getChildNodes(); + String chargeAmount = null; + String taxPercent = null; + for (int chargeChildIndex = 0; chargeChildIndex < chargeNodeChilds.getLength(); chargeChildIndex++) { + String chargeChildName = chargeNodeChilds.item(chargeChildIndex).getLocalName(); + if (chargeChildName != null) { + if (chargeChildName.equals("AppliedAmount")) { + chargeAmount = XMLTools.trimOrNull(chargeNodeChilds.item(chargeChildIndex)); + } else if (chargeChildName.equals("AppliedTradeTax")) { + NodeList taxChilds = chargeNodeChilds.item(chargeChildIndex).getChildNodes(); + for (int taxChildIndex = 0; taxChildIndex < taxChilds.getLength(); taxChildIndex++) { + String taxItemName = taxChilds.item(taxChildIndex).getLocalName(); + if ((taxItemName != null) && (taxItemName.equals("RateApplicablePercent"))) { + taxPercent = XMLTools.trimOrNull(taxChilds.item(taxChildIndex)); + } + } + } + } + //appliedAmount + //AppliedTradeTax + } + if (chargeAmount != null) { + Charge c = new Charge(new BigDecimal(chargeAmount)); + if (taxPercent != null) { + c.setTaxPercent(new BigDecimal(taxPercent)); + } + zpp.addCharge(c); + } + } TransactionCalculator tc = new TransactionCalculator(zpp); diff --git a/library/src/test/java/org/mustangproject/ZUGFeRD/ZF2ZInvoiceImporterTest.java b/library/src/test/java/org/mustangproject/ZUGFeRD/ZF2ZInvoiceImporterTest.java index 6b50de4e..1e918395 100644 --- a/library/src/test/java/org/mustangproject/ZUGFeRD/ZF2ZInvoiceImporterTest.java +++ b/library/src/test/java/org/mustangproject/ZUGFeRD/ZF2ZInvoiceImporterTest.java @@ -232,6 +232,24 @@ public class ZF2ZInvoiceImporterTest extends ResourceCase { } + public void testSpecifiedLogisticsChargeImport() { + ZUGFeRDInvoiceImporter zii = new ZUGFeRDInvoiceImporter(); + File expectedResult = getResourceAsFile("cii/extended_warenrechnung.xml"); + + + boolean hasExceptions = false; + CalculatedInvoice invoice = new CalculatedInvoice(); + try { + zii.setInputStream(new FileInputStream(expectedResult)); + zii.extractInto(invoice); + } catch (XPathExpressionException | ParseException | FileNotFoundException e) { + hasExceptions = true; + } + assertFalse(hasExceptions); + TransactionCalculator tc = new TransactionCalculator(invoice); + assertEquals(new BigDecimal("518.99"), tc.getGrandTotal()); + + } public void testItemAllowancesChargesImport() { ZUGFeRDInvoiceImporter zii = new ZUGFeRDInvoiceImporter("./target/testout-ZF2PushItemChargesAllowances.pdf"); diff --git a/library/src/test/resources/cii/extended_warenrechnung.xml b/library/src/test/resources/cii/extended_warenrechnung.xml new file mode 100644 index 00000000..46665e1a --- /dev/null +++ b/library/src/test/resources/cii/extended_warenrechnung.xml @@ -0,0 +1,567 @@ + + + + + + + + + + true + + + urn:cen.eu:en16931:2017#conformant#urn:factur-x.eu:1p0:extended + + + + R87654321012345 + WARENRECHNUNG + 380 + + 20180806 + + + ST3 + Es bestehen Rabatt- oder Bonusvereinbarungen. + AAK + + + EEV + Der Verkäufer bleibt Eigentümer der Waren bis zu vollständigen Erfüllung der Kaufpreisforderung. + AAJ + + + MUSTERLIEFERANT GMBH +BAHNHOFSTRASSE 99 +99199 MUSTERHAUSEN +Geschäftsführung: +Max Mustermann +USt-IdNr: DE123456789 +Telefon: +49 932 431 0 +www.musterlieferant.de +HRB Nr. 372876 +Amtsgericht Musterstadt +GLN 4304171000002 +WEEE-Reg-Nr.: DE87654321 + + REG + + + Leergutwert: 46,50 + + + Wichtige Information: Bei Bestellungen bis zum 19.12. ist die Auslieferung bis spätestens 23.12. garantiert. + + + + + + 1 + + + 4123456000014 + ZS997 + Zitronensäure 100ml + + Verpackungsart + BO + + + + + 1.0000 + + + 1.0000 + + + + 100.0000 + 4.0000 + + + + VAT + S + 19.00 + + + 100.00 + + + + + + 2 + + + 4123456000021 + GZ250 + Gelierzucker Extra 250g + + + + 1.5000 + + + false + + 0.0300 + Artikelrabatt 1 + + + + false + + 0.0200 + Artikelrabatt 2 + + + + 1.4500 + + + + 50.0000 + 1.0000 + + + + VAT + S + 7.00 + + + 72.50 + + + + + + 3 + + + 4123456000021 + GZ250 + Gelierzucker Extra 250g + Artikel wie vereinbart ohne Berechnung + + + + 0.0000 + + + 0.0000 + + + + 10.0000 + 1.0000 + + + + VAT + S + 7.00 + + + 0.00 + + + + + + 4 + + + 4100130013294 + 2031 + + Bierbrau Pils 20/0500 + EAN-VKE: 4100130913297 + + Verpackung + Kiste + + + + + 12.0000 + + + 12.0000 + + + + 15.0000 + 20.0000 + + + + VAT + S + 19.00 + + + 180.00 + + + + + + 5 + + + 2001015001325 + 1805 + + Leergutpfand 20 x 0,5l + + Verpackung + unverpackt + + + + + 3.1000 + + + 3.1000 + + + + 15.0000 + 1.0000 + + + + VAT + S + 19.00 + + + 46.50 + + + + + + 6 + + + 4123456000038 + MP107 + Mischpalette Joghurt Karton 3 x 20 + + Verpackung + Karton + + + 4123456001035 + JOG103 + Erdbeer 20 x 150g Becher + 20.0000 + + + 4123456002032 + JOG203 + Banane 20 x 150g Becher + 20.0000 + + + 4123456003039 + JOG303 + Schoko 20 x 150g Becher + 20.0000 + + + + + 30.0000 + + + false + + 0.9000 + Artikelrabatt 1 + + + + 29.1000 + + + + 2.0000 + 1.0000 + + + + VAT + S + 7.00 + + + 58.20 + + + + + + 549910 + 4333741000005 + MUSTERLIEFERANT GMBH + + + +49 932 431 500 + + + max.mustermann@musterlieferant.de + + + + 99199 + BAHNHOFSTRASSE 99 + MUSTERHAUSEN + DE + + + DE123456789 + + + + 009420 + 4304171000002 + MUSTER-KUNDE GMBH + + 40235 + KUNDENWEG 88 + DUESSELDORF + DE + + + + B123456789 + + + A456123 + 130 + + + + + 4304171088093 + MUSTER-MARKT + + 8211 + + + 31157 + HAUPTSTRASSE 44 + SARSTEDT + DE + + + + + 20180805 + + + + L87654321012345 + + + + EUR + + 009420 + 4304171000002 + MUSTER-KUNDE GMBH + + 40235 + KUNDENWEG 88 + DUESSELDORF + DE + + + + 61.07 + VAT + 321.40 + 326.50 + -5.10 + S + 19.00 + + + 8.93 + VAT + 127.59 + 130.70 + -3.11 + S + 7.00 + + + + false + + 2.00 + 280.00 + 5.60 + Rechnungsrabatt 1 + + VAT + S + 19.00 + + + + + false + + 2.00 + 130.70 + 2.61 + Rechnungsrabatt 1 + + VAT + S + 7.00 + + + + + false + + 280.00 + 2.50 + Rechnungsrabatt 2 + + VAT + S + 19.00 + + + + + false + + 130.70 + 0.50 + Rechnungsrabatt 2 + + VAT + S + 7.00 + + + + Transportkosten + 3.00 + + VAT + S + 19.00 + + + + Bei Zahlung innerhalb 14 Tagen gewähren wir 2,0% Skonto. + + 14 + 2.00 + + + + 457.20 + 3.00 + 11.21 + 448.99 + 70.00 + 518.99 + 0.00 + 518.99 + + + + \ No newline at end of file From 796f5333acfab07e584577122afab1a75b6c0052 Mon Sep 17 00:00:00 2001 From: jstaerk Date: Sat, 25 Jan 2025 17:33:19 +0100 Subject: [PATCH 19/22] closes #707 --- library/src/main/java/org/mustangproject/Item.java | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/library/src/main/java/org/mustangproject/Item.java b/library/src/main/java/org/mustangproject/Item.java index 8fefa87f..c73a7b7f 100644 --- a/library/src/main/java/org/mustangproject/Item.java +++ b/library/src/main/java/org/mustangproject/Item.java @@ -187,8 +187,12 @@ public class Item implements IZUGFeRDExportableItem { if (amountString!=null) { izac.setTotalAmount(new BigDecimal(amountString)); } - izac.setPercent(new BigDecimal(percentString)); - izac.setReason(reason); + if(percentString!=null) { + izac.setPercent(new BigDecimal(percentString)); + } + if(reason!=null) { + izac.setReason(reason); + } if (isChargeString.equalsIgnoreCase("false")) { addAllowance(izac); From 16c6f22566525f28bb92b91b37fefd07a965ad09 Mon Sep 17 00:00:00 2001 From: jstaerk Date: Mon, 27 Jan 2025 11:54:23 +0100 Subject: [PATCH 20/22] closes #708 --- History.md | 2 ++ .../mustangproject/ZUGFeRD/ZUGFeRDInvoiceImporter.java | 8 +++++++- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/History.md b/History.md index be39ae49..e550bcbc 100644 --- a/History.md +++ b/History.md @@ -1,4 +1,6 @@ #705 +#707 +-#708 2.16.1 ======= diff --git a/library/src/main/java/org/mustangproject/ZUGFeRD/ZUGFeRDInvoiceImporter.java b/library/src/main/java/org/mustangproject/ZUGFeRD/ZUGFeRDInvoiceImporter.java index a1cd2d8d..3f2231d1 100644 --- a/library/src/main/java/org/mustangproject/ZUGFeRD/ZUGFeRDInvoiceImporter.java +++ b/library/src/main/java/org/mustangproject/ZUGFeRD/ZUGFeRDInvoiceImporter.java @@ -1116,11 +1116,17 @@ public class ZUGFeRDInvoiceImporter { * * @return the file attachments embedded in XML (using base64) decoded as byte array, * for PDF embedded files in FX use getFileAttachmentsPDF() + * may return empty array * @deprecated use invoice.getAdditionalReferencedDocuments */ @Deprecated public List getFileAttachmentsXML() { - return new ArrayList<>(Arrays.asList(importedInvoice.getAdditionalReferencedDocuments())); + if (importedInvoice.getAdditionalReferencedDocuments()!=null) { + return new ArrayList<>(Arrays.asList(importedInvoice.getAdditionalReferencedDocuments())); + } else { + return new ArrayList<>(); + } + } /*** From f575cccfbeedfe4ea44e013c1ba902be101d9019 Mon Sep 17 00:00:00 2001 From: jstaerk Date: Mon, 27 Jan 2025 12:18:28 +0100 Subject: [PATCH 21/22] closes #709 (just added a test the issue resolved itself with #708) --- History.md | 1 + .../org/mustangproject/ZUGFeRD/ZF2ZInvoiceImporterTest.java | 5 ++++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/History.md b/History.md index e550bcbc..7043484d 100644 --- a/History.md +++ b/History.md @@ -1,6 +1,7 @@ #705 #707 -#708 +-#709 2.16.1 ======= diff --git a/library/src/test/java/org/mustangproject/ZUGFeRD/ZF2ZInvoiceImporterTest.java b/library/src/test/java/org/mustangproject/ZUGFeRD/ZF2ZInvoiceImporterTest.java index 1e918395..55b8b605 100644 --- a/library/src/test/java/org/mustangproject/ZUGFeRD/ZF2ZInvoiceImporterTest.java +++ b/library/src/test/java/org/mustangproject/ZUGFeRD/ZF2ZInvoiceImporterTest.java @@ -351,6 +351,7 @@ public class ZF2ZInvoiceImporterTest extends ResourceCase { byte[] fileA = null; byte[] fileB = null; + boolean facturXFound=false; ZUGFeRDInvoiceImporter zii = new ZUGFeRDInvoiceImporter("./target/testout-ZF2PushAttachments.pdf"); for (FileAttachment fa : zii.getFileAttachmentsPDF()) { @@ -358,17 +359,19 @@ public class ZF2ZInvoiceImporterTest extends ResourceCase { fileA = fa.getData(); } else if (fa.getFilename().equals("two.pdf")) { fileB = fa.getData(); + } else if (fa.getFilename().equals("factur-x.xml")) { + facturXFound=true; } } byte[] b = {12, 13}; // the sample data that was used to write the files + assertTrue(facturXFound); assertTrue(Arrays.equals(fileA, b)); assertEquals(fileA.length, 2); assertTrue(Arrays.equals(fileB, b)); assertEquals(fileB.length, 2); } - public void testImportDebit() { File CIIinputFile = getResourceAsFile("cii/minimalDebit.xml"); try { From 5ec06bd74bf8ce19b46cb21cfc19e264bcc532af Mon Sep 17 00:00:00 2001 From: jstaerk Date: Mon, 27 Jan 2025 14:15:06 +0100 Subject: [PATCH 22/22] some merges --- History.md | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/History.md b/History.md index 7043484d..21dc49d3 100644 --- a/History.md +++ b/History.md @@ -2,6 +2,18 @@ #707 -#708 -#709 +607 +649 +650 +665 +684 +703 +701 +691 + + + + 2.16.1 =======