From 1c5eac2362422c9d42ef057f38014ea3a8167df3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jochen=20St=C3=A4rk?= Date: Thu, 11 Dec 2025 14:22:46 +0100 Subject: [PATCH] closes #984 --- .../main/java/org/mustangproject/Product.java | 3 + .../ZUGFeRD/IZUGFeRDExportableProduct.java | 2 +- .../ZUGFeRD/ZUGFeRD2PullProvider.java | 2 +- .../model/DocumentCodeTypeConstants.java | 1 + .../ZUGFeRD/VATrelatedTest.java | 153 ++++++++++++++++++ .../mustangproject/validator/LibraryTest.java | 38 +++++ 6 files changed, 197 insertions(+), 2 deletions(-) create mode 100644 library/src/test/java/org/mustangproject/ZUGFeRD/VATrelatedTest.java diff --git a/library/src/main/java/org/mustangproject/Product.java b/library/src/main/java/org/mustangproject/Product.java index 8ca3680b..a472ac4a 100644 --- a/library/src/main/java/org/mustangproject/Product.java +++ b/library/src/main/java/org/mustangproject/Product.java @@ -233,6 +233,9 @@ public class Product implements IZUGFeRDExportableProduct { */ public Product setReverseCharge() { isReverseCharge = true; + if ((getTaxExemptionReason()==null)||(getTaxExemptionReason().isEmpty())) { + setTaxExemptionReason("Reverse charge"); + } setVATPercent(BigDecimal.ZERO); return this; } diff --git a/library/src/main/java/org/mustangproject/ZUGFeRD/IZUGFeRDExportableProduct.java b/library/src/main/java/org/mustangproject/ZUGFeRD/IZUGFeRDExportableProduct.java index 6ee5f22e..71402d8b 100644 --- a/library/src/main/java/org/mustangproject/ZUGFeRD/IZUGFeRDExportableProduct.java +++ b/library/src/main/java/org/mustangproject/ZUGFeRD/IZUGFeRDExportableProduct.java @@ -120,7 +120,7 @@ public interface IZUGFeRDExportableProduct { return TaxCategoryCodeTypeConstants.INTRACOMMUNITY;// "K"; // within europe } else if (isReverseCharge()) { return TaxCategoryCodeTypeConstants.REVERSECHARGE;// "AE"; // to out of europe... - } else if (getVATPercent().compareTo(BigDecimal.ZERO) == 0) { + } else if ((getVATPercent()==null)||(getVATPercent().compareTo(BigDecimal.ZERO) == 0)) { return TaxCategoryCodeTypeConstants.ZEROTAXPRODUCTS; // "Z"; // zero rated goods } else { return TaxCategoryCodeTypeConstants.STANDARDRATE;// "S"; // one of the "standard" rates (not diff --git a/library/src/main/java/org/mustangproject/ZUGFeRD/ZUGFeRD2PullProvider.java b/library/src/main/java/org/mustangproject/ZUGFeRD/ZUGFeRD2PullProvider.java index cddadf7a..50565ecc 100644 --- a/library/src/main/java/org/mustangproject/ZUGFeRD/ZUGFeRD2PullProvider.java +++ b/library/src/main/java/org/mustangproject/ZUGFeRD/ZUGFeRD2PullProvider.java @@ -538,7 +538,7 @@ public class ZUGFeRD2PullProvider implements IXMLProvider { xml += "" + XMLTools.encodeXML(currentItem.getProduct().getTaxExemptionReason()) + ""; } xml += "" + currentItem.getProduct().getTaxCategoryCode() + ""; - if (!currentItem.getProduct().getTaxCategoryCode().equals(TaxCategoryCodeTypeConstants.UNTAXEDSERVICE)) { + if ((!currentItem.getProduct().getTaxCategoryCode().equals(TaxCategoryCodeTypeConstants.UNTAXEDSERVICE)) && (!currentItem.getProduct().getTaxCategoryCode().equals(TaxCategoryCodeTypeConstants.ZEROTAXPRODUCTS))) { xml += "" + vatFormat(currentItem.getProduct().getVATPercent()) + ""; } xml += ""; diff --git a/library/src/main/java/org/mustangproject/ZUGFeRD/model/DocumentCodeTypeConstants.java b/library/src/main/java/org/mustangproject/ZUGFeRD/model/DocumentCodeTypeConstants.java index 74fac56a..61cfd66a 100644 --- a/library/src/main/java/org/mustangproject/ZUGFeRD/model/DocumentCodeTypeConstants.java +++ b/library/src/main/java/org/mustangproject/ZUGFeRD/model/DocumentCodeTypeConstants.java @@ -23,5 +23,6 @@ public class DocumentCodeTypeConstants { public static final String CREDITNOTE = "381"; public static final String DEBITNOTE = "84"; public static final String CORRECTEDINVOICE = "384"; + public static final String SELFBILLING = "389"; public static final String PARTIAL_BILLING = "326"; } diff --git a/library/src/test/java/org/mustangproject/ZUGFeRD/VATrelatedTest.java b/library/src/test/java/org/mustangproject/ZUGFeRD/VATrelatedTest.java new file mode 100644 index 00000000..7cd41ead --- /dev/null +++ b/library/src/test/java/org/mustangproject/ZUGFeRD/VATrelatedTest.java @@ -0,0 +1,153 @@ + +/** ********************************************************************** + * + * Copyright 2019 Jochen Staerk + * + * Use is subject to license terms. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy + * of the License at http://www.apache.org/licenses/LICENSE-2.0. + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + * See the License for the specific language governing permissions and + * limitations under the License. + * + *********************************************************************** */ +package org.mustangproject.ZUGFeRD; + +import com.fasterxml.jackson.databind.ObjectMapper; +import org.junit.FixMethodOrder; +import org.junit.runners.MethodSorters; +import org.mustangproject.*; + +import java.io.IOException; +import java.io.InputStream; +import java.math.BigDecimal; +import java.nio.charset.StandardCharsets; +import java.util.Date; + +@FixMethodOrder(MethodSorters.NAME_ASCENDING) +public class VATrelatedTest extends ResourceCase { + final String TARGET_PDF_REVERSE = "./target/testout-ReverseCharge.pdf"; + final String TARGET_PDF_Z = "./target/testout-TaxcodeZ.pdf"; + + public void testReverseCharge() { + + String orgname = "Test company"; + String number = "123"; + String priceStr = "3.00"; + BigDecimal price = new BigDecimal(priceStr); + try { + InputStream SOURCE_PDF = this.getClass().getResourceAsStream("/MustangGnuaccountingBeispielRE-20170509_505blanko.pdf"); + + ZUGFeRDExporterFromA1 ze = new ZUGFeRDExporterFromA1(); + ze.ignorePDFAErrors().load(SOURCE_PDF); + ze.setProfile(Profiles.getByName("Extended")); + + ze.setProducer("My Application").setCreator(System.getProperty("user.name")).setZUGFeRDVersion(2).setProfile("extended"); + // ze.setTransaction(new Invoice().setDueDate(new Date()).setIssueDate(new Date()).setDeliveryDate(new Date()).setSender(new TradeParty(orgname,"teststr", "55232","teststadt","DE")).setOwnTaxID("4711").setOwnVATID("DE0815").setRecipient(new TradeParty("Franz Müller", "teststr.12", "55232", "Entenhausen", "DE")).setNumber(number) + // .addItem(new Item(new Product("Testprodukt", "", "H84", new BigDecimal(19)), amount, new BigDecimal(1.0)).addAllowance(new Allowance().setPercent(new BigDecimal(50))))); + + Product p=new Product("Testprodukt", "", "H87", new BigDecimal(19)); + p.setReverseCharge(); + Invoice i = new Invoice().setDueDate(new Date()).setIssueDate(new Date()).setDeliveryDate(new Date()) + .setSender(new TradeParty(orgname, "teststr", "55232", "teststadt", "DE").addTaxID("4711").addVATID("DE0815")) + .setRecipient(new TradeParty("Franz Müller", "teststr.12", "55232", "Entenhausen", "DE") + .setContact(new Contact("contact testname", "123456", "contact.testemail@example.org").setFax("0911623562")).addVATID("DE0915")) + .setNumber(number) + .addCharge(new Charge(new BigDecimal(1)).setReasonCode("ABK").setReason("AReason").setTaxPercent(new BigDecimal(19))) + + .addItem(new Item(p, price, new BigDecimal(1.0))); + ze.setTransaction(i); + + + String theXML = new String(ze.getProvider().getXML(), StandardCharsets.UTF_8); + assertTrue(theXML.contains("