Files
Fuchs_Intranet/Fuchs/Docs/Decisions/0012-erechnung-single-pdfa-engine-pipeline.md
Stefan 628802db19 Add function to retrieve company address as JSON and update invoice procedures
- Created a new function `fds__getCompanyAddressJson` to return a company's postal address as a structured JSON object.
- Modified stored procedures `fds__createInvoice`, `fds__setInvoice`, and `fds__prepInvoice` to include a new parameter `@SendToAddressJson` for handling the address data.
- Updated the invoice table and user-defined types to accommodate the new `SendToAddressJson` field.
- Ensured that the address data is properly retrieved and stored in the invoice records.
2026-07-18 18:01:24 +02:00

4.4 KiB

status, date, applyTo, supersededBy
status date applyTo supersededBy
Accepted 2026-07-17
Fuchs/code/FuchsPdf.cs
Fuchs/Services/FuchsPdfService.cs
Fuchs/Services/InvoiceService.cs
Fuchs/Services/ERechnungSettings.cs
eRechnungLib/**

0012 — eRechnung uses a single PDF/A engine (eRechnungLib owns PDF/A-3)

Context

ADR 0005 established that invoices are emitted as eRechnung by embedding the CII XML into the FuchsPdf-rendered visual PDF via eRechnungLib.ToZugferd(...).

Two hard requirements then surfaced: the emitted invoice must (a) satisfy the ZUGFeRD 2.4 / Factur-X standard for DATEV ingestion, and (b) be a formally verifiable PDF/A-3 (veraPDF-clean).

A conflict became apparent in the rendering pipeline. FuchsPdf.DocToPdfBytes post-processes its MigraDoc/PdfSharp output to PDF/A via Spire (OCORE…pdfAFileContent). eRechnungLib's FacturXPdfBuilder also produces a PDF/A layer (raises to PDF 1.7, writes pdfaid XMP, adds an sRGB output intent, embeds factur-x.xml in /AF). Feeding a Spire-made PDF/A into eRechnungLib stacks two PDF/A conversions → duplicate/conflicting output intents and pdfaid markers, which veraPDF rejects. Spire's output is also PDF/A-1/2 and does not carry the /AF associated-file structure ZUGFeRD requires (PDF/A-3).

Separately, eRechnungLib shipped no sRGB ICC profile, so its output intent was silently omitted (PDFA-ICC warning) — never formally PDF/A-3 conformant.

Decision

  • eRechnungLib is the single PDF/A engine for eRechnung output. The invoice visual PDF is rendered by FuchsPdf without the Spire PDF/A step and handed to eRechnungLib.ToZugferd(ZugferdProfile.EN16931, rawPdfBytes), which owns the one PDF/A-3 conversion and embeds the CII XML. The render-only path is FuchsPdf.DocToPdfBytesRaw / IPdfService.DocToPdfBytesRaw (fonts still embedded via OCOREFontResolver, no PDF/A post-processing).
  • Spire stays only for on-screen preview rasterisation (DocToImageCollection / BytesToImageCollection for sprep/sedit). It is not part of the eRechnung file's PDF/A path. DocToPdfBytes (render + Spire PDF/A) is unchanged and remains the path for non-eRechnung documents (e.g. reminders).
  • A bundled sRGB ICC profile is required. eRechnungLib ships Resources/Color/sRGB.icc (sRGB IEC61966-2.1) so the PDF/A output intent is always attached. A caller may override it per conversion via ConversionOptions.IccProfile.
  • Profile is EN 16931. MINIMUM / BASIC WL are not offered for real invoices — DATEV needs at least EN 16931 (COMFORT) for full booking.
  • Formal conformance is verified by an external online service (veraPDF for PDF/A-3 + a ZUGFeRD/EN 16931 validator), behind the configurable Fuchs:ERechnung:Validation:ServiceUrl seam. Until the URL is provisioned, verification reports "not configured / skipped".

Consequences

  • The eRechnung invoice PDF and a plain Spire PDF/A must never both be produced for the same document — pick the render-only path when emitting eRechnung.
  • The incoming visual PDF must itself be PDF/A-friendly (fonts embedded — handled; letterhead images must be RGB, not CMYK; transparency is allowed because we target PDF/A-3).
  • Fuchs:ERechnung:Enabled gates emission and stays false until the FdsInvoiceDataeRechnungLib.Model.Invoice mapping is wired (the ADR 0005 follow-up). Open item for that mapping: the buyer address is currently a free-text block (SendToAddress); EN 16931 needs structured buyer fields (name/postcode/city/country, VAT id), so structured customer master data must feed the mapping. Seller data (currently hard-coded in FuchsPdf: name, address, tax number, IBAN/BIC) must be lifted into the seller model.
  • Fuchs.csproj must add a project reference to eRechnungLib when the flow is wired (not present yet).

Alternatives considered

  • Keep Spire PDF/A and have eRechnungLib only embed the XML: rejected — Spire produces the wrong PDF/A part (1/2, no /AF) and a second conversion collides with eRechnungLib's own output intent/XMP, failing veraPDF.
  • Drop Spire entirely: rejected — Spire is still needed to rasterise PDFs to the on-screen invoice/reminder preview images; PdfSharp/eRechnungLib cannot.
  • Ship no ICC and rely on callers: rejected — formal PDF/A-3 requires an output intent; bundling a profile makes conformance the default.