Pensionierung: Entscheid ans Element, AHV-Vorbezug/Aufschub, PK-Kapitalquote

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
2026-07-26 13:18:38 +02:00
parent 7979fed7bd
commit 9907fda6f4
9 changed files with 660 additions and 103 deletions
+52
View File
@@ -47,6 +47,58 @@ export function sortedPhases(plan: PlanInput) {
return [...plan.phases].sort((a, b) => a.sequenceNumber - b.sequenceNumber);
}
// --- Planungshorizont -----------------------------------------------------------------------
//
// Bis 0.33 ergab sich das Planende stillschweigend als Summe der Phasendauern. Zwei Szenarien
// konnten dadurch unbemerkt verschieden weit rechnen -- und waren dann nicht vergleichbar,
// obwohl genau das ihr Zweck ist. Neu ist der Horizont eine erfasste Zahl, und die LETZTE
// Phase folgt ihr (dieselbe Richtung wie beim Pensionsalter: Zahl stellen, Struktur folgt).
//
// Referenzperson ist die aelteste: Sie erreicht ihren Horizont zuerst, und der Plan muss so
// lange laufen, bis die LETZTE Person ihren erreicht hat.
export function planEndYear(plan: PlanInput): number | null {
const years = plan.persons
.filter((p) => typeof p.planningHorizonAge === "number")
.map((p) => (p.planningHorizonAge as number) - p.age);
return years.length > 0 ? Math.max(...years) : null;
}
export interface HorizonChange {
lastPhaseId: string;
oldDuration: number;
newDuration: number;
blocked: string | null;
}
// Was muesste an der letzten Phase geschehen, damit der Plan bis zum Horizont laeuft?
export function planHorizonChange(plan: PlanInput, horizonAge: number, role: PersonRole): HorizonChange | null {
const phases = sortedPhases(plan);
const last = phases[phases.length - 1];
if (!last) return null;
const person = plan.persons.find((p) => p.role === role);
if (!person) return null;
// Zielgesamtdauer aus SICHT DIESER Person; die uebrigen Horizonte bleiben unberuehrt und
// koennen laenger sein -- deshalb das Maximum ueber alle.
const wish = Math.max(
horizonAge - person.age,
...plan.persons.filter((p) => p.role !== role && typeof p.planningHorizonAge === "number")
.map((p) => (p.planningHorizonAge as number) - p.age)
);
const before = phases.slice(0, -1).reduce((s, p) => s + p.durationYears, 0);
const newDuration = wish - before;
const change: HorizonChange = {
lastPhaseId: last.id,
oldDuration: last.durationYears,
newDuration,
blocked: null,
};
if (newDuration < 1) {
change.blocked = `Der Horizont liegt vor dem Ende der zweitletzten Lebensphase. Die letzte Phase muss mindestens ein Jahr dauern kürze zuerst eine frühere Phase.`;
}
return change;
}
// Analysiert für JEDE Person, ob und wie weit sich ihr Pensionsalter verschieben lässt.
export function retirementBoundaries(plan: PlanInput): RetirementBoundary[] {
const phases = sortedPhases(plan);