Files
FPT/src/lib/basedata.test.ts
T
admGitAICDS f9d7ca07fd
Deploy App / deploy (push) Successful in 1m10s
Stammdaten gewinnen in Phase 1, Bestaetigung je Zeile statt pauschal
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-16 21:41:07 +02:00

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);
});
});