V7: Haushalt auf Plan-Ebene, Annahmen bleiben am Szenario
Deploy App / deploy (push) Successful in 1m52s

Haushaltsform, Personen (Name/Alter) und Startjahr wandern vom Szenario
auf den Plan. Das Pensionsalter bleibt szenario-eigen -- es ist der Kern
jedes Frueh-/Spaetpensionierungs-Szenarios.

Neue Tabelle PlanPerson; Person behaelt nur Rolle + Pensionsalter;
Plan bekommt householdType und startYear.

Der Rechenkern bleibt unberuehrt: toPlanInput fuegt beide Ebenen wieder
zu einem unveraenderten PlanInput zusammen. 43 Golden Tests unveraendert.

Nebeneffekt: Ein Ist-Satz trifft jetzt in ALLEN Szenarien dasselbe
Planjahr -- vorher war das nicht garantiert.

Wiederherstellen einer Version setzt nur noch Szenario-Eigenes zurueck.
Profil-Dialog kennzeichnet plan-weite vs. szenario-eigene Felder.

Zwei Tests spielen echte V6-Daten ein und pruefen die Uebernahme.
Spezifikation 0.23 (210 -> 212).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-07-20 21:53:12 +02:00
parent 44c6c81f5d
commit ce5f83823f
10 changed files with 290 additions and 51 deletions
+9 -3
View File
@@ -64,18 +64,24 @@ export async function POST(request: NextRequest) {
const error = validatePersonsForType(parsed.data);
if (error) return NextResponse.json({ error }, { status: 400 });
// Haushaltsform, Personen und Startjahr am PLAN; das Pensionsalter je Szenario.
const plan = await prisma.plan.create({
data: {
userId,
name: parsed.data.name,
householdType: parsed.data.householdType,
startYear: parsed.data.startYear ?? new Date().getFullYear(),
persons: {
create: parsed.data.persons.map((p) => ({ role: p.role, name: p.name ?? null, age: p.age })),
},
scenarios: {
create: {
name: "Basisszenario",
isBase: true,
householdType: parsed.data.householdType,
inflationRateDefault: parsed.data.inflationRateDefault,
startYear: parsed.data.startYear ?? new Date().getFullYear(),
persons: { create: parsed.data.persons },
persons: {
create: parsed.data.persons.map((p) => ({ role: p.role, retirementAge: p.retirementAge })),
},
},
},
},
@@ -29,17 +29,12 @@ export async function POST(request: NextRequest, { params }: { params: Promise<{
name: parsed.data.name,
isBase: false,
parentScenarioId: source.id,
householdType: source.householdType,
// Haushaltsform, Personen und Startjahr liegen am Plan und werden mitbenutzt, nicht
// kopiert. Szenario-eigen sind nur Inflation, Cash-Anfangswert und Pensionsalter.
inflationRateDefault: source.inflationRateDefault,
initialCash: source.initialCash,
startYear: source.startYear,
persons: {
create: source.persons.map((p) => ({
role: p.role,
name: p.name,
age: p.age,
retirementAge: p.retirementAge,
})),
create: source.persons.map((p) => ({ role: p.role, retirementAge: p.retirementAge })),
},
},
});
+21 -3
View File
@@ -88,16 +88,34 @@ export async function PATCH(request: NextRequest, { params }: { params: Promise<
if ("householdType" in data) {
const error = validatePersonsForType(data);
if (error) return NextResponse.json({ error }, { status: 400 });
// Haushaltsform, Personen (Name/Alter) und Startjahr gehen an den PLAN -- sie gelten
// fuer alle Szenarien. Am Szenario bleiben Inflation und die Pensionsalter.
const updated = await prisma.$transaction(async (tx) => {
await tx.plan.update({
where: { id: scenario.planId },
data: {
householdType: data.householdType,
startYear: data.startYear ?? undefined,
},
});
await tx.planPerson.deleteMany({ where: { planId: scenario.planId } });
await tx.planPerson.createMany({
data: data.persons.map((p) => ({
planId: scenario.planId,
role: p.role,
name: p.name ?? null,
age: p.age,
})),
});
await tx.person.deleteMany({ where: { scenarioId: scenario.id } });
return tx.scenario.update({
where: { id: scenario.id },
data: {
name: data.name ?? undefined,
householdType: data.householdType,
inflationRateDefault: data.inflationRateDefault,
startYear: data.startYear ?? undefined,
persons: { create: data.persons },
persons: {
create: data.persons.map((p) => ({ role: p.role, retirementAge: p.retirementAge })),
},
},
});
});