From b32f28fff5526ca91e44d43aea5134ca3bddaf34df4fd16d68198f7245cf4181 Mon Sep 17 00:00:00 2001 From: wlfb Date: Tue, 29 Sep 2026 16:49:50 +0200 Subject: [PATCH] chore: Release v0.13.0 (Seite Aufteilung) Co-Authored-By: Claude Opus 5.5 (1M context) --- docs/ARCHITEKTUR.md | 49 +++++++++++++++++++++++++++----- finance/VERSION | 2 +- finance/app/templates/hilfe.html | 18 ++++++++++++ 3 files changed, 61 insertions(+), 8 deletions(-) diff --git a/docs/ARCHITEKTUR.md b/docs/ARCHITEKTUR.md index 5cec9d8..e3b202b 100644 --- a/docs/ARCHITEKTUR.md +++ b/docs/ARCHITEKTUR.md @@ -18,11 +18,11 @@ direkt aus `routers/gui.py`. ```mermaid flowchart TD subgraph TPL["templates (Jinja2, app/templates/)"] - T_pages["base.html, login.html, index.html
transactions.html, salden.html
import.html, planning.html
admin.html, hilfe.html"] + T_pages["base.html, login.html, index.html
transactions.html, salden.html
import.html, planning.html
admin.html, hilfe.html
aufteilung.html"] end subgraph ROUTERS["routers/"] - R_gui["gui.py
gui_session, index, buchungen,
salden_page, import_page, planung_page"] + R_gui["gui.py
gui_session, index, buchungen,
salden_page, import_page, planung_page,
aufteilung_page, aufteilung_kategorie_fragment"] R_accounts["accounts.py
list_accounts, create_account,
patch_account"] R_transactions["transactions.py
list_transactions, create_transaction,
patch_transaction, count_transactions"] R_categories["categories.py
list_categories, create_category_rule"] @@ -37,7 +37,8 @@ flowchart TD S_balances["balances.py
account_balance(at), total_balance"] S_categorize["categorize.py
apply_rules"] S_suggestions["suggestions.py
suggest_recurring"] - S_projection["projection_service.py
run_projection, refresh_stale_projections"] + S_projection["projection_service.py
run_projection, load_scenario_inputs,
refresh_stale_projections"] + S_breakdown["breakdown.py
compute_breakdown"] S_planning_chart["planning_chart.py
refresh_planning_chart"] S_admin["admin.py
change_password, apply_rules_retroactively"] S_doublecount["double_count.py
all_conflicts, conflicts_for_recurring,
conflicts_for_loan"] @@ -50,6 +51,8 @@ flowchart TD E_recurrence["recurrence.py
occurrences"] E_projection["projection.py
project"] E_scenario["scenario.py
build_cashflows (PlainRecurring/
PlainPlanned/PlainModifier)"] + E_breakdown["breakdown.py
breakdown_rows, summarize,
category_rows, share"] + E_pie["pie.py
pie_slices"] end subgraph PARSERS["parsers/"] @@ -94,6 +97,9 @@ flowchart TD R_scenarios --> N_reserved R_categories --> N_reserved R_gui --> S_doublecount + R_gui --> S_breakdown + R_gui --> E_breakdown + R_gui --> E_pie R_admin --> S_admin ROUTERS -.-> M_tables @@ -106,6 +112,10 @@ flowchart TD S_projection --> E_projection S_projection --> E_scenario 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_recurrence S_planning_chart --> M_tables @@ -113,6 +123,8 @@ flowchart TD E_scenario --> E_loans E_scenario --> E_recurrence + E_breakdown --> E_scenario + E_breakdown --> E_recurrence P_registry --> P_vr 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). **Szenario-Engine ist rein.** `app/engine/*` (`loans.py`, `recurrence.py`, -`projection.py`, `scenario.py`) hat **keinen** Datenbank- oder -Request-Zugriff — nur `scenario.py` importiert innerhalb der Engine von -`loans.py`/`recurrence.py`, sonst keine Abhängigkeiten auf `app.*`. Ein-/ +`projection.py`, `scenario.py`, `breakdown.py`, `pie.py`) hat **keinen** +Datenbank- oder Request-Zugriff — `scenario.py` und `breakdown.py` +importieren innerhalb der Engine (`loans.py`/`recurrence.py`/`scenario.py`), +sonst keine Abhängigkeiten auf `app.*`. Ein-/ Ausgaben sind einfache Dataclasses (`Installment`, `PlainRecurring`, `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 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`, 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 werden bei GUI-Seitenaufrufen automatisch aufgefrischt, nie beim reinen API-Zugriff (`routers/gui.py::_safe_refresh`, fehlerresistent — ein Fehler diff --git a/finance/VERSION b/finance/VERSION index 15d9519..5976b2d 100644 --- a/finance/VERSION +++ b/finance/VERSION @@ -1 +1 @@ -0.12.0 +0.13.0 diff --git a/finance/app/templates/hilfe.html b/finance/app/templates/hilfe.html index f273850..1d9320f 100644 --- a/finance/app/templates/hilfe.html +++ b/finance/app/templates/hilfe.html @@ -91,6 +91,24 @@ Passwort wie hier anmelden, dann erscheint das Dashboard auch in der Übersicht. +
  • + Aufteilung: 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. +