Skip to content

Releases: StackForge-EU/factur-x

v1.4.3

Choose a tag to compare

@github-actions github-actions released this 28 Sep 21:37
  • Fixed half-cent rounding. Math.round(n * 100) / 100 turns
    402.50 × 19% (76.475) into 76.47, because 76.475 * 100 is
    7647.499999999999, so validateInput rejected the correctly rounded
    76.48 (BR-CO-17). Amounts are now rounded half away from zero on a
    noise-free value and compared in integer cents, in the validator and in
    the XML builder alike. Reported by @johannesspohr (#21).

v1.4.2

Choose a tag to compare

@github-actions github-actions released this 25 Sep 14:33
  • Fixed the embedded XML MIME type back to text/xml. 1.2.1 switched the
    factur-x.xml filespec /Subtype to application/xml (#7), but the
    Factur-X specification requires text/xml, and the official FNFE-MPE
    validator rejects anything else. PDFs generated with 1.2.1 through 1.4.1
    should be regenerated. A test now pins the value (#19).

v1.4.1

Choose a tag to compare

@github-actions github-actions released this 14 Sep 23:33
  • Added the rounding amount (BT-114). A new optional roundingAmount
    field on InvoiceTotalsInput emits ram:RoundingAmount between the tax
    total and the grand total, so an invoice can state the amount that takes
    the grand total (BT-112) to the payable figure (BT-115). Emitted for
    EN 16931 and EXTENDED only, as the lower-profile XSDs do not define the
    element. validateInput now also checks BR-CO-16
    (duePayableAmount = grandTotal - prepaidAmount + roundingAmount), which
    the schematron already enforced. Omitting the field keeps the output
    byte-for-byte identical (#17).

v1.4.0

Choose a tag to compare

@github-actions github-actions released this 03 Sep 06:48
  • Added the item price discount (BT-147). A new optional priceDiscount
    field on InvoiceLineInput emits ram:AppliedTradeAllowanceCharge inside
    the gross price element, so a line can state the discount that connects the
    gross unit price (BT-148) to the net unit price (BT-146) instead of leaving
    it implied by their difference. Requires grossUnitPrice to be set; the
    amount keeps up to four decimals for sub-cent discounts. Omitting the field
    keeps the output byte-for-byte identical (#15).

v1.3.1

Choose a tag to compare

@github-actions github-actions released this 01 Sep 07:47
  • Dropped the unused schema/cii-d22b/ code-list XSDs from the npm and JSR
    packages
    (~100 files, ~800 KB). Nothing references them at runtime — the
    per-profile schema directories are self-contained. One of these files also
    has a path longer than 100 characters, which a JSR server-side tar
    regression truncated, breaking the 1.3.0 JSR publish.
  • The release workflow now dry-runs npm pack and deno publish before
    tagging, and publishes to npm only after JSR has succeeded — npm versions
    are effectively immutable, so the unretractable registry ships last.

v1.3.0

Choose a tag to compare

@github-actions github-actions released this 01 Sep 06:34
  • Added item price base quantity (BT-149/BT-150). New optional
    basisQuantity and basisQuantityUnitCode fields on InvoiceLineInput
    emit ram:BasisQuantity inside the net (and, when present, gross) price
    element, so a unit price can state the quantity it refers to — e.g.
    12.50 per 10 m². The unit code defaults to the line's unitCode, as
    EN 16931 requires BT-150 to match BT-130. Omitting the field keeps the
    output byte-for-byte identical (#12).
  • CI now requires every PR to add a CHANGELOG.md entry (or carry the
    skip-changelog label), so contributor fixes always reach the release notes.

v1.2.1

Choose a tag to compare

@github-actions github-actions released this 27 Aug 18:50
  • Fixed XSD validation on Windows. Schema <xsd:import> resolution built a
    malformed file:// URL from backslash paths and keyed the schema lookup by
    a percent-encoded href that libxml2 normalises differently. Now uses
    pathToFileURL() and matches imported schemas by filename. Windows added
    to the CI matrix (thanks @muratkorkmaztr-cmyk, #6).
  • Embedded factur-x.xml now uses the application/xml MIME type instead
    of the deprecated text/xml (RFC 7303), which current Factur-X conformance
    checkers flag (thanks @muratkorkmaztr-cmyk, #7).
  • Expanded npm keywords (e.g. e-rechnung, pdfa, chorus-pro, un-cefact,
    spelling variants) so the package is easier to find on npm (#8).

v1.2.0

Choose a tag to compare

@github-actions github-actions released this 11 Jul 12:23
  • Updated to Factur-X 1.09 / ZUGFeRD 2.5 (spec dated 2026-06-10). Factur-X
    1.09 keeps the same UN/CEFACT CII D22B syntax, namespaces, and profile URNs
    (urn:factur-x.eu:1p0:*) as 1.08, so this is a backward-compatible refresh:
    XML produced by previous versions remains valid, and no input/output changes
    are required.

    • Bundled XSD schemas under schema/ swapped to the official 1.09 set
      (filenames normalized to the repo convention, internal schemaLocation
      references rewritten to match). For MINIMUM / BASIC WL / BASIC / EN 16931 the
      XSDs are content-identical to 1.08; the EXTENDED XSD gains only optional
      additions (debited-account name, financial adjustment / charges on behalf of
      a third party, an optional listID attribute on the allowance/charge reason
      code).
    • Reference .sch (Schematron) and _codedb.xml files updated to the 1.09
      code lists and CEN validation rules (effective 2026-05-15). These are not
      shipped in the npm package and not used at runtime — this library performs
      XSD validation only.
    • Documentation and code comments updated from "1.08 / 2.4" to "1.09 / 2.5".
  • New optional EXTENDED-profile input fields for selected Factur-X 1.09
    business terms (all additive, only emitted at Profile.EXTENDED):

    • PaymentInput.debtorAccountName (BT-216) and PaymentInput.debtorBic
      (BT-215) — debited-account name and its payment service provider BIC.
    • AllowanceChargeInput.exemptionReason / exemptionReasonCode
      (BT-173–176) — VAT exemption reason/code on document-level allowances and
      charges.
    • FacturXInvoiceInput.financialAdjustments (BG-34, new
      FinancialAdjustmentInput with reason BT-180 + amount BT-179) —
      charges collected on behalf of a third party.
    • InvoiceLineInput.manufacturer (BG-X-94) — line-level product manufacturer
      party (ram:ManufacturerTradeParty).
    • Remaining 1.09 EXTENDED terms (e.g. document-level non-VAT tax codes,
      logistic-service-charge exemption reasons) are still not emitted and can be
      added later without breaking changes.
  • Fixed: ram:FormattedIssueDateTime now emits a qdt:DateTimeString child
    instead of udt:DateTimeString. It is typed as qdt:FormattedDateTimeType in
    the CII XSD, so the previous udt: prefix failed XSD/Schematron validation for
    every credit note or cancellation invoice referencing a dated preceding
    invoice (#5).

  • Fixed: XSD validation no longer exhausts libxml2's fixed-size input-callback
    table. validateXsd now clears the table on each call, so validating many
    documents in one process no longer causes spurious failures once ~15
    validations have run.

v1.1.0

Choose a tag to compare

@github-actions github-actions released this 27 May 17:26
  • BREAKING: Every ram:GlobalID and the legal-organization ram:ID now
    carry the @schemeID attribute required by EN 16931. The XSD-canonical CII
    mapping makes the scheme mandatory on ram:GlobalID (BT-29-1 / BT-46-1 for
    parties, BT-157-1 for line items) and conditional on
    ram:SpecifiedLegalOrganization/ram:ID (BT-30-1 / BT-47-1); without these
    attributes Mustang / KoSIT / veraPDF's Factur-X profile reject the invoice
    even though the embedded CII looked superficially correct. Three input
    fields changed:
    • TradePartyInput.globalId is now IdentifierWithSchemeInput
      ({ value: string; schemeID: string }) instead of a bare string
    • InvoiceLineInput.standardIdentifier is now IdentifierWithSchemeInput
      instead of a bare string (sibling fix on top of #4 — the line-item
      ram:GlobalID had the same defect as the party one)
    • LegalOrganizationInput.schemeID?: string added (optional, mirrors the
      conditional spec rule)
      Migration: globalId: "4000001000005" →
      globalId: { value: "4000001000005", schemeID: "0088" } (and the analogous
      rewrite for standardIdentifier). The new IdentifierWithSchemeInput type
      is exported from src/index.ts. Regression test added in
      tests/xsd-validator.test.ts that drives all three schemed identifiers
      through the EN16931 XSD in one go (thanks @sco-indy, #4).

v1.0.8

Choose a tag to compare

@github-actions github-actions released this 27 May 16:21
  • PDF trailer was missing the /ID entry required by PDF/A-3. ISO 19005-3
    (and PDF 1.7 §14.4) require the trailer dictionary to carry an /ID array of
    two byte strings: the first immutable identifier of the original document,
    the second updated on every modification. pdf-lib initializes
    trailerInfo = {} and never writes one on save, so veraPDF and the FNFE-MPE
    Factur-X profile rejected every PDF this library produced as non-conformant.
    Added ensureIdTrailer in src/core/embed.ts which runs after the first
    pdfDoc.save() when addPdfA3Metadata is on: it computes an MD5 over the
    serialized bytes (per PDF 1.7 §14.4 — uniqueness, not cryptographic strength,
    is what matters), preserves any pre-existing first identifier, and writes the
    pair before re-saving. Regression tests added in tests/embed.test.ts
    covering both the no-existing-trailer and existing-trailer paths (thanks
    @sco-indy, #3).