8.1 KiB
Design — Ausbaustufe 5: Deutsche Formate, Posten-Bearbeitung, Szenario-Ende (v0.6.0)
Status: vom Nutzer freigegeben (Chat 2026-07-20). Umsetzungsplan folgt unter
docs/superpowers/plans/. Datenschutz-Regel ausCLAUDE.mdgilt: konkrete Empfängernamen/Beträge des Demo-Szenarios stehen nur im Chat und in der Live-Datenbank, nicht in diesem Dokument, nicht in Commits, nicht im Ledger.
Ziel
Drei Nutzeranforderungen an die Web-GUI des Finanzberatungs-Tools:
- Deutsche Formate — Beträge werden deutsch angezeigt (
1.234,56 €) und deutsch eingegeben (Komma-Dezimaltrenner); englische GUI-Begriffe (monthly,annuity,percent, …) werden übersetzt. - Wiederkehrende Posten — Felder
Start/Endein der GUI sichtbar und pflegbar (Modell/API/Engine können das bereits); jeder Posten bekommt einenBearbeiten-Button. Inline-Bearbeitung zusätzlich für Einmalposten, Kredite und den Szenario-Kopf (Nutzerentscheidung). - Szenarien — Modifikator-Art
Ende(Posten endet innerhalb des Szenarios an einem Datum, ohne den Posten selbst zu ändern) und szenario-eigene Einmalzahlungen. Demo-Szenario „Best Case" wird nach dem Release live angelegt und durchgerechnet.
Nutzerentscheidungen (Chat 2026-07-20)
- Szenario-Ende als neue Modifikator-Art
endemit Datumsfeld (nicht als Gültigkeitszeitraum aller Modifikatoren). - Einmalzahlung im Demo-Szenario ist szenario-spezifisch → neues Feature „Szenario-Einmalzahlungen".
- Formate: Zahlen + deutsche Begriffe (API-Werte bleiben englisch).
- Bearbeiten-Buttons für: wiederkehrende Posten, Einmalposten, Kredite, Szenario-Kopf.
Teil 1 — Deutsche Formate
Anzeige. Neuer Jinja-Filter eur in app/routers/gui.py
(templates.env.filters["eur"]), reine Decimal-Formatierung ohne
locale-Modul (Container-Locale unzuverlässig): Vorzeichen, Tausenderpunkt,
Komma, zwei Nachkommastellen. Ersetzt alle '%.2f'|format(...)-Stellen in
index.html, salden.html, transactions.html, planning.html,
_preview_table.html. Prozentwerte (Kredit-Zins) analog mit Komma.
Der per JavaScript nachgeladene Tilgungsplan (planning.html,
loadLoanSchedule) formatiert mit
toLocaleString('de-DE', {minimumFractionDigits: 2, maximumFractionDigits: 2}).
Eingabe. Die json-form-Extension in base.html lernt
data-type="amount": Whitespace/€ entfernen; enthält der Wert ein Komma →
Tausenderpunkte entfernen, Komma→Punkt; ohne Komma bleibt der Wert
unverändert (Punkt-Eingaben funktionieren weiter). Alle
Betrags-<input>-Felder (Fixposten, Einmalposten, Kredit, Modifikator-Wert,
Szenario-Einmalzahlung) bekommen data-type="amount" und deutsche
Platzhalter (-49,99). Versteckte Felder der Vorschlags-Übernahme bleiben
ohne data-type="amount" (Server liefert Punktformat). Die JSON-API bleibt
strikt Punkt-Dezimal — Tests, Skripte und Grafana sind nicht betroffen.
Begriffe. Zentrales Mapping als Jinja-Global de_label (Definition in
gui.py): monthly→monatlich, quarterly→vierteljährlich,
yearly→jährlich, annuity→Annuität, bullet→endfällig,
percent→Prozent, absolute→Absolut, remove→Entfällt, ende→Ende.
Dropdown-<option>-Texte und Tabellenzellen zeigen deutsche Labels;
value-Attribute, API-Literale und DB-Werte bleiben englisch (keine
Migration, keine API-Änderung). Unbekannte Werte fallen auf den Rohwert
zurück.
Teil 2 — Posten-Bearbeitung + Start/Ende
RecurringItem.start_date/end_date existieren in Modell, API
(RecurringIn/RecurringPatch) und Engine (recurrence.occurrences
klammert das Fenster bereits). Es fehlt nur GUI:
- Tabelle „Wiederkehrende Posten": neue Spalten Start und Ende (TT.MM.JJJJ, leer = „–").
- Anlege-Formular: zwei optionale
<input type="date">-Felder. - Inline-Bearbeiten je Zeile (Muster für alle vier Bereiche):
Bearbeiten-Button blendet eine vorbefüllte Formularzeile ein (Anzeigezeile wird versteckt, nicht entfernt — UX-Regel ausCLAUDE.mdbleibt gewahrt: Bedienelemente sichtbar/disabled, Fehler via bestehendemhtmx:responseError-Handler),SpeichernsendetPATCHüber die json-form-Extension,Abbrechenklappt zurück. Leeres Datumsfeld wird alsnullgesendet (Extension tut das bereits) und löscht den Wert. - Bereiche:
PATCH /api/recurring/{id}(alle Felder),PATCH /api/planned/{id}(Name, Betrag, Fälligkeit, Kategorie),PATCH /api/loans/{id}(alle Felder),PATCH /api/scenarios/{id}(Name, Beschreibung, include-Flags). - Neue Validierung in
RecurringIn/RecurringPatch(Pydantic model_validator):end_date ≥ start_date, wenn beide gesetzt (beim PATCH gegen den resultierenden Zustand geprüft), sonst 422 mit deutscher Meldung.
Teil 3 — Szenarien: Art ende + Szenario-Einmalzahlungen
Eine Alembic-Migration (scenario_ende) mit zwei Änderungen:
scenario_modifiers.end_date—Date, nullable.- Neue Tabelle
scenario_planned_items:id,scenario_id(FKscenarios.id),name(String 200),amount(MONEY),due(Date).
Modifikator-Art ende.
- API:
ModifierIn.kindum Literalendeerweitert; neues optionales Feldend_date: date | None. Validator:endeerfordertend_date; andere Arten ignorieren es (wird genullt).ModifierOutliefertend_datemit. - Engine:
PlainModifierbekommtend_date: date | None = None.build_cashflowsbestimmt voroccurrencesje Posten das effektive Ende: Minimum aus eigenemend_dateund allen treffendenende-Modifikatoren (Zielrecurringodercategory— bei Kategorie enden alle Posten der Kategorie)._modifiedbehandelt weiterhin nur die Betrags-Arten und überspringtende. - GUI (
planning.html): Art-Dropdown bekommt „Ende"; ein Datumsfeld wird nur bei dieser Art aktiviert (disabled-Umschalter analogonModTargetTypeChange), das Wert-Feld ist beiendedisabled. Modifikator-Tabelle zeigt das Datum.
Szenario-Einmalzahlungen.
- Endpunkte in
routers/scenarios.py:GET/POST /api/scenarios/{id}/planned(201, Name/Betrag/Datum),DELETE /api/scenarios/{id}/planned/{item_id}(404 bei fremder scenario_id, Musterdelete_modifier). run_projectionlädtscenario_planned_itemsdes Szenarios und hängt sie alsPlainPlannedan — unabhängig voninclude_planned(das steuert nur globale Einmalposten; szenario-eigene Zahlungen sind bewusst diesem Szenario zugeordnet).delete_scenariolöscht zusätzlichscenario_planned_items(kein DB-Cascade vorhanden, Muster der bestehenden Deletes).- GUI: je Szenario ein
<details>„Einmalzahlungen in diesem Szenario" (Tabelle Name/Betrag/Datum + Löschen, Anlege-Formular).
Teil 4 — Demo „Best Case", Release, Tests
Demo (live, nach Redeploy): Szenario „Best Case" anlegen; für die beiden
bestehenden Miet-Fixposten (falls nicht vorhanden: vorher anlegen bzw. aus
Vorschlägen übernehmen) je einen ende-Modifikator mit den im Chat
genannten Terminen setzen; szenario-eigene Einmalzahlung mit dem im Chat
genannten Betrag/Datum anlegen; Projektion durchrechnen. Anschließend
Schritt-für-Schritt-Erklärung der Eingabe im Chat. hilfe.html bekommt
einen kurzen Abschnitt zu Szenario-Varianten (generisch, ohne echte Daten).
Release: finance/VERSION → 0.6.0 vor ./create_pod_finance.sh
(Migration läuft im Entrypoint via alembic upgrade head).
Tests (TDD, Suite bleibt komplett grün):
eur-Filter: Rundung, Tausenderpunkte, negative Werte,Decimal-Input.- Engine:
ende-Modifikator (Ziel Posten/Kategorie, Zusammenspiel mit eigenemend_date, Minimum-Regel), Szenario-Einmalzahlung im Fenster. - API: Modifier-Validierung (
endeohne Datum → 422), CRUD Szenario-Einmalzahlungen, Cleanup beidelete_scenario,end ≥ start-Validierung bei Fixposten. - GUI-Tests: deutsche Betragsformatierung in gerenderten Seiten, neue Spalten/Formulare vorhanden, deutsche Labels statt englischer Rohwerte.
- Migration: Upgrade-Pfad in Wegwerf-DB (Muster Konto-Anker-Task).
Außerhalb des Scopes: Grafana-Dashboards (formatiert selbst), PDF/CSV-Parser (parsen deutsche Formate bereits), Übersetzung von API-Fehlertexten Dritter, Mehrsprachigkeit.