Erweiterte Warenrechnung

Eine vollständige Warenrechnung im Profil EXTENDED, Schritt für Schritt nachgebaut: sechs Positionen mit Verpackungsangaben und Artikelrabatten, zwei Steuersätze, Rechnungsrabatte auf Dokumentenebene, Transportkosten, Skonto und abweichende Waren- und Rechnungsempfänger.

Das ist nicht irgendein Beispiel, sondern die offizielle Beispielrechnung X19_01_Warenrechnung aus der Factur-X-/ZUGFeRD-Dokumentation (FeRD-Beispielpaket, ZUGFeRD 2.5.0, Profil EXTENDED). Der Code unten erzeugt genau diesen Beleg – Feld für Feld, in derselben Reihenfolge. Wer das Referenz-XML neben die Ausgabe legt, sieht dieselbe Baumstruktur.

Einige Verweise auf dieser Seite führen in die vertiefende Dokumentation im Kunden-Bereich und verlangen eine Anmeldung.

Fachlich handelt es sich um eine Lieferung aus dem Lebensmittel-Großhandel an eine Supermarkt-Filiale. Genau diese Mischung macht das Beispiel lehrreich: Fast jedes Feld, das im Alltag Fragen aufwirft, kommt darin mindestens einmal vor.

Die folgenden Abschnitte bauen aufeinander auf. Alle Ausschnitte gehören zu einem Programm und können in dieser Reihenfolge hintereinander geschrieben werden. Die Kommentare nennen jeweils die Nummern aus der EN 16931 und der ZUGFeRD-EXTENDED-Erweiterung; über die Referenz finden Sie zu jeder Nummer das zugehörige XML-Element.

Sämtliche EXTENDED-Felder dieses Beispiels – Dokumentenname, Testkennzeichen, Rechnungsempfänger, Verpackungsangaben, Bestandteile einer Verkaufseinheit, Transportkosten und die erweiterten Steuerbasen – werden nur geschrieben, wenn am Ende auch tatsächlich als Profile.Extended gespeichert wird. Ein kleineres Profil ist kein Fehler; die Felder fallen dann einfach lautlos weg.

1. Rechnungskopf

CreateInvoice setzt die drei Pflichtangaben des Kopfes: Rechnungsnummer (BT-1), Rechnungsdatum (BT-2) und Rechnungswährung (BT-5). Alles Weitere wird anschließend an das zurückgegebene Objekt gehängt.

2. Freitexte

Freitexte landen in ram:IncludedNote (BG-1). Zwei Codes steuern die maschinelle Auswertbarkeit: subjectCode (BT-21, UNTDID 4451) sagt, worüber die Notiz spricht, contentCode (BT-X-5) ist ein zusätzlicher standardisierter Textbaustein.

Die beiden Codes sind nicht frei kombinierbar: ST1, ST2 und ST3 gehören zu AAK (Rabatt- und Bonusvereinbarungen), EEV, WEB und VEV zu AAJ (Eigentumsvorbehalt).

3. Verkäufer

SetSeller füllt ram:SellerTradeParty (BG-4). Neben der Anschrift trägt die Partei zwei Kennungen: die interne Lieferantennummer (id, BT-29) und die GLN (globalID mit schemeID 0088). Kontakt, elektronische Adresse und Steuerregistrierung kommen über eigene Methoden dazu.

4. Käufer

ram:BuyerTradeParty (BG-7) funktioniert identisch. Wichtig für das Verständnis der nächsten Abschnitte: Der Käufer ist hier die Zentrale – geliefert wird an eine Filiale, die Rechnung geht an einen dritten Standort.

5. Belegverweise im Kopf

Vier Verweise verknüpfen die Rechnung mit dem übrigen Belegfluss: Bestellung, Rechnungsdatenblatt, Lieferschein und Lieferdatum. Weitere Referenzarten – etwa Projekt- und Vertragsnummern – beschreibt Dokumentreferenzen.

6. Warenempfänger und Rechnungsempfänger

Warenempfänger (BG-13) und Rechnungsempfänger (BG-X-36) werden nicht über Set…-Methoden, sondern als Party-Objekte gesetzt. Der Warenempfänger trägt hier nur eine GLN und keine interne ID; über ShipToContact.OrgUnit kommt zusätzlich die Abteilung in der Filiale in den Beleg.

7. Rechnungspositionen

Sechs Positionen (BG-25), jede mit einer eigenen Besonderheit. Das wiederkehrende Muster ist in allen sechs gleich:

Parameter BT-Code Bedeutung
lineID BT-126 Positionsnummer. Wird sie weggelassen, vergibt FactoorSharp sie automatisch – siehe Positions-IDs.
id BT-157 GTIN mit schemeID 0160 (GS1), in FactoorSharp GlobalIDSchemeIdentifiers.EAN.
sellerAssignedID BT-155 Artikelnummer des Lieferanten.
buyerAssignedID BT-156 Artikelnummer des Käufers für dieselbe Ware.
grossUnitPrice BT-148 Bruttopreis, also der Listenpreis vor Artikelrabatt.
netUnitPrice BT-146 Nettopreis nach Artikelrabatt – die Basis der Positionssumme.
lineTotalAmount BT-131 Positionssumme = netUnitPrice × billedQuantity.
PackageQuantity / PackageUnitCode BT-X-9 Versandeinheit, nur EXTENDED. XCT = Karton, XBC = Kiste, XBO = Flasche, XPX = Palette.

Position 1 – Artikelattribut und Versandeinheit

Die einfachste Position: keine Rabatte, ein Artikelattribut (BG-32) und die Angabe, in wie vielen Kartons die Ware geliefert wird. Zu den Artikelattributen gibt es eine eigene Seite: Produktmerkmale.

Position 2 – Artikelrabatte im Bruttopreis

Hier weichen Brutto- und Nettopreis voneinander ab, weil zwei Artikelrabatte im Spiel sind. Diese Rabatte stehen nicht auf Dokumentebene, sondern als ram:AppliedTradeAllowanceCharge innerhalb des Bruttopreises (BT-147).

Die Beträge sind pro Einheit zu verstehen, nicht für die ganze Position: 1,50 − 0,03 − 0,02 = 1,45. FactoorSharp rechnet den Nettopreis nicht aus – er wird gesetzt, die Rabatte erklären ihn.

Position 3 – Naturalrabatt

Zehn Stück derselben Ware ohne Berechnung. Die Position bleibt im Beleg, damit die gelieferte Menge nachvollziehbar ist, trägt aber 0,00 € zur Summe bei. Der Grund steht als Positionsbeschreibung (BT-154) daneben.

Positionen 4 und 5 – abweichende Einheiten und Käufer-Artikelnummer

Position 4 zeigt, dass Abrechnungs- und Versandeinheit verschieden sein dürfen: abgerechnet wird in Kisten, versandt in Flaschen. Position 5 ist das zugehörige Leergutpfand und führt zusätzlich die Artikelnummer des Käufers (BT-156).

Position 6 – Mischpalette mit Bestandteilen

Die Palette wird als eine Position abgerechnet, ihr Inhalt aber über IncludedReferencedProducts (BG-X-1) aufgeschlüsselt. Diese Angabe ist rein informativ: Preise und Steuern hängen weiterhin an der übergeordneten Position.

8. Rechnungsrabatte auf Dokumentenebene

Zwei Rabatte – einer prozentual, einer als fester Betrag – werden über ram:SpecifiedTradeAllowanceCharge (BG-20) abgebildet.

Ein Dokumentrabatt gilt immer nur für einen Steuersatz. Da der Beleg 19 % und 7 % mischt, muss jeder Rabatt zweimal angelegt werden, mit der jeweils passenden Bemessungsgrundlage. Aus zwei Rabatten werden so vier Aufrufe. Die Reihenfolge im XML entspricht der Aufrufreihenfolge im Code.

Die Bemessungsgrundlagen sind hier nicht einfach die Positionssummen: Für 19 % ergäben die Positionen 1, 4 und 5 zusammen 326,50 €, vereinbart sind aber nur 280,00 € – Leergutpfand und Teile der Ware sind rabattbefreit. Für 7 % gilt die volle Summe der Positionen 2, 3 und 6: 130,70 €.

9. Transportkosten

Logistikkosten haben in CII ein eigenes Element und sind nicht dasselbe wie ein gewöhnlicher Zuschlag über AddTradeCharge. In der XRechnung ist das Element nicht zulässig, in ZUGFeRD EXTENDED schon.

10. Umsatzsteueraufstellung

Je Kombination aus Steuersatz und Steuerkategorie ist eine Gruppe ram:ApplicableTradeTax (BG-23) verpflichtend – hier also zwei. FactoorSharp summiert die Beträge nicht selbst; die Werte kommen aus Ihrer Buchhaltung und werden gesetzt. Der Rechenweg dieses Belegs:

Feld 19 % (Pos. 1, 4, 5) 7 % (Pos. 2, 3, 6)
lineTotalBasisAmount 326,50 130,70
allowanceChargeBasisAmount −5,60 − 2,50 + 3,00 = −5,10 −2,61 − 0,50 = −3,11
basisAmount 326,50 − 5,10 = 321,40 130,70 − 3,11 = 127,59
taxAmount 321,40 × 19 % = 61,066 → 61,07 127,59 × 7 % = 8,9313 → 8,93

Der Zuschlag für die Transportkosten geht mit positivem Vorzeichen in die 19-%-Zeile ein, die Rabatte mit negativem. Mehr zum Umgang mit Steuerbeträgen, insbesondere bei abweichender Steuerwährung, steht unter Umsatzsteuerbeträge.

11. Zahlungsbedingung mit Skonto

Der Skonto steht nicht nur als Satz im Freitext, sondern auch maschinell auswertbar im XML – dafür sorgt PaymentTermsType.Skonto.

12. Gesamtsummen

SetTotals schreibt ram:SpecifiedTradeSettlementHeaderMonetarySummation (BG-22). Auch diese Werte werden gesetzt, nicht berechnet:

BT-Code Parameter Rechenweg
BT-106 lineTotalAmount 100,00 + 72,50 + 0,00 + 180,00 + 46,50 + 58,20 = 457,20
BT-108 chargeTotalAmount 3,00 (Transportkosten)
BT-107 allowanceTotalAmount 5,60 + 2,61 + 2,50 + 0,50 = 11,21
BT-109 taxBasisAmount 457,20 + 3,00 − 11,21 = 448,99
BT-110 taxTotalAmount 61,07 + 8,93 = 70,00
BT-112 grandTotalAmount 448,99 + 70,00 = 518,99
BT-113 totalPrepaidAmount 0,00 – keine Anzahlung. Wie Abschläge einzeln aufgeschlüsselt werden, steht unter Anzahlungen.
BT-115 duePayableAmount 518,99 − 0,00 = 518,99

13. Speichern

Erst beim Speichern entscheidet sich, welche der oben gesetzten Felder tatsächlich im XML landen. Version, Profil und Syntax gehören zusammen:

Wie dieselbe Rechnung stattdessen als PDF/A-3 mit eingebettetem XML entsteht, beschreibt Umgang mit PDF-Dateien; die übrigen Speicherwege stehen unter Laden und Speichern.

14. Gegenprobe

Weil das Ziel eine bekannte Referenzdatei ist, lässt sich das Ergebnis direkt prüfen: Die erzeugte Datei muss dieselbe Baumstruktur haben wie X19_01_Warenrechnung.xml aus dem FeRD-Beispielpaket. Zahlen vergleicht man dabei numerisch, damit 1.00 und 1.0000 als gleich gelten – FactoorSharp schreibt Preise adaptiv mit zwei Nachkommastellen.

Ob die Rechnung darüber hinaus dem Regelwerk entspricht, prüfen Sie entweder im Browser oder direkt aus Ihrem Code heraus. Für den Blick eines Menschen auf den Beleg erzeugt der Visualizer eine Lesefassung:

Das ist eine reine Ansicht, kein Rechtsdokument und kein hybrides ZUGFeRD-PDF – Details dazu unter Bildliche Darstellung.