Navigation auf Plan-Ebene und gespeicherte Analysen (Roadmap-Redesign)
Deploy App / deploy (push) Successful in 1m45s

Sidebar zweistufig: pro Plan die Unterpunkte Szenarien / Effektive Werte /
Analysen; Klick auf Plan-Name oeffnet ein Plan-Dashboard.

Plan-Dashboard: Kennzahlen, gerechnete Werte ausdruecklich "laut
Basisszenario", Ist-Abweichung falls erfasst.

Szenario-Liste: Version, Elementzahl, Endvermoegen, Ruinalter + Aktionen
Historie und Matrix. Baum in der Sidebar bleibt.

Analysen: vier umklappende Kacheln (auch per Antippen). Grafiken oeffnen
neu mit Auswahl EINER Grafik. Szenario-Vergleich zu den Grafiken,
CSV-Export auf die Matrix.

Gespeicherte Analysen: Grafik/MC/Einflussfaktoren als ZAHLEN einfrieren
(read-only, nichts wird neu gerechnet) -- druckfaehig fuer den spaeteren
PDF-Bericht, ohne finalWealthSorted. Einheitliche generische Ergebnisform.

Neue Tabelle SavedAnalysis, Endpunkte /analyses und /dashboard, Module
analyses.ts, Komponenten PlanViews/SavedAnalysisView/SaveAnalysisButton.
Kein Eingriff in den Rechenkern. Spezifikation 0.24 (3.10 und 9.30 neu).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-07-20 22:21:02 +02:00
parent ce5f83823f
commit fb70781e5b
19 changed files with 1720 additions and 113 deletions
+116 -3
View File
@@ -4,7 +4,7 @@
| | |
|---|---|
| **Dokument** | Funktionale und Technische Spezifikation FPT |
| **Version** | 0.23 |
| **Version** | 0.24 |
| **Datum** | 2026-07-18 |
| **Status** | Lebendes Dokument |
| **Codestand** | Arbeitsstand nach `1d046e9` inkl. zwei Monte-Carlo-Fragestellungen (Branch `main`) |
@@ -17,6 +17,7 @@
| Version | Datum | Autor | Änderung |
|---|---|---|---|
| 0.24 | 2026-07-20 | Claude (Opus 4.8) | **Navigation auf Plan-Ebene und gespeicherte Analysen** (neues Kapitel 3.10). Die Seitenleiste ist neu zweistufig: Unter jedem Plan liegen die Unterpunkte **Szenarien**, **Effektive Werte** und **Analysen**; ein Klick auf den Plan-Namen öffnet ein **Plan-Dashboard** (Kennzahlen Haushaltsdaten direkt, gerechnete Werte ausdrücklich «laut Basisszenario», dazu die Ist-Abweichung, falls erfasst). Die **Szenario-Liste** zeigt je Szenario Version, Elementzahl, Endvermögen und Ruinalter mit den Aktionen Historie und Matrix; das Basisszenario ist hervorgehoben, der Szenario-Baum in der Seitenleiste bleibt daneben erhalten. Die **Analysen-Ansicht** bietet vier umklappende Kacheln (Grafiken, Live-Simulation, Monte-Carlo, Einflussfaktoren Umklappen auch per Antippen für Touch). **Grafiken** öffnen neu nicht mehr alle drei Diagramme, sondern lassen zuerst **eines** wählen; der Szenario-Vergleich zieht mit zu den Grafiken, der CSV-Export auf die Matrix. **Gespeicherte Analysen:** Jede Grafik, MC-Simulation und Einflussfaktoren-Berechnung kann festgehalten werden als **Zahlen, nicht als Bild** (read-only, es wird nichts neu gerechnet). Das hält den Datensatz klein und macht ihn druckfähig: Der spätere PDF-Bericht (Roadmap Nr. 11) zeichnet daraus vektoriell in Druckauflösung, was ein Screenshot nicht könnte. Bewusst **nicht** gespeichert wird `finalWealthSorted` (megabyteweise). Jedes Werkzeug schreibt sein Ergebnis in dieselbe generische Form, sodass eine Nur-Lese-Ansicht genügt. Neue Tabelle `SavedAnalysis`, neue Endpunkte unter `/api/plans/<id>/analyses` und `/dashboard`; neue Komponenten `PlanViews`, `SavedAnalysisView`, `SaveAnalysisButton`, neues Modul `analyses.ts`. Kein Eingriff in den Rechenkern; Testbestand 212 (Migration um `SavedAnalysis` erweitert). |
| 0.23 | 2026-07-20 | Claude (Opus 4.8) | **V7: Der Haushalt liegt am Plan, die Annahmen am Szenario.** **Haushaltsform**, **Personen** (Name, Alter) und **Planstartjahr** wandern vom Szenario auf den **Plan**; das **Pensionsalter** bleibt szenario-eigen (es ist der Kern jedes Früh-/Spätpensionierungs-Szenarios), ebenso Inflation und Cash-Anfangswert. Begründung: Diese Angaben beschreiben den Haushalt, nicht eine Planungsvariante unterscheiden sie sich, ist es ein anderer **Plan**, kein anderes Szenario. Neue Tabelle `PlanPerson` (Rolle, Name, Alter je Plan); `Person` behält nur noch Rolle und Pensionsalter; `Plan` bekommt `householdType` und `startYear`. **Der Rechenkern bleibt unberührt:** `toPlanInput()` fügt Plan- und Szenario-Ebene wieder zu einem unveränderten `PlanInput` zusammen, die 43 Golden Tests laufen durch. **Nebeneffekt, der ein reales Problem löst:** Weil das Startjahr nun plan-weit ist, landet ein erfasster Ist-Satz für 2031 in **allen** Szenarien zwingend auf demselben Planjahr vorher war das nicht garantiert. Das **Wiederherstellen einer Szenario-Version** setzt folgerichtig nur noch das Szenario-Eigene zurück; plan-weite Angaben über eine Version *eines* Szenarios zu überschreiben, hätte die übrigen stillschweigend mitverändert. Der Profil-Dialog kennzeichnet neu je Feld, ob es **plan-weit** oder **nur dieses Szenario** gilt. Die Migration übernimmt die Werte aus dem **Basisszenario**; zwei neue Tests spielen dafür echte V6-Daten ein und prüfen die Übernahme inkl. abweichender Nebenszenarien (210 → 212). |
| 0.22 | 2026-07-20 | Claude (Opus 4.8) | **Fehlerbehebung: Cash-Vorbelegung im Ist-Wizard.** Der Wizard für die effektiven Werte zeigte als geplanten Cash-Bestand den Stand am **Phasenende** statt am gewählten Stichtag -- in einer Phase von 2026 bis 2036 also für 2031 den Wert von 2036. Ursache: Der Dialog las `cashBridge.cashEnd`, weil `computePlan` den Cash-Bestand bisher nur **je Phase** auswies. `YearPoint` trägt neu ein Feld `cash` (Stand am Jahresende), analog zu `wealthNominal`; der Wizard liest daraus. Die Vorbelegung der ELEMENTE war nie betroffen -- die stammte schon immer aus dem Jahresverlauf. Zwei Regressionstests decken den gemeldeten Fall ab (208 -> 210). Rein additiv, die 43 Golden Tests laufen unverändert. |
| 0.21 | 2026-07-20 | Claude (Opus 4.8) | **Effektive Werte / Plan-Ist-Vergleich** (Roadmap Nr. 5, neue Kapitel 3.9 und 9.29). Macht aus dem Planer ein Monitoring-Werkzeug. Neuer Knopf **«Effektive Werte»** auf Plan-Ebene: Liste der Erfassungen plus Wizard in zwei Schritten (Stichtag, dann alle Elemente **aller** Szenarien inkl. **Cash**, Einkommen und Ausgaben, vorbelegt mit dem Planwert für dieses Jahr). Ein Ist-Satz hängt am **Plan**, nicht am Szenario die Wirklichkeit ist dieselbe, egal wogegen man sie hält; die Zuordnung läuft über die Herkunfts-Kette `sourceElementId`. Das exakte Datum steht in Liste und Zeitachse, für die Rechnung zählt nur die **Jahreszahl**. **Zweiter Rechenlauf:** `computePlan` nimmt neu `{ actuals }`; die Werte schnappen in **jedem** erfassten Jahr auf die Realität und laufen von dort planmässig weiter (Lücken fallen auf die Plandaten zurück). Ohne die Option verhält sich die Funktion exakt wie bisher die 43 Golden Tests laufen unverändert. Der Sprung wird als eigene Brückenposition `actualsCorrection` geführt (Vermögens- **und** Cash-Brücke): Eine Planabweichung ist keine Rendite, und ohne diese Zeile ginge die Zerlegung im Ist-Jahr nicht mehr auf. **Matrix:** zweiter Umschalter «Plan» / «Effektiv»; im Ist-Modus steht neben dem Wert die **Abweichung** zum Plan, farbig bewusst kein «beide», das wären mit nominal/real acht Zahlen je Zelle (Begründung 9.29). **Zeitachse:** Marker je erfasstem Jahr, der jüngste farbig, ältere blass. **Alle vier Analysewerkzeuge** erhalten eine einheitliche Leiste (nominal/real als **Einfach**auswahl, Plan/Effektiv); im Vermögensverlauf kommt die Planlinie **gestrichelt** als Referenz dazu, max. vier Serien. **Monte-Carlo:** Der Zielbetrag dreht mit real/nominal mit und wird entsprechend beschriftet; eine Zeile weist aus, ab welchem Jahr simuliert wird die Jahre davor sind durch Ist-Werte belegt und werden nicht gewürfelt. Das Startjahr ist **abgeleitet, nicht eingebbar**: Ein frei gesetztes Jahr würde Jahre als sicher behandeln, die nie erfasst wurden. Ein Ist-Satz erzeugt **keine** Szenario-Version er ist eine Beobachtung, keine Planänderung. Neue Tabelle `ActualsSet` (Migration gegen echtes Postgres verifiziert), neue Endpunkte unter `/api/plans/<id>/actuals`, neue Module `actuals.ts` und `dataview.ts`; 27 Tests ergänzt (181 → 208). |
@@ -1480,6 +1481,70 @@ entfernt ihn ersatzlos; der Plan bleibt unberührt. Die beiden Historien bleiben
Referenz: `src/lib/actuals.ts`, `src/lib/dataview.ts`, `src/components/ActualsDialog.tsx`,
`src/components/AnalysisControls.tsx`.
## 3.10 Navigation auf Plan-Ebene und gespeicherte Analysen
Die Seitenleiste ist zweistufig: Unter jedem **Plan** liegen drei leicht eingezogene
Unterpunkte. Ein Klick auf den **Plan-Namen** öffnet dessen Dashboard.
| Ort | Führt zu |
|---|---|
| Plan-Name | **Plan-Dashboard** (Kennzahlen) |
| Szenarien | **Szenario-Liste** darunter bleibt der Szenario-Baum, dessen Einträge direkt in die Matrix führen |
| Effektive Werte | Liste + Wizard der Ist-Werte ([3.9](#39-effektive-werte-plan-ist-vergleich)) |
| Analysen | Vier Werkzeug-Kacheln + Liste gespeicherter Analysen |
### 3.10.1 Plan- vs. Szenario-Ebene der Kennzahlen
Seit V7 ([9.30](#930-warum-der-haushalt-am-plan-hängt)) liegen Haushaltsform, Personen und
Startjahr am Plan; Endvermögen, Ruinalter, Phasen und Elemente sind dagegen **szenario-eigen**.
Das Plan-Dashboard zeigt deshalb die Haushaltsdaten direkt, alle gerechneten Kennzahlen aber
ausdrücklich als **«laut Basisszenario»** das Basisszenario ist der kanonische Vertreter.
Falls Ist-Werte erfasst sind, weist es zusätzlich die **Abweichung** des Endvermögens gegenüber
dem Plan aus.
Die **Szenario-Liste** zeigt je Szenario Name, aktuelle Hauptversion, Anzahl Elemente, das
Endvermögen und ob das Kapital reicht; das Basisszenario ist farblich hervorgehoben, und die
Herkunft (aus welchem Szenario kopiert) steht darunter. Zwei Aktionen je Zeile: **Historie**
und **Matrix**.
### 3.10.2 Analysen: vier Kacheln
Die Werkzeuge **Grafiken**, **Live-Simulation**, **Monte-Carlo** und **Einflussfaktoren** liegen
als Kacheln vor, die beim Darüberfahren oder Antippen (Touch hat kein Hover) umklappen und
einen Erklärtext zeigen. Ein Klick startet das jeweilige Werkzeug auf dem Basisszenario; Szenario
und Version lassen sich darin weiterhin umstellen.
**Grafiken** öffnen neu **nicht mehr alle drei Diagramme**, sondern lassen zuerst **eine** wählen
(Vermögensverlauf / Einkommen vs. Ausgaben / Vermögensaufteilung), dazu wie bisher
nominal/real und Plan/effektiv. Der **Szenario-Vergleich** wandert mit zu den Grafiken; der
**CSV-Export** zieht dorthin, wo er hingehört auf die **Matrix**.
### 3.10.3 Gespeicherte Analysen
Jede Grafik, Monte-Carlo-Simulation und Einflussfaktoren-Berechnung kann **gespeichert** werden
(die Live-Simulation vorerst nicht sie ist bewusst flüchtig). Festgehalten werden **Eingaben
und Ergebnis als Zahlen**, read-only: Beim Öffnen wird **nichts neu gerechnet**, die
gespeicherten Werte werden nur gezeichnet.
**Zahlen statt Bild** und zwar bewusst: Ein Bildschirm-Abbild wäre im Druck unscharf (300 dpi
gegen 96), im Seitenformat fix und im Dunkelmodus falsch eingefärbt. Aus Zahlen zeichnet der
spätere **PDF-Bericht** (Roadmap Nr. 11) die Grafik **vektoriell und in Druckauflösung** neu
und braucht die Zahlen für Tabellen und Fliesstext ohnehin. Bewusst **nicht** gespeichert wird
`finalWealthSorted` aus der Monte-Carlo-Simulation (ein Eintrag je Lauf, megabyteweise); für die
Anzeige genügen die abgelesenen Wahrscheinlichkeiten und die Bänder.
Jedes Werkzeug schreibt sein Ergebnis in **dieselbe generische Form** (Parameter, Kernzahlen,
Tabelle, Grafik). Dadurch braucht die Nur-Lese-Ansicht nur einen Renderer, und der PDF-Bericht
findet überall dieselbe Struktur vor. Der Name wird automatisch vorgeschlagen
(«Typ · Szenario Version · Datum»), ist aber überschreibbar.
Die Liste je Plan zeigt Typ, Zeitpunkt, Szenario/Version, nominal/real und Plan/effektiv. Ein
gespeicherter Datensatz ist eine **Momentaufnahme**, keine Planänderung: Er erzeugt keine
Version, und Löschen entfernt ihn ersatzlos.
Referenz: `src/components/PlanViews.tsx`, `src/components/SavedAnalysisView.tsx`,
`src/components/SaveAnalysisButton.tsx`, `src/lib/analyses.ts`.
---
# 4. Berechnungsmodell
@@ -2682,7 +2747,7 @@ Referenz: `src/lib/livesim.ts`, `src/lib/sensitivity.ts` (`applyElementDriver`,
FPT/
├── prisma/
│ ├── schema.prisma Datenmodell
│ └── migrations/ 15 Migrationen (chronologisch, siehe 5.4.6)
│ └── migrations/ 16 Migrationen (chronologisch, siehe 5.4.6)
├── src/
│ ├── app/
│ │ ├── api/ Route Handlers (siehe Kapitel 6)
@@ -2724,6 +2789,7 @@ PlanComputed ← an den Client geliefert
| `montecarlo.ts` | Monte-Carlo-Simulation (Sampler + Treiber), Szenario-Vergleich und Element-Gruppierung über die Herkunfts-Kette. Keine I/O, läuft im Browser. |
| `sensitivity.ts` | Sensitivitätsanalyse / Tornado: Treiber-Katalog, Parameter-Transformationen, `computeTornado`; zusätzlich `applyElementDriver` / `tunableElements` für die einzeln regelbaren Element-Renditen. Rein, läuft im Browser. |
| `actuals.ts` | Effektive Werte: Zuordnung auf die Szenario-Elemente über die Herkunfts-Kette, Einspielen in den Rechenkern, Bestand/Fluss. Rein. |
| `analyses.ts` | Gespeicherte Analysen: Typen, generische Ergebnis-Form, Speicher-Helfer (Kap. 3.10). |
| `dataview.ts` | Bündelt Plan-Sicht und Ist-Sicht für Matrix, Grafiken und Analysewerkzeuge. Rein. |
| `ratefields.ts` | Ratenfelder je Kategorie, Reichweite der Übernahme über Lebensphasen, Aufbau der Schreibvorgänge (Kap. 3.6.11). Rein. |
| `versioning.ts` | Versionierung: Nummerierung A.B, Zusammenfassung je Sitzung, Bauplan des Wiederherstellens, Auswirkung auf Kind-Szenarien. Rein, ohne I/O. |
@@ -2941,7 +3007,8 @@ sondern zu leeren Werten.
| `20260718140000_scenario_start_year` | `Scenario.startYear` (Kalenderjahr des Planbeginns), bestehende auf das laufende Jahr gesetzt |
| `20260719210000_scenario_versioning` | Tabelle `ScenarioVersion` (Snapshot als JSONB, A.B eindeutig je Szenario) und `Scenario.currentMajor` |
| `20260720090000_actuals` | Tabelle `ActualsSet` (effektive Werte je Plan, Werte als JSONB, Cash separat) |
| `20260720140000_plan_level_profile` | **V7**: Haushaltsform, Personen (Name/Alter) und Startjahr vom Szenario auf den Plan; neue Tabelle `PlanPerson`; `Person` behaelt nur das Pensionsalter. Datenübernahme aus dem Basisszenario. |
| `20260720140000_plan_level_profile` | **V7**: Haushaltsform, Personen (Name/Alter) und Startjahr vom Szenario auf den Plan; neue Tabelle `PlanPerson`; `Person` behält nur das Pensionsalter. Datenübernahme aus dem Basisszenario. |
| `20260720160000_saved_analyses` | Tabelle `SavedAnalysis` (Eingaben + Ergebnis als JSONB, denormalisierte Kerndaten) |
**Zur V6-Migration:** Sie benennt die bisherige `Plan`-Tabelle in `Scenario` um dadurch
bleiben alle IDs und damit sämtliche Kind-Fremdschlüssel gültig. Für jedes bisherige
@@ -2991,6 +3058,9 @@ wird der Plan neu geladen; die Berechnung kommt immer vom Server.
| `MonteCarloDialog` | ~600 | Monte-Carlo-Dialog: Erklärung, Szenario-Auswahl, Eingaben, Lauf, Vergleichstabelle + Fächer |
| `VersionHistoryDialog` | ~290 | Änderungshistorie: Liste, Hauptversion festlegen, Anzeigen, Wiederherstellen mit Warnung ([3.8](#38-versionierung-und-änderungshistorie)) |
| `VersionMatrix` | ~110 | Nur-Lese-Matrix eines festgehaltenen Standes |
| `PlanViews` | ~350 | Plan-Dashboard, Szenario-Liste, Analysen-Kacheln + gespeicherte Liste (Kap. 3.10) |
| `SavedAnalysisView` | ~180 | Nur-Lese-Ansicht einer gespeicherten Analyse |
| `SaveAnalysisButton` | ~90 | Speichern-Knopf mit Namensvorschlag |
| `VersionPicker` | ~140 | Wahl der Berechnungsgrundlage in den vier Analysewerkzeugen |
| `LiveSimDialog` | ~330 | Live-Simulation: Regler links, Grafikwahl rechts, Kennzahlenleiste mit Differenz zum Plan ([4.15](#415-live-simulation-was-wäre-wenn-regler)) |
| `AllocationChart` | ~80 | Gestapelte Vermögensaufteilung je Phase; aus dem Dashboard herausgelöst, damit die Live-Simulation sie mitbenutzen kann |
@@ -3113,6 +3183,19 @@ Alle erfassten Ist-Sätze, neueste zuerst ([3.9](#39-effektive-werte-plan-ist-ve
→ 200 `{ ok: true }`. Ein Ist-Satz ist eine Beobachtung es gibt weder Versionierung noch
Wiederherstellung.
### `GET /api/plans/<planId>/dashboard`
Gebündelte Kennzahlen fürs Plan-Dashboard und die Szenario-Liste ([3.10](#310-navigation-auf-plan-ebene-und-gespeicherte-analysen)):
Haushaltsdaten, Zähler (Szenarien/Ist-Sätze/Analysen), Basisszenario-Kennzahlen inkl.
Ist-Abweichung, und je Szenario Version/Elementzahl/Endvermögen/Ruinalter.
### `GET/POST /api/plans/<planId>/analyses`
Gespeicherte Analysen GET listet die Kopfdaten (ohne die grossen JSON-Felder), POST legt eine
Momentaufnahme an (`{ name, type, metric, source, scenarioName?, versionLabel?, inputs, result }`).
### `GET/DELETE /api/plans/<planId>/analyses/<analysisId>`
GET liefert die vollständige Analyse inkl. der eingefrorenen Zahlen (Eingaben + Ergebnis), DELETE
entfernt sie. Read-only es wird nichts neu gerechnet.
## 6.3 Szenarien
### `GET /api/scenarios/<scenarioId>`
@@ -3817,6 +3900,36 @@ Aussage unkenntlich. Beide zeigen deshalb nur die gewählte Grundlage.
**Serienobergrenze vier.** Szenario mal Version mal Datenquelle wächst schnell; darüber hinaus
hilft keine Farbpalette mehr.
## 9.30 Warum der Haushalt am Plan hängt
Bis V6 trug jedes **Szenario** sein eigenes Grundprofil: Haushaltsform, Personen (Name, Alter,
Pensionsalter) und Startjahr. Das war zu grosszügig. Zwei Szenarien desselben Plans konnten so
verschiedene Startjahre oder Haushaltsformen tragen und niemand hätte es bemerkt, bis eine
Auswertung Unsinn ergab: Derselbe erfasste Ist-Satz für 2031 wäre je Szenario auf einem anderen
Planjahr gelandet.
**V7 zieht die Trennlinie neu.** Was den **Haushalt** beschreibt, gehört an den Plan; was eine
**Planungsvariante** ausmacht, ans Szenario:
| Am Plan (für alle Szenarien) | Am Szenario (variantenspezifisch) |
|---|---|
| Haushaltsform | **Pensionsalter** je Person |
| Personen: Name, Alter | Inflationsannahme |
| Planstartjahr | Cash-Anfangswert |
| | Phasen, Elemente, Werte |
Das **Pensionsalter** bleibt bewusst unten es ist der Kern jedes Früh- oder
Spätpensionierungs-Szenarios. Wollte man Name oder Alter einer Person unterschiedlich planen,
wäre das kein Szenario mehr, sondern ein **anderer Plan**.
**Der Rechenkern merkt nichts davon.** `toPlanInput()` fügt die beiden Ebenen wieder zu einem
unveränderten `PlanInput` zusammen; `computePlan` und die 43 Golden Tests bleiben unberührt. Die
Verlagerung ist eine Frage der Datenhaltung, nicht der Berechnung.
**Preis der Klarheit:** Der Profil-Dialog muss jetzt je Feld anzeigen, ob es plan-weit gilt oder
nur das Szenario betrifft sonst änderte man beim Bearbeiten eines Nebenszenarios unbemerkt den
ganzen Plan. Diese Beschriftung ist der sichtbare Teil der Entscheidung.
---
# 10. Glossar