Uebersicht der offenen Punkte statt Assistent, Bestaetigung je Phasenzelle
Deploy App / deploy (push) Successful in 1m55s

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-16 15:02:15 +02:00
parent 6ff144d7e1
commit f22b0a3f27
22 changed files with 710 additions and 1006 deletions
+102 -64
View File
@@ -4,10 +4,10 @@
| | |
|---|---|
| **Dokument** | Funktionale und Technische Spezifikation FPT |
| **Version** | 0.38 |
| **Version** | 0.39 |
| **Datum** | 2026-07-25 |
| **Status** | Lebendes Dokument |
| **Codestand** | Arbeitsstand nach `2f6b788` inkl. Assistent auf zwei Schritte (Branch `main`) |
| **Codestand** | Arbeitsstand nach `6ff144d` inkl. Übersicht der offenen Punkte (Branch `main`) |
| **Ersetzt** | `FDD_TDD_FPT.docx` (v1v5) 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.39 | 2026-08-16 | Claude (Opus 5) | **Aus dem Assistenten wird eine Übersicht der offenen Punkte** (Kap. 3.14 neu geschrieben). Der Unterschied ist grundsätzlich: Ein Assistent ist ein **Ablauf** («tu dies, dann das») und trägt nur beim ersten Aufsetzen -- wer einen bestehenden Plan öffnete, bekam Schritte angeboten, die längst erledigt waren, und die Kachel wusste nichts davon. Die Übersicht ist ein **Zustand** («das ist noch offen»); sie wird aus dem Plan abgeleitet und trägt bei jedem Plan, in jeder Reihenfolge, auch beim zwanzigsten Szenario. (1) Der Assistent samt Schritten, gespeichertem Fortschritt (`Scenario.assistantProgress`) und Endpunkt entfällt. Oben rechts steht neu die Übersicht: je Lebensphase und je Übergang, was dort fehlt, jeweils mit Sprung dorthin. Ist nichts offen, meldet sie das ausdrücklich -- **grün und in Worten**, nicht als «0». (2) **Die Bestätigung gilt neu auch für Phasenwerte** (`PhaseData.confirmed`). Eine neue Lebensphase übernimmt die Werte der Vorphase, aber sie gelten als unbestätigt, bis jemand hingeschaut hat: vorbelegen ja, stillschweigend übernehmen nein. Betroffen sind die phasenspezifischen Annahmen -- Renditen, Lohnentwicklung, Teuerung, Hypothekarzins, Wertsteigerung. Damit gilt in der ganzen Matrix derselbe Mechanismus, den die Übergänge seit 0.35 haben. **Bestätigen heisst «ich habe hingeschaut», nicht «festnageln»**: Der Haken steht NEBEN den Werten und kopiert nichts -- die Feld-Vererbung ([3.12.4](#3124-punkt-a-aus-vorphase-übernehmen)) bleibt unberührt, sonst wäre jede bestätigte Phase eingefroren und der ganze Punkt-A-Mechanismus hinfällig. Ein Test sichert genau das. (3) **Die Spar-/Verzehrquote zählt eigens** (`Phase.ratesConfirmed`). Man kann jede Zelle angeschaut und die Verteilung trotzdem nie getroffen haben -- dann bliebe der ganze Überschuss still auf dem Cash-Konto liegen, und die Übersicht meldete «alles erledigt». (4) **Sammel-Bestätigung je Phase:** Bei sechs Elementen und fünf Phasen wären es dreissig Klicks; der Phasenkopf trägt deshalb ein Abzeichen mit der Anzahl offener Annahmen, das alle auf einmal bestätigt. Unbestätigte Zellen tragen dieselbe Attention-Markierung wie offene Übergänge. (5) **Die Bestandsaufnahme bleibt als eigener Dialog** (`InventoryDialog`, vormals `AssistantSteps`) -- als grosser Knopf im leeren Plan und dauerhaft unter den Schnellaktionen. Sie ist der einzige Sammel-Dialog, der geblieben ist, weil sie als einzige etwas leistet, das die Matrix nicht kann: sieben Kategorien in einem Zug erfassen, bevor man weiss, wie das Tool aufgebaut ist. (6) Zwei **Startzustände** statt einer Zahl: Ohne Elemente und ohne Lebensphasen gibt es naturgemäss nichts Offenes, obwohl der Plan leer ist -- eine «0» wäre dort eine Lüge. Die Kachel fordert stattdessen zum nächsten Handgriff auf. Neues Modul `review.ts`, neue Komponenten `ReviewTile` und `InventoryDialog`; entfallen sind `assistant.ts`, `Assistant` und `AssistantStepDialog`. **Keine Datenmigration** (Pläne wurden vorgängig gelöscht). 7 Tests ergänzt (322 → 329). |
| 0.38 | 2026-08-16 | Claude (Opus 5) | **Der Assistent führt bis zur ersten Lebensphase und nicht weiter.** Sieben Schritte waren ein Versprechen, das die letzten fünf nicht einlösten: Sie zeigten Stationen, statt zu führen. Neu sind es **zwei**, die tragen; wie es danach weitergeht, wird eigens entworfen. (1) **Trennung von Tatsache und Annahme.** Schritt 1 erfasst nur noch, was man nachschlagen kann Kontostand, Guthaben, Kaufpreis, Restschuld. Renditen, Lohnentwicklung, Teuerung, Hypothekarzins und Wertsteigerung wandern in Schritt 2, wo sie hingehören: **Annahmen gelten immer nur für einen Zeitraum.** Bis 0.37 standen beide zusammen in der Bestandsaufnahme, wodurch eine Annahme wie eine Tatsache aussah. Neu trägt jede Kategorie `baseFields` und `phaseFields`. (2) **Neuer Schritt 2 «Erste Lebensphase»**: Name → Dauer → jährliche Annahmen je Element → **Sparquote verteilen**. Erst danach ist der Schritt fertig; endete er nach den Annahmen, bliebe der ganze Überschuss stumm auf dem Cash-Konto liegen. Die Dauer ist an der Pensionierung **gekappt** und wird im Feld begrenzt, statt hinterher als Fehler zu erscheinen. Der **PK-Beitrag** steht hier er stammt aus dem Bruttolohn und lässt sich aus der Sparquote gar nicht verteilen, wäre also sonst durch alle Maschen gefallen. (3) **Pensionsalter im Basisszenario fest auf 65**, nicht änderbar. Vorbezug, Aufschub und gestaffelte Kapitalbezüge bleiben im Rechenkern vollständig erhalten (samt Tests), werden hier aber nicht angeboten sie gehören zu einer eigenen Szenario-Art (Roadmap Nr. 48). Der Knopf «Pensionsplanung» und der zugehörige Bildschirm entfallen; die Grundeinstellungen nennen das Pensionsalter je Person und weisen auf die spätere Szenario-Art hin. (4) **Der Planungshorizont wird abgeleitet** er ist die Summe der Lebensphasen. Dritter und letzter Anlauf: 0.35 führte ihn als Endalter je Person, 0.36 als Jahreszahl am Szenario, beide Male eine zweite Wahrheit über dieselbe Sache. Wer die Phasen einzeln plant, hat den Horizont bereits bestimmt. `Scenario.planningHorizonYears`, der Endpunkt `/horizon` und `planHorizonChange` entfallen. (5) Entfallen: die Schritte 3 bis 7 samt ihren Werkzeugen und `RetirementPanel`. |
| 0.37 | 2026-08-16 | Claude (Opus 5) | **Nachbesserungen zur Bestandsaufnahme** (aus einer Testrunde des Nutzers). (1) **Die Knöpfe tragen die Kategorienamen** (Einkommen · Ausgaben · Pensionskasse · Säule 3a · Sonstiges Vermögen · Immobilie · Sonstige Schulden), die Alltagssprache steht als Erklärzeile darunter. Vorher hatte der Assistent eigene Vokabeln erfunden am deutlichsten «Lebenshaltung», hinter der sich ein Feld «Ausgaben pro Jahr» verbarg. Wer hier eine andere Sprache lernt als die, die Matrix, Rechenwege und Bericht sprechen, sucht sie später vergeblich. Der Elementname darf konkret bleiben: Kategorie «Ausgaben», Zeile «Lebenshaltung». (2) **Kein Speichern-Knopf je Element mehr.** Der Entwurf liegt neu auf Ebene des ganzen Schritts statt in der einzelnen Karte nur so überlebt er den Wechsel des Reiters, denn dabei verschwinden die Karten des vorigen Bereichs samt ihrem Zustand. «Schritt abschliessen» schreibt alles in einem Zug. Weil damit alle Zahlen an einem Ort liegen, zeigt der Dialog neu die Summe **«Vermögen heute» live beim Tippen** die Rückmeldung, die sonst mit dem Speichern-Knopf verloren gegangen wäre. Wer den Dialog mit ungespeicherten Eingaben schliesst, wird gefragt. (3) **«Zurück/Weiter» entfällt** in Schritt 1 und 2: Die Reiter oben sind der Weg durch die Bereiche, zwei Navigationen für dieselbe Bewegung sind eine zu viel. (4) Der 3a-Hinweis zum Höchstbetrag ist gestrichen er beantwortete an dieser Stelle eine Frage, die niemand stellt. (5) **Neue Matrix-Spalte «Start»** und: **die Matrix erscheint, sobald es Elemente gibt** Lebensphasen sind dafür nicht mehr nötig. Das war ein Fehler in 0.36: Die Stammdaten wurden eigens dafür eingeführt, dass eine Bestandsaufnahme ohne Zeitachse möglich ist, und dann rendete die Matrix nichts, weil sie ganz an den Phasen hing. Die Spezifikation behauptete das Richtige, der Code hielt es nicht. Die Spalte löst zugleich ein zweites Problem: Sie ist der Ort, an dem ein Startwert **änderbar** ist. Ohne sie hätte man ihn in Phase 1 bearbeitet und dabei still einen Phasenwert geschrieben, der die Stammdaten überdeckt dieselbe Zahl an zwei Orten. (6) Der Text des leeren Zustands ist korrigiert: Seit dem Umbau kommen die Elemente zuerst und die Phasen danach. Neue gemeinsame Komponente `BaseFields` (Assistent und Matrix nutzen dieselben Felder), neues Panel für die Stammdaten. 2 Tests ergänzt (334 → 336). |
| 0.36 | 2026-07-26 | Claude (Opus 5) | **Der Assistent wird das Herzstück** (neues Kapitel 3.14). Ein Umbau von Onboarding, Bildschirmaufbau und Führung. (1) **Ein Weg hinein.** Die Übersicht zeigt im leeren Zustand nur noch «Meinen ersten Finanzplan anlegen»; der geführte Start und der Beispielplan entfallen. Drei Knöpfe waren eine Wahl, die niemand treffen kann, der das Tool noch nicht kennt. Der Plan-Dialog fragt nur noch sechs Dinge: Name, Haushaltsform, Personennamen, Startjahr, Alter, Inflation. **Das Pensionsalter wird nicht mehr abgefragt** es ist kein Stammdatum, sondern der erste Entscheid der Pensionsplanung, und es erzeugt eine Phasengrenze. Bis dahin gilt das Referenzalter. (2) **Element-Stammdaten** (`FinancialElement.baseData`): Bestand bei Planbeginn und Ausgangs-Annahmen hängen neu am ELEMENT statt in Phase 1. Zwei Gründe ein Startwert ist nicht «phase-1-spezifisch», sondern schlicht der Stand am Anfang; und Elemente lassen sich damit erfassen, **bevor es Lebensphasen gibt**. Genau das braucht die Bestandsaufnahme als erster Schritt. Zugleich sind die Stammdaten die **Wurzel der Feld-Vererbung**: Phase 1 hatte bisher nichts, von dem sie hätte erben können, und fiel auf 0. (3) **Aus einem Fixpunkt werden bis zu vier je Person.** `phaseplan.ts` kannte nur das Erwerbsende. Da AHV, Pensionskasse und jedes 3a-Konto eigene Bezugsalter haben (`pkWithdrawalAge` neu), erzwingt jeder Bezugsbeginn eine Phasengrenze sonst fiele er mitten in eine Phase und rutschte auf die nächste Grenze, unter Umständen Jahre später. Die Phasendauer-Kappung zählt sie mit; Ereignisse im selben Jahr teilen sich eine Grenze. (4) **Planungshorizont in JAHREN** am Szenario (`planningHorizonYears`) statt als Endalter je Person: eine Zahl statt zweier, die bei einem Paar auseinanderlaufen könnten; die Endalter sind die Ableitung. Ersetzt `Person.planningHorizonAge` aus 0.35. (5) **Neuer Szenario-Bildschirm.** Zwei farblich getrennte Hälften: oben die Steuerung in vier Kacheln (Grundeinstellungen mit «Pensionsplanung» je Person · Kennzahlen inkl. neuem **«Vermögen heute»** · Schnellaktionen · Assistent) plus die Zeitachse über die volle Breite; unten die Matrix. Die Aktionsleiste über der Matrix ist verschwunden: **«+ Element», «+ Phase», der Nominal/Real-Umschalter und «Alle auf-/zuklappen» sitzen jetzt in der Ecke oben links der Matrix** sie steuern die Matrix und lagen vorher lose darüber wie Aktionen der ganzen Seite. (6) **Der FPT-Assistent** ersetzt die Karte «Nächste Schritte». Sieben Schritte von der Bestandsaufnahme bis zum Feinschliff. Jeder öffnet ein Popup, das **zuerst erklärt** (welche Fragen der Schritt beantwortet, was man wissen sollte) und **danach das Werkzeug** anbietet; «Selbst erledigen» überspringt beides. Der Haken ist **manuell** das Tool masst sich nicht an zu wissen, wann jemand fertig ist , aber daneben steht der **abgeleitete Stand** («0 Lebensphasen»), damit ein abgehakter Schritt ohne Substanz auffällt. Erledigte rutschen nach unten und werden blass; die Kachel ist gelb, bis alle sieben stehen, dann grün. (7) **Neue Tour**: ein grosses Popup mit einem **nachgebauten** Bildschirm und erfundenen Zahlen, schrittweise erklärt. Das frühere Spotlight lag über der echten Ansicht und hatte auf einem frisch angelegten, leeren Plan nichts hervorzuheben, also gerade dann nicht, wenn es am nötigsten war. Der Preis ist, dass die Attrappe bei UI-Änderungen nachzuführen ist. (8) Entfallen: `PlanWizard` (der Assistent führt jetzt IM Plan statt davor) und `demoplan.ts`. Neue Module `assistant.ts`, neue Komponenten `Assistant`, `AssistantStepDialog`, `AssistantSteps`; neue Endpunkte `PUT /api/elements/<id>/base` und `POST /api/scenarios/<id>/assistant`. **Keine Datenmigration** (Pläne wurden vorgängig gelöscht). 12 Tests ergänzt (322 → 334). |
@@ -308,7 +309,7 @@ die ersten 72 Byte), keine ARIA-Labels auf der Login-Maske und der Befehls-Palet
Es gibt genau **einen** Weg: den Knopf «Meinen ersten Finanzplan anlegen» in der Übersicht
bzw. das «+» in der Seitenleiste. Beide öffnen denselben Dialog. Zur Begründung, warum die
frühere Auswahl aus drei Wegen entfallen ist, siehe
[3.2.8](#328-der-einstieg-ein-weg-eine-tour-ein-assistent).
[3.2.8](#328-der-einstieg-ein-weg-eine-tour-eine-bestandsaufnahme).
Der Dialog fragt Name plus Grundprofil -- **ohne Pensionsalter**, das gehört in die
Pensionsplanung:
@@ -429,7 +430,7 @@ und Grafiken beschriften damit Jahre statt nur Alter. Die Berechnung rechnet unv
Beim Anlegen wird das laufende Jahr vorbelegt; bestehende Pläne wurden per Migration darauf
gesetzt. Kalenderjahr eines Planjahrs: `startYear + (Jahr 1)`.
### 3.2.8 Der Einstieg: ein Weg, eine Tour, ein Assistent
### 3.2.8 Der Einstieg: ein Weg, eine Tour, eine Bestandsaufnahme
Bis 0.35 standen im leeren Zustand **drei** Knöpfe: geführt starten, Beispielplan ansehen, leer
starten. Das ist eine Wahl, die niemand treffen kann, der das Tool noch nicht kennt -- und sie
@@ -444,7 +445,7 @@ Seit 0.36 gibt es genau einen Weg:
Plan-Dialog (sechs Felder)
|
v
Basisszenario, leer -> Tour (Demo-Popup) -> FPT-Assistent
Basisszenario, leer -> Tour (Demo-Popup) -> Bestandsaufnahme
```
**Der Plan-Dialog** fragt nur noch: Name des Plans, Haushaltsform, Namen der Personen
@@ -453,14 +454,14 @@ Seitenleiste.
**Das Pensionsalter wird bewusst NICHT gefragt.** Es ist kein Stammdatum, sondern der erste
Entscheid der Pensionsplanung -- und es erzeugt eine Phasengrenze
([3.14.4](#3144-fixpunkte-jeder-bezugsbeginn-erzwingt-eine-phasengrenze)). Bis Schritt 2 des
Assistenten gilt das Referenzalter.
([3.14.4](#3144-fixpunkte-jeder-bezugsbeginn-erzwingt-eine-phasengrenze)). Im Basisszenario
gilt durchgehend das Referenzalter 65.
**Der frühere Plan-Assistent (`PlanWizard`) und der Beispielplan sind entfallen.** Der Wizard
führte VOR dem Plan durch ein Formular und liess einen danach mit der Matrix allein; der
Assistent führt jetzt IM Plan und bleibt dort, solange man ihn braucht
([3.14](#314-der-fpt-assistent)). Was der Beispielplan leistete -- einmal sehen, wie ein
gefüllter Plan aussieht --, übernimmt die Tour.
führte VOR dem Plan durch ein Formular und liess einen danach mit der Matrix allein. Was der
Beispielplan leistete -- einmal sehen, wie ein gefüllter Plan aussieht --, übernimmt die Tour;
was danach zu tun ist, sagt die Übersicht der offenen Punkte
([3.14](#314-bestandsaufnahme-und-offene-punkte)).
### 3.3.1 Phase anlegen
@@ -772,6 +773,12 @@ Der Übergangs-Spaltenkopf zeigt „N offen" (Akzentfarbe), „N Vorgaben" (gede
Handlungsdefizit ist) oder „geprüft" (grün, Häkchen). Offene Zellen sind hervorgehoben und
zeigen „?".
**Seit 0.39 gilt derselbe Mechanismus auch für Phasenzellen** (`PhaseData.confirmed`,
[3.14.2](#3142-bestätigen-heisst-ich-habe-hingeschaut)). Eine neue Phase übernimmt die Werte
der Vorphase; sie gelten als unbestätigt, bis jemand hingeschaut hat. Damit ist die Ampel
nicht mehr auf die Übergänge beschränkt, sondern deckt die ganze Matrix ab -- die Zählung
liegt in `review.ts`, die Anzeige teilt sich die Attention-Farbe mit den Übergängen.
**Auswirkung auf den PDF-Bericht:** Die Kennzahl «Offene Entscheide» weist beide Töpfe
zusammen aus. Berichte von vor 0.35 sind deshalb nicht direkt vergleichbar.
@@ -1362,7 +1369,7 @@ der Kontext, den Modals nehmen, bleibt erhalten. Genau ein Panel kann offen sein
`Panel`-Union ersetzt die früheren Einzel-Zustände); der `key` erzwingt beim Wechsel den
Neuaufbau des Formulars wie zuvor bei den Dialogen.
**Modals bleiben** für Erstell-Flows (Element, Phase, Plan, Assistent), den geführten
**Modals bleiben** für Erstell-Flows (Element, Phase, Plan, Bestandsaufnahme), den geführten
Übergang (mehrere Objekte auf einmal), die Analysen und die Detailansichten.
Der frühere Dialog «Plan-Einstellungen» heisst im Panel korrekt **«Szenario-Profil»** er
@@ -1392,8 +1399,8 @@ Schritte ohne vorhandenes Ziel werden übersprungen; der «Tour»-Knopf in der o
Funktions-Leiste startet sie jederzeit neu.
Sie liegt seit 0.28 in `AppShell` (nicht mehr in `PlanView`), weil dort die Plan-Erstellung
zusammenläuft, und hat **zwei Auslöser**: Nach **jeder** Plan-Erstellung (Assistent,
Beispielplan, leerer Plan) startet sie **einmal** unabhängig davon, ob sie schon beendet
zusammenläuft, und hat **zwei Auslöser**: Nach **jeder** Plan-Erstellung startet sie
**einmal** unabhängig davon, ob sie schon beendet
wurde (die Erfolgsmeldung «die Tour zeigt dir gleich …» hält damit ihr Versprechen). Zusätzlich
startet sie beim ersten Öffnen eines bestehenden Plans mit Phasen, solange sie noch nie beendet
wurde (localStorage `fpt-tour-done`). Ein aus dem DOM gelesenes Ziel setzt voraus, dass die
@@ -1432,8 +1439,8 @@ Jedes **Szenario** trägt eine Version **A.B** und eine vollständige Änderungs
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**.
Bestandsaufnahme-Dialogs 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
@@ -2108,68 +2115,96 @@ eigenes Feld.
Referenz: `src/lib/retirement-decision.ts`, `src/components/RetirementPanel.tsx`,
`src/components/RetirementFields.tsx`, `src/lib/retirement.ts` (`planHorizonChange`).
## 3.14 Der FPT-Assistent
## 3.14 Bestandsaufnahme und offene Punkte
### 3.14.1 Warum er die «Nächsten Schritte» ersetzt
### 3.14.1 Warum aus dem Assistenten eine Übersicht wurde
Die frühere Karte leitete AB, was zu tun wäre -- und liess einen damit allein. Sie konnte
sagen «4 Übergangs-Entscheide offen», aber nicht, was ein Übergangs-Entscheid überhaupt ist
oder in welcher Reihenfolge man vorgeht.
Ein Assistent ist ein **Ablauf**: tu dies, dann das, dann bist du fertig. Das trägt genau
einmal -- beim ersten Aufsetzen. Wer einen bestehenden Plan öffnete, bekam Schritte
angeboten, die längst erledigt waren, und ein Häkchen, das nichts wusste. Vier Anläufe
(sieben Schritte, dann zwei) haben dasselbe Grundproblem nur kleiner gemacht.
Der Assistent führt stattdessen. Jeder Schritt hat ein eigenes Werkzeug und eine Seite davor,
die erklärt, worum es geht:
Eine Übersicht ist ein **Zustand**: das ist noch offen. Sie wird bei jedem Rendern aus dem
Plan abgeleitet (`reviewPlan`), speichert nichts und kann deshalb nie veralten. Sie trägt bei
jedem Plan, in jeder Reihenfolge, auch beim zwanzigsten Szenario.
| # | Schritt | Was dabei entsteht |
|---|---|---|
| 1 | **Bestandsaufnahme** | alle Elemente mit ihrem heutigen Stand |
| 2 | **Erste Lebensphase** | Name, Dauer, die Annahmen dieser Jahre und die verteilte Sparquote |
Sie steht oben rechts, wo vorher der Assistent stand, und nennt **je Lebensphase und je
Übergang**, was dort fehlt -- jede Zeile ist ein Sprung an die Stelle:
**Warum nur zwei.** Bis 0.37 waren es sieben. Die ersten drei trugen, die letzten vier
zeigten Stationen, statt zu führen -- sie öffneten bestehende Dialoge und überliessen den
Rest dem Nutzer. Ein Schritt, der nicht führt, ist schlimmer als keiner: Er behauptet
Anleitung und liefert Verwaltung. Wie es nach der ersten Lebensphase weitergeht, wird eigens
entworfen statt aus dem Vorhandenen zusammengesetzt.
| Zustand | Was die Kachel zeigt |
|---|---|
| `NO_ELEMENTS` | «Noch nichts erfasst» + grosser Knopf **Bestandsaufnahme** |
| `NO_PHASES` | «Noch keine Zeitachse» + Knopf **Erste Lebensphase anlegen** |
| `OPEN` | die Liste der Gruppen mit Anzahl und Grund |
| `DONE` | grün: «Alles angeschaut» |
Die Reihenfolge der beiden ist nicht beliebig: Sie beginnt mit dem, was **feststeht** (was
habe ich?), und geht dann zu dem, was man **annimmt** (womit rechne ich?). Genau deshalb
steht die Bestandsaufnahme vor der Zeitachse -- und genau deshalb brauchte es die
Element-Stammdaten (3.14.3).
**Warum zwei Startzustände statt einer Zahl.** Ohne Elemente und ohne Phasen gibt es
naturgemäss nichts Offenes -- der Plan ist trotzdem leer. Eine «0 offene Punkte» wäre dort
eine Lüge, und ein grünes Häkchen die falscheste Auskunft überhaupt. Deshalb zählt die Kachel
in diesen beiden Fällen nicht, sondern fordert zum nächsten Handgriff auf.
### Tatsache und Annahme gehören getrennt
Der Schnitt zwischen den beiden Schritten ist kein Ablaufdetail, sondern der Kern:
Dieser Schnitt ist geblieben; er ist der Grund, warum die Bestandsaufnahme vor der Zeitachse
kommt:
| Schritt 1 -- Tatsachen (`baseData`) | Schritt 2 -- Annahmen (`phaseValues`) |
| Tatsachen (`baseData`) | Annahmen (`phaseValues`) |
|---|---|
| Nettolohn, Ausgaben pro Jahr | Lohnerhöhung, Teuerung |
| PK-Guthaben, 3a-Guthaben, Wert der Wertschriften | Verzinsung, erwartete Renditen, **PK-Beitrag** |
| Kaufpreis, Hypothek, Restschuld | Hypothekarzins, Wertsteigerung, Zins-in-Ausgaben |
Links steht, was man nachschlagen kann. Rechts steht, was man vermutet -- und eine Vermutung
gilt immer nur für einen Zeitraum. Bis 0.37 standen beide zusammen in der Bestandsaufnahme;
dadurch sah eine Annahme aus wie eine Tatsache, und man traf sie, bevor überhaupt feststand,
für welche Jahre sie gelten sollte.
gilt immer nur für einen Zeitraum. Bis 0.37 standen beide zusammen; dadurch sah eine Annahme
aus wie eine Tatsache, und man traf sie, bevor überhaupt feststand, für welche Jahre sie
gelten sollte.
Der **PK-Beitrag** steht bewusst bei den Annahmen und nicht in der Sparquoten-Verteilung: Er
stammt aus dem Bruttolohn und belastet das Cash-Konto nicht
([4.6.3](#463-pension_fund)) -- aus der Quote liesse er sich gar nicht verteilen.
### 3.14.2 Der Haken ist manuell -- der Stand daneben nicht
**Die Bestandsaufnahme bleibt als eigener Dialog** (`InventoryDialog`): als grosser Knopf im
leeren Plan und dauerhaft unter den Schnellaktionen. Sie ist der einzige Sammel-Dialog, der
geblieben ist, weil sie als einzige etwas leistet, das die Matrix nicht kann -- sieben
Kategorien in einem Zug erfassen, bevor man weiss, wie das Tool aufgebaut ist. Der Entwurf
liegt auf Ebene des ganzen Dialogs, nicht in der einzelnen Karte; nur so überlebt er den
Wechsel des Reiters. «Fertig» schreibt alles in einem Zug, und die Summe **«Vermögen heute»**
läuft beim Tippen mit.
Zwei Gestaltungsentscheide, die zusammengehören:
### 3.14.2 Bestätigen heisst «ich habe hingeschaut»
1. **Abgehakt wird von Hand.** Wann jemand mit einem Schritt fertig ist, ist eine Einschätzung
und keine Messgrösse. «Genug geplant» kann das Tool nicht wissen.
2. **Daneben steht der abgeleitete Stand** (`stepStatus`): «0 Lebensphasen», «2 Elemente»,
«Planungshorizont fehlt». Ein abgehakter Schritt ohne Substanz fällt damit auf, ohne dass
das Tool den Haken verweigert.
Eine neue Lebensphase übernimmt alle Werte der Vorphase. Das ist richtig -- ohne Vorbelegung
müsste man in jeder Phase alles neu eintippen. Es ist aber auch gefährlich: Eine Rendite von
5 %, die aus dem Erwerbsleben stillschweigend in die Pension weiterläuft, ist keine
Entscheidung, sondern ein Versehen.
Gesperrt wird nur das **Werkzeug**, nie die Selbstauskunft (`stepBlockedReason`): Die
Phasenplanung ohne Planungshorizont wäre gegenstandslos, die Übergangs-Schritte ohne Phasen
ebenso. Der Haken bleibt trotzdem jederzeit setzbar.
Deshalb gilt seit 0.39 in der ganzen Matrix derselbe Mechanismus, den die Übergänge seit 0.35
haben: **vorbelegen ja, stillschweigend übernehmen nein.** Jede Phasenzelle trägt ein
`confirmed`-Flag; solange es fehlt, ist die Zelle mit der Attention-Farbe markiert und zählt
in der Übersicht.
Erledigte Schritte rutschen nach unten und werden blass -- oben steht immer das Nächste. Die
Kachel ist **gelb**, solange etwas offen ist, und **grün**, wenn alle sieben stehen.
> **Der Haken steht NEBEN den Werten, nicht statt ihrer.** Bestätigen kopiert nichts und
> friert nichts ein -- eine bestätigte Phase erbt weiterhin live aus der Vorphase
> ([3.12.4](#3124-punkt-a-aus-vorphase-übernehmen)). Würde die Bestätigung die geerbten Werte
> materialisieren, wäre der ganze Punkt-A-Mechanismus hinfällig: Eine Änderung in Phase 1
> erreichte Phase 3 nicht mehr. Ein Test sichert genau das.
Was gilt überhaupt als offen? Nur Zellen, die in dieser Phase noch etwas beitragen
(`needsConfirmation` prüft `status === "ACTIVE"`). Ein verkauftes Haus trägt in der Folgephase
keine Annahmen mehr und verlangt auch keine Bestätigung.
**Die Spar-/Verzehrquote zählt eigens** (`Phase.ratesConfirmed`). Sie ist keine Eigenschaft
eines Elements, sondern der Phase, und sie lässt sich nicht aus den Zellen ableiten: Man kann
jede einzelne angeschaut und die Verteilung trotzdem nie getroffen haben. Dann bliebe der
ganze Überschuss still auf dem Cash-Konto liegen -- und die Übersicht meldete «alles
erledigt», während das Geld unverzinst herumliegt. Das Flag wird gesetzt, sobald der
Verteil-Dialog einmal gespeichert hat.
**Sammel-Bestätigung je Phase.** Bei sechs Elementen und fünf Phasen wären dreissig einzelne
Klicks nötig -- das erzieht zum Durchklicken, also genau zum Gegenteil dessen, was der
Mechanismus will. Der Phasenkopf trägt deshalb ein Abzeichen mit der Anzahl offener Annahmen;
ein Klick bestätigt alle auf einmal, nach Rückfrage. Wer eine einzelne Zelle öffnet und
speichert, bestätigt sie dabei ohnehin.
### 3.14.3 Element-Stammdaten: Bestand vor Zeitachse
@@ -2240,7 +2275,7 @@ Oben vier gleichrangige Kacheln plus die Zeitachse über die volle Breite:
| **Grundeinstellungen** | plan-weit (Personen, Startjahr, Inflation) und szenario-eigen (Horizont, Endjahr, Endalter, Pensionsalter). Stift zum Bearbeiten; je Person ein Knopf **«Pensionsplanung»** |
| **Kennzahlen** | **Vermögen heute** (Summe der Stammdaten -- die einzige Zahl, die schon vor jeder Zeitplanung etwas aussagt), Endvermögen nominal und real, Reichweite |
| **Schnellaktionen** | Neues Szenario · Tour · Änderungshistorie · Rechenwege · CSV-Export |
| **Assistent** | siehe 3.14.1 |
| **Offene Punkte** | siehe 3.14.1 |
Das **Pensionsalter ist im Basisszenario auf 65 festgelegt** und nicht änderbar. Vorbezug,
Aufschub, eigene Bezugsalter für Pensionskasse und Säule 3a sowie die daraus folgenden
@@ -2261,7 +2296,7 @@ darüber wie Aktionen der ganzen Seite.
### 3.14.6 Die Tour
Ein grosses Popup mit einem **nachgebauten** Bildschirm und erfundenen Zahlen, in neun
Schritten erklärt. Der letzte führt zum Assistenten.
Schritten erklärt. Der letzte führt zur Bestandsaufnahme.
Das frühere Spotlight legte sich über die echte Ansicht. Zwei Nachteile liessen sich nicht
beheben: Auf einem frisch angelegten, leeren Plan gab es kaum etwas hervorzuheben -- also
@@ -2272,9 +2307,8 @@ Der Preis ist bekannt und bewusst in Kauf genommen: **Die Attrappe muss bei UI-
nachgeführt werden.** Dafür funktioniert die Tour ab der ersten Sekunde und unabhängig davon,
was im Plan schon steht.
Referenz: `src/lib/assistant.ts`, `src/components/Assistant.tsx`,
`src/components/AssistantStepDialog.tsx`, `src/components/AssistantSteps.tsx`,
`src/components/Tour.tsx`.
Referenz: `src/lib/review.ts`, `src/components/ReviewTile.tsx`,
`src/components/InventoryDialog.tsx`, `src/components/Tour.tsx`.
---
@@ -3741,7 +3775,8 @@ PlanComputed ← an den Client geliefert
| `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. |
| `phaseplan.ts` | Ableitung der Lebensabschnitte (Erwerb/Misch/Pension) aus den fixen Pensionierungszeitpunkten, Fixpunkte und Dauer-Kappung (Kap. 3.14.4). Rein. |
| `review.ts` | Leitet die offenen Punkte eines Plans ab: unbestätigte Phasenzellen, nicht verteilte Quoten, offene Übergänge (Kap. 3.14). 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) |
| `queries.ts` | Prisma-Includes, `toPlanInput()`, Ownership-Abfragen |
@@ -3812,6 +3847,7 @@ PlanComputed ← an den Client geliefert
| `name` | String | |
| `durationYears` | Int | 180 |
| `cashTransition` | Json? | Cash-Entscheid beim Übergang **nach** dieser Phase (siehe 5.4.5) |
| `ratesConfirmed` | Boolean | Default `false` wurde die Spar-/Verzehrquote dieser Phase je verteilt? ([3.14.2](#3142-bestätigen-heisst-ich-habe-hingeschaut)) |
| `sourcePhaseId` | String? | Gegenstück in der Vorlage (**lose** Referenz, kein FK) Diff-Grundlage |
| `createdAt` / `updatedAt` | DateTime | |
| | | `@@unique([scenarioId, sequenceNumber])` |
@@ -3894,6 +3930,7 @@ Referenz: `prisma/schema.prisma` Zeilen 46, `src/lib/elements.ts` Zeilen 48
| `interestHandling` | REAL_ESTATE Doppelzählungs-Schalter | `INCLUDED` (Default) \| `ADD` |
| `valueGrowth` | REAL_ESTATE Wertsteigerung %/Jahr auf die Liegenschaft | 20 bis 20 |
| `annualRepayment` | OTHER_DEBT | ≥ 0 |
| `confirmed` | alle «ich habe hingeschaut» ([3.14.2](#3142-bestätigen-heisst-ich-habe-hingeschaut)) | Boolean, optional |
**Vererbbare Felder (Punkt A, Kap. 3.12.4):** `teuerungsausgleich`, `expectedReturn`,
`annualContribution`, `annualWithdrawal`, `amortization`, `valueGrowth`, `interestRate` und
@@ -3966,6 +4003,7 @@ sondern zu leeren Werten.
| `20260721090000_reports` | Tabelle `Report`: gewählte Parameter, eingefrorenes Modell und die PDF-Datei als `BYTEA` |
| `20260724120000_version_zero_start` | `Scenario.currentMajor` startet bei **0** statt 1 die Versionierung beginnt bei 0.1 (Kap. 3.8). Nur der Default; bestehende Zeilen bleiben |
| `20260720160000_saved_analyses` | Tabelle `SavedAnalysis` (Eingaben + Ergebnis als JSONB, denormalisierte Kerndaten) |
| `20260816150000_review_state` | `Phase.ratesConfirmed` (Default `false`); `Scenario.assistantProgress` entfällt mit dem Assistenten (Kap. 3.14) |
**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
@@ -4027,7 +4065,7 @@ wird der Plan neu geladen; die Berechnung kommt immer vom Server.
| `SpecView` | 65 | Rendert `SPEZIFIKATION.md` (via `/api/spec`) als lesbares Dokument, inkl. Sprungmarken aus den Rechenwegen |
| `InfoBubble` | 28 | Hilfe-Tooltip |
| `ui` | ~370 | UI-Primitiven: Button, Modal, InspectorShell, Confirm, Toast, Skeleton, EmptyState ([3.7.6](#376-sprache-und-ui-primitiven)/[3.7.7](#377-inspector-panel-statt-modals)) |
| `Assistant` · `AssistantStepDialog` · `AssistantSteps` | ~1200 | Der FPT-Assistent: Fortschrittskachel, Erklärseiten und die Werkzeuge der sieben Schritte ([3.14](#314-der-fpt-assistent)) |
| `ReviewTile` · `InventoryDialog` | ~900 | Übersicht der offenen Punkte und der Sammel-Dialog der Bestandsaufnahme ([3.14](#314-bestandsaufnahme-und-offene-punkte)) |
| `Tour` | ~140 | Interaktive Kurz-Tour über die Planansicht ([3.7.8](#378-tour-und-nächste-schritte)) |
| `CommandPalette` | ~130 | Befehls-Palette Ctrl/Cmd+K ([3.7.9](#379-befehls-palette-und-sparklines)) |
| `DistributionDialogs` | ~460 | Verteil-Werkzeuge für Kapital und Spar-/Verzehrquote ([3.6.10](#3610-verteil-werkzeuge)) |
@@ -4348,7 +4386,7 @@ Include `src/**/*.test.ts`). Es gibt **keine** Komponenten-, API- oder E2E-Tests
| `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) |
| `rate-limit.test.ts` | 6 | Fixed-Window: erlaubt bis Limit, blockt danach, startet nach Fensterablauf neu, trennt je Schlüssel; Client-IP aus X-Forwarded-For / X-Real-IP |
| `csv.test.ts` | 7 | BOM, alle vier Blöcke, jedes Element als Zeile, Beginn-/Ende-/Übergangsspalten, Entscheid im Klartext, ein Eintrag je Planjahr, Maskierung von `;` und `"` |
| **Total** | **336** | |
| **Total** | **329** | |
## 8.2 Testfälle
@@ -4712,10 +4750,10 @@ seit 0.12 erklärt wird ([4.13.5](#4135-wirkungslose-treiber-werden-erklärt));
Vermögensbrücke erscheint stattdessen die Differenz `Verkaufspreis Verkehrswert` als eigener
Posten.
## 9.23 Assistent: Teilzustand bei Abbruch
## 9.23 Sammel-Dialoge: Teilzustand bei Abbruch
Der Plan-Assistent und der Beispielplan senden am Ende eine **Sequenz** bestehender API-Aufrufe
(Plan → Phase 1 → Elemente → Folgephasen). Bricht die Sequenz mittendrin ab (Netzfehler),
Die Bestandsaufnahme schreibt am Ende eine **Sequenz** bestehender API-Aufrufe
(Element anlegen → Stammdaten → Phasenwerte, je Element). Bricht die Sequenz mittendrin ab (Netzfehler),
existiert ein **Teil-Plan**. Der ist normal weiterbearbeitbar und der Fehlerhinweis sagt das
auch aber es gibt kein automatisches Rollback. Das wäre nur mit Backend-Unterstützung
(Transaktion über mehrere Requests oder Batch-Endpunkt) sauber lösbar und ist bewusst nicht