Versionierung und Aenderungshistorie je Szenario
Deploy App / deploy (push) Successful in 1m48s

Version A.B: B automatisch je Bearbeitungssitzung (10-Minuten-Fenster,
unveraenderte Staende erzeugen keine), A manuell mit Pflichtkommentar.

Eine Version haelt den vollstaendigen Zustand als PlanInput-JSON -- dadurch
ist die Versionsauswahl in allen vier Analysewerkzeugen fast kostenlos,
bei Monte-Carlo je Szenario einzeln.

Wiederherstellen erhaelt die IDs (sonst verlieren Kind-Szenarien ihre
Diff-Basis) und legt den Stand selbst als neue Version an. Wo ein Bezug
trotzdem bricht, warnt der Dialog vorher namentlich.

Statischer Waechter-Test: jeder schreibende Endpunkt loest eine Version aus.
Migration gegen echtes Postgres verifiziert.

Spezifikation 0.19 (3.8 und 9.28 neu), 25 Tests (139 -> 164).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-07-19 22:09:10 +02:00
parent d04e07fdfb
commit d023534a03
28 changed files with 2021 additions and 13 deletions
@@ -2,6 +2,7 @@ import { NextRequest, NextResponse } from "next/server";
import { prisma } from "@/lib/db";
import { getOwnedElement } from "@/lib/queries";
import { getCurrentUserId } from "@/lib/session";
import { touchScenario } from "@/lib/versioning-db";
import { phaseDataSchema } from "@/lib/elements";
// Speichert die Werte eines Elements innerhalb einer Lebensphase (Upsert).
@@ -29,5 +30,6 @@ export async function PUT(
update: { data: parsed.data },
});
await touchScenario(element.scenarioId, userId);
return NextResponse.json({ ok: true });
}
@@ -3,6 +3,7 @@ import { z } from "zod";
import { prisma } from "@/lib/db";
import { getOwnedElement } from "@/lib/queries";
import { getCurrentUserId } from "@/lib/session";
import { touchScenario } from "@/lib/versioning-db";
const patchSchema = z.object({ name: z.string().min(1).max(120) });
@@ -22,6 +23,7 @@ export async function PATCH(
if (!parsed.success) return NextResponse.json({ error: "Ungültige Eingabe." }, { status: 400 });
await prisma.financialElement.update({ where: { id: element.id }, data: { name: parsed.data.name } });
await touchScenario(element.scenarioId, userId);
return NextResponse.json({ ok: true });
}
@@ -37,5 +39,6 @@ export async function DELETE(
if (!element) return NextResponse.json({ error: "Element nicht gefunden." }, { status: 404 });
await prisma.financialElement.delete({ where: { id: element.id } });
await touchScenario(element.scenarioId, userId);
return NextResponse.json({ ok: true });
}
@@ -2,6 +2,7 @@ import { NextRequest, NextResponse } from "next/server";
import { prisma } from "@/lib/db";
import { getOwnedElement } from "@/lib/queries";
import { getCurrentUserId } from "@/lib/session";
import { touchScenario } from "@/lib/versioning-db";
import { transitionDataSchema } from "@/lib/elements";
// Speichert den Übergangs-Entscheid eines Elements nach der Phase fromPhase (Upsert).
@@ -29,5 +30,6 @@ export async function PUT(
update: { data: parsed.data },
});
await touchScenario(element.scenarioId, userId);
return NextResponse.json({ ok: true });
}