Files
StefanandClaude Opus 4.8 00e72c96d4
Playwright Tests / test (pull_request) Has been cancelled
Emit invoices as validated ZUGFeRD (DATEV) and XRechnung (B2G)
Completes the eRechnung output path: finalized invoices are emitted as a
ZUGFeRD/Factur-X EN 16931 hybrid (DATEV) or, when a Leitweg-ID is present, as
XRechnung 3.0 for B2G. Both are externally validated as ACCEPTED (0 errors) and
PDF/A-3B COMPLIANT against the ProcessWeb eInvoice service.

- ERechnungMapper: FdsInvoiceData -> EN 16931 model. Seller master data now from
  Fuchs:ERechnung:Seller config (VAT id BT-31, Steuernummer BT-32, optional
  Handelsregister BT-30, IBAN/BIC, contact). Adds payment terms BT-20/BT-9
  (BR-CO-25), buyer VAT id, §13b reverse charge, and the service date/period
  (BT-72 / BG-14) parsed from ProvisionPeriod (BR-DE-TMP-32). B2G -> XRechnung
  profile with the required electronic addresses/contact.
- ERechnungValidator: client for POST /validatepdf (EN 16931 XML + PDF/A-3 in
  one call). A pure "scenario not matched" with zero errors is not treated as a
  hard failure; real errors optionally withhold the hybrid (FailOnError).
- ERechnungSettings: seller + validation config; wired in Program.cs; ServiceUrl
  in appsettings.
- Online editor: structured German-only dialogs for the recipient address
  (incl. Leitweg-ID) and the service date/period (single date or range).
- Bumps the eRechnungLib submodule to the CII rsm-namespace / PDF-A subtype fix.

Fuchs.Tests 514/514, eRechnungLib 97/97.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-21 00:05:24 +02:00

5.3 KiB

status, lastUpdated, applyTo, relatedDecisions
status lastUpdated applyTo relatedDecisions
Active 2026-07-18
Fuchs/Services/ERechnungMapper.cs
Fuchs/Services/ERechnungService.cs
Fuchs/Services/ERechnungSettings.cs
Fuchs/Services/InvoiceRecipientAddress.cs
Fuchs/Services/InvoiceService.cs
Fuchs/code/FuchsPdf.cs
eRechnungLib/**
0005-pdf-generation-and-erechnung.md
0012-erechnung-single-pdfa-engine-pipeline.md

eRechnung output (ZUGFeRD/Factur-X)

Summary

Finalized invoices are emitted as a ZUGFeRD 2.4 / Factur-X hybrid: the FuchsPdf visual PDF with the EN 16931 CII XML embedded, in a formally conformant PDF/A-3. This makes invoices DATEV-ingestible and satisfies the B2B/B2G e-invoicing mandate. The library doing the structured XML + PDF/A-3 work is the eRechnungLib submodule; Fuchs supplies the invoice data and the visual PDF.

How it works

Editor (structured recipient dialog)
   → InvoiceDraftEditService  (address delta = JSON object, stored in CustomValues.sendToAddress)
   → fds__setInvoice/…        (persisted; dedicated SendToAddressJson column + composed SendToAddress)
   → InvoiceService.RenderInvoicePdfBytesAsync(final)
        ├─ FuchsPdf.DocToPdfBytesRaw(doc)          → visual PDF (no Spire PDF/A)
        └─ IERechnungService.TryBuildHybridPdf
               ├─ ERechnungMapper.BuildEInvoice     FdsInvoiceData → eRechnungLib.Model.Invoice
               └─ EInvoice.ToZugferd(EN16931, raw)  → PDF/A-3 + Factur-X hybrid (bundled sRGB ICC)
  • Single PDF/A engine (ADR 0012). eRechnungLib owns the one PDF/A-3 layer. For the eRechnung path the Spire PDF/A step is skipped (DocToPdfBytesRaw); Spire stays only for on-screen preview rasterisation. This avoids a conflicting second output intent / pdfaid marker.
  • Structured recipient address. InvoiceRecipientAddress holds the EN 16931 buyer fields (name, street, post code, city, country BT-55, optional VAT id BT-48). It is edited via a dialog form ($inv.eAddress in fis.inv_shared.js), prefilled from fds__prepInvoice's invoiceaddressData, and carried as a JSON object through the draft cache. A private person (no VAT id) is fully valid — B2C stays effortless. The free-text SendToAddress is composed from it so the PDF layout is unchanged.
  • Mapping. ERechnungMapper maps the Fuchs invoice to the EN 16931 model: seller = Fuchs (from Fuchs:ERechnung:Seller config — name, address, Steuernummer BT-32, USt-IdNr BT-31, IBAN/BIC, contact; defaults mirror the FuchsPdf letterhead), buyer = the structured recipient, lines from the invoice items, §13b → reverse charge (category AE + exemption reason), payment terms BT-20/BT-9, and the service date/period (BT-72 or BG-14) parsed from the structured ProvisionPeriod. VAT breakdown and totals are recomputed by the library.
  • Profile selection (B2B/B2C vs B2G). Default is ZUGFeRD EN 16931 (DATEV). When the recipient carries a Leitweg-ID (InvoiceRecipientAddress.LeitwegIdBuyerReference BT-10), the invoice is B2G and emitted as XRechnung (ZugferdProfile.XRechnung, embedded xrechnung.xml); the mapper then also fills the seller electronic address/contact (BT-34/BG-6) and buyer electronic address (BT-49) that XRechnung requires.
  • Feature flag & fallback. Emission is gated by Fuchs:ERechnung:Enabled (off until fully validated). IERechnungService returns null on disable or any failure, so RenderInvoicePdfBytesAsync falls back to the plain Spire PDF/A — invoicing never breaks.
  • Formal verification. Fuchs:ERechnung:Validation calls the ProcessWeb eInvoice service (POST {ServiceUrl}/validatepdf, raw application/pdf) which checks the EN 16931 XML and PDF/A-3 (veraPDF) in one call. Both the EN 16931-ZUGFeRD and the XRechnung 3.0 output are externally ACCEPTED (0 errors) and PDF/A-3B COMPLIANT. Getting there required fixing two eRechnungLib defects (CII root children must be rsm: not ram:; embedded-file /Subtype MIME encoding) and completing the mapper (seller VAT id BT-31, payment terms BT-20/BT-9). The validator's target feature scope is documented in ../eRechnung-Validator-Requirements.md.

Key files

  • Fuchs/Services/InvoiceRecipientAddress.cs — structured buyer address, composition, conformity.
  • Fuchs/Services/ERechnungMapper.csFdsInvoiceDataeRechnungLib.Model.Invoice.
  • Fuchs/Services/ERechnungService.cs / ERechnungSettings.cs — hybrid production + config.
  • Fuchs/Services/InvoiceService.cs — wiring in RenderInvoicePdfBytesAsync.
  • Fuchs/code/FuchsPdf.csDocToPdfBytesRaw (render-only visual PDF).
  • Fuchs/js/intranet/modules/fis.inv_shared.js$inv.eAddress structured dialog.
  • Fuchs_Databasefds__invoices.SendToAddressJson, fds__getCompanyAddressJson, fds__prepInvoice.invoiceaddressData, fds__createInvoice/setInvoice/getInvoice.
  • eRechnungLib/** — CII/UBL serialization, EN 16931 validation, FacturXPdfBuilder (PDF/A-3).