From 7871981c73645f1f12aa795c71b752226f0c2559 Mon Sep 17 00:00:00 2001 From: langfr Date: Fri, 6 Feb 2026 14:11:20 +0000 Subject: [PATCH 01/10] Add CalculationPercent (BT-101) and BasisAmount (BT-100) to allowances/charges on document level. --- .../ZUGFeRD/ZUGFeRD2PullProvider.java | 24 ++++++++++++++----- .../mustangproject/ZUGFeRD/ZF2PushTest.java | 5 ++-- 2 files changed, 21 insertions(+), 8 deletions(-) diff --git a/library/src/main/java/org/mustangproject/ZUGFeRD/ZUGFeRD2PullProvider.java b/library/src/main/java/org/mustangproject/ZUGFeRD/ZUGFeRD2PullProvider.java index d7aab617..61693ea5 100644 --- a/library/src/main/java/org/mustangproject/ZUGFeRD/ZUGFeRD2PullProvider.java +++ b/library/src/main/java/org/mustangproject/ZUGFeRD/ZUGFeRD2PullProvider.java @@ -811,8 +811,14 @@ public class ZUGFeRD2PullProvider implements IXMLProvider { xml += "" + "" + "true" + - "" + - "" + currencyFormat(charge.getTotalAmount(calc)) + ""; + ""; + if (charge.getPercent() != null && (profile == Profiles.getByName("EN16931") || (profile == Profiles.getByName("EXTENDED")))) { + xml += "" + vatFormat(charge.getPercent()) + ""; + } + if (charge.getBasisAmount() != null && (profile == Profiles.getByName("EN16931") || (profile == Profiles.getByName("EXTENDED")))) { + xml += "" + currencyFormat(charge.getBasisAmount()) + ""; + } + xml += "" + currencyFormat(charge.getTotalAmount(calc)) + ""; if (charge.getReasonCode() != null) { xml += "" + charge.getReasonCode() + ""; } @@ -855,14 +861,20 @@ public class ZUGFeRD2PullProvider implements IXMLProvider { xml += "" + "" + "false" + - "" + - "" + currencyFormat(allowance.getTotalAmount(calc)) + ""; - if (allowance.getReason() != null) { - xml += "" + XMLTools.encodeXML(allowance.getReason()) + ""; + ""; + if (allowance.getPercent() != null) { + xml += "" + vatFormat(allowance.getPercent()) + ""; } + if (allowance.getBasisAmount() != null) { + xml += "" + currencyFormat(allowance.getBasisAmount()) + ""; + } + xml += "" + currencyFormat(allowance.getTotalAmount(calc)) + ""; if (allowance.getReasonCode() != null) { xml += "" + allowance.getReasonCode() + ""; } + if (allowance.getReason() != null) { + xml += "" + XMLTools.encodeXML(allowance.getReason()) + ""; + } xml += "" + "VAT" + "" + allowance.getCategoryCode() + ""; diff --git a/library/src/test/java/org/mustangproject/ZUGFeRD/ZF2PushTest.java b/library/src/test/java/org/mustangproject/ZUGFeRD/ZF2PushTest.java index ade0ed6d..24a8ca63 100644 --- a/library/src/test/java/org/mustangproject/ZUGFeRD/ZF2PushTest.java +++ b/library/src/test/java/org/mustangproject/ZUGFeRD/ZF2PushTest.java @@ -763,8 +763,9 @@ public class ZF2PushTest extends TestCase { .setNumber(number) .addItem(new Item(new Product("Testprodukt", "", "H87", new BigDecimal(19)), price, new BigDecimal(1.0))) .addItem(new Item(new Product("Testprodukt", "", "H87", new BigDecimal(19)), price, new BigDecimal(1.0))) - .addItem(new Item(new Product("Testprodukt", "", "H87", new BigDecimal(19)), price, new BigDecimal(1.0))).addCharge(new Charge().setPercent(new BigDecimal(50)).setTaxPercent(new BigDecimal(19)).setReasonCode("ABK")) - .addAllowance(new Allowance().setPercent(new BigDecimal(50)).setTaxPercent(new BigDecimal(19)).setReason("Mengenrabatt")) + .addItem(new Item(new Product("Testprodukt", "", "H87", new BigDecimal(19)), price, new BigDecimal(1.0))) + .addCharge(new Charge().setPercent(new BigDecimal(50)).setBasisAmount(price).setTaxPercent(new BigDecimal(19)).setReasonCode("ABK").setReason("Verschiedenes")) + .addAllowance(new Allowance().setPercent(new BigDecimal(50)).setTaxPercent(new BigDecimal(19)).setReasonCode("95").setReason("Mengenrabatt")) ); String theXML = new String(ze.getProvider().getXML(), StandardCharsets.UTF_8); assertTrue(theXML.contains(" Date: Sat, 14 Mar 2026 20:04:45 +0100 Subject: [PATCH 02/10] Move getNamespaceForVersion and getPrefixForVersion to inheriting class. --- .../ZUGFeRD/DXExporterFromA1.java | 22 ------------------ .../ZUGFeRD/DXExporterFromA3.java | 23 +++++++++++++++++++ .../ZUGFeRD/OXExporterFromA1.java | 18 --------------- .../ZUGFeRD/OXExporterFromA3.java | 19 +++++++++++++++ 4 files changed, 42 insertions(+), 40 deletions(-) diff --git a/library/src/main/java/org/mustangproject/ZUGFeRD/DXExporterFromA1.java b/library/src/main/java/org/mustangproject/ZUGFeRD/DXExporterFromA1.java index f22d76c7..a3470ebc 100644 --- a/library/src/main/java/org/mustangproject/ZUGFeRD/DXExporterFromA1.java +++ b/library/src/main/java/org/mustangproject/ZUGFeRD/DXExporterFromA1.java @@ -41,28 +41,6 @@ public class DXExporterFromA1 extends DXExporterFromA3 { private static boolean isValidA1(DataSource dataSource) throws IOException { return getPDFAParserValidationResult(PreflightParserHelper.createPreflightParser (dataSource)); } - /*** - * internal helper function: get namespace for order-x - * @param ver the delivery-x version - * @return the URN of the namespace - */ - @Override - public String getNamespaceForVersion(int ver) { - // As of late 2022 the Delivery-X standard is not yet published. See specification: - // Die digitale Ablösung des Papier-Lieferscheins, Version 1.1, April 2022 - // Chapter 7.1 XMP-Erweiterungsschema für PDF/A-3 - // http://docplayer.org/230301085-Der-digitale-lieferschein-dls.html - return "urn:factur-x:pdfa:CrossIndustryDocument:despatchadvice:1p0#"; - } - /*** - * internal helper: returns the namespace prefix for the given order-x version number - * @param ver the ox version - * @return the namespace prefix as string, without colon - */ - @Override - public String getPrefixForVersion(int ver) { - return "fx"; - } private static boolean getPDFAParserValidationResult(PreflightParser parser) throws IOException { /* diff --git a/library/src/main/java/org/mustangproject/ZUGFeRD/DXExporterFromA3.java b/library/src/main/java/org/mustangproject/ZUGFeRD/DXExporterFromA3.java index 3324e36c..006b5c7e 100644 --- a/library/src/main/java/org/mustangproject/ZUGFeRD/DXExporterFromA3.java +++ b/library/src/main/java/org/mustangproject/ZUGFeRD/DXExporterFromA3.java @@ -129,6 +129,29 @@ public class DXExporterFromA3 extends ZUGFeRDExporterFromA3 { private boolean attachZUGFeRDHeaders = true; + /*** + * internal helper function: get namespace for order-x + * @param ver the delivery-x version + * @return the URN of the namespace + */ + @Override + public String getNamespaceForVersion(int ver) { + // As of late 2022 the Delivery-X standard is not yet published. See specification: + // Die digitale Ablösung des Papier-Lieferscheins, Version 1.1, April 2022 + // Chapter 7.1 XMP-Erweiterungsschema für PDF/A-3 + // http://docplayer.org/230301085-Der-digitale-lieferschein-dls.html + return "urn:factur-x:pdfa:CrossIndustryDocument:despatchadvice:1p0#"; + } + /*** + * internal helper: returns the namespace prefix for the given order-x version number + * @param ver the ox version + * @return the namespace prefix as string, without colon + */ + @Override + public String getPrefixForVersion(int ver) { + return "fx"; + } + /** * Makes A PDF/A3a-compliant document from a PDF-A1 compliant document (on the * metadata level, this will not e.g. convert graphics to JPG-2000) diff --git a/library/src/main/java/org/mustangproject/ZUGFeRD/OXExporterFromA1.java b/library/src/main/java/org/mustangproject/ZUGFeRD/OXExporterFromA1.java index 33f95aea..6b4b7e6c 100644 --- a/library/src/main/java/org/mustangproject/ZUGFeRD/OXExporterFromA1.java +++ b/library/src/main/java/org/mustangproject/ZUGFeRD/OXExporterFromA1.java @@ -41,24 +41,6 @@ public class OXExporterFromA1 extends OXExporterFromA3 { private static boolean isValidA1(DataSource dataSource) throws IOException { return getPDFAParserValidationResult(PreflightParserHelper.createPreflightParser(dataSource)); } - /*** - * internal helper function: get namespace for order-x - * @param ver the order-x version - * @return the URN of the namespace - */ - @Override - public String getNamespaceForVersion(int ver) { - return "urn:factur-x:pdfa:CrossIndustryDocument:1p0#"; - } - /*** - * internal helper: returns the namespace prefix for the given order-x version number - * @param ver the ox version - * @return the namespace prefix as string, without colon - */ - @Override - public String getPrefixForVersion(int ver) { - return "fx"; - } private static boolean getPDFAParserValidationResult(PreflightParser parser) throws IOException { /* diff --git a/library/src/main/java/org/mustangproject/ZUGFeRD/OXExporterFromA3.java b/library/src/main/java/org/mustangproject/ZUGFeRD/OXExporterFromA3.java index 5db1a693..40e181b1 100644 --- a/library/src/main/java/org/mustangproject/ZUGFeRD/OXExporterFromA3.java +++ b/library/src/main/java/org/mustangproject/ZUGFeRD/OXExporterFromA3.java @@ -129,6 +129,25 @@ public class OXExporterFromA3 extends ZUGFeRDExporterFromA3 { private boolean attachZUGFeRDHeaders = true; + /*** + * internal helper function: get namespace for order-x + * @param ver the order-x version + * @return the URN of the namespace + */ + @Override + public String getNamespaceForVersion(int ver) { + return "urn:factur-x:pdfa:CrossIndustryDocument:1p0#"; + } + /*** + * internal helper: returns the namespace prefix for the given order-x version number + * @param ver the ox version + * @return the namespace prefix as string, without colon + */ + @Override + public String getPrefixForVersion(int ver) { + return "fx"; + } + /** * Makes A PDF/A3a-compliant document from a PDF-A1 compliant document (on the * metadata level, this will not e.g. convert graphics to JPG-2000) From 4afa36c73ec03b9460949ed78fe51f1e09d806f7 Mon Sep 17 00:00:00 2001 From: langfr Date: Fri, 20 Mar 2026 16:16:55 +0100 Subject: [PATCH 03/10] Correct order of "DespatchAdviceReferencedDocument" and "DeliveryNoteReferencedDocument". --- .../ZUGFeRD/ZUGFeRD2PullProvider.java | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/library/src/main/java/org/mustangproject/ZUGFeRD/ZUGFeRD2PullProvider.java b/library/src/main/java/org/mustangproject/ZUGFeRD/ZUGFeRD2PullProvider.java index d7aab617..09b57358 100644 --- a/library/src/main/java/org/mustangproject/ZUGFeRD/ZUGFeRD2PullProvider.java +++ b/library/src/main/java/org/mustangproject/ZUGFeRD/ZUGFeRD2PullProvider.java @@ -700,6 +700,12 @@ public class ZUGFeRD2PullProvider implements IXMLProvider { + ""; } + if (trans.getDespatchAdviceReferencedDocumentID() != null && !trans.getDespatchAdviceReferencedDocumentID().trim().isEmpty()) { + xml += ""; + xml += "" + XMLTools.encodeXML(trans.getDespatchAdviceReferencedDocumentID()) + ""; + xml += ""; + } + if (trans.getDeliveryNoteReferencedDocumentID() != null && !trans.getDeliveryNoteReferencedDocumentID().trim().isEmpty()) { xml += ""; xml += "" + XMLTools.encodeXML(trans.getDeliveryNoteReferencedDocumentID()) + ""; @@ -708,14 +714,6 @@ public class ZUGFeRD2PullProvider implements IXMLProvider { xml += ""+XMLTools.encodeXML(dateFormat102.format(trans.getDeliveryNoteReferencedDocumentDate()))+""; } xml += ""; - - } - - if (trans.getDespatchAdviceReferencedDocumentID() != null && !trans.getDespatchAdviceReferencedDocumentID().trim().isEmpty()) { - xml += ""; - xml += "" + XMLTools.encodeXML(trans.getDespatchAdviceReferencedDocumentID()) + ""; - xml += ""; - } xml += ""; From 18e0b1c691e4a0e8efdebc6464accceb3345fcbc Mon Sep 17 00:00:00 2001 From: Philipp Date: Tue, 14 Apr 2026 15:43:35 +0200 Subject: [PATCH 04/10] #814 upgrade CEN EN16931 Schematron from v1.3.12 to v1.3.15 Replace EN16931-CII-validation.xslt and EN16931-UBL-validation.xslt with the v1.3.15 release. Fixes BR-DEC-23 XPath bug and aligns with KoSIT XRechnung 3.0.2 validator (v2026-01-31). Adds regression test. Made-with: Cursor --- .../EN16931-CII-validation.xslt | 482 +++++++++++------- .../EN16931-UBL-validation.xslt | 273 +++++----- .../validator/XMLValidatorTest.java | 32 ++ .../test/resources/invalidXRV30_BR-DEC-23.xml | 114 +++++ 4 files changed, 597 insertions(+), 304 deletions(-) create mode 100644 validator/src/test/resources/invalidXRV30_BR-DEC-23.xml diff --git a/validator/src/main/resources/xslt/en16931schematron/EN16931-CII-validation.xslt b/validator/src/main/resources/xslt/en16931schematron/EN16931-CII-validation.xslt index 7ab90522..3e184293 100644 --- a/validator/src/main/resources/xslt/en16931schematron/EN16931-CII-validation.xslt +++ b/validator/src/main/resources/xslt/en16931schematron/EN16931-CII-validation.xslt @@ -4,7 +4,7 @@ Licensed under European Union Public Licence (EUPL) version 1.2. --> - + @@ -175,6 +175,9 @@ + + + EN16931-CII-Model EN16931-CII-Model @@ -184,6 +187,9 @@ + + + EN16931-CII-Syntax EN16931-CII-Syntax @@ -193,6 +199,9 @@ + + + EN16931-Codes EN16931-Codes @@ -208,28 +217,7 @@ - - - - - - - - - BR-66 - fatal - - - - [BR-66]-Each Specified Trade Allowance Charge (BG-20)(BG-21) shall contain a Charge Indicator. - - - - - - - - + @@ -250,7 +238,7 @@ - + @@ -271,7 +259,7 @@ - + @@ -292,8 +280,8 @@ - - + + @@ -328,7 +316,7 @@ - + @@ -349,7 +337,7 @@ - + @@ -460,7 +448,7 @@ - + @@ -571,7 +559,7 @@ - + @@ -681,7 +669,7 @@ - + BR-CO-12 @@ -877,7 +865,7 @@ - + @@ -1288,7 +1276,7 @@ - + @@ -1458,9 +1446,9 @@ - + - + BR-DEC-23 fatal @@ -1474,7 +1462,7 @@ - + @@ -1570,7 +1558,7 @@ - + @@ -1666,7 +1654,7 @@ - + @@ -1702,7 +1690,7 @@ - + @@ -1738,7 +1726,7 @@ - + @@ -1759,7 +1747,7 @@ - + @@ -1780,7 +1768,7 @@ - + @@ -1797,12 +1785,18 @@ + + + + + + - + - + BR-CO-27 fatal @@ -1929,7 +1923,7 @@ - + BR-CO-14 @@ -1971,9 +1965,9 @@ - + - + BR-AE-08 fatal @@ -2355,9 +2349,9 @@ - + - + BR-AG-05 fatal @@ -2391,9 +2385,9 @@ - + - + BR-AG-06 fatal @@ -2427,9 +2421,9 @@ - + - + BR-AG-07 fatal @@ -2448,9 +2442,9 @@ - + - + BR-E-08 fatal @@ -2607,9 +2601,9 @@ - + - + BR-G-08 fatal @@ -2766,9 +2760,9 @@ - + - + BR-IC-08 fatal @@ -3066,9 +3060,9 @@ - + - + BR-O-03 fatal @@ -3102,9 +3096,9 @@ - + - + BR-O-04 fatal @@ -3138,9 +3132,9 @@ - + - + BR-O-02 fatal @@ -3620,7 +3614,28 @@ - + + + + + + + + + CII-SR-464 + warning + + + + [CII-SR-464] - Only one BT-86 element is allowed on an invoice. + + + + + + + + @@ -3776,7 +3791,7 @@ - + @@ -4067,7 +4082,7 @@ - + @@ -4088,7 +4103,7 @@ - + @@ -4169,7 +4184,7 @@ - + @@ -4265,7 +4280,7 @@ - + @@ -5111,7 +5126,7 @@ - + @@ -5147,7 +5162,7 @@ - + @@ -5587,21 +5602,6 @@ - - - - CII-SR-440 - fatal - - - - [CII-SR-440] - ActualAmount should exist maximum once - - - - - - @@ -5932,6 +5932,48 @@ + + + + + + + + + + CII-SR-463 + fatal + + + + [CII-SR-463]-Each Specified Trade Allowance Charge (BG-20)(BG-21) shall contain a Charge Indicator. + + + + + + + + + + + + + + + + CII-SR-440 + fatal + + + + [CII-SR-440] - ActualAmount should exist maximum once + + + + + + @@ -6995,8 +7037,8 @@ - - + + @@ -7900,9 +7942,9 @@ - + - + CII-SR-282 warning @@ -8384,7 +8426,7 @@ CII-SR-459 - warning + fatal @@ -8399,7 +8441,7 @@ CII-SR-460 - warning + fatal @@ -8407,6 +8449,36 @@ + + + + + + + CII-SR-465 + warning + + + + [CII-SR-465] - Only one BT-41 element is allowed on an invoice. + + + + + + + + + + CII-SR-466 + warning + + + + [CII-SR-466] - Only one BT-56 element is allowed on an invoice. + + + @@ -10237,9 +10309,9 @@ - + - + CII-SR-462 fatal @@ -10496,59 +10568,8 @@ - - - - - - - - - CII-DT-005 - fatal - - - - [CII-DT-005] - schemeID should not be present - - - - - - - - - - CII-DT-006 - fatal - - - - [CII-DT-006] - schemeAgencyID should not be present - - - - - - - - - - CII-DT-007 - fatal - - - - [CII-DT-007] - schemeVersionID should not be present - - - - - - - - - + + @@ -10609,6 +10630,117 @@ + + + + + + + CII-DT-005 + fatal + + + + [CII-DT-005] - schemeID should not be present + + + + + + + + + + CII-DT-006 + fatal + + + + [CII-DT-006] - schemeAgencyID should not be present + + + + + + + + + + CII-DT-007 + fatal + + + + [CII-DT-007] - schemeVersionID should not be present + + + + + + + + + + + + + + + + CII-DT-101 + fatal + + + + [CII-DT-101] - schemeName should not be present + + + + + + + + + + CII-DT-102 + fatal + + + + [CII-DT-102] - schemeAgencyName should not be present + + + + + + + + + + CII-DT-103 + fatal + + + + [CII-DT-103] - schemeDataURI should not be present + + + + + + + + + + CII-DT-104 + fatal + + + + [CII-DT-104] - schemeURI should not be present + + + @@ -10771,7 +10903,7 @@ - + CII-DT-015 @@ -12005,9 +12137,9 @@ - + - + BR-CL-01 fatal @@ -12026,9 +12158,9 @@ - + - + BR-CL-03 fatal @@ -12047,9 +12179,9 @@ - + - + BR-CL-04 fatal @@ -12068,9 +12200,9 @@ - + - + BR-CL-05 fatal @@ -12110,9 +12242,9 @@ - + - + BR-CL-07 fatal @@ -12152,9 +12284,9 @@ - + - + BR-CL-10 fatal @@ -12173,9 +12305,9 @@ - + - + BR-CL-11 fatal @@ -12194,9 +12326,9 @@ - + - + BR-CL-13 fatal @@ -12362,9 +12494,9 @@ - + - + BR-CL-21 fatal @@ -12384,9 +12516,9 @@ - + - + BR-CL-22 fatal @@ -12447,9 +12579,9 @@ - + - + BR-CL-25 fatal @@ -12468,9 +12600,9 @@ - + - + BR-CL-26 fatal diff --git a/validator/src/main/resources/xslt/en16931schematron/EN16931-UBL-validation.xslt b/validator/src/main/resources/xslt/en16931schematron/EN16931-UBL-validation.xslt index bc209719..07c2f7ba 100644 --- a/validator/src/main/resources/xslt/en16931schematron/EN16931-UBL-validation.xslt +++ b/validator/src/main/resources/xslt/en16931schematron/EN16931-UBL-validation.xslt @@ -4,7 +4,7 @@ Licensed under European Union Public Licence (EUPL) version 1.2. --> - + @@ -177,6 +177,9 @@ + + + UBL-model UBL-model @@ -186,6 +189,9 @@ + + + UBL-syntax UBL-syntax @@ -195,6 +201,9 @@ + + + Codesmodel Codesmodel @@ -231,8 +240,8 @@ - - + + @@ -341,7 +350,7 @@ - + BR-31 @@ -989,36 +998,6 @@ - - - - BR-66 - fatal - - - - [BR-66]-An Invoice shall contain maximum one Payment Card account (BG-18). - - - - - - - - - - BR-67 - fatal - - - - [BR-67]-An Invoice shall contain maximum one Payment Mandate (BG-19). - - - - - - @@ -1367,12 +1346,12 @@ - BR-IG-01 + BR-AF-01 fatal - [BR-IG-01]-An Invoice that contains an Invoice line (BG-25), a Document level allowance (BG-20) or a Document level charge (BG-21) where the VAT category code (BT-151, BT-95 or BT-102) is "IGIC" shall contain in the VAT breakdown (BG-23) at least one VAT category code (BT-118) equal with "IGIC". + [BR-AF-01]-An Invoice that contains an Invoice line (BG-25), a Document level allowance (BG-20) or a Document level charge (BG-21) where the VAT category code (BT-151, BT-95 or BT-102) is "IGIC" shall contain in the VAT breakdown (BG-23) at least one VAT category code (BT-118) equal with "IGIC". @@ -1382,12 +1361,12 @@ - BR-IG-02 + BR-AF-02 fatal - [BR-IG-02]-An Invoice that contains an Invoice line (BG-25) where the Invoiced item VAT category code (BT-151) is "IGIC" shall contain the Seller VAT Identifier (BT-31), the Seller tax registration identifier (BT-32) and/or the Seller tax representative VAT identifier (BT-63). + [BR-AF-02]-An Invoice that contains an Invoice line (BG-25) where the Invoiced item VAT category code (BT-151) is "IGIC" shall contain the Seller VAT Identifier (BT-31), the Seller tax registration identifier (BT-32) and/or the Seller tax representative VAT identifier (BT-63). @@ -1397,12 +1376,12 @@ - BR-IG-03 + BR-AF-03 fatal - [BR-IG-03]-An Invoice that contains a Document level allowance (BG-20) where the Document level allowance VAT category code (BT-95) is "IGIC" shall contain the Seller VAT Identifier (BT-31), the Seller tax registration identifier (BT-32) and/or the Seller tax representative VAT identifier (BT-63). + [BR-AF-03]-An Invoice that contains a Document level allowance (BG-20) where the Document level allowance VAT category code (BT-95) is "IGIC" shall contain the Seller VAT Identifier (BT-31), the Seller tax registration identifier (BT-32) and/or the Seller tax representative VAT identifier (BT-63). @@ -1412,12 +1391,12 @@ - BR-IG-04 + BR-AF-04 fatal - [BR-IG-04]-An Invoice that contains a Document level charge (BG-21) where the Document level charge VAT category code (BT-102) is "IGIC" shall contain the Seller VAT Identifier (BT-31), the Seller Tax registration identifier (BT-32) and/or the Seller tax representative VAT identifier (BT-63). + [BR-AF-04]-An Invoice that contains a Document level charge (BG-21) where the Document level charge VAT category code (BT-102) is "IGIC" shall contain the Seller VAT Identifier (BT-31), the Seller Tax registration identifier (BT-32) and/or the Seller tax representative VAT identifier (BT-63). @@ -1427,12 +1406,12 @@ - BR-IP-01 + BR-AG-01 fatal - [BR-IP-01]-An Invoice that contains an Invoice line (BG-25), a Document level allowance (BG-20) or a Document level charge (BG-21) where the VAT category code (BT-151, BT-95 or BT-102) is "IPSI" shall contain in the VAT breakdown (BG-23) at least one VAT category code (BT-118) equal with "IPSI". + [BR-AG-01]-An Invoice that contains an Invoice line (BG-25), a Document level allowance (BG-20) or a Document level charge (BG-21) where the VAT category code (BT-151, BT-95 or BT-102) is "IPSI" shall contain in the VAT breakdown (BG-23) at least one VAT category code (BT-118) equal with "IPSI". @@ -1442,12 +1421,12 @@ - BR-IP-02 + BR-AG-02 fatal - [BR-IP-02]-An Invoice that contains an Invoice line (BG-25) where the Invoiced item VAT category code (BT-151) is "IPSI" shall contain the Seller VAT Identifier (BT-31), the Seller tax registration identifier (BT-32) and/or the Seller tax representative VAT identifier (BT-63). + [BR-AG-02]-An Invoice that contains an Invoice line (BG-25) where the Invoiced item VAT category code (BT-151) is "IPSI" shall contain the Seller VAT Identifier (BT-31), the Seller tax registration identifier (BT-32) and/or the Seller tax representative VAT identifier (BT-63). @@ -1457,12 +1436,12 @@ - BR-IP-03 + BR-AG-03 fatal - [BR-IP-03]-An Invoice that contains a Document level allowance (BG-20) where the Document level allowance VAT category code (BT-95) is "IPSI" shall contain the Seller VAT Identifier (BT-31), the Seller Tax registration identifier (BT-32) and/or the Seller tax representative VAT identifier (BT-63). + [BR-AG-03]-An Invoice that contains a Document level allowance (BG-20) where the Document level allowance VAT category code (BT-95) is "IPSI" shall contain the Seller VAT Identifier (BT-31), the Seller Tax registration identifier (BT-32) and/or the Seller tax representative VAT identifier (BT-63). @@ -1472,12 +1451,12 @@ - BR-IP-04 + BR-AG-04 fatal - [BR-IP-04]-An Invoice that contains a Document level charge (BG-21) where the Document level charge VAT category code (BT-102) is "IPSI" shall contain the Seller VAT Identifier (BT-31), the Seller Tax registration identifier (BT-32) and/or the Seller tax representative VAT identifier (BT-63). + [BR-AG-04]-An Invoice that contains a Document level charge (BG-21) where the Document level charge VAT category code (BT-102) is "IPSI" shall contain the Seller VAT Identifier (BT-31), the Seller Tax registration identifier (BT-32) and/or the Seller tax representative VAT identifier (BT-63). @@ -2196,8 +2175,8 @@ - - + + @@ -3110,12 +3089,12 @@ - BR-IG-08 + BR-AF-08 fatal - [BR-IG-08]-For each different value of VAT category rate (BT-119) where the VAT category code (BT-118) is "IGIC", the VAT category taxable amount (BT-116) in a VAT breakdown (BG-23) shall equal the sum of Invoice line net amounts (BT-131) plus the sum of document level charge amounts (BT-99) minus the sum of document level allowance amounts (BT-92) where the VAT category code (BT-151, BT-102, BT-95) is "IGIC" and the VAT rate (BT-152, BT-103, BT-96) equals the VAT category rate (BT-119). + [BR-AF-08]-For each different value of VAT category rate (BT-119) where the VAT category code (BT-118) is "IGIC", the VAT category taxable amount (BT-116) in a VAT breakdown (BG-23) shall equal the sum of Invoice line net amounts (BT-131) plus the sum of document level charge amounts (BT-99) minus the sum of document level allowance amounts (BT-92) where the VAT category code (BT-151, BT-102, BT-95) is "IGIC" and the VAT rate (BT-152, BT-103, BT-96) equals the VAT category rate (BT-119). @@ -3125,12 +3104,12 @@ - BR-IG-09 + BR-AF-09 fatal - [BR-IG-09]-The VAT category tax amount (BT-117) in a VAT breakdown (BG-23) where VAT category code (BT-118) is "IGIC" shall equal the VAT category taxable amount (BT-116) multiplied by the VAT category rate (BT-119). + [BR-AF-09]-The VAT category tax amount (BT-117) in a VAT breakdown (BG-23) where VAT category code (BT-118) is "IGIC" shall equal the VAT category taxable amount (BT-116) multiplied by the VAT category rate (BT-119). @@ -3140,12 +3119,12 @@ - BR-IG-10 + BR-AF-10 fatal - [BR-IG-10]-A VAT breakdown (BG-23) with VAT Category code (BT-118) "IGIC" shall not have a VAT exemption reason code (BT-121) or VAT exemption reason text (BT-120). + [BR-AF-10]-A VAT breakdown (BG-23) with VAT Category code (BT-118) "IGIC" shall not have a VAT exemption reason code (BT-121) or VAT exemption reason text (BT-120). @@ -3161,12 +3140,12 @@ - BR-IG-06 + BR-AF-06 fatal - [BR-IG-06]-In a Document level allowance (BG-20) where the Document level allowance VAT category code (BT-95) is "IGIC" the Document level allowance VAT rate (BT-96) shall be 0 (zero) or greater than zero. + [BR-AF-06]-In a Document level allowance (BG-20) where the Document level allowance VAT category code (BT-95) is "IGIC" the Document level allowance VAT rate (BT-96) shall be 0 (zero) or greater than zero. @@ -3182,12 +3161,12 @@ - BR-IG-07 + BR-AF-07 fatal - [BR-IG-07]-In a Document level charge (BG-21) where the Document level charge VAT category code (BT-102) is "IGIC" the Document level charge VAT rate (BT-103) shall be 0 (zero) or greater than zero. + [BR-AF-07]-In a Document level charge (BG-21) where the Document level charge VAT category code (BT-102) is "IGIC" the Document level charge VAT rate (BT-103) shall be 0 (zero) or greater than zero. @@ -3203,12 +3182,12 @@ - BR-IG-05 + BR-AF-05 fatal - [BR-IG-05]-In an Invoice line (BG-25) where the Invoiced item VAT category code (BT-151) is "IGIC" the invoiced item VAT rate (BT-152) shall be 0 (zero) or greater than zero. + [BR-AF-05]-In an Invoice line (BG-25) where the Invoiced item VAT category code (BT-151) is "IGIC" the invoiced item VAT rate (BT-152) shall be 0 (zero) or greater than zero. @@ -3224,12 +3203,12 @@ - BR-IP-08 + BR-AG-08 fatal - [BR-IP-08]-For each different value of VAT category rate (BT-119) where the VAT category code (BT-118) is "IPSI", the VAT category taxable amount (BT-116) in a VAT breakdown (BG-23) shall equal the sum of Invoice line net amounts (BT-131) plus the sum of document level charge amounts (BT-99) minus the sum of document level allowance amounts (BT-92) where the VAT category code (BT-151, BT-102, BT-95) is "IPSI" and the VAT rate (BT-152, BT-103, BT-96) equals the VAT category rate (BT-119). + [BR-AG-08]-For each different value of VAT category rate (BT-119) where the VAT category code (BT-118) is "IPSI", the VAT category taxable amount (BT-116) in a VAT breakdown (BG-23) shall equal the sum of Invoice line net amounts (BT-131) plus the sum of document level charge amounts (BT-99) minus the sum of document level allowance amounts (BT-92) where the VAT category code (BT-151, BT-102, BT-95) is "IPSI" and the VAT rate (BT-152, BT-103, BT-96) equals the VAT category rate (BT-119). @@ -3239,12 +3218,12 @@ - BR-IP-09 + BR-AG-09 fatal - [BR-IP-09]-The VAT category tax amount (BT-117) in a VAT breakdown (BG-23) where VAT category code (BT-118) is "IPSI" shall equal the VAT category taxable amount (BT-116) multiplied by the VAT category rate (BT-119). + [BR-AG-09]-The VAT category tax amount (BT-117) in a VAT breakdown (BG-23) where VAT category code (BT-118) is "IPSI" shall equal the VAT category taxable amount (BT-116) multiplied by the VAT category rate (BT-119). @@ -3254,12 +3233,12 @@ - BR-IP-10 + BR-AG-10 fatal - [BR-IP-10]-A VAT breakdown (BG-23) with VAT Category code (BT-118) "IPSI" shall not have a VAT exemption reason code (BT-121) or VAT exemption reason text (BT-120). + [BR-AG-10]-A VAT breakdown (BG-23) with VAT Category code (BT-118) "IPSI" shall not have a VAT exemption reason code (BT-121) or VAT exemption reason text (BT-120). @@ -3275,12 +3254,12 @@ - BR-IP-06 + BR-AG-06 fatal - [BR-IP-06]-In a Document level allowance (BG-20) where the Document level allowance VAT category code (BT-95) is "IPSI" the Document level allowance VAT rate (BT-96) shall be 0 (zero) or greater than zero. + [BR-AG-06]-In a Document level allowance (BG-20) where the Document level allowance VAT category code (BT-95) is "IPSI" the Document level allowance VAT rate (BT-96) shall be 0 (zero) or greater than zero. @@ -3296,12 +3275,12 @@ - BR-IP-07 + BR-AG-07 fatal - [BR-IP-07]-In a Document level charge (BG-21) where the Document level charge VAT category code (BT-102) is "IPSI" the Document level charge VAT rate (BT-103) shall be 0 (zero) or greater than zero. + [BR-AG-07]-In a Document level charge (BG-21) where the Document level charge VAT category code (BT-102) is "IPSI" the Document level charge VAT rate (BT-103) shall be 0 (zero) or greater than zero. @@ -3317,12 +3296,12 @@ - BR-IP-05 + BR-AG-05 fatal - [BR-IP-05]-In an Invoice line (BG-25) where the Invoiced item VAT category code (BT-151) is "IPSI" the Invoiced item VAT rate (BT-152) shall be 0 (zero) or greater than zero. + [BR-AG-05]-In an Invoice line (BG-25) where the Invoiced item VAT category code (BT-151) is "IPSI" the Invoiced item VAT rate (BT-152) shall be 0 (zero) or greater than zero. @@ -3679,8 +3658,8 @@ - - + + @@ -3700,7 +3679,7 @@ - + @@ -3721,7 +3700,7 @@ - + @@ -3757,7 +3736,7 @@ - + @@ -3778,7 +3757,7 @@ - + @@ -3814,7 +3793,7 @@ - + @@ -3835,7 +3814,7 @@ - + @@ -3856,7 +3835,7 @@ - + @@ -3876,6 +3855,27 @@ + + + + + + + + + + UBL-SR-53 + fatal + + + + [UBL-SR-53]- CompanyID (VAT Identifier) must be stated when providing the PartyTaxScheme/TaxScheme/ID. + + + + + + @@ -12410,7 +12410,7 @@ - [UBL-CR-572]-A UBL invoice should not include the InvoiceLine Item BuyersItemIdentification BarecodeSymbologyID + [UBL-CR-572]-A UBL invoice should not include the InvoiceLine Item BuyersItemIdentification BarcodeSymbologyID @@ -12485,7 +12485,7 @@ - [UBL-CR-577]-A UBL invoice should not include the InvoiceLine Item SellersItemIdentification BarecodeSymbologyID + [UBL-CR-577]-A UBL invoice should not include the InvoiceLine Item SellersItemIdentification BarcodeSymbologyID @@ -12575,7 +12575,7 @@ - [UBL-CR-583]-A UBL invoice should not include the InvoiceLine Item StandardItemIdentification BarecodeSymbologyID + [UBL-CR-583]-A UBL invoice should not include the InvoiceLine Item StandardItemIdentification BarcodeSymbologyID @@ -13287,21 +13287,6 @@ - - - - UBL-CR-631 - warning - - - - [UBL-CR-631]-A UBL invoice should not include the InvoiceLine Item Price OrderableUnitFactorRate - - - - - - @@ -14754,6 +14739,36 @@ + + + + + + + UBL-SR-54 + fatal + + + + [UBL-SR-54]-An Invoice shall contain maximum one Payment Card account (BG-18). + + + + + + + + + + UBL-SR-55 + fatal + + + + [UBL-SR-55]-An Invoice shall contain maximum one Payment Mandate (BG-19). + + + @@ -15012,9 +15027,9 @@ - + - + UBL-SR-22 fatal @@ -15076,9 +15091,9 @@ - + - + BR-CL-01 fatal @@ -15097,9 +15112,9 @@ - + - + BR-CL-03 fatal @@ -15118,9 +15133,9 @@ - + - + BR-CL-04 fatal @@ -15139,9 +15154,9 @@ - + - + BR-CL-05 fatal @@ -15181,9 +15196,9 @@ - + - + BR-CL-07 fatal @@ -15202,9 +15217,9 @@ - + - + BR-CL-10 fatal @@ -15223,9 +15238,9 @@ - + - + BR-CL-11 fatal @@ -15244,9 +15259,9 @@ - + - + BR-CL-13 fatal @@ -15413,9 +15428,9 @@ - + - + BR-CL-21 fatal @@ -15434,9 +15449,9 @@ - + - + BR-CL-22 fatal @@ -15498,9 +15513,9 @@ - + - + BR-CL-25 fatal @@ -15519,9 +15534,9 @@ - + - + BR-CL-26 fatal diff --git a/validator/src/test/java/org/mustangproject/validator/XMLValidatorTest.java b/validator/src/test/java/org/mustangproject/validator/XMLValidatorTest.java index 66fce4e3..0993b66a 100644 --- a/validator/src/test/java/org/mustangproject/validator/XMLValidatorTest.java +++ b/validator/src/test/java/org/mustangproject/validator/XMLValidatorTest.java @@ -405,6 +405,38 @@ public class XMLValidatorTest extends ResourceCase { } + public void testBRDEC23Regression() { + // Regression test: BR-DEC-23 must fire for XRechnung CII invoices whose + // LineTotalAmount (BT-131) has more than 2 decimal places. + // The bug existed in CEN Schematron <=1.3.12 due to a wrong XPath context + // (SpecifiedTradeSettlement instead of SpecifiedLineTradeSettlement). + final ValidationContext ctx = new ValidationContext(null); + final XMLValidator xv = new XMLValidator(ctx); + final XPathEngine xpath = new JAXPXPathEngine(); + + File tempFile = getResourceAsFile("invalidXRV30_BR-DEC-23.xml"); + boolean noExceptions = true; + try { + xv.setFilename(tempFile.getAbsolutePath()); + xv.validate(); + + String s = "" + xv.getXMLResult() + ""; + Source source = Input.fromString(s).build(); + + // must be invalid overall + String status = xpath.evaluate("/validation/summary/@status", source); + assertEquals("invalid", status); + + // must contain the BR-DEC-23 rule ID in at least one error + assertThat(s).valueByXPath("count(//error[contains(text(),'BR-DEC-23')])") + .asInt() + .isGreaterThanOrEqualTo(1); + } catch (IrrecoverableValidationError e) { + noExceptions = false; + } + assertTrue(noExceptions); + } + public void testSubInvoiceLineHierarchy() { final ValidationContext ctx = new ValidationContext(null); final XMLValidator xv = new XMLValidator(ctx); diff --git a/validator/src/test/resources/invalidXRV30_BR-DEC-23.xml b/validator/src/test/resources/invalidXRV30_BR-DEC-23.xml new file mode 100644 index 00000000..28b7c839 --- /dev/null +++ b/validator/src/test/resources/invalidXRV30_BR-DEC-23.xml @@ -0,0 +1,114 @@ + + + + + + urn:fdc:peppol.eu:2017:poacc:billing:01:1.0 + + + urn:cen.eu:en16931:2017#compliant#urn:xeinkauf.de:kosit:xrechnung_3.0 + + + + BR-DEC-23-REGRESSION + 380 + + 20240101 + + + + + + 1 + + + Test product + + + + 288.79 + + + + 1 + + + + VAT + S + 19 + + + + 288.7934 + + + + + 04011000-12345-03 + + Test Seller GmbH + + 12345 + Seller Street 1 + Berlin + DE + + + seller@test.de + + + DE123456789 + + + + Test Buyer AG + + 10115 + Buyer Street 2 + Berlin + DE + + + buyer@test.de + + + + + + EUR + + 58 + + DE75512108001245126199 + + + + 54.87 + VAT + 288.79 + S + 19 + + + Zahlbar sofort ohne Abzug. + + + 288.79 + 288.79 + 54.87 + 343.66 + 343.66 + + + + From e09723f82f0b792f8c5a3fb302aa05e61bde2032 Mon Sep 17 00:00:00 2001 From: Philipp Fischer Date: Thu, 16 Apr 2026 09:17:25 +0200 Subject: [PATCH 05/10] Fixes #1095 (validator): avoid floating-point artifacts in ZF_240 FACTUR-X_EXTENDED CO-10/CO-13 --- .../src/main/resources/xslt/ZF_240/FACTUR-X_EXTENDED.xslt | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/validator/src/main/resources/xslt/ZF_240/FACTUR-X_EXTENDED.xslt b/validator/src/main/resources/xslt/ZF_240/FACTUR-X_EXTENDED.xslt index 2e5b60fa..32882558 100644 --- a/validator/src/main/resources/xslt/ZF_240/FACTUR-X_EXTENDED.xslt +++ b/validator/src/main/resources/xslt/ZF_240/FACTUR-X_EXTENDED.xslt @@ -2903,9 +2903,9 @@ - + - + FX-SCH-A-000365 @@ -3008,9 +3008,9 @@ - + - + FX-SCH-A-000366 From 27f827c097a8f60c3882aa7f08f787a3869237cc Mon Sep 17 00:00:00 2001 From: Philipp Fischer Date: Thu, 16 Apr 2026 10:29:32 +0200 Subject: [PATCH 06/10] Fixes #1095: ZF_240 XSLT monetary sums to cast inputs to xs:decimal before aggregation --- .../resources/xslt/ZF_240/FACTUR-X_BASIC-WL.xslt | 8 ++++---- .../main/resources/xslt/ZF_240/FACTUR-X_BASIC.xslt | 12 ++++++------ .../main/resources/xslt/ZF_240/FACTUR-X_EN16931.xslt | 12 ++++++------ 3 files changed, 16 insertions(+), 16 deletions(-) diff --git a/validator/src/main/resources/xslt/ZF_240/FACTUR-X_BASIC-WL.xslt b/validator/src/main/resources/xslt/ZF_240/FACTUR-X_BASIC-WL.xslt index c30bd62d..2d97527c 100644 --- a/validator/src/main/resources/xslt/ZF_240/FACTUR-X_BASIC-WL.xslt +++ b/validator/src/main/resources/xslt/ZF_240/FACTUR-X_BASIC-WL.xslt @@ -2025,9 +2025,9 @@ - + - + FX-SCH-A-000118 @@ -2040,9 +2040,9 @@ - + - + FX-SCH-A-000119 diff --git a/validator/src/main/resources/xslt/ZF_240/FACTUR-X_BASIC.xslt b/validator/src/main/resources/xslt/ZF_240/FACTUR-X_BASIC.xslt index 6d688013..66f6faa1 100644 --- a/validator/src/main/resources/xslt/ZF_240/FACTUR-X_BASIC.xslt +++ b/validator/src/main/resources/xslt/ZF_240/FACTUR-X_BASIC.xslt @@ -2633,9 +2633,9 @@ - + - + FX-SCH-A-000226 @@ -2708,9 +2708,9 @@ - + - + FX-SCH-A-000118 @@ -2723,9 +2723,9 @@ - + - + FX-SCH-A-000119 diff --git a/validator/src/main/resources/xslt/ZF_240/FACTUR-X_EN16931.xslt b/validator/src/main/resources/xslt/ZF_240/FACTUR-X_EN16931.xslt index 0a0e0c64..d216c8a9 100644 --- a/validator/src/main/resources/xslt/ZF_240/FACTUR-X_EN16931.xslt +++ b/validator/src/main/resources/xslt/ZF_240/FACTUR-X_EN16931.xslt @@ -2703,9 +2703,9 @@ - + - + FX-SCH-A-000226 @@ -2778,9 +2778,9 @@ - + - + FX-SCH-A-000118 @@ -2793,9 +2793,9 @@ - + - + FX-SCH-A-000119 From 2490d1caf9e17d6dc98a6e9785737b3935180ade Mon Sep 17 00:00:00 2001 From: langfr Date: Sat, 25 Apr 2026 14:20:27 +0100 Subject: [PATCH 07/10] Add BasisAmount to CashDiscount. --- .../java/org/mustangproject/CashDiscount.java | 35 ++++++++++++------- 1 file changed, 23 insertions(+), 12 deletions(-) diff --git a/library/src/main/java/org/mustangproject/CashDiscount.java b/library/src/main/java/org/mustangproject/CashDiscount.java index b1993a02..d27b3dcb 100644 --- a/library/src/main/java/org/mustangproject/CashDiscount.java +++ b/library/src/main/java/org/mustangproject/CashDiscount.java @@ -21,7 +21,9 @@ public class CashDiscount implements IZUGFeRDCashDiscount { /*** * the period (usually days) count how long the percent apply */ - protected Integer days=null; + protected Integer days; + + protected BigDecimal basisAmount; /*** * Create a cash discount (skonto) with the specified height in the specified period. @@ -61,18 +63,31 @@ public class CashDiscount implements IZUGFeRDCashDiscount { return this; } + public BigDecimal getBasisAmount() { + return basisAmount; + } + + public CashDiscount setBasisAmount(BigDecimal basisAmount) { + this.basisAmount = basisAmount; + return this; + } + /*** * @return this particular cash discount as cross industry invoice XML */ @JsonIgnore public String getAsCII() { - return ""+ - "Cash Discount"+ - " "+ - " "+days+""+ - " "+XMLTools.nDigitFormat(percent,3)+""+ - " "+ - ""; + String s = "" + + " Cash Discount" + + " " + + " " + days + ""; + if (basisAmount != null) { + s += " " + XMLTools.nDigitFormat(basisAmount, 2) + ""; + } + s += " "+XMLTools.nDigitFormat(percent,3)+""+ + " "+ + ""; + return s; } /*** @@ -84,8 +99,4 @@ public class CashDiscount implements IZUGFeRDCashDiscount { public String getAsXRechnung() { return "#SKONTO#TAGE="+days+"#PROZENT="+XMLTools.nDigitFormat(percent,2)+"#\n"; } - - - - } From 81a3e857e2927fb06d15c9d1ccf5a3162d0346fe Mon Sep 17 00:00:00 2001 From: Marcel Westenberg Aimpulse Date: Mon, 4 May 2026 15:47:51 +0200 Subject: [PATCH 08/10] Added correct link to ZUGFeRD documentation --- .../java/org/mustangproject/ZUGFeRD/ZUGFeRDExporterFromA3.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/src/main/java/org/mustangproject/ZUGFeRD/ZUGFeRDExporterFromA3.java b/library/src/main/java/org/mustangproject/ZUGFeRD/ZUGFeRDExporterFromA3.java index 5a232747..5f85c562 100644 --- a/library/src/main/java/org/mustangproject/ZUGFeRD/ZUGFeRDExporterFromA3.java +++ b/library/src/main/java/org/mustangproject/ZUGFeRD/ZUGFeRDExporterFromA3.java @@ -684,7 +684,7 @@ public class ZUGFeRDExporterFromA3 extends XRExporter implements IZUGFeRDExporte } PDFAttachGenericFile(doc, filename, relationship, - "Invoice metadata conforming to ZUGFeRD standard (http://www.ferd-net.de/front_content.php?idcat=231&lang=4)", + "Invoice metadata conforming to ZUGFeRD standard (https://www.ferd-net.de/en/standards/zugferd/factur-x)", "text/xml", xmlProvider.getXML()); for (FileAttachment attachment : fileAttachments) { From 1af1e2f14a62a030392bc1efe6b9a3b5e5ee2119 Mon Sep 17 00:00:00 2001 From: Marcel Westenberg Aimpulse Date: Mon, 4 May 2026 15:59:52 +0200 Subject: [PATCH 09/10] Added variable for XML_DESCRIPTION string --- .../org/mustangproject/ZUGFeRD/ZUGFeRDExporterFromA3.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/library/src/main/java/org/mustangproject/ZUGFeRD/ZUGFeRDExporterFromA3.java b/library/src/main/java/org/mustangproject/ZUGFeRD/ZUGFeRDExporterFromA3.java index 5f85c562..437e0979 100644 --- a/library/src/main/java/org/mustangproject/ZUGFeRD/ZUGFeRDExporterFromA3.java +++ b/library/src/main/java/org/mustangproject/ZUGFeRD/ZUGFeRDExporterFromA3.java @@ -78,6 +78,8 @@ import jakarta.activation.FileDataSource; public class ZUGFeRDExporterFromA3 extends XRExporter implements IZUGFeRDExporter { + private static final String XML_DESCRIPTION = "Invoice metadata conforming to ZUGFeRD standard (https://www.ferd-net.de/en/standards/zugferd/factur-x)"; + private boolean isFacturX = true; public static final int DefaultZUGFeRDVersion = 2; @@ -684,8 +686,7 @@ public class ZUGFeRDExporterFromA3 extends XRExporter implements IZUGFeRDExporte } PDFAttachGenericFile(doc, filename, relationship, - "Invoice metadata conforming to ZUGFeRD standard (https://www.ferd-net.de/en/standards/zugferd/factur-x)", - "text/xml", xmlProvider.getXML()); + XML_DESCRIPTION, "text/xml", xmlProvider.getXML()); for (FileAttachment attachment : fileAttachments) { PDFAttachGenericFile(doc, attachment.getFilename(), attachment.getRelation(), attachment.getDescription(), attachment.getMimetype(), attachment.getData()); From 189bbda4166017c9fcab97df73ece5e0d18530a0 Mon Sep 17 00:00:00 2001 From: langfr Date: Tue, 5 May 2026 13:36:58 +0100 Subject: [PATCH 10/10] Avoid NPE by checking zi.getXMP() is not null. --- .../java/org/mustangproject/validator/PDFValidator.java | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/validator/src/main/java/org/mustangproject/validator/PDFValidator.java b/validator/src/main/java/org/mustangproject/validator/PDFValidator.java index 08d0906f..993a2d0a 100644 --- a/validator/src/main/java/org/mustangproject/validator/PDFValidator.java +++ b/validator/src/main/java/org/mustangproject/validator/PDFValidator.java @@ -127,7 +127,13 @@ public class PDFValidator extends Validator { final ZUGFeRDImporter zi = new ZUGFeRDImporter(); zi.doIgnoreCalculationErrors();//of course the calculation will still be schematron checked zi.setInputStream(inputStream); - final String xmp = zi.getXMP().substring(zi.getXMP().indexOf('<')); + + String xmp; + if (zi.getXMP() == null) { + xmp = null; + } else { + xmp = zi.getXMP().substring(zi.getXMP().indexOf('<')); + } final Document docXMP;