Einmalige Sonderein-/ausgaben am Cash-Uebergang (Roadmap Nr. 1)
Deploy App / deploy (push) Successful in 1m51s

Der Cash-Uebergang zwischen zwei Phasen ist neu ein eigener Entscheid:
1:1 uebernehmen / einmaliger Zufluss / einmalige Kosten / beides. Die Betraege
gehen direkt aufs Cash-Konto und bleiben aus der Spar-/Verzehrquote heraus.

- Zufluss NOMINAL erfasst, real angezeigt (wie Einkommen), optionaler Steuersatz
  (Default 0 %). Kosten REAL erfasst, nominal angezeigt (wie Ausgaben).
  Umrechnung ueber den Bestands-Deflator an der Phasengrenze.
- Entscheid startet unbeantwortet und zaehlt im "offen"-Badge mit; eine neue Phase
  erzeugt damit automatisch einen offenen Cash-Entscheid am neuen Uebergang.
- Eigene Kopf-Kennzahlen statt Vermischung mit Kapitalzufluss/-investitionen:
  eine Erbschaft ist kein Verkaufserloes, ein Poolbau keine Investition.
- Cash ist kein FinancialElement -> der Entscheid haengt als JSON an der Von-Phase
  (neue Spalte Phase.cashTransition + Migration). Szenario-Kopie nimmt ihn mit.

Fuenf Regressionstests ergaenzt (13 -> 18). Spezifikation auf v0.3.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-07-17 08:14:44 +02:00
parent 87e6a5f564
commit 9f5bd754eb
12 changed files with 665 additions and 30 deletions
+8 -2
View File
@@ -1,7 +1,7 @@
import { Prisma } from "@/generated/prisma/client";
import { prisma } from "@/lib/db";
import { phaseDataSchema, transitionDataSchema } from "@/lib/elements";
import type { PhaseData, TransitionData } from "@/lib/elements";
import { cashTransitionSchema, phaseDataSchema, transitionDataSchema } from "@/lib/elements";
import type { CashTransitionData, PhaseData, TransitionData } from "@/lib/elements";
import type { PlanInput } from "@/lib/types";
export const planInclude = {
@@ -25,6 +25,11 @@ function parseTransitionData(raw: unknown): TransitionData {
return parsed.success ? parsed.data : {};
}
function parseCashTransition(raw: unknown): CashTransitionData {
const parsed = cashTransitionSchema.safeParse(raw);
return parsed.success ? parsed.data : {};
}
export function toPlanInput(plan: PlanWithRelations): PlanInput {
return {
id: plan.id,
@@ -44,6 +49,7 @@ export function toPlanInput(plan: PlanWithRelations): PlanInput {
sequenceNumber: phase.sequenceNumber,
name: phase.name,
durationYears: phase.durationYears,
cashTransition: parseCashTransition(phase.cashTransition),
})),
elements: plan.elements.map((e) => {
const phaseValues: Record<string, PhaseData> = {};