Teilverkauf (Sonstiges Vermoegen) + Sonderamortisation (Immobilie)
Deploy App / deploy (push) Successful in 1m1s

Roadmap Nr. 42: OTHER_ASSET am Uebergang neu mit Teilverkauf. Neben Halten und
Vollverkauf kann ein Betrag ins Cash fliessen, der Rest bleibt investiert und
waechst weiter. Der Teilverkauf erscheint als "Kapitalzufluss" im Phasenkopf
("zu investierendes Kapital"). Abgegrenzt zur laufenden Bezugsrate: der Teilverkauf
ist die einmalige Entnahme AM Uebergang, die Bezugsrate der laufende Verzehr IN der
Phase -- beide koexistieren.

Roadmap Nr. 15: REAL_ESTATE im Halten-Fall neu mit Sonderamortisation -- Einmaltilgung
der Hypothek aus dem Cash, am Restsaldo gekappt, bringt die Immobilie auf Augenhoehe
mit OTHER_DEBT (immediateRepayment). Erscheint als "Kapitalinvestition" im Phasenkopf.
Damit laesst sich die indirekte Amortisation via 3a MECHANISCH nachbilden (3a wachsen
lassen -> bei Pensionierung ins Cash -> Sonderamortisation).

Die Steuerwirkung der indirekten Amortisation (3a-Abzug, Zinsabzug) bleibt bewusst
zurueckgestellt mit dem Steuer-Buendel 13/14/23 (SPEZ 9.14).

Fuenf Regressionstests (36 -> 41). Spezifikation auf v0.6. Keine Verhaltensaenderung
fuer bestehende Plaene.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-07-17 21:02:44 +02:00
parent a97de5b1ed
commit 5bf35bd6da
6 changed files with 182 additions and 28 deletions
+12 -1
View File
@@ -36,6 +36,7 @@ export interface ElementPhaseComputed {
carried: boolean; // Phase >= 2: Start-/Basiswert wird aus der Vorphase fortgeschrieben
baseValue: number; // fortgeschriebener Basiswert (read-only Anzeige ab Phase 2; ohne Zusatzeinlage)
mortgageStart: number; // nur REAL_ESTATE: Resthypothek zu Phasenbeginn (0 sonst)
mortgageEnd: number; // nur REAL_ESTATE: Resthypothek zu Phasenende (0 sonst)
startValue: number; // Wert/Flow zu Phasenbeginn (Aktiven +, Schulden -, Einkommen/Ausgaben = Flow Jahr 1)
endValue: number; // Wert/Flow am Phasenende (letztes Jahr)
summary: string;
@@ -361,6 +362,7 @@ export function computePlan(plan: PlanInput): PlanComputed {
carried: carry.hasCarry,
baseValue: 0,
mortgageStart: 0,
mortgageEnd: 0,
startValue: 0,
endValue: 0,
summary: "",
@@ -647,6 +649,7 @@ export function computePlan(plan: PlanInput): PlanComputed {
}
for (const re of realEstates) {
re.ec.endValue = Math.round(re.value - re.mortgage);
re.ec.mortgageEnd = Math.round(re.mortgage);
re.ec.summary = fmt(re.ec.endValue);
wealthEnd += re.ec.endValue;
}
@@ -803,6 +806,11 @@ export function computePlan(plan: PlanInput): PlanComputed {
if (td.decision === "SELL") {
txInflow += ec.endValue;
carry.status = "SOLD";
} else if (td.decision === "PARTIAL") {
// Teilverkauf: Betrag fliesst ins Cash, der Rest bleibt aktiv und waechst weiter.
const part = Math.min(ec.endValue, Math.round(num(td.partialSaleAmount)));
txInflow += part;
carry.value = ec.endValue - part;
} else {
carry.value = ec.endValue;
}
@@ -820,7 +828,10 @@ export function computePlan(plan: PlanInput): PlanComputed {
txInflow += Math.round(salePrice - re.mortgage - tax);
carry.status = "SOLD";
} else {
carry.mortgage = re.mortgage;
// Sonderamortisation: Einmaltilgung der Hypothek aus dem Cash, am Restsaldo gekappt.
const extra = Math.min(re.mortgage, Math.round(num(td.extraAmortization)));
if (extra > 0) txImmediateRepay += extra;
carry.mortgage = re.mortgage - extra;
carry.propertyValue = re.value;
carry.propertyPurchase = re.purchase;
}