diff --git a/docs/superpowers/plans/2026-07-20-ausbaustufe-5.md b/docs/superpowers/plans/2026-07-20-ausbaustufe-5.md index 9d142da..fbeedc8 100644 --- a/docs/superpowers/plans/2026-07-20-ausbaustufe-5.md +++ b/docs/superpowers/plans/2026-07-20-ausbaustufe-5.md @@ -1,6 +1,6 @@ # Ausbaustufe 5 Implementation Plan — Deutsche Formate, Posten-Bearbeitung, Szenario-Ende (v0.6.0) -> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. +> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [x]`) syntax for tracking. **Goal:** Deutsche Zahlenformate in der gesamten Web-GUI, Inline-Bearbeitung aller Planungs-Objekte inkl. Start/Ende bei Fixposten, Szenario-Modifikator-Art »Ende« plus szenario-eigene Einmalzahlungen; Release v0.6.0 mit Live-Demo-Szenario »Best Case«. @@ -36,7 +36,7 @@ **Interfaces:** - Produces: `app.formats.eur(value) -> str` (Decimal/str/int → `"−"`-lose deutsche Darstellung, z.B. `Decimal("-2474.5")` → `"-2.474,50"`, ohne €-Zeichen), `app.formats.de_label(value: str) -> str` (englischer API-Wert → deutsches Label, unbekannt → Rohwert), `app.formats.DE_LABELS: dict[str, str]`. Jinja-Filter `|eur` und `|de_label` in allen Templates verfügbar. Spätere Tasks (2, 4, 7) nutzen beide Filter. -- [ ] **Step 1: Failing Tests schreiben** — `tests/test_formats.py`: +- [x] **Step 1: Failing Tests schreiben** — `tests/test_formats.py`: ```python from decimal import Decimal @@ -84,12 +84,12 @@ def test_de_label_bekannte_und_unbekannte_werte(): "bullet", "percent", "absolute", "remove", "ende"} ``` -- [ ] **Step 2: Fehlschlag verifizieren** +- [x] **Step 2: Fehlschlag verifizieren** Run: `.venv/bin/python -m pytest tests/test_formats.py -q` Expected: FAIL/ERROR mit `ModuleNotFoundError: No module named 'app.formats'` -- [ ] **Step 3: `app/formats.py` implementieren** +- [x] **Step 3: `app/formats.py` implementieren** ```python """Deutsche Anzeige-Formate fuer die GUI (Ausbaustufe 5). @@ -124,12 +124,12 @@ def de_label(value: str) -> str: return DE_LABELS.get(value, value) ``` -- [ ] **Step 4: Tests grün verifizieren** +- [x] **Step 4: Tests grün verifizieren** Run: `.venv/bin/python -m pytest tests/test_formats.py -q` Expected: alle Tests PASS -- [ ] **Step 5: Filter registrieren** — `app/routers/gui.py`, direkt nach `templates.env.globals["app_version"] = get_version()`: +- [x] **Step 5: Filter registrieren** — `app/routers/gui.py`, direkt nach `templates.env.globals["app_version"] = get_version()`: ```python from app.formats import de_label, eur # (oben bei den Imports einsortieren) @@ -138,7 +138,7 @@ templates.env.filters["eur"] = eur templates.env.filters["de_label"] = de_label ``` -- [ ] **Step 6: Failing GUI-Test für deutsche Anzeige** — in `tests/test_gui.py` ergänzen: +- [x] **Step 6: Failing GUI-Test für deutsche Anzeige** — in `tests/test_gui.py` ergänzen: ```python def test_gui_zeigt_deutsche_betragsformate(client, db): @@ -168,7 +168,7 @@ def test_planung_zeigt_deutsche_labels(client, db): Run: `.venv/bin/python -m pytest tests/test_gui.py -q` → die zwei neuen Tests FAIL (Punktformat/englische Labels im HTML). -- [ ] **Step 7: Templates umstellen** — alle `{{ '%.2f'|format(X) }}`-Vorkommen ersetzen durch `{{ X|eur }}` (Fundstellen: `index.html` Zeilen 14/29/32/71, `salden.html` 20/27/51/53, `transactions.html` 56, `_preview_table.html` 11/28/29, `planning.html` 16/67/101/146/147/231/284). In `planning.html` zusätzlich Labels: +- [x] **Step 7: Templates umstellen** — alle `{{ '%.2f'|format(X) }}`-Vorkommen ersetzen durch `{{ X|eur }}` (Fundstellen: `index.html` Zeilen 14/29/32/71, `salden.html` 20/27/51/53, `transactions.html` 56, `_preview_table.html` 11/28/29, `planning.html` 16/67/101/146/147/231/284). In `planning.html` zusätzlich Labels: - Zeile 17 `{{ r.rhythm }}` → `{{ r.rhythm|de_label }}`; Zeile 68 (Vorschläge) `{{ s.rhythm }}` → `{{ s.rhythm|de_label }}`; Zeile 148 `{{ l.repayment_type }}` → `{{ l.repayment_type|de_label }}`; Zeile 230 `{{ m.kind }}` → `{{ m.kind|de_label }}`. - Dropdown-Optionen: ``, ``, ``. - Kredit-Zins Zeile 147: `{{ l.annual_rate_pct|eur }} %`. @@ -185,18 +185,18 @@ function fmtEur(n) { + '' + fmtEur(r.remaining) + ' €'; ``` -- [ ] **Step 8: Bestehende Punktformat-Assertions anpassen** +- [x] **Step 8: Bestehende Punktformat-Assertions anpassen** Run: `grep -rn '\.00\|\.50\|\.99' tests/test_gui.py` und jede Assertion prüfen, die gerendertes HTML gegen Punktformat testet. Bekannt: `test_index_shows_account_anchor` erwartet `"Anker: 321.00"` → ändern in `"Anker: 321,00"`. Weitere Treffer analog auf Komma-Format umstellen (NUR Assertions gegen `r.text`/HTML — JSON-API-Assertions bleiben Punktformat!). -- [ ] **Step 9: Gesamte Suite grün** +- [x] **Step 9: Gesamte Suite grün** Run: `.venv/bin/python -m pytest -q` Expected: alle Tests passed (154 alt + 8 neu) -- [ ] **Step 10: Fable-Testagent-Abnahme einholen** (Faktencheck: Filter-Korrektheit inkl. Rundung/negativer Werte, Vollständigkeit der Template-Umstellung via grep `'%.2f'` → 0 Treffer in `app/templates/`, keine API-Format-Änderung). Erst nach VERIFIED weiter. +- [x] **Step 10: Fable-Testagent-Abnahme einholen** (Faktencheck: Filter-Korrektheit inkl. Rundung/negativer Werte, Vollständigkeit der Template-Umstellung via grep `'%.2f'` → 0 Treffer in `app/templates/`, keine API-Format-Änderung). Erst nach VERIFIED weiter. -- [ ] **Step 11: Commit** +- [x] **Step 11: Commit** ```bash cd /home/wlfb/bin && git add finance/app/formats.py finance/tests/test_formats.py finance/app/routers/gui.py finance/app/templates/ finance/tests/test_gui.py @@ -216,7 +216,7 @@ git commit -m "feat: deutsche Betragsanzeige (eur-Filter) und deutsche GUI-Label - Consumes: Filter `|eur` aus Task 1 (Vorbefüllung des Anker-Inputs). - Produces: json-form-Konvention `data-type="amount"`: Eingabewert wird vor dem JSON-Versand normalisiert (Leerzeichen/`€` entfernen; enthält der Wert ein Komma → Tausenderpunkte entfernen, Komma→Punkt; ohne Komma unverändert). Tasks 4 und 7 verwenden `data-type="amount"` in ihren Formularen. -- [ ] **Step 1: Failing Test — Attribute im gerenderten HTML** (die JS-Konvertierung selbst ist serverseitig nicht testbar; sie wird im Fable-Live-Test geprüft). In `tests/test_gui.py`: +- [x] **Step 1: Failing Test — Attribute im gerenderten HTML** (die JS-Konvertierung selbst ist serverseitig nicht testbar; sie wird im Fable-Live-Test geprüft). In `tests/test_gui.py`: ```python def test_betragsfelder_haben_amount_typ_und_deutsche_platzhalter(client): @@ -232,7 +232,7 @@ def test_betragsfelder_haben_amount_typ_und_deutsche_platzhalter(client): Run: `.venv/bin/python -m pytest tests/test_gui.py::test_betragsfelder_haben_amount_typ_und_deutsche_platzhalter -q` → FAIL -- [ ] **Step 2: json-form-Extension erweitern** — `base.html`, in `encodeParameters` vor dem `else`-Zweig: +- [x] **Step 2: json-form-Extension erweitern** — `base.html`, in `encodeParameters` vor dem `else`-Zweig: ```javascript } else if (kind === 'int') { @@ -248,7 +248,7 @@ Run: `.venv/bin/python -m pytest tests/test_gui.py::test_betragsfelder_haben_amo } else { ``` -- [ ] **Step 3: Eingabefelder umstellen** +- [x] **Step 3: Eingabefelder umstellen** - `planning.html` Fixposten-Formular: `` - `planning.html` Einmalposten-Formular: `placeholder="-2000,00"`, `data-type="amount"` - `planning.html` Kredit-Formular: `principal` → `data-type="amount" placeholder="10000,00"`; `annual_rate_pct` → `data-type="amount" placeholder="4,5"` @@ -256,14 +256,14 @@ Run: `.venv/bin/python -m pytest tests/test_gui.py::test_betragsfelder_haben_amo - `index.html` Anker-Formular: `` - Die versteckten Felder der Vorschlags-Übernahme (``) bekommen KEIN `data-type="amount"` — der Server liefert dort Punktformat, und die amount-Normalisierung ließe es unverändert; ohne Attribut bleibt die Semantik explizit. -- [ ] **Step 4: Tests grün + Suite** +- [x] **Step 4: Tests grün + Suite** Run: `.venv/bin/python -m pytest tests/test_gui.py -q && .venv/bin/python -m pytest -q` Expected: alles PASS -- [ ] **Step 5: Fable-Testagent-Abnahme** — diesmal MIT Live-Anteil: gegen die laufende Instanz (oder lokal gestartete App) im Browser/per curl prüfen, dass ein Fixposten mit Eingabe `1.234,56` als `1234.56` gespeichert wird und `-49,99` korrekt ankommt. Erst nach VERIFIED weiter. (Hinweis: Wenn erst beim Release-Task deployt wird, den Live-Anteil per `uvicorn`-Teststart gegen SQLite erledigen: `cd finance && FB_DATABASE_URL=sqlite:///./demo.sqlite FB_GUI_PASSWORD_HASH=$(.venv/bin/python -c "from app.auth import hash_password; print(hash_password('demo'))") .venv/bin/uvicorn app.main:app --port 8123` — Wegwerf-DB danach löschen.) +- [x] **Step 5: Fable-Testagent-Abnahme** — diesmal MIT Live-Anteil: gegen die laufende Instanz (oder lokal gestartete App) im Browser/per curl prüfen, dass ein Fixposten mit Eingabe `1.234,56` als `1234.56` gespeichert wird und `-49,99` korrekt ankommt. Erst nach VERIFIED weiter. (Hinweis: Wenn erst beim Release-Task deployt wird, den Live-Anteil per `uvicorn`-Teststart gegen SQLite erledigen: `cd finance && FB_DATABASE_URL=sqlite:///./demo.sqlite FB_GUI_PASSWORD_HASH=$(.venv/bin/python -c "from app.auth import hash_password; print(hash_password('demo'))") .venv/bin/uvicorn app.main:app --port 8123` — Wegwerf-DB danach löschen.) -- [ ] **Step 6: Commit** +- [x] **Step 6: Commit** ```bash cd /home/wlfb/bin && git add finance/app/templates/ finance/tests/test_gui.py @@ -283,7 +283,7 @@ git commit -m "feat: Betragseingabe mit Komma (data-type=amount in json-form)" - Consumes: bestehende Felder `RecurringItem.start_date/end_date`, `RecurringIn/RecurringPatch.start_date/end_date` (existieren schon), Filter `|eur`/`|de_label`. - Produces: Validierungsregel »`end_date >= start_date`, sonst 422 mit Meldung „Ende darf nicht vor Start liegen"« bei POST und PATCH `/api/recurring`. GUI-Spalten »Start«/»Ende«. Task 4 baut das Bearbeiten-Formular auf dieselben Felder. -- [ ] **Step 1: Failing API-Tests** — in `tests/test_planning_api.py`: +- [x] **Step 1: Failing API-Tests** — in `tests/test_planning_api.py`: ```python def test_recurring_ende_vor_start_wird_abgelehnt(client): @@ -312,7 +312,7 @@ def test_recurring_start_ende_roundtrip_und_patch_validierung(client): Run: `.venv/bin/python -m pytest tests/test_planning_api.py -q` → die neuen Tests FAIL (POST liefert 201 statt 422 bzw. PATCH 200 statt 422) -- [ ] **Step 2: Validierung implementieren** — `app/routers/planning.py`: +- [x] **Step 2: Validierung implementieren** — `app/routers/planning.py`: Import ergänzen: `from pydantic import BaseModel, ConfigDict, Field, model_validator` @@ -345,11 +345,11 @@ In `patch_recurring` nach der `setattr`-Schleife, vor `session.commit()`: raise HTTPException(422, "Ende darf nicht vor Start liegen") ``` -- [ ] **Step 3: API-Tests grün** +- [x] **Step 3: API-Tests grün** Run: `.venv/bin/python -m pytest tests/test_planning_api.py -q` → PASS -- [ ] **Step 4: Failing GUI-Test** — in `tests/test_gui.py`: +- [x] **Step 4: Failing GUI-Test** — in `tests/test_gui.py`: ```python def test_planung_fixposten_zeigt_start_und_ende(client, db): @@ -366,7 +366,7 @@ def test_planung_fixposten_zeigt_start_und_ende(client, db): Run: → FAIL -- [ ] **Step 5: Template** — `planning.html`, Fixposten-Tabelle: +- [x] **Step 5: Template** — `planning.html`, Fixposten-Tabelle: Kopfzeile: `NameBetragRhythmusFälligkeitstagStartEndeKategorie` @@ -386,13 +386,13 @@ Datenzeile (nach der Fälligkeitstag-Zelle): (Leere Datumsfelder sendet die json-form-Extension bereits als `null`.) -- [ ] **Step 6: Suite grün** +- [x] **Step 6: Suite grün** Run: `.venv/bin/python -m pytest -q` → PASS -- [ ] **Step 7: Fable-Testagent-Abnahme** (Faktencheck: Validierung POST+PATCH inkl. Null-Löschen; GUI-Spalten; `occurrences` klammert Start/Ende korrekt — bestehende Engine-Tests referenzieren). Erst nach VERIFIED weiter. +- [x] **Step 7: Fable-Testagent-Abnahme** (Faktencheck: Validierung POST+PATCH inkl. Null-Löschen; GUI-Spalten; `occurrences` klammert Start/Ende korrekt — bestehende Engine-Tests referenzieren). Erst nach VERIFIED weiter. -- [ ] **Step 8: Commit** +- [x] **Step 8: Commit** ```bash cd /home/wlfb/bin && git add finance/app/routers/planning.py finance/app/templates/planning.html finance/tests/ @@ -411,7 +411,7 @@ git commit -m "feat: Start/Ende fuer Fixposten in der GUI + Datumsvalidierung" - Consumes: `PATCH /api/recurring/{id}`, `/api/planned/{id}`, `/api/loans/{id}`, `/api/scenarios/{id}` (existieren alle); json-form inkl. `data-type="amount"` (Task 2); Filter `|eur`/`|de_label` (Task 1); Start/Ende-Felder (Task 3). - Produces: JS-Helfer `toggleEdit(prefix, id, editing)` in `planning.html`; Formularzeilen-IDs `rec-edit-{id}`, `pln-edit-{id}`, `loan-edit-{id}`, `sc-edit-{id}` und Anzeige-IDs `rec-row-{id}`, `pln-row-{id}`, `loan-row-{id}`, `sc-head-{id}`. Task 7 folgt demselben Muster nicht (Szenario-Unterobjekte haben eigene Formulare), benötigt aber `toggleEdit` nicht. -- [ ] **Step 1: Failing GUI-Test** +- [x] **Step 1: Failing GUI-Test** ```python def test_planung_hat_bearbeiten_formulare(client, db): @@ -434,7 +434,7 @@ Imports in `tests/test_gui.py` ergänzen: `from app.models.tables import Account Run: `.venv/bin/python -m pytest tests/test_gui.py::test_planung_hat_bearbeiten_formulare -q` → FAIL -- [ ] **Step 2: JS-Helfer in `planning.html`** (in den bestehenden `