Gutschrift (Selbstabrechnung)
Das Gutschriftverfahren nach § 14 Abs. 2 UStG: Der Leistungsempfänger rechnet ab, nicht der Lieferant. Zwei Positionen, zwei Steuersätze, keine Zu- und Abschläge – und als einziger Unterschied zu einer gewöhnlichen Handelsrechnung der Dokumenttyp 389.
Grundlage ist die offizielle Beispielrechnung
E10_01_Gutschrift aus der Factur-X-/ZUGFeRD-Dokumentation
(FeRD-Beispielpaket, ZUGFeRD 2.5.0). Der Code unten erzeugt genau diesen
Beleg, Feld für Feld und in derselben Reihenfolge.
Einige Verweise auf dieser Seite führen in die vertiefende Dokumentation im Kunden-Bereich und verlangen eine Anmeldung.
Zwei Dinge, die „Gutschrift“ heißen
Das deutsche Wort trägt zwei völlig verschiedene Bedeutungen, und die Verwechslung ist der häufigste Fehler in diesem Bereich:
| Gutschriftverfahren (389) | Gutschrift im Sinne einer Stornierung (381) | |
|---|---|---|
| FactoorSharp | InvoiceType.SelfBilledInvoice |
InvoiceType.CreditNote |
| Wer stellt aus? | Der Leistungsempfänger rechnet für den Lieferanten ab. | Der Lieferant selbst. |
| Beträge | positiv – der Beleg sieht aus wie eine Rechnung | positiv, das Dokument spiegelt die Rechnung als Ganzes |
| Rechtsgrundlage | § 14 Abs. 2 UStG (self-billed invoice) | Korrektur einer Forderung |
| Diese Seite | behandelt diesen Fall | siehe Rechnungskorrektur (Typ 384) für die Abgrenzung |
Beim Gutschriftverfahren bleiben alle Beträge positiv. Wer hier negative Mengen setzt, weil „Gutschrift“ nach Rückabwicklung klingt, baut einen fachlich falschen Beleg. Negative Beträge gehören in die Rechnungskorrektur.
Profil COMFORT statt EXTENDED
Anders als die übrigen Beispiele auf diesen Seiten liegt dieser Beleg im Profil
Profile.Comfort – der Guideline-ID
urn:cen.eu:en16931:2017, also dem reinen EN 16931-Profil ohne
Erweiterung. Das ist keine Nachlässigkeit der Referenz, sondern die Aussage,
dass dieser Geschäftsvorfall ohne EXTENDED-Felder auskommt.
Folgerichtig fehlen hier alle Felder, die die Erweiterte Warenrechnung vorführt: Dokumentenname (BT-X-2), Testkennzeichen, Verpackungsangaben (BT-X-9) und die erweiterten Steuerbasen. Sie zu setzen wäre kein Fehler – der Writer würde sie im Profil COMFORT stillschweigend verwerfen.
1. Belegkopf
Nummer, Datum und Währung wie bei jeder Rechnung. Die einzige Zeile, die den
Beleg zur Gutschrift macht, ist invoice.Type.
// BT-1 Belegnummer, BT-2 Belegdatum, BT-5 Währung
FacturXInvoice invoice = FacturXInvoice.CreateInvoice(
invoiceNo: "471102",
invoiceDate: new DateTime(2026, 3, 5),
currency: CurrencyCodes.EUR);
// BT-3: 389 = Gutschrift (Selbstabrechnung). Der einzige Unterschied zu einer
// gewöhnlichen Handelsrechnung (380) im gesamten Beleg – alle Beträge bleiben positiv.
invoice.Type = InvoiceType.SelfBilledInvoice;
' BT-1 Belegnummer, BT-2 Belegdatum, BT-5 Währung
Dim invoice As FacturXInvoice = FacturXInvoice.CreateInvoice(
invoiceNo:="471102",
invoiceDate:=New DateTime(2026, 3, 5),
currency:=CurrencyCodes.EUR)
' BT-3: 389 = Gutschrift (Selbstabrechnung). Der einzige Unterschied zu einer
' gewöhnlichen Handelsrechnung (380) im gesamten Beleg – alle Beträge bleiben positiv.
invoice.Type = InvoiceType.SelfBilledInvoice
2. Freitexte
Vier Notizen in ram:IncludedNote (BG-1). Die erste kommt ohne
subjectCode aus, zwei tragen REG für regulatorische
Angaben, die letzte ACB für die Formatkennzeichnung.
// Notiz ohne subjectCode: reiner Freitext, hier der Bezug auf die Bestellung.
invoice.AddNote("Rechnung gemäß Bestellung vom 01.03.2026.");
// REG = Regulatorische Angaben. Das Beispiel nutzt zwei davon: handelsrechtliche
// Vertretung und Handelsregisternummer.
invoice.AddNote("Geschäftsführer: Hans Muster",
subjectCode: SubjectCodes.REG);
invoice.AddNote("Handelsregisternummer: H A 123",
subjectCode: SubjectCodes.REG);
// ACB = zusätzliche Information, hier die Formatkennzeichnung.
invoice.AddNote("ZUGFeRD vers 2.5.0 EN16931 (Comfort)",
subjectCode: SubjectCodes.ACB);
' Notiz ohne subjectCode: reiner Freitext, hier der Bezug auf die Bestellung.
invoice.AddNote("Rechnung gemäß Bestellung vom 01.03.2026.")
' REG = Regulatorische Angaben. Das Beispiel nutzt zwei davon: handelsrechtliche
' Vertretung und Handelsregisternummer.
invoice.AddNote("Geschäftsführer: Hans Muster",
subjectCode:=SubjectCodes.REG)
invoice.AddNote("Handelsregisternummer: H A 123",
subjectCode:=SubjectCodes.REG)
' ACB = zusätzliche Information, hier die Formatkennzeichnung.
invoice.AddNote("ZUGFeRD vers 2.5.0 EN16931 (Comfort)",
subjectCode:=SubjectCodes.ACB)
3. Verkäufer mit zwei Steuerregistrierungen
AddSellerTaxRegistration lässt sich mehrfach aufrufen. Hier steht
zuerst die nationale Steuernummer (FC, BT-32), dann die USt-IdNr.
(VA, BT-31). Die Reihenfolge im XML folgt der Aufrufreihenfolge.
// id → ram:ID (BT-29) interne Lieferantennummer
// globalID → ram:GlobalID (BT-29-0) mit schemeID "0088" = GLN
invoice.SetSeller(
name: "Lieferant GmbH",
postcode: "80333",
city: "München",
street: "Lieferantenstraße 20",
country: CountryCodes.DE,
id: "549910",
globalID: new GlobalID(GlobalIDSchemeIdentifiers.GLN, "4000001123452"));
// BT-32 / BT-31: zwei Steuerregistrierungen des Verkäufers. Die Reihenfolge im XML
// entspricht der Aufrufreihenfolge hier.
// FC = nationale Steuernummer (BT-32)
// VA = USt-IdNr. (BT-31)
invoice.AddSellerTaxRegistration("201/113/40209", TaxRegistrationSchemeID.FC);
invoice.AddSellerTaxRegistration("DE136695976", TaxRegistrationSchemeID.VA);
' id → ram:ID (BT-29) interne Lieferantennummer
' globalID → ram:GlobalID (BT-29-0) mit schemeID "0088" = GLN
invoice.SetSeller(
name:="Lieferant GmbH",
postcode:="80333",
city:="München",
street:="Lieferantenstraße 20",
country:=CountryCodes.DE,
id:="549910",
globalID:=New GlobalID(GlobalIDSchemeIdentifiers.GLN, "4000001123452"))
' BT-32 / BT-31: zwei Steuerregistrierungen des Verkäufers. Die Reihenfolge im XML
' entspricht der Aufrufreihenfolge hier.
' FC = nationale Steuernummer (BT-32)
' VA = USt-IdNr. (BT-31)
invoice.AddSellerTaxRegistration("201/113/40209", TaxRegistrationSchemeID.FC)
invoice.AddSellerTaxRegistration("DE136695976", TaxRegistrationSchemeID.VA)
4. Käufer – mit eigener Steuernummer
Hier zeigt sich das Gutschriftverfahren im Datenmodell: Weil der Leistungsempfänger abrechnet, trägt auch er eine USt-IdNr. im Beleg (BT-48). Bei einer gewöhnlichen Handelsrechnung bleibt dieses Feld meist leer.
// Hier nur eine interne Kundennummer, keine GLN.
invoice.SetBuyer(
name: "Kunden AG Mitte",
postcode: "69876",
city: "Frankfurt",
street: "Kundenstraße 15",
country: CountryCodes.DE,
id: "GE2020211");
// BT-48: USt-IdNr. des Käufers. Im Gutschriftverfahren rechnet der Leistungsempfänger
// ab, deshalb steht seine Steuernummer im Beleg.
invoice.AddBuyerTaxRegistration("DE136695976", TaxRegistrationSchemeID.VA);
// BT-72: tatsächliches Lieferdatum → ram:ActualDeliverySupplyChainEvent
invoice.ActualDeliveryDate = new DateTime(2026, 3, 3);
' Hier nur eine interne Kundennummer, keine GLN.
invoice.SetBuyer(
name:="Kunden AG Mitte",
postcode:="69876",
city:="Frankfurt",
street:="Kundenstraße 15",
country:=CountryCodes.DE,
id:="GE2020211")
' BT-48: USt-IdNr. des Käufers. Im Gutschriftverfahren rechnet der Leistungsempfänger
' ab, deshalb steht seine Steuernummer im Beleg.
invoice.AddBuyerTaxRegistration("DE136695976", TaxRegistrationSchemeID.VA)
' BT-72: tatsächliches Lieferdatum → ram:ActualDeliverySupplyChainEvent
invoice.ActualDeliveryDate = New DateTime(2026, 3, 3)
5. Belegpositionen
Zwei Positionen (BG-25) ohne jeden Artikelrabatt. Brutto- und Nettopreis sind
deshalb identisch – die Referenz schreibt trotzdem beide Elemente aus.
Bemerkenswert ist, dass beide Positionen in derselben Steuerkategorie
S liegen und sich nur im Satz unterscheiden: 19 % für
Büromaterial, 7 % für Lebensmittel.
// Position 1: 20 Stück Trennblätter zu 9,90 € = 198,00 €, 19 % USt
invoice.AddTradeLineItem(
lineID: "1",
name: "Trennblätter A4",
netUnitPrice: 9.90m,
grossUnitPrice: 9.90m,
unitCode: QuantityCodes.H87, // H87 = Stück
billedQuantity: 20m,
lineTotalAmount: 198.00m,
taxType: TaxTypes.VAT,
categoryCode: TaxCategoryCodes.S, // S = Regelsteuersatz
taxPercent: 19m,
sellerAssignedID: "TB100A4",
id: new GlobalID(GlobalIDSchemeIdentifiers.EAN, "4012345001235"));
// Position 2: 50 Stück Joghurt zu 5,50 € = 275,00 €, 7 % USt.
// Lebensmittel, deshalb der ermäßigte Satz – innerhalb derselben Steuerkategorie S.
invoice.AddTradeLineItem(
lineID: "2",
name: "Joghurt Banane",
netUnitPrice: 5.50m,
grossUnitPrice: 5.50m,
unitCode: QuantityCodes.H87,
billedQuantity: 50m,
lineTotalAmount: 275.00m,
taxType: TaxTypes.VAT,
categoryCode: TaxCategoryCodes.S,
taxPercent: 7m,
sellerAssignedID: "ARNR2",
id: new GlobalID(GlobalIDSchemeIdentifiers.EAN, "4000050986428"));
' Position 1: 20 Stück Trennblätter zu 9,90 € = 198,00 €, 19 % USt
invoice.AddTradeLineItem(
lineID:="1",
name:="Trennblätter A4",
netUnitPrice:=9.9D,
grossUnitPrice:=9.9D,
unitCode:=QuantityCodes.H87, ' H87 = Stück
billedQuantity:=20D,
lineTotalAmount:=198D,
taxType:=TaxTypes.VAT,
categoryCode:=TaxCategoryCodes.S, ' S = Regelsteuersatz
taxPercent:=19D,
sellerAssignedID:="TB100A4",
id:=New GlobalID(GlobalIDSchemeIdentifiers.EAN, "4012345001235"))
' Position 2: 50 Stück Joghurt zu 5,50 € = 275,00 €, 7 % USt.
' Lebensmittel, deshalb der ermäßigte Satz – innerhalb derselben Steuerkategorie S.
invoice.AddTradeLineItem(
lineID:="2",
name:="Joghurt Banane",
netUnitPrice:=5.5D,
grossUnitPrice:=5.5D,
unitCode:=QuantityCodes.H87,
billedQuantity:=50D,
lineTotalAmount:=275D,
taxType:=TaxTypes.VAT,
categoryCode:=TaxCategoryCodes.S,
taxPercent:=7D,
sellerAssignedID:="ARNR2",
id:=New GlobalID(GlobalIDSchemeIdentifiers.EAN, "4000050986428"))
6. Zahlungsweg
SEPA-Überweisung (TypeCode 58) und das Konto des Zahlungsempfängers. Weitere Zahlungswege und deren Felder beschreibt Laden und Speichern.
// BG-16 ram:SpecifiedTradeSettlementPaymentMeans, TypeCode 58 = SEPA-Überweisung.
invoice.SetPaymentMeans(PaymentMeansTypeCodes.SEPACreditTransfer);
// BG-17: Konto des Zahlungsempfängers. BIC bleibt leer – bei einer SEPA-Überweisung
// innerhalb Deutschlands genügt die IBAN. AccountName (BT-85) ist der Kontoinhaber.
invoice.AddCreditorFinancialAccount(
iban: "DE89370400440532013000",
bic: null,
name: "Lieferant GmbH");
' BG-16 ram:SpecifiedTradeSettlementPaymentMeans, TypeCode 58 = SEPA-Überweisung.
invoice.SetPaymentMeans(PaymentMeansTypeCodes.SEPACreditTransfer)
' BG-17: Konto des Zahlungsempfängers. BIC bleibt leer – bei einer SEPA-Überweisung
' innerhalb Deutschlands genügt die IBAN. AccountName (BT-85) ist der Kontoinhaber.
invoice.AddCreditorFinancialAccount(
iban:="DE89370400440532013000",
bic:=Nothing,
name:="Lieferant GmbH")
7. Umsatzsteueraufstellung
Ohne Zu- und Abschläge auf Dokumentenebene ist die Bemessungsgrundlage schlicht
die Summe der zugehörigen Positionen – kein
allowanceChargeBasisAmount, keine Korrekturrechnung:
| Feld | 7 % (Pos. 2) | 19 % (Pos. 1) |
|---|---|---|
basisAmount |
275,00 | 198,00 |
taxAmount |
275,00 × 7 % = 19,25 | 198,00 × 19 % = 37,62 |
Die Spaltenfolge ist kein Versehen: Die Referenz führt 7 % vor
19 % auf, also nicht in der Reihenfolge der Positionen.
FactoorSharp gibt die Steuergruppen in der Reihenfolge der
AddApplicableTradeTax-Aufrufe aus und sortiert nicht nach. Wer
die Referenzdatei zeichengenau treffen will, muss die Aufrufe entsprechend
anordnen.
// Die Reihenfolge entspricht der Referenz – 7 % vor 19 %, also NICHT der
// Positionsreihenfolge. Der Writer gibt die Gruppen in Aufrufreihenfolge aus und
// sortiert nicht nach.
invoice.AddApplicableTradeTax(
basisAmount: 275.00m,
percent: 7m,
taxAmount: 19.25m,
typeCode: TaxTypes.VAT,
categoryCode: TaxCategoryCodes.S);
invoice.AddApplicableTradeTax(
basisAmount: 198.00m,
percent: 19m,
taxAmount: 37.62m,
typeCode: TaxTypes.VAT,
categoryCode: TaxCategoryCodes.S);
' Die Reihenfolge entspricht der Referenz – 7 % vor 19 %, also NICHT der
' Positionsreihenfolge. Der Writer gibt die Gruppen in Aufrufreihenfolge aus und
' sortiert nicht nach.
invoice.AddApplicableTradeTax(
basisAmount:=275D,
percent:=7D,
taxAmount:=19.25D,
typeCode:=TaxTypes.VAT,
categoryCode:=TaxCategoryCodes.S)
invoice.AddApplicableTradeTax(
basisAmount:=198D,
percent:=19D,
taxAmount:=37.62D,
typeCode:=TaxTypes.VAT,
categoryCode:=TaxCategoryCodes.S)
8. Zahlungsbedingung
Nur ein Fälligkeitsdatum, kein Skonto. Dass description hier
null ist, ist Absicht: Der Writer gibt ram:Description
dann gar nicht erst aus, statt ein leeres Element zu schreiben.
// BT-9: nur ein Fälligkeitsdatum, kein Skonto und kein Freitext. description bleibt
// deshalb null; der Writer gibt ram:Description dann gar nicht erst aus.
invoice.AddTradePaymentTerms(
description: null,
dueDate: new DateTime(2026, 4, 5));
' BT-9: nur ein Fälligkeitsdatum, kein Skonto und kein Freitext. description bleibt
' deshalb Nothing; der Writer gibt ram:Description dann gar nicht erst aus.
invoice.AddTradePaymentTerms(
description:=Nothing,
dueDate:=New DateTime(2026, 4, 5))
9. Gesamtsummen
| BT-Code | Parameter | Rechenweg |
|---|---|---|
| BT-106 | lineTotalAmount |
198,00 + 275,00 = 473,00 |
| BT-107 / BT-108 | allowanceTotalAmount / chargeTotalAmount |
0,00 – keine Zu- oder Abschläge |
| BT-109 | taxBasisAmount |
473,00 |
| BT-110 | taxTotalAmount |
19,25 + 37,62 = 56,87 |
| BT-112 | grandTotalAmount |
473,00 + 56,87 = 529,87 |
| BT-115 | duePayableAmount |
529,87 − 0,00 = 529,87 |
invoice.SetTotals(
lineTotalAmount: 473.00m, // BT-106
chargeTotalAmount: 0.00m, // BT-108
allowanceTotalAmount: 0.00m, // BT-107
taxBasisAmount: 473.00m, // BT-109
taxTotalAmount: 56.87m, // BT-110
grandTotalAmount: 529.87m, // BT-112
totalPrepaidAmount: 0.00m, // BT-113
duePayableAmount: 529.87m); // BT-115
invoice.SetTotals(
lineTotalAmount:=473D, ' BT-106
chargeTotalAmount:=0D, ' BT-108
allowanceTotalAmount:=0D, ' BT-107
taxBasisAmount:=473D, ' BT-109
taxTotalAmount:=56.87D, ' BT-110
grandTotalAmount:=529.87D, ' BT-112
totalPrepaidAmount:=0D, ' BT-113
duePayableAmount:=529.87D) ' BT-115
10. Speichern
Hier steht Profile.Comfort statt Profile.Extended
– der einzige Unterschied zu den übrigen Beispielen an dieser Stelle:
// Version25 + Profile.Comfort + CII ergibt die Profilkennung
// "urn:cen.eu:en16931:2017" – das reine EN 16931-Profil ohne Erweiterung.
invoice.Save("E10_01_Gutschrift.xml",
ZUGFeRDVersion.Version25,
Profile.Comfort,
ZUGFeRDFormats.CII);
' Version25 + Profile.Comfort + CII ergibt die Profilkennung
' "urn:cen.eu:en16931:2017" – das reine EN 16931-Profil ohne Erweiterung.
invoice.Save("E10_01_Gutschrift.xml",
ZUGFeRDVersion.Version25,
Profile.Comfort,
ZUGFeRDFormats.CII)
11. Gegenprobe
Die erzeugte Datei muss dieselbe Baumstruktur haben wie
E10_01_Gutschrift.xml aus dem FeRD-Beispielpaket. Zahlen vergleicht
man numerisch, damit 9.90 und 9.9000 als gleich gelten.
Ob der Beleg 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, "E10_01_Gutschrift.pdf");
InvoiceVisualizer.RenderHtml(invoice, "E10_01_Gutschrift.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, "E10_01_Gutschrift.pdf")
InvoiceVisualizer.RenderHtml(invoice, "E10_01_Gutschrift.html")
Das ist eine reine Ansicht, kein Rechtsdokument und kein hybrides ZUGFeRD-PDF – Details dazu unter Bildliche Darstellung.