firefly-import-preprocessor/README.md
Reindl David (IT-PTR-CEN2-SL10) 90dc37cafc amend readme
2026-05-02 18:00:42 +02:00

22 KiB

Firefly Import Preprocessor - Dokumentation

Version: 1.0.0
Datum: 02. May 2026
Status: Production Ready


📋 Inhaltsverzeichnis

  1. Überblick
  2. Installation & Setup
  3. Schnellstart
  4. Konfiguration
  5. Transformationstypen
  6. CLI-Referenz
  7. Debug-Modus
  8. Firefly III Integration
  9. Architektur
  10. Fehlerbehandlung

Überblick

Der Firefly Import Preprocessor ist ein produktionsreifer PHP-Preprocessor für Banken-CSV-Exportdateien. Er transformiert Bankdaten in ein standardisiertes Format und kann sie optional in Firefly III importieren.

Kernfeatures

Vollständige CSV-Transformation mit komplexen Pipelines
Metadaten-Extraktion mit Regex (IBAN, Währung, Kontoname)
11 Transformationstypen für flexible Datenverarbeitung
Firefly III Integration mit Docker-Support
Debug-Modus für Transparenz bei Verarbeitung
Production Ready mit vollständiger Fehlerbehandlung
Zero Dependencies für Core-Funktionalität

Workflow

Input CSV
    ↓
Metadaten extrahieren (Regex)
    ↓
Datenzeilen transformieren (Pipeline)
    ↓
Output CSV schreiben
    ↓
[Optional] In Firefly III importieren

Installation & Setup

Voraussetzungen

  • PHP 8.1+
  • Composer (empfohlen)
  • [Optional] Docker für Firefly III Integration

Installation

# 1. Repository clonen/kopieren
cd ff-imp-preprocessor

# 2. Abhängigkeiten installieren (optional)
composer install

# 3. Konfiguration erstellen
cp config/config.example.json config/config.json
# Bearbeite config/config.json mit deinen Einstellungen

# 4. Directories erstellen
mkdir -p config/import/{source,output,archive,error}
chmod 755 config/import/{source,output,archive,error}

# 5. Test durchführen
php bin/transformer.php validate config/config.json input.csv

Schnellstart

1. Konfiguration anpassen

Bearbeite config/config.json und stelle sicher, dass die Extraction-Rules zu deinem CSV-Format passen:

{
  "metadata": {
    "extractionRules": [
      {
        "name": "account_iban",
        "lineNumber": 2,
        "regex": "IBAN:\\s*([A-Z0-9 ]+)",
        "captureGroup": 1
      }
    ]
  },
  "csvStructure": {
    "headerLine": 5,
    "delimiter": ";",
    "encoding": "UTF-8"
  }
}

2. CSV validieren

php bin/transformer.php validate config/config.json input.csv

Output:

✓ CSV ist valid
  IBAN: CH9300777222666888999
  Währung: CHF
  Zeilen: 150

3. Transformation durchführen

php bin/transformer.php process config/config.json input.csv

# Mit Debug-Modus für Fehlersuche
php bin/transformer.php process config/config.json input.csv --debug

4. Output prüfen

# Transformierte Datei
cat config/import/output/transformed.csv

# Oder mit Debug-Ausgabe
php bin/transformer.php test config/config.json input.csv --debug
# Zeigt max. 10 transformierte Zeilen und Debug-Logs

Konfiguration

config.json Struktur

metadata - Metadaten-Extraktion

{
  "metadata": {
    "extractionRules": [
      {
        "name": "account_iban",
        "lineNumber": 2,
        "regex": "IBAN:\\s*([A-Z0-9 ]+)",
        "captureGroup": 1
      },
      {
        "name": "currency_code",
        "lineNumber": 3,
        "regex": "Währung:\\s*([A-Z]{3})",
        "captureGroup": 1
      }
    ]
  }
}
Feld Typ Beschreibung
name string Name der Metadaten-Variable (verwendet in constantvalue)
lineNumber int Zeilennummer in CSV (1-basiert, menschenlesbar)
regex string Regex-Pattern zur Extraktion (ohne Delimiter)
captureGroup int Nummer der Klammer-Gruppe (0=komplett, 1=erste Klammer, etc.)

Beispiel Regex:

  • Pattern: IBAN:\s*([A-Z0-9 ]+)
  • Input: IBAN: CH93 0077 2020 6262 5252 7
  • Capture Group 1: CH93 0077 2020 6262 5252 7

csvStructure - CSV-Format

{
  "csvStructure": {
    "headerLine": 5,
    "delimiter": ";",
    "encoding": "UTF-8",
    "hasBom": false
  }
}
Feld Typ Default Beschreibung
headerLine int 5 Zeilennummer der Header (1-basiert)
delimiter string ; CSV-Delimiter
encoding string UTF-8 Zeichenkodierung (UTF-8, ISO-8859-1, CP1252)
hasBom bool false Hat die Datei BOM (Byte Order Mark)?

columnTransformations - Spalten-Transformationen

{
  "columnTransformations": [
    {
      "sourceColumn": "Buchungsdatum",
      "transformations": [
        {
          "type": "dateformat",
          "fromFormat": "d.m.Y",
          "toFormat": "Y-m-d"
        }
      ],
      "outputColumn": "date",
      "outputAction": "overwrite"
    }
  ]
}

outputAction:

  • overwrite - Überschreibe sourceColumn
  • create - Erstelle neue Spalte (für Regex-Extract, Split, etc.)

directories - Dateisystem

{
  "directories": {
    "source": "/opt/ff-imp-preprocessor/import/source",
    "output": "/opt/ff-imp-preprocessor/import/output",
    "archive": "/opt/ff-imp-preprocessor/import/archive",
    "error": "/opt/ff-imp-preprocessor/import/error"
  }
}
Feld Beschreibung
source Eingabe-Verzeichnis
output Ausgabe-Verzeichnis
archive Archiv für verarbeitete Dateien
error Error-Verzeichnis für ungültige Dateien

fireflyImport - Firefly III Integration

{
  "fireflyImport": {
    "jsonConfig": "/opt/firefly/import-config.json",
    "importerCommand": "docker exec -it firefly-importer php artisan importer:import",
    "autoImport": false,
    "deleteAfterImport": false,
    "timeout": 300,
    "environment": {
      "FIREFLY_III_URL": "https://your-firefly.com",
      "FIREFLY_III_ACCESS_TOKEN": "your-token-here"
    }
  }
}

Transformationstypen

Es gibt 13 unterstützte Transformationstypen, die als Pipeline kombiniert werden können:

1. trim - Leerzeichen entfernen

Entfernt Leerzeichen am Anfang und Ende.

{
  "type": "trim"
}

Beispiel:

  • Input: Coop Pronto
  • Output: Coop Pronto

2. lowercase - Zu Kleinbuchstaben

Wandelt in Kleinbuchstaben um (UTF-8 safe).

{
  "type": "lowercase"
}

Beispiel:

  • Input: COOP PRONTO CHUR
  • Output: coop pronto chur

3. uppercase - Zu Grossbuchstaben

Wandelt in Grossbuchstaben um (UTF-8 safe).

{
  "type": "uppercase"
}

Beispiel:

  • Input: Coop Pronto Chur
  • Output: COOP PRONTO CHUR

4. ucwordsfirst - Grossschreibung nach Trennzeichen

Grossschreibt ersten Buchstaben nach Worttrennzeichen.

{
  "type": "ucwordsfirst"
}

Beispiele:

  • COOP PRONTO CHURCoop Pronto Chur
  • migros-rail cityMigros-Rail City
  • O'NEILL STOREO'Neill Store
  • SAINT-JEAN-DE-MAURIENNESaint-Jean-De-Maurienne

Trennzeichen: Leerzeichen, Bindestrich, Apostroph, Slash, Punkt, Komma, Semikolon, Doppelpunkt, Klammern.


5. replace - String-Replacement

Ersetzt Substring durch anderen String (case-sensitive).

{
  "type": "replace",
  "search": "  ",
  "replace": " "
}

Beispiel:

  • Input: Coop Pronto (2 Leerzeichen)
  • Output: Coop Pronto (1 Leerzeichen)

6. split - Spalte teilen

Teilt einen Wert bei Delimiter und behält einen definierten Teil.

{
  "type": "split",
  "delimiter": ";",
  "part": 0
}

Beispiel:

  • Input: Coop Pronto Chur;7007 Chur
  • Config: delimiter=";", part=0
  • Output: Coop Pronto Chur

7. regex - Regex-Ersetzung

Ersetzt Teile des Strings per regulärem Ausdruck. Nutzt PHP preg_replace.

{
  "type": "regex",
  "pattern": "^(.*?);.*$",
  "replace": "$1"
}

Beispiel — mit Match:

  • Input: Meier, Anna; Zahlung UBS TWINT
  • Pattern: ^(.*?);.*$, Replace: $1
  • Output: Meier, Anna

Beispiel — kein Match (pass-through):

  • Input: Coop Pronto Chur (kein Semikolon)
  • Output: Coop Pronto Chur ← Wert bleibt unverändert

Beispiel — Pattern ohne Anchors (ersetzt nur den gematchten Teil):

  • Input: Zahlung UBS TWINT; Referenz 12345
  • Pattern: UBS TWINT, Replace: TWINT
  • Output: Zahlung TWINT; Referenz 12345

Hinweise:

  • Kein Match → Originalwert wird unverändert weitergegeben (pipeline-sicher)
  • Capture Groups als $1, $2, … im replace referenzieren
  • Pattern ohne ^/$-Anchors ersetzt nur den gematchten Teilstring, nicht den ganzen Wert

8. regexextract - Regex-Extraktion

Extrahiert eine Capture Group per regulärem Ausdruck und gibt nur diese zurück. Nutzt PHP preg_match.

{
  "sourceColumn": "Mitteilungen",
  "transformations": [
    {
      "type": "regexextract",
      "pattern": "(\\d{4,} [^;]+)"
    }
  ],
  "outputColumn": "location",
  "outputAction": "create"
}

Beispiel — mit Match:

  • Input: Coop Pronto Chur, 7007 Chur
  • Pattern: (\d{4,} [^;]+)
  • Output: 7007 Chur ← nur Capture Group 1

Beispiel — kein Match:

  • Input: Dauerauftrag Miete
  • Output: `` (leerer String)

Hinweise:

  • Kein Match → leerer String (vorherige Pipeline-Schritte gehen verloren)
  • Nutzt Capture Group 1 wenn vorhanden, sonst komplettes Match
  • ⚠ Nicht pipeline-sicher: Nur als einzigen oder letzten Schritt verwenden. Wenn bei Kein-Match der bisherige Wert erhalten bleiben soll, regex verwenden.

9. dateformat - Datum-Umformat

Konvertiert zwischen Datum-Formaten.

{
  "type": "dateformat",
  "fromFormat": "d.m.Y",
  "toFormat": "Y-m-d"
}

Beispiel:

  • Input: 10.12.2025
  • Output: 2025-12-10

Supported Formate: Alle PHP DateTime-Formate (d, m, Y, H, i, s, etc.)


10. truncate - String kürzen

Kürzt String auf maximale Länge.

{
  "type": "truncate",
  "maxLength": 100
}

Beispiel:

  • Input: Dieser sehr lange Text... (150 Zeichen)
  • Output: Dieser sehr lange Text... (100 Zeichen)

11. constantvalue - Konstanten-Wert aus Metadaten

Nutzt extrahierte Metadaten als konstanten Wert.

{
  "sourceColumn": "_constant_",
  "transformations": [
    {
      "type": "constantvalue",
      "metadataKey": "account_iban"
    }
  ],
  "outputColumn": "account_iban",
  "outputAction": "create"
}

Beispiel:

  • metadataKey: account_iban (aus Extraktion)
  • Wert: CH9300777222666888999
  • Jede Zeile erhält diesen Wert in neuer Spalte

12. map - Spalte kopieren (Standard)

Kopiert oder benannt eine Spalte um.

{
  "sourceColumn": "Buchungstext",
  "transformations": [
    {
      "type": "map"
    }
  ],
  "outputColumn": "description",
  "outputAction": "overwrite"
}

Beispiel:

  • Input Spalte: Buchungstext
  • Output Spalte: description mit gleichem Inhalt

13. pipeline - Verschachtelte Pipeline

Führt eine Unter-Pipeline innerhalb eines Transformationsschritts aus. Ermöglicht das Gruppieren von Schritten als Einheit.

{
  "type": "pipeline",
  "steps": [
    { "type": "trim" },
    { "type": "lowercase" },
    { "type": "ucwordsfirst" }
  ]
}

Hinweis: In der Praxis werden mehrere Schritte direkt als transformations-Array aufgelistet. pipeline als Typ ist nützlich wenn innerhalb einer transformations-Liste eine Gruppe von Schritten bedingt oder als Einheit behandelt werden soll.


Pipeline-Beispiel

Mehrere Transformationen hintereinander:

{
  "sourceColumn": "Buchungstext",
  "transformations": [
    { "type": "trim" },
    { "type": "replace", "search": "  ", "replace": " " },
    { "type": "lowercase" },
    { "type": "ucwordsfirst" }
  ],
  "outputColumn": "description",
  "outputAction": "overwrite"
}

Verarbeitung:

  1. " COOP PRONTO " → trim → "COOP PRONTO"
  2. "COOP PRONTO" → replace → "COOP PRONTO"
  3. "COOP PRONTO" → lowercase → "coop pronto"
  4. "coop pronto" → ucwordsfirst → "Coop Pronto"

CLI-Referenz

Der Transformer wird über bin/transformer.php aufgerufen:

php bin/transformer.php <command> <config> <input> [options]

Kommandos

process - Führe Transformation aus

php bin/transformer.php process config/config.json input.csv [--debug]
  • Transformiert CSV-Datei komplett
  • Schreibt in directories.output (aus config)
  • Optional: --debug für Debug-Logs

Output:

✓ Transformation erfolgreich
  Input:  input.csv
  Output: config/import/output/transformed.csv
  Zeilen: 245
  Sample:
    - [Datum: 2025-01-10, Beschreibung: ...]

validate - Validiere CSV

php bin/transformer.php validate config/config.json input.csv
  • Prüft CSV-Format
  • Extrahiert Metadaten
  • Validiert erforderliche Felder

Output:

✓ CSV ist valid
  Zeilen: 245
  IBAN: CH9300777222666888999
  Währung: CHF

test - Test-Run mit Beispieldaten

php bin/transformer.php test config/config.json input.csv [--debug]
  • Transformiert maximal 10 Zeilen
  • Zeigt Metadaten und Beispiel-Output
  • Schnell zur Prüfung von Konfiguration

Output:

✓ Test erfolgreich
  Max 10 Zeilen verarbeitet: 10
  Metadaten:
    - IBAN: CH93...
    - Währung: CHF
  Beispiel (Zeile 1):
    - date: 2025-01-10
    - description: coop pronto chur
    - amount: 25.50

help - Zeige Hilfe

php bin/transformer.php help

Optionen

Option Beschreibung Beispiel
--debug Aktiviere Debug-Modus process config.json input.csv --debug
--test Führe nur Test mit 10 Zeilen aus process config.json input.csv --test

Debug-Modus

Der Debug-Modus bietet maximale Transparenz über die Verarbeitung.

Aktivierung

php bin/transformer.php process config/config.json input.csv --debug

Ausgegabene Informationen

Der Debug-Modus protokolliert:

1. Transformer Level

  • Transformation gestartet (Input-Datei, maxRows)
  • Transformation abgeschlossen
  • Fehler

2. CSV Reader Level

  • Metadaten-Zeilen gelesen (Anzahl)
  • Datenzeilen gelesen (Anzahl)

3. Metadata Extraction Level

  • Extraktion-Regel angewendet
  • IBAN extrahiert
  • Währung extrahiert
  • Fehlerhafte Regex
  • Fehlende Zeilen

4. Transformation Level

  • Split-Transformation angewendet
  • RegexExtract-Transformation angewendet
  • Fehlerhafte Transformationen

5. CSV Writer Level

  • Transformierte Daten geschrieben
  • Output-Datei-Pfad

JSON-Output mit Debug-Logs

Mit --debug wird das Ergebnis erweitert:

{
  "success": true,
  "inputFile": "input.csv",
  "outputFile": "output/transformed.csv",
  "rowsProcessed": 245,
  "metadata": { "account_iban": "CH93...", "currency_code": "CHF" },
  "debug_logs": [
    {
      "timestamp": 1702200120.5432,
      "category": "transformer",
      "message": "Transformation started",
      "data": { "inputFile": "input.csv", "maxRows": 0 }
    },
    {
      "timestamp": 1702200120.5445,
      "category": "metadata",
      "message": "Extraction rule applied",
      "data": { "rule_name": "account_iban", "value": "CH93..." }
    }
    // ... weitere Logs
  ]
}

Debug-Log-Kategorien

Kategorie Beispiel Wann
transformer Transformation gestartet Anfang/Ende Transformation
csv_reader Zeilen gelesen Beim CSV lesen
metadata IBAN extrahiert Bei Metadaten-Extraktion
metadata_warning Regex matched nicht Bei Problemen
transformation Split angewendet Bei jeder Transformation
csv_writer Daten geschrieben Beim CSV schreiben

Firefly III Integration

Der Transformer kann transformierte Daten automatisch in Firefly III importieren.

Setup

  1. Firefly III laufen lassen
docker-compose up -d firefly-iii
docker-compose up -d firefly-importer
  1. Konfiguration anpassen
{
  "fireflyImport": {
    "autoImport": true,
    "environment": {
      "FIREFLY_III_URL": "https://your-firefly.local",
      "FIREFLY_III_ACCESS_TOKEN": "your-personal-access-token"
    }
  }
}
  1. Import-Konfiguration erstellen

/opt/firefly/import-config.json:

{
  "accounts": [
    {
      "name": "UBS Checking",
      "iban": "CH9300777222666888999",
      "account_type": "asset"
    }
  ],
  "column_mapping": {
    "date": "date",
    "amount": "amount",
    "description": "description"
  }
}
  1. Auto-Import aktivieren
php bin/transformer.php process config/config.json input.csv
# Wenn autoImport=true, wird die transformierte Datei automatisch importiert

Manueller Import

docker exec -it firefly-importer php artisan importer:import

Architektur

Komponenten

bin/transformer.php (CLI Entry Point)
  ↓
TransformerEngine (Orchestrierung)
  ├─ ConfigurationLoader (Config laden/validieren)
  ├─ CsvReader (CSV einlesen)
  ├─ MetadataExtractor (Metadaten mit Regex)
  ├─ ColumnTransformer (Transformationen anwenden)
  ├─ CsvWriter (CSV schreiben)
  ├─ FireflyImporter (Firefly III Integration)
  └─ DebugLogger (Debug-Protokolle)

Datenfluss

Input CSV
  ↓
CsvReader::readMetadataLines() → Array von Zeilen
  ↓
MetadataExtractor::extract() → {iban: "...", currency: "..."}
  ↓
CsvReader::readCsvData() → Array von Zeilen
  ↓
ColumnTransformer::transformRow() → Transformierte Zeile (Pipeline)
  ↓
CsvWriter::write() → Output CSV

Klassen

Klasse Verantwortung
TransformerEngine Orchestriert gesamten Workflow
ConfigurationLoader Lädt JSON/YAML Konfiguration
CsvReader Liest CSV mit Metadaten
MetadataExtractor Extrahiert mit Regex
ColumnTransformer Transformiert Spalten (Pipeline)
CsvWriter Schreibt CSV
FireflyImporter Importiert in Firefly III
DebugLogger Statischer Logger für Debug

Fehlerbehandlung

Häufige Fehler

1. "Input-Datei nicht gefunden"

Error: Input-Datei nicht gefunden: input.csv

Ursache: Dateipfad falsch oder Datei nicht vorhanden

Lösung:

# Prüfe Dateipfad
ls -la input.csv

# Nutze absoluten Pfad wenn relativ nicht funktioniert
php bin/transformer.php process config.json /absolute/path/input.csv

2. "Fehlende Metadaten: account_iban"

Error: Fehlende Metadaten: account_iban

Ursache: IBAN konnte nicht extrahiert werden (Regex oder Zeile falsch)

Lösung:

# Debugging mit --debug
php bin/transformer.php validate config.json input.csv

# Prüfe erste 5 Zeilen des CSV
head -5 input.csv

# Überprüfe lineNumber und regex in config.json
# lineNumber sollte auf Zeile mit IBAN zeigen

3. "Ungültiges JSON"

Error: Ungültiges JSON: Syntax error, malformed JSON

Ursache: JSON-Syntax-Fehler in config.json

Lösung:

# Validiere JSON
php -r "json_decode(file_get_contents('config/config.json'), true) or die('JSON invalid');"

# Oder nutze online JSON validator
# https://jsonlint.com/

4. "CSV-Struktur-Fehler"

Error: 'csvStructure.headerLine' erforderlich

Ursache: Erforderliche Konfigurationsfelder fehlen

Lösung:

# Vergleiche mit config.example.json
diff config/config.json config/config.example.json

# Stellen Sie sicher, dass folgende Felder vorhanden sind:
# - csvStructure.headerLine (Zeilennummer der Kopfzeile)
# - csvStructure.delimiter (CSV-Trennzeichen)
# - metadata.extractionRules (mindestens 1)
# - columnTransformations (mindestens 1)

Exception-Handling

Der Transformer nutzt Try-Catch für robuste Fehlerbehandlung:

try {
    $result = $engine->transform($inputFile);
    if (!$result['success']) {
        echo "Fehler: " . $result['error'];
    }
} catch (Exception $e) {
    echo "Fatal Error: " . $e->getMessage();
}

Debug-Modus für Fehlersuche

Der Debug-Modus zeigt detailliert wo der Fehler liegt:

php bin/transformer.php process config/config.json input.csv --debug 2>&1 | tail -50

Besonderheiten & Tipps

UTF-8 Handling

Der Transformer nutzt UTF-8 safe Funktionen:

  • mb_strtolower() statt strtolower()
  • mb_strtoupper() statt strtoupper()
  • mb_strlen() für korrektes Character-Counting

Unterstützte Encodings: UTF-8, ISO-8859-1, CP1252

Regex-Tipps

Pattern ohne Delimiter:

"pattern": "IBAN:\\s*([A-Z0-9 ]+)"
// Wird zu: /IBAN:\s*([A-Z0-9 ]+)/u

Mit Flags:

"pattern": "/IBAN:\\s*([A-Z0-9 ]+)/iu"
// Case-insensitive

Spezielle Zeichen escapen:

"pattern": "\\(\\d{4}\\)"
// Matcht: (1234)

Performance

  • Max. Dateigröße: Abhängig von RAM (typically 100MB+)
  • Optimiert für: Bis zu 1 Million Zeilen
  • Typical: 10-100k Zeilen pro Datei

Batch-Processing

#!/bin/bash
for file in import/source/*.csv; do
    php bin/transformer.php process config/config.json "$file"
    if [ $? -eq 0 ]; then
        mv "$file" import/archive/
    else
        mv "$file" import/error/
    fi
done

Support & Troubleshooting

Logging aktivieren

Nutze den Built-in Debug-Logger:

php bin/transformer.php test config/config.json input.csv --debug > debug.log 2>&1
cat debug.log | grep -i error

Nützliche Kommandos

# Test CSV-Validität
php bin/transformer.php validate config.json input.csv

# Test nur mit 10 Zeilen
php bin/transformer.php test config.json input.csv

# Mit voller Debug-Ausgabe
php bin/transformer.php process config.json input.csv --debug

# Prüfe PHP-Syntax aller Dateien
php -l bin/transformer.php
php -l src/*.php

# Validiere Konfiguration
php -r "require 'src/ConfigurationLoader.php'; new UbsCsvTransformer\ConfigurationLoader('config/config.json');"

Version & Änderungen

v1.0.0 (10. Dezember 2025)

  • Initial Release
  • 11 Transformationstypen
  • Metadaten-Extraktion mit Regex
  • Debug-Modus
  • Firefly III Integration
  • Vollständige Dokumentation

Lizenz: MIT
Author: PHP CSV Transformer Project
Repository: https://git.andare.ch/david.reindl/ff-imp-preprocessor