Guide
Invoice with ZUGFeRD
Make a German invoice as a PDF/A-3b file that also carries the invoice as EN 16931 data, so the buyer's software can book it without typing.
On this page
ZUGFeRD and Factur-X are one format with two names. The file is a PDF/A-3b
PDF (the PDF standard for long-term storage that allows attached files). A
person reads the page. Inside the PDF sits the file factur-x.xml. It holds
the same invoice as data in the standard EN 16931. The buyer's software reads
that file.
Since 1 January 2025, every German company must be able to receive such an invoice. A company with a turnover above 800,000 EUR in the year before must send one from 1 January 2027. All other companies must send one from 1 January 2028.
Start from the template
The template rechnung-zugferd is a full invoice on A4 for a window envelope, to DIN 5008 form B. Its folder holds five files:
| File | What it is |
|---|---|
rechnung-zugferd.html |
The page. It links ../din5008/din5008.css, so keep the folder din5008/ next to it. |
rechnung-zugferd.css |
The settings for form B. |
rechnung-zugferd.schema.json |
The fields and what each one takes. |
sample.json |
An invoice with 3 lines at 19 % and 7 % VAT (Umsatzsteuer, the sales tax). Start from it. |
rechnung-zugferd.pdf |
The sample, rendered with --einvoice. |
The table of lines uses data-brevier-uebertrag, so on a second page the
running total (Übertrag) is printed at the break. See Tables.
Fill in the data
Put your invoice in a JSON file shaped like sample.json.
| Field | Meaning |
|---|---|
number, issueDate, deliveryDate, currency |
Invoice number, date, date of the service (YYYY-MM-DD) and a currency code such as EUR. All four are required. |
dueDate or paymentTerms |
The date payment is due, or a sentence about it. One of the two is required. |
seller, buyer |
name and address. Only country is required in the address, as a code such as DE. The seller also gives vatId. |
lines |
At least one. Each has position, name, quantity, unitCode, unitPrice, netAmount and vatRate. |
vat |
One entry for each VAT rate: rate, taxableAmount, taxAmount. |
totals |
lineNet, taxBasis, tax, gross, due. |
unitCode is a UN/ECE code: HUR is hours, DAY is days, C62 is pieces.
The XML always gets the code. The page prints unitLabel (for example
Std.) and falls back to the code.
Write the PDF
brevier pdf rechnung-zugferd.html --data my-invoice.json --einvoice -o rechnung.pdfWithout --einvoice you get the same page as a plain PDF/A-3b file. All
options are on the CLI reference.
What Brevier checks
Before it writes the file, Brevier checks your data against the schema. With
--einvoice it also checks that your amounts add up. It does not calculate
them for you.
| Check | Rule |
|---|---|
| A line | netAmount is quantity times unitPrice, rounded to cents. |
| A VAT entry | taxableAmount is the sum of the lines with that rate. taxAmount is that sum times the rate, rounded to cents. |
| The totals | lineNet is the sum of the lines. taxBasis equals lineNet. tax is the sum of the VAT entries. gross is taxBasis plus tax. due equals gross. |
If a rule fails, the command stops, writes no file and names the field:
Error: E-invoice amounts do not add up: BT-112 Invoice total with VAT: is 3610.54 but BT-109 plus BT-110 is 3610.53Check the file
List the attached files. A good invoice has one, factur-x.xml:
pdfdetach -list rechnung.pdfTo validate the XML and the PDF/A part, use Mustang and veraPDF:
java -jar Mustang-CLI-2.26.0.jar --action validate --no-notices --source rechnung.pdf
verapdf --flavour 3b rechnung.pdfMustang ends with exit code 0 even for an invalid file. Read its report for
status="invalid".
What it cannot do yet
- Only the standard VAT category
Swith a rate above zero. Zero rate, tax exempt and reverse charge are not in the schema. - Only invoices (type 380). No credit notes.
- No allowances, charges or prepayments.