Neuer Knopf auf Plan-Ebene: Liste plus Wizard in zwei Schritten. Ein
Ist-Satz haengt am PLAN, nicht am Szenario -- die Zuordnung laeuft ueber
die Herkunfts-Kette sourceElementId.
computePlan nimmt neu { actuals }: Die Werte schnappen in jedem erfassten
Jahr auf die Realitaet und laufen von dort planmaessig weiter. Luecken
fallen auf die Plandaten zurueck. Ohne die Option unveraendert -- die 43
Golden Tests laufen durch.
Der Sprung ist keine Rendite: eigene Brueckenposition actualsCorrection
in Vermoegens- und Cash-Bruecke, sonst ginge die Zerlegung nicht auf.
Matrix: Umschalter Plan/Effektiv, im Ist-Modus mit farbiger Abweichung
statt acht Zahlen je Zelle. Zeitachse: Marker je Jahr, juengster farbig.
Vier Analysewerkzeuge mit einheitlicher Leiste (nominal/real als
Einfachauswahl, Plan/Effektiv). MC: Zielbetrag dreht mit, Startjahr
abgeleitet statt eingebbar.
Neue Tabelle ActualsSet (gegen echtes Postgres verifiziert), Module
actuals.ts und dataview.ts. Spezifikation 0.21, 27 Tests (181 -> 208).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -3,9 +3,11 @@
|
||||
import { useMemo, useState } from "react";
|
||||
import { Bar, BarChart, CartesianGrid, ReferenceLine, ResponsiveContainer, Tooltip, XAxis, YAxis } from "recharts";
|
||||
import { Tornado, X } from "lucide-react";
|
||||
import { RequiredNumberField, SelectField } from "@/components/FormField";
|
||||
import { RequiredNumberField } from "@/components/FormField";
|
||||
import { InfoBubble } from "@/components/InfoBubble";
|
||||
import { useVersionedPlan, VersionBar } from "@/components/VersionPicker";
|
||||
import { AnalysisBar, useAnalysisBasis } from "@/components/AnalysisControls";
|
||||
import type { ActualsSetInput, ElementOrigin } from "@/lib/actuals";
|
||||
import { formatChf } from "@/lib/format";
|
||||
import {
|
||||
computeTornado,
|
||||
@@ -35,12 +37,23 @@ function formatRange(low: number, high: number, unit: DriverDef["unit"]): string
|
||||
return `${sign(low)} ${suffix} → ${sign(high)} ${suffix}`;
|
||||
}
|
||||
|
||||
export function SensitivityDialog({ plan: currentPlan, onClose }: { plan: PlanInput; onClose: () => void }) {
|
||||
export function SensitivityDialog({
|
||||
plan: currentPlan,
|
||||
actuals = [],
|
||||
origins = [],
|
||||
onClose,
|
||||
}: {
|
||||
plan: PlanInput;
|
||||
actuals?: ActualsSetInput[];
|
||||
origins?: ElementOrigin[];
|
||||
onClose: () => void;
|
||||
}) {
|
||||
// Gerechnet wird wahlweise auf dem Arbeitsstand oder auf einer festgehaltenen Version.
|
||||
const { versionId, setVersionId, plan, loading: versionLoading } = useVersionedPlan(currentPlan.id, currentPlan);
|
||||
const available = useMemo(() => DRIVERS.filter((d) => d.applies(plan)), [plan]);
|
||||
|
||||
const [metric, setMetric] = useState<TornadoMetric>("real");
|
||||
const basis = useAnalysisBasis(plan, actuals, origins, "real");
|
||||
const metric: TornadoMetric = basis.metric;
|
||||
const [drafts, setDrafts] = useState<Record<string, RangeDraft>>({});
|
||||
const [result, setResult] = useState<TornadoResult | null>(null);
|
||||
|
||||
@@ -75,7 +88,10 @@ export function SensitivityDialog({ plan: currentPlan, onClose }: { plan: PlanIn
|
||||
checked.map((d) => {
|
||||
const dr = draftFor(d.id);
|
||||
return { id: d.id, low: Number(dr.low), high: Number(dr.high) };
|
||||
})
|
||||
}),
|
||||
// Mit Ist-Werten wirken die Treiber nur noch auf die NICHT belegten Jahre -- was
|
||||
// erfasst ist, steht fest. Die Balken fallen dadurch zu Recht kuerzer aus.
|
||||
basis.resolvedActuals
|
||||
)
|
||||
);
|
||||
}
|
||||
@@ -143,22 +159,8 @@ export function SensitivityDialog({ plan: currentPlan, onClose }: { plan: PlanIn
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* Zielgrösse */}
|
||||
<div className="grid grid-cols-1 gap-3 sm:grid-cols-3">
|
||||
<SelectField
|
||||
label="Zielgrösse"
|
||||
help="Woran die Wirkung gemessen wird: das Endvermögen der letzten Lebensphase. «Real» ist kaufkraftbereinigt auf den Planbeginn und damit die ehrlichere Grösse."
|
||||
value={metric}
|
||||
onChange={(v: TornadoMetric) => {
|
||||
setMetric(v);
|
||||
setResult(null);
|
||||
}}
|
||||
options={[
|
||||
{ value: "real", label: "Endvermögen real (kaufkraftbereinigt)" },
|
||||
{ value: "nominal", label: "Endvermögen nominal" },
|
||||
]}
|
||||
/>
|
||||
</div>
|
||||
{/* Zielgrösse und Datengrundlage */}
|
||||
<AnalysisBar basis={basis} onChange={() => setResult(null)} />
|
||||
|
||||
{/* Parameter */}
|
||||
<div>
|
||||
|
||||
Reference in New Issue
Block a user