Version A.B: B automatisch je Bearbeitungssitzung (10-Minuten-Fenster, unveraenderte Staende erzeugen keine), A manuell mit Pflichtkommentar. Eine Version haelt den vollstaendigen Zustand als PlanInput-JSON -- dadurch ist die Versionsauswahl in allen vier Analysewerkzeugen fast kostenlos, bei Monte-Carlo je Szenario einzeln. Wiederherstellen erhaelt die IDs (sonst verlieren Kind-Szenarien ihre Diff-Basis) und legt den Stand selbst als neue Version an. Wo ein Bezug trotzdem bricht, warnt der Dialog vorher namentlich. Statischer Waechter-Test: jeder schreibende Endpunkt loest eine Version aus. Migration gegen echtes Postgres verifiziert. Spezifikation 0.19 (3.8 und 9.28 neu), 25 Tests (139 -> 164). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
+171
-4
@@ -4,7 +4,7 @@
|
||||
| | |
|
||||
|---|---|
|
||||
| **Dokument** | Funktionale und Technische Spezifikation FPT |
|
||||
| **Version** | 0.18 |
|
||||
| **Version** | 0.19 |
|
||||
| **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.19 | 2026-07-19 | Claude (Opus 4.8) | **Versionierung und Änderungshistorie je Szenario** (neues Kapitel 3.8). Jedes Szenario trägt eine Version **A.B**: **B** entsteht automatisch, **A** manuell mit Pflichtkommentar. **Der zentrale Entwurfsentscheid:** FPT hat keinen Speichern-Knopf – jede Änderung schreibt sofort, ein Assistenten-Durchlauf macht ~14 Schreibvorgänge, ein Verteil-Klick einen je Zielelement. Eine Version je Schreibvorgang wäre ein Tastenprotokoll gewesen; stattdessen werden alle Schreibvorgänge innerhalb von **10 Minuten zu einer** Nebenversion zusammengefasst, inhaltlich unveränderte Stände erzeugen gar keine, und verschiedene Benutzer laufen nie in einer Version zusammen. Eine Version hält den **vollständigen** Zustand als JSON in der Form `PlanInput` – dadurch ist die **Versionsauswahl in allen vier Analysewerkzeugen** (Grafiken, Live-Simulation, Monte-Carlo, Einflussfaktoren) fast kostenlos; bei Monte-Carlo **je Szenario einzeln**, weil dort mehrere gleichzeitig laufen. **Wiederherstellen** ist ungefährlich gebaut: Es legt den zurückgesetzten Stand selbst als neue Version an («Wiederhergestellt aus A.B»), löscht also nichts, und **erhält die IDs** von Phasen und Elementen – sonst verlören alle Kind-Szenarien ihre Diff-Basis und zeigten schlagartig alles als «neu». Wo ein Bezug trotzdem bricht (der alte Stand kannte das Element noch nicht), **warnt der Dialog vorher namentlich**. Die destruktive Logik liegt als reine Funktion `planRestore` vor und ist dort getestet; `versioning-db.ts` führt sie nur aus. Ein **statischer Wächter-Test** liest alle Route-Dateien und verlangt, dass jeder schreibende Endpunkt eine Version auslöst – eine vergessene Stelle wäre eine stille Lücke. Neue Tabelle `ScenarioVersion` + `Scenario.currentMajor` (Migration gegen echtes Postgres verifiziert), neue Endpunkte unter `/api/scenarios/<id>/versions`. Neue Kapitel 3.8 und 9.28; 25 Tests ergänzt (139 → 164). |
|
||||
| 0.18 | 2026-07-19 | Claude (Opus 4.8) | **Live-Simulation** (Roadmap Nr. 22). Neuer Button und Dialog als Zweispalter: links Schieberegler, rechts eine wählbare Grafik, darüber eine Kennzahlenleiste. Dreht man an einem Regler, wird der Plan **sofort** neu gerechnet – ohne für jede Variante eine Szenario-Kopie anzulegen. **Keine eigene Rechenlogik:** Die Regler benutzen dieselben Transformationen wie der Tornado (`applyDriver`), können also gar nicht etwas anderes zeigen als die Einflussfaktoren-Analyse. Neu ist nur `applyElementDriver` – dieselbe Verschiebung auf ein **einzelnes** Element statt auf eine ganze Kategorie: Standardmässig gibt es einen Sammelregler «Rendite», ein Klick auf «Renditen einzeln aufschlüsseln» ersetzt ihn durch je einen Regler pro Anlage (der Sammelregler wird dabei **entfernt**, nicht ergänzt, sonst zählte eine Bewegung doppelt; ein Test sichert ab, dass beide Ansichten denselben Plan beschreiben). Der unveränderte Plan wird als **Referenzlinie** mitgezeichnet, und die Kennzahlenleiste weist Endvermögen nominal/real **mit Differenz zum Plan** aus sowie – als eigene Karte – ob das Kapital reicht; ein gekippter Plan ist einer Verlaufslinie sonst nicht anzusehen. Gemessene Laufzeit von `computePlan`: **0.2 ms** auf einem 60-Jahres-Plan mit 10 Elementen, also rund 1 % des 16-ms-Frame-Budgets – deshalb wird synchron gerechnet, **ohne Debounce und ohne Worker**. Anders als der Tornado haben die Regler **Standardbereiche** (Begründung des scheinbaren Widerspruchs zu 9.18: neues Kapitel 9.27), beide Enden editierbar. **Das Pensionsalter fehlt weiterhin** (9.18, eigener Roadmap-Punkt); «Als Szenario speichern» ist bewusst zurückgestellt, ersatzweise zeigt der Dialog die aktive Einstellung als lesbare Zeile. Die Vermögensaufteilung wurde als `AllocationChart` aus dem Dashboard herausgelöst, damit beide sie nutzen. Neue Kapitel 4.15 und 9.27; 15 Tests ergänzt (124 → 139). Keine API-, DB- oder Schreib-Änderung – das Feature liest ausschliesslich. Nebenbei dieselbe vom Umlaut-Sweep verstümmelte Hex-Farbe (`#7c3aed`) wie in 0.17, diesmal im Dashboard. |
|
||||
| 0.17 | 2026-07-19 | Claude (Opus 4.8) | **Monte-Carlo: zwei Welten, vier Fälle.** Behebt einen Darstellungs-Widerspruch: Zuvor konnte «Planung 69 % erreicht» neben «Ziel 3 Mio nur 41 %» stehen, obwohl 3 Mio unter dem Plan-Endbetrag von 3.7 Mio lag – die beiden Zahlen stammten aus **verschiedenen simulierten Welten**. Neu läuft die Simulation **immer zweimal** (historische Renditen / geplante Werte, gemeinsamer Seed) und liest aus **jeder** Verteilung **beide** Schwellen ab: Plan-Endbetrag und Zielbetrag. Fall 1 und Fall 3 stammen damit aus derselben Verteilung, wodurch ein tieferes Ziel **nie** unwahrscheinlicher sein kann als ein höheres – der Widerspruch ist strukturell ausgeschlossen (Test). Zweite Korrektur: Der Nullpunkt für das Urteil ist **nicht 50 %**, sondern **Fall 2** (derselbe Schwellwert in der eigenen geplanten Welt); durch den Volatilitäts-Drag liegt der je nach Streuung bei 27–48 %. Verglichen wird Fall 1 gegen Fall 2 mit ± 5 pp Toleranzband → «zurückhaltend / realistisch / zu optimistisch». Darstellung: Fall 1 prominent mit Urteil, Fall 3+4 als Satzpaar untergeordnet, Fall 2 und die Mediane klein als Referenz. Der Drei-Wege-Umschalter aus 0.16 entfällt; historische Mittelwerte **und** Zielbetrag sind jetzt beide Pflicht. Technisch: `MonteCarloResult.finalWealthSorted` (alle Endvermögen sortiert) plus neuer Helfer `probabilityAtLeast` (Binärsuche) – vier Zahlen aus zwei Läufen statt vier Läufen. Kapitel 4.12.7 und 9.26 neu gefasst; 3 Tests ergänzt (121 → 124). Keine Änderung am Rechenkern. |
|
||||
| 0.16 | 2026-07-19 | Claude (Opus 4.8) | **Monte-Carlo mit zwei Fragestellungen** (Roadmap Nr. 46). Ein Umschalter oben trennt: **«Planung prüfen»** (Fall 1, wie bisher) würfelt um die **historischen** Renditen und prüft gegen den **Planungs-Endbetrag** (read-only) – «wie realistisch ist meine Planung?». **«Ziel prüfen»** (Fall 2, neu) würfelt um die **geplanten** Werte aus dem Plan und prüft gegen einen **manuellen Zielbetrag** – «erreiche ich mein Ziel?»; hier sind keine historischen Mittelwerte nötig, nur die Streuung. **«Beides»** rechnet beide Durchgänge gleichzeitig (gemeinsamer Seed). Neue Ergebnis-**Deutungstexte** je Fall (weich formuliert wegen des Volatilitäts-Drags). Der Fächer stammt aus dem historischen Durchgang; der Zielbetrag ist in Fall 2 **einer für alle** Szenarien. `runMonteCarloMulti` nimmt neu die Inflation **je Szenario** (`inflationMeanFor`); neuer Helfer `plannedReturnOf`. Kapitel 4.12 überarbeitet, 4.12.7 und 9.26 neu; 8 Tests ergänzt (119 → 121). Nebenbei eine vom Umlaut-Sweep verstümmelte Hex-Farbe (`#7c3aed`) korrigiert. Keine Änderung am Rechenkern. |
|
||||
@@ -1204,6 +1205,104 @@ Jede Element-Zeile der Matrix trägt eine **Sparkline** (`Sparkline.tsx`): der W
|
||||
(`ElementPhaseComputed.yearly`, seit 0.11) – keine Neuberechnung, reine Darstellung. Flache
|
||||
Verläufe werden nicht gezeichnet (keine Information).
|
||||
|
||||
## 3.8 Versionierung und Änderungshistorie
|
||||
|
||||
Jedes **Szenario** trägt eine Version **A.B** und eine vollständige Änderungshistorie. Zugang
|
||||
über den Knopf **«Änderungshistorie»** in der Szenario-Leiste.
|
||||
|
||||
| | Bedeutung | Entsteht |
|
||||
|---|---|---|
|
||||
| **B** (Nebenversion) | ein Bearbeitungsstand | **automatisch**, eine je Bearbeitungssitzung |
|
||||
| **A** (Hauptversion) | ein bewusst gesetzter Meilenstein | **manuell**, mit Pflichtkommentar; setzt B auf 0 |
|
||||
|
||||
### 3.8.1 Eine Version je Sitzung – nicht je Änderung
|
||||
|
||||
FPT hat **keinen Speichern-Knopf**: Jede Änderung schreibt sofort. Eine Version je
|
||||
Schreibvorgang wäre deshalb ein Tastenprotokoll und keine Historie – ein Durchlauf des
|
||||
Plan-Assistenten macht rund **14** Schreibvorgänge, ein Klick im Verteil-Dialog einen **je
|
||||
Zielelement**.
|
||||
|
||||
Stattdessen werden alle Schreibvorgänge innerhalb eines **Zeitfensters von 10 Minuten** zu
|
||||
**einer** Nebenversion zusammengefasst: Der erste legt sie an, alle weiteren aktualisieren
|
||||
sie. Zwei Sicherungen ergänzen das:
|
||||
|
||||
- **Unverändert = keine Version.** Ergibt ein Schreibvorgang inhaltlich denselben Stand
|
||||
(Dialog geöffnet und unverändert geschlossen), entsteht nichts. Verglichen wird über eine
|
||||
Serialisierung mit **sortierten Schlüsseln** – ohne das würden identische Stände als
|
||||
verschieden gelten, weil die Reihenfolge der Werte aus der Datenbank-Zeilenfolge stammt.
|
||||
- **Benutzerwechsel trennt immer.** Änderungen verschiedener Benutzer laufen nie in einer
|
||||
Version zusammen, auch nicht innerhalb des Fensters – Vorbereitung auf die spätere Freigabe
|
||||
an einen Finanzberater.
|
||||
|
||||
Eine **Hauptversion** wird nie zusammengefasst und nie nachträglich verändert; die nächste
|
||||
Änderung beginnt bei A.1.
|
||||
|
||||
### 3.8.2 Was die Historie zeigt
|
||||
|
||||
Je Version: **wer** (Benutzername), **wann** (Zeitpunkt der letzten Änderung dieser Sitzung)
|
||||
und – bei Hauptversionen – der **Kommentar**. Die oberste Zeile ist der aktuelle Stand.
|
||||
|
||||
Zwei Aktionen je Version:
|
||||
|
||||
- **Anzeigen** – ein Nur-Lese-Fenster mit der Matrix dieses Standes ([VersionMatrix](#552-komponenten)),
|
||||
Phasen als Spalten, Elemente als Zeilen, dazu Endvermögen und Ruinalter. Bewusst **nicht**
|
||||
die Bearbeitungs-Matrix: Inspector, Übergangs-Ampeln und Diff-Markierung haben für einen
|
||||
alten Stand keine Bedeutung.
|
||||
- **Wiederherstellen** – setzt das Szenario vollständig auf diesen Stand zurück.
|
||||
|
||||
### 3.8.3 Wiederherstellen
|
||||
|
||||
Zwei Eigenschaften machen den Knopf ungefährlich:
|
||||
|
||||
**Es geht nichts verloren.** Der wiederhergestellte Stand wird selbst als **neue Version**
|
||||
festgehalten, mit dem Kommentar «Wiederhergestellt aus A.B». Die bisherige Historie bleibt
|
||||
vollständig – auch alles, was nach dem Zielstand kam.
|
||||
|
||||
**IDs bleiben erhalten.** Phasen und Elemente behalten ihre IDs (der Snapshot trägt sie mit).
|
||||
Das ist keine Kosmetik: Kind-Szenarien zeigen über `sourceElementId` / `sourcePhaseId` auf
|
||||
genau diese IDs, und daran hängt die Abweichungs-Markierung
|
||||
([3.2.6](#326-abweichungs-markierung-diff)). Würden neue IDs entstehen, erschiene in
|
||||
jedem Kind-Szenario schlagartig **alles als «neu»** statt als «geändert».
|
||||
|
||||
Restlos vermeiden lässt sich das nicht: Geht man auf einen Stand zurück, in dem ein Element
|
||||
noch gar nicht existierte, auf das ein Kind verweist, bricht dieser eine Bezug zwangsläufig.
|
||||
Der Dialog **prüft das vorher** und benennt die betroffenen Szenarien namentlich, statt es
|
||||
stillschweigend zu tun.
|
||||
|
||||
### 3.8.4 Analyse auf einer bestimmten Version
|
||||
|
||||
Alle vier Analysewerkzeuge – **Grafiken**, **Live-Simulation**, **Monte-Carlo** und
|
||||
**Einflussfaktoren** – haben oben einen Wähler **«Berechnungsgrundlage»**: aktueller Stand
|
||||
(Vorgabe) oder eine festgehaltene Version.
|
||||
|
||||
In der **Monte-Carlo-Simulation** ist die Auswahl **zweidimensional**: Sie rechnet mehrere
|
||||
Szenarien gleichzeitig, und der Wähler hängt deshalb an der jeweiligen Szenario-Zeile – man
|
||||
kann Szenario A in Version 1.2 gegen Szenario B in Version 2.0 stellen.
|
||||
|
||||
Das war fast kostenlos: Der Snapshot ist bereits ein **`PlanInput`**, also genau der Typ, den
|
||||
alle Werkzeuge ohnehin entgegennehmen. Es genügt, den Arbeitsstand durch den Snapshot zu
|
||||
ersetzen; das Gerechnete entsteht lokal über `computePlan`.
|
||||
|
||||
**Eine Ausnahme ist beschriftet:** Der CSV-Export liest immer das Szenario aus der Datenbank
|
||||
und liefert deshalb den **aktuellen** Stand, nicht die betrachtete Version. Der Knopf sagt das
|
||||
in diesem Fall ausdrücklich.
|
||||
|
||||
### 3.8.5 Vollständigkeit der Historie
|
||||
|
||||
Die Versionierung hängt daran, dass **jeder** inhaltsverändernde Endpunkt sie auslöst
|
||||
(`touchScenario`). Ein vergessener Pfad fiele nicht auf – er erzeugte still keine Version, und
|
||||
die Lücke bemerkte man erst Wochen später. Ein **Test liest deshalb alle Route-Dateien** und
|
||||
verlangt, dass jeder schreibende Endpunkt entweder `touchScenario` aufruft oder mit Begründung
|
||||
in einer Ausnahmeliste steht; verwaiste Ausnahmen meldet er ebenfalls.
|
||||
|
||||
Das Festhalten einer Version ist bewusst **fehlertolerant**: Schlägt es fehl, wird es
|
||||
protokolliert, die eigentliche Änderung des Nutzers aber nicht zurückgewiesen. Die Historie
|
||||
ist Begleitinformation, nicht der Zweck der Anfrage.
|
||||
|
||||
Referenz: `src/lib/versioning.ts` (reine Logik), `src/lib/versioning-db.ts` (Datenbank),
|
||||
`src/components/VersionHistoryDialog.tsx`, `src/components/VersionMatrix.tsx`,
|
||||
`src/components/VersionPicker.tsx`.
|
||||
|
||||
---
|
||||
|
||||
# 4. Berechnungsmodell
|
||||
@@ -2406,7 +2505,7 @@ Referenz: `src/lib/livesim.ts`, `src/lib/sensitivity.ts` (`applyElementDriver`,
|
||||
FPT/
|
||||
├── prisma/
|
||||
│ ├── schema.prisma Datenmodell
|
||||
│ └── migrations/ 12 Migrationen (chronologisch, siehe 5.4.6)
|
||||
│ └── migrations/ 13 Migrationen (chronologisch, siehe 5.4.6)
|
||||
├── src/
|
||||
│ ├── app/
|
||||
│ │ ├── api/ Route Handlers (siehe Kapitel 6)
|
||||
@@ -2447,6 +2546,8 @@ PlanComputed ← an den Client geliefert
|
||||
| `constants.ts` | Schweizer Systemparameter |
|
||||
| `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. |
|
||||
| `versioning.ts` | Versionierung: Nummerierung A.B, Zusammenfassung je Sitzung, Bauplan des Wiederherstellens, Auswirkung auf Kind-Szenarien. Rein, ohne I/O. |
|
||||
| `versioning-db.ts` | Datenbank-Anbindung der Versionierung: Snapshot festhalten, Hauptversion, Wiederherstellen. Führt den Bauplan aus `versioning.ts` nur aus. |
|
||||
| `livesim.ts` | Live-Simulation: Reglerkatalog mit Standardbereichen, Anwenden mehrerer Regler, Kennzahlen (Kap. 4.15). Rein, läuft im Browser. |
|
||||
| `distribution.ts` | Kapitaltopf und Quoten-Zerlegung, Anwenden von Entwurfswerten für die Verteil-Werkzeuge (Kap. 3.6.9/3.6.10). Rein. |
|
||||
| `phaseplan.ts` | Ableitung der Lebensabschnitte (Erwerb/Misch/Pension) aus den fixen Pensionierungszeitpunkten für den Assistenten (Kap. 3.2.8). Rein. |
|
||||
@@ -2658,6 +2759,7 @@ sondern zu leeren Werten.
|
||||
| `20260716230000_phase_cash_transition` | `Phase.cashTransition` (JSONB) für einmalige Sonderein-/ausgaben |
|
||||
| `20260718090000_plan_scenario_hierarchy` | **V6**: `Plan` → `Scenario` (IDs erhalten), neuer Behälter `Plan`, `planId` → `scenarioId`, Herkunfts-Verweise |
|
||||
| `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` |
|
||||
|
||||
**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
|
||||
@@ -2705,6 +2807,9 @@ wird der Plan neu geladen; die Berechnung kommt immer vom Server.
|
||||
| `PhaseDetail` | 95 | Phase bearbeiten/löschen |
|
||||
| `SparquoteChart` | 64 | Einkommen/Ausgaben pro Jahr mit Spar-/Verzehrbändern |
|
||||
| `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 |
|
||||
| `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 |
|
||||
| `SensitivityDialog` | ~330 | Einflussfaktoren: Erklärung, Treiber-Auswahl mit Bandbreiten, Tornado + Tabelle |
|
||||
@@ -2838,7 +2943,34 @@ Akzeptiert eine **Union** von zwei Formen:
|
||||
→ 201 `{ scenarioId: "<neue Id>" }`
|
||||
|
||||
### `GET /api/scenarios/<scenarioId>/export`
|
||||
→ `text/csv; charset=utf-8`, `Content-Disposition: attachment`.
|
||||
→ `text/csv; charset=utf-8`, `Content-Disposition: attachment`. Liefert immer den **aktuellen**
|
||||
Stand, auch wenn im Client eine ältere Version betrachtet wird ([3.8.4](#384-analyse-auf-einer-bestimmten-version)).
|
||||
|
||||
### `GET /api/scenarios/<scenarioId>/versions`
|
||||
Änderungshistorie, neueste zuerst – **ohne** die Snapshots (je zig Kilobyte).
|
||||
```json
|
||||
{ "currentMajor": 2,
|
||||
"versions": [ { "id", "major", "minor", "comment", "isMajor",
|
||||
"createdAt", "updatedAt", "author" } ] }
|
||||
```
|
||||
|
||||
### `POST /api/scenarios/<scenarioId>/versions`
|
||||
`{ comment }` (Pflicht, ≥ 3 Zeichen) – legt den aktuellen Stand als **Hauptversion** fest und
|
||||
setzt `Scenario.currentMajor`. → 201 `{ version: { major, minor } }` · 400 ohne Kommentar.
|
||||
|
||||
### `GET /api/scenarios/<scenarioId>/versions/<versionId>`
|
||||
Ein einzelner Stand samt Berechnung und der Vorwarnung für das Wiederherstellen:
|
||||
```json
|
||||
{ "version": { "id", "major", "minor", "comment", "isMajor", "createdAt" },
|
||||
"plan": <PlanInput>, "computed": <PlanComputed>,
|
||||
"impact": { "lostElementIds": [], "lostPhaseIds": [], "affectedChildren": [] } }
|
||||
```
|
||||
`impact` benennt die Kind-Szenarien, die durch ein Wiederherstellen ihre Diff-Basis verlören.
|
||||
|
||||
### `POST /api/scenarios/<scenarioId>/versions/<versionId>`
|
||||
Setzt das Szenario auf diesen Stand zurück – **IDs bleiben erhalten**, und der wiederhergestellte
|
||||
Stand wird selbst als neue Version festgehalten («Wiederhergestellt aus A.B»).
|
||||
→ 200 `{ version: { major, minor } }`
|
||||
|
||||
## 6.4 Phasen
|
||||
|
||||
@@ -2949,12 +3081,14 @@ Include `src/**/*.test.ts`). Es gibt **keine** Komponenten-, API- oder E2E-Tests
|
||||
| `explain.test.ts` | 12 | Verlaufswerte je Element, Vollständigkeit beider Wasserfall-Zerlegungen, Rechenweg-Protokoll, Gültigkeit der Spezifikations-Verweise |
|
||||
| `montecarlo.test.ts` | 18 | Determinismus, Volatilität/Vol-Drag, Böden, Reproduzierbarkeit; Element-Gruppierung, Szenario-Vergleich, Inflation je Szenario, plannedReturnOf; Schwellen-Ablesung (probabilityAtLeast), Monotonie über Schwellen, Fall 2 systematisch unter 50 % |
|
||||
| `distribution.test.ts` | 8 | Kapitaltopf und Quoten-Zerlegung gegen die Cash-Brücke; Entwurfswerte anwenden ohne Verlust bestehender Felder |
|
||||
| `versioning.test.ts` | 22 | Nummerierung und Zusammenfassung je Sitzung, unveränderte Stände, Benutzertrennung, Bauplan des Wiederherstellens, Auswirkung auf Kind-Szenarien |
|
||||
| `versioning-coverage.test.ts` | 3 | statischer Wächter: jeder schreibende Endpunkt löst eine Version aus |
|
||||
| `livesim.test.ts` | 15 | Element-Regler bewegt genau ein Element; Aufschlüsselung deckt sich mit dem Sammelregler; neutrale Stellung verändert den Plan nicht; Ruinmeldung |
|
||||
| `phaseplan.test.ts` | 8 | Ableitung der Lebensabschnitte aus den Pensionierungszeitpunkten (Einzel/Paar/bereits pensioniert); letzter Teil immer offen |
|
||||
| `bridges.test.ts` | 10 | Vermögens- und Cash-Brücke gehen über sieben Plankonstellationen ohne Restgrösse auf; Umbuchungen bleiben aus der Vermögensbrücke heraus |
|
||||
| `diff.test.ts` | 9 | Abweichungs-Erkennung gegen das Eltern-Szenario |
|
||||
| `migrations.test.ts` | 1 | spielt alle Migrationen gegen echtes PostgreSQL (PGlite) ein |
|
||||
| **Total** | **139** | |
|
||||
| **Total** | **164** | |
|
||||
|
||||
## 8.2 Testfälle
|
||||
|
||||
@@ -3416,6 +3550,39 @@ neben jedem Regler steht sein **Neutralpunkt** – der Wert, bei dem der Plan un
|
||||
Bei allen Verschiebungs-Treibern ist das 0, bei der Inflation der Planwert selbst, weil sie als
|
||||
einzige absolut und nicht als Differenz eingegeben wird.
|
||||
|
||||
## 9.28 Was die Versionierung nicht leistet
|
||||
|
||||
**Das Zeitfenster ist eine Konvention, keine Wahrheit.** Zehn Minuten sind gesetzt, weil FPT
|
||||
keinen Speichern-Knopf hat und der Nutzer den Schnitt sonst nie selbst zieht
|
||||
([3.8.1](#381-eine-version-je-sitzung--nicht-je-änderung)). Wer nach acht Minuten Pause
|
||||
weiterarbeitet, landet in derselben Version; wer nach zwölf Minuten eine Kleinigkeit ändert,
|
||||
bekommt eine neue. Beides ist gelegentlich nicht das, was man gemeint hätte. Ein
|
||||
Speichern-Knopf wäre die exaktere Lösung, würde aber die Bedienung des ganzen Werkzeugs
|
||||
umkrempeln.
|
||||
|
||||
**Eine Nebenversion hält den Stand am ENDE der Sitzung fest**, nicht jeden Zwischenschritt
|
||||
darin. Wer innerhalb einer Sitzung etwas ändert und wieder zurücknimmt, findet den
|
||||
Zwischenstand nirgends. Die Historie ist eine Folge von Arbeitsständen, kein Undo.
|
||||
|
||||
**Es gibt keinen Versionsvergleich.** Zwei Stände lassen sich nur nacheinander ansehen, nicht
|
||||
nebeneinander stellen. Der bestehende Diff ([3.2.6](#326-abweichungs-markierung-diff))
|
||||
vergleicht Szenarien gegen ihr Eltern-Szenario, nicht Versionen gegeneinander – technisch
|
||||
wäre beides verwandt, aber es ist bewusst nicht Teil dieser Stufe.
|
||||
|
||||
**Die Historie wird nie beschnitten.** Jede Version hält den vollständigen Zustand als JSON
|
||||
(Grössenordnung 40 KB bei sechs Phasen und zwölf Elementen). Bei intensiver Nutzung wächst
|
||||
das linear; eine Aufräumregel (etwa: Nebenversionen älter als ein Jahr verdichten, Haupt-
|
||||
versionen behalten) gibt es noch nicht. Bei den heutigen Datenmengen ist das unkritisch.
|
||||
|
||||
**«Wer» unterscheidet heute nichts.** Ein Plan gehört genau einem Benutzer, es gibt keine
|
||||
Freigabe und keine Rollen – die Spalte zeigt also immer denselben Namen. Sie ist Vorbereitung
|
||||
auf den Finanzberater, und die Logik trennt Benutzer bereits sauber (Änderungen verschiedener
|
||||
Benutzer werden nie in einer Version zusammengefasst).
|
||||
|
||||
**Der Plan selbst ist nicht versioniert**, nur seine Szenarien. Wird ein Szenario gelöscht,
|
||||
verschwindet seine Historie mit ihm (Cascade). Das ist gewollt: Eine Historie ohne das Objekt,
|
||||
das sie beschreibt, wäre nicht wiederherstellbar.
|
||||
|
||||
---
|
||||
|
||||
# 10. Glossar
|
||||
|
||||
Reference in New Issue
Block a user