Das Pensionsalter laesst sich neu veraendern: Nicht das Alter wird gesetzt, sondern die Phasengrenze verschoben -- Vorphase laenger, Folgephase kuerzer, Gesamtdauer gleich. Faellt eine Phase dabei weg, werden die beiden Uebergaenge nach Bestaetigung zusammengelegt. - neues reines Modul lib/retirement.ts + POST /api/scenarios/<id>/retirement - Pensionsalter als Tornado-Treiber und Live-Simulations-Regler - AHV-Referenzalter 65: Rente ab 65 unabhaengig vom Pensionsalter; vor 65 Beitrag als Nichterwerbstaetige(r) (neues Feld ahvContribution). AHV wird dafuer jahresweise statt phasenweise gerechnet. - Punkt B: personenzugeordnetes Einkommen faellt bei Pensionierung auf 0 - Punkt A: Wiederkehr-Parameter werden live aus der Vorphase geerbt, sichtbar als "Aus Vorphase uebernehmen" - Punkt C: Kapitalzufluss am Pensions-Uebergang per Quote auf Amortisation, Anlage und Cash verteilbar - Fix: carry.flowBasis wurde vor der Jahresschleife berechnet, effektive Werte kamen deshalb nie in der Folgephase an SPEZIFIKATION 0.26 (neue Kapitel 3.12, 4.4.7, 4.16). 221 -> 261 Tests. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
+164
-33
@@ -4,6 +4,7 @@ import {
|
||||
AHV_FULL_CONTRIBUTION_YEARS,
|
||||
AHV_GROSS_FROM_NET_FACTOR,
|
||||
AHV_MAX_ANNUAL_SINGLE,
|
||||
AHV_REFERENCE_AGE,
|
||||
AHV_MIN_MONTHLY_FULL,
|
||||
AHV_PENSION_MONTHS,
|
||||
DEFAULT_CAPITAL_TAX_RATE,
|
||||
@@ -292,6 +293,9 @@ interface Carry {
|
||||
owed: number; // Schulden: Restschuld (positiv)
|
||||
pkPensionAnnual: number; // PK: jährliche Rente nach Verrentung
|
||||
flowBasis: number; // Einkommen/Ausgaben: indexierter Basiswert der nächsten Phase
|
||||
// Punkt A (Roadmap Nr. 44): zuletzt verwendete Wiederkehr-Parameter (Raten, Beiträge,
|
||||
// Amortisation). Fehlt der Wert in einer Phase, gilt der aus der Vorphase.
|
||||
rates: Record<string, number>;
|
||||
hasCarry: boolean;
|
||||
}
|
||||
|
||||
@@ -305,6 +309,7 @@ function emptyCarry(): Carry {
|
||||
owed: 0,
|
||||
pkPensionAnnual: 0,
|
||||
flowBasis: 0,
|
||||
rates: {},
|
||||
hasCarry: false,
|
||||
};
|
||||
}
|
||||
@@ -448,12 +453,17 @@ export function computePlan(plan: PlanInput, sample?: PlanSample, options?: Comp
|
||||
});
|
||||
}
|
||||
|
||||
// AHV-Renten der Pensionierten: Vollrente zum mdJE, gekürzt um die Ausfalljahre.
|
||||
// AHV-Renten: Vollrente zum mdJE, gekürzt um die Ausfalljahre.
|
||||
//
|
||||
// Seit Roadmap Nr. 44 wird die Rente für JEDE Person mit AHV-Element gerechnet, nicht nur
|
||||
// für bereits pensionierte: Sie fliesst ab dem REFERENZALTER -- auch wenn jemand darüber
|
||||
// hinaus arbeitet. Ob sie in einem Jahr tatsächlich fliesst, entscheidet die Jahres-
|
||||
// schleife anhand des Alters (Kap. 4.4.6).
|
||||
const ahvUncapped = new Map<string, number>();
|
||||
for (const e of plan.elements) {
|
||||
if (e.category !== "AHV" || !e.ownerRole) continue;
|
||||
const owner = personByRole(persons, e.ownerRole);
|
||||
if (!owner || workingByPerson.get(owner.id)) continue;
|
||||
if (!owner) continue;
|
||||
const before = ahvBeforeByPerson.get(owner.id) ?? { avg: 0, gap: 0 };
|
||||
const career = buildCareer(owner, ahvIncomeAccum, ahvYearsAccum, gapYearsByPerson);
|
||||
const mdJE = ahvMdje(career, before.avg, before.gap);
|
||||
@@ -472,9 +482,19 @@ export function computePlan(plan: PlanInput, sample?: PlanSample, options?: Comp
|
||||
const ecById = new Map<string, ElementPhaseComputed>();
|
||||
// Reales Durchschnittseinkommen dieser Phase je Person (für die AHV-Karriere).
|
||||
const phaseRealIncomeByPerson = new Map<string, number>();
|
||||
const incomes: { basis: number; idx: number; ec: ElementPhaseComputed }[] = [];
|
||||
const expenses: { basis: number; idx: number; ec: ElementPhaseComputed }[] = [];
|
||||
let renteTotal = 0; // AHV + PK-Renten (nominal fix)
|
||||
const incomes: { basis: number; idx: number; ec: ElementPhaseComputed; carry: Carry }[] = [];
|
||||
const expenses: { basis: number; idx: number; ec: ElementPhaseComputed; carry: Carry }[] = [];
|
||||
let renteTotal = 0; // PK-Renten (nominal fix ueber die ganze Phase)
|
||||
// AHV je Person: Rente und Beitrag stehen fest, WANN sie greifen entscheidet das Alter
|
||||
// im jeweiligen Jahr -- deshalb eine eigene Liste statt eines Phasenbetrags.
|
||||
const ahvItems: {
|
||||
ownerId: string;
|
||||
ownerStartAge: number;
|
||||
rente: number;
|
||||
beitrag: number;
|
||||
working: boolean;
|
||||
ec: ElementPhaseComputed;
|
||||
}[] = [];
|
||||
// `isPk` für die Vermögens-Brücke: PK-Beiträge verlassen das Cash NICHT (sie sind im
|
||||
// Nettolohn bereits abgezogen), erhöhen aber das Vermögen -- sie sind also ein echter
|
||||
// Zugang und keine Umbuchung.
|
||||
@@ -512,6 +532,17 @@ export function computePlan(plan: PlanInput, sample?: PlanSample, options?: Comp
|
||||
const owner = e.ownerRole && e.ownerRole !== "HOUSEHOLD" ? personByRole(persons, e.ownerRole) : null;
|
||||
const ownerWorking = owner ? workingByPerson.get(owner.id) ?? false : anyWorking;
|
||||
|
||||
// Punkt A (Roadmap Nr. 44): Ein nicht erfasster Wiederkehr-Parameter wird aus der
|
||||
// Vorphase ÜBERNOMMEN, statt stillschweigend auf 0 zu fallen. Im UI ist das das
|
||||
// angehakte «Aus Vorphase übernehmen»; ein eigener Wert hakt es ab. Der jeweils
|
||||
// verwendete Wert wird mitgeführt, damit die Kette über mehrere Phasen trägt.
|
||||
const inherited = (key: string, fallback = 0): number => {
|
||||
const own = (pd as Record<string, unknown>)[key];
|
||||
const v = typeof own === "number" ? own : carry.rates[key] ?? fallback;
|
||||
carry.rates[key] = v;
|
||||
return v;
|
||||
};
|
||||
|
||||
const ec: ElementPhaseComputed = {
|
||||
elementId: e.id,
|
||||
category: e.category,
|
||||
@@ -549,14 +580,24 @@ export function computePlan(plan: PlanInput, sample?: PlanSample, options?: Comp
|
||||
// REAL (heutige Kaufkraft; reale Mehrausgaben) -- die Inflation kommt separat dazu.
|
||||
// Rate-Default = 0 %. Basiswert ab Phase 2 = fortgeschriebener Wert der Vorphase
|
||||
// (nominal für Einkommen, real für Ausgaben), ausser bewusst geändert (pd.amount).
|
||||
const idx = num(pd.teuerungsausgleich, 0);
|
||||
const idx = inherited("teuerungsausgleich");
|
||||
ec.baseValue = carry.hasCarry ? Math.round(carry.flowBasis) : Math.round(num(pd.amount));
|
||||
const basis = !carry.hasCarry
|
||||
let basis = !carry.hasCarry
|
||||
? Math.round(num(pd.amount))
|
||||
: typeof pd.amount === "number"
|
||||
? Math.round(pd.amount)
|
||||
: ec.baseValue;
|
||||
(e.category === "INCOME" ? incomes : expenses).push({ basis, idx, ec });
|
||||
// Ist die besitzende Person pensioniert, fällt ihr Erwerbseinkommen weg -- sonst liefe
|
||||
// der Lohn stillschweigend in die Pension weiter (Kap. 4.4.7). Ein ausdrücklich
|
||||
// erfasster Betrag gewinnt, damit ein Teilzeitpensum oder eine Erwerbsersatz-Zahlung
|
||||
// modellierbar bleibt. Gemeinsame Einkommen (Mieterträge o. Ä.) sind NICHT betroffen,
|
||||
// weil sie nicht an der Erwerbstätigkeit einer Person hängen.
|
||||
if (e.category === "INCOME" && owner && !ownerWorking && typeof pd.amount !== "number") {
|
||||
basis = 0;
|
||||
ec.baseValue = 0;
|
||||
ec.note = "Wegen Pensionierung auf 0 gesetzt. Für ein Teilzeitpensum trage hier einen Betrag ein.";
|
||||
}
|
||||
(e.category === "INCOME" ? incomes : expenses).push({ basis, idx, ec, carry });
|
||||
|
||||
// AHV: reales Erwerbseinkommen der Person mitführen. Nur Einkommen, die einer
|
||||
// Person zugeordnet sind -- bei einem Einzelplan zählt "Gemeinsam" zur Person A.
|
||||
@@ -576,20 +617,41 @@ export function computePlan(plan: PlanInput, sample?: PlanSample, options?: Comp
|
||||
}
|
||||
}
|
||||
|
||||
// Basiswert der Folgephase fortschreiben (nominal für Einkommen, real für Ausgaben).
|
||||
carry.flowBasis = basis * Math.pow(1 + idx / 100, duration);
|
||||
// Die Fortschreibung in die Folgephase passiert NACH der Jahresschleife: Dort kann ein
|
||||
// effektiver Wert `basis` noch neu setzen (`rebaseFlow`), und genau der soll weiter-
|
||||
// getragen werden -- nicht der ursprünglich geplante.
|
||||
break;
|
||||
}
|
||||
case "AHV": {
|
||||
if (owner && !ownerWorking) {
|
||||
// Die AHV wird JAHRESWEISE gerechnet (Kap. 4.4.6): Bis zum Referenzalter zahlt eine
|
||||
// frühpensionierte Person Beiträge, ab dem Referenzalter fliesst die Rente -- beides
|
||||
// kann INNERHALB derselben Phase kippen, deshalb nicht über `renteTotal`.
|
||||
if (owner) {
|
||||
const rente = ahvFinal.get(owner.id) ?? 0;
|
||||
renteTotal += rente;
|
||||
ec.startValue = rente;
|
||||
ec.endValue = rente;
|
||||
ec.summary = `Rente ${fmt(rente)}`;
|
||||
} else {
|
||||
const gap = Math.max(0, Math.round(num(pd.gapYears)));
|
||||
ec.summary = gap > 0 ? `${gap} Ausfalljahre` : "Keine Ausfalljahre";
|
||||
const beitrag = Math.round(num(pd.ahvContribution));
|
||||
const ageStart = owner.age + yearsBefore; // Alter im ersten Jahr der Phase
|
||||
const ageEnd = ageStart + duration - 1; // Alter im letzten Jahr der Phase
|
||||
ahvItems.push({ ownerId: owner.id, ownerStartAge: ageStart, rente, beitrag, working: ownerWorking, ec });
|
||||
|
||||
const reachesRef = ageEnd >= AHV_REFERENCE_AGE;
|
||||
const startsRetired = ageStart >= AHV_REFERENCE_AGE;
|
||||
ec.startValue = startsRetired ? rente : 0;
|
||||
ec.endValue = reachesRef ? rente : 0;
|
||||
|
||||
if (startsRetired) {
|
||||
ec.summary = `Rente ${fmt(rente)}`;
|
||||
} else if (reachesRef && !ownerWorking) {
|
||||
ec.summary = `Beitrag ${fmt(beitrag)} → Rente ${fmt(rente)}`;
|
||||
ec.note = `Rente ab Alter ${AHV_REFERENCE_AGE}; bis dahin Beitrag als Nichterwerbstätige(r).`;
|
||||
} else if (reachesRef) {
|
||||
ec.summary = `Rente ab ${AHV_REFERENCE_AGE} ${fmt(rente)}`;
|
||||
} else if (!ownerWorking) {
|
||||
ec.summary = `Beitrag ${fmt(beitrag)}`;
|
||||
ec.note = `Frühpensioniert: beitragspflichtig bis Alter ${AHV_REFERENCE_AGE}.`;
|
||||
} else {
|
||||
const gap = Math.max(0, Math.round(num(pd.gapYears)));
|
||||
ec.summary = gap > 0 ? `${gap} Ausfalljahre` : "Keine Ausfalljahre";
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
@@ -606,12 +668,12 @@ export function computePlan(plan: PlanInput, sample?: PlanSample, options?: Comp
|
||||
const base = carry.hasCarry ? carry.value : Math.round(num(pd.currentValue));
|
||||
const topUp = carry.hasCarry ? Math.round(num(pd.additionalInvestment)) : 0;
|
||||
const start = base + topUp;
|
||||
const rate = Math.round(num(pd.annualContribution)); // PK-Beitrag zählt NICHT zur Quote
|
||||
const rate = Math.round(inherited("annualContribution")); // PK-Beitrag zählt NICHT zur Quote
|
||||
if (!isFirstPhase) investmentsFromCash += topUp;
|
||||
ec.baseValue = base;
|
||||
ec.startValue = start;
|
||||
wealthStart += start;
|
||||
assets.push({ value: start, rate, r: num(pd.expectedReturn), withdrawal: 0, isPk: true, ec });
|
||||
assets.push({ value: start, rate, r: inherited("expectedReturn"), withdrawal: 0, isPk: true, ec });
|
||||
}
|
||||
break;
|
||||
}
|
||||
@@ -623,13 +685,13 @@ export function computePlan(plan: PlanInput, sample?: PlanSample, options?: Comp
|
||||
const base = carry.hasCarry ? carry.value : Math.round(num(pd.currentValue));
|
||||
const topUp = carry.hasCarry ? Math.round(num(pd.additionalInvestment)) : 0;
|
||||
const start = base + topUp;
|
||||
const rate = Math.round(num(pd.annualContribution));
|
||||
const rate = Math.round(inherited("annualContribution"));
|
||||
fixedRatesTotal += rate;
|
||||
if (!isFirstPhase) investmentsFromCash += topUp;
|
||||
ec.baseValue = base;
|
||||
ec.startValue = start;
|
||||
wealthStart += start;
|
||||
assets.push({ value: start, rate, r: num(pd.expectedReturn), withdrawal: 0, isPk: false, ec });
|
||||
assets.push({ value: start, rate, r: inherited("expectedReturn"), withdrawal: 0, isPk: false, ec });
|
||||
}
|
||||
break;
|
||||
}
|
||||
@@ -637,15 +699,15 @@ export function computePlan(plan: PlanInput, sample?: PlanSample, options?: Comp
|
||||
const base = carry.hasCarry ? carry.value : Math.round(num(pd.startValue));
|
||||
const topUp = carry.hasCarry ? Math.round(num(pd.additionalInvestment)) : 0;
|
||||
const start = base + topUp;
|
||||
const rate = Math.round(num(pd.annualContribution));
|
||||
const withdrawal = Math.round(num(pd.annualWithdrawal));
|
||||
const rate = Math.round(inherited("annualContribution"));
|
||||
const withdrawal = Math.round(inherited("annualWithdrawal"));
|
||||
fixedRatesTotal += rate;
|
||||
plannedWithdrawTotal += withdrawal;
|
||||
if (!isFirstPhase) investmentsFromCash += topUp;
|
||||
ec.baseValue = base;
|
||||
ec.startValue = start;
|
||||
wealthStart += start;
|
||||
assets.push({ value: start, rate, r: num(pd.expectedReturn), withdrawal, isPk: false, ec });
|
||||
assets.push({ value: start, rate, r: inherited("expectedReturn"), withdrawal, isPk: false, ec });
|
||||
break;
|
||||
}
|
||||
case "REAL_ESTATE": {
|
||||
@@ -654,7 +716,7 @@ export function computePlan(plan: PlanInput, sample?: PlanSample, options?: Comp
|
||||
const purchase = carry.hasCarry ? carry.propertyPurchase : Math.round(num(pd.purchasePrice));
|
||||
const valueStart = carry.hasCarry ? carry.propertyValue : Math.round(num(pd.purchasePrice));
|
||||
const mortgageStart = carry.hasCarry ? carry.mortgage : Math.round(num(pd.mortgage));
|
||||
const amort = Math.round(num(pd.amortization));
|
||||
const amort = Math.round(inherited("amortization"));
|
||||
const equity = valueStart - mortgageStart;
|
||||
if (!carry.hasCarry && !isFirstPhase) investmentsFromCash += Math.max(0, equity);
|
||||
ec.baseValue = equity;
|
||||
@@ -666,8 +728,8 @@ export function computePlan(plan: PlanInput, sample?: PlanSample, options?: Comp
|
||||
purchase,
|
||||
mortgage: mortgageStart,
|
||||
amort,
|
||||
growth: num(pd.valueGrowth),
|
||||
interestRate: num(pd.interestRate),
|
||||
growth: inherited("valueGrowth"),
|
||||
interestRate: inherited("interestRate"),
|
||||
// Default INCLUDED: bestehende Pläne haben die Zinsen in den Ausgaben -> nicht
|
||||
// nochmals abziehen. Nur bei bewusstem "ADD" rechnet das Tool sie dazu.
|
||||
addInterest: pd.interestHandling === "ADD",
|
||||
@@ -677,7 +739,7 @@ export function computePlan(plan: PlanInput, sample?: PlanSample, options?: Comp
|
||||
}
|
||||
case "OTHER_DEBT": {
|
||||
const owedStart = carry.hasCarry ? carry.owed : Math.round(num(pd.startValue));
|
||||
const repay = Math.round(num(pd.annualRepayment));
|
||||
const repay = Math.round(inherited("annualRepayment"));
|
||||
ec.baseValue = -owedStart;
|
||||
ec.startValue = -owedStart;
|
||||
wealthStart += -owedStart;
|
||||
@@ -742,8 +804,20 @@ export function computePlan(plan: PlanInput, sample?: PlanSample, options?: Comp
|
||||
interestNominal += re.mortgage * (re.interestRate / 100);
|
||||
}
|
||||
|
||||
const expenseNominal = expenseRealBase * inflFactor + interestNominal;
|
||||
const expenseReal = expenseRealBase + interestNominal / (inflFactor || 1);
|
||||
// AHV jahresweise: Die Rente fliesst ab dem Referenzalter -- auch wenn die Person noch
|
||||
// arbeitet. Vorher zahlt eine bereits pensionierte Person Beiträge, die wie eine
|
||||
// Ausgabe auf die Quote schlagen (Kap. 4.4.6).
|
||||
let ahvIncome = 0;
|
||||
let ahvCost = 0;
|
||||
for (const a of ahvItems) {
|
||||
const ageThisYear = a.ownerStartAge + t - 1; // Alter zu Jahresbeginn
|
||||
if (ageThisYear >= AHV_REFERENCE_AGE) ahvIncome += a.rente;
|
||||
else if (!a.working) ahvCost += a.beitrag;
|
||||
}
|
||||
incomeFlow += ahvIncome;
|
||||
|
||||
const expenseNominal = expenseRealBase * inflFactor + interestNominal + ahvCost;
|
||||
const expenseReal = expenseRealBase + (interestNominal + ahvCost) / (inflFactor || 1);
|
||||
const quote = incomeFlow - expenseNominal;
|
||||
|
||||
// Der Vermögenswert wird erst nach Verzinsung/Cash-Fortschreibung bekannt und weiter
|
||||
@@ -899,9 +973,16 @@ export function computePlan(plan: PlanInput, sample?: PlanSample, options?: Comp
|
||||
});
|
||||
}
|
||||
for (const d of debts) d.ec.yearly.push({ year: yr, age, value: -Math.round(d.owed) });
|
||||
// Renten (AHV, verrentete PK) laufen nominal fix durch die Phase.
|
||||
// AHV jahresweise: vor dem Referenzalter der Beitrag als Nichterwerbstätige(r) (negativ,
|
||||
// also als Belastung sichtbar), ab dem Referenzalter die Rente.
|
||||
for (const a of ahvItems) {
|
||||
const ageThisYear = a.ownerStartAge + t - 1; // Alter zu Jahresbeginn
|
||||
const value = ageThisYear >= AHV_REFERENCE_AGE ? a.rente : a.working ? 0 : -a.beitrag;
|
||||
if (a.ec.yearly.length < t) a.ec.yearly.push({ year: yr, age, value });
|
||||
}
|
||||
// Verrentete PK läuft nominal fix durch die Phase.
|
||||
for (const ec of ecById.values()) {
|
||||
if ((ec.category === "AHV" || ec.category === "PENSION_FUND") && ec.startValue > 0 && ec.yearly.length < t) {
|
||||
if (ec.category === "PENSION_FUND" && ec.startValue > 0 && ec.yearly.length < t) {
|
||||
ec.yearly.push({ year: yr, age, value: ec.startValue });
|
||||
}
|
||||
}
|
||||
@@ -932,6 +1013,11 @@ export function computePlan(plan: PlanInput, sample?: PlanSample, options?: Comp
|
||||
exp.ec.endValue = Math.round(exp.basis * Math.pow(1 + exp.idx / 100, duration - 1) * flowDeflatorEnd);
|
||||
exp.ec.summary = fmt(exp.ec.startValue);
|
||||
}
|
||||
// Basiswert der Folgephase fortschreiben (nominal für Einkommen, real für Ausgaben). Erst
|
||||
// hier, weil `basis` in der Jahresschleife durch effektive Werte neu gesetzt worden sein kann.
|
||||
for (const f of [...incomes, ...expenses]) {
|
||||
f.carry.flowBasis = f.basis * Math.pow(1 + f.idx / 100, duration);
|
||||
}
|
||||
for (const a of assets) {
|
||||
a.ec.endValue = Math.round(a.value);
|
||||
a.ec.summary = fmt(a.ec.endValue);
|
||||
@@ -1571,6 +1657,51 @@ export function computePlan(plan: PlanInput, sample?: PlanSample, options?: Comp
|
||||
carry.hasCarry = true;
|
||||
}
|
||||
|
||||
// --- Punkt C (Roadmap Nr. 44): Verwendung des Kapitalzuflusses -----------------------
|
||||
//
|
||||
// Bei der Pensionierung fliesst oft ein grosser Betrag auf einmal (PK-Kapital, 3a,
|
||||
// Immobilienverkauf). Ihn vollständig als Cash liegen zu lassen ist selten die Absicht.
|
||||
// Die Verwendung wird deshalb als QUOTE erfasst: Verschiebt man das Pensionsalter, ändert
|
||||
// sich der Betrag -- die Aufteilung skaliert mit, statt still falsch zu werden.
|
||||
//
|
||||
// Mechanisch nichts Neues: Die Amortisations-Quote wirkt wie eine Sonderamortisation, die
|
||||
// Anlage-Quote wie eine Zusatzinvestition. Beide sind schon heute Cash-Abflüsse an der
|
||||
// Grenze und laufen damit korrekt durch beide Brücken.
|
||||
if (txInflow > 0) {
|
||||
const ct = phase.cashTransition ?? {};
|
||||
const amortPct = Math.max(0, Math.min(100, num(ct.capitalUseAmortizationPct)));
|
||||
const investPct = Math.max(0, Math.min(100 - amortPct, num(ct.capitalUseInvestPct)));
|
||||
if (amortPct > 0 || investPct > 0) {
|
||||
let amortBudget = Math.round((txInflow * amortPct) / 100);
|
||||
for (const e of orderedElements) {
|
||||
if (amortBudget <= 0) break;
|
||||
if (e.category !== "REAL_ESTATE") continue;
|
||||
const c = carries.get(e.id)!;
|
||||
if (c.status !== "ACTIVE" || c.mortgage <= 0) continue;
|
||||
const pay = Math.min(amortBudget, c.mortgage);
|
||||
c.mortgage -= pay;
|
||||
amortBudget -= pay;
|
||||
txImmediateRepay += pay;
|
||||
}
|
||||
|
||||
const investBudget = Math.round((txInflow * investPct) / 100);
|
||||
if (investBudget > 0) {
|
||||
const target =
|
||||
orderedElements.find(
|
||||
(e) =>
|
||||
e.id === ct.capitalUseTargetElementId &&
|
||||
e.category === "OTHER_ASSET" &&
|
||||
carries.get(e.id)!.status === "ACTIVE"
|
||||
) ??
|
||||
orderedElements.find((e) => e.category === "OTHER_ASSET" && carries.get(e.id)!.status === "ACTIVE");
|
||||
if (target) {
|
||||
carries.get(target.id)!.value += investBudget;
|
||||
txImmediateRepay += investBudget;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
cashCarryIn = cashEnd + txInflow + txOneOffInflow - txImmediateRepay - txOneOffOutflow;
|
||||
incomingInflow = txInflow;
|
||||
incomingImmediateRepay = txImmediateRepay;
|
||||
|
||||
Reference in New Issue
Block a user