Fix: sonstiges Vermoegen zieht Rate in Verzehrphasen ab statt sie zu addieren
Deploy App / deploy (push) Successful in 1m3s

Vorlauf berechnet das Quoten-Vorzeichen (Einkommen inkl. Renten minus Ausgaben)
vor der Element-Schleife; in Verzehrphasen (Quote < 0) wird die Jahresrate beim
sonstigen Vermoegen als Bezug (negativ) statt als Sparbeitrag verrechnet.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-07-14 07:33:00 +02:00
parent 32adfb4476
commit f49163af60
+35 -4
View File
@@ -187,13 +187,42 @@ export function computePlan(plan: PlanInput): PlanComputed {
} }
// --- Elemente dieser Phase berechnen --- // --- Elemente dieser Phase berechnen ---
const orderedElements = [...plan.elements].sort((a, b) => a.orderIndex - b.orderIndex);
// --- Vorlauf: Quoten-Vorzeichen bestimmen (Einkommen inkl. Renten minus Ausgaben) ---
// Wird VOR der Element-Schleife gebraucht, damit sonstiges Vermoegen in Verzehrphasen
// die Rate abzieht (Bezug) statt sie zu addieren (Sparen).
let preIncome = 0;
let preExpense = 0;
for (const e of orderedElements) {
const carry = carries.get(e.id)!;
if (carry.status !== "ACTIVE") continue;
const pd = e.phaseValues[phase.id] ?? {};
const owner = e.ownerRole && e.ownerRole !== "HOUSEHOLD" ? personByRole(persons, e.ownerRole) : null;
const ownerWorking = owner ? workingByPerson.get(owner.id) ?? false : anyWorking;
switch (e.category) {
case "INCOME":
preIncome += Math.round(num(pd.amount));
break;
case "EXPENSE":
preExpense += Math.round(num(pd.amount));
break;
case "AHV":
if (owner && !ownerWorking) preIncome += ahvFinal.get(owner.id) ?? 0;
break;
case "PENSION_FUND":
if (!ownerWorking && carry.pkPensionAnnual > 0) preIncome += carry.pkPensionAnnual;
break;
}
}
const isConsumptionPhase = preIncome - preExpense < 0;
const elementsComputed: ElementPhaseComputed[] = []; const elementsComputed: ElementPhaseComputed[] = [];
let incomeTotal = 0; let incomeTotal = 0;
let expenseTotal = 0; let expenseTotal = 0;
let quotaAllocated = 0; let quotaAllocated = 0;
let capitalUsed = 0; let capitalUsed = 0;
const orderedElements = [...plan.elements].sort((a, b) => a.orderIndex - b.orderIndex);
for (const e of orderedElements) { for (const e of orderedElements) {
const carry = carries.get(e.id)!; const carry = carries.get(e.id)!;
const pd = e.phaseValues[phase.id] ?? {}; const pd = e.phaseValues[phase.id] ?? {};
@@ -312,11 +341,13 @@ export function computePlan(plan: PlanInput): PlanComputed {
ec.startValue = start; ec.startValue = start;
ec.capitalUse = topUp; ec.capitalUse = topUp;
capitalUsed += topUp; capitalUsed += topUp;
// In Erwerbsphasen Sparbeitrag, in Verzehrphasen Bezugsrate -- beides zaehlt gegen // In Erwerbsphasen Sparbeitrag (wird eingezahlt/waechst), in Verzehrphasen
// die Quote (Vorzeichen ergibt sich aus der Phasenquote). // Bezugsrate (wird entnommen/mindert das Vermoegen). Beides zaehlt betragsmaessig
// gegen die Quote.
ec.quotaUse = contribution; ec.quotaUse = contribution;
quotaAllocated += contribution; quotaAllocated += contribution;
ec.endValue = growAsset(start, r, contribution, phase.durationYears); const annual = isConsumptionPhase ? -contribution : contribution;
ec.endValue = growAsset(start, r, annual, phase.durationYears);
ec.summary = fmt(ec.endValue); ec.summary = fmt(ec.endValue);
break; break;
} }