E-Rechnung XML selbst erstellen: Anleitung für technisch Versierte
Wer möchte, kann E-Rechnungen direkt als XML-Datei erstellen. Wir zeigen, wie eine valide XML-Rechnung aufgebaut ist und welche Tools dabei helfen.
Wer ein eigenes ERP, eine eigene Plattform oder ein Spezialsystem baut, stößt früher oder später auf die Frage: „Wie generiere ich das XML selbst?" Die Antwort ist erstaunlich machbar, sobald man die zwei Syntaxen, die Namespaces und die wichtigsten Pflichtfelder verstanden hat. Bibliotheken sparen viel Arbeit, sind aber kein Muss – ein gut strukturierter XML-Generator reicht für die meisten Fälle.
Dieser Beitrag zeigt den Aufbau einer XRechnung in beiden Syntaxen, listet die Pflichtfelder, erklärt Validierung und nennt Bibliotheken für die populärsten Sprachen.
UBL oder CII?
Die Norm EN 16931 erlaubt zwei XML-Syntaxen:
| Syntax | Vollform | Wo verbreitet |
|---|---|---|
| UBL 2.1 | Universal Business Language | Peppol, internationale B2B-Welt |
| CII | UN/CEFACT Cross Industry Invoice | ZUGFeRD/Factur-X, Bundesbehörden in Deutschland |
XRechnung kann beide Syntaxen, ZUGFeRD nur CII. Wer für die Bundesverwaltung produziert, ist mit CII flexibel. Wer Peppol bedient, sollte UBL können. Mehr in XML-Format.
Grundgerüst UBL
Eine minimale UBL-Rechnung sieht so aus:
<?xml version="1.0" encoding="UTF-8"?>
<Invoice
xmlns="urn:oasis:names:specification:ubl:schema:xsd:Invoice-2"
xmlns:cbc="urn:oasis:names:specification:ubl:schema:xsd:CommonBasicComponents-2"
xmlns:cac="urn:oasis:names:specification:ubl:schema:xsd:CommonAggregateComponents-2">
<cbc:CustomizationID>urn:cen.eu:en16931:2017#compliant#urn:xoev-de:kosit:standard:xrechnung_3.0</cbc:CustomizationID>
<cbc:ID>2027-0001</cbc:ID>
<cbc:IssueDate>2027-01-15</cbc:IssueDate>
<cbc:DueDate>2027-02-14</cbc:DueDate>
<cbc:InvoiceTypeCode>380</cbc:InvoiceTypeCode>
<cbc:DocumentCurrencyCode>EUR</cbc:DocumentCurrencyCode>
<cbc:BuyerReference>04011000-12345-67</cbc:BuyerReference>
<cac:AccountingSupplierParty>...</cac:AccountingSupplierParty>
<cac:AccountingCustomerParty>...</cac:AccountingCustomerParty>
<cac:PaymentMeans>...</cac:PaymentMeans>
<cac:TaxTotal>...</cac:TaxTotal>
<cac:LegalMonetaryTotal>...</cac:LegalMonetaryTotal>
<cac:InvoiceLine>...</cac:InvoiceLine>
</Invoice>Drei Namespaces sind Standard: der Invoice-Namespace, cbc für Basiskomponenten, cac für Aggregate. Der CustomizationID-Wert teilt dem Validator mit, dass es eine XRechnung ist.
Grundgerüst CII
Die CII-Variante ist verbose-r und nutzt vier Namespaces:
<?xml version="1.0" encoding="UTF-8"?>
<rsm:CrossIndustryInvoice
xmlns:rsm="urn:un:unece:uncefact:data:standard:CrossIndustryInvoice:100"
xmlns:ram="urn:un:unece:uncefact:data:standard:ReusableAggregateBusinessInformationEntity:100"
xmlns:udt="urn:un:unece:uncefact:data:standard:UnqualifiedDataType:100"
xmlns:qdt="urn:un:unece:uncefact:data:standard:QualifiedDataType:100">
<rsm:ExchangedDocumentContext>
<ram:GuidelineSpecifiedDocumentContextParameter>
<ram:ID>urn:cen.eu:en16931:2017#compliant#urn:xoev-de:kosit:standard:xrechnung_3.0</ram:ID>
</ram:GuidelineSpecifiedDocumentContextParameter>
</rsm:ExchangedDocumentContext>
<rsm:ExchangedDocument>
<ram:ID>2027-0001</ram:ID>
<ram:TypeCode>380</ram:TypeCode>
<ram:IssueDateTime>
<udt:DateTimeString format="102">20270115</udt:DateTimeString>
</ram:IssueDateTime>
</rsm:ExchangedDocument>
<rsm:SupplyChainTradeTransaction>...</rsm:SupplyChainTradeTransaction>
</rsm:CrossIndustryInvoice>Achtung: In CII ist das Datumsformat YYYYMMDD mit format="102", in UBL YYYY-MM-DD. Verwechslung ist Klassiker Nr. 1.
Pflichtfelder, ohne die nichts geht
Das ist die Kurzliste – siehe auch Pflichtfelder:
| Feld | UBL-Element | CII-Element |
|---|---|---|
| Rechnungsnummer | cbc:ID | ram:ExchangedDocument/ram:ID |
| Rechnungsdatum | cbc:IssueDate | ram:IssueDateTime |
| Rechnungstyp | cbc:InvoiceTypeCode | ram:TypeCode |
| Währung | cbc:DocumentCurrencyCode | ram:InvoiceCurrencyCode |
| Käuferreferenz | cbc:BuyerReference | ram:BuyerReference |
| Verkäufer | cac:AccountingSupplierParty | ram:SellerTradeParty |
| Käufer | cac:AccountingCustomerParty | ram:BuyerTradeParty |
| Steueraufschlüsselung | cac:TaxTotal/cac:TaxSubtotal | ram:ApplicableTradeTax |
| Summen | cac:LegalMonetaryTotal | ram:SpecifiedTradeSettlementHeaderMonetarySummation |
| Position | cac:InvoiceLine | ram:IncludedSupplyChainTradeLineItem |
Eine Position in UBL
<cac:InvoiceLine>
<cbc:ID>1</cbc:ID>
<cbc:InvoicedQuantity unitCode="HUR">8</cbc:InvoicedQuantity>
<cbc:LineExtensionAmount currencyID="EUR">800.00</cbc:LineExtensionAmount>
<cac:Item>
<cbc:Name>Beratung Migration</cbc:Name>
<cac:ClassifiedTaxCategory>
<cbc:ID>S</cbc:ID>
<cbc:Percent>19</cbc:Percent>
<cac:TaxScheme>
<cbc:ID>VAT</cbc:ID>
</cac:TaxScheme>
</cac:ClassifiedTaxCategory>
</cac:Item>
<cac:Price>
<cbc:PriceAmount currencyID="EUR">100.00</cbc:PriceAmount>
</cac:Price>
</cac:InvoiceLine>HUR ist die Maßeinheit „Stunde" nach UN/ECE Rec. 20. Mehr zur Maßeinheit in BT-130.
Validierung in der eigenen Pipeline
Ihr Generator ist erst dann fertig, wenn jede Ausgabe automatisch durch zwei Schritte geht:
- XSD-Validierung mit dem offiziellen Schema (für UBL:
UBL-Invoice-2.1.xsd, für CII:CrossIndustryInvoice_100pD22B.xsd). - Schematron-Validierung mit den EN-16931- und XRechnung-Regeln (KoSIT-Validator-Konfiguration).
Beides lässt sich in Java mit javax.xml.validation und ph-schematron umsetzen, in Python mit lxml, in .NET mit System.Xml.Schema. In CI integrieren – sonst zerlegt das nächste Refactoring den Output.
Mehr in Integrationstest.
Bibliotheken pro Sprache
Wer nicht alles von Hand baut, nimmt eine Bibliothek:
| Sprache | Bibliothek | Anmerkung |
|---|---|---|
| Java | mustangproject | de facto Standard, ZUGFeRD + XRechnung |
| Java | ph-ubl, ph-cii | Helper-Bibliotheken zu UBL/CII |
| .NET | s2industries.ZUGFeRD | aktiv gepflegt |
| PHP | horstoeko/zugferd | gut dokumentiert |
| Python | drafthorse | CII-Generator |
| JavaScript / TS | @e-rechnung/* | wachsendes Ökosystem |
Vorteil von Bibliotheken: Codeschlüssel, Maßeinheiten, BR-Logik sind eingebaut. Nachteil: Sie sind an deren Datenmodell gebunden.
Häufige Fehler beim Eigenbau
- Falsche Reihenfolge: UBL/CII haben strikte Reihenfolgen. Das Schema schlägt sofort an.
- Beträge mit Komma: Nur Punkt als Dezimaltrennzeichen.
- Rundung inkonsistent: Positions-, Steuer- und Gesamtsummen müssen exakt zusammenpassen (BR-CO-10 ff.).
- Default-Namespace verloren: Wer manuell konkateniert, vergisst Namespace-Präfixe.
- Falscher InvoiceTypeCode: 380 (Rechnung), 381 (Gutschrift), 384 (Korrektur). Mehr in Gutschrift.
- CustomizationID veraltet: bei jedem XRechnung-Versions-Update prüfen.
Versionsmanagement
Die Schemata und Schematron-Regeln entwickeln sich weiter. Empfehlung:
- Aktuelle Version (XRechnung 3.x) als Default.
- Verwendete Version pro Empfänger speichern (manche akzeptieren noch 2.x).
- Validator-Bibliothek nicht dauerhaft auf eine Version pinnen, sondern jährlich aktualisieren.
Häufige Fragen
Lohnt sich der Eigenbau?
Wenn Sie eine Standard-Buchhaltungslösung haben: nein. Wenn Sie eine eigene Plattform betreiben (z. B. Marktplatz, Telco, Energieversorger) oder sehr viele Rechnungen erzeugen, fast immer ja.
Welche Syntax soll ich nehmen?
Wenn Sie hauptsächlich an Behörden und Mittelstand in Deutschland liefern: CII. Wenn Sie international über Peppol versenden: UBL. Im Idealfall können Sie beides.
Reicht XSD-Validierung aus?
Nein. XSD prüft nur die Struktur. Die EN-16931-Regeln werden über Schematron geprüft. Beides ist nötig.
Muss ich PDF/A-3 selbst bauen können?
Nur, wenn Sie ZUGFeRD-Hybriddateien erzeugen wollen. Reine XRechnung ist XML-only. Für PDF/A-3 nutzt fast jeder eine Bibliothek (iText, PDFBox, FPDI), kein Eigenbau.
Wo finde ich offizielle Beispieldateien?
Auf der Seite des KoSIT und im Mustang-Repository. Beide bieten getestete Referenz-Rechnungen, gegen die Sie Ihren Generator regredieren lassen können.