chore: Release v0.13.0 (Seite Aufteilung)

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-09-29 16:49:50 +02:00
co-authored by Claude Opus 5.5
parent 3ef20034d3
commit b32f28fff5
3 changed files with 61 additions and 8 deletions
+42 -7
View File
@@ -18,11 +18,11 @@ direkt aus `routers/gui.py`.
```mermaid ```mermaid
flowchart TD flowchart TD
subgraph TPL["templates (Jinja2, app/templates/)"] subgraph TPL["templates (Jinja2, app/templates/)"]
T_pages["base.html, login.html, index.html<br/>transactions.html, salden.html<br/>import.html, planning.html<br/>admin.html, hilfe.html"] T_pages["base.html, login.html, index.html<br/>transactions.html, salden.html<br/>import.html, planning.html<br/>admin.html, hilfe.html<br/>aufteilung.html"]
end end
subgraph ROUTERS["routers/"] subgraph ROUTERS["routers/"]
R_gui["gui.py<br/>gui_session, index, buchungen,<br/>salden_page, import_page, planung_page"] R_gui["gui.py<br/>gui_session, index, buchungen,<br/>salden_page, import_page, planung_page,<br/>aufteilung_page, aufteilung_kategorie_fragment"]
R_accounts["accounts.py<br/>list_accounts, create_account,<br/>patch_account"] R_accounts["accounts.py<br/>list_accounts, create_account,<br/>patch_account"]
R_transactions["transactions.py<br/>list_transactions, create_transaction,<br/>patch_transaction, count_transactions"] R_transactions["transactions.py<br/>list_transactions, create_transaction,<br/>patch_transaction, count_transactions"]
R_categories["categories.py<br/>list_categories, create_category_rule"] R_categories["categories.py<br/>list_categories, create_category_rule"]
@@ -37,7 +37,8 @@ flowchart TD
S_balances["balances.py<br/>account_balance(at), total_balance"] S_balances["balances.py<br/>account_balance(at), total_balance"]
S_categorize["categorize.py<br/>apply_rules"] S_categorize["categorize.py<br/>apply_rules"]
S_suggestions["suggestions.py<br/>suggest_recurring"] S_suggestions["suggestions.py<br/>suggest_recurring"]
S_projection["projection_service.py<br/>run_projection, refresh_stale_projections"] S_projection["projection_service.py<br/>run_projection, load_scenario_inputs,<br/>refresh_stale_projections"]
S_breakdown["breakdown.py<br/>compute_breakdown"]
S_planning_chart["planning_chart.py<br/>refresh_planning_chart"] S_planning_chart["planning_chart.py<br/>refresh_planning_chart"]
S_admin["admin.py<br/>change_password, apply_rules_retroactively"] S_admin["admin.py<br/>change_password, apply_rules_retroactively"]
S_doublecount["double_count.py<br/>all_conflicts, conflicts_for_recurring,<br/>conflicts_for_loan"] S_doublecount["double_count.py<br/>all_conflicts, conflicts_for_recurring,<br/>conflicts_for_loan"]
@@ -50,6 +51,8 @@ flowchart TD
E_recurrence["recurrence.py<br/>occurrences"] E_recurrence["recurrence.py<br/>occurrences"]
E_projection["projection.py<br/>project"] E_projection["projection.py<br/>project"]
E_scenario["scenario.py<br/>build_cashflows (PlainRecurring/<br/>PlainPlanned/PlainModifier)"] E_scenario["scenario.py<br/>build_cashflows (PlainRecurring/<br/>PlainPlanned/PlainModifier)"]
E_breakdown["breakdown.py<br/>breakdown_rows, summarize,<br/>category_rows, share"]
E_pie["pie.py<br/>pie_slices"]
end end
subgraph PARSERS["parsers/"] subgraph PARSERS["parsers/"]
@@ -94,6 +97,9 @@ flowchart TD
R_scenarios --> N_reserved R_scenarios --> N_reserved
R_categories --> N_reserved R_categories --> N_reserved
R_gui --> S_doublecount R_gui --> S_doublecount
R_gui --> S_breakdown
R_gui --> E_breakdown
R_gui --> E_pie
R_admin --> S_admin R_admin --> S_admin
ROUTERS -.-> M_tables ROUTERS -.-> M_tables
@@ -106,6 +112,10 @@ flowchart TD
S_projection --> E_projection S_projection --> E_projection
S_projection --> E_scenario S_projection --> E_scenario
S_projection --> S_balances S_projection --> S_balances
S_breakdown --> S_projection
S_breakdown --> E_breakdown
S_breakdown --> E_loans
S_breakdown --> S_doublecount
S_planning_chart --> E_loans S_planning_chart --> E_loans
S_planning_chart --> E_recurrence S_planning_chart --> E_recurrence
S_planning_chart --> M_tables S_planning_chart --> M_tables
@@ -113,6 +123,8 @@ flowchart TD
E_scenario --> E_loans E_scenario --> E_loans
E_scenario --> E_recurrence E_scenario --> E_recurrence
E_breakdown --> E_scenario
E_breakdown --> E_recurrence
P_registry --> P_vr P_registry --> P_vr
P_registry --> P_hvb P_registry --> P_hvb
@@ -304,12 +316,15 @@ nur noch über `rollback` (löscht Statement + Transaktionen komplett) zu
entfernen, nicht mehr über `DELETE /api/imports/{id}` (nur für Entwürfe und Fehl-Importe). entfernen, nicht mehr über `DELETE /api/imports/{id}` (nur für Entwürfe und Fehl-Importe).
**Szenario-Engine ist rein.** `app/engine/*` (`loans.py`, `recurrence.py`, **Szenario-Engine ist rein.** `app/engine/*` (`loans.py`, `recurrence.py`,
`projection.py`, `scenario.py`) hat **keinen** Datenbank- oder `projection.py`, `scenario.py`, `breakdown.py`, `pie.py`) hat **keinen**
Request-Zugriff — nur `scenario.py` importiert innerhalb der Engine von Datenbank- oder Request-Zugriff — `scenario.py` und `breakdown.py`
`loans.py`/`recurrence.py`, sonst keine Abhängigkeiten auf `app.*`. Ein-/ importieren innerhalb der Engine (`loans.py`/`recurrence.py`/`scenario.py`),
sonst keine Abhängigkeiten auf `app.*`. Ein-/
Ausgaben sind einfache Dataclasses (`Installment`, `PlainRecurring`, Ausgaben sind einfache Dataclasses (`Installment`, `PlainRecurring`,
`PlainPlanned`, `PlainModifier`, `Projection`). `services/projection_service.py` `PlainPlanned`, `PlainModifier`, `Projection`). `services/projection_service.py`
ist die einzige Brücke: es lädt ORM-Objekte (`Loan`, `RecurringItem`, ist die einzige Brücke: `load_scenario_inputs(session, scenario)` ist die
gemeinsame Ladefunktion (`None` = System-Plan, ohne Szenario-Änderungen)
und lädt ORM-Objekte (`Loan`, `RecurringItem`,
`PlannedItem`, `Scenario`, `ScenarioModifier`), baut daraus die Plain-Objekte `PlannedItem`, `Scenario`, `ScenarioModifier`), baut daraus die Plain-Objekte
und ruft `engine.scenario.build_cashflows` → `engine.projection.project`. und ruft `engine.scenario.build_cashflows` → `engine.projection.project`.
@@ -352,6 +367,26 @@ Formular (`data-doppelzaehlung-bestaetigt="1"`) und sendet es erneut, die
einen dauerhaften ⚠-Hinweis je betroffenem Posten/Kredit in `planning.html`, einen dauerhaften ⚠-Hinweis je betroffenem Posten/Kredit in `planning.html`,
solange der Konflikt fortbesteht. solange der Konflikt fortbesteht.
**Aufteilung (Ausbaustufe 13).** `/aufteilung` (`routers/gui.py::aufteilung_page`,
GET-Formular, die Auswahl steht in der URL) zeigt für einen Monat die Kosten
je Kategorie aus reiner Planung, nie aus Ist-Buchungen.
`services/breakdown.py::compute_breakdown` lädt die Planung über
`load_scenario_inputs`; `PLAN_NAME` („System-Plan") entspricht
`load_scenario_inputs(session, None)`, ein Szenario wird mit seinen
Änderungen und Krediten gerechnet. Die Rechenregeln stehen in
`engine/breakdown.py` (`breakdown_rows`, `summarize`, `category_rows`,
`share`): Fixposten werden per `recurrence.STEP` auf den Monat normalisiert
(vierteljährlich ÷ 3, jährlich ÷ 12), Einmalposten und Kreditraten zählen
voll im Fälligkeitsmonat, eine Kredit-Auszahlung nie als Einkunft; `FREI`
(„Frei verfügbar") ist der Rest der Einkünfte. Das Tortendiagramm entsteht
serverseitig in `engine/pie.py::pie_slices` (`MIN_SHARE` 3 %, `MAX_NAMED` 8,
Sammelsegment `UEBRIGE`). Drill-down: `GET /aufteilung/kategorie`
(`aufteilung_kategorie_fragment`) liefert die Posten der Kategorie und
tauscht die Haupttorte per `hx-swap-oob` aus (gewähltes Segment markiert);
`sammel=1` wählt die Sammelgruppe „Übrige". Der Doppelzählungs-Hinweis nutzt
`services/double_count.py::all_conflicts`. Keine Tabelle, keine View,
Grafana unberührt.
**Abgeleitete Tabellen + Advisory-Locks (Ausbaustufe 11).** Zwei Datenstände **Abgeleitete Tabellen + Advisory-Locks (Ausbaustufe 11).** Zwei Datenstände
werden bei GUI-Seitenaufrufen automatisch aufgefrischt, nie beim reinen werden bei GUI-Seitenaufrufen automatisch aufgefrischt, nie beim reinen
API-Zugriff (`routers/gui.py::_safe_refresh`, fehlerresistent — ein Fehler API-Zugriff (`routers/gui.py::_safe_refresh`, fehlerresistent — ein Fehler
+1 -1
View File
@@ -1 +1 @@
0.12.0 0.13.0
+18
View File
@@ -91,6 +91,24 @@
Passwort wie hier anmelden, dann erscheint das Dashboard auch in der Passwort wie hier anmelden, dann erscheint das Dashboard auch in der
Übersicht. Übersicht.
</li> </li>
<li>
<strong>Aufteilung:</strong> zeigt für einen Monat, welche monatlichen
Kosten je Kategorie anfallen und wie viel der Einkünfte frei verfügbar
bleibt. Grundlage ist immer die Planung (Fixposten, Einmalposten,
Kredite), nie die tatsächlichen Buchungen: „System-Plan" rechnet mit
allen Posten und Krediten ohne Szenario-Änderungen und ist für jeden
Monat wählbar; ein Szenario (mit seinen Änderungen und Krediten) erst ab
dem laufenden Monat. Fixposten werden auf den Monat umgerechnet
(vierteljährlich ÷ 3, jährlich ÷ 12), Einmalposten und Kreditraten zählen
voll im Monat ihrer Fälligkeit, eine Kredit-Auszahlung zählt nicht als
Einkunft. Die Häkchen „Fixposten", „Einmalposten", „Kredite" gelten für
Einnahmen und Ausgaben. Das Tortendiagramm zeigt die Einkünfte (Kosten je
Kategorie plus „Frei verfügbar"); übersteigen die Kosten die Einkünfte,
zeigt es die Gesamtkosten. Kleine Kategorien (unter 3 %, bzw. ab der
neunten) sind zu „Übrige" zusammengefasst – höchstens 8 benannte
Kategorien plus „Übrige" und „Frei verfügbar". Ein Klick auf ein Segment oder
einen Legendeneintrag zeigt darunter die Posten dieser Kategorie.
</li>
</ul> </ul>
<p> <p>