<!-- md-source: ae792a99ab80 FactoorSharpWeb/wwwroot/de/Service/GoodsInvoice.md -->

# Facture de marchandises étendue

> Version Markdown de <https://www.factoorsharp.de/fr/Service/GoodsInvoice> destinée aux agents IA.
> Langues : [Deutsch](https://www.factoorsharp.de/de/Service/GoodsInvoice.md) ·
> [English](https://www.factoorsharp.de/en/Service/GoodsInvoice.md) ·
> [Français](https://www.factoorsharp.de/fr/Service/GoodsInvoice.md)

Une facture de marchandises complète dans le profil EXTENDED : six lignes avec
indications de conditionnement et remises sur article, deux taux de TVA, des remises au
niveau du document, des frais de transport, un escompte, ainsi qu'un destinataire des
marchandises et un destinataire de la facture différents de l'acheteur.

La base est la facture d'exemple officielle `X19_01_Warenrechnung` issue de la
documentation Factur-X / ZUGFeRD (jeu d'exemples FeRD, ZUGFeRD 2.5.0, profil EXTENDED).
Sur le plan métier, une livraison d'un grossiste en alimentation vers un magasin de
supermarché.

Tous les champs EXTENDED – nom de document, indicateur de test, destinataire de la
facture, indications de conditionnement, composants d'une unité de vente, frais de
transport et bases d'imposition étendues – ne sont écrits que si l'enregistrement final
se fait effectivement en `Profile.Extended`. Un profil plus petit n'est pas une erreur ;
les champs disparaissent alors simplement sans un mot.

Les textes des notes, les noms de produits et les motifs de remise restent en allemand :
ce sont les données de la facture de référence officielle, et les traduire romprait la
comparaison avec elle.

## 1. En-tête de facture

```csharp
// BT-1 numéro de facture, BT-2 date de facture, BT-5 devise de facturation
FacturXInvoice invoice = FacturXInvoice.CreateInvoice(
    invoiceNo: "R87654321012345",
    invoiceDate: new DateTime(2025, 10, 1),
    currency: CurrencyCodes.EUR);

// BT-3 : type de facture 380 = facture commerciale. C'est la valeur par défaut, écrite ici
// explicitement pour montrer où le type est défini.
invoice.Type = InvoiceType.Invoice;

// BT-X-2 : nom de document libre. Autorisé et écrit uniquement dans le profil EXTENDED –
// en BASIC/COMFORT, le writer omet silencieusement l'élément.
invoice.Name = "WARENRECHNUNG";

// ram:TestIndicator marque le document comme jeu de test (également EXTENDED uniquement).
invoice.IsTest = true;
```

## 2. Notes en texte libre

Le texte libre aboutit dans `ram:IncludedNote` (BG-1). `subjectCode` (BT-21, UNTDID 4451)
dit de quoi parle la note, `contentCode` (BT-X-5) est un bloc de texte normalisé
supplémentaire. Les deux codes **ne se combinent pas librement** : `ST1`, `ST2` et `ST3`
appartiennent à `AAK` (accords de remise et de bonus), `EEV`, `WEB` et `VEV` à `AAJ`
(réserve de propriété).

```csharp
// REG = mentions réglementaires, ici la représentation légale.
invoice.AddNote("Geschäftsführer: Herr Geschäftsführer ,  MUSTERLIEFERANT GmbH  ",
                subjectCode: SubjectCodes.REG);

// AAI = information générale.
invoice.AddNote("Es bestehen Vereinbarungen, aus denen sich Minderungen des Entgelts ergeben können.",
                subjectCode: SubjectCodes.AAI);

// ACB = information complémentaire ; ici pour l'identification du format …
invoice.AddNote("ZUGFeRD vers 2.5.0 (Extended)", subjectCode: SubjectCodes.ACB);
// … et pour indiquer qu'il s'agit d'un exemple.
invoice.AddNote("Dies ist ein Waren-Rechnungs-Beispiel", subjectCode: SubjectCodes.ACB);

// AAK + ST3 : mention normalisée d'accords de remise ou de bonus existants.
invoice.AddNote("Es bestehen Rabatt- oder Bonusvereinbarungen.",
                subjectCode: SubjectCodes.AAK,
                contentCode: ContentCodes.ST3);

// AAJ + EEV : réserve de propriété normalisée.
invoice.AddNote("Der Verkäufer bleibt Eigentümer der Waren bis zu vollständigen Erfüllung der Kaufpreisforderung.",
                subjectCode: SubjectCodes.AAJ,
                contentCode: ContentCodes.EEV);

// Les notes sans subjectCode sont autorisées – texte libre pur.
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. Vendeur

`SetSeller` remplit `ram:SellerTradeParty` (BG-4). La partie porte deux identifiants : le
numéro de fournisseur interne (`id`, BT-29) et le GLN (`globalID` avec le schemeID `0088`).

```csharp
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 : interlocuteur. Nom et service restent vides – le writer n'émet alors
// pas du tout ram:PersonName / ram:DepartmentName.
invoice.SetSellerContact(
    emailAddress: "max.mustermann@musterlieferant.de",
    phoneno: "+49 932 431 500");

// BT-34 : adresse électronique du vendeur, ici un EAN Location Code (0088).
invoice.SetSellerElectronicAddress("info@musterlieferant.de",
                                   ElectronicAddressSchemeIdentifiers.EanLocationCode);

// BT-31 : n° de TVA du vendeur (schéma VA). FC serait le numéro fiscal national.
invoice.AddSellerTaxRegistration("DE123456788", TaxRegistrationSchemeID.VA);
```

## 4. Acheteur

L'acheteur est le siège – la livraison va à un magasin, la facture à un troisième site.

```csharp
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. Références de documents dans l'en-tête

```csharp
// BT-13 : numéro de commande de l'acheteur → ram:BuyerOrderReferencedDocument
invoice.SetBuyerOrderReferenceDocument("B123456789");

// BT-18 / BG-24 : référence de document supplémentaire. Code de type 130 = fiche de données.
invoice.AddAdditionalReferencedDocument(
    id: "A456123",
    typeCode: AdditionalReferencedDocumentTypeCode.InvoiceDataSheet);

// BT-16 : numéro de bon de livraison → ram:DeliveryNoteReferencedDocument (EXTENDED)
invoice.SetDeliveryNoteReferenceDocument("L87654321012345");

// BT-72 : date de livraison effective → ram:ActualDeliverySupplyChainEvent
invoice.ActualDeliveryDate = new DateTime(2025, 10, 1);
```

## 6. Destinataire des marchandises et de la facture

Le destinataire des marchandises (BG-13) et celui de la facture (BG-X-36) ne sont pas
définis par des méthodes `Set…` mais comme des objets `Party`.

```csharp
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 – le service ("8211") dans le magasin.
invoice.ShipToContact = new Contact() { OrgUnit = "8211" };

// BG-X-36 (EXTENDED uniquement) : même société que l'acheteur, autre site.
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
};
```

## 7. Lignes de facture

Six lignes (BG-25). Le motif récurrent :

| Paramètre | Code BT | Signification |
|---|---|---|
| `lineID` | BT-126 | Numéro de ligne ; s'il est omis, FactoorSharp l'attribue automatiquement |
| `id` | BT-157 | GTIN avec le schemeID `0160` (GS1), `GlobalIDSchemeIdentifiers.EAN` |
| `sellerAssignedID` | BT-155 | Référence article du fournisseur |
| `buyerAssignedID` | BT-156 | Référence article de l'acheteur pour la même marchandise |
| `grossUnitPrice` | BT-148 | Prix brut, prix catalogue avant remise sur article |
| `netUnitPrice` | BT-146 | Prix net après remise sur article – base du total de ligne |
| `lineTotalAmount` | BT-131 | `netUnitPrice` × `billedQuantity` |
| `PackageQuantity` / `PackageUnitCode` | BT-X-9 | Unité d'expédition, EXTENDED uniquement. `XCT` = carton, `XBC` = caisse, `XBO` = bouteille, `XPX` = palette |

### Ligne 1 – attribut d'article et unité d'expédition

```csharp
// 100 bouteilles d'acide citrique à 1,00 € = 100,00 €, 19 % de TVA
TradeLineItem line1 = invoice.AddTradeLineItem(
    lineID: "1",
    name: "Zitronensäure 100ml",
    netUnitPrice: 1.00m,
    grossUnitPrice: 1.00m,
    unitCode: QuantityCodes.H87,       // H87 = pièce
    billedQuantity: 100m,
    lineTotalAmount: 100.00m,
    taxType: TaxTypes.VAT,
    categoryCode: TaxCategoryCodes.S,  // S = taux normal
    taxPercent: 19m,
    sellerAssignedID: "ZS997",
    id: new GlobalID(GlobalIDSchemeIdentifiers.EAN, "4123456000014"));

// BG-32 attribut d'article : paire nom/valeur librement définissable (BT-160 / BT-161).
line1.ApplicableProductCharacteristics.Add(new ApplicableProductCharacteristic()
{
    Description = "Verpackungsart",
    Value       = "BO"
});

line1.PackageQuantity = 4m;
line1.PackageUnitCode = QuantityCodes.XCT;
```

### Ligne 2 – remises sur article dans le prix brut

Ces remises ne se situent pas au niveau du document mais comme
`ram:AppliedTradeAllowanceCharge` à l'intérieur du prix brut (BT-147). Les montants
valent **par unité** : 1,50 − 0,03 − 0,02 = 1,45. FactoorSharp ne calcule pas le prix
net – vous le définissez, et les remises l'expliquent.

```csharp
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"));

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;
```

### Ligne 3 – remise en nature

Dix pièces sans facturation. La ligne reste dans le document pour que la quantité livrée
reste traçable, mais contribue 0,00 € au total.

```csharp
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;
```

### Lignes 4 et 5 – unités divergentes et référence acheteur

Facturée en caisses, expédiée en bouteilles. La ligne 5 est la consigne d'emballages
correspondante et porte en plus la référence article de l'acheteur (BT-156).

```csharp
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 = caisse
    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 = bouteille

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;
```

### Ligne 6 – palette mixte et ses composants

La palette est facturée comme une seule ligne, son contenu décomposé via
`IncludedReferencedProducts` (BG-X-1). Purement informatif : les prix et les taxes restent
attachés à la ligne parente.

```csharp
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"
});

// La méthode pratique AddIncludedReferencedProduct() ne connaît pas GlobalID, c'est pourquoi
// les objets sont créés directement ici.
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 comme à la ligne 2 : remise par unité, 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
```

## 8. Remises au niveau du document

Une remise au niveau du document ne vaut jamais que pour **un** taux de TVA. Comme ce
document mêle 19 % et 7 %, chaque remise doit être créée **deux fois** – deux remises
deviennent quatre appels. L'ordre dans le XML correspond à l'ordre des appels dans le
code.

Les bases de calcul ne sont pas simplement les totaux de ligne : pour 19 %, les lignes 1,
4 et 5 donneraient ensemble 326,50 €, mais seuls 280,00 € ont été convenus – la consigne
d'emballages et une partie de la marchandise sont exclues de la remise. Pour 7 %, c'est le
total complet des lignes 2, 3 et 6 qui s'applique : 130,70 €.

```csharp
// Remise sur facture 1 – en pourcentage, 2 % (BT-94 CalculationPercent + BT-93 BasisAmount).
invoice.AddTradeAllowance(
    basisAmount: 280.00m, currency: CurrencyCodes.EUR, actualAmount: 5.60m,
    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,
    chargePercentage: 2.00m, reason: "Rechnungsrabatt 1",
    taxTypeCode: TaxTypes.VAT, taxCategoryCode: TaxCategoryCodes.S, taxPercent: 7m);

// Remise sur facture 2 – montant fixe, donc sans 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);
```

## 9. Frais de transport

Les frais logistiques ont un élément dédié en CII et ne sont pas la même chose qu'un
supplément ordinaire via `AddTradeCharge`. Interdit dans XRechnung, autorisé dans ZUGFeRD
EXTENDED. Alimente `ChargeTotalAmount` (BT-108).

```csharp
invoice.AddLogisticsServiceCharge(
    amount: 3.00m,
    description: "Transportkosten",
    taxTypeCode: TaxTypes.VAT,
    taxCategoryCode: TaxCategoryCodes.S,
    taxPercent: 19m);
```

## 10. Ventilation de la TVA

Un groupe `ram:ApplicableTradeTax` (BG-23) est obligatoire par combinaison de taux et de
catégorie de TVA. FactoorSharp ne fait pas les sommes lui-même ; les valeurs sont définies.

| Champ | 19 % (lignes 1, 4, 5) | 7 % (lignes 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** |

Le supplément pour les frais de transport entre dans la ligne à 19 % avec un signe
positif, les remises avec un signe négatif.

```csharp
// lineTotalBasisAmount et allowanceChargeBasisAmount sont des champs EXTENDED ;
// en BASIC/COMFORT, ils disparaissent sans remplacement.
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);
```

## 11. Condition de paiement avec escompte

`paymentTermsType = Skonto` crée le bloc `ram:ApplicableTradePaymentDiscountTerms` avec
`BasisPeriodMeasure` (jours) et `CalculationPercent`. Sans ce type, il ne resterait que le
texte libre – l'escompte ne serait alors pas exploitable automatiquement.

```csharp
invoice.AddTradePaymentTerms(
    description: "Bei Zahlung innerhalb 14 Tagen gewähren wir 2,0% Skonto.",
    paymentTermsType: PaymentTermsType.Skonto,
    dueDays: 14,
    percentage: 2.00m);
```

## 12. Totaux du document

| Code BT | Paramètre | Calcul |
|---|---|---|
| BT-106 | `lineTotalAmount` | 100,00 + 72,50 + 0,00 + 180,00 + 46,50 + 58,20 = **457,20** |
| BT-108 | `chargeTotalAmount` | 3,00 (frais de transport) |
| 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 – aucun acompte |
| BT-115 | `duePayableAmount` | 518,99 − 0,00 = **518,99** |

```csharp
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
```

## 13. Enregistrement

Ce n'est qu'à l'enregistrement que se décide quels champs définis ci-dessus atterrissent
réellement dans le XML. Version, profil et syntaxe vont ensemble.

```csharp
// Version25 + Profile.Extended + CII donne l'identifiant de profil
// "urn:cen.eu:en16931:2017#conformant#urn:factur-x.eu:1p0:extended".
invoice.Save("X19_01_Warenrechnung.xml",
             ZUGFeRDVersion.Version25,
             Profile.Extended,
             ZUGFeRDFormats.CII);
```

## 14. Contrôle croisé

Le fichier produit doit présenter la même structure arborescente que
`X19_01_Warenrechnung.xml` du jeu d'exemples FeRD. Les nombres se comparent
numériquement, de sorte que `1.00` et `1.0000` comptent comme égaux – FactoorSharp écrit
les prix de manière adaptative avec deux décimales.

```csharp
// 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, "X19_01_Warenrechnung.pdf");
InvoiceVisualizer.RenderHtml(invoice, "X19_01_Warenrechnung.html");
```

Le validateur, le visualiseur et leur documentation détaillée se trouvent dans l'espace
client sous <https://www.factoorsharp.de/support/>.

## Pages liées

- [Facture rectificative](https://www.factoorsharp.de/fr/Service/CorrectionInvoice.md) : type 384 avec montants négatifs, distinguée de l'avoir.
- [Facture en devise étrangère](https://www.factoorsharp.de/fr/Service/ForeignCurrencyInvoice.md) : GBP avec montant de TVA également en EUR et taux de change.
- [Pour commencer](https://www.factoorsharp.de/fr/Home/GettingStarted.md) : installation, clé de licence et première facture.
- [Référence Factur-X](https://www.factoorsharp.de/fr/Service/Documentation) : éléments XML et numéros BT/BG à consulter.
