Fix Uebergang: Entscheide werden zuverlaessig gespeichert; Klick auf Zelle oeffnet Popup
Deploy App / deploy (push) Successful in 1m0s

- Ursache des "?"-Bugs: sichtbarer Default (Halten/Rente) wurde ohne aktive Auswahl
  nicht in den Datensatz geschrieben -> beim Speichern blieb der Entscheid leer.
  Fix: withTransitionDefaults belegt den Entscheid explizit vor (Popup + Review-Panel),
  sodass ein blosses Speichern den Default persistiert.
- Uebergangszellen oeffnen neu ein kleines Popup (TransitionCellDialog) statt des
  unteren Detail-Panels; offene Zellen sind kraeftig eingefaerbt.
- PK und 3a bekommen im normalen Uebergang einen echten Bezugs-Entscheid
  (Kein Bezug / Bezug + Betrag) inkl. offenem "?"-Status bis entschieden.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-07-14 07:45:05 +02:00
parent f49163af60
commit 737bc2051f
3 changed files with 182 additions and 58 deletions
+72 -18
View File
@@ -44,6 +44,76 @@ function DerivedField({ label, value, help }: { label: string; value: number; he
);
}
// PK/3a-Bezugs-Entscheid im normalen Uebergang: Kein Bezug / Bezug (+ Betrag).
function WithdrawalDecision({
td,
setT,
max,
label,
}: {
td: TransitionData;
setT: (patch: Partial<TransitionData>) => void;
max: number;
label: string;
}) {
const mode = td.withdrawalMode ?? "NONE";
return (
<>
<SelectField
label="Bezug?"
value={mode}
onChange={(v: "NONE" | "AMOUNT") => setT(v === "NONE" ? { withdrawalMode: v, withdrawal: 0 } : { withdrawalMode: v })}
options={[
{ value: "NONE", label: "Kein Bezug" },
{ value: "AMOUNT", label: "Bezug" },
]}
/>
{mode === "AMOUNT" && (
<MoneyField
label={label}
help={`Maximal ${formatChf(max)} (Endwert der Vorphase).`}
value={num(td.withdrawal)}
max={max}
onChange={(v) => setT({ withdrawal: v })}
/>
)}
</>
);
}
// Vorbelegung expliziter Entscheide, damit ein blosses "Speichern" den sichtbaren Default
// (Halten / Kein Bezug / Rente) auch tatsaechlich persistiert.
export function withTransitionDefaults(category: ElementCategory, isRetirement: boolean, td: TransitionData): TransitionData {
const out = { ...td };
if (category === "REAL_ESTATE" || category === "OTHER_ASSET") {
if (out.decision === undefined) out.decision = "HOLD";
} else if (category === "PENSION_FUND") {
if (isRetirement) {
if (out.payoutMode === undefined) out.payoutMode = "PENSION";
} else if (out.withdrawalMode === undefined) {
out.withdrawalMode = "NONE";
}
} else if (category === "PILLAR_3A") {
if (!isRetirement && out.withdrawalMode === undefined) out.withdrawalMode = "NONE";
}
return out;
}
// "Beantwortet" = ein konkreter Entscheid liegt vor (kein offenes Fragezeichen).
export function isTransitionAnswered(category: ElementCategory, isRetirement: boolean, td: TransitionData): boolean {
switch (category) {
case "REAL_ESTATE":
case "OTHER_ASSET":
return td.decision !== undefined;
case "PENSION_FUND":
return isRetirement ? td.payoutMode !== undefined : td.withdrawalMode !== undefined;
case "PILLAR_3A":
return isRetirement ? true : td.withdrawalMode !== undefined;
default:
return true;
}
}
// --- Wiederverwendbare Feldgruppen (Detail-Panel, Erstell-Popup, Uebergangs-Review) ---
export function ElementPhaseFields({
@@ -285,15 +355,7 @@ export function ElementTransitionFields({
</>
);
}
return (
<MoneyField
label="PK-Bezug (CHF)"
help={`Optionaler Bezug. Maximal ${formatChf(context.carriedEndValue)} (Endwert der Vorphase).`}
value={num(td.withdrawal)}
max={context.carriedEndValue}
onChange={(v) => setT({ withdrawal: v })}
/>
);
return <WithdrawalDecision td={td} setT={setT} max={context.carriedEndValue} label="PK-Bezug (CHF)" />;
case "PILLAR_3A":
if (context.isRetirementTransition) {
return (
@@ -306,15 +368,7 @@ export function ElementTransitionFields({
/>
);
}
return (
<MoneyField
label="3a-Bezug (CHF)"
help={`Maximal ${formatChf(context.carriedEndValue)} (Endwert der Vorphase).`}
value={num(td.withdrawal)}
max={context.carriedEndValue}
onChange={(v) => setT({ withdrawal: v })}
/>
);
return <WithdrawalDecision td={td} setT={setT} max={context.carriedEndValue} label="3a-Bezug (CHF)" />;
case "REAL_ESTATE": {
const decision = td.decision ?? "HOLD";
return (