chore: Release v0.14.0 (Szenario-Zeitraum, Neuer Betrag, Szenario-Zahlungen)

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-09-30 13:30:02 +02:00
co-authored by Claude Opus 5.5
parent 30330801cc
commit eb68781a1d
3 changed files with 43 additions and 15 deletions
+24 -6
View File
@@ -28,7 +28,7 @@ flowchart TD
R_categories["categories.py<br/>list_categories, create_category_rule"] R_categories["categories.py<br/>list_categories, create_category_rule"]
R_imports["imports.py<br/>upload, scan_inbox, preview,<br/>confirm, rollback"] R_imports["imports.py<br/>upload, scan_inbox, preview,<br/>confirm, rollback"]
R_planning["planning.py<br/>list_recurring, list_planned,<br/>list_loans, recurring_suggestions"] R_planning["planning.py<br/>list_recurring, list_planned,<br/>list_loans, recurring_suggestions"]
R_scenarios["scenarios.py<br/>create_scenario, add_modifier,<br/>project_scenario"] R_scenarios["scenarios.py<br/>create_scenario, add_modifier,<br/>add_scenario_recurring,<br/>project_scenario"]
R_admin["admin.py<br/>admin_page, admin_change_password,<br/>apply_category_rules"] R_admin["admin.py<br/>admin_page, admin_change_password,<br/>apply_category_rules"]
end end
@@ -47,10 +47,10 @@ flowchart TD
N_reserved["app/reserved.py<br/>check_reserved_name"] N_reserved["app/reserved.py<br/>check_reserved_name"]
subgraph ENGINE["engine/ (rein, kein DB-Zugriff)"] subgraph ENGINE["engine/ (rein, kein DB-Zugriff)"]
E_loans["loans.py<br/>loan_schedule, annuity_payment, add_months"] E_loans["loans.py<br/>loan_schedule, schedule_for,<br/>annuity_payment, add_months"]
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, modified_amount<br/>(Stichtag on, gilt_am)<br/>PlainRecurring/PlainPlanned/<br/>PlainModifier"]
E_breakdown["breakdown.py<br/>breakdown_rows, summarize,<br/>category_rows, share"] E_breakdown["breakdown.py<br/>breakdown_rows, summarize,<br/>category_rows, share"]
E_pie["pie.py<br/>pie_slices"] E_pie["pie.py<br/>pie_slices"]
end end
@@ -67,9 +67,9 @@ flowchart TD
end end
subgraph MODELS["models/ + alembic/"] subgraph MODELS["models/ + alembic/"]
M_tables["tables.py<br/>Account, Statement, Transaction,<br/>RecurringItem, PlannedItem, Loan,<br/>Scenario, ProjectionResult, ..."] M_tables["tables.py<br/>Account, Statement, Transaction,<br/>RecurringItem, PlannedItem, Loan,<br/>Scenario, ScenarioRecurringItem,<br/>ProjectionResult, ..."]
M_views["views.py — create_views()<br/>v_balance_history, v_balance_total,<br/>v_monthly_by_category, v_projection"] M_views["views.py — create_views()<br/>v_balance_history, v_balance_total,<br/>v_monthly_by_category, v_projection"]
M_alembic["alembic/versions<br/>b2b1f5a18a74_initial_schema<br/>1ef6a356f028_konto_anker"] M_alembic["alembic/versions<br/>b2b1f5a18a74_initial_schema<br/>1ef6a356f028_konto_anker<br/>e7f1a9c3b214_szenario_zeitraum"]
end end
R_gui --> T_pages R_gui --> T_pages
@@ -326,9 +326,27 @@ Ausgaben sind einfache Dataclasses (`Installment`, `PlainRecurring`,
ist die einzige Brücke: `load_scenario_inputs(session, scenario)` ist die ist die einzige Brücke: `load_scenario_inputs(session, scenario)` ist die
gemeinsame Ladefunktion (`None` = System-Plan, ohne Szenario-Änderungen) gemeinsame Ladefunktion (`None` = System-Plan, ohne Szenario-Änderungen)
und lädt ORM-Objekte (`Loan`, `RecurringItem`, und lädt ORM-Objekte (`Loan`, `RecurringItem`,
`PlannedItem`, `Scenario`, `ScenarioModifier`), baut daraus die Plain-Objekte `PlannedItem`, `Scenario`, `ScenarioModifier`, `ScenarioRecurringItem` — Letztere
als `PlainRecurring` mit `id=None`), baut daraus die Plain-Objekte
und ruft `engine.scenario.build_cashflows` → `engine.projection.project`. und ruft `engine.scenario.build_cashflows` → `engine.projection.project`.
**Szenario-Einträge mit Zeitraum (Ausbaustufe 14).** `ScenarioModifier.start_date`/
`end_date` sind „Gültig ab/bis" (inklusiv, leer = unbegrenzt; bei Art `ende`
zählt nur `end_date` als Enddatum). Neue Art `set` („Neuer Betrag", nur für
Posten) ersetzt den Betrag. `engine.scenario.modified_amount(..., on=)` wertet
die Einträge mit einem Stichtag je Fälligkeit aus — `build_cashflows` nimmt
jedes Fälligkeitsdatum, `engine.breakdown.breakdown_rows` über
`engine.recurrence.due_in_month` das Fälligkeitsdatum im Monat; `gilt_am`
prüft den Zeitraum, mehrere treffende Einträge wirken in Anlage-Reihenfolge.
`ScenarioRecurringItem` (Tabelle `scenario_recurring_items`, API
`/api/scenarios/{id}/recurring`) sind wiederkehrende Zahlungen nur in einem
Szenario: sie zählen unabhängig von `include_recurring` und werden von
Posten-Einträgen nie getroffen (`PlainRecurring.id=None`).
`ScenarioPlannedItem.category_id` ordnet Szenario-Einmalzahlungen einer
Kategorie zu. Die Szenarien-Seite (`szenarien.html`) nutzt dafür
`static/szenarien.js` (`window.FBSzenario`). Migration:
`e7f1a9c3b214_szenario_zeitraum`.
**`v_projection` (Ausbaustufe 11) — System-Ist + Szenarien ab heute.** **`v_projection` (Ausbaustufe 11) — System-Ist + Szenarien ab heute.**
`models/views.py::VIEWS["v_projection"]` liefert für den Grafana-Szenario- `models/views.py::VIEWS["v_projection"]` liefert für den Grafana-Szenario-
Vergleich drei Teile in einer Tabelle: die Ist-Gesamtsaldo-Serie unter dem Vergleich drei Teile in einer Tabelle: die Ist-Gesamtsaldo-Serie unter dem
+1 -1
View File
@@ -1 +1 @@
0.13.1 0.14.0
+18 -8
View File
@@ -67,16 +67,26 @@
<li> <li>
<strong>Szenarien:</strong> eine "Was-wäre-wenn"-Rechnung. Basis = alle <strong>Szenarien:</strong> eine "Was-wäre-wenn"-Rechnung. Basis = alle
wiederkehrenden Posten + Einmalposten. Varianten entstehen durch Zuordnen wiederkehrenden Posten + Einmalposten. Varianten entstehen durch Zuordnen
von Krediten und Modifikatoren (Kategorie oder Posten prozentual/absolut von Krediten und Einträgen (Kategorie oder Posten prozentual oder um einen Betrag
kürzen oder ganz streichen). "Durchrechnen" liefert: tiefster Kontostand kürzen, auf einen neuen Betrag setzen oder ganz streichen – auch nur für
einen Zeitraum). "Durchrechnen" liefert: tiefster Kontostand
mit Datum, erstes Datum unter 0 und unter der Warnschwelle. mit Datum, erstes Datum unter 0 und unter der Warnschwelle.
Varianten wie „Best Case“ pflegst du in der Tabelle „Einträge“ des Varianten wie „Best Case“ pflegst du in der Tabelle „Einträge“ des
Szenarios: Art „Ende“ lässt einen wiederkehrenden Posten (z. B. eine Szenarios. Das Formular „Neue Änderung an Bestehendem“ ändert
auslaufende Miete) zu einem Datum enden, ohne den Posten selbst zu vorhandene Posten oder Kategorien: „Prozent“ und „Kürzung (€)“ kürzen
ändern; „Prozent“/„Absolut“ kürzen Beträge; „Entfällt“ blendet sie ganz Beträge, „Neuer Betrag“ ersetzt den Betrag eines Postens (mit
aus; „Einmalzahlung“ ergänzt einen einmaligen Zu- oder Abfluss, der nur Vorzeichen, z. B. -250,00), „Entfällt“ blendet ihn aus, „Ende“ lässt
in diesem Szenario zählt. Jeder Eintrag ist über „Bearbeiten“ direkt einen wiederkehrenden Posten zu einem Datum enden. Mit „Gültig ab“ und
änderbar. Ein Szenario lässt sich über „Löschen“ mit allen Einträgen „Gültig bis“ wirken Prozent, Kürzung, Neuer Betrag und Entfällt nur in
diesem Zeitraum (Grenzen einschließlich, leer = unbegrenzt). Beispiel:
ein Posten wird mit „Prozent 50 ab 16.10.“ halbiert – ist er am 14.
fällig, zählt er im Oktober noch voll, ab November zur Hälfte. Das
Formular „Neue Zahlung nur in diesem Szenario“ ergänzt eine
Einmalzahlung oder eine wiederkehrende Zahlung (Rhythmus,
Fälligkeitstag, Start/Ende, optional mit Kategorie), die nur in diesem
Szenario zählt – auch wenn „Wiederkehrende Posten einschließen“ bzw.
„Einmalposten einschließen“ aus ist. Jeder Eintrag ist über
„Bearbeiten“ direkt änderbar. Ein Szenario lässt sich über „Löschen“ mit allen Einträgen
wieder entfernen. Bereits durchgerechnete Szenarien werden automatisch wieder entfernen. Bereits durchgerechnete Szenarien werden automatisch
mit dem Standard-Horizont neu gerechnet, sobald ihre Projektion nicht mit dem Standard-Horizont neu gerechnet, sobald ihre Projektion nicht
mehr bei heute beginnt (z. B. nach ein paar Tagen Inaktivität) – das mehr bei heute beginnt (z. B. nach ein paar Tagen Inaktivität) – das