csv2zugferd
# csv2zugferd Dokumentation
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.
Demo-Paket
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 |
| Windows-64-bit-Exe, self-contained (kein .NET nötig) |
| YAML-Konfiguration für die Demo-CSV-Struktur |
| Beispiel-Rechnung 1 – Softwarelizenzen mit Rabatt |
| PDF-Vorlage zu Rechnung 1 |
| Beispiel-Rechnung 2 – Lizenzen, Service und Schulung |
| PDF-Vorlage zu Rechnung 2 |
| 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/
Related MCP server: bort
Eingaben und Ausgaben
Typ | Format | Beschreibung |
CSV-Datei |
| Rechnungsdaten mit Kopf-, Käufer-, Verkäufer-, Positions- und Summenfeldern |
PDF-Vorlage |
| Bestehende Rechnungs-PDF, in die das XML eingebettet wird |
Konfiguration |
| YAML-Datei mit CSV-Einstellungen und Mapping-Regeln |
XML-Ausgabe |
| ZUGFeRD XML nach konfiguriertem Profil |
PDF-Ausgabe |
| ZUGFeRD PDF mit eingebettetem XML |
CLI-Nutzung
dotnet run --project .\csv2zugferd\csv2zugferd.csproj -- --csv .\test\0002486.csv --pdf .\test\2483.pdf --config .\test\config_datasrc.yml --output .\test\outputOption | Kurz | Pflicht | Beschreibung |
|
| Ja | Pfad zur CSV-Datei |
|
| Ja | Pfad zur PDF-Vorlage |
|
| Ja | Pfad zur YAML-Konfiguration |
|
| Nein | Ausgabeverzeichnis, Standard ist das aktuelle Verzeichnis |
| Nein | Nur XML erzeugen, kein PDF | |
|
| Nein | Debug-Ausgabe aktivieren |
Die Ausgabedateien werden anhand der Rechnungsnummer benannt, zum Beispiel 0002483.xml und 0002483.pdf.
MCP-Server (Einrichtungsassistent)
Seit Version 1.3.0 stellt csv2zugferd mcp das Tool als MCP-Server (Model Context Protocol) über stdio bereit. Zweck ist die Einrichtung: Ein KI-Assistent in Claude Desktop, Claude Code oder VS Code liest den Kopf einer CSV-Datei, entwirft die YAML-Konfiguration, prüft sie im Probelauf gegen den echten Mapping-Code und speichert sie als Datei. Der Produktivlauf bleibt bei der CLI: lokal, deterministisch, ohne Modell und ohne Cloud-Upload durch csv2zugferd. Die gespeicherte Konfiguration gilt für alle weiteren Rechnungen derselben CSV-Struktur.
Vorbereitung: Für die Einrichtung mit einem KI-Assistenten anonymisierte Beispieldaten verwenden. Die Werkzeuge liefern Zellwerte nur auf Anforderung (sampleRows, includeLineItems, includeParties).
Start und Client-Konfiguration
Der Server wird vom MCP-Client gestartet und liest JSON-RPC von stdin; stdout trägt ausschließlich JSON-RPC, Warnungen gehen zusätzlich nach stderr. Die Logdatei zugferd-*.log liegt nicht im Arbeitsverzeichnis (das bestimmt der Client), sondern unter csv2zugferd\logs im lokalen Anwendungsdatenordner (Windows %LOCALAPPDATA%\csv2zugferd\logs, Linux und macOS ~/.local/share/csv2zugferd/logs); die Umgebungsvariable CSV2ZUGFERD_LOG_DIR setzt ein anderes Verzeichnis. --verbose schaltet die Logdatei auf Debug.
Zwei Wege zum Start: die Windows-Exe aus dem GitHub-Release (csv2zugferd.exe mcp, kein .NET nötig) oder das .NET-Tool-Paket csv2zugferd von NuGet, das MCP-Clients mit dnx csv2zugferd@1.3.1 mcp starten (benötigt das .NET-10-SDK; läuft unter Windows, Linux und macOS). Mit dem Release 1.3.1 erscheint der Server in der Official MCP Registry unter dem Namen de.zugferd-tools/csv2zugferd.
Claude Desktop (claude_desktop_config.json):
{
"mcpServers": {
"csv2zugferd": {
"command": "C:\\Tools\\csv2zugferd.exe",
"args": ["mcp"]
}
}
}Claude Desktop mit dem NuGet-Paket statt der Exe:
{
"mcpServers": {
"csv2zugferd": {
"command": "dnx",
"args": ["csv2zugferd@1.3.1", "mcp"]
}
}
}Claude Code:
claude mcp add csv2zugferd -- C:\Tools\csv2zugferd.exe mcp
# oder mit dem NuGet-Paket:
claude mcp add csv2zugferd -- dnx csv2zugferd@1.3.1 mcpVS Code (.vscode/mcp.json):
{
"servers": {
"csv2zugferd": {
"type": "stdio",
"command": "C:\\Tools\\csv2zugferd.exe",
"args": ["mcp"]
}
}
}Docker
Für Linux-Umgebungen und Kataloge, die den Server selbst bauen, liegt ein Dockerfile im Repository. Das Image ist framework-abhängig auf mcr.microsoft.com/dotnet/runtime:10.0-noble-chiseled-extra (rund 207 MB, ohne Shell) und läuft ohne Root-Rechte:
docker build -t csv2zugferd .
docker run -i --rm -v "/pfad/zu/rechnungen:/data" csv2zugferdDer Container startet csv2zugferd mcp und spricht JSON-RPC über stdin und stdout. Die Werkzeuge sehen nur den eingehängten Ordner /data (Arbeitsverzeichnis im Container); Pfade in Werkzeugaufrufen lauten entsprechend /data/<datei>. Die Logdatei bleibt im Container (/tmp/csv2zugferd/logs). Dateien, die der Container unter /data anlegt (csv2zugferd_config_file_create, CLI-Ausgaben), gehören auf Linux-Hosts dem Benutzer mit der ID 1654 (app im Image). Mit Argumenten hinter dem Imagenamen läuft statt des Servers die CLI, zum Beispiel docker run --rm -v "/pfad:/data" csv2zugferd --csv /data/rechnung.csv --pdf /data/vorlage.pdf --config /data/config.yml --output /data/out.
Claude Desktop mit dem Container:
{
"mcpServers": {
"csv2zugferd": {
"command": "docker",
"args": ["run", "-i", "--rm", "-v", "C:\\Rechnungen:/data", "csv2zugferd"]
}
}
}Werkzeuge
Werkzeug | Zweck | Wirkung |
| Spaltennamen und Zeilenzahl einer CSV lesen, optional bis zu 3 Beispielzeilen ( | lesend |
| Vollständige Konfigurationsreferenz: JSON-Schema aller YAML-Schlüssel mit Beschreibung, Typ, Default und zulässigen Werten plus Semantik je Schlüssel (Wirkung, Modusabhängigkeit, Auflösungsreihenfolge, Rückfall). | lesend |
| Konfiguration (Pfad oder YAML-Text) strukturell prüfen: effektive Einstellungen, gemappte Felder mit Quellen, unbekannte Schlüssel, unzulässige Werte mit Rückfall, ignorierte oder fehlende Abschnitte; mit | lesend |
| Probelauf mit dem echten Mapping ohne Ausgabedatei: Rechnungsnummer, Typ, Summen, Steueraufschlüsselung, Warnungen mit Code und Pfad, XML-Größe; optional Positionen, Parteien und PDF-Probelauf in eine temporäre Datei ( | lesend |
| Geprüfte YAML als Datei speichern (UTF-8 ohne BOM); vorhandene Datei nur mit | schreibend |
Die Werkzeuge nutzen denselben Code wie die CLI. csv2zugferd_config_schema_get ersetzt das Lesen dieser README: Alle Konfigurationsmöglichkeiten sind allein über die Werkzeuge abrufbar und prüfbar.
Ressourcen (resources/list): csv2zugferd://docs/config-reference (diese README), csv2zugferd://examples/config-rows und csv2zugferd://examples/config-columns-suffix (Beispielkonfigurationen aus testdata/).
Ablauf der Inbetriebnahme
CSV-Struktur erfassen:
csv2zugferd_csv_header_getmit dem Pfad der (anonymisierten) CSV, bei BedarfsampleRows: 2.Mapping erarbeiten:
csv2zugferd_config_schema_getliefert Schlüssel, Werte und Wirkung; der Assistent entwirft die YAML.Prüfen und korrigieren:
csv2zugferd_config_getmitconfigYamlundcsvPathmeldet Tippfehler (unknownKeys), unzulässige Werte (invalidValues) und fehlende Spalten (columnsMissing);csv2zugferd_invoice_getzeigt Summen, Steuergruppen und Warnungen des echten Mappings. Wiederholen, bis das Ergebnis stimmt.Speichern:
csv2zugferd_config_file_createschreibt die YAML und gibt den CLI-Befehl zurück.Produktivlauf: Den Befehl mit PDF-Vorlage und Ausgabeverzeichnis ausführen, z. B.
csv2zugferd --csv rechnung.csv --pdf vorlage.pdf --config config.yml --output out. Dafür ist weder ein Modell noch ein laufender MCP-Server nötig.
Antworten, Fehler und Grenzen
Erfolgreiche Antworten kommen als
structuredContentund als inhaltsgleicher JSON-Text; jedes Werkzeug veröffentlicht einoutputSchema, gegen das die Antwort vor dem Senden geprüft wird.Warnungen sind Objekte
{code, path, message}mit stabilem englischem Code (z. B.invalid_date,config_field_ignored,invalid_iban_checksum), vollständigem Konfigurationspfad und deutschem Text.Fachliche Ablehnungen sind Tool-Ergebnisse mit
isError: true; die erste Textzeile ist der Code (z. B.csv_file_not_found,config_invalid_yaml,file_exists),_meta.errorCodeträgt ihn zusätzlich. Unbekannte Werkzeuge und Schemaverletzungen sind JSON-RPC-Fehler-32602mit Feldpfad.Lesende Werkzeuge liefern keine Antwort über 900 000 Byte; darüber kommt
response_too_largemit den Eingrenzungsargumenten des Werkzeugs (sampleRows: 0,includeLineItems: false,section).
YAML-Grundstruktur
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: {}
paymentMeans: {}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.
Feste Verkäuferdaten
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"
id:
value: "1234567890"
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"id ist optional und trägt die Verkäuferkennung (BT-29), zum Beispiel die Lieferantennummer, unter der der Käufer den Verkäufer führt. Sie erscheint im XML als ram:ID der SellerTradeParty. Ohne id wird kein Element geschrieben.
Unterstützte Steuer-Schemata:
Scheme | Bedeutung |
| Steuernummer |
| Umsatzsteuer-ID |
taxRegistrations unterstützt sowohl feste Werte per value als auch CSV-Werte per column.
Rechnungskopf
invoice:
invoiceNumber:
column: "INVOICE_NO"
invoiceDate:
column: "INVOICE_DUEDATE"
typeCode: # Rechnungstyp (BT-3), ohne Angabe 380
value: "380"
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.
Rechnungstyp, Storno und Gutschrift
typeCode setzt den Rechnungstyp (BT-3, rsm:ExchangedDocument/ram:TypeCode). Ohne Angabe wird 380 geschrieben. Zulässig ist eine Zahl nach UNTDID 1001 oder der Name aus der ZUGFeRD-Bibliothek (Invoice, CreditNote, Correction, SelfBilledInvoice, …); ein unbekannter Wert führt zu einem Fehler.
Code | Bedeutung | Einsatz |
| Rechnung | Standard |
| Gutschrift (kaufmännisch), Stornorechnung | vollständiges Storno, Rückerstattung |
| Rechnungskorrektur | Teilkorrektur einer bestehenden Rechnung (Preis, Menge, Steuersatz) |
| Selbstfakturierung (Gutschriftsverfahren, § 14 Abs. 2 UStG) | der Leistungsempfänger rechnet ab; Aussteller und Verkäufer sind vertauscht |
| Teil-, Vorauszahlungs- und Bau-Abschlagsrechnungen | werden durchgereicht |
Ein Storno ist fachlich keine Gutschrift im Sinne des Gutschriftsverfahrens. Wer „Gutschrift“ meint und selbst der Leistende ist, braucht 381 oder 384, nicht 389.
Rechnung und Storno lassen sich aus einer CSV-Spalte steuern:
invoice:
typeCode:
default: "380"
rules:
- when:
column: "Belegart"
regex: "(?i)^(gutschrift|storno)"
value: "381"
- when:
column: "Belegart"
regex: "(?i)^korrektur"
value: "384"
precedingInvoices: # Vorgängerrechnung (BT-25/BT-26), eine oder mehrere
- number:
column: "Ursprungsrechnung"
date:
column: "Ursprungsdatum"Liefert die CSV keine Belegart, kann die Regel auch auf das Vorzeichen einer Betragsspalte prüfen, zum Beispiel regex: "^-" auf INVOICE_TOTAL.
precedingInvoices erzeugt je Eintrag ein ram:InvoiceReferencedDocument mit Nummer und Datum der Ursprungsrechnung. Einträge ohne Nummer werden übersprungen; das Datum ist optional. Mehrere Einträge sind für Sammelgutschriften oder Schlussrechnungen nach mehreren Abschlägen gedacht. Das Element wird ab Profil BasicWL geschrieben, nicht in Minimum.
Vorzeichen der Beträge:
Typ | Beträge | Hinweis |
| positiv | Das Minus steckt im Code. Negative Beträge bei |
| negativ | „Negativrechnung“, laut Factur-X zulässig; manche Empfänger buchen sie als normale Rechnung. |
| positiv oder negativ je Position | Teilkorrektur, Mengen dürfen negativ sein. |
Das Tool schreibt die Beträge so, wie die CSV sie liefert. Empfohlen ist 381 mit positiven Beträgen und precedingInvoices.
Weitere Hinweise:
namesollte bei Storno und Korrektur aufSTORNORECHNUNGbzw.RECHNUNGSKORREKTURstattGUTSCHRIFTstehen. Das Element wird nur im ProfilExtendedgeschrieben.Ein Storno bekommt eine eigene Rechnungsnummer; das Original wird nie überschrieben (GoBD).
Der sichtbare PDF-Teil muss zum XML-Typ passen (kein PDF „Gutschrift“ zu einem XML mit
380).Chorus Pro (Frankreich) akzeptiert nur
380.
Unvollständige Angaben erzeugen eine Warnung im Log, keinen Abbruch:
381oder384ohneprecedingInvoices(beiXRechnungund384: BR-DE-26)Profil
XRechnungmit einem Code außerhalb von326,380,381,384,389,875,876,877(BR-DE-17)Profil
MinimummitprecedingInvoices
Eine lauffähige Beispielkonfiguration ist testdata/config_creditnote.yml mit testdata/test_creditnote.csv.
Käuferdaten
buyer:
name:
column: "ACCOUNT_NAME"
id:
column: "ACCOUNT_ID"
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"id ist optional und trägt die Käuferkennung (BT-46), in der Regel die Kundennummer des Käufers beim Verkäufer. Sie erscheint im XML als ram:ID der BuyerTradeParty. Ohne id wird kein Element geschrieben. Wie alle Felder unterstützt id auch value, default und rules.
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:
Scheme | Bedeutung |
| Deutsche Umsatzsteuer-ID (EAS 9930) |
| Luxemburgische Umsatzsteuer-ID (EAS 9938) |
| E-Mail-Adresse (EAS |
Andere Werte fallen auf GermanyVatNumber zurück. Die vollständige Liste der Kennungen steht in ZUGFeRD-csharp; weitere Werte müssen erst im Mapper ergänzt werden.
Lieferdaten
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 (BT-72).
Sind weder Lieferschein noch Lieferdatum konfiguriert, wird das Rechnungsdatum als Leistungsdatum geschrieben und eine Warnung protokolliert. Der Block ApplicableHeaderTradeDelivery ist im ZUGFeRD-Schema Pflicht; ohne Inhalt würde die XML-Datei die Validierung nicht bestehen.
Bestellreferenz
orderReference:
orderNumber:
column: "ORDER_NO"
orderDate:
column: "ORDER_DATE"orderNumber entspricht BT-13 (Bestellnummer des Käufers). orderDate ist optional.
Positionsdaten
Das Tool unterstützt zwei CSV-Strukturen. Alle Positionsfelder sind FieldMapping-Einträge; neben den Beispielen unten gibt es sellerAssignedId (Artikelnummer des Verkäufers, BT-155) und buyerAssignedId (Artikelnummer des Käufers, BT-156).
Modus rows
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 Positionsrabatt (BG-27), false einen Positionszuschlag (BG-28), jeweils als SpecifiedTradeAllowanceCharge in der Positionsabrechnung mit Basisbetrag, Prozentsatz und Grund. Der Zeilennettobetrag (BT-131) ist bereits um den Rabatt gemindert bzw. um den Zuschlag erhöht; die Summen auf Dokumentebene folgen daraus. chargePercentage akzeptiert Werte wie 10, 10.00 oder 10%. Im columns-Modus wird Rabatt stattdessen über discountPercent in fields konfiguriert.
Modus columns
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.
Bei style: "prefix" steht der Index vor dem Feldnamen; prefix ist der Text vor dem Index, separator trennt Index und Feldname: prefix: "Pos", separator: "_" und startIndex: 1 ergeben Pos1_Artikel, Pos2_Artikel usw. (Beispiel: testdata/config_columns_prefix.yml). Bei style: "suffix" wird prefix nicht verwendet; zeroPadding: 3 erzeugt Artikel_001.
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.
Wirkung je Modus, Auflösung und Rückfälle
Jedes Mapping-Feld wird in der Reihenfolge value, column, rules, default aufgelöst; der erste nicht leere Treffer gewinnt. Einige Schlüssel wirken nur in einer Betriebsart:
Schlüssel | Wirkt nur bei |
|
|
|
|
|
|
|
|
Zwei Schlüssel werden aus Kompatibilitätsgründen akzeptiert, haben aber keine Wirkung: lineItems.columnsFields (Positionsfelder gehören nach lineItems.fields) und lineItems.fields.discountReason (ein Rabattgrund wird nur im rows-Modus über lineItems.allowanceCharge.reason geschrieben). Die CLI protokolliert ihre Verwendung als Warnung, csv2zugferd_config_get meldet sie als config_field_ignored.
Unbekannte Werte brechen die Verarbeitung nicht ab, sondern fallen mit Warnung zurück: zugferd.version → 2.3, zugferd.profile → Extended, zugferd.format → CII, lineItems.mode → rows, columnPattern.style → suffix, totals.mode → manual, Währung → EUR, Land → DE, Einheit → C62, Steuerart → VAT, Steuerkategorie → S, Steuernummernschema → VA, electronicAddress.scheme → GermanyVatNumber. Ein unbekannter invoice.typeCode oder paymentMeans.typeCode ist dagegen ein Fehler. Unbekannte YAML-Schlüssel (Tippfehler) werden beim Laden ignoriert; csv2zugferd_config_get listet sie unter unknownKeys. Der Spaltenabgleich von csv2zugferd_config_get ist strukturell: Er vergleicht konfigurierte Spaltennamen (im columns-Modus einschließlich der indexierten Namen) mit dem CSV-Kopf, liest aber keine Zellwerte; ob ein Wert leer ist oder eine Regel greift, zeigt erst csv2zugferd_invoice_get.
Summen
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"Im manuellen Modus werden nur die Rechnungssummen aus der CSV übernommen. Die Steueraufschlüsselung je Steuersatz (BG-23) wird weiterhin aus den Positionen abgeleitet: Die Steuerbasis je Satz ist die Summe der Positionsnettobeträge, der Steuerbetrag je Satz wird daraus berechnet. Eine Differenz zwischen der berechneten Steuersumme und taxTotalAmount wird der letzten Steuergruppe zugeschlagen, damit die Summe der Gruppen exakt taxTotalAmount ergibt (BR-CO-14). Bei nur einem Steuersatz entspricht der Steuerbetrag der Gruppe damit genau dem CSV-Wert. Rundungsabweichungen im Cent-Bereich zwischen Steuerbasis × Steuersatz und dem übernommenen Steuerbetrag sind zulässig (BR-CO-17 sieht eine Toleranz vor). Zeilennettobeträge (BT-131) werden in beiden Modi aus Einzelpreis × Menge berechnet und kaufmännisch auf zwei Nachkommastellen gerundet; alle Kopfsummen und Steuerbasen summieren diese gerundeten Werte (BR-CO-10, BR-S-08). Im manuellen Modus muss lineTotalAmount der Summe der gerundeten Zeilennettobeträge entsprechen.
Für Rabatt-Szenarien mit Positionsdaten ist auto bevorzugt, weil BT-131, Steuerbasis und Gesamtsummen dann konsistent aus den Positionen berechnet werden.
Zahlungsbedingungen
paymentTerms:
description:
value: "Zahlbar ohne Abzug bis zum Faelligkeitsdatum"
dueDate:
column: "INVOICE_DUEDATE"Eine Beschreibung ist optional, aber in echten Rechnungen meistens sinnvoll.
Zahlungsmittel und Bankverbindung
Der Abschnitt paymentMeans erzeugt die Zahlungsanweisungen nach EN 16931 (BG-16) inklusive Bankverbindung des Verkäufers und SEPA-Lastschriftdaten. Alle Felder unterstützen column, value, default und rules.
Überweisung
paymentMeans:
typeCode:
value: "58" # SEPA-Überweisung
information:
value: "SEPA-Überweisung"
paymentReference: # Verwendungszweck (BT-83)
column: "INVOICE_NO"
sellerAccounts: # ein oder mehrere Konten des Verkäufers
- iban:
value: "DE02 1203 0000 0000 2020 51"
bic:
value: "BYLADEM1001"
name:
value: "Muster GmbH"SEPA-Lastschrift
paymentMeans:
typeCode:
value: "59" # SEPA-Lastschrift
paymentReference:
column: "INVOICE_NO"
directDebit:
creditorId: # Gläubiger-ID (BT-90)
value: "DE98ZZZ09999999999"
mandateReference: # Mandatsreferenz (BT-89)
column: "MANDATE_ID"
buyerIban: # zu belastendes Konto des Käufers (BT-91)
column: "CUSTOMER_IBAN"
buyerBic:
column: "CUSTOMER_BIC"Überweisung und Lastschrift lassen sich pro Rechnung über eine Regel umschalten:
typeCode:
default: "58"
rules:
- when:
column: "PAYMENT_METHOD"
regex: "(?i)^lastschrift$"
value: "59"directDebit wird nur bei typeCode 59 oder 49 ausgewertet.
Feld | EN 16931 | Hinweis |
| BT-81 | Zahl nach UNTDID 4461 ( |
| BT-82 | Freitext zur Zahlungsart |
| BT-83 | Verwendungszweck |
| BT-84 | Leerzeichen werden entfernt |
| BT-85 | Kontoinhaber |
| BT-86 | |
| BT-89 | |
| BT-90 | |
| BT-91 | wird im Log maskiert |
Code | Bedeutung |
| Überweisung (nicht SEPA) |
| SEPA-Überweisung |
| SEPA-Lastschrift |
| Lastschrift (nicht SEPA) |
Unvollständige Angaben brechen die Verarbeitung nicht ab, sondern erzeugen eine Warnung im Log:
Überweisung (
30,58) ohne IBAN des Verkäufers (BR-61)SEPA-Lastschrift ohne Gläubiger-ID, Mandatsreferenz oder IBAN des Käufers
Profil
XRechnungohnepaymentMeans(BR-DE-1)IBAN mit ungültiger Prüfsumme
Ein unbekannter typeCode führt dagegen zu einem Fehler.
Konfiguration für test\0002486.csv
Die Datei test\0002486.csv verwendet:
Eigenschaft | Wert |
Trennzeichen | Komma |
Dezimaltrennzeichen | Punkt |
Datumsformat |
|
Positionsmodus |
|
Positionsmuster | Suffix, Startindex |
Steuer | globale Spalte |
Produktcode | positionsbezogen, zum Beispiel |
Rabatt | positionsbezogen, zum Beispiel |
Summen | automatisch aus Positionen berechnet |
Die passende Konfiguration liegt unter test\config_datasrc.yml.
Verarbeitung
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.
Externe Validierung mit Mustang-CLI
Die Unit-Tests prüfen Mapping und Summen. Ob das erzeugte XML den ZUGFeRD-Regeln entspricht (Schema, EN16931- und XRechnung-Schematron), prüft zusätzlich das Mustang-CLI. Diese Tests laufen nicht bei dotnet test, sondern nur auf ausdrückliche Anforderung, weil sie Java 17 und das Mustang-Jar benötigen.
# einmalig: Jar nach tools/Mustang-CLI.jar laden (nicht im Git)
.\tools\Get-MustangCli.ps1
# alle Testdaten-Konfigurationen exportieren und validieren
dotnet test --filter Category=ExternalValidationAlternativ kann der Jar-Pfad über die Umgebungsvariable MUSTANG_CLI_JAR gesetzt werden. Fehlt das Jar, melden die Tests Inconclusive statt eines Fehlers. Für eine manuelle Gegenprobe einzelner Dateien eignet sich der Browser-Upload auf e-rechnungs-checker.de.
Hinweise
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.
Haftungsausschluss
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.
Available Tools
5 toolscsv2zugferd_config_file_createSave configuration fileADestructiveIdempotent
Validates configYaml (it must deserialize as a csv2zugferd configuration) and writes it to configPath as UTF-8 without BOM. An existing file is replaced only with overwrite: true; otherwise the call is rejected with file_exists and nothing is written. Returns the full path, the size and the csv2zugferd CLI command that uses the saved file for production runs. Does not run the mapping (use csv2zugferd_invoice_get first) and does not create XML or PDF files.
Inputs: configPath (required), configYaml (required), csvPath, overwrite. Returns: Saved configuration file.
csv2zugferd_config_getCheck configurationARead-onlyIdempotent
Loads a YAML configuration from configPath or configYaml and reports its structure: effective settings and modes, every mapped field with its sources, unknown keys (typos), values outside the allowed sets with the fallback the CLI would apply, and ignored or missing sections. With csvPath the referenced columns are compared with the CSV header (missing and unused columns). Does not read cell values and does not calculate totals; use csv2zugferd_invoice_get for a trial run. Read-only, writes no files.
Inputs: csvPath, configPath, configYaml. Returns: Structural check of a configuration.
csv2zugferd_config_schema_getGet configuration referenceARead-onlyIdempotent
Returns the reference of the csv2zugferd YAML configuration: a JSON Schema of every key with description, type, default and allowed values, plus semantics per key (effect, mode dependency, resolution order value > column > rules > default, fallback for unknown values). Use it before drafting a configuration; no README is needed. The complete reference is about 100 KB; pass section (for example mapping.lineItems) to get one subtree with its definitions. Does not check a concrete configuration (use csv2zugferd_config_get). Read-only.
Inputs: section. Returns: Complete reference of the YAML configuration, or the requested subtree.
csv2zugferd_csv_header_getRead CSV headerARead-onlyIdempotent
Reads the header row of a CSV file and returns the column names in file order plus the number of data rows. Optionally returns up to 3 sample rows (sampleRows, default 0) so the mapping can be drafted from real values. Does not validate a configuration (use csv2zugferd_config_get) and does not map an invoice (use csv2zugferd_invoice_get). Read-only, writes no files.
Inputs: csvPath (required), encoding, separator, sampleRows. Returns: Header of the CSV file.
csv2zugferd_invoice_getTrial run invoice mappingARead-onlyIdempotent
Runs the real csv2zugferd mapping for one CSV file with the given configuration (configPath or configYaml) without writing any output file: reads the CSV, maps header, parties, line items and totals, builds the ZUGFeRD XML in memory and returns invoice number, type, totals, tax breakdown, warnings with codes and paths, and the XML size. includeLineItems and includeParties (default false) add details; pdfTemplatePath also embeds the XML into a temporary PDF copy to check format and template. Does not create the XML or PDF for production; that is done by the csv2zugferd CLI (csv2zugferd_config_file_create returns the command). Read-only for the user's files; a temporary PDF is created and deleted only with pdfTemplatePath.
Inputs: csvPath (required), configPath, configYaml, includeParties, +2 more. Returns: Result of the trial run (same code path as the CLI up to writing the XML).
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections.
TDQS
Scored across 5 tools
Each tool targets a distinct step of the workflow: writing a config file, inspecting a config, fetching the schema reference, reading a CSV header, and doing an in-memory trial run. The descriptions explicitly disambiguate with cross-references ('use csv2zugferd_invoice_get first', 'use csv2zugferd_config_get'). No two tools overlap in purpose.
All names use snake_case with a shared csv2zugferd_ prefix and a resource_then_action ordering (config_get, csv_header_get, invoice_get). The one deviation is csv2zugferd_config_file_create, which nests an extra noun (config_file) while config_get does not, making resource granularity slightly uneven.
Five tools map cleanly onto five distinct workflow stages (schema reference → CSV inspection → config authoring → config validation → trial run), with no filler. The surface is well scoped for a config/validation assistant around a CLI.
The surface covers the full prepare-validate-preview lifecycle including schema lookup, header inspection, config write, config lint and a dry-run mapping, which is coherent for the stated read-only purpose. The absence of any tool to actually emit production XML/PDF (delegated to the CLI) and no way to remove or list saved config files are minor gaps agents can work around.
Maintenance
Related MCP Connectors
Create ZUGFeRD e-invoices from JSON or PDF, verify Factur-X, check VAT IDs and Peppol.
XRechnung and ZUGFeRD e-invoicing (EN 16931): create, validate, check Leitweg-IDs, German VAT.
Validiert E-Rechnungen (ZUGFeRD/Factur-X, XRechnung) gegen EN 16931 mit Korrekturvorschlägen.
EN 16931: validate invoice data, UBL/CII or PDF; emit XRechnung/Peppol XML, never a PDF.
Related MCP Servers
- AlicenseNot gradedqualityBmaintenanceEnables AI assistants to locally parse, validate, audit, explain, generate, and convert XRechnung and ZUGFeRD/Factur-X e-invoices using official rule sets, fully offline with no API keys required.Apache 2.0
- FlicenseBqualityBmaintenanceEnables an AI assistant to read and manage a local project-management database over stdio: listing and creating projects, tasks, expenses, chats and people, and pulling money, margin, deadline and overdue summaries. It runs on the same service layer and SQLite file as the bundled web UI, so figures shown to the assistant and to the human always agree.14-
- AlicenseAqualityBmaintenanceRead and check European e-invoices (Factur-X, ZUGFeRD, XRechnung, UBL, CII) from an AI assistant: contents in plain text plus an EN 16931 consistency check.2MIT

rechnungsapi-mcpofficial
AlicenseAqualityBmaintenanceEnables AI assistants and agents to create, validate, read and convert German and EU e-invoices in ZUGFeRD, XRechnung and Factur-X formats, including extracting structured data from scanned or PDF invoices and embedding XML into PDF/A-3 files. It can run as a hosted remote server or locally, with each request authenticated by the caller's own API token.1020 npmMIT