diff --git a/docs/superpowers/specs/2026-07-17-finanzberatung-design.md b/docs/superpowers/specs/2026-07-17-finanzberatung-design.md new file mode 100644 index 0000000..b99e3f8 --- /dev/null +++ b/docs/superpowers/specs/2026-07-17-finanzberatung-design.md @@ -0,0 +1,218 @@ +# 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-Service + `pod-finance_pod.service` in `~/.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 + +1. PDF landet in der Inbox (Drag-and-Drop in der GUI oder Datei-Kopie auf dem + Host). +2. **Bank-Erkennung** anhand von Textmerkmalen wählt den Parser; drei + Parser-Module (`vr.py`, `hvb.py`, `dkb.py`) mit gemeinsamem Interface + `parse(pdf) → (Auszugsdaten, Buchungsliste)`. +3. **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. +4. **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. +5. **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: + +1. **Übersicht:** Gesamtsaldo und Saldo je Konto, nächste anstehende + Zahlungen, Warnhinweis bei drohender Schwellen-Unterschreitung, + eingebettete Grafana-Panels. +2. **Import:** Drag-and-Drop-Fläche, Importliste mit Status, Vorschau-Tabelle + mit Bestätigen-Aktion. +3. **Buchungen:** filterbare Tabelle (Konto, Zeitraum, Kategorie, Volltext), + manuelle Kategorien-Korrektur mit „Regel daraus erzeugen“, **manuelles + Erfassen** von Zahlungen/Einkünften per Formular. +4. **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.