# XRechnung generator: runbook

A single zero-dependency file, `xrechnung.mjs`, that turns a Softr/Airtable
invoice record (one invoice row + its linked line-item records) into a
legally-valid German **E-Rechnung** in the **XRechnung 3.0.2** (UBL) syntax.

You own this file. It calls no external service. It runs unchanged in a Softr
workflow custom-code step, a Cloudflare Worker, an n8n Function node, Node, or
the browser.

## What it does

```js
import { softrRecordToXRechnung } from "./xrechnung.mjs";

const xml = softrRecordToXRechnung(invoiceRecord); // -> valid XRechnung 3.0.2 XML string
```

The input is the shape you described in the thread: an invoice record with a
linked `items` array. See `sample-softr-record.json` for the exact shape.

## The one place you adapt it

Softr/Airtable field labels differ per app, so `normalizeSoftrRecord()` (top of
`xrechnung.mjs`) is the single place you map **your** collection's column names
onto the invoice model. Rename the keys there to match your fields; nothing else
needs to change.

## Drop it into a Softr workflow

Softr workflows can call a webhook / run a code step on a record. The pattern:

1. **Trigger:** a Softr workflow fires when an invoice record is marked "ready"
   (button click, status change, etc.).
2. **Assemble the record:** pass the invoice fields + its linked line items to
   your code step (or POST them to a Worker you own).
3. **Generate:** `const xml = softrRecordToXRechnung(record);`
4. **Store / send:** write the XML back to an attachment field on the record, or
   email it. The XML file **is** the E-Rechnung (XRechnung). For the hybrid
   **ZUGFeRD** PDF, embed the equivalent XML into a PDF/A-3, see below.

Because the generator is pure and self-contained, "the code step" can be:
- a Cloudflare Worker you own (the record is POSTed in, the XML comes back), or
- an n8n / Make Function node, or
- anything that runs JavaScript.

There is no server of mine in this loop.

## Validate it yourself (the receipt)

The sample output was validated with the **official KoSIT validator**, the same
tool the German public sector uses, and accepted with **0 errors, 0 warnings**.
Reproduce it:

```bash
# 1. the validator tool (Java 8+)
curl -LO https://github.com/itplr-kosit/validator/releases/download/v1.6.2/validator-1.6.2-standalone.jar

# 2. the XRechnung 3.0.2 configuration (scenarios + resources)
curl -LO https://github.com/itplr-kosit/validator-configuration-xrechnung/releases/download/v2026-01-31/xrechnung-3.0.2-validator-configuration-2026-01-31.zip
unzip -d config xrechnung-3.0.2-validator-configuration-2026-01-31.zip

# 3. validate your generated file
java -jar validator-1.6.2-standalone.jar -r config -s config/scenarios.xml -o report invoice.xml
```

A green report means: schema-valid UBL, all EN16931 + XRechnung (BR-DE) business
rules pass, scenario accepted.

## XRechnung vs ZUGFeRD

- **XRechnung** = the pure-XML E-Rechnung. This generator produces it (UBL
  syntax). It is a complete, valid E-Rechnung on its own.
- **ZUGFeRD / Factur-X** = a hybrid: a human-readable PDF/A-3 with the invoice
  XML embedded inside it (CII syntax). If you want the PDF-with-embedded-XML
  variant too, the next step is: keep the same field mapping, emit the CII flavor
  of the XML, and embed it into your existing Softr-generated invoice PDF as a
  PDF/A-3 attachment. That is a bounded add-on to this same generator, not a
  rebuild.

## Fields the generator requires (EN16931 / XRechnung)

Missing any of these throws a clear error naming the field:
- Invoice: number, issue date, currency, **BuyerReference (Leitweg-ID, BT-10)**.
- Seller: name, full address, **VAT ID**, electronic address, and a **contact
  group** (name, phone, email), XRechnung (BR-DE-2..7) requires the seller
  contact.
- Buyer: name, full address, electronic address.
- Payment: IBAN.
- Each item: name, quantity, unit code (UN/ECE Rec 20, e.g. `HUR` hour, `C62`
  piece), net unit price, VAT %.
