Skip to content

[Bug]: xRechnung/ZUGFeRD export missing TaxExemptionReason and unsupported VATEX codes #29863

@miljance

Description

@miljance

Describe the issue

The xRechnung / ZUGFeRD / PEPPOL BIS 3.0 export has two related problems with VAT exemption handling:

Problem 1 – TaxExemptionReason (BT-120) is never exported

The element cbc:TaxExemptionReason (BT-120) is never included in the exported XML, regardless of the Tax Category or VAT setup. This is the human-readable text that explains why VAT is exempt or not charged. Multiple PEPPOL rules require that either BT-120 or BT-121 is present for exempt categories ([BR-E-10], [BR-AE-10], [BR-G-10], [BR-O-10]). While technically only one of the two is mandatory, best practice - and many validators and receiving systems - expect both the code and the textual reason to be present.

Problem 2 – TaxExemptionReasonCode (BT-121) is hard-coded to VATEX-EU-O only

The only scenario where cbc:TaxExemptionReasonCode (BT-121) is exported is for VAT % = 0% ("Not subject to VAT"), and in that case the code is always hard-coded to VATEX-EU-O.

The [PEPPOL VATEX code list] defines dozens of specific exemption reason codes (e.g., VATEX-EU-132-1I for education, VATEX-EU-AE for reverse charge, VATEX-EU-G for export outside the EU, VATEX-EU-IC for intra-community supply, VATEX-EU-D/VATEX-EU-F/VATEX-EU-I/VATEX-EU-J for margin schemes, etc.).

The fix should be straightforward: Business Central already provides both the Tax Category and the VAT Clause Code per line in the VAT Posting Setup. The VAT Clause table holds a Code and a Description. The export should use the VAT Clause Code as cbc:TaxExemptionReasonCode (BT-121) and the VAT Clause Description as cbc:TaxExemptionReason (BT-120), instead of hard-coding only the VATEX-EU-O case.

Expected behavior

When a VAT Posting Setup line has a Tax Category that requires an exemption reason (e.g., E, G, AE, K, O) and a VAT Clause Code is configured, the export should produce:

<cac:TaxCategory>
  <cbc:ID>E</cbc:ID>
  <cbc:Percent>0</cbc:Percent>
  <cbc:TaxExemptionReasonCode>VATEX-EU-132-1I</cbc:TaxExemptionReasonCode>
  <cbc:TaxExemptionReason>Exempt based on article 132, section 1 (i) of Council Directive 2006/112/EC</cbc:TaxExemptionReason>
  <cac:TaxScheme>
    <cbc:ID>VAT</cbc:ID>
  </cac:TaxScheme>
</cac:TaxCategory>

Currently the export produces (for VAT % = 0% only — for other exempt categories neither element is present):

<cac:TaxCategory>
  <cbc:ID>O</cbc:ID>
  <cbc:Percent>0</cbc:Percent>
  <cbc:TaxExemptionReasonCode>VATEX-EU-O</cbc:TaxExemptionReasonCode>
  <cac:TaxScheme>
    <cbc:ID>VAT</cbc:ID>
  </cac:TaxScheme>
</cac:TaxCategory>

Specifically:

  • cbc:TaxExemptionReasonCode (BT-121) should be populated from the VAT Clause Code - not hard-coded.
  • cbc:TaxExemptionReason (BT-120) should be populated from the VAT Clause Description.
  • Both fields should be exported for all Tax Categories that require them (E, AE, G, K, O) and omitted where they must not appear (S, Z per [BR-Z-10]).

Steps to reproduce

  1. Use Business Central for Germany.
  2. Enable E-Document Service for XRechnung (or ZUGFeRD).
  3. Configure a Workflow and a Document Sending Profile for XRechnung or ZUGFeRD. Mark the sending profile as default and assign it to a Customer.
  4. Open VAT Posting Setup.
  5. Find a line with Tax Category = E and VAT % = 0 (e.g., INLAND / OHNE MWST.).
  6. Set the VAT Clause Code to a valid VATEX code (e.g., create a VAT Clause with Code = VATEX-EU-132-1I and Description = Exempt based on article 132, section 1 (i) of Council Directive 2006/112/EC).
  7. Create a new Sales Invoice for the assigned Customer.
  8. Add a Sales Line using an Item or G/L Account that maps to the VAT Prod. Posting Group from step 5 (so Tax Category E applies).
  9. Post the Sales Invoice.
  10. Open the Posted Sales InvoiceRelated → E-Document → Open E-Document.
  11. From E-Document Logs, export the XML file.
  12. Inspect the XML:
    • Actual result: <cbc:TaxExemptionReasonCode> = VATEX-EU-O and <cbc:TaxExemptionReason> is missing.
    • Expected result: <cbc:TaxExemptionReasonCode> = VATEX-EU-132-1I (from VAT Clause Code) and <cbc:TaxExemptionReason> = VAT Clause Description.

Additional context

The infrastructure is already in place in Business Central: the VAT Posting Setup page exposes both the Tax Category and the VAT Clause Code columns. The VAT Clause table holds Code and Description. The export logic just needs to read the VAT Clause Code and Description and write them into cbc:TaxExemptionReasonCode and cbc:TaxExemptionReason respectively, instead of only handling the hard-coded VATEX-EU-O case.

Relevant PEPPOL BIS Billing 3.0 rules:

I will provide a fix for a bug

  • I will provide a fix for a bug

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions