V5-Modell: Einkommen nominal, Ausgaben real->nominal, Inflation plan-weit, Sparquoten-Grafik
Deploy App / deploy (push) Successful in 57s

- Einkommen: nominale Basis + nominale Lohnerhoehung (Default 0). Real read-only als Info.
- Ausgaben: REALE Basis (heutige Kaufkraft) + reale Mehrausgaben (Default 0); nominal =
  real x plan-weite Inflation, read-only. Loest die "nominale Ausgaben verwirren"-Problematik.
- Inflation nur noch plan-weit (Phasen-Override entfernt).
- Engine liefert flowDeflatorEnd (Flow-Realwerte) + yearly-Serie (Jahr/Alter/Einkommen/
  Ausgabe nominal/real). Nominal/Real-Umschalter nutzt Flow- vs. Bestands-Deflatoren.
- Neue Grafik (Dashboard): Einkommen vs. nominale Ausgabe mit eingefaerbter Sparquoten-
  Flaeche (gruen/rot) + reale Ausgabe als Referenzlinie (Inflationskeil).
- In-Tool-Erklaerungen in den Einkommen/Ausgaben-Popups.
- Golden Tests aufs neue Modell hergeleitet (8 gruen, u.a. Ausgaben real->nominal, Ruin 94).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-07-15 22:20:06 +02:00
parent 310ebc66fb
commit 6402583e0c
7 changed files with 212 additions and 80 deletions
+3 -12
View File
@@ -10,20 +10,17 @@ export function PhaseDetail({
phase,
maxDurationYears,
isLast,
inflationDefault,
onSaved,
onDeleted,
}: {
phase: PhaseInput;
maxDurationYears: number | null;
isLast: boolean;
inflationDefault: number;
onSaved: () => void;
onDeleted: () => void;
}) {
const [name, setName] = useState(phase.name);
const [durationYears, setDurationYears] = useState(phase.durationYears);
const [inflationRate, setInflationRate] = useState<number | null>(phase.inflationRate);
const [saving, setSaving] = useState(false);
const [error, setError] = useState<string | null>(null);
@@ -33,7 +30,7 @@ export function PhaseDetail({
setSaving(true);
setError(null);
try {
await api.put(`/api/phases/${phase.id}`, { name, durationYears, inflationRate });
await api.put(`/api/phases/${phase.id}`, { name, durationYears });
onSaved();
} catch (e) {
setError(e instanceof Error ? e.message : "Speichern fehlgeschlagen.");
@@ -69,7 +66,7 @@ export function PhaseDetail({
)}
</div>
<div className="grid grid-cols-1 gap-3 sm:grid-cols-3">
<div className="grid grid-cols-1 gap-3 sm:grid-cols-2">
<TextField label="Bezeichnung" value={name} onChange={setName} />
<NumberField
label={`Dauer (Jahre)${cap != null ? ` · max. ${cap}` : ""}`}
@@ -79,14 +76,8 @@ export function PhaseDetail({
max={cap ?? undefined}
onChange={(v) => setDurationYears(cap != null ? Math.min(v, cap) : v)}
/>
<NumberField
label="Inflationsrate (%)"
help="Ueberschreibt die Standardannahme aus dem Plan-Grundprofil."
value={inflationRate ?? inflationDefault}
step={0.1}
onChange={setInflationRate}
/>
</div>
<p className="text-xs text-faint">Die Inflationsrate gilt plan-weit und wird in den Plan-Einstellungen gesetzt.</p>
{error && <p className="text-sm text-danger">{error}</p>}
<div>