Detailansichten, Wasserfaelle und vollstaendige Rechenweg-Offenlegung
Deploy App / deploy (push) Successful in 57s
Deploy App / deploy (push) Successful in 57s
Roadmap Nr. 43 (Detailansichten) und Nr. 41 (Berechnungslogiken offenlegen).
Systemparameter-Ansicht:
- SYSTEM_PARAMETERS in constants.ts: Wert, Bedeutung, Herleitung, Quelle,
Stand -- aus derselben Datei, aus der gerechnet wird
Detailansichten (nur lesen, per Expand-Icon):
- Element: Verlauf ueber ALLE Planjahre (neu ElementPhaseComputed.yearly),
bei Immobilien Verkehrswert/Restschuld/Eigenkapital getrennt
- Phase: Vermoegensaufteilung + zwei Wasserfaelle
Zwei getrennte Wasserfaelle (WealthBridge / CashBridge):
- Sparraten, Amortisationen und Investitionen sind UMBUCHUNGEN und erscheinen
nur im Cash-Wasserfall -- als Vermoegensabgang gezeichnet wuerden sie einen
Verlust vortaeuschen, den es nicht gibt
- PK-Beitraege dagegen sind ein echter Vermoegenszugang (belasten kein Cash),
Verrentung ein echter Abgang (Kapital verlaesst die Bilanz)
- residual als Kontrollgroesse fuer die Vollstaendigkeit der Zerlegung
Rechenweg-Protokoll, vollstaendige Abdeckung:
- computePlan(plan, sample?, { explain }) protokolliert die Schritte, die es
ohnehin ausfuehrt -- die Erklaerung IST die Rechnung, statt einer zweiten
Formel-Implementierung im UI, die still abdriften koennte
- standardmaessig aus (Monte Carlo bleibt unberuehrt)
- Arithmetik nicht umgestellt: Zwischengroessen werden als Differenz
abgeleitet, damit die 43 Golden Tests bitgleich bleiben
- jeder Trace verlinkt in die SPEZIFIKATION; ein Test prueft gegen die echte
Datei, dass alle Verweise eine existierende Ueberschrift treffen
SPEZIFIKATION auf 0.11: neue Kapitel 3.6.7, 3.6.8, 4.14, 9.20, 9.21.
12 Tests ergaenzt (80 -> 92). Keine DB-Aenderung.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,92 @@
|
||||
"use client";
|
||||
|
||||
import { SlidersHorizontal } from "lucide-react";
|
||||
import { formatChf } from "@/lib/format";
|
||||
import { SYSTEM_PARAMETERS, type ParameterGroup, type SystemParameter } from "@/lib/constants";
|
||||
|
||||
const GROUPS: { key: ParameterGroup; title: string; intro: string }[] = [
|
||||
{
|
||||
key: "AHV",
|
||||
title: "AHV",
|
||||
intro:
|
||||
"Diese Werte legt der Bund fest. Sie ändern sich periodisch – der Stand ist je Parameter ausgewiesen.",
|
||||
},
|
||||
{
|
||||
key: "Vorsorge",
|
||||
title: "Berufliche und gebundene Vorsorge",
|
||||
intro: "Höchstbeträge und Umwandlung. Der Umwandlungssatz ist ein Vorschlag und pro Ereignis überschreibbar.",
|
||||
},
|
||||
{
|
||||
key: "Steuern",
|
||||
title: "Steuersätze",
|
||||
intro:
|
||||
"Bewusst pauschale Vorschlagswerte, keine Steuerberechnung: Die tatsächlichen Sätze sind kantonal, progressiv und vom Einzelfall abhängig. Sie lassen sich pro Ereignis überschreiben.",
|
||||
},
|
||||
];
|
||||
|
||||
function formatValue(p: SystemParameter): string {
|
||||
switch (p.unit) {
|
||||
case "CHF":
|
||||
return `${formatChf(p.value)} CHF`;
|
||||
case "%":
|
||||
return `${p.value} %`;
|
||||
case "Jahre":
|
||||
return `${p.value} Jahre`;
|
||||
default:
|
||||
return String(p.value);
|
||||
}
|
||||
}
|
||||
|
||||
// Zeigt die Systemparameter mit Wert, Bedeutung, Herleitung und Quelle (Roadmap Nr. 41).
|
||||
// Die Eintraege stammen direkt aus constants.ts -- dieselbe Datenquelle, aus der auch
|
||||
// gerechnet wird. Ein Abdriften zwischen Anzeige und Rechnung ist damit ausgeschlossen.
|
||||
export function SystemParametersView() {
|
||||
return (
|
||||
<div className="flex flex-col gap-5">
|
||||
<div className="flex items-center gap-2">
|
||||
<SlidersHorizontal className="h-5 w-5 text-accent" />
|
||||
<h2 className="text-lg font-semibold text-fg">Systemparameter</h2>
|
||||
</div>
|
||||
|
||||
<p className="max-w-3xl text-sm text-muted">
|
||||
Alle fest hinterlegten Grössen, mit denen das Tool rechnet – inklusive Herleitung und Quelle. Sie stammen aus
|
||||
derselben Datei, aus der auch die Berechnung liest; was hier steht, ist also garantiert das, was gerechnet wird.
|
||||
</p>
|
||||
|
||||
{GROUPS.map((g) => {
|
||||
const rows = SYSTEM_PARAMETERS.filter((p) => p.group === g.key);
|
||||
if (rows.length === 0) return null;
|
||||
return (
|
||||
<section key={g.key} className="rounded-xl border border-border bg-surface p-4 shadow-sm">
|
||||
<h3 className="text-sm font-semibold text-fg">{g.title}</h3>
|
||||
<p className="mb-3 mt-0.5 text-xs text-muted">{g.intro}</p>
|
||||
<div className="flex flex-col gap-3">
|
||||
{rows.map((p) => (
|
||||
<div key={p.key} className="rounded-lg border border-border bg-surface-2 p-3">
|
||||
<div className="flex flex-wrap items-baseline justify-between gap-2">
|
||||
<span className="text-sm font-medium text-fg">{p.label}</span>
|
||||
<span className="text-sm font-semibold text-accent">{formatValue(p)}</span>
|
||||
</div>
|
||||
<p className="mt-1 text-xs text-muted">{p.meaning}</p>
|
||||
{p.derivation && (
|
||||
<p className="mt-1 font-mono text-[11px] text-faint">Herleitung: {p.derivation}</p>
|
||||
)}
|
||||
<div className="mt-1.5 flex flex-wrap gap-x-4 gap-y-1 text-[11px] text-faint">
|
||||
<span>Quelle: {p.source}</span>
|
||||
<span>Stand: {p.validFrom}</span>
|
||||
{p.editablePerEvent && <span className="text-accent">pro Ereignis überschreibbar</span>}
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
})}
|
||||
|
||||
<p className="max-w-3xl text-xs text-faint">
|
||||
Die drei Steuersätze werden sowohl als Vorschlag im Eingabefeld als auch in der Berechnung als Rückfallwert
|
||||
verwendet – damit ein nicht angetippter Wert nicht fälschlich als 0 % gerechnet wird.
|
||||
</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user