f9d7ca07fd
Deploy App / deploy (push) Successful in 1m10s
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
72 lines
2.8 KiB
TypeScript
72 lines
2.8 KiB
TypeScript
// «Ein Wert, ein Ort» -- durchgesetzt im Rechenkern, nicht nur in der Oberfläche
|
|
// (SPEZIFIKATION 3.14.3).
|
|
//
|
|
// Der Bestand bei Planbeginn gehört in die Stammdaten. In der ersten Phase stand dafür bis
|
|
// 0.39 zusätzlich ein Eingabefeld; eine dort gespeicherte Zahl überschattete die Stammdaten
|
|
// still. Seit 0.40 ist das Feld read-only -- womit so ein Altwert unerreichbar WÄRE, wenn er
|
|
// weiterhin gewönne. Deshalb entscheidet die Regel der Rechenkern.
|
|
|
|
import { describe, it, expect } from "vitest";
|
|
import { computePlan, firstPhaseValues } from "@/lib/calculations";
|
|
import type { PlanInput } from "@/lib/types";
|
|
|
|
function plan(elements: PlanInput["elements"]): PlanInput {
|
|
return {
|
|
id: "plan",
|
|
name: "T",
|
|
householdType: "SINGLE",
|
|
inflationRateDefault: 0,
|
|
initialCash: 0,
|
|
startYear: 2026,
|
|
persons: [{ id: "A", role: "PERSON_A", name: null, age: 40, retirementAge: 65 }],
|
|
phases: [{ id: "p1", sequenceNumber: 1, name: "P1", durationYears: 10, cashTransition: { mode: "NONE" } }],
|
|
elements,
|
|
};
|
|
}
|
|
|
|
function haus(phaseValues: Record<string, Record<string, unknown>>) {
|
|
return [
|
|
{
|
|
id: "e1",
|
|
category: "REAL_ESTATE" as never,
|
|
name: "Haus",
|
|
ownerRole: "HOUSEHOLD" as never,
|
|
orderIndex: 1,
|
|
baseData: { purchasePrice: 800000, mortgage: 500000 } as Record<string, number>,
|
|
phaseValues: phaseValues as never,
|
|
transitionValues: {},
|
|
},
|
|
];
|
|
}
|
|
|
|
describe("Bestandsfelder in der ersten Phase", () => {
|
|
it("nimmt den Bestand aus den Stammdaten", () => {
|
|
const c = computePlan(plan(haus({})));
|
|
expect(c.phases[0].elements[0].startValue).toBe(300000);
|
|
});
|
|
|
|
it("lässt sich von einem Phasenwert NICHT überschatten", () => {
|
|
// Genau der Fall aus der Testrunde: In 0.39 schrieb das Speichern der Zelle eine 0 --
|
|
// seither stand das Haus mit Startwert 0 da, ohne dass man es noch korrigieren konnte.
|
|
const c = computePlan(plan(haus({ p1: { confirmed: true, purchasePrice: 0, mortgage: 0 } })));
|
|
expect(c.phases[0].elements[0].startValue).toBe(300000);
|
|
});
|
|
|
|
it("fällt auf den Phasenwert zurück, solange die Stammdaten leer sind", () => {
|
|
// Ohne diesen Rückfall wären Pläne aus der Zeit vor den Stammdaten schlagartig auf 0.
|
|
const els = haus({ p1: { purchasePrice: 600000, mortgage: 400000 } });
|
|
els[0].baseData = {};
|
|
expect(computePlan(plan(els)).phases[0].elements[0].startValue).toBe(200000);
|
|
});
|
|
|
|
it("lässt die ANNAHMEN in der ersten Phase überschreibbar", () => {
|
|
// Eine Rendite gilt für einen Zeitraum, kein Anfangsbestand -- sie darf je Phase abweichen.
|
|
const merged = firstPhaseValues(
|
|
{ startValue: 100000, expectedReturn: 3 },
|
|
{ expectedReturn: 7, startValue: 0 }
|
|
);
|
|
expect(merged.expectedReturn).toBe(7);
|
|
expect(merged.startValue).toBe(100000);
|
|
});
|
|
});
|