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.
+