import { NextRequest, NextResponse } from "next/server"; import { z } from "zod"; import { prisma } from "@/lib/db"; import { phaseInclude, planInclude } from "@/lib/queries"; const scenarioSchema = z.object({ name: z.string().min(1).max(120), branchFromPhaseId: z.string().min(1), }); // Erstellt ein neues Szenario als Kopie eines bestehenden Plans ab einer gewaehlten // Phase (inklusive). Die Phasenkette bis zu diesem Punkt wird per Deep-Copy dupliziert; // ab dort kann der Benutzer die Kette unabhaengig weiterentwickeln (TDD Kapitel 13). export async function POST( request: NextRequest, { params }: { params: Promise<{ planId: string }> } ) { const { planId } = await params; const body = await request.json(); const parsed = scenarioSchema.safeParse(body); if (!parsed.success) { return NextResponse.json({ error: parsed.error.flatten() }, { status: 400 }); } const sourcePlan = await prisma.plan.findUnique({ where: { id: planId }, include: planInclude }); if (!sourcePlan) { return NextResponse.json({ error: "Ursprungsplan nicht gefunden." }, { status: 404 }); } const branchPhase = sourcePlan.phases.find((p) => p.id === parsed.data.branchFromPhaseId); if (!branchPhase) { return NextResponse.json({ error: "Verzweigungsphase nicht gefunden." }, { status: 404 }); } const phasesToCopy = sourcePlan.phases .filter((p) => p.sequenceNumber <= branchPhase.sequenceNumber) .sort((a, b) => a.sequenceNumber - b.sequenceNumber); const newPlanId = await prisma.$transaction(async (tx) => { const newPlan = await tx.plan.create({ data: { householdId: sourcePlan.householdId, name: parsed.data.name, parentPlanId: sourcePlan.id, }, }); let lastNewPhaseId = ""; for (const phase of phasesToCopy) { const newPhase = await tx.phase.create({ data: { planId: newPlan.id, sequenceNumber: phase.sequenceNumber, name: phase.name, durationYears: phase.durationYears, inflationRate: phase.inflationRate, incomeMode: phase.incomeMode, incomingCapital: phase.incomingCapital, incomeEntries: { create: phase.incomeEntries.map((e) => ({ personId: e.personId, label: e.label, amount: e.amount, })), }, expenseEntries: { create: phase.expenseEntries.map((e) => ({ label: e.label, amount: e.amount })), }, securities: { create: phase.securities.map((s) => ({ name: s.name, startValue: s.startValue, expectedReturn: s.expectedReturn, annualContribution: s.annualContribution, ownerTag: s.ownerTag, saleTaxRate: s.saleTaxRate, carriedBaseValue: s.carriedBaseValue, })), }, realEstates: { create: phase.realEstates.map((re) => ({ name: re.name, marketValue: re.marketValue, mortgage: re.mortgage, valueGrowth: re.valueGrowth, amortization: re.amortization, salePrice: re.salePrice, saleTaxRate: re.saleTaxRate, })), }, oneTimeEvents: { create: phase.oneTimeEvents.map((e) => ({ type: e.type, amount: e.amount, description: e.description, })), }, retirementInfos: { create: phase.retirementInfos.map((r) => ({ personId: r.personId, ahvAmount: r.ahvAmount, pkPensionAmount: r.pkPensionAmount, lumpSumAmount: r.lumpSumAmount, lumpSumTaxRate: r.lumpSumTaxRate, })), }, }, include: phaseInclude, }); lastNewPhaseId = newPhase.id; } await tx.plan.update({ where: { id: newPlan.id }, data: { branchFromPhaseId: lastNewPhaseId }, }); return newPlan.id; }); return NextResponse.json({ planId: newPlanId }, { status: 201 }); }