Elektronische Rechnungen mit freier und quelloffener Software erzeugen
Ab dem 1. Januar 2025 sind elektronische Rechnungen für alle B2B-Transaktionen in Deutschland verpflichtend. Einige andere Länder im Europäischen Wirtschaftsraum haben bereits ähnliche Maßnahmen umgesetzt oder werden bald folgen. Das hat zur Folge, dass etliche Firmen in Deutschland mit Hochdruck an der Einführung elektronischer Rechnungen arbeiten, und nicht wenige sich dabei mit erheblichen Schwierigkeiten konfrontiert sehen. Dieser Artikel beschreibt, wie sich elektronische Rechnungen in verschiedenen Formaten komplett mit freier und quelloffener Software erzeugen lassen.

Table Of Contents
GitHub-Repository
Die Software zur Erzeugung elektronischer Rechnungen ist im GitHub-Repository e-invoice-eu
organisiert. Es handelt sich um eine Node.JS-Applikation, genauer gesagt um einen NestJS-Server der lokal installiert und auf jeder Plattform laufen sollte, mit den üblichen Einschränkungen auch unter MS-Windows.
Detaillierte Instruktionen zur Benutzung finden sich in der Dokumantation der Software.
Rechtlicher Hintergrund - EN16931
Die Europäische Union plant die vollständige Digitalisierung von Rechnungen im Verlauf der nächsten Jahre. In diesem Kontext wurde auch die Norm EN16931 veröffentlicht, die Anforderungen an elektronische Rechnungen definiert.
Formate
Zur Zeit werden zwei Formate für Rechnungen gemäß EN16931 akzeptiert. Zum einen ist das UBL (Universal Business Language), zum anderen CII (Cross-Industry Invoice). Bei beiden handelt es sich um XML-Formate.
Eine weitere Ausprägung ist Factur-X bzw. ZUGFeRD. Factur-X wurde in Frankreich entwickelt; ZUGFeRD ist das deutsche Gegenstück. Intern wird als Format CII verwendet, aber das XML-Dokument wird nicht direkt mit den Kunden ausgetauscht, sondern an das PDF angehangen.
Die Möglichkeit, Dokumente an PDFs anzuhängen ist wenig bekannt, und die einzige gängige Software mit graphischer Benutzeroberfläche, die in der Lage ist, PDF-Anhänge darzustellen oder zu extrahieren, ist Adobe Acrobat Reader.

Die Funktionalität verbirgt sich hinter einer Büroklammer am rechten Fensterrand. Ein Klick fördert die Liste mit Anhängen zu Tage. In diesem Fall wurde nicht nur die XML-Rechnung sondern auch ein weiteres Dokument mit Zusatzinformationen angehangen.
Business Terms
Die Norm verwendet "business terms", also Geschäftsterminologie, um die Informationen in einer Rechnung zu beschreiben. Die Rechnungsnummer beispielsweise ist Business-Term 1 oder kurz BT-1, der Ländercode der Käuferpostadresse ist BT-40 und so weiter.
Die Software-Dokumentation enthält eine (fast) vollständige Liste der Business-Terms mit Links zu weiterführenden Informationen.
Business Rules
Der Standard formuliert auch zahlreiche Business-Rules, also Geschäftsregeln, welche inhaltliche Anforderungen an die Rechnung beschreiben. So fordert zum Beispiel die Business-Rule BR-CO-27, dass die Summe aller Nettosummen der Rechnungspositionen (BT-106) gleich dem Nettorechnungsbetrag (BT-131) sein muss.
Hier ist es mit der Standardisierung dann auch schnell vorbei. Mitgliedsstaaten können ihre eigenen Business-Rules definieren. Eine elektronische Rechnung, die in Deutschland gültig ist, muss nicht zwangsläufig auch in Dänemark gültig sein und akzeptiert werden. Das gilt auch umgekehrt. Zum Beispiel hat Deutschland seine eigenen Standards XRECHNUNG-UBL und XRECHNUNG-CII definiert, und Rechnungen, die an die öffentliche Hand ausgestellt werden, müssen diesen Standards folgen.
Diese Erweiterungen werden als Core Invoice Usage Specifications (CIUS) bezeichnet. Das eInvoice Country Factsheet der EU gibt einen Überblick über die in den Mitgliedsländern gültigen Erweiterungen.
Was sind elektronische Rechnungen?
PDF-Dokumente gelten nicht als elektronische Rechnung im Sinne von EN16931, und Word-Dokumente schon gar nicht. Elektronische Rechnungen müssen maschinenlesbar sein. Dokumente im Format Factur-X/ZUGFeRD gelten als maschinenlesbar, weil die Rechnung im XML-Format aus der PDF-Datei extrahiert werden kann.
Dokumentation
Der Text von EN16931 ist nicht frei verfügbar, jedenfalls nicht als Download im Internet. Glücklicherweise hat OpenPeppol - die Organisation hinter PEPPOL (Pan-European Public Procurement OnLine) - die sogenannten Business Interoperability Specifications (BIS) veröffentlicht. Eine dieser Spezifikationen ist die für UBL-Rechnungen und unter https://docs.peppol.eu/poacc/billing/3.0/syntax/ubl-invoice/tree/ verfügbar.
Diese Dokumentation beschreibt die Struktur und alle Anforderungen an UBL-Rechnungsdaten auf sehr übersichtliche und einfach zu verwendende Art. Das ist einer der Gründe, weshalb e-invoice-eu
dieses Format als Basis für das interne Format für Rechnungsdaten verwendet. Der größte Unterschied ist, dass e-invoice-eu
JSON bzw. YAML statt XML verwendet, aber die Struktur ist weitestgehend gleich.
Business-Terms und Business-Rules
Business-Terms und Business-Rules wurden bereits oben beschrieben und werden an vielen Stellen in der Dokumentation erwähnt. Die einschlägigen Business-Rules sind unterhalb der Dokumentation der jeweiligen Elemente aufgelistet. Für das Element /ubl:Invoice/cbc:LineExtensionamount
gelten beispielsweise die Business-Rules BR-12, BR-CO-10, BR-DEC-09, UBL-DT-01 und BR-CL-03.
Kardinalitäten verstehen
Der Standard verwendet "Kardinalitäten", um auszudrücken in welchem Umfang ein bestimmtes Element erforderlich ist. Kardinalitäten werden als zwei Zahlen, die durch zwei Punkte verbunden sind ausgedrückt, zum Beispiel 0..1
. Die erste Zahl gibt die Mindestanzahl an Vorkommen an, die zweite die maximale Anzahl. Eine Mindestanzahl von 0
bedeutet, dass es sich um ein optionales Element handelt.
Für die maximale Anzahl kann auch der Buchstabe n
verwendet werden, der für jede Ganzzahl größer 1 steht.
Eine Kardinalität 1..1
bedeutet allerdings nicht zwangsläufig, dass ein bestimmtes Element in einem Rechnungsdokument vorhanden sein muss. Das hängt auch von den Elternelementen ab.
Beispiel: Das Element /ubl:Invoice/cac:InvoiceLine/cac:AllowanceCharge/cbc:Amount
hat die Kardinalität 1..1
. Das Elterelement /ubl:Invoice/cac:InvoiceLine/cac:AllowanceCharge
hat aber die Kardinalität 0..n
, was bedeutet, dass es optional ist, aber beliebig oft vorhanden sein darf. Das bedeutet, dass cbc:Amount
nur verpflichtend ist, wenn die Rechnungsposition ein cac:AllowanceCharge
hat.
Die Notwendigkeit, dass ein Element vorhanden sein muss, kann sich auch aus Business-Rules ergeben. Zum Beispiel fordert BR-CO-25, falls der fällige Rechnungsbetrag (BT-115) ist, müssen entweder das Fälligkeitsdatum (BT-9) oder die Zahlungsbedingungen (BT-20) angegeben sein, siehe https://docs.peppol.eu/poacc/billing/3.0/syntax/ubl-invoice/cac-PaymentTerms/cbc-Note/.
Es ist ebenfalls möglich, dass bestimmte Element von Gesetzes wegen vorhanden sein müssen. Es ist zum Beispiel laut Standard ausreichend, dass die Käufer- und Lieferantenaddresse alleine aus dem Ländercode besteht. Allerdings wird dies kaum den gesetzlichen Bestimmungen entsprechen, solange andere Adress-Elemente wie Straße und Stadt fehlen.
Code-Listen
Damit elektronische Rechnungen nicht nur maschinenlesbar sondern auch maschinen-verstehbar sind, müssen gewisse textuelle Informationen standardisiert werden, indem die erlaubten Werte auf Werte aus Code-Listen eingeschränkt werden.
So hat zum Beispiel die in Rechnung gestellte Menge /ubl:Invoice/cac:InvoiceLine/cbc:InvoicedQuantity ein Attribut "@unitCode", dass einen standardisierten Code für die Maßeinheit statt eines Textes wie Stück, Stk., kg oder Stunde angeben muss. Die Peppol-Dokumentation fordert, dass dieser Code der "UN/ECE-Empfehlung 20 mit der Erweiterung aus der Empfehlung 21" entsprechen muss.
Alle Code-Listen können auf der PEPPOL-Site https://docs.peppol.eu/poacc/billing/3.0/ im Abschnitt "Code lists" gefunden werden. Ein Klick auf den Link Recommendation 20, including Recommendation 21 codes - prefixed with X (UN/ECE) öffnet eine Seite mit der Code-Liste, die alle legalen Werte zeigt. Dort ist ebenfalls ersichtlich, für welche Elemente diese Code-Liste verwendet wird.
"Stück" beispielsweise können mit H87
oder XPP
kodiert werden. Viele Einheiten können mit zwei Codes kodiert werden; einmal mit und einmal ohne ein führendes X.
Die Validierung in E-Invoice-EU erzwingt übrigens die Verwendung der korrekten Code-Listen.
Status von E-Invoice-EU
Meine Firma stellt bereits seit Jahren elektronische Rechnungen aus. Dafür kamen ein selbstgeschriebenes Perl-Modul, LibreOffice, Java, und exiftool zum Einsatz aber dieser Ansatz war nicht sehr flexibel, weshalb ich entschieden habe, die Software mit Node.JS komplett neuzuschreiben, weil JavaScript heute eine bessere Akzeptanz hat.
Zum jetzigen Zeitpunkt kann die Software Rechnungen in allen in der EU akzeptierten Formaten zu erzeugen.
Entgegen der ursprünglichen Planung ist es gelungen, die Erzeugung revisionssicherer PDF/A-Dokumente für Factur-X/ZUGFeRD alleine mit JavaScript, ohne Ghostscript und Exiftool zu implementieren.
Allgemeiner Workflow
Die Zielgruppe für die Software sind kleine und mittelgroße Firmen, die keine ERP-Software sondern eine Tabellenkalkulation zur Erzeugung von Rechnungen verwenden.
Zuordnung der Tabellenkalkulations-Daten
Im ersten Schritt müssen die Daten aus der Tabellenkalkulation auf Rechnungsdaten abgebildet werden. Der entsprechende Endpunkt erwartet daher eine Zuordnungsdatei (Mapping-Datei) und eine Tabellenkalkulationsdatei und erzeugt daraus eine JSON-Datei mit den Rechnungsdaten. Das Schema für diese JSON-Daten ist 100 % äquivalent zur XML-Struktur, die in der PEPPOL-Doku unter https://docs.peppol.eu/poacc/billing/3.0/syntax/ubl-invoice/ beschrieben ist.
Auf dieses Format wird als "internes Format" Bezug genommen.
Elektronische Rechnungen erzeugen
Ein anderer Endpunkt kann dazu genutzt werden, um das eigentliche Rechnungsdokument auf Basis der im internen Format zur Verfügung gestellten Daten entweder als XML oder PDF zu erzeugen. Wenn man keine Tabellenkalkulation zur Erzeugung der Daten im internen Format verwenden will, kann dieser Endpunkt genutzt werden, um das Rechnungsdokument, das an Kunden versendet werden kann, zu erzeugen.
Mappen und Erzeugen auf einmal
Die Umsetzung der Daten und die Erzeung der Rechnung kann auch in einem einzigen Schritt erfolgen. Dies ist die empfohlene Vorgehensweise, wenn eine Tabellenkalkulation für die Rechnungsdaten verwendet wird.
Validierung
Der Service validiert die Eingabedaten gegen Schemata (genauer gesagt JSON schema). Das schließt allerdings lediglich die Prüfung struktureller Restriktionen ein, wie zum Beispiel die Kardinalität von Elementen. Business-Rules werden zur Zeit nicht überprüft. Das wird sich in der Zukunft eventuell ändern, aber derzeit müssen entweder Online-Validatoren verwendet oder ein Offline-Validator installiert werden. Alle verfügbaren Offline-Validatoren erfordern eine Java-Laufzeit-Umgebung und sind dementsprechend langsam.
Das E-Invoice-EU-Projekt bietet Wrapper-Skripte um diese Validatoren, siehe https://github.com/gflohr/e-invoice-eu/tree/main/contrib/validators für weitere Informationen.
Es wird dringend empfohlen, den Validierungsschritt nicht zu überspringen, besonders am Anfang. Die Erfahrung zeigt, dass Rechnungen die Validierung anfangs praktisch nie passieren, und es ist daher sehr wahrscheinlich, dass Kunden die Dokumente zurückweisen werden.
Einfach mal loslegen
Wer den Service für das eigene Business verwenden will, sollte unbedingt mit den zur Verfügung gestellten Beispieldateien beginnen. Diese können dann an die eigenen Bedüfnisse angepasst werden.
Die Tabelle
Eine Beispiel-Tabelle gibt es in https://github.com/gflohr/e-invoice-eu/tree/main/contrib/templates.
Formate
Die Beispieltabelle wird im Open-Document-Format mit der Dateinamenserweiterung .ods
zur Verfügung gestellt. Es können aber auch Excel-Dateien mit der Erweiterung .xlsx
verwendet werden. Genau gesagt, kann jedes Format, das von der Bibliothek SheetJS unterstützt wird, verwendet werden. Siehe dazu die Dateiformatsseite der SheetJS-Dokumentation
Druckbereiche
Die Tabelle ist mit ziemlich vielen zusätzlichen Informationen für die elektronische Rechnung verunstaltet. Es ist wichtig, diese zusätzlichen Daten in der PDF- bzw. Papierversion zu verstecken, indem Druckbereiche definiert werden.
Wer lediglich XML-Dokumente erzeugen will, kann dies natürlich ignorieren. Will mann allerdings in der Zukunft eines der hybriden Factur-X/ZUGFeRD-Formate verwenden, sollte man auch ein Auge auf das Design des Dokuments haben. Und auch mit XML ist es möglich, eine PDF-Version der Rechnung als base64-kodierten String einzubetten.
Stellt man hybride Rechnungen aus, fordert zumindest die deutsche Finanzverwaltung, dass die PDF- und XML-Versionen inhaltsgleiche Mehrstücke darstellen. Das bedeutet, das beiden Dokumente die exakt gleichen steuerrelevanten Informationen enthalten müssen. Dies muss ebenfalls beachtet werden.
Codelisten
Wie oben erwähnt, müssen viele Daten mit Codelisten kodiert werden, zum Beispiel Maßeinheiten oder Steuerkategorien. Speziell Steuerkategorien müssen auch noch zu Steuersätzen zugeordnet werden.
Diese Codelisten lege ich normalerweise in separaten Tabs ab. Der Tab "Tax" (also Steuer) enthält zum Beispiel fünf Spalten. Die erste Spalte enthält den Code, der in der Druck-Version der Rechnung angezeigt wird, also prinzipiell die Werte aus der entsprechenden Codeliste Duty or tax or fee category code (Subset of UNCL5305) mit der Ausnahme der Kategorie "S", wo der Steuersatz als Suffix zugefügt wurde.
Wo auch immer Steuerinformationen benötigt werden, wurden Datengültigkeitsbereiche definiert, die auf diese Liste von Steuerkategorien zeigen. In LibreOffice lässt sich dies erreichen, indem man die entsprechenden Zellen auswählt, und dann den Bereich mit dem Menüeintrag Daten -> Güligkeit...
auswählt. Klickt man danach in die Zelle, lassen sich die Werte aus einem Drop-Down auswählen.
Allein, S19
oder S7
sind keine gültigen Codes. Die Zahlen 19 und 7 wurden lediglich als Hinweis auf den Steuersatz angehängt. Der korrekte Code für beide lautet S
. Im Tab "Tax" findet sich dieser Code in Spalte C. Wie kann dieser jetzt in die relevanten Zellen kopiert werden? Dies passiert mithilfe der Formel VLOOKUP
(SVERWEIS
in der deutschen Version, aber VLOOKUP
funktioniert ebenfalls).
Beispiel:
=VLOOKUP(H23, Tax.$A$2:$C$12, 3, 0)
Diese Formel macht einen vertikalen Lookup oder senkrechten Verweis. Im Detail funktioniert das so:
Funktion:
VLOOKU
P steht für "vertikalen Lookup" oder "senkrechten Verweis." Sie sucht nach einem Wert in der ersten Spalte eines Bereichs und liefert einen Wert in der selben Zeile in der angegebenen Spalte zurück.Lookup-Wert:
$H23
- Dies ist der Wert, nach dem gesucht wird. Das Dollarzeichen vor dem Spaltenbuchstaben ($H
) zeigt an, dass die Spaltenreferenz absolut ist und sich nicht ändert, wenn die Formel in eine andere Zelle kopiert wird. Die Zeilennummer (23
) dagegen ist relativ, so dass die Referenz sich zu$H24
,$H25
, etc. ändert, wenn die Formel nach unten kopiert wird.Tabellen-Array:
Tax.$A$2:$C$12
- Dies gibt den Bereich an, in dem dieVLOOKUP
-Funktion sucht. Die Verwendung von$A$2:$C$12
bedeutet, dass sowohl die Spalten (A
undC
) als auch die Zeilennummern (2
und12
) absolute Referenzen sind. Wenn die Formel kopiert wird, bezieht sich diese Referenz auf exakt diese Zellen, was wichtig ist, damit die Formel immer auf die gewünschten Daten zeigt.Spalten-Index:
3
- Damit wird angegeben, dassVLOOKUP
einen Wert aus der dritten Spalte des definierten Bereichs zurückgibt. In diesem Fall wird sie einen Wert aus SpalteC
zurückgeben, weil der Bereich mit SpalteA
beginnt. Die SpalteC
des TabsTax
enthält den korrekten Code aus der Code-Liste.Bereichs-Lookup:
0
- Dies gibt an, dass ein exakter Treffer gewünscht ist. Der Wert0
(oderFALSE
in einigen anderen Excel-Kontexten) weist die Funktion an, einen exakten Treffer für den Wert in$H23
zu finden.
Die Übertragung der Steuerrate in Prozent passiert auf exakt die gleiche Weise, ebenso wie die Zuordnung der Maßeinheiten zu Unit-Codes.
All diese Felder kann man natürlich auch von Hand befüllen, was aber fehlerträchtig ist. Es lohnt sich also, sich zu bemühen, die Formel stattdessen zu verstehen.
Der Rest der Tabelle sollte selbsterklärend sein, weil nur Standard-Features benutzt werden. Bis man mit der Datei vertraut ist, sollte man allerdings vor der Bearbeitung immer auf den aktuellen Wert der Zelle schauen, damit nicht unbeabsichtigt Formeln überschrieben werden.
Mapping-Definition
Zum Beispiel-Rechnungstemplate gibt es eine begleitende Mapping Datei im YAML-Format, contrib/mappings/default-invoice.yaml
. Da die YAML-Syntax eine Obermenge der JSON-Syntax ist, kann man auf Wunsch auf JSON verwenden. YAML hat allerdings den Vorzug, dass es Kommentare in der Datei erlaubt.
Sinn der Mapping-Datei ist die Übertragung der tabellarischen Daten aus der Tabellenkalkulation in die Baumstruktur der Rechnungsdaten im internen Format.
Allgemeiner Aufbau
meta:
sectionColumn:
Invoice: L
ubl:Invoice:
\# ... omitted for brevity
Der erste Abschnitt der Datei ist der meta
-Abschnitt, der zusätzliche Informationen für die Zuordnung enthält. Im Moment gibt es nur die Eigenschaft sectionColumn
für Sektions-Spalten, deren Sinn weiter unten erklärt wird.
Der zweite Abschnitt ist ubl:Invoice
. Das JSON-Schema für diesen Abschnitt ist identisch zum Rechnungsdaten-Schema, dem "internen Format", mit dem einzigen Unterschied, dass die Werte der Blattknoten des Baums nicht (notwendigerweise) echte Werte sind, sondern in der Regel Referenzen auf Tabellenzellen, die dem Service mitteilen, wo eine bestimmte Information gefunden werden kann.
Zellenreferenzen
Referenzen auf Zellen sehen exakt so aus, wie man es von einer Tabellenkalkulation gewohnt ist. Sie beginnen immer mit einem Gleichheitszeichen, gefolgt vom Namen der Zelle.
Beispiel:
ubl:Invoice:
\# ...
cbc:ID: =R2
cbc:IssueDate: =Invoice.I6
\# ...
Die erste Referenz =R2
bedeutet, dass sich der Wert von cbc:ID in Spalte R
, Zeile 2
der Tabelle befindet.
Die zweite Referenz =Invoice.I6
verwendet auch den Namen des Tabs. Wird der Name des Tabs weggelassen, wird die Zelle im ersten Tab der Datei gesucht. Hält man alle Rechnungsdaten in einem einzigen Tab, kann man dieses Feature also komplett vergessen, und immer einfach den Zellennamen vewenden.
Konstante Werte
Alles, was nicht mit einem Gleichheitszeichen beginnt, ist ein konstanter Wert. Das ist praktisch, um Dinge hartzukodieren:
ubl:Invoice:
\# ...
cbc:Name: Acme Ltd.
cbc:DocumentTypeCode: "380"
\# ...
Der cbc:Name
im obigen Beispiel wird unverändert in die Rechnungsdaten kopiert.
Es ist zu beachten, dass nur Strings, also Zeichenketten als konstante Werte erlaubt sind. Zahlen müssen deshalb in Anführungszeichen gesetzt werden, so wie bei cbc:DocumentTypeCode
oben.
Will man einen Konstante verwenden, die mit einem Gleichheitszeichen beginnt, muss man ein (gerades) einfaches Anführungszeichen '
voranstellen, dass bei der Übertragung entfernt wird. Will man eine Konstante, die mit einem solchen einfachen Anführungszeichen beginnt, verwenden, muss man am Anfang zwei Anführungszeichen verwenden, zum Beispiel ''Wert in Anführungszeichen'
.
Es ist wichtig, dass die geraden Anführungszeichen nicht mit Backticks \`` oder anderen Anführungszeichen wie
‘oder
’` vertauscht werden, besonders beim Kopieren und Einfügen aus anderer Software.
Sektionen
Außer, wenn die Rechnung lediglich eine Position hat, werden einige Zellen keine fixe Position haben. Zum Beispiel werden alle Summen und Zwischensummen der Rechnung nach unten verschoben, wenn weitere Positionen zugefügt werden. Dies wird durch das Konzept von Sektionen in der Tabelle unterstützt.
Eine Spalte in jedem Tab muss für die Namen der Sektionen reserviert werden. Das wird durch das Objekt /meta/sectionColumns
im Mapping angegeben, dessen Schlüssel die Namen der Tabs sind. Die entsprechenden Werte sind die Namen der Sektions-Spalten.
Groß- und Kleinschreibung bei Sektions-Namen muss beachtet werden!
Die Sektion, mit der Summe aller Rechnungspositionen kann zum Beispiel mit Subtotal
. Dieser Name wird in die Zeile, in der die Sektion anfängt in die Sektions-Spalte eingetragen.

Für Zellenreferenzen muss jetzt eine leicht veränderte Syntax verwendet werden. Sektions-Namen beginnen immer mit einem Doppelpunkt :
(weil Doppelpunkte nicht als Teil eines Tabnamens erlaubt sind):
cbc:Amount: =:Subtotal.J1
Diese Referenz wird als erste Zeile in der Sektion Subtotal
interpretiert. Sind die Daten auf mehrere Tabs verteilt, kann man auch =Invoice:Subtotal.J1
verwenden, um anzugeben, dass sich die Zelle im Tab Invoice
befindet.
Zugeordnete Sektionen
Im Allgemeinen dürfen Sektions-Namen nur genau einmal pro Tab verwendet werden. Eine Ausnahme stellen zugeordnete Sektionen dar.
Einige Teile der Rechnung können wiederholt auftreten. Ein typisches Beispiel dafür sind die Rechnungspositionen, weil jede Rechnung eine Zeile pro in Rechnung gestellte Leistung enthält. Ein Mapping dafür kann ungefähr so aussehen:
ubl:Invoice:
\# ...
cac:InvoiceLine:
section: :InvoiceLine
cbc:ID: =:InvoiceLine.A1
cbc:InvoicedQuantity: =:InvoiceLine.E1
cbc:InvoicedQuantity@unitCode: =:InvoiceLine.M1
cbc:LineExtensionAmount: =:InvoiceLine.J1
cbc:LineExtensionAmount@currencyID: EUR
Das Element /ubl:Invoice/cac:InvoiceLine
wird einmal pro Rechnungsposition erzeugt. Es handelt sich also um eine Liste bzw. um ein Array. Für solche Arrays muss eine spezielle Eigenschaft section
mit dem Namen der Sektion, diesem Array zugeordnet ist, definiert werden. Ein Tabname kann optional mit section: Invoice:InvoiceLine
zugefügt werden. Es muss sichergestellt sein, dass das Tab einen entsprechenden Eintrag für die Sektions-Spalte hat.
Referenzen mit zugeordneten Sektionen funktionieren mehr oder weniger wie mit normalen Sektionen, nur dass die Zeilennummer nicht relativ zur ersten Sektion, sondern relativ zur aktuellen Sektion interpretiert wird.
Meistens werden Referenzen innerhalb solcher Listen immer die gleiche Sektion referenzieren. Man kann allerdings auch eine andere Sektion referenzieren, allerdings nur, wenn es keine zugeordnete Sektion ist. Genauso kann man natürlich auch Informationen aus dem Rechnungskopf (normalerweise kein Teil einer Sektion) oder Konstanten referenzieren.
Verschachtelte zugeordnete Sektionen
Sektionen können beliebig tief verschachtelt werden. Ein wichtiges Beispiel dafür sind Zu- und Abschläge auf Positionsebene. Genau wie die Positionszeile selber, kann es eine beliebige Anzahl an Zu- und Abschlägen für jede Position geben.
Es muss lediglich darauf geachtet werden, dass für jede Ebene ein eigener Sektions-Name verwendet wird. Zu- und Abschläge können auf Dokumentenebene, auf Positionsebene und auf Preisebene auftauchen. Verwendet man alle drei Type, müssen auch unterschiedliche Sektions-Namen für jeden Typ verwendet werden.
Beispiel:

In diesem Beispiel wurde der Name ACInvoiceLine
für Zu- und Abschläge auf Positionseben gewählt. Zur ersten Zeile gibt es zwei Zu- oder Abschläge mit einem Beträgen von 33,50 € und 12,28 €. Zur zweiten Zeile gibt es einen Zu- oder Abschlag in Höhe von 23,04 €. Zu den restlichen Zeilen gibt es keine Zu- oder Abschläge. Der relevante Teil des Mappings hierfür sieht folgendermaßen aus:
ubl:Invoice:
\# ...
cac:InvoiceLine:
section: :InvoiceLine
cbc:LineExtensionAmount: =:InvoiceLine.J1
cac:AllowanceCharge:
section: :ACInvoiceLine
cbc:Amount: :ACInvoiceLine.J1
Sowohl die Zeilensumme cbc:LineExtensionAmount
als auch die Beträge der Zu- und Abschläge cbc:AllowancheCharge/cbc:Amount
stehen in Spalte J
, aber die Zeilensumme steht in der ersten Zeile der jeweiligen Sektion für die Position, und der Betrag des Zu- oder Abschläge in der ersten Zeile der Sektion für die Zu- und Abwschläge, die sich innerhalb der Sektion für die Position befindet.
Attribute
Einige Elemente haben (XML)-Attribute. So haben zum Beispiel die meisten Geldbeträge ein verpflichtendes Attribut currencyID
. Für die Definition einer Referenz für solche Attribut wird der Attributname mit einem führenden Klammeraffen @
an den Elementnamen angehangen:
cbc:Amount: :ACInvoiceLine.J1
cbc:Amount@currencyID: EUR
Alternative Mappings
Das Mapping der Daten aus der Tabellenkalkulation auf die Rechnungsdaten ist zugegebenermaßen etwas kompliziert.
Ist man der Auffassung, dass es zu kompliziert ist, oder aber die Features für den eigenen Anwendungsfall nicht ausreichen, steht es es einem frei, eine eigene Version zu bauen. So lange es gelingt, Rechnungsdaten im internen Format zu produzieren, kann der Mappingschritt auch entfallen.
Häufige Mapping-Probleme
Obwohl das Format der Rechnungsdaten gut dokumentiert ist, werfen einige Felder Fragen auf.
Dokumententyp-Code
Der /ubl:Invoice/cbc:DocumentTypeCode
muss der Liste Invoice type code (UNCL1001 subset) entnommen werden. Der meistverwendete Code ist 380 für Handelsrechnungen.
Für Gutschriften (selbst-ausgestellte Rechnungen) schlägt die Dokumentation von Factur-X/ZUGFeRD den Typ-Code 389 vor. Dieser Code ist allerdings in der PEPPOL-Liste nicht enthalten.
Das gleiche Problem existiert für Rechnungskorrekturen, für die in der Dokumentation von Factur-X der Code 384 vorgeschlagen wird, der ebenfalls in der PEPPOL-Liste fehlt.
Wer etwas zur Aufklärung dieses Sachverhalts beitragen kann, wird gebeten, einen Kommentar zu hinterlassen.
Käuferreferenz (Buyer Reference)
Das Feld /ubl:Invoice/cbc:BuyerReference
ist eigentlich ein optionales Feld. Die Business-Regel PEPPOL-EN16931-R003 fordert allerdings, dass entweder eine Käuferreferenz oder aber eine Bestellreferenz /ubl:Invoice/cac:OrderReference/cbc:ID
angegeben werden müssen.
Die Bestellreferenz ist normalerweise eine vom Kunden verwendete Bestellnummer für bestellten Leistungen.
Die Käuferreferenz hat dagegen eine spezielle Semantik in Rechnungen für die öffentliche Hand. Der deutsche Standard XRECHNUNG schreibt vor, dass dieses Feld vorhanden und mit der sogenannten Leitweg-ID gefüllt sein muss. Diese Leitweg-ID findet man oft im Internet. Ansonsten fragt man einfach den Kunden, der die eigene ID kennen wird.
Endpoint-IDs
Sowohl Lieferant als auch Kunde müssen durch Endpunkt-IDs identifiziert werden. Die entsprechenden Felder sind /ubl:Invoice/cac:AccountingSupplierParty/cac:Party/cbc:EndpointID (BT-34) und /ubl:Invoice/cac:AccountingCustomerParty/cac:Party/cbc:EndpointID (BT-49). Eine sichere Wahl ist die jeweilige USt-ID mit der @schemeID
9930 für deutsche USt-IDs. Für andere Länder kann die @schemeID
der Code-Liste CEF Electronic Address Scheme (EAS). entnommen werden. Andere populäre Optionen sind die bereits erwähnte Leitweg-ID (@schemeID
0204) oder der EAN Location Code (@schemeID
0088), auch bekannt als Global Location Number GLN.
Die Dokumentation zu Factur-X/ZUGFeRD erlaubt hier auch Mail-Adressen mit der @schemeID
EM
. Hier gibt es leider wieder eine Diskrepanz zwischen dieser Dokumentation und der Dokumentation von PEPPOL, denn EM
fehlt in der PEPPOL-Liste. Wer dazu etwas weiß, wird ebenfalls gebeten, einen Kommentar zu hinterlassen.
Für größere Kunden ist es durchaus üblich, dass sie genaue Vorgaben machen, welche Endpunkt-IDs mit welcher @schemeID
zu verwenden sind. Dies wird normalerweise vom Kunden mitgeteilt. Ansonsten ist die USt-ID aber ein guter Standardwert.
IBAN und BIC
Sowohl IBAN als auch BIC/SWIFT-Code sind eigentlich optional. Viele Kunden werden aber auf der Angabe bestehen, damit die Bezahlung organisiert werden kann. Auch Kontoinhaber und Zahlungstyp werden benötigt. Die entsprechenden Feler sind ubl:Invoice/cac:PaymentMeans/cac:CardAccount/cbc:HolderName
für den Namen des Kontoinhabers, ubl:Invoice/cac:PaymentMeans/cbc:PaymentMeansCode
für den Zahlungstyp-Code (eine Banküberweisung hat den Code 30), ubl:Invoice/cac:PaymentMeans/cac:PayeeFinancialAccount/cbc:ID
für die IBAN, und ` ubl:Invoice/cac:PaymentMeans/cac:PayeeFinancialAccount/cac:FinancialInstitutionBranch/cbc:ID für den BIC/SWIFT-Code.
Zu- und Abschläge
Zu- und Abschläge gibt es auf Dokumentenebene, auf Positionsebene und auf Preisebene. Sie sind jeweils in einer Gruppe cac:AllowanceCharge
definiert, zum Beispiel /ubl:Invoice/cac:AllowanceCharge
für Zu- und Abschläge. Entgegen der Gepflogenheiten auf Papierrechnungen, haben jedoch auch Abschläge einen positiven Betrag. Ob es sich um einen Zu- oder um einen Abschlag handelt, ergibt sich jeweils aus dem Wert des Feldes cbc:ChargeIndicator
innerhalb der Gruppe, z. B. /ubl:Invoice/cac:AllowanceCharge/cbc:ChargeIndicator
, dass true
für Zuschläge und false
für Abschläge enthalten muss.
Damit Abschläge in der Print-Version mit negativem Vorzeichen dargestellt werden, muss in der Tabellenkalkulation etwas getrickst werden. Im Beispiel-Template wird das demnächst gezeigt werden.
Customization-ID und Profile-ID
Die Felder /ubl:Invoice/cbc:CustomizationID
und /ubl:Invoice/cbc:ProfileID
sind im Standard Pflichtfelder. In Mappings oder Rechnungs-Eingabe-Daten für e-invoice-eu
sind sie dagegen optional, weil ihre Werte sich aus dem gewählten Rechnungsformat ergeben. Lässt man die Werte weg, was die empfohlene Vorgehensweise ist, werden die jeweils aktuellen Werte verwendet. Wenn man weiß, was man tut, kann man sich allerdings darüber hinwegsetzen und seine eigenen Werte für die Customization- und Profile-ID übergeben.
Zusammenfassung
Die hier angegebenen Informationen sollten ausreichen, um die erste elektronische Rechnung zu erzeugen. Am Projekt e-invoice-eu
wird noch intensiv entwickelt. Informationen über neue Features gibt es auf der GitHub-Seite von e-invoice-eu
und hier.
Leider bin ich nicht im Stande, feste Daten für die Einführung bestimmter Features zu nennen. Anfragen für kommerziellen Support und Beratungsleistungen rund um elektronische Rechnungen können jedoch an die Mailadresse info@cantanea.com gestellt werden.
Kommentar hinterlassen
Die Angabe der E-Mail-Adresse ist freiwillig. Bitte bedenke aber, dass ohne gültige E-Mail-Adresse keine Benachrichtigung über eine Antwort möglich ist. Die Adresse wird nicht zusammen mit dem Kommentar angezeigt!