Files
FPT/prisma/migrations/20260718090000_plan_scenario_hierarchy/migration.sql
T
admGitAICDS a5d4868c58
Deploy App / deploy (push) Successful in 1m43s
Szenario-Hierarchie: Plan als Behaelter, Diff-Markierung (V6)
Groesste Umstrukturierung bisher. Der PLAN ist neu ein schlanker Behaelter ohne
Finanzdaten; die berechenbare Einheit ist das SZENARIO.

Modell:
- Jeder Plan bekommt beim Anlegen automatisch ein Basisszenario (isBase).
- Das Grundprofil liegt am Szenario, nicht am Plan -- nur so sind Szenarien mit
  abweichendem PENSIONSALTER moeglich (Fruehpensionierung), das in Person steckt.
- Neue Szenarien sind vollstaendige Kopien eines BELIEBIGEN Szenarios und haengen
  als Baum darunter (parentScenarioId); die Seitenleiste rueckt sie ein.
- Kopierte Phasen/Elemente tragen Herkunfts-Verweise (sourcePhaseId,
  sourceElementId). Ueber den Namen zu matchen waere fragil gewesen.

Abweichungs-Markierung (Diff gegen das Eltern-Szenario, live):
- geaendert = gelb, neu = gruen + Badge, entfernt = graue Geisterzeile.
- Markiert: Phasen-/Uebergangszellen, Element-Zeilen, Phasenkoepfe, Cash-Anfangswert,
  Cash-Uebergaenge, Grundprofil. Zaehler ueber der Matrix.
- Eigene Theme-Tokens fuer Hell/Dunkel/Warm -- ein fester Gelbwert waere im
  Dunkelschema unbrauchbar.

Charts vergleichen neu die Geschwister-Szenarien statt fremder Plaene.

Datenmodell/Migration:
- Neue Tabelle Plan; bisheriger Plan -> Scenario (IDs erhalten, damit alle
  Kind-Fremdschluessel gueltig bleiben); planId -> scenarioId in Person/Phase/
  FinancialElement. Bestehende Szenarien werden per rekursivem CTE demselben
  Behaelter zugeordnet, auch mehrfach verschachtelte.
- Migration VOR dem Deploy gegen echtes PostgreSQL verifiziert (PGlite, in-process),
  inkl. verschachtelter Szenarien und Cascade. Der Test ist als
  migrations.test.ts committet und sichert kuenftige Migrationen ab.

API neu unter /api/scenarios/*. 10 neue Tests (48 -> 58). Spezifikation auf v0.8.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-18 09:51:44 +02:00

87 lines
4.2 KiB
SQL

-- V6: Szenario-Hierarchie. Der bisherige "Plan" wird zum SZENARIO (IDs bleiben erhalten,
-- damit alle Kind-Datensaetze gueltig bleiben); darueber entsteht ein neuer, schlanker PLAN
-- als Behaelter. Bestehende Szenarien (bisher: Plan mit parentPlanId) werden als Baum unter
-- denselben Plan gehaengt wie ihr Ursprung.
-- 1) Bisherige Plan-Tabelle wird zum Szenario. Kind-FKs folgen dem Rename automatisch.
ALTER TABLE "Plan" RENAME TO "Scenario";
ALTER TABLE "Scenario" RENAME COLUMN "parentPlanId" TO "parentScenarioId";
ALTER TABLE "Scenario" RENAME CONSTRAINT "Plan_pkey" TO "Scenario_pkey";
ALTER TABLE "Scenario" RENAME CONSTRAINT "Plan_parentPlanId_fkey" TO "Scenario_parentScenarioId_fkey";
ALTER TABLE "Scenario" ADD COLUMN "isBase" BOOLEAN NOT NULL DEFAULT false;
ALTER TABLE "Scenario" ADD COLUMN "planId" TEXT;
-- 2) Neuer Behaelter "Plan".
CREATE TABLE "Plan" (
"id" TEXT NOT NULL,
"userId" TEXT NOT NULL,
"name" TEXT NOT NULL,
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT "Plan_pkey" PRIMARY KEY ("id")
);
-- 3) Je Wurzel-Szenario (bisher: eigenstaendiger Plan) einen Behaelter anlegen. Die Plan-ID
-- wird deterministisch aus der Szenario-ID abgeleitet, damit die Zuordnung ohne
-- Hilfstabelle moeglich ist.
INSERT INTO "Plan" ("id", "userId", "name", "createdAt", "updatedAt")
SELECT 'plan_' || s."id", s."userId", s."name", s."createdAt", CURRENT_TIMESTAMP
FROM "Scenario" s
WHERE s."parentScenarioId" IS NULL;
-- 4) Wurzel-Szenarien werden zum Basisszenario ihres Behaelters. Der bisherige Plan-Name
-- wandert auf den Behaelter (Schritt 3); das Szenario heisst neu "Basisszenario".
UPDATE "Scenario" s
SET "planId" = 'plan_' || s."id", "isBase" = true, "name" = 'Basisszenario'
WHERE s."parentScenarioId" IS NULL;
-- 5) Kind-Szenarien erben den Behaelter ihrer Wurzel (beliebig tief verschachtelt).
WITH RECURSIVE tree AS (
SELECT "id", "planId" FROM "Scenario" WHERE "parentScenarioId" IS NULL
UNION ALL
SELECT c."id", t."planId" FROM "Scenario" c JOIN tree t ON c."parentScenarioId" = t."id"
)
UPDATE "Scenario" s
SET "planId" = t."planId"
FROM tree t
WHERE s."id" = t."id" AND s."planId" IS NULL;
-- 6) Sicherheitsnetz: Szenarien, deren Elternteil fehlt (verwaist), werden eigenstaendig.
INSERT INTO "Plan" ("id", "userId", "name", "createdAt", "updatedAt")
SELECT 'plan_' || s."id", s."userId", s."name", s."createdAt", CURRENT_TIMESTAMP
FROM "Scenario" s
WHERE s."planId" IS NULL;
UPDATE "Scenario" s
SET "planId" = 'plan_' || s."id", "isBase" = true, "parentScenarioId" = NULL
WHERE s."planId" IS NULL;
-- 7) Beziehungen des Behaelters festziehen.
ALTER TABLE "Scenario" ALTER COLUMN "planId" SET NOT NULL;
ALTER TABLE "Plan" ADD CONSTRAINT "Plan_userId_fkey"
FOREIGN KEY ("userId") REFERENCES "User"("id") ON DELETE CASCADE ON UPDATE CASCADE;
ALTER TABLE "Scenario" ADD CONSTRAINT "Scenario_planId_fkey"
FOREIGN KEY ("planId") REFERENCES "Plan"("id") ON DELETE CASCADE ON UPDATE CASCADE;
-- 8) Eigentuemer haengt neu am Behaelter; die alten Felder am Szenario entfallen.
ALTER TABLE "Scenario" DROP CONSTRAINT "Plan_userId_fkey";
ALTER TABLE "Scenario" DROP COLUMN "userId";
ALTER TABLE "Scenario" DROP COLUMN "branchFromPhaseId";
-- 9) Kind-Tabellen: planId -> scenarioId (inkl. Constraint- und Index-Namen).
ALTER TABLE "Person" RENAME COLUMN "planId" TO "scenarioId";
ALTER TABLE "Person" RENAME CONSTRAINT "Person_planId_fkey" TO "Person_scenarioId_fkey";
ALTER INDEX "Person_planId_role_key" RENAME TO "Person_scenarioId_role_key";
ALTER TABLE "Phase" RENAME COLUMN "planId" TO "scenarioId";
ALTER TABLE "Phase" RENAME CONSTRAINT "Phase_planId_fkey" TO "Phase_scenarioId_fkey";
ALTER INDEX "Phase_planId_sequenceNumber_key" RENAME TO "Phase_scenarioId_sequenceNumber_key";
ALTER TABLE "FinancialElement" RENAME COLUMN "planId" TO "scenarioId";
ALTER TABLE "FinancialElement" RENAME CONSTRAINT "FinancialElement_planId_fkey" TO "FinancialElement_scenarioId_fkey";
-- 10) Herkunfts-Verweise fuer die Abweichungs-Markierung (Diff gegen das Eltern-Szenario).
ALTER TABLE "Phase" ADD COLUMN "sourcePhaseId" TEXT;
ALTER TABLE "FinancialElement" ADD COLUMN "sourceElementId" TEXT;