Drei Berechnungs-Fixes: Phaseninflation, Tilgungsraten, Vorbezugssteuer
- Phase.inflationRate ersatzlos entfernt (inkl. Migration). Die Inflation liegt seit V5 plan-weit; das Feld wurde von computePlan nie gelesen und war wirkungslos. - Amortisation und Tilgung stoppen, sobald Hypothek bzw. Schuld abbezahlt sind: Hypothek/Restschuld sind neu laufende Salden, die Rate ist pro Jahr am Restsaldo gekappt. Belastet danach weder Cash noch Sparquote. - Kapitalbezugssteuer greift neu auch bei PK-/3a-Vorbezuegen vor der Pensionierung. Der Bezug wird brutto dem Kapital entnommen, netto ins Cash gebucht. plannedSaveRate ist neu die Rate des ersten Phasenjahres. Drei Regressionstests ergaenzt (10 -> 13). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -32,7 +32,7 @@ function plan(opts: {
|
||||
inflationRateDefault: opts.inflation ?? 2,
|
||||
initialCash: opts.initialCash ?? 0,
|
||||
persons: [{ id: "A", role: "PERSON_A", name: null, age: opts.age, retirementAge: opts.retirementAge }],
|
||||
phases: opts.phases.map((p, i) => ({ id: p.id, sequenceNumber: i + 1, name: p.id, durationYears: p.durationYears, inflationRate: null })),
|
||||
phases: opts.phases.map((p, i) => ({ id: p.id, sequenceNumber: i + 1, name: p.id, durationYears: p.durationYears })),
|
||||
elements: opts.elements,
|
||||
};
|
||||
}
|
||||
@@ -185,6 +185,72 @@ describe("V5 Golden Tests", () => {
|
||||
expect(ph.cashEnd).toBe(50000);
|
||||
});
|
||||
|
||||
it("Tilgung stoppt, sobald die Schuld getilgt ist (belastet Cash und Sparrate nicht weiter)", () => {
|
||||
// Schuld 25'000, Tilgung 10'000/Jahr, 5 Jahre: getilgt im Jahr 3 (10'000 + 10'000 + 5'000).
|
||||
// Gesamtabfluss = 25'000, NICHT 50'000. Einkommen = Ausgaben, damit nur die Tilgung wirkt.
|
||||
const p = plan({
|
||||
age: 40,
|
||||
retirementAge: 70,
|
||||
inflation: 0,
|
||||
initialCash: 100000,
|
||||
phases: [{ id: "p1", durationYears: 5 }],
|
||||
elements: [
|
||||
el("INCOME", "PERSON_A", { p1: { amount: 50000, teuerungsausgleich: 0 } }),
|
||||
el("EXPENSE", "HOUSEHOLD", { p1: { amount: 50000, teuerungsausgleich: 0 } }),
|
||||
el("OTHER_DEBT", "HOUSEHOLD", { p1: { startValue: 25000, annualRepayment: 10000 } }),
|
||||
],
|
||||
});
|
||||
const ph = computePlan(p).phases[0];
|
||||
expect(ph.cashEnd).toBe(75000); // 100'000 - 25'000
|
||||
expect(ph.plannedSaveRate).toBe(10000); // erstes Jahr volle Rate
|
||||
const debt = ph.elements.find((e) => e.category === "OTHER_DEBT")!;
|
||||
expect(debt.endValue).toBe(0);
|
||||
});
|
||||
|
||||
it("Amortisation stoppt, sobald die Hypothek abbezahlt ist", () => {
|
||||
// Hypothek 15'000, Amortisation 10'000/Jahr, 4 Jahre: abbezahlt im Jahr 2 (10'000 + 5'000).
|
||||
const p = plan({
|
||||
age: 40,
|
||||
retirementAge: 70,
|
||||
inflation: 0,
|
||||
initialCash: 100000,
|
||||
phases: [{ id: "p1", durationYears: 4 }],
|
||||
elements: [
|
||||
el("REAL_ESTATE", "HOUSEHOLD", { p1: { purchasePrice: 500000, mortgage: 15000, amortization: 10000 } }),
|
||||
],
|
||||
});
|
||||
const ph = computePlan(p).phases[0];
|
||||
expect(ph.cashEnd).toBe(85000); // 100'000 - 15'000 (nicht - 40'000)
|
||||
const re = ph.elements.find((e) => e.category === "REAL_ESTATE")!;
|
||||
expect(re.endValue).toBe(500000); // schuldenfrei
|
||||
});
|
||||
|
||||
it("Vorbezug PK vor Pensionierung: Kapitalbezugssteuer wird abgezogen", () => {
|
||||
// Bezug 100'000 brutto bei 8% Steuer -> 92'000 netto ins Cash der Folgephase.
|
||||
const p = plan({
|
||||
age: 40,
|
||||
retirementAge: 70,
|
||||
inflation: 0,
|
||||
phases: [
|
||||
{ id: "p1", durationYears: 1 },
|
||||
{ id: "p2", durationYears: 1 },
|
||||
],
|
||||
elements: [
|
||||
el(
|
||||
"PENSION_FUND",
|
||||
"PERSON_A",
|
||||
{ p1: { currentValue: 300000, expectedReturn: 0, annualContribution: 0 }, p2: {} },
|
||||
{ p1: { withdrawalMode: "AMOUNT", withdrawal: 100000, capitalTaxRate: 8 } }
|
||||
),
|
||||
],
|
||||
});
|
||||
const r = computePlan(p);
|
||||
expect(r.phases[1].capitalInflow).toBe(92000); // netto nach 8% Steuer
|
||||
expect(r.phases[1].cashStart).toBe(92000);
|
||||
const pk = r.phases[1].elements.find((e) => e.category === "PENSION_FUND")!;
|
||||
expect(pk.startValue).toBe(200000); // brutto 100'000 dem Kapital entnommen
|
||||
});
|
||||
|
||||
it("Fortschreibung: nominaler Einkommens-Basiswert Phase 1 -> Startwert Phase 2; Cash laeuft fort", () => {
|
||||
const p = plan({
|
||||
age: 40,
|
||||
|
||||
Reference in New Issue
Block a user