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.
// BT-1 Rechnungsnummer, BT-2 Rechnungsdatum, BT-5 Rechnungswährung
FacturXInvoice invoice = FacturXInvoice.CreateInvoice(
invoiceNo: "R87654321012345",
invoiceDate: new DateTime(2025, 10, 1),
currency: CurrencyCodes.EUR);
// BT-3: Rechnungstyp 380 = Handelsrechnung. Das ist der Default, steht hier aber
// bewusst ausgeschrieben, damit sichtbar ist, wo der Typ gesetzt wird.
invoice.Type = InvoiceType.Invoice;
// BT-X-2: freier Dokumentenname. Nur im Profil EXTENDED erlaubt und nur dort
// ausgegeben – in BASIC/COMFORT lässt der Writer das Element stillschweigend weg.
invoice.Name = "WARENRECHNUNG";
// ram:TestIndicator kennzeichnet den Beleg als Testdatensatz (ebenfalls nur EXTENDED).
invoice.IsTest = true;
' BT-1 Rechnungsnummer, BT-2 Rechnungsdatum, BT-5 Rechnungswährung
Dim invoice As FacturXInvoice = FacturXInvoice.CreateInvoice(
invoiceNo:="R87654321012345",
invoiceDate:=New DateTime(2025, 10, 1),
currency:=CurrencyCodes.EUR)
' BT-3: Rechnungstyp 380 = Handelsrechnung. Das ist der Default, steht hier aber
' bewusst ausgeschrieben, damit sichtbar ist, wo der Typ gesetzt wird.
invoice.Type = InvoiceType.Invoice
' BT-X-2: freier Dokumentenname. Nur im Profil EXTENDED erlaubt und nur dort
' ausgegeben – in BASIC/COMFORT lässt der Writer das Element stillschweigend weg.
invoice.Name = "WARENRECHNUNG"
' ram:TestIndicator kennzeichnet den Beleg als Testdatensatz (ebenfalls nur EXTENDED).
invoice.IsTest = True
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).
// REG = Regulatorische Angaben, hier die handelsrechtliche Vertretung.
invoice.AddNote("Geschäftsführer: Herr Geschäftsführer , MUSTERLIEFERANT GmbH ",
subjectCode: SubjectCodes.REG);
// AAI = allgemeine Information.
invoice.AddNote("Es bestehen Vereinbarungen, aus denen sich Minderungen des Entgelts ergeben können.",
subjectCode: SubjectCodes.AAI);
// ACB = zusätzliche Information; hier für die Formatkennzeichnung …
invoice.AddNote("ZUGFeRD vers 2.5.0 (Extended)",
subjectCode: SubjectCodes.ACB);
// … und für den Hinweis, dass es sich um ein Beispiel handelt.
invoice.AddNote("Dies ist ein Waren-Rechnungs-Beispiel",
subjectCode: SubjectCodes.ACB);
// AAK + ST3: standardisierter Hinweis auf bestehende Rabatt- oder Bonusvereinbarungen.
invoice.AddNote("Es bestehen Rabatt- oder Bonusvereinbarungen.",
subjectCode: SubjectCodes.AAK,
contentCode: ContentCodes.ST3);
// AAJ + EEV: standardisierter Eigentumsvorbehalt.
invoice.AddNote("Der Verkäufer bleibt Eigentümer der Waren bis zu vollständigen Erfüllung der Kaufpreisforderung.",
subjectCode: SubjectCodes.AAJ,
contentCode: ContentCodes.EEV);
// Notizen ohne subjectCode sind zulässig – reiner Freitext.
invoice.AddNote("Leergutwert: 46,50");
invoice.AddNote("Wichtige Information: Bei Bestellungen bis zum 19.12. ist die Auslieferung bis spätestens 23.12. garantiert.");
' REG = Regulatorische Angaben, hier die handelsrechtliche Vertretung.
invoice.AddNote("Geschäftsführer: Herr Geschäftsführer , MUSTERLIEFERANT GmbH ",
subjectCode:=SubjectCodes.REG)
' AAI = allgemeine Information.
invoice.AddNote("Es bestehen Vereinbarungen, aus denen sich Minderungen des Entgelts ergeben können.",
subjectCode:=SubjectCodes.AAI)
' ACB = zusätzliche Information; hier für die Formatkennzeichnung …
invoice.AddNote("ZUGFeRD vers 2.5.0 (Extended)",
subjectCode:=SubjectCodes.ACB)
' … und für den Hinweis, dass es sich um ein Beispiel handelt.
invoice.AddNote("Dies ist ein Waren-Rechnungs-Beispiel",
subjectCode:=SubjectCodes.ACB)
' AAK + ST3: standardisierter Hinweis auf bestehende Rabatt- oder Bonusvereinbarungen.
invoice.AddNote("Es bestehen Rabatt- oder Bonusvereinbarungen.",
subjectCode:=SubjectCodes.AAK,
contentCode:=ContentCodes.ST3)
' AAJ + EEV: standardisierter Eigentumsvorbehalt.
invoice.AddNote("Der Verkäufer bleibt Eigentümer der Waren bis zu vollständigen Erfüllung der Kaufpreisforderung.",
subjectCode:=SubjectCodes.AAJ,
contentCode:=ContentCodes.EEV)
' Notizen ohne subjectCode sind zulässig – reiner Freitext.
invoice.AddNote("Leergutwert: 46,50")
invoice.AddNote("Wichtige Information: Bei Bestellungen bis zum 19.12. ist die Auslieferung bis spätestens 23.12. garantiert.")
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.
// id → ram:ID (BT-29) interne Lieferantennummer
// globalID → ram:GlobalID (BT-29-0) mit schemeID "0088" = GLN
invoice.SetSeller(
name: "MUSTERLIEFERANT GMBH",
postcode: "99199",
city: "MUSTERHAUSEN",
street: "BAHNHOFSTRASSE 99",
country: CountryCodes.DE,
id: "549910",
globalID: new GlobalID(GlobalIDSchemeIdentifiers.GLN, "4333741000005"));
// BT-42 / BT-43: Ansprechpartner. Name und Abteilung bleiben leer – der Writer gibt
// ram:PersonName / ram:DepartmentName dann gar nicht erst aus.
invoice.SetSellerContact(
emailAddress: "max.mustermann@musterlieferant.de",
phoneno: "+49 932 431 500");
// BT-34: elektronische Adresse des Verkäufers, hier als EAN Location Code (0088).
invoice.SetSellerElectronicAddress("info@musterlieferant.de",
ElectronicAddressSchemeIdentifiers.EanLocationCode);
// BT-31: USt-IdNr. des Verkäufers (Schema VA). FC wäre die nationale Steuernummer.
invoice.AddSellerTaxRegistration("DE123456788", TaxRegistrationSchemeID.VA);
' id → ram:ID (BT-29) interne Lieferantennummer
' globalID → ram:GlobalID (BT-29-0) mit schemeID "0088" = GLN
invoice.SetSeller(
name:="MUSTERLIEFERANT GMBH",
postcode:="99199",
city:="MUSTERHAUSEN",
street:="BAHNHOFSTRASSE 99",
country:=CountryCodes.DE,
id:="549910",
globalID:=New GlobalID(GlobalIDSchemeIdentifiers.GLN, "4333741000005"))
' BT-42 / BT-43: Ansprechpartner. Name und Abteilung bleiben leer – der Writer gibt
' ram:PersonName / ram:DepartmentName dann gar nicht erst aus.
invoice.SetSellerContact(
emailAddress:="max.mustermann@musterlieferant.de",
phoneno:="+49 932 431 500")
' BT-34: elektronische Adresse des Verkäufers, hier als EAN Location Code (0088).
invoice.SetSellerElectronicAddress("info@musterlieferant.de",
ElectronicAddressSchemeIdentifiers.EanLocationCode)
' BT-31: USt-IdNr. des Verkäufers (Schema VA). FC wäre die nationale Steuernummer.
invoice.AddSellerTaxRegistration("DE123456788", TaxRegistrationSchemeID.VA)
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.
// Der Käufer ist hier die Zentrale; Warenempfänger und Rechnungsempfänger
// weichen davon ab und stehen weiter unten.
invoice.SetBuyer(
name: "MUSTER-KUNDE GMBH",
postcode: "40235",
city: "KUNDENSTADT",
street: "KUNDENWEG 88",
country: CountryCodes.DE,
id: "009420",
globalID: new GlobalID(GlobalIDSchemeIdentifiers.GLN, "4304171000002"));
' Der Käufer ist hier die Zentrale; Warenempfänger und Rechnungsempfänger
' weichen davon ab und stehen weiter unten.
invoice.SetBuyer(
name:="MUSTER-KUNDE GMBH",
postcode:="40235",
city:="KUNDENSTADT",
street:="KUNDENWEG 88",
country:=CountryCodes.DE,
id:="009420",
globalID:=New GlobalID(GlobalIDSchemeIdentifiers.GLN, "4304171000002"))
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.
// BT-13: Bestellnummer des Käufers → ram:BuyerOrderReferencedDocument
invoice.SetBuyerOrderReferenceDocument("B123456789");
// BT-18 / BG-24: zusätzliche Dokumentenreferenz. TypeCode 130 = Rechnungsdatenblatt
// (InvoiceDataSheet) – die vom Lieferanten vergebene Belegnummer des Datenblatts.
invoice.AddAdditionalReferencedDocument(
id: "A456123",
typeCode: AdditionalReferencedDocumentTypeCode.InvoiceDataSheet);
// BT-16: Lieferscheinnummer → ram:DeliveryNoteReferencedDocument (EXTENDED)
invoice.SetDeliveryNoteReferenceDocument("L87654321012345");
// BT-72: tatsächliches Lieferdatum → ram:ActualDeliverySupplyChainEvent
invoice.ActualDeliveryDate = new DateTime(2025, 10, 1);
' BT-13: Bestellnummer des Käufers → ram:BuyerOrderReferencedDocument
invoice.SetBuyerOrderReferenceDocument("B123456789")
' BT-18 / BG-24: zusätzliche Dokumentenreferenz. TypeCode 130 = Rechnungsdatenblatt
' (InvoiceDataSheet) – die vom Lieferanten vergebene Belegnummer des Datenblatts.
invoice.AddAdditionalReferencedDocument(
id:="A456123",
typeCode:=AdditionalReferencedDocumentTypeCode.InvoiceDataSheet)
' BT-16: Lieferscheinnummer → ram:DeliveryNoteReferencedDocument (EXTENDED)
invoice.SetDeliveryNoteReferenceDocument("L87654321012345")
' BT-72: tatsächliches Lieferdatum → ram:ActualDeliverySupplyChainEvent
invoice.ActualDeliveryDate = New DateTime(2025, 10, 1)
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.
// BG-13 ram:ShipToTradeParty: geliefert wird an die Filiale, nicht an die Zentrale.
// Hier gibt es keine interne ID, nur eine GLN.
invoice.ShipTo = new Party()
{
GlobalID = new GlobalID(GlobalIDSchemeIdentifiers.GLN, "4304171088093"),
Name = "MUSTER-MARKT",
Postcode = "31157",
Street = "HAUPTSTRASSE 44",
City = "SARSTEDT",
Country = CountryCodes.DE
};
// ram:DefinedTradeContact/ram:DepartmentName – die Abteilung ("8211") in der Filiale.
// OrgUnit ist genau dieses Feld; Name, Telefon und E-Mail bleiben leer.
invoice.ShipToContact = new Contact()
{
OrgUnit = "8211"
};
// BG-X-36 ram:InvoiceeTradeParty (nur EXTENDED): gleiche Firma wie der Käufer,
// aber die Rechnung geht an den Standort Düsseldorf.
invoice.Invoicee = new Party()
{
ID = new GlobalID(null, "009420"),
GlobalID = new GlobalID(GlobalIDSchemeIdentifiers.GLN, "4304171000002"),
Name = "MUSTER-KUNDE GMBH",
Postcode = "40235",
Street = "KUNDENWEG 88",
City = "DUESSELDORF",
Country = CountryCodes.DE
};
' BG-13 ram:ShipToTradeParty: geliefert wird an die Filiale, nicht an die Zentrale.
' Hier gibt es keine interne ID, nur eine GLN.
invoice.ShipTo = New Party() With
{
.GlobalID = New GlobalID(GlobalIDSchemeIdentifiers.GLN, "4304171088093"),
.Name = "MUSTER-MARKT",
.Postcode = "31157",
.Street = "HAUPTSTRASSE 44",
.City = "SARSTEDT",
.Country = CountryCodes.DE
}
' ram:DefinedTradeContact/ram:DepartmentName – die Abteilung ("8211") in der Filiale.
' OrgUnit ist genau dieses Feld; Name, Telefon und E-Mail bleiben leer.
invoice.ShipToContact = New Contact() With
{
.OrgUnit = "8211"
}
' BG-X-36 ram:InvoiceeTradeParty (nur EXTENDED): gleiche Firma wie der Käufer,
' aber die Rechnung geht an den Standort Düsseldorf.
invoice.Invoicee = New Party() With
{
.ID = New GlobalID(Nothing, "009420"),
.GlobalID = New GlobalID(GlobalIDSchemeIdentifiers.GLN, "4304171000002"),
.Name = "MUSTER-KUNDE GMBH",
.Postcode = "40235",
.Street = "KUNDENWEG 88",
.City = "DUESSELDORF",
.Country = CountryCodes.DE
}
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.
// 100 Flaschen Zitronensäure zu 1,00 € = 100,00 €, 19 % USt
TradeLineItem line1 = invoice.AddTradeLineItem(
lineID: "1",
name: "Zitronensäure 100ml",
netUnitPrice: 1.00m,
grossUnitPrice: 1.00m,
unitCode: QuantityCodes.H87, // H87 = Stück
billedQuantity: 100m,
lineTotalAmount: 100.00m,
taxType: TaxTypes.VAT,
categoryCode: TaxCategoryCodes.S, // S = Regelsteuersatz
taxPercent: 19m,
sellerAssignedID: "ZS997",
id: new GlobalID(GlobalIDSchemeIdentifiers.EAN, "4123456000014"));
// BG-32 Artikelattribut: frei definierbares Merkmal/Wert-Paar (BT-160 / BT-161).
line1.ApplicableProductCharacteristics.Add(new ApplicableProductCharacteristic()
{
Description = "Verpackungsart",
Value = "BO"
});
// 4 Kartons als Versandeinheit.
line1.PackageQuantity = 4m;
line1.PackageUnitCode = QuantityCodes.XCT;
' 100 Flaschen Zitronensäure zu 1,00 € = 100,00 €, 19 % USt
Dim line1 As TradeLineItem = invoice.AddTradeLineItem(
lineID:="1",
name:="Zitronensäure 100ml",
netUnitPrice:=1.0D,
grossUnitPrice:=1.0D,
unitCode:=QuantityCodes.H87, ' H87 = Stück
billedQuantity:=100D,
lineTotalAmount:=100.0D,
taxType:=TaxTypes.VAT,
categoryCode:=TaxCategoryCodes.S, ' S = Regelsteuersatz
taxPercent:=19D,
sellerAssignedID:="ZS997",
id:=New GlobalID(GlobalIDSchemeIdentifiers.EAN, "4123456000014"))
' BG-32 Artikelattribut: frei definierbares Merkmal/Wert-Paar (BT-160 / BT-161).
line1.ApplicableProductCharacteristics.Add(New ApplicableProductCharacteristic() With
{
.Description = "Verpackungsart",
.Value = "BO"
})
' 4 Kartons als Versandeinheit.
line1.PackageQuantity = 4D
line1.PackageUnitCode = QuantityCodes.XCT
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.
// 50 × Gelierzucker, Listenpreis 1,50 € abzüglich zweier Artikelrabatte
// (0,03 € + 0,02 €) → Nettopreis 1,45 €; 50 × 1,45 = 72,50 €, 7 %.
TradeLineItem line2 = invoice.AddTradeLineItem(
lineID: "2",
name: "Gelierzucker Extra 250g",
netUnitPrice: 1.45m,
grossUnitPrice: 1.50m,
unitCode: QuantityCodes.H87,
billedQuantity: 50m,
lineTotalAmount: 72.50m,
taxType: TaxTypes.VAT,
categoryCode: TaxCategoryCodes.S,
taxPercent: 7m,
sellerAssignedID: "GZ250",
id: new GlobalID(GlobalIDSchemeIdentifiers.EAN, "4123456000021"));
// BT-147: Artikelrabatte werden als ram:AppliedTradeAllowanceCharge INNERHALB des
// Bruttopreises ausgegeben. Die Beträge gelten PRO EINHEIT, nicht für die ganze
// Position. Der Nettopreis oben ist das Ergebnis: 1,50 − 0,03 − 0,02 = 1,45.
line2.AddTradeAllowance(CurrencyCodes.EUR, basisAmount: null, actualAmount: 0.03m, reason: "Artikelrabatt 1");
line2.AddTradeAllowance(CurrencyCodes.EUR, basisAmount: null, actualAmount: 0.02m, reason: "Artikelrabatt 2");
line2.PackageQuantity = 1m;
line2.PackageUnitCode = QuantityCodes.XCT;
' 50 × Gelierzucker, Listenpreis 1,50 € abzüglich zweier Artikelrabatte
' (0,03 € + 0,02 €) → Nettopreis 1,45 €; 50 × 1,45 = 72,50 €, 7 %.
Dim line2 As TradeLineItem = invoice.AddTradeLineItem(
lineID:="2",
name:="Gelierzucker Extra 250g",
netUnitPrice:=1.45D,
grossUnitPrice:=1.5D,
unitCode:=QuantityCodes.H87,
billedQuantity:=50D,
lineTotalAmount:=72.5D,
taxType:=TaxTypes.VAT,
categoryCode:=TaxCategoryCodes.S,
taxPercent:=7D,
sellerAssignedID:="GZ250",
id:=New GlobalID(GlobalIDSchemeIdentifiers.EAN, "4123456000021"))
' BT-147: Artikelrabatte werden als ram:AppliedTradeAllowanceCharge INNERHALB des
' Bruttopreises ausgegeben. Die Beträge gelten PRO EINHEIT, nicht für die ganze
' Position. Der Nettopreis oben ist das Ergebnis: 1,50 − 0,03 − 0,02 = 1,45.
line2.AddTradeAllowance(CurrencyCodes.EUR, basisAmount:=Nothing, actualAmount:=0.03D, reason:="Artikelrabatt 1")
line2.AddTradeAllowance(CurrencyCodes.EUR, basisAmount:=Nothing, actualAmount:=0.02D, reason:="Artikelrabatt 2")
line2.PackageQuantity = 1D
line2.PackageUnitCode = QuantityCodes.XCT
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.
// Naturalrabatt: 10 Stück derselben Ware zum Preis 0,00 €. Die Position bleibt im
// Beleg stehen (Nachvollziehbarkeit der Liefermenge), trägt aber 0,00 € zur Summe bei.
TradeLineItem line3 = invoice.AddTradeLineItem(
lineID: "3",
name: "Gelierzucker Extra 250g",
description: "Artikel wie vereinbart ohne Berechnung", // BT-154
netUnitPrice: 0.00m,
grossUnitPrice: 0.00m,
unitCode: QuantityCodes.H87,
billedQuantity: 10m,
lineTotalAmount: 0.00m,
taxType: TaxTypes.VAT,
categoryCode: TaxCategoryCodes.S,
taxPercent: 7m,
sellerAssignedID: "GZ250",
id: new GlobalID(GlobalIDSchemeIdentifiers.EAN, "4123456000021"));
line3.PackageQuantity = 1m;
line3.PackageUnitCode = QuantityCodes.XCT;
' Naturalrabatt: 10 Stück derselben Ware zum Preis 0,00 €. Die Position bleibt im
' Beleg stehen (Nachvollziehbarkeit der Liefermenge), trägt aber 0,00 € zur Summe bei.
Dim line3 As TradeLineItem = invoice.AddTradeLineItem(
lineID:="3",
name:="Gelierzucker Extra 250g",
description:="Artikel wie vereinbart ohne Berechnung", ' BT-154
netUnitPrice:=0D,
grossUnitPrice:=0D,
unitCode:=QuantityCodes.H87,
billedQuantity:=10D,
lineTotalAmount:=0D,
taxType:=TaxTypes.VAT,
categoryCode:=TaxCategoryCodes.S,
taxPercent:=7D,
sellerAssignedID:="GZ250",
id:=New GlobalID(GlobalIDSchemeIdentifiers.EAN, "4123456000021"))
line3.PackageQuantity = 1D
line3.PackageUnitCode = QuantityCodes.XCT
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).
// 15 Kisten Bier zu 12,00 € = 180,00 €, 19 %. Abrechnungseinheit ist die Kiste (XBC),
// Versandeinheit die Flasche (XBO) – 20 Flaschen je Kiste.
TradeLineItem line4 = invoice.AddTradeLineItem(
lineID: "4",
name: "Bierbrau Pils 20/0500",
description: "EAN-VKE: 4100130913297",
netUnitPrice: 12.00m,
grossUnitPrice: 12.00m,
unitCode: QuantityCodes.XBC, // XBC = Kiste
billedQuantity: 15m,
lineTotalAmount: 180.00m,
taxType: TaxTypes.VAT,
categoryCode: TaxCategoryCodes.S,
taxPercent: 19m,
sellerAssignedID: "2031",
id: new GlobalID(GlobalIDSchemeIdentifiers.EAN, "4100130013294"));
line4.ApplicableProductCharacteristics.Add(new ApplicableProductCharacteristic()
{
Description = "Verpackung",
Value = "Kiste"
});
line4.PackageQuantity = 20m;
line4.PackageUnitCode = QuantityCodes.XBO; // XBO = Flasche
// Leergutpfand, 15 × 3,10 € = 46,50 €, 19 %. buyerAssignedID (BT-156) zeigt, dass der
// Käufer für denselben Artikel eine eigene Nummer führt.
TradeLineItem line5 = invoice.AddTradeLineItem(
lineID: "5",
name: "Leergutpfand 20 x 0,5l",
netUnitPrice: 3.10m,
grossUnitPrice: 3.10m,
unitCode: QuantityCodes.H87,
billedQuantity: 15m,
lineTotalAmount: 46.50m,
taxType: TaxTypes.VAT,
categoryCode: TaxCategoryCodes.S,
taxPercent: 19m,
sellerAssignedID: "1805",
buyerAssignedID: "4711",
id: new GlobalID(GlobalIDSchemeIdentifiers.EAN, "2001015001325"));
line5.ApplicableProductCharacteristics.Add(new ApplicableProductCharacteristic()
{
Description = "Verpackung",
Value = "unverpackt"
});
line5.PackageQuantity = 1m;
line5.PackageUnitCode = QuantityCodes.XBC;
' 15 Kisten Bier zu 12,00 € = 180,00 €, 19 %. Abrechnungseinheit ist die Kiste (XBC),
' Versandeinheit die Flasche (XBO) – 20 Flaschen je Kiste.
Dim line4 As TradeLineItem = invoice.AddTradeLineItem(
lineID:="4",
name:="Bierbrau Pils 20/0500",
description:="EAN-VKE: 4100130913297",
netUnitPrice:=12D,
grossUnitPrice:=12D,
unitCode:=QuantityCodes.XBC, ' XBC = Kiste
billedQuantity:=15D,
lineTotalAmount:=180D,
taxType:=TaxTypes.VAT,
categoryCode:=TaxCategoryCodes.S,
taxPercent:=19D,
sellerAssignedID:="2031",
id:=New GlobalID(GlobalIDSchemeIdentifiers.EAN, "4100130013294"))
line4.ApplicableProductCharacteristics.Add(New ApplicableProductCharacteristic() With
{
.Description = "Verpackung",
.Value = "Kiste"
})
line4.PackageQuantity = 20D
line4.PackageUnitCode = QuantityCodes.XBO ' XBO = Flasche
' Leergutpfand, 15 × 3,10 € = 46,50 €, 19 %. buyerAssignedID (BT-156) zeigt, dass der
' Käufer für denselben Artikel eine eigene Nummer führt.
Dim line5 As TradeLineItem = invoice.AddTradeLineItem(
lineID:="5",
name:="Leergutpfand 20 x 0,5l",
netUnitPrice:=3.1D,
grossUnitPrice:=3.1D,
unitCode:=QuantityCodes.H87,
billedQuantity:=15D,
lineTotalAmount:=46.5D,
taxType:=TaxTypes.VAT,
categoryCode:=TaxCategoryCodes.S,
taxPercent:=19D,
sellerAssignedID:="1805",
buyerAssignedID:="4711",
id:=New GlobalID(GlobalIDSchemeIdentifiers.EAN, "2001015001325"))
line5.ApplicableProductCharacteristics.Add(New ApplicableProductCharacteristic() With
{
.Description = "Verpackung",
.Value = "unverpackt"
})
line5.PackageQuantity = 1D
line5.PackageUnitCode = QuantityCodes.XBC
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.
// Mischpalette mit drei Sorten Joghurt. Listenpreis 30,00 € abzüglich 0,90 €
// Artikelrabatt → 29,10 €; 2 × 29,10 = 58,20 €, 7 %.
TradeLineItem line6 = invoice.AddTradeLineItem(
lineID: "6",
name: "Mischpalette Joghurt Karton 3 x 20",
netUnitPrice: 29.10m,
grossUnitPrice: 30.00m,
unitCode: QuantityCodes.H87,
billedQuantity: 2m,
lineTotalAmount: 58.20m,
taxType: TaxTypes.VAT,
categoryCode: TaxCategoryCodes.S,
taxPercent: 7m,
sellerAssignedID: "MP107",
id: new GlobalID(GlobalIDSchemeIdentifiers.EAN, "4123456000038"));
line6.ApplicableProductCharacteristics.Add(new ApplicableProductCharacteristic()
{
Description = "Verpackung",
Value = "Karton"
});
// BG-X-1 ram:IncludedReferencedProduct (nur EXTENDED): Aufschlüsselung einer
// Verkaufseinheit in ihre Bestandteile. Rein informativ – Preise und Steuern hängen
// weiterhin an der übergeordneten Position.
//
// Die Komfort-Methode AddIncludedReferencedProduct() kennt keine GlobalID, deshalb
// werden die Objekte hier direkt angelegt.
line6.IncludedReferencedProducts.Add(new IncludedReferencedProduct()
{
GlobalID = new GlobalID(GlobalIDSchemeIdentifiers.EAN, "4123456001035"),
SellerAssignedID = "JOG103",
Name = "Erdbeer 20 x 150g Becher",
UnitQuantity = 20m,
UnitCode = QuantityCodes.H87
});
line6.IncludedReferencedProducts.Add(new IncludedReferencedProduct()
{
GlobalID = new GlobalID(GlobalIDSchemeIdentifiers.EAN, "4123456002032"),
SellerAssignedID = "JOG203",
Name = "Banane 20 x 150g Becher",
UnitQuantity = 20m,
UnitCode = QuantityCodes.H87
});
line6.IncludedReferencedProducts.Add(new IncludedReferencedProduct()
{
GlobalID = new GlobalID(GlobalIDSchemeIdentifiers.EAN, "4123456003039"),
SellerAssignedID = "JOG303",
Name = "Schoko 20 x 150g Becher",
UnitQuantity = 20m,
UnitCode = QuantityCodes.H87
});
// BT-147 wie bei Position 2: Rabatt pro Einheit, 30,00 − 0,90 = 29,10.
line6.AddTradeAllowance(CurrencyCodes.EUR, basisAmount: null, actualAmount: 0.90m, reason: "Artikelrabatt 1");
line6.PackageQuantity = 1m;
line6.PackageUnitCode = QuantityCodes.XPX; // XPX = Palette
' Mischpalette mit drei Sorten Joghurt. Listenpreis 30,00 € abzüglich 0,90 €
' Artikelrabatt → 29,10 €; 2 × 29,10 = 58,20 €, 7 %.
Dim line6 As TradeLineItem = invoice.AddTradeLineItem(
lineID:="6",
name:="Mischpalette Joghurt Karton 3 x 20",
netUnitPrice:=29.1D,
grossUnitPrice:=30D,
unitCode:=QuantityCodes.H87,
billedQuantity:=2D,
lineTotalAmount:=58.2D,
taxType:=TaxTypes.VAT,
categoryCode:=TaxCategoryCodes.S,
taxPercent:=7D,
sellerAssignedID:="MP107",
id:=New GlobalID(GlobalIDSchemeIdentifiers.EAN, "4123456000038"))
line6.ApplicableProductCharacteristics.Add(New ApplicableProductCharacteristic() With
{
.Description = "Verpackung",
.Value = "Karton"
})
' BG-X-1 ram:IncludedReferencedProduct (nur EXTENDED): Aufschlüsselung einer
' Verkaufseinheit in ihre Bestandteile. Rein informativ – Preise und Steuern hängen
' weiterhin an der übergeordneten Position.
'
' Die Komfort-Methode AddIncludedReferencedProduct() kennt keine GlobalID, deshalb
' werden die Objekte hier direkt angelegt.
line6.IncludedReferencedProducts.Add(New IncludedReferencedProduct() With
{
.GlobalID = New GlobalID(GlobalIDSchemeIdentifiers.EAN, "4123456001035"),
.SellerAssignedID = "JOG103",
.Name = "Erdbeer 20 x 150g Becher",
.UnitQuantity = 20D,
.UnitCode = QuantityCodes.H87
})
line6.IncludedReferencedProducts.Add(New IncludedReferencedProduct() With
{
.GlobalID = New GlobalID(GlobalIDSchemeIdentifiers.EAN, "4123456002032"),
.SellerAssignedID = "JOG203",
.Name = "Banane 20 x 150g Becher",
.UnitQuantity = 20D,
.UnitCode = QuantityCodes.H87
})
line6.IncludedReferencedProducts.Add(New IncludedReferencedProduct() With
{
.GlobalID = New GlobalID(GlobalIDSchemeIdentifiers.EAN, "4123456003039"),
.SellerAssignedID = "JOG303",
.Name = "Schoko 20 x 150g Becher",
.UnitQuantity = 20D,
.UnitCode = QuantityCodes.H87
})
' BT-147 wie bei Position 2: Rabatt pro Einheit, 30,00 − 0,90 = 29,10.
line6.AddTradeAllowance(CurrencyCodes.EUR, basisAmount:=Nothing, actualAmount:=0.9D, reason:="Artikelrabatt 1")
line6.PackageQuantity = 1D
line6.PackageUnitCode = QuantityCodes.XPX ' XPX = Palette
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 €.
// Rechnungsrabatt 1 – prozentual, 2 % (BT-94 CalculationPercent + BT-93 BasisAmount).
invoice.AddTradeAllowance(
basisAmount: 280.00m,
currency: CurrencyCodes.EUR,
actualAmount: 5.60m, // 2 % von 280,00
chargePercentage: 2.00m,
reason: "Rechnungsrabatt 1",
taxTypeCode: TaxTypes.VAT,
taxCategoryCode: TaxCategoryCodes.S,
taxPercent: 19m);
invoice.AddTradeAllowance(
basisAmount: 130.70m,
currency: CurrencyCodes.EUR,
actualAmount: 2.61m, // 2 % von 130,70 (kaufmännisch gerundet)
chargePercentage: 2.00m,
reason: "Rechnungsrabatt 1",
taxTypeCode: TaxTypes.VAT,
taxCategoryCode: TaxCategoryCodes.S,
taxPercent: 7m);
// Rechnungsrabatt 2 – absoluter Betrag, deshalb ohne chargePercentage.
invoice.AddTradeAllowance(
basisAmount: 280.00m,
currency: CurrencyCodes.EUR,
actualAmount: 2.50m,
reason: "Rechnungsrabatt 2",
taxTypeCode: TaxTypes.VAT,
taxCategoryCode: TaxCategoryCodes.S,
taxPercent: 19m);
invoice.AddTradeAllowance(
basisAmount: 130.70m,
currency: CurrencyCodes.EUR,
actualAmount: 0.50m,
reason: "Rechnungsrabatt 2",
taxTypeCode: TaxTypes.VAT,
taxCategoryCode: TaxCategoryCodes.S,
taxPercent: 7m);
' Rechnungsrabatt 1 – prozentual, 2 % (BT-94 CalculationPercent + BT-93 BasisAmount).
invoice.AddTradeAllowance(
basisAmount:=280D,
currency:=CurrencyCodes.EUR,
actualAmount:=5.6D, ' 2 % von 280,00
chargePercentage:=2D,
reason:="Rechnungsrabatt 1",
taxTypeCode:=TaxTypes.VAT,
taxCategoryCode:=TaxCategoryCodes.S,
taxPercent:=19D)
invoice.AddTradeAllowance(
basisAmount:=130.7D,
currency:=CurrencyCodes.EUR,
actualAmount:=2.61D, ' 2 % von 130,70 (kaufmännisch gerundet)
chargePercentage:=2D,
reason:="Rechnungsrabatt 1",
taxTypeCode:=TaxTypes.VAT,
taxCategoryCode:=TaxCategoryCodes.S,
taxPercent:=7D)
' Rechnungsrabatt 2 – absoluter Betrag, deshalb ohne chargePercentage.
invoice.AddTradeAllowance(
basisAmount:=280D,
currency:=CurrencyCodes.EUR,
actualAmount:=2.5D,
reason:="Rechnungsrabatt 2",
taxTypeCode:=TaxTypes.VAT,
taxCategoryCode:=TaxCategoryCodes.S,
taxPercent:=19D)
invoice.AddTradeAllowance(
basisAmount:=130.7D,
currency:=CurrencyCodes.EUR,
actualAmount:=0.5D,
reason:="Rechnungsrabatt 2",
taxTypeCode:=TaxTypes.VAT,
taxCategoryCode:=TaxCategoryCodes.S,
taxPercent:=7D)
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.
// ram:SpecifiedLogisticsServiceCharge – eigenes Element für Logistikkosten, NICHT
// identisch mit einem normalen Zuschlag (AddTradeCharge). In XRechnung ist es nicht
// zulässig, in ZUGFeRD EXTENDED schon. Es fließt in ChargeTotalAmount (BT-108) ein.
invoice.AddLogisticsServiceCharge(
amount: 3.00m,
description: "Transportkosten",
taxTypeCode: TaxTypes.VAT,
taxCategoryCode: TaxCategoryCodes.S,
taxPercent: 19m);
' ram:SpecifiedLogisticsServiceCharge – eigenes Element für Logistikkosten, NICHT
' identisch mit einem normalen Zuschlag (AddTradeCharge). In XRechnung ist es nicht
' zulässig, in ZUGFeRD EXTENDED schon. Es fließt in ChargeTotalAmount (BT-108) ein.
invoice.AddLogisticsServiceCharge(
amount:=3D,
description:="Transportkosten",
taxTypeCode:=TaxTypes.VAT,
taxCategoryCode:=TaxCategoryCodes.S,
taxPercent:=19D)
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.
// lineTotalBasisAmount und allowanceChargeBasisAmount sind EXTENDED-Felder;
// in BASIC/COMFORT entfallen sie ersatzlos.
invoice.AddApplicableTradeTax(
basisAmount: 321.40m,
percent: 19m,
taxAmount: 61.07m,
typeCode: TaxTypes.VAT,
categoryCode: TaxCategoryCodes.S,
allowanceChargeBasisAmount: -5.10m,
lineTotalBasisAmount: 326.50m);
invoice.AddApplicableTradeTax(
basisAmount: 127.59m,
percent: 7m,
taxAmount: 8.93m,
typeCode: TaxTypes.VAT,
categoryCode: TaxCategoryCodes.S,
allowanceChargeBasisAmount: -3.11m,
lineTotalBasisAmount: 130.70m);
' lineTotalBasisAmount und allowanceChargeBasisAmount sind EXTENDED-Felder;
' in BASIC/COMFORT entfallen sie ersatzlos.
invoice.AddApplicableTradeTax(
basisAmount:=321.4D,
percent:=19D,
taxAmount:=61.07D,
typeCode:=TaxTypes.VAT,
categoryCode:=TaxCategoryCodes.S,
allowanceChargeBasisAmount:=-5.1D,
lineTotalBasisAmount:=326.5D)
invoice.AddApplicableTradeTax(
basisAmount:=127.59D,
percent:=7D,
taxAmount:=8.93D,
typeCode:=TaxTypes.VAT,
categoryCode:=TaxCategoryCodes.S,
allowanceChargeBasisAmount:=-3.11D,
lineTotalBasisAmount:=130.7D)
11. Zahlungsbedingung mit Skonto
Der Skonto steht nicht nur als Satz im Freitext, sondern auch maschinell
auswertbar im XML – dafür sorgt PaymentTermsType.Skonto.
// paymentTermsType = Skonto erzeugt den Block ram:ApplicableTradePaymentDiscountTerms
// mit BasisPeriodMeasure (Tage) und CalculationPercent. Ohne diesen Typ bliebe nur der
// Freitext übrig – der Skonto wäre dann maschinell nicht auswertbar.
invoice.AddTradePaymentTerms(
description: "Bei Zahlung innerhalb 14 Tagen gewähren wir 2,0% Skonto.",
paymentTermsType: PaymentTermsType.Skonto,
dueDays: 14,
percentage: 2.00m);
' paymentTermsType = Skonto erzeugt den Block ram:ApplicableTradePaymentDiscountTerms
' mit BasisPeriodMeasure (Tage) und CalculationPercent. Ohne diesen Typ bliebe nur der
' Freitext übrig – der Skonto wäre dann maschinell nicht auswertbar.
invoice.AddTradePaymentTerms(
description:="Bei Zahlung innerhalb 14 Tagen gewähren wir 2,0% Skonto.",
paymentTermsType:=PaymentTermsType.Skonto,
dueDays:=14,
percentage:=2D)
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 |
invoice.SetTotals(
lineTotalAmount: 457.20m, // BT-106
chargeTotalAmount: 3.00m, // BT-108
allowanceTotalAmount: 11.21m, // BT-107
taxBasisAmount: 448.99m, // BT-109
taxTotalAmount: 70.00m, // BT-110
grandTotalAmount: 518.99m, // BT-112
totalPrepaidAmount: 0.00m, // BT-113
duePayableAmount: 518.99m); // BT-115
invoice.SetTotals(
lineTotalAmount:=457.2D, ' BT-106
chargeTotalAmount:=3D, ' BT-108
allowanceTotalAmount:=11.21D, ' BT-107
taxBasisAmount:=448.99D, ' BT-109
taxTotalAmount:=70D, ' BT-110
grandTotalAmount:=518.99D, ' BT-112
totalPrepaidAmount:=0D, ' BT-113
duePayableAmount:=518.99D) ' BT-115
13. Speichern
Erst beim Speichern entscheidet sich, welche der oben gesetzten Felder tatsächlich im XML landen. Version, Profil und Syntax gehören zusammen:
// Version25 + Profile.Extended + CII ergibt die Profilkennung
// "urn:cen.eu:en16931:2017#conformant#urn:factur-x.eu:1p0:extended".
// Ein anderes Profil würde alle EXTENDED-Felder oben stillschweigend verwerfen.
invoice.Save("X19_01_Warenrechnung.xml",
ZUGFeRDVersion.Version25,
Profile.Extended,
ZUGFeRDFormats.CII);
' Version25 + Profile.Extended + CII ergibt die Profilkennung
' "urn:cen.eu:en16931:2017#conformant#urn:factur-x.eu:1p0:extended".
' Ein anderes Profil würde alle EXTENDED-Felder oben stillschweigend verwerfen.
invoice.Save("X19_01_Warenrechnung.xml",
ZUGFeRDVersion.Version25,
Profile.Extended,
ZUGFeRDFormats.CII)
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:
// Lesefassung für Menschen – kein Rechtsdokument und kein hybrides ZUGFeRD-PDF.
// Gerendert wird aus dem Objekt, nicht aus der XML-Datei.
InvoiceVisualizer.RenderPdf(invoice, "X19_01_Warenrechnung.pdf");
InvoiceVisualizer.RenderHtml(invoice, "X19_01_Warenrechnung.html");
' Lesefassung für Menschen – kein Rechtsdokument und kein hybrides ZUGFeRD-PDF.
' Gerendert wird aus dem Objekt, nicht aus der XML-Datei.
InvoiceVisualizer.RenderPdf(invoice, "X19_01_Warenrechnung.pdf")
InvoiceVisualizer.RenderHtml(invoice, "X19_01_Warenrechnung.html")
Das ist eine reine Ansicht, kein Rechtsdokument und kein hybrides ZUGFeRD-PDF – Details dazu unter Bildliche Darstellung.