Playwright Tests / test (pull_request) Has been cancelled
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>
85 lines
5.3 KiB
Markdown
85 lines
5.3 KiB
Markdown
---
|
|
status: Active
|
|
lastUpdated: 2026-07-18
|
|
applyTo:
|
|
- "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/**"
|
|
relatedDecisions:
|
|
- "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.LeitwegId` → `BuyerReference` 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`](../eRechnung-Validator-Requirements.md).
|
|
|
|
## Key files
|
|
- `Fuchs/Services/InvoiceRecipientAddress.cs` — structured buyer address, composition, conformity.
|
|
- `Fuchs/Services/ERechnungMapper.cs` — `FdsInvoiceData` → `eRechnungLib.Model.Invoice`.
|
|
- `Fuchs/Services/ERechnungService.cs` / `ERechnungSettings.cs` — hybrid production + config.
|
|
- `Fuchs/Services/InvoiceService.cs` — wiring in `RenderInvoicePdfBytesAsync`.
|
|
- `Fuchs/code/FuchsPdf.cs` — `DocToPdfBytesRaw` (render-only visual PDF).
|
|
- `Fuchs/js/intranet/modules/fis.inv_shared.js` — `$inv.eAddress` structured dialog.
|
|
- `Fuchs_Database` — `fds__invoices.SendToAddressJson`, `fds__getCompanyAddressJson`,
|
|
`fds__prepInvoice.invoiceaddressData`, `fds__createInvoice`/`setInvoice`/`getInvoice`.
|
|
- `eRechnungLib/**` — CII/UBL serialization, EN 16931 validation, `FacturXPdfBuilder` (PDF/A-3).
|
|
|
|
## Related decisions
|
|
- [`0005-pdf-generation-and-erechnung.md`](../Decisions/0005-pdf-generation-and-erechnung.md)
|
|
- [`0012-erechnung-single-pdfa-engine-pipeline.md`](../Decisions/0012-erechnung-single-pdfa-engine-pipeline.md)
|