Zum Hauptinhalt springen
csv2zugferd

docs

YAML-Konfiguration – ZUGFeRD-Mapping mit csv2zugferd

YAML-Mapping für csv2zugferd: CSV-Spalten auf ZUGFeRD-Pflichtfelder abbilden, rows- und columns-Modus konfigurieren und Rechnungsfelder zuweisen.

Die YAML-Datei beschreibt, wie CSV-Spalten auf Rechnungsdaten und Positionen gemappt werden.

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: {}

FieldMapping
#

Fast alle Mapping-Felder verwenden dieselbe FieldMapping-Struktur:

feldName:
  value: "Fester Wert"
  column: "CSV_SPALTE"
  default: "Fallback"
  scheme: "VA"
  rules:
    - when:
        column: "PRODUCT_CODE"
        regex: "^003"
      value: "DAY"
SchlüsselBeschreibung
valueFester Wert. Hat Vorrang vor allen anderen Angaben.
columnCSV-Spalte, aus der der Wert gelesen wird.
defaultFallback, wenn value, column und rules keinen Wert liefern.
schemeOptionaler Schema-Qualifier, z. B. für elektronische Adresse oder Steuerregistrierung.
rulesGeordnete Regex-Regeln, mit denen Werte aus anderen CSV-Spalten abgeleitet werden.

Auflösungsreihenfolge:

  1. value, wenn gesetzt
  2. column, wenn gesetzt und der CSV-Wert nicht leer ist
  3. rules von oben nach unten
  4. default, wenn gesetzt
  5. leerer String bzw. fachlicher Code-Fallback im Mapper

Regex-Regeln
#

Regex-Regeln erlauben flexible Ableitungen ohne Codeänderung. Die erste passende Regel gewinnt. Ungültige Ausdrücke werden geloggt und ignoriert.

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"

Groß-/Kleinschreibung lässt sich mit Inline-Optionen wie (?i) steuern. Im columns-Modus werden Regelspalten positionsbezogen aufgelöst: bei column: "PRODUCT_CODE" und Suffix-Muster wird für Position 0 PRODUCT_CODE_0, für Position 1 PRODUCT_CODE_1 geprüft.

Beispiel für den Rechnungskopf
#

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"

Beispiel für Käuferdaten
#

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"

taxScheme akzeptiert FC (Steuernummer) oder VA (Umsatzsteuer-ID). Unterstützte Werte für scheme bei electronicAddress: GermanyVatNumber, LuxemburgVatNumber.

Beispiel für Verkäuferdaten
#

Verkäuferdaten können aus CSV-Spalten kommen oder als feste Stammdaten in der YAML-Datei stehen. Für wiederkehrende Verkäuferdaten sind feste Werte empfohlen.

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"
SchemeBedeutung
FCSteuernummer
VAUmsatzsteuer-ID

taxRegistrations unterstützt sowohl feste Werte per value als auch CSV-Werte per column.

Beispiel für 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.

Beispiel für Bestellreferenz
#

orderReference:
  orderNumber:
    column: "ORDER_NO"
  orderDate:
    column: "ORDER_DATE"

orderNumber entspricht BT-13 (Bestellnummer des Käufers). orderDate ist optional.

Beispiel für lineItems im rows-Modus mit Rabatt
#

lineItems:
  mode: "rows"
  fields:
    name:
      column: "Artikel_Name"
    description:
      column: "Artikel_Beschreibung"
    sellerAssignedId:
      column: "Artikelnummer"
    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"

isDiscount: true erzeugt einen Rabatt (TradeAllowance), false einen Zuschlag (TradeCharge). Im columns-Modus wird Rabatt stattdessen über discountPercent in fields konfiguriert.

Beispiel für lineItems im columns-Modus
#

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"

Wichtig dabei:

  • PRODUCT_NAME, PRODUCT_PRICE und ähnliche Felder werden als Fragment für nummerierte Spalten wie PRODUCT_NAME_0 verwendet.
  • INVOICE_TAX kann im selben Block als globale Rechnungs-Spalte verwendet werden und gilt dann für jede erzeugte Position.
  • sellerAssignedId ist für interne Produktnummern. description bleibt für echte Artikelbeschreibungen reserviert.
  • fixedFields unterstützt vollständiges FieldMapping inklusive rules — nützlich z. B. für positionsabhängige Einheitencodes.

Rabatte
#

discountPercent akzeptiert Werte wie 10, 10.00 oder 10%. Der Rabatt wird validierungsfreundlich als rabattierter Netto-Einzelpreis abgebildet:

  • Brutto-Listenpreis → GrossPriceProductTradePrice (= Originalpreis)
  • Rabattierter Netto-Einzelpreis → NetPriceProductTradePrice (netPrice × (1 − percent/100), 4 Dezimalstellen)
  • Zeilennettobetrag = rabattierter Einzelpreis × Menge

Es wird kein AppliedTradeAllowanceCharge für discountPercent-Positionsrabatte erzeugt. Dadurch bleiben EN-16931-Regeln wie BR-S-08 konsistent.

Im rows-Modus gibt es zusätzlich lineItems.allowanceCharge für weitere Rabatte oder Zuschläge auf Positionsebene, unabhängig von discountPercent.

Summen
#

Automatisch (empfohlen, wenn Preise, Mengen und Steuerprozentsatz vollständig aus der CSV kommen):

totals:
  mode: "auto"

Manuell (nur sinnvoll, wenn die CSV fachlich geprüfte Summen liefert):

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"

Empfehlung
#

Die YAML-Datei sollte gemeinsam mit dem CSV-Export versioniert werden. Dadurch bleibt nachvollziehbar, welche CSV-Struktur zu welcher Ausgabe geführt hat.

Praxisbeispiel: columns-Modus mit suffix-Spalten.

Häufige Fragen

Welche ZUGFeRD-Felder sind Pflicht?

csv2zugferd benötigt mindestens: Rechnungsnummer, Ausstellungsdatum, Lieferantendaten, Kundendaten, mindestens eine Rechnungsposition mit Menge, Einzelpreis und Steuersatz sowie den Gesamtbetrag.

Kann ich mehrere YAML-Konfigurationen für verschiedene CSV-Formate nutzen?

Ja. csv2zugferd nimmt die Konfigurationsdatei über --config entgegen. Für verschiedene CSV-Quellen (CRM, ERP, Excel-Export) genügt jeweils eine eigene .yml-Datei.