Skip to content

Order of elements CategoryTradeTax #113

@Exifers

Description

@Exifers

To Reproduce

With a charge or allowance at the document level, it generates the current XML :

      <ram:SpecifiedTradeAllowanceCharge>
        <ram:ChargeIndicator>
          <udt:Indicator>true</udt:Indicator>
        </ram:ChargeIndicator>
        <ram:ActualAmount>20</ram:ActualAmount>
        <ram:Reason>the reason</ram:Reason>
        <ram:CategoryTradeTax>
          <ram:CategoryCode>S</ram:CategoryCode>
          <ram:RateApplicablePercent>20</ram:RateApplicablePercent>
          <ram:TypeCode>VAT</ram:TypeCode>
        </ram:CategoryTradeTax>
      </ram:SpecifiedTradeAllowanceCharge>
full code to reproduce
/* eslint-disable capitalized-comments */
import fs from "fs";
import { zugferd } from "node-zugferd";
import { EN16931, type ProfileEN16931 } from "node-zugferd/profile/en16931";

const perform = async () => {
    const invoicer = zugferd({
        profile: EN16931,
        strict: true,
    });

    const data: ProfileEN16931 = {
        number: 'invoiceNumber',
        issueDate: new Date(),
        typeCode: '380',
        // includedNote: [],
        transaction: {
            line: [
                {
                    identifier: "1",
                    tradeProduct: {
                        name: "peinture",
                    },
                    tradeAgreement: {
                        netTradePrice: {
                            chargeAmount: 120,
                        }
                    },
                    tradeDelivery: {
                        billedQuantity: {
                            amount: 2,
                            unitMeasureCode: "C62",
                        }
                    },
                    tradeSettlement: {
                        tradeTax: {
                            typeCode: "VAT",
                            categoryCode: "S",
                            rateApplicablePercent: "20.00",
                        },
                        monetarySummation: {
                            lineTotalAmount: 240,
                        }
                    }
                },
                {
                    identifier: "2",
                    tradeProduct: {
                        name: "demolition",
                    },
                    tradeAgreement: {
                        netTradePrice: {
                            chargeAmount: 20,
                        }
                    },
                    tradeDelivery: {
                        billedQuantity: {
                            amount: 4,
                            unitMeasureCode: "80",
                        }
                    },
                    tradeSettlement: {
                        tradeTax: {
                            typeCode: "VAT",
                            categoryCode: "S",
                            rateApplicablePercent: 50,
                        },
                        monetarySummation: {
                            lineTotalAmount: 80,
                        }
                    }
                }
            ],
            tradeAgreement: {
                seller: {
                    name: "Nom company",
                    postalAddress: {
                        countryCode: "FR"
                    },
                    taxRegistration: {
                        vatIdentifier: "FR 03 552081317",
                    }
                },
                buyer: {
                    postalAddress: {
                        postCode: "75001",
                        line1: "line1",
                        line2: "line2",
                        line3: "line3",
                        city: "Paris",
                        countryCode: "FR",
                    },
                    name: "Nom client",
                },
            },
            tradeDelivery: {},
            tradeSettlement: {
                currencyCode: "EUR",
                allowances: [
                    {
                        actualAmount: 20,
                        reason: "the reason",
                        categoryTradeTax: {
                            typeCode: "VAT",
                            categoryCode: "S",
                            vatRate: 20
                        }
                    }
                ],
                charges: [
                    {
                        actualAmount: 20,
                        reason: "the reason",
                        categoryTradeTax: {
                            typeCode: "VAT",
                            categoryCode: "S",
                            vatRate: 20
                        }
                    },
                ],
                vatBreakdown: [
                    {
                        calculatedAmount: 48,
                        typeCode: "VAT",
                        basisAmount: 240,
                        categoryCode: "S",
                        rateApplicablePercent: 20,
                    },
                    {
                        calculatedAmount: 40,
                        typeCode: "VAT",
                        basisAmount: 80,
                        categoryCode: "S",
                        rateApplicablePercent: 50,
                    }
                ],
                paymentTerms: {
                    description: "foobar",
                },
                monetarySummation: {
                    lineTotalAmount: 320,
                    chargeTotalAmount: 20,
                    allowanceTotalAmount: 20,
                    taxBasisTotalAmount: 320,
                    taxTotal: {
                        amount: 88,
                        currencyCode: "EUR",
                    },
                    grandTotalAmount: 408,
                    duePayableAmount: 408,
                }
            }
        }
    }

    const invoice = invoicer.create(data);
    // Only generate XML
    const xml = await invoice.toXML();
    console.log(xml);
    // Or embed XML in a PDF-A/3b
    const pdf = fs.readFileSync("./invoice.pdf");
    const pdfA = await invoice.embedInPdf(pdf);
    fs.writeFileSync("./invoice-factur-x.pdf", pdfA);
}

perform().catch(console.error);

Current vs. Expected behavior

The validation using the xsd schema fails :

 Element '{urn:un:unece:uncefact:data:standard:ReusableAggregateBusinessInformationEntity:100}CategoryCode': This element is not expected. Expected is one of ( {urn:un:unece:uncefact:data:standard:ReusableAggregateBusinessInformationEntity:100}CalculatedAmount, {urn:un:unece:uncefact:data:standard:ReusableAggregateBusinessInformationEntity:100}TypeCode ).

It seems TypeCode must be before CategoryCode inside CategoryTradeTax.

What version of node-zugferd are you using?

0.1.1-beta.1

Provide environment information

OS : ubuntu
node : 24.12.0

Which area(s) are affected? (Select all that apply)

Package

Config (if applicable)

import { zugferd } from "node-zugferd";
import { EN16931 } from "node-zugferd/profile/en16931";

export const invoicer = zugferd({
  profile: EN16931
})

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions