113616cc1f
Deploy App / deploy (push) Successful in 1m18s
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
93 lines
3.1 KiB
TypeScript
93 lines
3.1 KiB
TypeScript
// Verwendung des bezogenen Alterskapitals: das Tilgungs-Ziel ist wählbar (0.41).
|
|
//
|
|
// Bis 0.40 floss die Tilgungs-Quote stur in die erstbeste Immobilie. Wer neben einer Hypothek
|
|
// zu 1,5 % einen Konsumkredit zu 6 % trägt, will aber zuerst den Kredit los -- und hatte keine
|
|
// Möglichkeit, das zu sagen.
|
|
|
|
import { describe, it, expect } from "vitest";
|
|
import { computePlan } from "@/lib/calculations";
|
|
import type { PlanInput } from "@/lib/types";
|
|
|
|
function plan(debtTarget?: string): PlanInput {
|
|
return {
|
|
id: "plan",
|
|
name: "T",
|
|
householdType: "SINGLE",
|
|
inflationRateDefault: 0,
|
|
initialCash: 0,
|
|
startYear: 2026,
|
|
persons: [{ id: "A", role: "PERSON_A", name: null, age: 64, retirementAge: 65 }],
|
|
phases: [
|
|
{ id: "p1", sequenceNumber: 1, name: "Erwerb", durationYears: 1, cashTransition: { mode: "NONE" } },
|
|
{ id: "p2", sequenceNumber: 2, name: "Pension", durationYears: 5, cashTransition: { mode: "NONE" } },
|
|
],
|
|
elements: [
|
|
{
|
|
id: "pk",
|
|
category: "PENSION_FUND" as never,
|
|
name: "PK",
|
|
ownerRole: "PERSON_A" as never,
|
|
orderIndex: 1,
|
|
baseData: { currentValue: 400000, expectedReturn: 0 },
|
|
phaseValues: { p1: {}, p2: {} },
|
|
transitionValues: {},
|
|
// Volles Kapital, keine Steuer, alles in die Tilgung: 400'000 stehen zur Verfügung.
|
|
retirementDecision: {
|
|
capitalSharePct: 100,
|
|
capitalTaxRate: 0,
|
|
conversionRate: 6,
|
|
capitalUseAmortizationPct: 100,
|
|
...(debtTarget ? { capitalUseDebtTargetElementId: debtTarget } : {}),
|
|
} as never,
|
|
},
|
|
{
|
|
id: "haus",
|
|
category: "REAL_ESTATE" as never,
|
|
name: "Haus",
|
|
ownerRole: "HOUSEHOLD" as never,
|
|
orderIndex: 2,
|
|
baseData: { purchasePrice: 900000, mortgage: 300000, interestRate: 1.5, valueGrowth: 0 },
|
|
phaseValues: { p1: {}, p2: {} },
|
|
transitionValues: { p1: { decision: "HOLD" as never } },
|
|
},
|
|
{
|
|
id: "kredit",
|
|
category: "OTHER_DEBT" as never,
|
|
name: "Konsumkredit",
|
|
ownerRole: "HOUSEHOLD" as never,
|
|
orderIndex: 3,
|
|
baseData: { startValue: 100000 },
|
|
phaseValues: { p1: {}, p2: {} },
|
|
transitionValues: {},
|
|
},
|
|
],
|
|
};
|
|
}
|
|
|
|
const restOf = (p: PlanInput, id: string) => {
|
|
const c = computePlan(p);
|
|
const ph = c.phases[1];
|
|
const ce = ph.elements.find((e) => e.elementId === id)!;
|
|
return id === "haus" ? ce.mortgageStart : Math.abs(ce.startValue);
|
|
};
|
|
|
|
describe("Schuldentilgung aus dem Alterskapital", () => {
|
|
it("tilgt ohne Wahl weiterhin die Hypothek", () => {
|
|
// Bestandsschutz: Pläne aus der Zeit vor der Zielwahl müssen unverändert rechnen.
|
|
const p = plan();
|
|
expect(restOf(p, "haus")).toBe(0);
|
|
expect(restOf(p, "kredit")).toBe(100000);
|
|
});
|
|
|
|
it("tilgt den gewählten Kredit zuerst", () => {
|
|
const p = plan("kredit");
|
|
expect(restOf(p, "kredit")).toBe(0);
|
|
});
|
|
|
|
it("lässt eine nicht gewählte Schuld unberührt, wenn die Hypothek gewählt ist", () => {
|
|
const p = plan("haus");
|
|
expect(restOf(p, "haus")).toBe(0);
|
|
expect(restOf(p, "kredit")).toBe(100000);
|
|
});
|
|
});
|