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

# Extended goods invoice

> Markdown version of <https://www.factoorsharp.de/en/Service/GoodsInvoice> for AI agents.
> Languages: [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)

A complete goods invoice in the EXTENDED profile: six line items with packaging details
and item discounts, two tax rates, document-level invoice discounts, transport costs, an
early payment discount, and a ship-to party and invoicee that both differ from the buyer.

The basis is the official sample invoice `X19_01_Warenrechnung` from the
Factur-X / ZUGFeRD documentation (FeRD sample package, ZUGFeRD 2.5.0, EXTENDED profile).
In business terms, a delivery from a food wholesaler to a supermarket branch.

Every EXTENDED field – document name, test indicator, invoicee, packaging details, the
components of a sales unit, transport costs and the extended tax bases – is only written
if the document is in fact saved as `Profile.Extended` at the end. A smaller profile is
not an error; the fields simply drop out without a word.

Note texts, product names and discount reasons stay in German throughout: they are the
payload of the official reference document, and translating them would break the
comparison with it.

## 1. Invoice header

```csharp
// BT-1 invoice number, BT-2 invoice date, BT-5 invoice currency
FacturXInvoice invoice = FacturXInvoice.CreateInvoice(
    invoiceNo: "R87654321012345",
    invoiceDate: new DateTime(2025, 10, 1),
    currency: CurrencyCodes.EUR);

// BT-3: invoice type 380 = commercial invoice. This is the default, but it is spelled
// out here so that it is visible where the type is set.
invoice.Type = InvoiceType.Invoice;

// BT-X-2: free-form document name. Allowed in the EXTENDED profile only and written
// only there – in BASIC/COMFORT the writer silently drops the element.
invoice.Name = "WARENRECHNUNG";

// ram:TestIndicator marks the document as test data (EXTENDED only as well).
invoice.IsTest = true;
```

## 2. Free-text notes

Free text ends up in `ram:IncludedNote` (BG-1). `subjectCode` (BT-21, UNTDID 4451) says
what the note is about, `contentCode` (BT-X-5) is an additional standardised text block.
The two codes **cannot be combined freely**: `ST1`, `ST2` and `ST3` belong to `AAK`
(discount and bonus agreements), `EEV`, `WEB` and `VEV` to `AAJ` (retention of title).

```csharp
// REG = regulatory information, here the legal representation of the company.
invoice.AddNote("Geschäftsführer: Herr Geschäftsführer ,  MUSTERLIEFERANT GmbH  ",
                subjectCode: SubjectCodes.REG);

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

// ACB = additional information; used here for the format identification …
invoice.AddNote("ZUGFeRD vers 2.5.0 (Extended)", subjectCode: SubjectCodes.ACB);
// … and for the note that this is a sample document.
invoice.AddNote("Dies ist ein Waren-Rechnungs-Beispiel", subjectCode: SubjectCodes.ACB);

// AAK + ST3: standardised reference to existing discount or bonus agreements.
invoice.AddNote("Es bestehen Rabatt- oder Bonusvereinbarungen.",
                subjectCode: SubjectCodes.AAK,
                contentCode: ContentCodes.ST3);

// AAJ + EEV: standardised retention of title.
invoice.AddNote("Der Verkäufer bleibt Eigentümer der Waren bis zu vollständigen Erfüllung der Kaufpreisforderung.",
                subjectCode: SubjectCodes.AAJ,
                contentCode: ContentCodes.EEV);

// Notes without a subjectCode are allowed – plain free text.
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. Seller

`SetSeller` fills `ram:SellerTradeParty` (BG-4). The party carries two identifiers: the
internal supplier number (`id`, BT-29) and the GLN (`globalID` with 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: contact person. Name and department stay empty – the writer then
// does not emit ram:PersonName / ram:DepartmentName at all.
invoice.SetSellerContact(
    emailAddress: "max.mustermann@musterlieferant.de",
    phoneno: "+49 932 431 500");

// BT-34: electronic address of the seller, here as an EAN location code (0088).
invoice.SetSellerElectronicAddress("info@musterlieferant.de",
                                   ElectronicAddressSchemeIdentifiers.EanLocationCode);

// BT-31: seller VAT ID (scheme VA). FC would be the national tax number.
invoice.AddSellerTaxRegistration("DE123456788", TaxRegistrationSchemeID.VA);
```

## 4. Buyer

The buyer is the head office – the goods go to a branch, and the invoice goes to a third
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. Document references in the header

```csharp
// BT-13: buyer's purchase order number → ram:BuyerOrderReferencedDocument
invoice.SetBuyerOrderReferenceDocument("B123456789");

// BT-18 / BG-24: additional document reference. Type code 130 = invoice data sheet.
invoice.AddAdditionalReferencedDocument(
    id: "A456123",
    typeCode: AdditionalReferencedDocumentTypeCode.InvoiceDataSheet);

// BT-16: despatch advice number → ram:DeliveryNoteReferencedDocument (EXTENDED)
invoice.SetDeliveryNoteReferenceDocument("L87654321012345");

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

## 6. Ship-to party and invoicee

The ship-to party (BG-13) and the invoicee (BG-X-36) are not set through `Set…` methods
but as `Party` objects.

```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 – the department ("8211") in the store.
invoice.ShipToContact = new Contact() { OrgUnit = "8211" };

// BG-X-36 (EXTENDED only): same company as the buyer, different 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. Invoice lines

Six line items (BG-25). The recurring pattern:

| Parameter | BT code | Meaning |
|---|---|---|
| `lineID` | BT-126 | Line number; if omitted, FactoorSharp assigns it automatically |
| `id` | BT-157 | GTIN with schemeID `0160` (GS1), `GlobalIDSchemeIdentifiers.EAN` |
| `sellerAssignedID` | BT-155 | The supplier's item number |
| `buyerAssignedID` | BT-156 | The buyer's item number for the same goods |
| `grossUnitPrice` | BT-148 | Gross price, list price before item discounts |
| `netUnitPrice` | BT-146 | Net price after item discounts – basis of the line total |
| `lineTotalAmount` | BT-131 | `netUnitPrice` × `billedQuantity` |
| `PackageQuantity` / `PackageUnitCode` | BT-X-9 | Shipping unit, EXTENDED only. `XCT` = carton, `XBC` = crate, `XBO` = bottle, `XPX` = pallet |

### Line 1 – item attribute and shipping unit

```csharp
// 100 bottles of citric acid at EUR 1.00 = EUR 100.00, 19 % VAT
TradeLineItem line1 = invoice.AddTradeLineItem(
    lineID: "1",
    name: "Zitronensäure 100ml",
    netUnitPrice: 1.00m,
    grossUnitPrice: 1.00m,
    unitCode: QuantityCodes.H87,       // H87 = piece
    billedQuantity: 100m,
    lineTotalAmount: 100.00m,
    taxType: TaxTypes.VAT,
    categoryCode: TaxCategoryCodes.S,  // S = standard rate
    taxPercent: 19m,
    sellerAssignedID: "ZS997",
    id: new GlobalID(GlobalIDSchemeIdentifiers.EAN, "4123456000014"));

// BG-32 item attribute: a freely definable name/value pair (BT-160 / BT-161).
line1.ApplicableProductCharacteristics.Add(new ApplicableProductCharacteristic()
{
    Description = "Verpackungsart",
    Value       = "BO"
});

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

### Line 2 – item discounts inside the gross price

These discounts do not live at document level but as `ram:AppliedTradeAllowanceCharge`
inside the gross price (BT-147). The amounts are **per unit**:
1.50 − 0.03 − 0.02 = 1.45. FactoorSharp does not compute the net price – you set it, and
the discounts explain it.

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

### Line 3 – free goods

Ten pieces free of charge. The line stays in the document so that the delivered quantity
remains traceable, but contributes EUR 0.00 to the 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;
```

### Lines 4 and 5 – differing units and a buyer item number

Billed in crates, shipped in bottles. Line 5 is the matching empties deposit and
additionally carries the buyer's item number (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 = crate
    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 = bottle

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

### Line 6 – mixed pallet with its components

The pallet is billed as a single line, its content broken down through
`IncludedReferencedProducts` (BG-X-1). Purely informational: prices and taxes remain
attached to the parent line.

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

// The convenience method AddIncludedReferencedProduct() has no GlobalID parameter,
// which is why the objects are created directly here.
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 as in line 2: discount per 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 = pallet
```

## 8. Document-level invoice discounts

A document-level discount always applies to **one** tax rate only. Since this document
mixes 19 % and 7 %, each discount has to be added **twice** – two discounts become four
calls. The order in the XML follows the order of the calls in your code.

The basis amounts are not simply the line totals: at 19 % lines 1, 4 and 5 would add up
to EUR 326.50, but only EUR 280.00 was agreed – the empties deposit and part of the goods
are exempt from the discount. At 7 % the full sum of lines 2, 3 and 6 applies: EUR 130.70.

```csharp
// Invoice discount 1 – percentage based, 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);

// Invoice discount 2 – a fixed amount, therefore without 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. Transport costs

Logistics costs have a dedicated element in CII and are not the same as an ordinary
charge via `AddTradeCharge`. It is not permitted in XRechnung, but it is in ZUGFeRD
EXTENDED. It feeds into `ChargeTotalAmount` (BT-108).

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

## 10. VAT breakdown

One `ram:ApplicableTradeTax` group (BG-23) is mandatory per combination of tax rate and
tax category. FactoorSharp does not add the amounts up for you; the values are set.

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

The transport charge enters the 19 % row with a positive sign, the discounts with a
negative one.

```csharp
// lineTotalBasisAmount and allowanceChargeBasisAmount are EXTENDED fields;
// in BASIC/COMFORT they are dropped without replacement.
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. Payment terms with an early payment discount

`paymentTermsType = Skonto` creates the `ram:ApplicableTradePaymentDiscountTerms` block
with `BasisPeriodMeasure` (days) and `CalculationPercent`. Without that type only the
free text would remain – the discount would not be machine-readable.

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

## 12. Document totals

| BT code | Parameter | Calculation |
|---|---|---|
| BT-106 | `lineTotalAmount` | 100.00 + 72.50 + 0.00 + 180.00 + 46.50 + 58.20 = **457.20** |
| BT-108 | `chargeTotalAmount` | 3.00 (transport costs) |
| 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 – no prepayment |
| 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. Saving

Only at save time does it become clear which of the fields set above actually end up in
the XML. Version, profile and syntax belong together.

```csharp
// Version25 + Profile.Extended + CII produces the profile identifier
// "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. Cross-check

The generated file has to have the same tree structure as `X19_01_Warenrechnung.xml` from
the FeRD sample package. Numbers are compared numerically, so that `1.00` and `1.0000`
count as equal – FactoorSharp writes prices adaptively with two decimal places.

```csharp
// A human-readable rendition – not a legal document and not a hybrid ZUGFeRD PDF.
// It is rendered from the object, not from the XML file.
InvoiceVisualizer.RenderPdf(invoice, "X19_01_Warenrechnung.pdf");
InvoiceVisualizer.RenderHtml(invoice, "X19_01_Warenrechnung.html");
```

The validator, the visualizer and the detailed documentation for them live in the
customer area at <https://www.factoorsharp.de/support/>.

## Related pages

- [Correction invoice](https://www.factoorsharp.de/en/Service/CorrectionInvoice.md): type 384 with negative amounts, set apart from the credit note.
- [Foreign currency invoice](https://www.factoorsharp.de/en/Service/ForeignCurrencyInvoice.md): GBP with the tax amount also stated in EUR, including the exchange rate.
- [Getting started](https://www.factoorsharp.de/en/Home/GettingStarted.md): installation, license key and your first invoice.
- [Factur-X reference](https://www.factoorsharp.de/en/Service/Documentation): XML elements and BT/BG codes to look up.
