Website: zugferd-tools.de
csv2zugferd ist ein .NET-CLI-Tool, das Rechnungsdaten aus CSV-Dateien liest und daraus ZUGFeRD-konforme XML-Dateien oder PDF-Dateien mit eingebettetem ZUGFeRD-XML erzeugt. Die Struktur der CSV-Datei und das Mapping auf ZUGFeRD-Felder werden über eine YAML-Konfiguration gesteuert.
Das Demo-Paket ist der schnellste Einstieg. Es enthält eine fertige Windows-Exe, zwei Beispiel-Rechnungen und eine vollständige YAML-Konfiguration.
Download: GitHub Releases → Demo-ZIP
| Datei | Beschreibung |
|---|---|
csv2zugferd-win-x64.exe |
Windows-64-bit-Exe, self-contained (kein .NET nötig) |
config_demo.yml |
YAML-Konfiguration für die Demo-CSV-Struktur |
demo-2026-0001.csv |
Beispiel-Rechnung 1 – Softwarelizenzen mit Rabatt |
demo-2026-0001.pdf |
PDF-Vorlage zu Rechnung 1 |
demo-2026-0002.csv |
Beispiel-Rechnung 2 – Lizenzen, Service und Schulung |
demo-2026-0002.pdf |
PDF-Vorlage zu Rechnung 2 |
create-zugferd-demo.cmd |
Startskript – per Doppelklick oder CMD ausführen |
Schnellstart:
:: 1. ZIP entpacken, dann:
create-zugferd-demo.cmd
:: Ergebnis: output\DEMO-2026-0001.pdf und output\DEMO-2026-0002.pdfDie erzeugten Dateien sind valide ZUGFeRD-2.3-Rechnungen (Profil Extended) und können mit dem Mustang-Validator geprüft werden.
Detaillierte Erklärung der config_demo.yml: zugferd-tools.de/docs/schnellstart/
| Typ | Format | Beschreibung |
|---|---|---|
| CSV-Datei | .csv |
Rechnungsdaten mit Kopf-, Käufer-, Verkäufer-, Positions- und Summenfeldern |
| PDF-Vorlage | .pdf |
Bestehende Rechnungs-PDF, in die das XML eingebettet wird |
| Konfiguration | .yml |
YAML-Datei mit CSV-Einstellungen und Mapping-Regeln |
| XML-Ausgabe | .xml |
ZUGFeRD XML nach konfiguriertem Profil |
| PDF-Ausgabe | .pdf |
ZUGFeRD PDF mit eingebettetem XML |
dotnet run --project .\csv2zugferd\csv2zugferd.csproj -- --csv .\test\0002486.csv --pdf .\test\2483.pdf --config .\test\config_datasrc.yml --output .\test\output| Option | Kurz | Pflicht | Beschreibung |
|---|---|---|---|
--csv |
-c |
Ja | Pfad zur CSV-Datei |
--pdf |
-p |
Ja | Pfad zur PDF-Vorlage |
--config |
-g |
Ja | Pfad zur YAML-Konfiguration |
--output |
-o |
Nein | Ausgabeverzeichnis, Standard ist das aktuelle Verzeichnis |
--xml-only |
Nein | Nur XML erzeugen, kein PDF | |
--verbose |
-v |
Nein | Debug-Ausgabe aktivieren |
Die Ausgabedateien werden anhand der Rechnungsnummer benannt, zum Beispiel 0002483.xml und 0002483.pdf.
csv:
separator: ","
encoding: "UTF-8"
hasHeader: true
decimalSeparator: "."
dateFormat: "dd.MM.yyyy"
zugferd:
version: "2.3"
profile: "Extended"
format: "CII"
mapping:
invoice: {}
buyer: {}
seller: {}
delivery: {}
orderReference: {}
lineItems: {}
totals: {}
paymentTerms: {}Jedes Mapping-Feld kann entweder einen CSV-Spaltennamen oder einen festen Wert verwenden:
invoiceNumber:
column: "INVOICE_NO"
currency:
value: "EUR"value ist sinnvoll für Stammdaten oder Standards, die nicht in jeder CSV wiederholt werden sollen. column liest den Wert aus der CSV-Zeile.
Zusätzlich kann ein Mapping-Feld einen Fallback und Regex-Regeln definieren:
unitCode:
default: "C62"
rules:
- when:
column: "PRODUCT_CODE"
regex: "^002"
value: "ANN"
- when:
column: "PRODUCT_CODE"
regex: "^003"
value: "DAY"
- when:
column: "PRODUCT_NAME"
regex: "(?i)vor-ort-schulung|schulung"
value: "DAY"Die Reihenfolge ist: value, column, rules, default. Im columns-Modus werden Regelspalten positionsbezogen aufgelöst, also z. B. PRODUCT_CODE_0, PRODUCT_CODE_1 usw.
Verkäuferdaten können vollständig in der YAML-Konfiguration festgelegt werden. Das ist der empfohlene Weg, wenn alle Rechnungen vom gleichen Absender erzeugt werden.
seller:
name:
value: "Muster Lieferant GmbH"
postalCode:
value: "80333"
city:
value: "Muenchen"
street:
value: "Lieferantenstrasse 20"
country:
value: "DE"
taxRegistrations:
- value: "201/113/40209"
scheme: "FC"
- value: "DE123456789"
scheme: "VA"
electronicAddress:
value: "DE123456789"
scheme: "GermanyVatNumber"Unterstützte Steuer-Schemata:
| Scheme | Bedeutung |
|---|---|
FC |
Steuernummer |
VA |
Umsatzsteuer-ID |
taxRegistrations unterstützt sowohl feste Werte per value als auch CSV-Werte per column.
invoice:
invoiceNumber:
column: "INVOICE_NO"
invoiceDate:
column: "INVOICE_DUEDATE"
currency:
value: "EUR"
orderReferenceId:
column: "SALESORDER_ID"
referenceOrderNo:
column: "SALESORDER_ID"
name:
value: "WARENRECHNUNG"
note:
column: "INVOICE_NOTE"Pflichtnah sind Rechnungsnummer, Rechnungsdatum und Währung. Wenn die CSV kein echtes Rechnungsdatum enthält, muss eine passende Ersatzspalte bewusst gewählt werden.
buyer:
name:
column: "ACCOUNT_NAME"
postalCode:
column: "INVOICE_BILL_CODE"
city:
column: "INVOICE_BILL_CITY"
street:
column: "INVOICE_BILL_STREET"
country:
value: "DE"
contact:
column: "CONTACT_LASTNAME"
taxId:
column: "BUYER_VAT_ID"
taxScheme:
value: "VA"
electronicAddress:
column: "BUYER_VAT_ID"
scheme: "GermanyVatNumber"Für Länder sollte ein ISO-3166-Alpha-2-Code wie DE verwendet werden. Freitext wie Deutschland wird nicht zuverlässig als ZUGFeRD-Ländercode erkannt.
taxScheme akzeptiert FC (Steuernummer) oder VA (Umsatzsteuer-ID). Unterstützte Werte für scheme bei electronicAddress: GermanyVatNumber, LuxemburgVatNumber.
delivery:
deliveryNoteNumber:
column: "DELIVERY_NOTE_NO"
deliveryNoteDate:
column: "DELIVERY_NOTE_DATE"
actualDeliveryDate:
column: "DELIVERY_DATE"Alle drei Felder sind optional. deliveryNoteNumber und deliveryNoteDate werden gemeinsam als Lieferscheinreferenz geschrieben. actualDeliveryDate setzt das tatsächliche Lieferdatum.
orderReference:
orderNumber:
column: "ORDER_NO"
orderDate:
column: "ORDER_DATE"orderNumber entspricht BT-13 (Bestellnummer des Käufers). orderDate ist optional.
Das Tool unterstützt zwei CSV-Strukturen.
Im rows-Modus entspricht jede CSV-Zeile einer Rechnungsposition. Kopfdaten werden aus der ersten Zeile gelesen.
lineItems:
mode: "rows"
fields:
name:
column: "Artikel_Name"
description:
column: "Artikel_Beschreibung"
netUnitPrice:
column: "Einzelpreis_Netto"
billedQuantity:
column: "Menge"
taxPercent:
column: "MwSt_Prozent"
unitCode:
value: "C62"
taxType:
value: "VAT"
taxCategoryCode:
value: "S"
allowanceCharge:
enabled: true
isDiscount:
value: true
chargePercentage:
column: "Rabatt_Prozent"
reason:
column: "Rabatt_Grund"allowanceCharge ist optional. isDiscount: true erzeugt einen TradeAllowance (Rabatt), false einen TradeCharge (Zuschlag). chargePercentage akzeptiert Werte wie 10, 10.00 oder 10%. Im columns-Modus wird Rabatt stattdessen über discountPercent in fields konfiguriert.
Im columns-Modus steht eine ganze Rechnung in einer CSV-Zeile. Positionen werden über nummerierte Spalten erkannt.
Suffix-Beispiel:
PRODUCT_NAME_0,PRODUCT_QUANTITY_0,PRODUCT_PRICE_0,PRODUCT_NAME_1,PRODUCT_QUANTITY_1,PRODUCT_PRICE_1
Rillsoft Cloud Standard,1,1440.00,,,Passende YAML:
lineItems:
mode: "columns"
columnPattern:
style: "suffix"
separator: "_"
startIndex: 0
zeroPadding: 0
fields:
name:
column: "PRODUCT_NAME"
sellerAssignedId:
column: "PRODUCT_CODE"
netUnitPrice:
column: "PRODUCT_PRICE"
billedQuantity:
column: "PRODUCT_QUANTITY"
taxPercent:
column: "INVOICE_TAX"
discountPercent:
column: "PRODUCT_DISCOUNT"
fixedFields:
unitCode:
default: "C62"
rules:
- when:
column: "PRODUCT_CODE"
regex: "^002"
value: "ANN"
- when:
column: "PRODUCT_CODE"
regex: "^003"
value: "DAY"
- when:
column: "PRODUCT_NAME"
regex: "(?i)vor-ort-schulung|schulung"
value: "DAY"
taxType:
value: "VAT"
taxCategoryCode:
value: "S"Im columns-Modus kann ein Feld entweder als nummeriertes Positionsfeld gelesen werden, zum Beispiel PRODUCT_NAME_0, oder als globale Einzelspalte, zum Beispiel INVOICE_TAX.
PRODUCT_CODE ist hier eine positionsbezogene Verkäufer-Artikelnummer und wird deshalb als sellerAssignedId gemappt. Es sollte nicht als description verwendet werden.
discountPercent akzeptiert Werte wie 10, 10.00 oder 10%. Der Rabatt wird als rabattierter Netto-Einzelpreis geschrieben. Ein Listenpreis bleibt optional als GrossPriceProductTradePrice erhalten; es wird kein AppliedTradeAllowanceCharge für Positionsrabatte erzeugt.
Summen können automatisch berechnet oder manuell aus der CSV gelesen werden.
Automatisch:
totals:
mode: "auto"Manuell:
totals:
mode: "manual"
lineTotalAmount:
column: "INVOICE_SUBTOTAL"
allowanceTotalAmount:
value: "0"
taxBasisAmount:
column: "INVOICE_SUBTOTAL"
taxTotalAmount:
column: "INVOICE_TAXTOTAL"
grandTotalAmount:
column: "INVOICE_TOTAL"
totalPrepaidAmount:
value: "0"Für Rabatt-Szenarien mit Positionsdaten ist auto bevorzugt, weil BT-131, Steuerbasis und Gesamtsummen dann konsistent aus den Positionen berechnet werden.
paymentTerms:
description:
value: "Zahlbar ohne Abzug bis zum Faelligkeitsdatum"
dueDate:
column: "INVOICE_DUEDATE"Eine Beschreibung ist optional, aber in echten Rechnungen meistens sinnvoll.
Die Datei test\0002486.csv verwendet:
| Eigenschaft | Wert |
|---|---|
| Trennzeichen | Komma |
| Dezimaltrennzeichen | Punkt |
| Datumsformat | dd.MM.yyyy |
| Positionsmodus | columns |
| Positionsmuster | Suffix, Startindex 0, zum Beispiel PRODUCT_NAME_0 |
| Steuer | globale Spalte INVOICE_TAX |
| Produktcode | positionsbezogen, zum Beispiel PRODUCT_CODE_0 |
| Rabatt | positionsbezogen, zum Beispiel PRODUCT_DISCOUNT_0 |
| Summen | automatisch aus Positionen berechnet |
Die passende Konfiguration liegt unter test\config_datasrc.yml.
- YAML-Konfiguration laden.
- CSV mit Separator, Encoding, Header- und Zahlenformat lesen.
- Rechnungskopf, Käufer, Verkäufer, Lieferung und Bestellung aus erster Zeile oder festen Werten mappen.
- Positionen je nach Modus aus Zeilen oder nummerierten Spalten erzeugen.
- Summen automatisch berechnen oder aus CSV übernehmen.
InvoiceDescriptorals ZUGFeRD-XML speichern.- Optional das XML in eine PDF-Vorlage einbetten.
- Paketversionen werden zentral in
Directory.Packages.propsverwaltet. - Das Target Framework steht zentral in
Directory.Build.props. - Markdown-Dateien im Repository sollen UTF-8 mit BOM verwenden.
countrysollte als ISO-Code gesetzt werden, zum BeispielDE.- Verkäufer-Stammdaten gehören in die YAML-Konfiguration, wenn sie für alle Rechnungen identisch sind.
csv2zugferdwird ohne Gewähr bereitgestellt. Die erzeugten XML- und PDF-Dateien müssen vor produktiver Nutzung fachlich und technisch geprüft werden. Das Tool ersetzt keine steuerliche, rechtliche oder fachliche Beratung.
Für die Richtigkeit, Vollständigkeit und steuerliche Bewertung der Eingabe- und Ausgabedaten bleibt der Anwender verantwortlich. Die Bereitstellung erfolgt zu den Bedingungen der Lizenz Apache-2.0, die einen Gewährleistungs- und Haftungsausschluss enthält.