Facture d’autofacturation
L’autofacturation au sens du § 14 al. 2 de la loi allemande sur la TVA : c’est le preneur qui établit le document, pas le fournisseur. Deux lignes, deux taux de TVA, ni supplément ni remise – et comme seule différence avec une facture commerciale ordinaire, le type de document 389.
La base est la facture d’exemple officielle
E10_01_Gutschrift issue de la documentation
Factur-X / ZUGFeRD (jeu d’exemples FeRD, ZUGFeRD 2.5.0). Le code
ci-dessous produit exactement ce document, champ par champ et dans le même
ordre.
Certains liens de cette page mènent à la documentation approfondie dans l’espace client et demandent une connexion.
Deux choses appelées « Gutschrift »
Le mot allemand recouvre deux réalités totalement différentes, et les confondre est l’erreur la plus fréquente dans ce domaine :
| Autofacturation (389) | Avoir (381) | |
|---|---|---|
| FactoorSharp | InvoiceType.SelfBilledInvoice |
InvoiceType.CreditNote |
| Qui l’établit ? | Le preneur de la prestation facture pour le compte du fournisseur. | Le fournisseur lui-même. |
| Montants | positifs – le document ressemble à une facture | positifs ; le document reflète la facture dans son ensemble |
| Fondement juridique | § 14 al. 2 UStG (self-billed invoice) | correction d’une créance |
| Cette page | traite ce cas | voir la facture rectificative (type 384) pour la distinction |
En autofacturation, tous les montants restent positifs. Mettre ici des quantités négatives parce que « Gutschrift » évoque une contre-passation produit un document faux sur le plan métier. Les montants négatifs relèvent de la facture rectificative.
Profil COMFORT plutôt qu’EXTENDED
Contrairement aux autres exemples de ces pages, ce document relève du profil
Profile.Comfort – identifiant de guideline
urn:cen.eu:en16931:2017, c’est-à-dire le profil EN 16931
pur, sans extension. Ce n’est pas une négligence de la référence mais
l’affirmation que ce cas de gestion se passe des champs EXTENDED.
En conséquence, tous les champs que présente la facture de marchandises étendue sont absents ici : nom de document (BT-X-2), indicateur de test, indications de conditionnement (BT-X-9) et bases d’imposition étendues. Les renseigner ne serait pas une erreur – le writer les écarterait silencieusement dans le profil COMFORT.
1. En-tête du document
Numéro, date et devise comme dans toute facture. La seule ligne qui fait de ce
document une autofacturation est invoice.Type.
// BT-1 numéro du document, BT-2 date du document, BT-5 devise
FacturXInvoice invoice = FacturXInvoice.CreateInvoice(
invoiceNo: "471102",
invoiceDate: new DateTime(2026, 3, 5),
currency: CurrencyCodes.EUR);
// BT-3 : 389 = autofacturation. La seule différence avec une facture commerciale
// ordinaire (380) dans tout le document – tous les montants restent positifs.
invoice.Type = InvoiceType.SelfBilledInvoice;
' BT-1 numéro du document, BT-2 date du document, BT-5 devise
Dim invoice As FacturXInvoice = FacturXInvoice.CreateInvoice(
invoiceNo:="471102",
invoiceDate:=New DateTime(2026, 3, 5),
currency:=CurrencyCodes.EUR)
' BT-3 : 389 = autofacturation. La seule différence avec une facture commerciale
' ordinaire (380) dans tout le document – tous les montants restent positifs.
invoice.Type = InvoiceType.SelfBilledInvoice
2. Notes en texte libre
Quatre notes dans ram:IncludedNote (BG-1). La première se passe de
subjectCode, deux portent REG pour les mentions
réglementaires, la dernière ACB pour l’identification du
format.
// Note sans subjectCode : texte libre pur, ici le renvoi à la commande.
invoice.AddNote("Rechnung gemäß Bestellung vom 01.03.2026.");
// REG = mentions réglementaires. L'exemple en utilise deux : la représentation
// légale et le numéro du registre du commerce.
invoice.AddNote("Geschäftsführer: Hans Muster",
subjectCode: SubjectCodes.REG);
invoice.AddNote("Handelsregisternummer: H A 123",
subjectCode: SubjectCodes.REG);
// ACB = information complémentaire, ici l'identification du format.
invoice.AddNote("ZUGFeRD vers 2.5.0 EN16931 (Comfort)",
subjectCode: SubjectCodes.ACB);
' Note sans subjectCode : texte libre pur, ici le renvoi à la commande.
invoice.AddNote("Rechnung gemäß Bestellung vom 01.03.2026.")
' REG = mentions réglementaires. L'exemple en utilise deux : la représentation
' légale et le numéro du registre du commerce.
invoice.AddNote("Geschäftsführer: Hans Muster",
subjectCode:=SubjectCodes.REG)
invoice.AddNote("Handelsregisternummer: H A 123",
subjectCode:=SubjectCodes.REG)
' ACB = information complémentaire, ici l'identification du format.
invoice.AddNote("ZUGFeRD vers 2.5.0 EN16931 (Comfort)",
subjectCode:=SubjectCodes.ACB)
3. Vendeur avec deux immatriculations fiscales
AddSellerTaxRegistration peut être appelée plusieurs fois. Ici, le
numéro fiscal national (FC, BT-32) vient d’abord, puis le
numéro de TVA (VA, BT-31). L’ordre dans le XML suit
l’ordre des appels.
// id → ram:ID (BT-29) numéro de fournisseur interne
// globalID → ram:GlobalID (BT-29-0) avec 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 : deux immatriculations fiscales du vendeur. L'ordre dans le XML
// suit l'ordre des appels ici.
// FC = numéro fiscal national (BT-32)
// VA = n° de TVA (BT-31)
invoice.AddSellerTaxRegistration("201/113/40209", TaxRegistrationSchemeID.FC);
invoice.AddSellerTaxRegistration("DE136695976", TaxRegistrationSchemeID.VA);
' id → ram:ID (BT-29) numéro de fournisseur interne
' globalID → ram:GlobalID (BT-29-0) avec 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 : deux immatriculations fiscales du vendeur. L'ordre dans le XML
' suit l'ordre des appels ici.
' FC = numéro fiscal national (BT-32)
' VA = n° de TVA (BT-31)
invoice.AddSellerTaxRegistration("201/113/40209", TaxRegistrationSchemeID.FC)
invoice.AddSellerTaxRegistration("DE136695976", TaxRegistrationSchemeID.VA)
4. Acheteur – avec son propre numéro fiscal
C’est ici que l’autofacturation apparaît dans le modèle de données : comme c’est le preneur qui établit le document, lui aussi porte un numéro de TVA (BT-48). Sur une facture commerciale ordinaire, ce champ reste le plus souvent vide.
// Ici seulement un numéro client interne, pas de GLN.
invoice.SetBuyer(
name: "Kunden AG Mitte",
postcode: "69876",
city: "Frankfurt",
street: "Kundenstraße 15",
country: CountryCodes.DE,
id: "GE2020211");
// BT-48 : n° de TVA de l'acheteur. En autofacturation, c'est le preneur qui établit le
// document, c'est pourquoi son numéro fiscal y figure.
invoice.AddBuyerTaxRegistration("DE136695976", TaxRegistrationSchemeID.VA);
// BT-72 : date de livraison effective → ram:ActualDeliverySupplyChainEvent
invoice.ActualDeliveryDate = new DateTime(2026, 3, 3);
' Ici seulement un numéro client interne, pas de GLN.
invoice.SetBuyer(
name:="Kunden AG Mitte",
postcode:="69876",
city:="Frankfurt",
street:="Kundenstraße 15",
country:=CountryCodes.DE,
id:="GE2020211")
' BT-48 : n° de TVA de l'acheteur. En autofacturation, c'est le preneur qui établit le
' document, c'est pourquoi son numéro fiscal y figure.
invoice.AddBuyerTaxRegistration("DE136695976", TaxRegistrationSchemeID.VA)
' BT-72 : date de livraison effective → ram:ActualDeliverySupplyChainEvent
invoice.ActualDeliveryDate = New DateTime(2026, 3, 3)
5. Lignes du document
Deux lignes (BG-25) sans aucune remise sur article. Prix brut et prix net sont
donc identiques – la référence écrit tout de même les deux éléments. À
noter : les deux lignes relèvent de la même catégorie de TVA S
et ne diffèrent que par le taux, 19 % pour la papeterie, 7 % pour les
denrées alimentaires.
// Ligne 1 : 20 intercalaires à 9,90 € = 198,00 €, 19 % de TVA
invoice.AddTradeLineItem(
lineID: "1",
name: "Trennblätter A4",
netUnitPrice: 9.90m,
grossUnitPrice: 9.90m,
unitCode: QuantityCodes.H87, // H87 = pièce
billedQuantity: 20m,
lineTotalAmount: 198.00m,
taxType: TaxTypes.VAT,
categoryCode: TaxCategoryCodes.S, // S = taux normal
taxPercent: 19m,
sellerAssignedID: "TB100A4",
id: new GlobalID(GlobalIDSchemeIdentifiers.EAN, "4012345001235"));
// Ligne 2 : 50 yaourts à 5,50 € = 275,00 €, 7 % de TVA.
// Denrée alimentaire, d'où le taux réduit – dans la même catégorie de TVA 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"));
' Ligne 1 : 20 intercalaires à 9,90 € = 198,00 €, 19 % de TVA
invoice.AddTradeLineItem(
lineID:="1",
name:="Trennblätter A4",
netUnitPrice:=9.9D,
grossUnitPrice:=9.9D,
unitCode:=QuantityCodes.H87, ' H87 = pièce
billedQuantity:=20D,
lineTotalAmount:=198D,
taxType:=TaxTypes.VAT,
categoryCode:=TaxCategoryCodes.S, ' S = taux normal
taxPercent:=19D,
sellerAssignedID:="TB100A4",
id:=New GlobalID(GlobalIDSchemeIdentifiers.EAN, "4012345001235"))
' Ligne 2 : 50 yaourts à 5,50 € = 275,00 €, 7 % de TVA.
' Denrée alimentaire, d'où le taux réduit – dans la même catégorie de TVA 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. Moyen de paiement
Virement SEPA (code de type 58) et compte du bénéficiaire. Les autres moyens de paiement et leurs champs sont décrits sous Charger et enregistrer.
// BG-16 ram:SpecifiedTradeSettlementPaymentMeans, code de type 58 = virement SEPA.
invoice.SetPaymentMeans(PaymentMeansTypeCodes.SEPACreditTransfer);
// BG-17 : compte du bénéficiaire. Le BIC reste vide – pour un virement SEPA
// en Allemagne, l'IBAN suffit. AccountName (BT-85) est le titulaire du compte.
invoice.AddCreditorFinancialAccount(
iban: "DE89370400440532013000",
bic: null,
name: "Lieferant GmbH");
' BG-16 ram:SpecifiedTradeSettlementPaymentMeans, code de type 58 = virement SEPA.
invoice.SetPaymentMeans(PaymentMeansTypeCodes.SEPACreditTransfer)
' BG-17 : compte du bénéficiaire. Le BIC reste vide – pour un virement SEPA
' en Allemagne, l'IBAN suffit. AccountName (BT-85) est le titulaire du compte.
invoice.AddCreditorFinancialAccount(
iban:="DE89370400440532013000",
bic:=Nothing,
name:="Lieferant GmbH")
7. Ventilation de la TVA
Sans supplément ni remise au niveau du document, la base d’imposition est
simplement la somme des lignes correspondantes – pas de
allowanceChargeBasisAmount, aucun calcul de correction :
| Champ | 7 % (ligne 2) | 19 % (ligne 1) |
|---|---|---|
basisAmount |
275,00 | 198,00 |
taxAmount |
275,00 × 7 % = 19,25 | 198,00 × 19 % = 37,62 |
L’ordre des colonnes n’est pas une erreur : la référence
place 7 % avant 19 %, donc pas dans l’ordre
des lignes. FactoorSharp écrit les groupes de TVA dans l’ordre des
appels à AddApplicableTradeTax et ne les trie pas ensuite. Pour
retrouver le fichier de référence caractère par caractère, il faut ordonner
les appels en conséquence.
// L'ordre suit la référence – 7 % avant 19 %, donc PAS celui des
// lignes. Le writer écrit les groupes dans l'ordre d'ajout et ne les trie pas
// ensuite.
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);
' L'ordre suit la référence – 7 % avant 19 %, donc PAS celui des
' lignes. Le writer écrit les groupes dans l'ordre d'ajout et ne les trie pas
' ensuite.
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. Condition de paiement
Uniquement une date d’échéance, sans escompte. Que description
vaille ici null est délibéré : le writer n’émet alors pas
du tout ram:Description plutôt que d’écrire un élément vide.
// BT-9 : uniquement une date d'échéance, sans escompte ni texte libre. description reste
// donc null ; le writer n'émet alors pas du tout ram:Description.
invoice.AddTradePaymentTerms(
description: null,
dueDate: new DateTime(2026, 4, 5));
' BT-9 : uniquement une date d'échéance, sans escompte ni texte libre. description reste
' donc Nothing ; le writer n'émet alors pas du tout ram:Description.
invoice.AddTradePaymentTerms(
description:=Nothing,
dueDate:=New DateTime(2026, 4, 5))
9. Totaux du document
| Code BT | Paramètre | Calcul |
|---|---|---|
| BT-106 | lineTotalAmount |
198,00 + 275,00 = 473,00 |
| BT-107 / BT-108 | allowanceTotalAmount / chargeTotalAmount |
0,00 – ni supplément ni remise |
| 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. Enregistrement
Ici, c’est Profile.Comfort et non
Profile.Extended – la seule différence avec les autres
exemples à cet endroit :
// Version25 + Profile.Comfort + CII donne l'identifiant de profil
// "urn:cen.eu:en16931:2017" – le profil EN 16931 pur, sans extension.
invoice.Save("E10_01_Gutschrift.xml",
ZUGFeRDVersion.Version25,
Profile.Comfort,
ZUGFeRDFormats.CII);
' Version25 + Profile.Comfort + CII donne l'identifiant de profil
' "urn:cen.eu:en16931:2017" – le profil EN 16931 pur, sans extension.
invoice.Save("E10_01_Gutschrift.xml",
ZUGFeRDVersion.Version25,
Profile.Comfort,
ZUGFeRDFormats.CII)
11. Contrôle croisé
Le fichier produit doit présenter la même structure arborescente que
E10_01_Gutschrift.xml du jeu d’exemples FeRD. Les nombres se
comparent numériquement, de sorte que 9.90 et 9.9000
comptent comme égaux.
Quant à savoir si le document respecte également le jeu de règles, vous le vérifiez soit dans le navigateur, soit directement depuis votre code. Pour le regard d’un humain sur le document, le visualiseur produit une version lisible :
// Version lisible par un humain – ni document juridique ni PDF ZUGFeRD hybride.
// Le rendu se fait à partir de l'objet, pas du fichier XML.
InvoiceVisualizer.RenderPdf(invoice, "E10_01_Gutschrift.pdf");
InvoiceVisualizer.RenderHtml(invoice, "E10_01_Gutschrift.html");
' Version lisible par un humain – ni document juridique ni PDF ZUGFeRD hybride.
' Le rendu se fait à partir de l'objet, pas du fichier XML.
InvoiceVisualizer.RenderPdf(invoice, "E10_01_Gutschrift.pdf")
InvoiceVisualizer.RenderHtml(invoice, "E10_01_Gutschrift.html")
C’est une simple vue, ni document juridique ni PDF ZUGFeRD hybride – détails sous Représentation visuelle.