10 KiB
Design: Finanzberatungs-Umgebung & Finanzberatungs-Tool
Datum: 2026-07-17 · Status: vom Nutzer abgenommen
Ziel
Eine finanziell schwierige Phase planbar machen: Kontobewegungen der nächsten Zeit vorhersagen, Kreditbedarf (Zeitpunkt und Höhe) ermitteln und sinnvolle Ausgabenkürzungen identifizieren. Dazu werden PDF-Kontoauszüge ausgewertet, in einer Datenbank abgelegt und über Szenarien in die Zukunft gerechnet.
Das Vorhaben besteht aus zwei Repositories:
~/fb(dieses Repo): die Claude-Code-Beratungsumgebung — CLAUDE.md, Projekt-Skills, Memory, Spezifikation und Plan. Kein Tool-Code.~/bin: das Finanzberatungs-Tool als rootless-Podman-Pod mit Web-GUI, API und Grafana.
Rahmenbedingungen (geklärt)
- Kontoauszüge liegen als PDF vor, von mehreren Konten bei mehreren Banken: Volksbank/Raiffeisenbank, HypoVereinsbank, DKB.
- PDF-Verarbeitung durch feste, deterministische Parser je Bank (pdfplumber), keine KI-Abhängigkeit im Import.
- Import immer mit Vorschau: extrahierte Buchungen werden vor der Übernahme angezeigt und explizit bestätigt.
- Zugriff: Das Tool bindet nur an lokale Ports (ab 8096); externe Erreichbarkeit regelt der bestehende Traefik-Reverse-Proxy/VPN des Betreibers. Zusätzlich eigener Login-Schutz im Tool (siehe Sicherheit).
- Szenarien umfassen: wiederkehrende Posten, geplante Einmalposten, Kredit-Simulation, Sparpotenzial-Analyse (alle vier bestätigt).
- Architektur-Entscheidung: Ansatz A, schlanker Pod (drei Container), bewusst gegen feingranulare Microservices (B) und gegen Firefly III (C).
Architektur
Podman Pod finance_pod, erstellt durch ~/bin/create_pod_finance.sh exakt
nach dem Muster von ~/bin/example_create_pod_langflow.sh:
- gepinnte, unveränderliche Image-Tags (kein
:latest), - Bind-Verzeichnisse unter
~/.local/share/finance_pod/mit:Z-Flag, podman generate systemd --new+ systemd-User-Servicepod-finance_pod.servicein~/.config/systemd/user/,- Host-IP/Ports als Variablen am Skriptanfang.
| Container | Image | Port (Host → Container) |
|---|---|---|
finance-db_ctr |
postgres:17.x (gepinnt) |
nur pod-intern (5432) |
finance-api_ctr |
eigenes Image aus ~/bin/finance/Containerfile |
127.0.0.1:8096 → 8000 |
finance-grafana_ctr |
grafana/grafana-oss (gepinnt) |
127.0.0.1:8097 → 3000 |
Das Skript baut das API-Image vor dem Pod-Start per podman build.
Bind-Verzeichnisse: postgres-data, grafana-data, inbox (Import-Eingang,
auch vom Host direkt befüllbar), uploads (archivierte Original-PDFs).
Sicherheit
- Web-GUI: Login-Seite mit Benutzername/Passwort, Session-Cookie. Passwort wird beim Setup gesetzt und nur als Hash gespeichert.
- API: statischer API-Schlüssel (langer Zufallswert), von Claude Code als
Authorization: Bearer-Header mitgeschickt; Anfragen ohne gültigen Schlüssel werden abgelehnt. - Secrets:
~/bin/finance/.env(gitignored), beim ersten Skriptlauf mit Zufallswerten erzeugt: Postgres-Passwort, API-Schlüssel, Grafana-Admin-Passwort, GUI-Zugangsdaten. - Grafana hat zusätzlich seinen eigenen Login; TLS/externes Auth macht Traefik.
Datenmodell (Postgres, SQLAlchemy + Alembic)
accounts— Konten: Bank, IBAN, Name, Typ (Giro/Tagesgeld/Kredit).transactions— Buchungen: Buchungs-/Wertstellungsdatum, Betrag (positiv = Eingang), Verwendungszweck, Gegenpartei, Konto, Kategorie, Quell-Auszug, Duplikat-Hash über (Konto, Datum, Betrag, Zweck).categories+category_rules— Kategorien und Zuordnungsregeln (Muster im Verwendungszweck/Gegenpartei → Kategorie), automatische Kategorisierung beim Import.statements— importierte PDFs: Datei, Bank, Konto, Zeitraum, Anfangs-/Endsaldo laut PDF, Status (Entwurf/übernommen/fehlerhaft).recurring_items— wiederkehrende Posten: Betrag, Rhythmus (monatlich/quartalsweise/jährlich), Stichtag, optionales Enddatum.planned_items— geplante Einmalposten: Datum, Betrag, Beschreibung.loans— Kredite: Auszahlungsdatum, Betrag, Zinssatz, Laufzeit, Tilgungsart (Annuität/endfällig).scenarios(+ Zuordnungs-/Modifikator-Tabellen) — benannte Was-wäre-wenn-Pakete; Kürzungs-Modifikatoren auf Kategorien oder einzelne wiederkehrende Posten (prozentual oder absolut, Streichung).
Import-Pipeline
- PDF landet in der Inbox (Drag-and-Drop in der GUI oder Datei-Kopie auf dem Host).
- Bank-Erkennung anhand von Textmerkmalen wählt den Parser; drei
Parser-Module (
vr.py,hvb.py,dkb.py) mit gemeinsamem Interfaceparse(pdf) → (Auszugsdaten, Buchungsliste). - Prüfungen:
- Saldo-Plausibilisierung: Anfangssaldo + Summe der Buchungen muss den Endsaldo ergeben; Abweichung ⇒ Auszug wird als fehlerhaft markiert und nicht übernommen.
- Duplikat-Prüfung über den Duplikat-Hash; bereits vorhandene Buchungen werden markiert und nicht doppelt übernommen.
- Vorschau (immer): Buchungen werden als Entwurf gespeichert und in der GUI (bzw. per API) als Tabelle mit Saldo-Status, automatischen Kategorien und Duplikat-Markierungen angezeigt.
- Bestätigung übernimmt die Buchungen endgültig; danach greifen sie in allen Auswertungen. Fehlgeschlagene Parses bleiben mit Fehlergrund auf der Import-Seite sichtbar, die Original-PDF bleibt in der Inbox.
Szenario-Engine
Reines Python-Modul (app/engine/), ohne DB-/Web-Abhängigkeiten testbar.
- Eingabe: Startsaldo (echter aktueller Gesamtsaldo bzw. je Konto), Szenario-Definition, Horizont (konfigurierbar, Standard 18 Monate).
- Abrollen aller Posten zu einem tagesgenauen Zahlungsstrom: wiederkehrende Posten nach Rhythmus, Einmalposten am Datum, Kredite als Auszahlung + berechnete Raten (Annuitätenformel bzw. endfällig, inkl. Zins-/Tilgungsaufteilung und Restschuldverlauf), Kürzungs-Modifikatoren auf die betroffenen Posten/Kategorien.
- Ausgabe je Szenario: Saldo-Verlauf (Tagesreihe), Tiefpunkt (Datum + Betrag), erstes Datum der Unterschreitung von Null bzw. einer konfigurierbaren Warnschwelle.
- Ergebnisse werden in Projektions-Tabellen persistiert, damit Grafana sie direkt visualisieren kann; Neuberechnung per Knopf/API-Aufruf.
- Unterstützung beim Anlegen: Muster-Erkennung über importierte Buchungen schlägt wiederkehrende Posten vor (Bestätigen/Verwerfen durch den Nutzer).
- Echte Buchungen werden von Szenarien nie verändert.
Web-GUI
FastAPI + Jinja2 + HTMX, deutschsprachig, vier Seiten:
- Übersicht: Gesamtsaldo und Saldo je Konto, nächste anstehende Zahlungen, Warnhinweis bei drohender Schwellen-Unterschreitung, eingebettete Grafana-Panels.
- Import: Drag-and-Drop-Fläche, Importliste mit Status, Vorschau-Tabelle mit Bestätigen-Aktion.
- Buchungen: filterbare Tabelle (Konto, Zeitraum, Kategorie, Volltext), manuelle Kategorien-Korrektur mit „Regel daraus erzeugen“, manuelles Erfassen von Zahlungen/Einkünften per Formular.
- Planung: wiederkehrende Posten (inkl. automatischer Vorschläge), Einmalposten, Kredite, Szenarien mit „Durchrechnen“-Aktion und Ergebnisanzeige.
Grafana
Vollständig provisioniert (Datenquelle Postgres mit lesendem DB-User,
Dashboards als Dateien im Repo ~/bin/finance/grafana/):
- Kontostand-Verlauf (je Konto und gesamt),
- Einnahmen/Ausgaben pro Monat, gestapelt nach Kategorie,
- Top-Ausgabenkategorien,
- Szenario-Vergleich (Vorschau-Kurven übereinander, Nulllinie, Warnschwelle).
API für Claude Code
REST/JSON unter http://127.0.0.1:8096/api/…, OpenAPI-dokumentiert.
Abgedeckte Funktionen: Konten/Buchungen lesen und anlegen, Kategorien und
Regeln verwalten, Import-Status abfragen und Entwürfe bestätigen,
wiederkehrende/geplante Posten, Kredite und Szenarien anlegen/ändern,
Szenarien durchrechnen und Ergebnisse (Verlauf, Tiefpunkt, Unterschreitung)
abrufen.
Claude-Code-Umgebung in ~/fb
CLAUDE.md: Umgebungsbeschreibung (API-URL, Ablageort des API-Schlüssels, Sprache Deutsch), Grundregeln („echte Buchungen nie ändern, Planung nur über Szenarien“).- Skills (
.claude/skills/):finanz-api— Endpunkt-Referenz mit curl-Beispielen,finanzberatung— Beratungs-Workflow: Lage abrufen → Basis-Szenario → Varianten (Kredit/Kürzung) anlegen und rechnen → vergleichen → Empfehlung mit Zahlen begründen,auszug-import— Import-Betreuung: wartende/fehlgeschlagene Importe prüfen, Parser-Fehler im~/bin-Repo beheben.
- Memory: Kategorien-Konventionen, Entscheidungen (z. B. Warnschwelle), Eigenheiten einzelner Banken/Auszüge.
Projektstruktur ~/bin
~/bin/
├── create_pod_finance.sh # Pod-Skript nach dem Beispiel-Muster
└── finance/
├── Containerfile # Image für finance-api_ctr
├── .env # Secrets (gitignored, vom Skript erzeugt)
├── app/ # FastAPI: API-Routen, GUI-Routen, Auth
│ ├── models/ # SQLAlchemy-Tabellen + Alembic-Migrationen
│ ├── parsers/ # vr.py, hvb.py, dkb.py + Bank-Erkennung
│ ├── engine/ # Szenario-/Kredit-Rechnung (reines Python)
│ └── templates/ # Jinja2 + HTMX
├── grafana/ # provisionierte Datenquelle + Dashboards
└── tests/ # pytest
└── fixtures/ # Beispiel-PDFs je Bank (gitignored)
Tests
Testgetriebene Entwicklung (pytest):
- Engine: Annuitäten-/Zins-Mathematik und Projektionslogik als Unit-Tests (zuerst geschrieben).
- Parser: Tests gegen echte Beispiel-PDFs in
tests/fixtures/(gitignored). Der Nutzer stellt pro Bank mindestens einen echten Auszug bereit (Beträge dürfen verfremdet sein, Layout muss echt sein) — zugesagt. - API/GUI: FastAPI-TestClient gegen eine Test-Datenbank.
- Deployment: Smoke-Test am Skriptende (analog Beispiel-Skript: curl-Readiness-Check auf GUI und Grafana).
Bewusst weggelassen (YAGNI)
- Kein automatischer Bank-Abruf (FinTS/PSD2) — Import nur über PDFs.
- Keine Mehrbenutzer-/Rollenverwaltung — ein GUI-Login genügt.
- Kein KI-Aufruf im Server — KI-Nutzung ausschließlich über Claude Code gegen die API.
- Keine Sofort-Übernahme beim Import — Vorschau ist verpflichtend.