Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Having two items with different VATs and adding a charge/allowance will generate an invalid or wrong XML #640

Open
FrankHossfeld opened this issue Jan 2, 2025 · 12 comments

Comments

@FrankHossfeld
Copy link

FrankHossfeld commented Jan 2, 2025

It looks like that there is an issue when having an invoice with two items and different VAT. Adding a charge/allowance will generate an invalid XML or has a wrong calculation. A n invoice where all items have the same VAT works as expected.

Generate an invoice with the following two items:

  • item 1: quantity: 10, price: 10, VAT 19 %

  • item 2: quantity: 1, price: 10, VAT: 19 %

  • invoice with two items and same VAT without a charge/allowance -> works (XML is valid)

  • invoice with two items and same VAT with a charge/allowance -> works (XML is valid)

  • invoice with two items (one item 19% VAT, second 7 % VAT) without a charge/allowance -> works (XML is valid)

  • invoice with two items (one item 19% VAT, second 7 % VAT) with a charge/allowance -> works (XML is invalid or calculation is wrong depending on the input)

  1. Setting up a charge with reason and percent and without VAT will result in an invalid XML
  2. Setting up a charge with reason, percent and VAT will result in an valid XML but a wrong calculation

In the first case the XML for the charges look like this:

      <ram:SpecifiedTradeAllowanceCharge>
        <ram:ChargeIndicator>
          <udt:Indicator>true</udt:Indicator>
        </ram:ChargeIndicator>
        <ram:ActualAmount>11.00</ram:ActualAmount>
        <ram:Reason>Teuerungszuschlag</ram:Reason>
        <ram:CategoryTradeTax>
          <ram:TypeCode>VAT</ram:TypeCode>
          <ram:CategoryCode>S</ram:CategoryCode>
          <ram:RateApplicablePercent>0.00</ram:RateApplicablePercent>
        </ram:CategoryTradeTax>
      </ram:SpecifiedTradeAllowanceCharge>

The XML is invalid due to the fact that the tax percents are missing/is by default BigDEcimal.ZERO.

In the second case adding two charges (one for VAT 19% and a second one for 7 %) generates the following XML:

      <ram:SpecifiedTradeAllowanceCharge>
        <ram:ChargeIndicator>
          <udt:Indicator>true</udt:Indicator>
        </ram:ChargeIndicator>
        <ram:ActualAmount>11.00</ram:ActualAmount>
        <ram:Reason>Teuerungszuschlag</ram:Reason>
        <ram:CategoryTradeTax>
          <ram:TypeCode>VAT</ram:TypeCode>
          <ram:CategoryCode>S</ram:CategoryCode>
          <ram:RateApplicablePercent>7.00</ram:RateApplicablePercent>
        </ram:CategoryTradeTax>
      </ram:SpecifiedTradeAllowanceCharge>
      <ram:SpecifiedTradeAllowanceCharge>
        <ram:ChargeIndicator>
          <udt:Indicator>true</udt:Indicator>
        </ram:ChargeIndicator>
        <ram:ActualAmount>11.00</ram:ActualAmount>
        <ram:Reason>Teuerungszuschlag</ram:Reason>
        <ram:CategoryTradeTax>
          <ram:TypeCode>VAT</ram:TypeCode>
          <ram:CategoryCode>S</ram:CategoryCode>
          <ram:RateApplicablePercent>19.00</ram:RateApplicablePercent>
        </ram:CategoryTradeTax>
      </ram:SpecifiedTradeAllowanceCharge>

Now, the XML is valid, but the totalamount is now the amount of all items and not the sum of items with VAT 19 % or VAT 7%.

Expected behavior

Setting up a charge using

          invoice.addAllowance(new Allowance().setReason(model.getText())
                                              .setPercent(model.getValue()));

should result in the following XML:

      <ram:SpecifiedTradeAllowanceCharge>
        <ram:ChargeIndicator>
          <udt:Indicator>true</udt:Indicator>
        </ram:ChargeIndicator>
        <ram:ActualAmount>1.00</ram:ActualAmount>
        <ram:Reason>charge me</ram:Reason>
        <ram:CategoryTradeTax>
          <ram:TypeCode>VAT</ram:TypeCode>
          <ram:CategoryCode>S</ram:CategoryCode>
          <ram:RateApplicablePercent>7.00</ram:RateApplicablePercent>
        </ram:CategoryTradeTax>
      </ram:SpecifiedTradeAllowanceCharge>
      <ram:SpecifiedTradeAllowanceCharge>
        <ram:ChargeIndicator>
          <udt:Indicator>true</udt:Indicator>
        </ram:ChargeIndicator>
        <ram:ActualAmount>10.00</ram:ActualAmount>
        <ram:Reason>charge me</ram:Reason>
        <ram:CategoryTradeTax>
          <ram:TypeCode>VAT</ram:TypeCode>
          <ram:CategoryCode>S</ram:CategoryCode>
          <ram:RateApplicablePercent>19.00</ram:RateApplicablePercent>
        </ram:CategoryTradeTax>
      </ram:SpecifiedTradeAllowanceCharge>

macOS, Java 17 & version 2.15.2

@FrankHossfeld FrankHossfeld changed the title Having two items with different VATs and adding a charge/allowance will generate a invalid XML Having two items with different VATs and adding a charge/allowance will generate an invalid XML Jan 2, 2025
@FrankHossfeld
Copy link
Author

FrankHossfeld commented Jan 2, 2025

I looked into the code and I think the problem is related to the fact, that the getTotal-method inside the TransactionCalculator-class does not respect the the VAT, the charge is calculated for.

In the method getVATPercentAmountMap charges and allowance will be calculated using this code (showing it for charges):

    IZUGFeRDAllowanceCharge[] charges = this.trans.getZFCharges();
    if (charges != null && charges.length > 0) {
      for(IZUGFeRDAllowanceCharge currentCharge : charges) {
        BigDecimal taxPercent = currentCharge.getTaxPercent();
        if (taxPercent != null) {
          VATAmount theAmount = (VATAmount)hm.get(taxPercent.stripTrailingZeros());
          if (theAmount == null) {
            theAmount = new VATAmount(BigDecimal.ZERO, BigDecimal.ZERO, currentCharge.getCategoryCode() != null ? currentCharge.getCategoryCode() : "S", vatDueDateTypeCode);
          }

          theAmount.setBasis(theAmount.getBasis().add(currentCharge.getTotalAmount(this)));
          BigDecimal factor = taxPercent.divide(new BigDecimal(100));
          theAmount.setCalculated(theAmount.getBasis().multiply(factor));
          hm.put(taxPercent.stripTrailingZeros(), theAmount);
        }
      }
    }

First confusing part is, that the charges needs to have set the taxPercent. where as all VATs can be get from the keys of the hm-map. So there is no need to add the taxPercent to the charges class. Actually if not set, BigDecimal.ZERO is used as taxPercent.

Second, if percent are set inside the Charges-class, the line of code will be used to calculate the total amount:

      return currentItem.getValue().multiply(this.getPercent().divide(new BigDecimal(100)));

This code is problematic, because currentItem.getValue()will call the method inside the TransactionCalculator-class, which calls getTotal() (line 162) which will return the amount for all items without respecting the VAT. This will cause problems, in case items have different VATs.

Setting the totalAmount to the calculated charge will work as expected.

In case it is a bug, I can create a PR.

@FrankHossfeld FrankHossfeld changed the title Having two items with different VATs and adding a charge/allowance will generate an invalid XML Having two items with different VATs and adding a charge/allowance will generate an invalid or wrong XML Jan 2, 2025
@jstaerk
Copy link
Collaborator

jstaerk commented Jan 7, 2025

Hi,
would you maybe have a short source code snippet available which I could use to
validate the issue and make a testcase out of it?
thanks
Jochen

@FrankHossfeld
Copy link
Author

FrankHossfeld commented Jan 9, 2025

Hi,
what is the preferred way using charges/allowance? I mean, which information needs to be set.
Thanks

@jstaerk
Copy link
Collaborator

jstaerk commented Jan 9, 2025

Hi, what is the preferred way using charges/allowance? I mean, which information needs to be set. Thanks

BigDecimal itemVATPercent=new BigDecimal("19");
BigDecimal theAmount=new BigDecimal("10");
item.addCharge(new Charge(theamount).setTaxPercent(itemVATPercent));

puts a charge of 10€ while I believe

BigDecimal thePercent=new BigDecimal("10");
item.addCharge(new Charge().setPercent(thePercent).setTaxPercent(itemVATPercent));

adds a 10% charge, each on 19% VAT items. Maybe you were missing the setTaxPercent part?

@FrankHossfeld
Copy link
Author

That's the way I am doing it:

      for (RechnungNachlassZuschlagModelForPrinting model : nachlassZuschlagModels) {
        if (RechnungNachlassZuschlag.Type.NACHLASS == model.getType()) {
          invoice.addAllowance(new Allowance(model.getNachlassZuschlagBetrag()).setReason(model.getText())
                                                                               .setPercent(model.getValue())
                                                                               .setTaxPercent(model.getSteuersatz())
                                                                               .setReasonCode("95"));
        } else {
          invoice.addCharge(new Charge(model.getNachlassZuschlagBetrag()).setReason(model.getText())
                                                                         .setPercent(model.getValue())
                                                                         .setTaxPercent(model.getSteuersatz())
                                                                         .setReasonCode("104"));
        }

this is working as long as all items have the same VAT. Having items with different VATs fail.

I'll check it again and try to set up a test case in case I don't find an issue in my code.

@FrankHossfeld
Copy link
Author

FrankHossfeld commented Jan 10, 2025

This bug gets more and more interesting!

I'll created two invoices. Both have 2 items:

  1. Item 01 - (quantity: 10, price: 10,00 €)
  2. Item 02 - (quantity: 1, price:10,00 €)

and a charge of 10 %.

Inside the first invoice both items have a VAT of 19 % and in opposite to the first one, the second invoice has a second item with a VAT of 7 %;

I attached both invoices as JSON:

invoice-ok.json

invoice-fail.json

the only difference is the VAT of the second item. These JSONs can be used as an input for a test case.

Let's say, that invoice 01 is the one where both items have a VAT of 19% and invoice 02 the one where the second item has a VAT of 7%. To validate the pdf and the xml I used this URL: https://erechnungs-validator.de/

The invoice 01 looks like that:
RechnungV2Issue640-ok.pdf

Everything is fine and valid.

The correct version of Invoice 02 looks like that:
RechnungV2Issue640-fail.pdf

The Grand total of the invoice is 142.67 €, which is right.

This is a pdf, where the generated XML of invoice 02 is used for creating a PDF for visualization:

RechnungV2Issue640-fail-pdf-generated-from-xml.pdf

This one has a grand total of 154.56 €, which is wrong (it should be 142.67 €) due to a bug in the charge and VAT calculation, I think.

Funny thing is, that all XMLs & PDfs are valid!

@FrankHossfeld
Copy link
Author

FrankHossfeld commented Jan 10, 2025

v2.16.0 does not fix it.

@jstaerk
Copy link
Collaborator

jstaerk commented Jan 13, 2025

Thanks, I'll have a look tonight

@jstaerk
Copy link
Collaborator

jstaerk commented Jan 13, 2025

Unfortunately I cant reproduce the XML from your JSON (wrong date format, and then other problems), and the source code is not complete but when I try it myself with the following java

package de.usegroup;

import org.mustangproject.*;
import org.mustangproject.ZUGFeRD.Profiles;
import org.mustangproject.ZUGFeRD.ZUGFeRD2PullProvider;

import java.io.File;
import java.io.IOException;
import java.math.BigDecimal;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.util.Date;

public class Main {
    public static void main(String[] args) {
        System.out.println("Hello world!");


        BigDecimal thePercent=new BigDecimal("10");
        BigDecimal vat1=new BigDecimal("19");
        BigDecimal vat2=new BigDecimal("7");

        TradeParty recipient = new TradeParty("Franz Müller", "teststr.12", "55232", "Entenhausen", "DE").setEmail("[email protected]");
        Invoice i = new Invoice().setDueDate(new Date()).setIssueDate(new
                        Date()).setDeliveryDate(new Date())
                .setSender(new TradeParty("Test company", "teststr", "55232", "teststadt", "DE").addTaxID("DE4711").addVATID("DE0815").setContact(new org.mustangproject.Contact("Hans Test", "+49123456789", "[email protected]")).addBankDetails(new org.mustangproject.BankDetails("DE12500105170648489890", "COBADEFXXX")).setEmail("[email protected]"))
                .setRecipient(recipient)
                .setReferenceNumber("991-01484-64")//leitweg-id
                .setNumber("123")
                .addItem(new Item(new Product("Testprodukt", "", "C62", vat1), new BigDecimal("10.00"), new BigDecimal(10.0)).addCharge(new Charge().setPercent(thePercent).setTaxPercent(vat1)))
                .addItem(new Item(new Product("Testprodukt", "", "C62", vat2), new BigDecimal("1.00"), new BigDecimal(10.0)).addCharge(new Charge().setPercent(thePercent).setTaxPercent(vat2))
                        );

        ZUGFeRD2PullProvider zf2p = new ZUGFeRD2PullProvider();
        zf2p.setProfile(Profiles.getByName("XRechnung"));
        zf2p.generateXML(i);

        try {
            Files.write(new File("xr.xml").toPath(), zf2p.getXML());
        } catch (IOException e) {
            throw new RuntimeException(e);
        }

    }
}

the following XML is produced

<?xml version="1.0" encoding="UTF-8"?>

<rsm:CrossIndustryInvoice xmlns:rsm="urn:un:unece:uncefact:data:standard:CrossIndustryInvoice:100" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:ram="urn:un:unece:uncefact:data:standard:ReusableAggregateBusinessInformationEntity:100" xmlns:udt="urn:un:unece:uncefact:data:standard:UnqualifiedDataType:100" xmlns:qdt="urn:un:unece:uncefact:data:standard:QualifiedDataType:100">
  <!-- generated by: mustangproject.org v2.16.0-->
  <rsm:ExchangedDocumentContext>
 
    <ram:BusinessProcessSpecifiedDocumentContextParameter>
 
      <ram:ID>urn:fdc:peppol.eu:2017:poacc:billing:01:1.0</ram:ID>

    </ram:BusinessProcessSpecifiedDocumentContextParameter>
 
    <ram:GuidelineSpecifiedDocumentContextParameter>
      <ram:ID>urn:cen.eu:en16931:2017#compliant#urn:xeinkauf.de:kosit:xrechnung_3.0</ram:ID>
    </ram:GuidelineSpecifiedDocumentContextParameter>
  </rsm:ExchangedDocumentContext>
  <rsm:ExchangedDocument>
    <ram:ID>123</ram:ID>
    <ram:TypeCode>380</ram:TypeCode>
    <ram:IssueDateTime>
      <udt:DateTimeString format="102">20250113</udt:DateTimeString>
    </ram:IssueDateTime>
  </rsm:ExchangedDocument>
  <rsm:SupplyChainTradeTransaction>
    <ram:IncludedSupplyChainTradeLineItem>
      <ram:AssociatedDocumentLineDocument>
        <ram:LineID>1</ram:LineID>
      </ram:AssociatedDocumentLineDocument>
      <ram:SpecifiedTradeProduct>
        <ram:Name>Testprodukt</ram:Name>
      </ram:SpecifiedTradeProduct>
      <ram:SpecifiedLineTradeAgreement>
        <ram:GrossPriceProductTradePrice>
          <ram:ChargeAmount>10.0000</ram:ChargeAmount>
          <ram:BasisQuantity unitCode="C62">1.0000</ram:BasisQuantity>
          <ram:AppliedTradeAllowanceCharge>
            <ram:ChargeIndicator>
              <udt:Indicator>true</udt:Indicator>
            </ram:ChargeIndicator>
            <ram:ActualAmount>1.0000</ram:ActualAmount>
          </ram:AppliedTradeAllowanceCharge>
        </ram:GrossPriceProductTradePrice>
        <ram:NetPriceProductTradePrice>
          <ram:ChargeAmount>11.0000</ram:ChargeAmount>
          <ram:BasisQuantity unitCode="C62">1.0000</ram:BasisQuantity>
        </ram:NetPriceProductTradePrice>
      </ram:SpecifiedLineTradeAgreement>
      <ram:SpecifiedLineTradeDelivery>
        <ram:BilledQuantity unitCode="C62">10.0000</ram:BilledQuantity>
      </ram:SpecifiedLineTradeDelivery>
      <ram:SpecifiedLineTradeSettlement>
        <ram:ApplicableTradeTax>
          <ram:TypeCode>VAT</ram:TypeCode>
          <ram:CategoryCode>S</ram:CategoryCode>
          <ram:RateApplicablePercent>19.00</ram:RateApplicablePercent>
        </ram:ApplicableTradeTax>
        <ram:SpecifiedTradeSettlementLineMonetarySummation>
          <ram:LineTotalAmount>110.00</ram:LineTotalAmount>
        </ram:SpecifiedTradeSettlementLineMonetarySummation>
      </ram:SpecifiedLineTradeSettlement>
    </ram:IncludedSupplyChainTradeLineItem>
    <ram:IncludedSupplyChainTradeLineItem>
      <ram:AssociatedDocumentLineDocument>
        <ram:LineID>2</ram:LineID>
      </ram:AssociatedDocumentLineDocument>
      <ram:SpecifiedTradeProduct>
        <ram:Name>Testprodukt</ram:Name>
      </ram:SpecifiedTradeProduct>
      <ram:SpecifiedLineTradeAgreement>
        <ram:GrossPriceProductTradePrice>
          <ram:ChargeAmount>1.0000</ram:ChargeAmount>
          <ram:BasisQuantity unitCode="C62">1.0000</ram:BasisQuantity>
          <ram:AppliedTradeAllowanceCharge>
            <ram:ChargeIndicator>
              <udt:Indicator>true</udt:Indicator>
            </ram:ChargeIndicator>
            <ram:ActualAmount>0.1000</ram:ActualAmount>
          </ram:AppliedTradeAllowanceCharge>
        </ram:GrossPriceProductTradePrice>
        <ram:NetPriceProductTradePrice>
          <ram:ChargeAmount>1.1000</ram:ChargeAmount>
          <ram:BasisQuantity unitCode="C62">1.0000</ram:BasisQuantity>
        </ram:NetPriceProductTradePrice>
      </ram:SpecifiedLineTradeAgreement>
      <ram:SpecifiedLineTradeDelivery>
        <ram:BilledQuantity unitCode="C62">10.0000</ram:BilledQuantity>
      </ram:SpecifiedLineTradeDelivery>
      <ram:SpecifiedLineTradeSettlement>
        <ram:ApplicableTradeTax>
          <ram:TypeCode>VAT</ram:TypeCode>
          <ram:CategoryCode>S</ram:CategoryCode>
          <ram:RateApplicablePercent>7.00</ram:RateApplicablePercent>
        </ram:ApplicableTradeTax>
        <ram:SpecifiedTradeSettlementLineMonetarySummation>
          <ram:LineTotalAmount>11.00</ram:LineTotalAmount>
        </ram:SpecifiedTradeSettlementLineMonetarySummation>
      </ram:SpecifiedLineTradeSettlement>
    </ram:IncludedSupplyChainTradeLineItem>
    <ram:ApplicableHeaderTradeAgreement>
      <ram:BuyerReference>991-01484-64</ram:BuyerReference>
      <ram:SellerTradeParty>
        <ram:Name>Test company</ram:Name>
        <ram:DefinedTradeContact>
          <ram:PersonName>Hans Test</ram:PersonName>
          <ram:TelephoneUniversalCommunication>
            <ram:CompleteNumber>+49123456789</ram:CompleteNumber>
          </ram:TelephoneUniversalCommunication>
          <ram:EmailURIUniversalCommunication>
            <ram:URIID>[email protected]</ram:URIID>
          </ram:EmailURIUniversalCommunication>
        </ram:DefinedTradeContact>
        <ram:PostalTradeAddress>
          <ram:PostcodeCode>55232</ram:PostcodeCode>
          <ram:LineOne>teststr</ram:LineOne>
          <ram:CityName>teststadt</ram:CityName>
          <ram:CountryID>DE</ram:CountryID>
        </ram:PostalTradeAddress>
        <ram:URIUniversalCommunication>
          <ram:URIID schemeID="EM">[email protected]</ram:URIID>
        </ram:URIUniversalCommunication>
        <ram:SpecifiedTaxRegistration>
          <ram:ID schemeID="VA">DE0815</ram:ID>
        </ram:SpecifiedTaxRegistration>
        <ram:SpecifiedTaxRegistration>
          <ram:ID schemeID="FC">DE4711</ram:ID>
        </ram:SpecifiedTaxRegistration>
      </ram:SellerTradeParty>
      <ram:BuyerTradeParty>
        <ram:Name>Franz Müller</ram:Name>
        <ram:PostalTradeAddress>
          <ram:PostcodeCode>55232</ram:PostcodeCode>
          <ram:LineOne>teststr.12</ram:LineOne>
          <ram:CityName>Entenhausen</ram:CityName>
          <ram:CountryID>DE</ram:CountryID>
        </ram:PostalTradeAddress>
        <ram:URIUniversalCommunication>
          <ram:URIID schemeID="EM">[email protected]</ram:URIID>
        </ram:URIUniversalCommunication>
      </ram:BuyerTradeParty>
    </ram:ApplicableHeaderTradeAgreement>
    <ram:ApplicableHeaderTradeDelivery>
      <ram:ActualDeliverySupplyChainEvent>
        <ram:OccurrenceDateTime>
          <udt:DateTimeString format="102">20250113</udt:DateTimeString>
        </ram:OccurrenceDateTime>
      </ram:ActualDeliverySupplyChainEvent>
    </ram:ApplicableHeaderTradeDelivery>
    <ram:ApplicableHeaderTradeSettlement>
      <ram:PaymentReference>123</ram:PaymentReference>
      <ram:InvoiceCurrencyCode>EUR</ram:InvoiceCurrencyCode>
      <ram:SpecifiedTradeSettlementPaymentMeans>
        <ram:TypeCode>58</ram:TypeCode>
        <ram:Information>SEPA credit transfer</ram:Information>
        <ram:PayeePartyCreditorFinancialAccount>
          <ram:IBANID>DE12500105170648489890</ram:IBANID>
        </ram:PayeePartyCreditorFinancialAccount>
        <ram:PayeeSpecifiedCreditorFinancialInstitution>
          <ram:BICID>COBADEFXXX</ram:BICID>
        </ram:PayeeSpecifiedCreditorFinancialInstitution>
      </ram:SpecifiedTradeSettlementPaymentMeans>
      <ram:ApplicableTradeTax>
        <ram:CalculatedAmount>0.77</ram:CalculatedAmount>
        <ram:TypeCode>VAT</ram:TypeCode>
        <ram:BasisAmount>11.00</ram:BasisAmount>
        <ram:CategoryCode>S</ram:CategoryCode>
        <ram:RateApplicablePercent>7.00</ram:RateApplicablePercent>
      </ram:ApplicableTradeTax>
      <ram:ApplicableTradeTax>
        <ram:CalculatedAmount>20.90</ram:CalculatedAmount>
        <ram:TypeCode>VAT</ram:TypeCode>
        <ram:BasisAmount>110.00</ram:BasisAmount>
        <ram:CategoryCode>S</ram:CategoryCode>
        <ram:RateApplicablePercent>19.00</ram:RateApplicablePercent>
      </ram:ApplicableTradeTax>
      <ram:SpecifiedTradePaymentTerms>
        <ram:Description>Please remit until 13.01.2025</ram:Description>
        <ram:DueDateDateTime>
          <udt:DateTimeString format="102">20250113</udt:DateTimeString>
        </ram:DueDateDateTime>
      </ram:SpecifiedTradePaymentTerms>
      <ram:SpecifiedTradeSettlementHeaderMonetarySummation>
        <ram:LineTotalAmount>121.00</ram:LineTotalAmount>
        <ram:ChargeTotalAmount>0.00</ram:ChargeTotalAmount>
        <ram:AllowanceTotalAmount>0.00</ram:AllowanceTotalAmount>
        <ram:TaxBasisTotalAmount>121.00</ram:TaxBasisTotalAmount>
        <ram:TaxTotalAmount currencyID="EUR">21.67</ram:TaxTotalAmount>
        <ram:GrandTotalAmount>142.67</ram:GrandTotalAmount>
        <ram:TotalPrepaidAmount>0.00</ram:TotalPrepaidAmount>
        <ram:DuePayableAmount>142.67</ram:DuePayableAmount>
      </ram:SpecifiedTradeSettlementHeaderMonetarySummation>
    </ram:ApplicableHeaderTradeSettlement>
  </rsm:SupplyChainTradeTransaction>
</rsm:CrossIndustryInvoice>

which has the correct 142,67 and is valid: what precisely was/is the issue?

@FrankHossfeld
Copy link
Author

FrankHossfeld commented Jan 13, 2025

Unfortunately I cant reproduce the XML from your JSON (wrong date format, and then other problems), and the source code is not complete but when I try it myself with the following java

Yeah, the class model of the invoice class can't be used with Gson.

In your code example you are adding the charge on item level. To reproduce the issue, add a charge on document level.

Something like that:

    Invoice i = new Invoice().setDueDate(new Date())
                             .setIssueDate(new Date())
                             .setDeliveryDate(new Date())
                             .setSender(new TradeParty("Test company",
                                                       "teststr",
                                                       "55232",
                                                       "teststadt",
                                                       "DE").addTaxID("DE4711")
                                                            .addVATID("DE0815")
                                                            .setContact(new org.mustangproject.Contact("Hans Test",
                                                                                                       "+49123456789",
                                                                                                       "[email protected]"))
                                                            .addBankDetails(new org.mustangproject.BankDetails("DE12500105170648489890",
                                                                                                               "COBADEFXXX"))
                                                            .setEmail("[email protected]"))
                             .setRecipient(recipient)
                             .setReferenceNumber("991-01484-64")//leitweg-id
                             .setNumber("123")
                             .addItem(new Item(new Product("Testprodukt",
                                                           "",
                                                           "C62",
                                                           vat1),
                                               new BigDecimal("10.00"),
                                               new BigDecimal(10.0)))
                             .addItem(new Item(new Product("Testprodukt",
                                                           "",
                                                           "C62",
                                                           vat2),
                                               new BigDecimal("1.00"),
                                               new BigDecimal(10.0)))
                             .addCharge(new Charge().setPercent(thePercent)
                                                    .setTaxPercent(vat1))
                             .addCharge(new Charge().setPercent(thePercent)
                                                    .setTaxPercent(vat2));

BTW, in both examples I have to set the tax percent. Which seams to be is not necessary, bc on line charges (allowance) the VAT from the item can be used and on document level, the projects needs to create the sums for each items with the same VATs, to calculate the netto sum, to add the charge (allowance) to it and to calculate the VAT correctly. In both cases, the project knows the VAT. Or, do I miss something?

@FrankHossfeld
Copy link
Author

FrankHossfeld commented Jan 18, 2025

Please use this class to reproduce the issue (I had to add a reason to generate a valid XML):

package de.usegroup;

import org.mustangproject.Charge;
import org.mustangproject.Invoice;
import org.mustangproject.Item;
import org.mustangproject.Product;
import org.mustangproject.TradeParty;
import org.mustangproject.ZUGFeRD.Profiles;
import org.mustangproject.ZUGFeRD.ZUGFeRD2PullProvider;

import java.io.File;
import java.io.IOException;
import java.math.BigDecimal;
import java.nio.file.Files;
import java.util.Date;

public class ChargesOnDocumentLevel {
  public static void main(String[] args) {
    BigDecimal thePercent = new BigDecimal("10");
    BigDecimal vat1       = new BigDecimal("19");
    BigDecimal vat2       = new BigDecimal("7");

    TradeParty recipient = new TradeParty("Franz Müller",
                                          "teststr.12",
                                          "55232",
                                          "Entenhausen",
                                          "DE").setEmail("[email protected]");
    Invoice i = new Invoice().setDueDate(new Date())
                             .setIssueDate(new Date())
                             .setDeliveryDate(new Date())
                             .setSender(new TradeParty("Test company",
                                                       "teststr",
                                                       "55232",
                                                       "teststadt",
                                                       "DE").addTaxID("DE4711")
                                                            .addVATID("DE0815")
                                                            .setContact(new org.mustangproject.Contact("Hans Test",
                                                                                                       "+49123456789",
                                                                                                       "[email protected]"))
                                                            .addBankDetails(new org.mustangproject.BankDetails("DE12500105170648489890",
                                                                                                               "COBADEFXXX"))
                                                            .setEmail("[email protected]"))
                             .setRecipient(recipient)
                             .setReferenceNumber("991-01484-64")//leitweg-id
                             .setNumber("123")
                             .addItem(new Item(new Product("Testprodukt",
                                                           "",
                                                           "C62",
                                                           vat1),
                                               new BigDecimal("10.00"),
                                               new BigDecimal("10.0")))
                             .addItem(new Item(new Product("Testprodukt",
                                                           "",
                                                           "C62",
                                                           vat2),
                                               new BigDecimal("1.00"),
                                               new BigDecimal("10.0")))
                             .addCharge(new Charge().setPercent(thePercent)
                                                    .setTaxPercent(vat1)
                                                    .setReason("Teuerungszuschlag"))
                             .addCharge(new Charge().setPercent(thePercent)
                                                    .setTaxPercent(vat2)
                                                    .setReason("Teuerungszuschlag"));

    ZUGFeRD2PullProvider zf2p = new ZUGFeRD2PullProvider();
    zf2p.setProfile(Profiles.getByName("XRechnung"));
    zf2p.generateXML(i);

    try {
      Files.write(new File("ChargesOnDocumentLevel.xml").toPath(),
                  zf2p.getXML());
    } catch (IOException e) {
      throw new RuntimeException(e);
    }

  }
}

which will generate the following XML:

<?xml version="1.0" encoding="UTF-8"?>

<rsm:CrossIndustryInvoice xmlns:rsm="urn:un:unece:uncefact:data:standard:CrossIndustryInvoice:100" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:ram="urn:un:unece:uncefact:data:standard:ReusableAggregateBusinessInformationEntity:100" xmlns:udt="urn:un:unece:uncefact:data:standard:UnqualifiedDataType:100" xmlns:qdt="urn:un:unece:uncefact:data:standard:QualifiedDataType:100">
  <!-- generated by: mustangproject.org vnull-->
  <rsm:ExchangedDocumentContext>
 
    <ram:BusinessProcessSpecifiedDocumentContextParameter>
 
      <ram:ID>urn:fdc:peppol.eu:2017:poacc:billing:01:1.0</ram:ID>

    </ram:BusinessProcessSpecifiedDocumentContextParameter>
 
    <ram:GuidelineSpecifiedDocumentContextParameter>
      <ram:ID>urn:cen.eu:en16931:2017#compliant#urn:xeinkauf.de:kosit:xrechnung_3.0</ram:ID>
    </ram:GuidelineSpecifiedDocumentContextParameter>
  </rsm:ExchangedDocumentContext>
  <rsm:ExchangedDocument>
    <ram:ID>123</ram:ID>
    <ram:TypeCode>380</ram:TypeCode>
    <ram:IssueDateTime>
      <udt:DateTimeString format="102">20250118</udt:DateTimeString>
    </ram:IssueDateTime>
  </rsm:ExchangedDocument>
  <rsm:SupplyChainTradeTransaction>
    <ram:IncludedSupplyChainTradeLineItem>
      <ram:AssociatedDocumentLineDocument>
        <ram:LineID>1</ram:LineID>
      </ram:AssociatedDocumentLineDocument>
      <ram:SpecifiedTradeProduct>
        <ram:Name>Testprodukt</ram:Name>
      </ram:SpecifiedTradeProduct>
      <ram:SpecifiedLineTradeAgreement>
        <ram:NetPriceProductTradePrice>
          <ram:ChargeAmount>10.0000</ram:ChargeAmount>
          <ram:BasisQuantity unitCode="C62">1.0000</ram:BasisQuantity>
        </ram:NetPriceProductTradePrice>
      </ram:SpecifiedLineTradeAgreement>
      <ram:SpecifiedLineTradeDelivery>
        <ram:BilledQuantity unitCode="C62">10.0000</ram:BilledQuantity>
      </ram:SpecifiedLineTradeDelivery>
      <ram:SpecifiedLineTradeSettlement>
        <ram:ApplicableTradeTax>
          <ram:TypeCode>VAT</ram:TypeCode>
          <ram:CategoryCode>S</ram:CategoryCode>
          <ram:RateApplicablePercent>19.00</ram:RateApplicablePercent>
        </ram:ApplicableTradeTax>
        <ram:SpecifiedTradeSettlementLineMonetarySummation>
          <ram:LineTotalAmount>100.00</ram:LineTotalAmount>
        </ram:SpecifiedTradeSettlementLineMonetarySummation>
      </ram:SpecifiedLineTradeSettlement>
    </ram:IncludedSupplyChainTradeLineItem>
    <ram:IncludedSupplyChainTradeLineItem>
      <ram:AssociatedDocumentLineDocument>
        <ram:LineID>2</ram:LineID>
      </ram:AssociatedDocumentLineDocument>
      <ram:SpecifiedTradeProduct>
        <ram:Name>Testprodukt</ram:Name>
      </ram:SpecifiedTradeProduct>
      <ram:SpecifiedLineTradeAgreement>
        <ram:NetPriceProductTradePrice>
          <ram:ChargeAmount>1.0000</ram:ChargeAmount>
          <ram:BasisQuantity unitCode="C62">1.0000</ram:BasisQuantity>
        </ram:NetPriceProductTradePrice>
      </ram:SpecifiedLineTradeAgreement>
      <ram:SpecifiedLineTradeDelivery>
        <ram:BilledQuantity unitCode="C62">10.0000</ram:BilledQuantity>
      </ram:SpecifiedLineTradeDelivery>
      <ram:SpecifiedLineTradeSettlement>
        <ram:ApplicableTradeTax>
          <ram:TypeCode>VAT</ram:TypeCode>
          <ram:CategoryCode>S</ram:CategoryCode>
          <ram:RateApplicablePercent>7.00</ram:RateApplicablePercent>
        </ram:ApplicableTradeTax>
        <ram:SpecifiedTradeSettlementLineMonetarySummation>
          <ram:LineTotalAmount>10.00</ram:LineTotalAmount>
        </ram:SpecifiedTradeSettlementLineMonetarySummation>
      </ram:SpecifiedLineTradeSettlement>
    </ram:IncludedSupplyChainTradeLineItem>
    <ram:ApplicableHeaderTradeAgreement>
      <ram:BuyerReference>991-01484-64</ram:BuyerReference>
      <ram:SellerTradeParty>
        <ram:Name>Test company</ram:Name>
        <ram:DefinedTradeContact>
          <ram:PersonName>Hans Test</ram:PersonName>
          <ram:TelephoneUniversalCommunication>
            <ram:CompleteNumber>+49123456789</ram:CompleteNumber>
          </ram:TelephoneUniversalCommunication>
          <ram:EmailURIUniversalCommunication>
            <ram:URIID>[email protected]</ram:URIID>
          </ram:EmailURIUniversalCommunication>
        </ram:DefinedTradeContact>
        <ram:PostalTradeAddress>
          <ram:PostcodeCode>55232</ram:PostcodeCode>
          <ram:LineOne>teststr</ram:LineOne>
          <ram:CityName>teststadt</ram:CityName>
          <ram:CountryID>DE</ram:CountryID>
        </ram:PostalTradeAddress>
        <ram:URIUniversalCommunication>
          <ram:URIID schemeID="EM">[email protected]</ram:URIID>
        </ram:URIUniversalCommunication>
        <ram:SpecifiedTaxRegistration>
          <ram:ID schemeID="VA">DE0815</ram:ID>
        </ram:SpecifiedTaxRegistration>
        <ram:SpecifiedTaxRegistration>
          <ram:ID schemeID="FC">DE4711</ram:ID>
        </ram:SpecifiedTaxRegistration>
      </ram:SellerTradeParty>
      <ram:BuyerTradeParty>
        <ram:Name>Franz Müller</ram:Name>
        <ram:PostalTradeAddress>
          <ram:PostcodeCode>55232</ram:PostcodeCode>
          <ram:LineOne>teststr.12</ram:LineOne>
          <ram:CityName>Entenhausen</ram:CityName>
          <ram:CountryID>DE</ram:CountryID>
        </ram:PostalTradeAddress>
        <ram:URIUniversalCommunication>
          <ram:URIID schemeID="EM">[email protected]</ram:URIID>
        </ram:URIUniversalCommunication>
      </ram:BuyerTradeParty>
    </ram:ApplicableHeaderTradeAgreement>
    <ram:ApplicableHeaderTradeDelivery>
      <ram:ActualDeliverySupplyChainEvent>
        <ram:OccurrenceDateTime>
          <udt:DateTimeString format="102">20250118</udt:DateTimeString>
        </ram:OccurrenceDateTime>
      </ram:ActualDeliverySupplyChainEvent>
    </ram:ApplicableHeaderTradeDelivery>
    <ram:ApplicableHeaderTradeSettlement>
      <ram:PaymentReference>123</ram:PaymentReference>
      <ram:InvoiceCurrencyCode>EUR</ram:InvoiceCurrencyCode>
      <ram:SpecifiedTradeSettlementPaymentMeans>
        <ram:TypeCode>58</ram:TypeCode>
        <ram:Information>SEPA credit transfer</ram:Information>
        <ram:PayeePartyCreditorFinancialAccount>
          <ram:IBANID>DE12500105170648489890</ram:IBANID>
        </ram:PayeePartyCreditorFinancialAccount>
        <ram:PayeeSpecifiedCreditorFinancialInstitution>
          <ram:BICID>COBADEFXXX</ram:BICID>
        </ram:PayeeSpecifiedCreditorFinancialInstitution>
      </ram:SpecifiedTradeSettlementPaymentMeans>
      <ram:ApplicableTradeTax>
        <ram:CalculatedAmount>1.47</ram:CalculatedAmount>
        <ram:TypeCode>VAT</ram:TypeCode>
        <ram:BasisAmount>21.00</ram:BasisAmount>
        <ram:CategoryCode>S</ram:CategoryCode>
        <ram:RateApplicablePercent>7.00</ram:RateApplicablePercent>
      </ram:ApplicableTradeTax>
      <ram:ApplicableTradeTax>
        <ram:CalculatedAmount>21.09</ram:CalculatedAmount>
        <ram:TypeCode>VAT</ram:TypeCode>
        <ram:BasisAmount>111.00</ram:BasisAmount>
        <ram:CategoryCode>S</ram:CategoryCode>
        <ram:RateApplicablePercent>19.00</ram:RateApplicablePercent>
      </ram:ApplicableTradeTax>
      <ram:SpecifiedTradeAllowanceCharge>
        <ram:ChargeIndicator>
          <udt:Indicator>true</udt:Indicator>
        </ram:ChargeIndicator>
        <ram:ActualAmount>11.00</ram:ActualAmount>
        <ram:CategoryTradeTax>
          <ram:TypeCode>VAT</ram:TypeCode>
          <ram:CategoryCode>S</ram:CategoryCode>
          <ram:RateApplicablePercent>19.00</ram:RateApplicablePercent>
        </ram:CategoryTradeTax>
      </ram:SpecifiedTradeAllowanceCharge>
      <ram:SpecifiedTradeAllowanceCharge>
        <ram:ChargeIndicator>
          <udt:Indicator>true</udt:Indicator>
        </ram:ChargeIndicator>
        <ram:ActualAmount>11.00</ram:ActualAmount>
        <ram:CategoryTradeTax>
          <ram:TypeCode>VAT</ram:TypeCode>
          <ram:CategoryCode>S</ram:CategoryCode>
          <ram:RateApplicablePercent>7.00</ram:RateApplicablePercent>
        </ram:CategoryTradeTax>
      </ram:SpecifiedTradeAllowanceCharge>
      <ram:SpecifiedTradePaymentTerms>
        <ram:Description>Please remit until 18.01.2025</ram:Description>
        <ram:DueDateDateTime>
          <udt:DateTimeString format="102">20250118</udt:DateTimeString>
        </ram:DueDateDateTime>
      </ram:SpecifiedTradePaymentTerms>
      <ram:SpecifiedTradeSettlementHeaderMonetarySummation>
        <ram:LineTotalAmount>110.00</ram:LineTotalAmount>
        <ram:ChargeTotalAmount>22.00</ram:ChargeTotalAmount>
        <ram:AllowanceTotalAmount>0.00</ram:AllowanceTotalAmount>
        <ram:TaxBasisTotalAmount>132.00</ram:TaxBasisTotalAmount>
        <ram:TaxTotalAmount currencyID="EUR">22.56</ram:TaxTotalAmount>
        <ram:GrandTotalAmount>154.56</ram:GrandTotalAmount>
        <ram:TotalPrepaidAmount>0.00</ram:TotalPrepaidAmount>
        <ram:DuePayableAmount>154.56</ram:DuePayableAmount>
      </ram:SpecifiedTradeSettlementHeaderMonetarySummation>
    </ram:ApplicableHeaderTradeSettlement>
  </rsm:SupplyChainTradeTransaction>
</rsm:CrossIndustryInvoice>

which delivers the incorrect 154,56. The generated xml is validated successfully.

The visualized XML: invoice-123.pdf

The validation report (as txt): validation-report-xml.txt

@FrankHossfeld
Copy link
Author

@jstaerk Did you test the code with charges of document level to recreate the error??

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants