Das Pensionsalter laesst sich neu veraendern: Nicht das Alter wird gesetzt, sondern die Phasengrenze verschoben -- Vorphase laenger, Folgephase kuerzer, Gesamtdauer gleich. Faellt eine Phase dabei weg, werden die beiden Uebergaenge nach Bestaetigung zusammengelegt. - neues reines Modul lib/retirement.ts + POST /api/scenarios/<id>/retirement - Pensionsalter als Tornado-Treiber und Live-Simulations-Regler - AHV-Referenzalter 65: Rente ab 65 unabhaengig vom Pensionsalter; vor 65 Beitrag als Nichterwerbstaetige(r) (neues Feld ahvContribution). AHV wird dafuer jahresweise statt phasenweise gerechnet. - Punkt B: personenzugeordnetes Einkommen faellt bei Pensionierung auf 0 - Punkt A: Wiederkehr-Parameter werden live aus der Vorphase geerbt, sichtbar als "Aus Vorphase uebernehmen" - Punkt C: Kapitalzufluss am Pensions-Uebergang per Quote auf Amortisation, Anlage und Cash verteilbar - Fix: carry.flowBasis wurde vor der Jahresschleife berechnet, effektive Werte kamen deshalb nie in der Folgephase an SPEZIFIKATION 0.26 (neue Kapitel 3.12, 4.4.7, 4.16). 221 -> 261 Tests. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
+316
-34
@@ -4,10 +4,10 @@
|
||||
| | |
|
||||
|---|---|
|
||||
| **Dokument** | Funktionale und Technische Spezifikation FPT |
|
||||
| **Version** | 0.25 |
|
||||
| **Datum** | 2026-07-18 |
|
||||
| **Version** | 0.26 |
|
||||
| **Datum** | 2026-07-21 |
|
||||
| **Status** | Lebendes Dokument |
|
||||
| **Codestand** | Arbeitsstand nach `1d046e9` inkl. zwei Monte-Carlo-Fragestellungen (Branch `main`) |
|
||||
| **Codestand** | Arbeitsstand nach `c429624` inkl. anpassbarem Pensionsalter (Branch `main`) |
|
||||
| **Ersetzt** | `FDD_TDD_FPT.docx` (v1–v5) im Ordner `Info Dateien` – diese sind ab Version 0.1 dieses Dokuments obsolet |
|
||||
| **Geltungsbereich** | Gesamter Code im Verzeichnis `FPT` |
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
|
||||
| Version | Datum | Autor | Änderung |
|
||||
|---|---|---|---|
|
||||
| 0.26 | 2026-07-21 | Claude (Opus 4.8) | **Pensionsalter anpassen** (Roadmap Nr. 44, neue Kapitel 3.12, 4.4.7 und 4.16). Bisher war das Pensionsalter faktisch unantastbar: Es bestimmt, wo eine Lebensphase endet – ein frei geändertes Alter hätte die Phasengrenze zerrissen. Neu wird nicht das Alter geändert, sondern **die Grenze verschoben**: Die Phase davor wird länger, die danach kürzer, die Gesamtdauer bleibt gleich. Der Spielraum endet dort, wo eine angrenzende Phase unter ein Jahr fiele; ein Schritt weiter **entfällt sie ganz**, was vorher bestätigt wird, weil dabei zwei Übergänge **zusammengelegt** werden (bereits getroffene Entscheide bleiben, leere Felder werden aus dem entfallenden Übergang ergänzt, einmalige Cash-Beträge werden addiert – der Steuersatz betragsgewichtet). Das Pensionsalter ist damit auch **Treiber im Tornado** und **Regler in der Live-Simulation**. **AHV-Referenzalter (4.4.7):** Die Rente beginnt neu **immer mit 65**, unabhängig vom Pensionsalter – wer länger arbeitet, erhält sie zusätzlich zum Lohn; wer früher aufhört, zahlt bis 65 einen **Beitrag als Nichterwerbstätige(r)**, der als laufende Ausgabe auf die Verzehrquote schlägt und mit 65 wegfällt (neues Feld `ahvContribution`, ohne Default, Hilfetext nennt die reale Bandbreite von rund 530 bis 26'500 CHF pro Jahr). Beide Wechsel können **innerhalb** einer Phase liegen, die AHV wird deshalb **jahresweise** statt phasenweise gerechnet. **Punkt B:** Ein Einkommen, das einer **Person** zugeordnet ist, fällt bei deren Pensionierung auf 0 – bisher lief der Lohn stillschweigend in die Pension weiter. Gemeinsame Einkommen (Mieterträge o. Ä.) bleiben; ein ausdrücklich erfasster Betrag gewinnt, damit ein Teilzeitpensum modellierbar bleibt. **Punkt A:** Wiederkehr-Parameter (Raten, Beiträge, Amortisation, Wertsteigerung, Zinssatz) werden neu **live aus der Vorphase geerbt** statt beim Anlegen der Phase kopiert – sichtbar als angehaktes **«Aus Vorphase übernehmen»** je Feld. Vorher blieb die Kopie stehen, wenn man die Vorphase später änderte. **Punkt C:** Der Kapitalzufluss am Pensions-Übergang (PK, 3a, Verkaufserlös) lässt sich in **Prozent** auf Amortisation, Anlage und Cash aufteilen – bewusst nicht in Franken, weil sich der Betrag mit dem Pensionsalter ändert und eine Quote mitskaliert. **Nebenbei ein echter Fehler behoben:** Der fortgeschriebene Basiswert für Einkommen und Ausgaben wurde **vor** der Jahresschleife berechnet – effektive Werte kamen dadurch nie in der Folgephase an. Neues Modul `retirement.ts`, neuer Endpunkt `POST /api/scenarios/<id>/retirement`; 40 Tests ergänzt (221 → 261). |
|
||||
| 0.25 | 2026-07-21 | Claude (Opus 4.8) | **PDF-Berichte** (Roadmap Nr. 11, neues Kapitel 3.11). Neuer Unterpunkt **«Berichte»** auf Plan-Ebene: Liste der erzeugten Berichte plus Assistent zum Anlegen (Titel, Notiz, nominal **oder** real, Plan- oder effektive Daten, bis zu **drei** Szenarien, beliebige gespeicherte Analysen). Das **Layout ist immer gleich**; die Auswahl bestimmt nur, welche Bausteine erscheinen: Deckblatt mit Zusammenfassung und drei Kernaussagen, dann je Szenario Kennzahlen, Vermögensverlauf, Lebensphasen und Annahmen, danach Vergleich, Plan/Ist, Analysen und die Hinweise. **Die PDF-Datei wird als Datei abgelegt** (BYTEA in Postgres, nicht im Container-Dateisystem, das jeder Deploy neu baut): Ein Bericht muss in drei Jahren byte-identisch wieder herunterladbar sein – eine Neuerzeugung könnte das nach Änderungen an Plan, Rechenkern oder Layout nicht garantieren. **Kennzahlen je Szenario:** Endvermögen, Kapitalreichweite, Vermögen und Vorsorgekapital bei Pensionierung, AHV- und PK-Rente sowie die **offenen Entscheide** – die einzige unmittelbar handlungsleitende Zahl. Damit Bericht und Matrix nie verschiedene Zahlen nennen, liegt deren Zählung neu als reine Funktion in `decisions.ts`, die beide benutzen. **Zu jeder Kennzahl steht ihre Grundlage** als kurzer Verweis; die vollständigen Annahmen (Startwerte, Renditen, Raten je Element) stehen **einmal** je Szenario, statt bei jeder Kennzahl wiederholt zu werden. Ein **Haftungsausschluss** ist verpflichtend und durch einen Test gesichert – ein formal gesetztes PDF wird sonst als Beratung gelesen. **Technik:** `pdfkit` in der Node-Runtime statt Headless-Browser (kein Chromium im Image); `@react-pdf/renderer` schied aus, weil es mit React 19 / Next 16 bricht. Diagramme entstehen als **echte Vektoren** aus den gespeicherten Zahlen – genau dafür wurden die Analysen in 0.24 als Zahlen und nicht als Bilder abgelegt. `pdfkit` ist als externes Paket deklariert, weil es Font-Metriken über Dateipfade lädt und gebündelt erst in der Produktion bräche. Neue Tabelle `Report`, Endpunkte unter `/api/plans/<id>/reports`, neue Module `report.ts`, `report-pdf.ts`, `decisions.ts`; 9 Tests ergänzt (212 → 221). |
|
||||
| 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). |
|
||||
@@ -970,8 +971,9 @@ analog zur Monte-Carlo-Simulation:
|
||||
4. **Ergebnis** – Basisfall, Tornado-Chart und Tabelle.
|
||||
|
||||
Der Dialog ist bewusst **nicht** Teil des Grafiken-Bereichs: Dieser ist reine Ausgabe, während der
|
||||
Tornado Pflichteingaben braucht. Ein Hinweis am Ende der Parameterliste benennt, warum das
|
||||
**Pensionsalter** nicht enthalten ist (siehe [9.18](#918-tornado-was-der-chart-nicht-leistet)).
|
||||
Tornado Pflichteingaben braucht. Seit 0.26 gehört auch das **Pensionsalter** je Person zu den
|
||||
Treibern – es erscheint nur, wenn die Phasengrenze überhaupt Spielraum hat
|
||||
(siehe [9.18](#918-tornado-was-der-chart-nicht-leistet) und [4.16](#416-pensionsalter-verschieben)).
|
||||
|
||||
Referenz: `src/components/SensitivityDialog.tsx`, `src/components/AppShell.tsx`.
|
||||
|
||||
@@ -1651,6 +1653,107 @@ das, was eingefroren wird, dasselbe, was geprüft wird.
|
||||
Referenz: `src/lib/report.ts`, `src/lib/report-pdf.ts`, `src/lib/decisions.ts`,
|
||||
`src/components/ReportsView.tsx`.
|
||||
|
||||
## 3.12 Pensionsalter anpassen
|
||||
|
||||
> Roadmap Nr. 44. Bedienung im Szenario-Profil, Abschnitt «Pensionsalter anpassen».
|
||||
|
||||
### 3.12.1 Warum das kein Zahlenfeld ist
|
||||
|
||||
Das Pensionsalter bestimmt, **wo eine Lebensphase endet**: `maxPhaseDuration` kappt jede
|
||||
Phasendauer beim nächsten Pensionierungsereignis, jede Pensionierung liegt deshalb zwangsläufig
|
||||
auf einer Phasengrenze. Ein frei änderbares Alter hätte diese Zusammengehörigkeit zerrissen –
|
||||
die Phasen blieben, wo sie sind, und der Phasentyp (Erwerb/Misch/Pension) käme mitten in einer
|
||||
Phase ins Rutschen.
|
||||
|
||||
Deshalb ändert die Bedienung nicht das Alter, sondern verschiebt **die Grenze**:
|
||||
|
||||
| | vorher | nachher (−4 Jahre) |
|
||||
|---|---|---|
|
||||
| Phase 3 «Misch» | 10 Jahre | **6 Jahre** |
|
||||
| Phase 4 «Pension» | 15 Jahre | **19 Jahre** |
|
||||
| Gesamtdauer | 60 Jahre | 60 Jahre |
|
||||
|
||||
Die Gesamtdauer des Plans bleibt immer gleich – es wird Zeit umverteilt, nicht hinzugefügt.
|
||||
|
||||
### 3.12.2 Spielraum und Sperren
|
||||
|
||||
Angezeigt wird je Person das aktuelle Pensionsalter, Knöpfe für ±1 Jahr, Kurzwahl-Chips für
|
||||
grössere Schritte und eine **Hilfebox**, die den Spielraum benennt. Ein deaktivierter Knopf
|
||||
ohne Erklärung wirkt wie ein Fehler; die Box sagt deshalb immer, was möglich ist **und warum
|
||||
nicht mehr**.
|
||||
|
||||
Der Spielraum ist `−(Dauer der Vorphase − 1)` bis `+(Dauer der Folgephase − 1)`: Eine
|
||||
Lebensphase muss mindestens ein Jahr dauern.
|
||||
|
||||
Gesperrt wird ganz, wenn:
|
||||
|
||||
| Situation | Meldung |
|
||||
|---|---|
|
||||
| Person ist bei Planbeginn schon pensioniert | es gibt keine Grenze zu verschieben |
|
||||
| Pensionierung liegt am oder nach dem Planende | zuerst eine Lebensphase anhängen |
|
||||
| Pensionierung liegt nicht auf einer Phasengrenze | zuerst die Lebensphasen anpassen (Altdaten) |
|
||||
| **Beide Personen teilen dieselbe Grenze** | zuerst eine Lebensphase einfügen, um sie zu trennen |
|
||||
|
||||
Der letzte Fall ist der Kehrseite der Zusammenlegung: Sind zwei Pensionierungen auf demselben
|
||||
Zeitpunkt, liesse sich die eine nicht bewegen, ohne die andere mitzunehmen.
|
||||
|
||||
### 3.12.3 Zusammenlegung: wenn eine Lebensphase entfällt
|
||||
|
||||
Genau ein Jahr über den Spielraum hinaus fällt die angrenzende Phase auf 0 und **verschwindet**.
|
||||
Das ist erlaubt – es ist der Fall «beide werden gleichzeitig pensioniert, die Mischphase gibt es
|
||||
nicht mehr» –, wird aber **vorher bestätigt**, weil dabei zwei Übergänge zu einem werden.
|
||||
|
||||
Regel für die Zusammenführung (überlebende Grenze ist die des **Vorgängers** der entfallenden
|
||||
Phase):
|
||||
|
||||
| | Regel | Begründung |
|
||||
|---|---|---|
|
||||
| Element-Entscheide | Was am überlebenden Übergang schon entschieden ist, **bleibt**. Nur leere Felder werden aus dem entfallenden ergänzt. | Ein bewusster Entscheid darf nie von einem anderen überschrieben werden. |
|
||||
| Einmalige Cash-Beträge | werden **addiert** | Beide Ereignisse finden weiterhin statt, nur zum selben Zeitpunkt. |
|
||||
| Steuersatz auf dem Zufluss | **betragsgewichteter** Mischsatz | Nur so bleibt der Netto-Zufluss derselbe wie vorher. |
|
||||
| Bezeichnungen | mit « + » verbunden | damit nachvollziehbar bleibt, woraus die Summe entstand |
|
||||
|
||||
Die Verschiebung ist **nicht** über die Undo-Funktion rückgängig zu machen – sie erzeugt wie
|
||||
jede Änderung eine neue Nebenversion, aus der sich der alte Stand wiederherstellen lässt
|
||||
([3.8](#38-versionierung-und-änderungshistorie)).
|
||||
|
||||
### 3.12.4 Punkt A: «Aus Vorphase übernehmen»
|
||||
|
||||
Wiederkehr-Parameter – Teuerungsausgleich, erwartete Rendite, jährliche Einzahlung, Bezugsrate,
|
||||
Amortisation, Wertsteigerung, Hypothekarzins, Tilgung – galten bisher als **Kopie**: Beim
|
||||
Anlegen einer Phase wurde der Wert der Vorphase hineingeschrieben. Änderte man die Vorphase
|
||||
später, blieb die Kopie stehen.
|
||||
|
||||
Neu ist das Feld in Folgephasen standardmässig **leer** und wird live geerbt; darunter steht ein
|
||||
angehaktes Kästchen **«Aus Vorphase übernehmen»** mit dem geerbten Wert daneben. Ein Häkchen
|
||||
weg macht das Feld editierbar und den Wert phasen-eigen.
|
||||
|
||||
Nicht vererbt werden bewusst: **Ausfalljahre** (sie gelten für genau eine Phase – ein geerbter
|
||||
Wert würde eine Lücke erfinden), der **Kaufpreis** einer Immobilie (eine Tatsache, keine
|
||||
Annahme) und die **Zins-Behandlung** (ein Schalter ohne Zahlenwert).
|
||||
|
||||
Bestehende Pläne verhalten sich unverändert: Dort sind die Werte gespeichert und gewinnen daher
|
||||
gegen die Vererbung, bis man das Häkchen aktiv setzt.
|
||||
|
||||
### 3.12.5 Punkt C: Verwendung des Kapitalzuflusses
|
||||
|
||||
Am Pensions-Übergang kommt oft ein grosser Betrag auf einmal herein (PK-Kapital, Säule 3a,
|
||||
Verkaufserlös). Ihn vollständig als Cash liegen zu lassen ist selten die Absicht. Im
|
||||
Cash-Übergang lässt sich deshalb erfassen, wie viel **in Prozent** in die Amortisation der
|
||||
Hypothek und in eine Anlage fliesst; der Rest bleibt Cash.
|
||||
|
||||
**Warum Prozent und nicht Franken:** Verschiebt man das Pensionsalter, ändert sich das bezogene
|
||||
Kapital. Ein Frankenbetrag müsste von Hand nachgezogen werden – und würde bis dahin still eine
|
||||
falsche Aufteilung rechnen. Eine Quote skaliert mit.
|
||||
|
||||
Die Amortisations-Quote ist am Restsaldo der Hypothek gekappt; ist sie grösser, bleibt der Rest
|
||||
Cash. Die Anlage-Quote fliesst in ein wählbares Vermögens-Element (Vorgabe: das erste aktive).
|
||||
Beide sind mechanisch nichts Neues – die eine wirkt wie eine Sonderamortisation, die andere wie
|
||||
eine Zusatzinvestition, und beide laufen dadurch korrekt durch die zwei Wasserfall-Brücken.
|
||||
|
||||
Referenz: `src/lib/retirement.ts`, `src/components/RetirementAdjuster.tsx`,
|
||||
`src/components/FormField.tsx` (`InheritableField`).
|
||||
|
||||
---
|
||||
|
||||
# 4. Berechnungsmodell
|
||||
@@ -1857,11 +1960,58 @@ falls (renteA + renteB) > cap:
|
||||
|
||||
Die Rente ist danach **nominal fix** – sie wird über die Phasen hinweg nicht indexiert und
|
||||
verliert damit real an Kaufkraft (siehe [9.11](#911-ahv-rente-wird-nach-der-pensionierung-nicht-indexiert)).
|
||||
Sie fliesst in `renteTotal` und wird im Einkommen mitgeführt.
|
||||
**Wann** sie fliesst, entscheidet seit 0.26 nicht mehr der Phasentyp, sondern das Alter im
|
||||
jeweiligen Jahr – siehe 4.4.7.
|
||||
|
||||
Referenz: `src/lib/calculations.ts` (`ahvMonthlyFullPension`, `ahvMdje`, `ahvAnnualPension`),
|
||||
`src/lib/constants.ts`.
|
||||
|
||||
### 4.4.7 Referenzalter: Die Rente beginnt mit 65, nicht mit der Pensionierung
|
||||
|
||||
Bis Version 0.25 galt implizit «pensioniert = Rente». Sobald sich das Pensionsalter verschieben
|
||||
lässt ([4.16](#416-pensionsalter-verschieben)), ist das falsch: Die AHV-Rente hängt am
|
||||
**Referenzalter** (`AHV_REFERENCE_AGE = 65`), nicht daran, wann jemand aufhört zu arbeiten.
|
||||
|
||||
Daraus folgen drei Fälle:
|
||||
|
||||
| Fall | Was passiert |
|
||||
|---|---|
|
||||
| Pensionierung **mit 65** | unverändert – die Rente fliesst ab Beginn der Pensionsphase |
|
||||
| Pensionierung **nach 65** | Die Erwerbsphase deckt das Referenzalter ab. Die Rente fliesst ab 65 **zusätzlich zum Lohn**. Erfasst werden nur noch die Ausfalljahre bis 65. |
|
||||
| Pensionierung **vor 65** | Die Pensionsphase beginnt vor 65. Bis dahin ist die Person **beitragspflichtig als Nichterwerbstätige(r)**; der Beitrag (`ahvContribution`) ist eine laufende Ausgabe. Mit 65 fällt er weg und die Rente setzt ein. |
|
||||
|
||||
Beide Wechsel können **innerhalb derselben Lebensphase** stattfinden. Die AHV wird deshalb
|
||||
**jahresweise** ausgewertet statt als Phasenkonstante:
|
||||
|
||||
```
|
||||
für jedes Jahr t der Phase:
|
||||
alterImJahr = alterZuPhasenbeginn + t − 1
|
||||
alterImJahr ≥ 65 → Rente fliesst (Einkommen)
|
||||
sonst, wenn pensioniert → Beitrag fällt an (Ausgabe, wirkt auf die Verzehrquote)
|
||||
sonst → nichts
|
||||
```
|
||||
|
||||
Der Verlaufspunkt des AHV-Elements zeigt den Beitrag als **negativen** Wert – so ist in der
|
||||
Grafik zu sehen, dass die AHV in diesen Jahren Geld kostet, statt welches zu bringen.
|
||||
|
||||
**Ein Aufschub der Rente wird nicht abgebildet.** Wer über 65 hinaus arbeitet, könnte den Bezug
|
||||
aufschieben und erhielte dafür einen Zuschlag. Das Tool lässt die Rente stattdessen fliessen –
|
||||
die vorsichtigere Annahme, und eine, die keinen zusätzlichen Entscheid verlangt.
|
||||
|
||||
**Der Beitrag als Nichterwerbstätige(r)** bemisst sich am Vermögen und am Renteneinkommen, nicht
|
||||
am Lohn. Die Bandbreite ist entsprechend enorm: vom Mindestbeitrag von rund **530 CHF/Jahr** bis
|
||||
zum Höchstbeitrag von rund **26'500 CHF/Jahr**. Deshalb gibt es hier **keinen Default** – ein
|
||||
stiller Vorschlag würde nicht hinterfragt (vgl. [9.15](#915-defaults-für-annahmen-sind-gefährlich)).
|
||||
Der Hilfetext nennt die Bandbreite ausdrücklich, damit auch jemand ohne Vorwissen ein Gefühl für
|
||||
die Grössenordnung bekommt.
|
||||
|
||||
**Beitragsjahre:** Wer den Beitrag zahlt, hat keine Ausfalljahre – die Rentenskala bleibt
|
||||
unberührt. Das Modell kürzt die Rente bei Frühpensionierung deshalb nicht; eine Kürzung entsteht
|
||||
nur, wenn man die Jahre ausdrücklich als Ausfalljahre erfasst.
|
||||
|
||||
Referenz: `src/lib/calculations.ts` (`ahvItems` in der Jahresschleife), `AHV_REFERENCE_AGE` in
|
||||
`src/lib/constants.ts`.
|
||||
|
||||
## 4.5 Nominal, real und die Deflatoren
|
||||
|
||||
### 4.5.1 Das V5-Modell
|
||||
@@ -1905,11 +2055,16 @@ Vorab-Abbruch: Ist der Carry-Status `SOLD` → Notiz „Verkauft"; ist er `SETTL
|
||||
### 4.6.1 INCOME / EXPENSE
|
||||
|
||||
```
|
||||
idx = phaseData.teuerungsausgleich ?? 0
|
||||
idx = phaseData.teuerungsausgleich ?? carry.rates.teuerungsausgleich ?? 0 // Punkt A
|
||||
baseValue = hasCarry ? round(carry.flowBasis) : round(phaseData.amount)
|
||||
basis = !hasCarry → round(phaseData.amount)
|
||||
phaseData.amount ist Zahl → round(phaseData.amount) // bewusster Override
|
||||
sonst → baseValue // live vererbt
|
||||
|
||||
// Punkt B: Besitzer ist eine PERSON und in dieser Phase pensioniert,
|
||||
// und es ist kein Betrag erfasst → basis = 0
|
||||
|
||||
// NACH der Jahresschleife (siehe 4.16.6):
|
||||
carry.flowBasis = basis × (1 + idx/100)^duration // für die Folgephase
|
||||
```
|
||||
|
||||
@@ -1921,10 +2076,17 @@ durch. Nur ein bewusst abweichender Wert wird fix gespeichert.
|
||||
Man beachte den Exponenten-Unterschied: der **Endwert** der Phase nutzt `duration − 1`
|
||||
(letztes Jahr), der **Carry** für die Folgephase nutzt `duration` (ein Jahr weiter).
|
||||
|
||||
Die Fortschreibung passiert seit 0.26 **nach** der Jahresschleife – sonst kämen effektive Werte
|
||||
nie in der Folgephase an ([4.16.6](#4166-fehlerbehebung-fortgeschriebener-basiswert-und-effektive-werte)).
|
||||
|
||||
### 4.6.2 AHV
|
||||
|
||||
Erwerbstätig → nur Zusammenfassung („N Ausfalljahre" / „Keine Ausfalljahre"), kein Wert.
|
||||
Pensioniert → `startValue = endValue = rente`, Summand in `renteTotal`.
|
||||
Seit 0.26 **jahresweise** statt phasenweise ([4.4.7](#447-referenzalter-die-rente-beginnt-mit-65-nicht-mit-der-pensionierung)):
|
||||
Das Element meldet Rente, Beitrag und Startalter des Besitzers an die Jahresschleife an, die je
|
||||
Jahr entscheidet, was fliesst. `startValue` / `endValue` zeigen den Stand im ersten bzw. letzten
|
||||
Jahr der Phase; die Zusammenfassung nennt beide Zustände, wenn das Referenzalter mitten in der
|
||||
Phase liegt («Beitrag … → Rente …»). Die Rente läuft **nicht** mehr über `renteTotal`, weil sie
|
||||
innerhalb einer Phase einsetzen kann.
|
||||
|
||||
### 4.6.3 PENSION_FUND
|
||||
|
||||
@@ -2258,6 +2420,7 @@ Zentral in `src/lib/constants.ts` geführt, weil sie sich periodisch durch Bunde
|
||||
| `AHV_GROSS_FROM_NET_FACTOR` | 1.12 | Netto → Brutto für die AHV; Herleitung siehe [4.4.5](#445-netto-brutto-umrechnung-für-die-ahv) |
|
||||
| `AHV_COUPLE_CAP_FACTOR` | 1.5 | Ehepaar-Plafonierung: 150 % der Einzel-Maximalrente |
|
||||
| `AHV_FULL_CONTRIBUTION_YEARS` | 44 | Volle Beitragsdauer (Rentenskala 44) |
|
||||
| `AHV_REFERENCE_AGE` | 65 | Referenzalter – ab hier fliesst die Rente, **unabhängig vom Pensionsalter** ([4.4.7](#447-referenzalter-die-rente-beginnt-mit-65-nicht-mit-der-pensionierung)) |
|
||||
| `PILLAR_3A_MAX_ANNUAL` | 7'258 | Max. 3a-Beitrag/Jahr für PK-Versicherte (2026) |
|
||||
| `DEFAULT_PK_CONVERSION_RATE` | 6 % | Umwandlungssatz |
|
||||
| `DEFAULT_CAPITAL_TAX_RATE` | 8 % | Kapitalbezugssteuer |
|
||||
@@ -2526,6 +2689,7 @@ falsch zu werden:
|
||||
| Einkommen | **relativ %** | skaliert `amount` aller `INCOME`-Elemente |
|
||||
| Lohnentwicklung | **Δ Prozentpunkte** | verschiebt `teuerungsausgleich` der `INCOME`-Elemente |
|
||||
| Wertsteigerung der Immobilie | **Δ Prozentpunkte** | verschiebt `valueGrowth` |
|
||||
| Pensionsalter Person A / B | **Δ Jahre** | verschiebt die Phasengrenze der Pensionierung ([4.16](#416-pensionsalter-verschieben)) |
|
||||
|
||||
Die Begründungen im Einzelnen:
|
||||
- **Absolut** nur bei der Inflation – es gibt genau einen plan-weiten Wert.
|
||||
@@ -2777,10 +2941,11 @@ Elemente einzeln um +1 pp zu heben ergibt exakt dasselbe Endvermögen wie der Sa
|
||||
Frage, die man tatsächlich stellt; Ausgaben-Elemente sind typischerweise viele kleine Posten,
|
||||
deren Einzelregler das Panel fluten würden, ohne eine bessere Frage zu ermöglichen.
|
||||
|
||||
**Das Pensionsalter fehlt weiterhin** – aus demselben Grund wie beim Tornado
|
||||
([9.18](#918-tornado-was-der-chart-nicht-leistet)): Es liesse sich nicht verschieben,
|
||||
ohne die Phasengrenzen mitzuziehen. Ersatzweise gibt es **Lebensdauer** (letzte Phase
|
||||
verlängern/verkürzen), was das Langlebigkeitsrisiko abdeckt, nicht aber die Frühpensionierung.
|
||||
**Das Pensionsalter ist seit 0.26 als Regler dabei** (je Person einer, sofern überhaupt
|
||||
Spielraum besteht). Sein Bereich ist **plan-abhängig**: Er endet dort, wo eine angrenzende
|
||||
Lebensphase unter ein Jahr fiele – ein Regler, der stumm an seiner Grenze klebt, wäre schlechter
|
||||
als keiner. Der Treiber **Lebensdauer** bleibt daneben bestehen; er beantwortet die andere Frage
|
||||
(wie lange muss es reichen, statt wann höre ich auf).
|
||||
|
||||
### 4.15.3 Referenz und Kennzahlen
|
||||
|
||||
@@ -2821,6 +2986,96 @@ echtes Szenario übertragen lässt.
|
||||
Referenz: `src/lib/livesim.ts`, `src/lib/sensitivity.ts` (`applyElementDriver`,
|
||||
`tunableElements`), `src/components/LiveSimDialog.tsx`, `src/components/AllocationChart.tsx`.
|
||||
|
||||
## 4.16 Pensionsalter verschieben
|
||||
|
||||
> Modul `src/lib/retirement.ts`. Rein: Es entscheidet nur, **was** geschehen soll; das
|
||||
> Schreiben übernimmt der Aufrufer (Panel: API-Aufrufe; Tornado/Live-Simulation: reine
|
||||
> Plan-Kopie).
|
||||
|
||||
### 4.16.1 Die tragende Invariante
|
||||
|
||||
`maxPhaseDuration(persons, yearsBefore)` kappt jede Phasendauer beim nächsten
|
||||
Pensionierungsereignis. Daraus folgt: **Jede Pensionierung liegt auf einer Phasengrenze.** Genau
|
||||
das macht die Anpassung überhaupt erst möglich – «Pensionsalter ändern» ist gleichbedeutend mit
|
||||
«diese eine Grenze verschieben».
|
||||
|
||||
`retirementBoundaries(plan)` liefert je Person:
|
||||
|
||||
| Feld | Bedeutung |
|
||||
|---|---|
|
||||
| `planYear` | Planjahr der Pensionierung (`retirementAge − age`) |
|
||||
| `phaseIndex` | Index der Phase **vor** der Grenze |
|
||||
| `minDelta` / `maxDelta` | Spielraum, ohne dass eine Nachbarphase unter 1 Jahr fällt |
|
||||
| `mergeDeltaDown` / `mergeDeltaUp` | genau der Wert, bei dem eine Phase entfällt |
|
||||
| `blocked` | Erklärtext, wenn gar keine Anpassung möglich ist (siehe 3.12.2) |
|
||||
|
||||
### 4.16.2 `shiftRetirement(plan, role, delta)`
|
||||
|
||||
```
|
||||
Phase[i].dauer += delta // vor der Grenze
|
||||
Phase[i+1].dauer −= delta // nach der Grenze
|
||||
person.retirementAge += delta
|
||||
```
|
||||
|
||||
Fällt eine der beiden auf 0, wird sie entfernt und die `sequenceNumber` lückenlos neu vergeben.
|
||||
Die Übergangsdaten der entfallenden Phase werden nach der Regel aus 3.12.3 in den Vorgänger
|
||||
gezogen (`mergeTransition`, `mergeCashTransition`). Ausserhalb des erlaubten Bereichs liefert
|
||||
die Funktion `null` – sie klemmt nicht still.
|
||||
|
||||
### 4.16.3 Als Treiber und Regler
|
||||
|
||||
`applyDriver(plan, "retirementA" | "retirementB", jahre)` benutzt dieselbe Funktion. Zwei
|
||||
bewusste Abweichungen gegenüber der Bedienung:
|
||||
|
||||
* Der Bereich endet bei `minDelta`/`maxDelta`, **ohne** Zusammenlegung. Eine Zusammenlegung
|
||||
verändert den Plan inhaltlich (zwei Übergänge werden einer) – dafür ist eine
|
||||
Was-wäre-wenn-Betrachtung der falsche Ort.
|
||||
* Eine zu weite Eingabe wird auf das Mögliche **gekürzt** statt verworfen: Eine Bandbreite von
|
||||
±5 Jahren soll auch dann etwas zeigen, wenn nur ±2 möglich sind. Bleibt gar kein Spielraum,
|
||||
erklärt `ineffectiveReason` den Nullbalken.
|
||||
|
||||
Der Hebel ist doppelt – länger Einkommen **und** kürzer Verzehr – und deshalb meist einer der
|
||||
grössten im Tornado.
|
||||
|
||||
### 4.16.4 Punkt B: Einkommen endet mit der Pensionierung
|
||||
|
||||
Bis 0.25 lief ein Erwerbseinkommen stillschweigend in die Pensionsphase weiter (der Basiswert
|
||||
wird ja fortgeschrieben). Bei fixem Pensionsalter fiel das kaum auf; sobald sich die Grenze
|
||||
verschieben lässt, ist es ein handfester Fehler – «drei Jahre früher aufhören» hätte sonst gar
|
||||
keine Wirkung gehabt.
|
||||
|
||||
```
|
||||
Kategorie INCOME, Element ist EINER PERSON zugeordnet,
|
||||
Person in dieser Phase pensioniert, kein ausdrücklicher Betrag erfasst
|
||||
→ Basiswert = 0
|
||||
```
|
||||
|
||||
Drei bewusste Einschränkungen:
|
||||
|
||||
* **Nur personenzugeordnete Einkommen.** «Gemeinsam» (Mieterträge, Ausschüttungen) hängt nicht
|
||||
an der Erwerbstätigkeit einer Person und läuft weiter.
|
||||
* **Ein ausdrücklich erfasster Betrag gewinnt.** Sonst liesse sich ein Teilzeitpensum oder eine
|
||||
Überbrückungsrente nach der Pensionierung nicht abbilden.
|
||||
* Das Element zeigt in dieser Phase den Hinweis, dass es wegen der Pensionierung auf 0 steht.
|
||||
|
||||
### 4.16.5 Vererbte Wiederkehr-Parameter (Punkt A)
|
||||
|
||||
Der `Carry` führt neu eine Tabelle `rates` mit den zuletzt verwendeten Werten. Ist ein Feld in
|
||||
einer Phase nicht gesetzt, gilt der Wert aus der Vorphase; der verwendete Wert wird wieder
|
||||
abgelegt, sodass die Kette über beliebig viele Phasen trägt. Betroffen sind
|
||||
`teuerungsausgleich`, `expectedReturn`, `annualContribution`, `annualWithdrawal`,
|
||||
`amortization`, `valueGrowth`, `interestRate` und `annualRepayment`.
|
||||
|
||||
### 4.16.6 Fehlerbehebung: fortgeschriebener Basiswert und effektive Werte
|
||||
|
||||
Der Basiswert der Folgephase (`carry.flowBasis`) wurde beim **Element-Setup** berechnet, also
|
||||
**vor** der Jahresschleife. Ein effektiver Wert setzt den Basiswert aber erst **in** der
|
||||
Jahresschleife neu (`rebaseFlow`). Folge: Ein für 2031 erfasster Lohn wirkte bis zum Ende seiner
|
||||
Phase – und fiel an der Phasengrenze stillschweigend auf den geplanten Wert zurück.
|
||||
|
||||
Die Fortschreibung passiert neu **nach** der Jahresschleife, gemeinsam mit den Endwerten. Zwei
|
||||
Regressionstests in `actuals.test.ts` halten den Fall fest.
|
||||
|
||||
---
|
||||
|
||||
# 5. Technische Spezifikation
|
||||
@@ -2905,6 +3160,8 @@ PlanComputed ← an den Client geliefert
|
||||
| `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. |
|
||||
| `retirement.ts` | Pensionsalter verschieben: Spielraum je Person, Verschiebung der Phasengrenze, Zusammenlegung zweier Übergänge (Kap. 4.16). Rein, ohne I/O. |
|
||||
| `transitions.ts` | Reine Übergangs-Regeln (Vorbelegung, «beantwortet?», Cash-Zusammenfassung). Liegt hier und nicht in einer Komponente, weil auch der Server sie braucht -- ein Import aus `src/components` bricht erst in der Produktion. |
|
||||
| `phaseplan.ts` | Ableitung der Lebensabschnitte (Erwerb/Misch/Pension) aus den fixen Pensionierungszeitpunkten für den Assistenten (Kap. 3.2.8). Rein. |
|
||||
| `constants.ts` (erweitert) | zusätzlich `SYSTEM_PARAMETERS`: dieselben Werte maschinenlesbar mit Bedeutung, Herleitung, Quelle und Stand – Grundlage der Systemparameter-Ansicht |
|
||||
| `diff.ts` | Abweichungs-Erkennung eines Szenarios gegen sein Eltern-Szenario (Kap. 3.2.6) |
|
||||
@@ -3041,6 +3298,7 @@ Referenz: `prisma/schema.prisma` Zeilen 4–6, `src/lib/elements.ts` Zeilen 48
|
||||
| `amount` | INCOME, EXPENSE | ≥ 0 |
|
||||
| `teuerungsausgleich` | INCOME, EXPENSE | −20 bis 50 |
|
||||
| `gapYears` | AHV | Integer ≥ 0 |
|
||||
| `ahvContribution` | AHV | ≥ 0 – Beitrag als Nichterwerbstätige(r) bis zum Referenzalter ([4.4.7](#447-referenzalter-die-rente-beginnt-mit-65-nicht-mit-der-pensionierung)) |
|
||||
| `avgIncomeBefore` | AHV – nur wenn bei Planbeginn **bereits pensioniert** | ≥ 0, **real** |
|
||||
| `gapYearsBefore` | AHV – dito | Integer 0–50 |
|
||||
| `currentValue` | PENSION_FUND, PILLAR_3A | ≥ 0 |
|
||||
@@ -3057,6 +3315,11 @@ Referenz: `prisma/schema.prisma` Zeilen 4–6, `src/lib/elements.ts` Zeilen 48
|
||||
| `valueGrowth` | REAL_ESTATE – Wertsteigerung %/Jahr auf die Liegenschaft | −20 bis 20 |
|
||||
| `annualRepayment` | OTHER_DEBT | ≥ 0 |
|
||||
|
||||
**Vererbbare Felder (Punkt A, Kap. 3.12.4):** `teuerungsausgleich`, `expectedReturn`,
|
||||
`annualContribution`, `annualWithdrawal`, `amortization`, `valueGrowth`, `interestRate` und
|
||||
`annualRepayment` sind ab Phase 2 in der Regel **nicht gesetzt** – die Berechnung übernimmt dann
|
||||
den Wert der Vorphase. Alle übrigen Felder bedeuten «nicht gesetzt = 0» wie bisher.
|
||||
|
||||
### 5.4.4 JSON-Payload `TransitionData`
|
||||
|
||||
| Feld | Kategorien | Zod-Regel |
|
||||
@@ -3089,6 +3352,9 @@ Liegt in `Phase.cashTransition`. Validierung über `cashTransitionSchema`.
|
||||
| `inflowTaxRate` | Steuer auf den Zufluss, Default 0 % | 0–100 |
|
||||
| `outflowLabel` | Bezeichnung der Kosten (z. B. „Poolbau") | ≤ 120 Zeichen |
|
||||
| `outflowAmount` | Betrag **real** (heutige Kaufkraft) | ≥ 0 |
|
||||
| `capitalUseAmortizationPct` | Anteil des Kapitalzuflusses in die Amortisation (Kap. 3.12.5) | 0–100 |
|
||||
| `capitalUseInvestPct` | Anteil des Kapitalzuflusses in eine Anlage | 0–100 |
|
||||
| `capitalUseTargetElementId` | Ziel der Anlage-Quote; ohne Angabe das erste aktive Sonstige Vermögen | ≤ 60 Zeichen |
|
||||
|
||||
Pro Übergang ist **genau ein** Zufluss und **eine** Kostenposition möglich – siehe
|
||||
[9.7](#97-nur-ein-zufluss-und-eine-kostenposition-pro-übergang).
|
||||
@@ -3318,6 +3584,20 @@ Liefert Eingabe, Berechnung **und die Vergleichsbasis** in einem Zug:
|
||||
`base` ist das Eltern-Szenario (null beim Basisszenario) – daraus rechnet der Client den Diff.
|
||||
Dies ist der einzige Endpunkt, der die Berechnung ausführt (neben `export`). → 404 wenn fremd.
|
||||
|
||||
### `POST /api/scenarios/<scenarioId>/retirement`
|
||||
Verschiebt das Pensionsalter einer Person und damit die zugehörige Phasengrenze
|
||||
([3.12](#312-pensionsalter-anpassen)):
|
||||
```json
|
||||
{ "role": "PERSON_A", "delta": -4, "confirmMerge": false }
|
||||
```
|
||||
Bewusst kein `PATCH` auf `retirementAge`: Die Änderung betrifft immer **zwei** Phasendauern
|
||||
gleichzeitig und kann eine Phase entfallen lassen.
|
||||
→ 200 `{ ok, removedPhaseId, retirementAge }` ·
|
||||
→ 400 mit Erklärtext, wenn gesperrt oder ausserhalb des Spielraums ·
|
||||
→ **409** `{ needsMergeConfirmation: true, removedPhaseId, removedPhaseName, mergedIntoPhaseId }`,
|
||||
wenn dabei eine Lebensphase entfiele und `confirmMerge` nicht gesetzt ist. Der Client fragt
|
||||
vorher selbst (er kennt den Plan), der 409 ist die serverseitige Absicherung.
|
||||
|
||||
### `PATCH /api/scenarios/<scenarioId>`
|
||||
Akzeptiert eine **Union** von zwei Formen:
|
||||
1. Vollständiges Profil: `{ householdType, inflationRateDefault, persons[], name? }` – ersetzt
|
||||
@@ -3467,12 +3747,12 @@ Include `src/**/*.test.ts`). Es gibt **keine** Komponenten-, API- oder E2E-Tests
|
||||
|
||||
| Datei | Tests | Schwerpunkt |
|
||||
|---|---|---|
|
||||
| `calculations.test.ts` | 43 | AHV-Rentenformel, Immobilie, Teilverkauf, Sonderamortisation, AHV einkommensabhängig, „V5 Golden Tests" |
|
||||
| `sensitivity.test.ts` | 15 | Treiber-Transformationen (Reinheit, Einheiten, Kappung), Verfügbarkeit, Tornado-Sortierung und -Richtung, Erklärung wirkungsloser Treiber |
|
||||
| `calculations.test.ts` | 53 | AHV-Rentenformel, Immobilie, Teilverkauf, Sonderamortisation, AHV einkommensabhängig, AHV-Referenzalter (Beitrag vor 65, Rente ab 65), Einkommen endet mit der Pensionierung, Vererbung der Wiederkehr-Parameter, „V5 Golden Tests" |
|
||||
| `sensitivity.test.ts` | 20 | Treiber-Transformationen (Reinheit, Einheiten, Kappung), Verfügbarkeit, Tornado-Sortierung und -Richtung, Erklärung wirkungsloser Treiber, Pensionsalter als Treiber |
|
||||
| `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 |
|
||||
| `actuals.test.ts` | 22 | Zuordnung über Kopie-Ketten, Sprung im Ist-Jahr, Weiterrechnen ab dem Ist-Wert, geschlossene Brücken, Fluss-Rückrechnung |
|
||||
| `actuals.test.ts` | 24 | Zuordnung über Kopie-Ketten, Sprung im Ist-Jahr, Weiterrechnen ab dem Ist-Wert, geschlossene Brücken, Fluss-Rückrechnung, Wirkung über die Phasengrenze hinaus |
|
||||
| `dataview.test.ts` | 7 | beide Sichten in einem Zug, Rückfall ohne Ist-Werte, Abweichungsmessung |
|
||||
| `ratefields.test.ts` | 17 | Erkennung geänderter Raten, Reichweite (diese/folgende/alle), Erhalt der übrigen Werte in den Zielphasen |
|
||||
| `versioning.test.ts` | 22 | Nummerierung und Zusammenfassung je Sitzung, unveränderte Stände, Benutzertrennung, Bauplan des Wiederherstellens, Auswirkung auf Kind-Szenarien |
|
||||
@@ -3480,10 +3760,12 @@ Include `src/**/*.test.ts`). Es gibt **keine** Komponenten-, API- oder E2E-Tests
|
||||
| `report.test.ts` | 9 | Zusammenfassung, Basis-Angabe zu JEDER Kennzahl, Szenario-Deckelung, Plan/Ist-Block, Haftungsausschluss, gültige PDF-Datei |
|
||||
| `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 |
|
||||
| `bridges.test.ts` | 13 | Vermögens- und Cash-Brücke gehen über acht Plankonstellationen ohne Restgrösse auf; Umbuchungen bleiben aus der Vermögensbrücke heraus; Kapitalverwendung nach Quote (Punkt C) |
|
||||
| `retirement.test.ts` | 16 | Spielraum und Sperren je Person, Verschiebung ohne Änderung der Gesamtdauer, Wegfall einer Phase, Zusammenführung der Übergangs-Entscheide |
|
||||
| `server-boundary.test.ts` | 1 | statischer Wächter: kein Modul unter `src/lib` importiert aus `src/components` |
|
||||
| `diff.test.ts` | 9 | Abweichungs-Erkennung gegen das Eltern-Szenario |
|
||||
| `migrations.test.ts` | 3 | spielt alle Migrationen gegen echtes PostgreSQL (PGlite) ein; prüft zusätzlich die V7-**Datenübernahme** (Basisszenario gewinnt, Pensionsalter bleiben szenario-eigen) |
|
||||
| **Total** | **221** | |
|
||||
| **Total** | **261** | |
|
||||
|
||||
## 8.2 Testfälle
|
||||
|
||||
@@ -3768,25 +4050,25 @@ Aussagekräftig ist die **Reihenfolge**, nicht der absolute Betrag.
|
||||
härter als die Summe der Einzelbalken – weil in der Folge Kapital verzehrt wird, das später zur
|
||||
Verzinsung fehlt. Für Kombinationen ist die Monte-Carlo-Simulation zuständig.
|
||||
|
||||
**Das Pensionsalter ist bewusst nicht enthalten.** Die Roadmap nennt es als Top-Hebel, aber
|
||||
`retirementAge` lässt sich im aktuellen Datenmodell nicht isoliert variieren, ohne die
|
||||
Phasengrenzen mitzuverschieben – und das Ergebnis wäre nicht ungenau, sondern **irreführend**.
|
||||
**Das Pensionsalter ist seit 0.26 enthalten** – zuvor bewusst nicht, und die Begründung von
|
||||
damals erklärt, warum die heutige Umsetzung so aussieht, wie sie aussieht.
|
||||
|
||||
Ein isoliert verändertes `retirementAge` wäre nicht ungenau gewesen, sondern **irreführend**.
|
||||
Beispiel: Person 45, Pension mit 65, Phase 1 = 20 Jahre (45→65), Phase 2 = 20 Jahre.
|
||||
|
||||
- **Pensionsalter auf 62:** Phase 1 beginnt mit 45, also `45 < 62` → die Phase bleibt vollständig
|
||||
Erwerbsphase. Die Person arbeitet im Modell weiterhin bis 65, der Pensions-Übergang liegt an
|
||||
derselben Grenze. Wirkung auf das Ergebnis: **praktisch null.**
|
||||
- **Pensionsalter auf 68:** Phase 2 beginnt mit 65, also `65 < 68` → Phase 2 wird zur
|
||||
**Erwerbsphase**, das Einkommen läuft weiter. Zugleich wird `ownerRetiresNext` an der Grenze
|
||||
nach Phase 1 falsch (65 ≥ 68 trifft nicht zu), womit der **Pensions-Übergang komplett entfällt**:
|
||||
keine PK-Verrentung, kein 3a-Bezug, keine AHV-Rente. Der Balken wäre riesig – er misst aber den
|
||||
Ausfall der Vorsorgelogik, nicht „drei Jahre länger arbeiten".
|
||||
- **Auf 62 gesetzt:** Phase 1 beginnt mit 45, also `45 < 62` → die Phase bliebe vollständig
|
||||
Erwerbsphase. Die Person arbeitete im Modell weiterhin bis 65. Wirkung: **praktisch null.**
|
||||
- **Auf 68 gesetzt:** Phase 2 beginnt mit 65, also `65 < 68` → Phase 2 würde zur Erwerbsphase.
|
||||
Zugleich wäre `ownerRetiresNext` an der Grenze nach Phase 1 falsch, womit der
|
||||
**Pensions-Übergang komplett entfiele**: keine PK-Verrentung, kein 3a-Bezug. Der Balken wäre
|
||||
riesig – er misst aber den Ausfall der Vorsorgelogik, nicht „drei Jahre länger arbeiten".
|
||||
|
||||
Fachlich korrekt wäre nur, `retirementAge` **und** die Phasengrenze gemeinsam zu verschieben
|
||||
(Erwerbsphase kürzer, Pensionsphase länger). Das hat eigene Sonderfälle – Paare mit
|
||||
unterschiedlichem Pensionsalter, Grenzen abseits des Pensionsereignisses, Verschiebung grösser als
|
||||
die Phasendauer – und ist als eigener Arbeitsschritt offen. Der verwandte Treiber **Lebensdauer**
|
||||
(Dauer der letzten Phase) ist dagegen sauber abgebildet und deckt einen Teil des Bedürfnisses ab.
|
||||
Der Treiber verschiebt deshalb `retirementAge` **und** die Phasengrenze gemeinsam
|
||||
([4.16](#416-pensionsalter-verschieben)). Zwei Eigenheiten bleiben und sind im Dialog benannt:
|
||||
Der Spielraum endet bei den angrenzenden Phasendauern (eine **Zusammenlegung** findet im Tornado
|
||||
bewusst nicht statt), und eine zu weite Bandbreite wird auf das Mögliche gekürzt statt verworfen.
|
||||
Der verwandte Treiber **Lebensdauer** (Dauer der letzten Phase) bleibt daneben bestehen – er
|
||||
beantwortet die andere Frage: nicht „wann höre ich auf", sondern „wie lange muss es reichen".
|
||||
|
||||
## 9.19 Simulationsparameter werden nicht gespeichert
|
||||
|
||||
|
||||
Reference in New Issue
Block a user