Netto/Brutto-Klarstellung fuer die AHV + erweitertes Immobilien-Modul
Deploy App / deploy (push) Successful in 59s
Deploy App / deploy (push) Successful in 59s
Roadmap Nr. 9 (reduziert): Einkommen ist neu explizit als NETTOLOHN definiert (Label + Hilfetext). Bisher stand nirgends, ob netto oder brutto gemeint ist -- fuer den Cash-Fluss egal (beide Konventionen heben sich auf), aber seit der einkommensabhaengigen AHV haengt eine Rente daran. Die AHV bemisst sich am Bruttolohn, deshalb rechnet das Tool intern mit AHV_GROSS_FROM_NET_FACTOR = 1.12 hoch. Ohne das war die Rente um bis zu ~1'900/Jahr zu tief (Details: SPEZ 9.13). Der Faktor ist hergeleitet und dokumentiert (AHV/IV/EO 5.3% + ALV 1.1% + NBU ~1% + PK ~2-5% auf den koordinierten Lohn) -- fix vertretbar, weil das mdJE selbst ein Karriere-Durchschnitt ist. Keine Aufschluesselung, kein sichtbares Feld (kommt mit Roadmap Nr. 41 als erklaerte Konstante). Roadmap Nr. 8: Immobilie neu mit Hypothekarzins (% der Restschuld, Zinsbetrag sinkt mit der Amortisation, read-only "Beginn -> Ende") und Wertsteigerung. WICHTIG: Die Wertsteigerung wirkt auf die LIEGENSCHAFT, nicht auf das Eigenkapital. 1% von 1 Mio sind 10'000/Jahr, also 10% eines Eigenkapitals von 100'000 -- das ist der Hebel. Auf dem EK gerechnet waeren es 1'000 (Beispiel: 304'622 statt 210'462). Kaufpreis und Verkehrswert laufen deshalb getrennt; die Grundstueckgewinnsteuer bemisst sich weiterhin am urspruenglichen Kaufpreis. Doppelzaehlung: Schalter interestHandling auf der Immobilie, Default INCLUDED -- bestehende Plaene haben die Zinsen in den Ausgaben und aendern sich nicht. Keine Steuerschaetzung (Begruendung: SPEZ 9.14). Sechs Regressionstests (30 -> 36). Spezifikation auf v0.5. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
+175
-37
@@ -4,10 +4,10 @@
|
||||
| | |
|
||||
|---|---|
|
||||
| **Dokument** | Funktionale und Technische Spezifikation FPT |
|
||||
| **Version** | 0.4 |
|
||||
| **Version** | 0.5 |
|
||||
| **Datum** | 2026-07-17 |
|
||||
| **Status** | Lebendes Dokument |
|
||||
| **Codestand** | Arbeitsstand nach `9f5bd75` inkl. einkommensabhängiger AHV und Fortschreibungs-Warnhinweis (Branch `main`) |
|
||||
| **Codestand** | Arbeitsstand nach `e901d23` inkl. Netto/Brutto-Klarstellung und erweitertem Immobilien-Modul (Branch `main`) |
|
||||
| **Ersetzt** | `FDD_TDD_FPT.docx` (v1–v5) im Ordner `Info Dateien` – diese sind ab Version 0.1 dieses Dokuments obsolet |
|
||||
| **Geltungsbereich** | Gesamter Code im Verzeichnis `FPT` |
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
|
||||
| Version | Datum | Autor | Änderung |
|
||||
|---|---|---|---|
|
||||
| 0.5 | 2026-07-17 | Claude (Opus 4.8) | **Netto/Brutto geklärt** (Roadmap Nr. 9, reduziert) und **Immobilien-Modul erweitert** (Roadmap Nr. 8). Einkommen ist neu explizit als **Nettolohn** definiert (Label und Hilfetext); für die AHV rechnet das Tool intern mit `AHV_GROSS_FROM_NET_FACTOR = 1.12` auf den Bruttolohn hoch – die AHV bemisst sich am Brutto, die bisherige Netto-Basis unterschätzte die Rente um bis zu ~1'900/Jahr. Immobilie neu mit **Hypothekarzins** (% der Restschuld, sinkt mit der Amortisation, mit Doppelzählungs-Schalter) und **Wertsteigerung** (auf die **Liegenschaft**, nicht auf das Eigenkapital – Hebeleffekt). Grundstückgewinnsteuer bemisst sich neu explizit am ursprünglichen Kaufpreis. Keine Aufschlüsselung bei Einkommen oder Ausgaben, keine Steuerschätzung (Begründung: 9.14). Neues Kapitel 4.4.5; 4.6.5 und 4.9.4 überarbeitet; Abschnitt 9 um zwei Punkte ergänzt. Sechs Regressionstests (30 → 36). **Verhaltensänderung:** siehe 9.13. |
|
||||
| 0.4 | 2026-07-17 | Claude (Opus 4.8) | **AHV-Rente einkommensabhängig** (Roadmap Nr. 3) und **Fortschreibungs-Warnhinweis** (Roadmap Nr. 4). Die AHV-Rente folgt neu der amtlichen Rentenformel (Skala 44) über das massgebende durchschnittliche Jahreseinkommen statt pauschal der Maximalrente; verifiziert gegen die amtliche Tabelle 318.117.1 (51/51 Zeilen). Prüfung der Beitragskarriere am Pensions-Übergang, mit Zusatzfeldern für die Jahre vor Planbeginn (ab Alter 21). Alles **real** gerechnet. Neue Konstanten `AHV_MIN_MONTHLY_FULL`, `AHV_PENSION_MONTHS`, `AHV_CONTRIBUTION_START_AGE`; `AHV_MAX_ANNUAL_SINGLE` neu abgeleitet. Warnhinweis in Phasenzellen und Phasen-Detail, wenn Folgephasen existieren. Neue Kapitel 3.5.6, 4.4; Abschnitt 9 um zwei Punkte ergänzt. Zwölf Regressionstests (18 → 30). **Verhaltensänderung:** siehe 9.10. |
|
||||
| 0.3 | 2026-07-16 | Claude (Opus 4.8) | **Einmalige Sonderein-/ausgaben** umgesetzt (Roadmap Nr. 1). Der Cash-Übergang zwischen zwei Phasen ist neu ein eigener Entscheid: 1:1 übernehmen · einmaliger Zufluss · einmalige Kosten · beides. Beträge gehen direkt aufs Cash-Konto. Zufluss wird nominal erfasst (real angezeigt) mit optionalem Steuersatz (Default 0 %), Kosten werden real erfasst (nominal angezeigt); beide bleiben aus der Sparquote heraus. Der Entscheid startet unbeantwortet und zählt im „offen"-Badge mit. Neue Spalte `Phase.cashTransition` (JSON) + Migration, neue Route `PUT /api/phases/<id>/cash-transition`, eigene Kennzahlen im Phasenkopf. Fünf Regressionstests ergänzt (13 → 18). Neue Kapitel 3.5.5, 4.9.6; Abschnitt 9 um zwei Grenzen ergänzt. |
|
||||
| 0.2 | 2026-07-16 | Claude (Opus 4.8) | Drei Fixes umgesetzt und dokumentiert: **(1)** `Phase.inflationRate` ersatzlos entfernt (DB-Migration, API, Typen, UI) – die Inflation liegt seit V5 plan-weit; das Feld war wirkungslos. **(2)** Amortisation und Tilgung stoppen neu, sobald Hypothek bzw. Schuld abbezahlt sind – sie belasten danach weder Cash noch Sparquote (Kap. 4.6.5, 4.6.7, 4.7, 4.8). **(3)** Kapitalbezugssteuer greift neu auch bei PK-/3a-Vorbezügen vor der Pensionierung, inkl. Steuerfeld und Netto-Vorschau im UI (Kap. 3.5.2, 4.9.1, 4.9.2). Kennzahl `plannedSaveRate` ist neu die Rate des **ersten** Phasenjahres. Drei Regressionstests ergänzt (10 → 13). Abschnitt 9 neu nummeriert (erledigte Punkte entfernt, „Sparraten werden nicht indexiert" ergänzt). |
|
||||
@@ -385,14 +386,22 @@ Referenz: `src/components/ElementDetail.tsx` Zeilen 124–340.
|
||||
|
||||
#### INCOME (Einkommen)
|
||||
|
||||
Hinweistext: „Einkommen wird NOMINAL erfasst (die Zahl auf dem Lohnausweis)."
|
||||
**Einkommen ist der NETTOLOHN** – der Betrag, der nach allen Lohnabzügen (AHV/ALV,
|
||||
Pensionskasse, NBU) tatsächlich aufs Konto kommt. Das ist die für den Cash-Fluss richtige
|
||||
Grösse und die, in der man denkt.
|
||||
|
||||
| Feld | JSON | Semantik |
|
||||
|---|---|---|
|
||||
| Jahreseinkommen NOMINAL (erstes Jahr) | `amount` | Basiswert Jahr 1. Ab Phase 2 vorbelegt mit dem fortgeschriebenen Wert, bewusst änderbar (Teilzeit, Beförderung). |
|
||||
| Jahreseinkommen NETTO, nominal (erstes Jahr) | `amount` | Basiswert Jahr 1. Ab Phase 2 vorbelegt mit dem fortgeschriebenen Wert, bewusst änderbar (Teilzeit, Beförderung). |
|
||||
| ≈ real (heutige Kaufkraft) | – | Read-only Info: `amount / deflatorStart` |
|
||||
| Nominale Lohnerhöhung (%/Jahr) | `teuerungsausgleich` | Default 0 %. 0 % = nominal gleichbleibend, real sinkend. |
|
||||
|
||||
Für die **AHV** rechnet das Tool intern auf den Bruttolohn zurück – siehe
|
||||
[4.4.5](#445-netto-brutto-umrechnung-für-die-ahv). Warum die Definition überhaupt nötig war:
|
||||
Für den Cash-Fluss sind beide Konventionen gleichwertig (`brutto − Ausgaben inkl. Abzüge`
|
||||
≡ `netto − Ausgaben ohne Abzüge`), weshalb die Ambiguität lange folgenlos blieb. Mit der
|
||||
einkommensabhängigen AHV hängt daran aber eine Rente.
|
||||
|
||||
#### EXPENSE (Ausgaben)
|
||||
|
||||
Hinweistext: „Ausgaben werden REAL erfasst (in heutiger Kaufkraft)."
|
||||
@@ -433,10 +442,28 @@ Wie PK, aber:
|
||||
|
||||
#### REAL_ESTATE (Immobilie)
|
||||
|
||||
| Zustand | Felder |
|
||||
|---|---|
|
||||
| Phase 1 / Neukauf | **Kaufpreis** (`purchasePrice`), **Hypothek** (`mortgage`), **Amortisation CHF/Jahr** (`amortization`) |
|
||||
| ab Phase 2, fortgeschrieben | **Startwert Netto (fortgeschrieben)** (read-only = Kaufpreis − Resthypothek), **Amortisation** |
|
||||
| Feld | JSON | Semantik |
|
||||
|---|---|---|
|
||||
| Kaufpreis | `purchasePrice` | nur Phase 1 / Neukauf; ab Phase 2 read-only fortgeschrieben |
|
||||
| Hypothek | `mortgage` | dito |
|
||||
| Startwert Netto (fortgeschrieben) | – | read-only ab Phase 2: **Verkehrswert** − Resthypothek |
|
||||
| Resthypothek (fortgeschrieben) | – | read-only ab Phase 2 |
|
||||
| Amortisation (CHF/Jahr) | `amortization` | endet, sobald die Hypothek abbezahlt ist |
|
||||
| **Hypothekarzins (%/Jahr)** | `interestRate` | Zinssatz auf der **Restschuld** |
|
||||
| **Hypothekarzins-Betrag (Beginn → Ende)** | – | read-only: Zinsbetrag im ersten und im letzten Jahr der Phase |
|
||||
| **Geschätzte Wertsteigerung (%/Jahr)** | `valueGrowth` | wirkt auf die **Liegenschaft** |
|
||||
| **Sind die Zinsen bereits in den Ausgaben enthalten?** | `interestHandling` | `INCLUDED` (Default) / `ADD` |
|
||||
|
||||
Der Zinsbetrag sinkt automatisch mit der Amortisation – das kann kein manueller
|
||||
Ausgabenposten. Beispiel: Hypothek 1'000'000, Amortisation 10'000/Jahr, Zins 1 %, 10 Jahre →
|
||||
Anzeige `10'000 → 9'100` (Jahr 1 auf 1'000'000, Jahr 10 auf 910'000).
|
||||
|
||||
Der Schalter `interestHandling` verhindert die Doppelzählung: Bestehende Pläne haben die
|
||||
Zinsen im Ausgabenbetrag, deshalb ist `INCLUDED` der Default und das Tool zieht **nichts** ab.
|
||||
Erst `ADD` rechnet die Zinsen dazu – dann gehören sie aus dem Ausgabenbetrag entfernt. Der
|
||||
Schalter sitzt bewusst auf der **Immobilie** und nicht auf dem Ausgaben-Element: Bei mehreren
|
||||
Ausgaben-Elementen wäre sonst unklar, welches die Zinsen trägt (und zwei auf „Ja" würden
|
||||
doppelt zählen).
|
||||
|
||||
#### OTHER_ASSET (Sonstiges Vermögen)
|
||||
|
||||
@@ -866,7 +893,7 @@ Pro Person wird über die Phasen hinweg akkumuliert (`AhvCareer`):
|
||||
|
||||
| Feld | Bedeutung |
|
||||
|---|---|
|
||||
| `plannedAvgIncome` | reales Durchschnittseinkommen der Beitragsjahre **im Plan** |
|
||||
| `plannedAvgGrossIncome` | reales **Brutto**-Durchschnittseinkommen der Beitragsjahre **im Plan** (siehe 4.4.5) |
|
||||
| `planYears` | Beitragsjahre im Plan = Σ (Phasendauer − Ausfalljahre der Phase) |
|
||||
| `yearsBeforePlan` | `max(0, Alter bei Planbeginn − 21)` |
|
||||
| `gapYearsInPlan` | Summe der Ausfalljahre im Plan |
|
||||
@@ -919,7 +946,45 @@ Einkommen wird einer Person nur zugerechnet, wenn das `INCOME`-Element ihr zugeo
|
||||
einem **Einzelplan** zählt „Gemeinsam" (`HOUSEHOLD`) zur Person A; bei einem **Paar-Plan** nicht
|
||||
(siehe [9.9](#99-gemeinsames-einkommen-zählt-bei-paaren-nicht-für-die-ahv)).
|
||||
|
||||
### 4.4.5 Jahresrente, Skala und Plafonierung
|
||||
### 4.4.5 Netto-Brutto-Umrechnung für die AHV
|
||||
|
||||
Das Tool erfasst das Einkommen **netto**, die AHV bemisst sich am **Bruttolohn**. Beim Aufbau
|
||||
der Karriere wird deshalb hochgerechnet:
|
||||
|
||||
```
|
||||
plannedAvgGrossIncome = avgRealFlow(...) × AHV_GROSS_FROM_NET_FACTOR // = 1.12
|
||||
```
|
||||
|
||||
Sämtliche Werte in `AhvCareer`, im Prüf-Dialog und im mdJE sind damit **brutto** – auch das
|
||||
Feld „Durchschnittliches Bruttoeinkommen vor Planbeginn", das der Benutzer aus der
|
||||
Rentenvorausberechnung übernimmt (die ohnehin brutto-basiert ist). Eine Einheit im ganzen
|
||||
Ablauf, keine Umrechnung an der Feldgrenze.
|
||||
|
||||
**Herleitung des Faktors** (Arbeitnehmer-Abzüge in % des Bruttolohns):
|
||||
|
||||
| Abzug | Satz | Bemerkung |
|
||||
|---|---|---|
|
||||
| AHV/IV/EO | 5.30 % | 10.6 % total, hälftig geteilt |
|
||||
| ALV | 1.10 % | 2.2 % total, hälftig geteilt, bis 148'200 |
|
||||
| NBU | ~1.00 % | variiert 0.4–1.6 %, teils vom Arbeitgeber getragen |
|
||||
| PK | ~2–5 % | BVG-Altersgutschrift 7/10/15/18 % auf den **koordinierten** Lohn (Brutto − 26'460, max. 90'720), Arbeitnehmer ≥ die Hälfte |
|
||||
|
||||
Total ~9–13 % je nach Alter und Lohn → Faktor `1/(1−q)` = **1.10 bis 1.16**, Mittel **1.12**.
|
||||
|
||||
**Warum ein fixer Faktor genügt:** Das mdJE ist selbst ein Durchschnitt über die ganze
|
||||
Beitragskarriere (44 Jahre). Der altersabhängige PK-Satz mittelt sich dabei heraus – ein
|
||||
Karriere-Faktor gegen einen Karriere-Durchschnitt ist konzeptionell stimmig, nicht bloss eine
|
||||
Näherung. Die Restunschärfe (~3 %) ist deutlich kleiner als der Fehler, den die Umrechnung
|
||||
behebt (~11 %, siehe 9.13).
|
||||
|
||||
**Grenzen:** Überobligatorische PK-Pläne, vom Arbeitgeber getragene NBU, Selbstständige und
|
||||
Löhne über 148'200 (ALV sinkt auf 0.5 %) weichen ab. Der Faktor ist heute eine nicht
|
||||
sichtbare Konstante; mit Roadmap Nr. 41 wird er in der Formel-Erklärung ausgewiesen.
|
||||
|
||||
Quellen: BSV „Beträge gültig ab 1.1.2026" (Koordinationsabzug 26'460, obere Limite 90'720,
|
||||
„keine Änderung gegenüber 2025"); AHV-Merkblätter 2.01 (AHV/IV/EO) und 2.08 (ALV).
|
||||
|
||||
### 4.4.6 Jahresrente, Skala und Plafonierung
|
||||
|
||||
```
|
||||
factor = max(0, (44 − Ausfalljahre total) / 44) // Rentenskala 44
|
||||
@@ -1030,14 +1095,29 @@ Identisch zu PK, mit zwei Unterschieden:
|
||||
### 4.6.5 REAL_ESTATE (Immobilie)
|
||||
|
||||
```
|
||||
purchase = round(phaseData.purchasePrice) // in JEDER Phase aus den Phasendaten
|
||||
mortgageStart = hasCarry ? carry.mortgage : round(phaseData.mortgage)
|
||||
amort = round(phaseData.amortization) // wird JÄHRLICH am Restsaldo gekappt
|
||||
equity = purchase − mortgageStart → startValue, wealthStart
|
||||
purchase = hasCarry ? carry.propertyPurchase : round(phaseData.purchasePrice) // Kaufpreis
|
||||
valueStart = hasCarry ? carry.propertyValue : round(phaseData.purchasePrice) // Verkehrswert
|
||||
mortgage = hasCarry ? carry.mortgage : round(phaseData.mortgage)
|
||||
amort = round(phaseData.amortization) // wird JÄHRLICH am Restsaldo gekappt
|
||||
equity = valueStart − mortgage → startValue, wealthStart
|
||||
```
|
||||
|
||||
Die Hypothek wird als **laufender Saldo** in der Jahresschleife geführt (siehe 4.7), nicht per
|
||||
Linearformel. Sobald sie 0 erreicht, entfällt die Amortisationsrate.
|
||||
**Kaufpreis und Verkehrswert laufen getrennt.** Der Verkehrswert wächst mit `valueGrowth`, der
|
||||
Kaufpreis bleibt der ursprüngliche – er ist die Basis der Grundstückgewinnsteuer beim Verkauf
|
||||
(4.9.4). Beide werden über die Phasen fortgeschrieben (`carry.propertyValue`,
|
||||
`carry.propertyPurchase`); die Hypothek läuft als Saldo in der Jahresschleife (4.7).
|
||||
|
||||
**Die Wertsteigerung wirkt auf die Liegenschaft, nicht auf das Eigenkapital.** Das ist der
|
||||
Hebel, der Wohneigentum finanziell auszeichnet: 1 % von 1'000'000 sind 10'000 im Jahr – bei
|
||||
einem Eigenkapital von 100'000 also 10 % darauf. Würde man die Wertsteigerung auf das
|
||||
Eigenkapital rechnen, ergäbe dieselbe Annahme nur 1'000 im Jahr:
|
||||
|
||||
> Kaufpreis 1'000'000 · Hypothek 900'000 · EK 100'000 · 1 %/J. · Amortisation 10'000/J. · 10 J.
|
||||
> → korrekt **304'622**; auf das Eigenkapital gerechnet nur **210'462**. Differenz **94'160**,
|
||||
> und sie wächst mit dem Belehnungsgrad.
|
||||
|
||||
Der angezeigte Elementwert bleibt das **Eigenkapital** (`Verkehrswert − Restschuld`); nur die
|
||||
Basis der Verzinsung ist die Liegenschaft.
|
||||
|
||||
Falls **nicht** fortgeschrieben und **nicht** Phase 1 (= Neukauf in einer späteren Phase):
|
||||
`investmentsFromCash += max(0, equity)` – das Eigenkapital wird aus dem Cash finanziert.
|
||||
@@ -1085,10 +1165,12 @@ Dann für `t = 1 .. duration`:
|
||||
// 1. Einkommen (nominal)
|
||||
incomeFlow = renteTotal + Σ (inc.basis × (1 + inc.idx/100)^(t−1))
|
||||
|
||||
// 2. Ausgaben (real → nominal)
|
||||
inflFactor = cumInflStart × (1 + infl/100)^(t−1)
|
||||
expenseReal = Σ (exp.basis × (1 + exp.idx/100)^(t−1))
|
||||
expenseNominal = expenseReal × inflFactor
|
||||
// 2. Ausgaben (real → nominal) + Hypothekarzins
|
||||
inflFactor = cumInflStart × (1 + infl/100)^(t−1)
|
||||
expenseRealBase = Σ (exp.basis × (1 + exp.idx/100)^(t−1))
|
||||
interestNominal = Σ (re.mortgage × re.interestRate/100) // nur wo interestHandling = ADD
|
||||
expenseNominal = expenseRealBase × inflFactor + interestNominal
|
||||
expenseReal = expenseRealBase + interestNominal / inflFactor
|
||||
|
||||
// 3. Quote
|
||||
quote = incomeFlow − expenseNominal
|
||||
@@ -1108,6 +1190,7 @@ für jede Immobilie re:
|
||||
pay = min(re.amort, re.mortgage) // nie mehr als die Restschuld
|
||||
re.mortgage −= pay
|
||||
debtRates += pay
|
||||
re.value ×= (1 + re.growth/100) // Wertsteigerung auf die LIEGENSCHAFT
|
||||
für jede Schuld d:
|
||||
pay = min(d.repay, d.owed)
|
||||
d.owed −= pay
|
||||
@@ -1119,7 +1202,7 @@ cash += quote − fixedRatesTotal − debtRates + cashFromWithdraw
|
||||
falls cash < 0 → cashNegative = true
|
||||
|
||||
// 8. Ruin prüfen (Gesamtvermögen zum Jahresende)
|
||||
total = cash + Σ asset.value + Σ (re.purchase − re.mortgage) + Σ (−d.owed)
|
||||
total = cash + Σ asset.value + Σ (re.value − re.mortgage) + Σ (−d.owed)
|
||||
falls ruinAge === null und total < 0 → ruinAge = age(Person A) + yearsBefore + t
|
||||
```
|
||||
|
||||
@@ -1146,7 +1229,7 @@ Einkommen: startValue = basis
|
||||
Ausgaben: startValue = basis × cumInflStart
|
||||
endValue = basis × (1 + idx/100)^(duration−1) × flowDeflatorEnd
|
||||
Assets: endValue = a.value (nach der Jahresschleife)
|
||||
Immobilie: endValue = purchase − re.mortgage (laufender Saldo nach der Jahresschleife)
|
||||
Immobilie: endValue = re.value − re.mortgage (Verkehrswert inkl. Wertsteigerung, minus Restschuld)
|
||||
Schulden: endValue = −d.owed (0, falls getilgt; + Notiz „Wird getilgt")
|
||||
```
|
||||
|
||||
@@ -1209,19 +1292,25 @@ Sonst → `carry.value = ec.endValue`.
|
||||
|
||||
### 4.9.4 REAL_ESTATE
|
||||
|
||||
Die Resthypothek wird zurückgerechnet: `restMortgage = purchase − ec.endValue`.
|
||||
Gelesen werden die laufenden Werte aus der Jahresschleife (`re.value`, `re.mortgage`,
|
||||
`re.purchase`).
|
||||
|
||||
`decision = "SELL"`:
|
||||
```
|
||||
gain = max(0, salePrice − purchase)
|
||||
gain = max(0, salePrice − re.purchase) // URSPRÜNGLICHER Kaufpreis, nicht der Verkehrswert
|
||||
tax = gain × (saleTaxRate / 100)
|
||||
txInflow += round(salePrice − restMortgage − tax)
|
||||
txInflow += round(salePrice − re.mortgage − tax)
|
||||
carry.status = "SOLD"
|
||||
```
|
||||
Der Nettoerlös ist also Verkaufspreis minus Hypothekenablösung minus Grundstückgewinnsteuer.
|
||||
Ein Verlustverkauf erzeugt keine Steuer (`gain` bei 0 geklammert).
|
||||
Der Nettoerlös ist Verkaufspreis minus Hypothekenablösung minus Grundstückgewinnsteuer. Ein
|
||||
Verlustverkauf erzeugt keine Steuer (`gain` bei 0 geklammert).
|
||||
|
||||
Sonst → `carry.mortgage = restMortgage`.
|
||||
**Die Steuer bemisst sich am ursprünglichen Kaufpreis**, nicht am zwischenzeitlich gestiegenen
|
||||
Verkehrswert – deshalb führt das Modell beide getrennt (4.6.5). Nicht modelliert sind
|
||||
wertvermehrende Investitionen und die Haltedauer-Abstufung (Roadmap Nr. 23).
|
||||
|
||||
Sonst → `carry.mortgage`, `carry.propertyValue` und `carry.propertyPurchase` werden
|
||||
fortgeschrieben.
|
||||
|
||||
### 4.9.5 OTHER_DEBT
|
||||
|
||||
@@ -1293,6 +1382,7 @@ Zentral in `src/lib/constants.ts` geführt, weil sie sich periodisch durch Bunde
|
||||
| `AHV_PENSION_MONTHS` | 13 | Rentenzahlungen pro Jahr – 13. Altersrente ab 1.1.2026 |
|
||||
| `AHV_CONTRIBUTION_START_AGE` | 21 | Beitragspflicht ab 1. Januar nach dem 20. Geburtstag |
|
||||
| `AHV_MAX_ANNUAL_SINGLE` | 32'760 | **abgeleitet**: `2 × R0 × 13` |
|
||||
| `AHV_GROSS_FROM_NET_FACTOR` | 1.12 | Netto → Brutto für die AHV; Herleitung siehe [4.4.5](#445-netto-brutto-umrechnung-für-die-ahv) |
|
||||
| `AHV_COUPLE_CAP_FACTOR` | 1.5 | Ehepaar-Plafonierung: 150 % der Einzel-Maximalrente |
|
||||
| `AHV_FULL_CONTRIBUTION_YEARS` | 44 | Volle Beitragsdauer (Rentenskala 44) |
|
||||
| `PILLAR_3A_MAX_ANNUAL` | 7'258 | Max. 3a-Beitrag/Jahr für PK-Versicherte (2026) |
|
||||
@@ -1503,6 +1593,9 @@ Referenz: `prisma/schema.prisma` Zeilen 4–6, `src/lib/elements.ts` Zeilen 48
|
||||
| `purchasePrice` | REAL_ESTATE | ≥ 0 |
|
||||
| `mortgage` | REAL_ESTATE | ≥ 0 |
|
||||
| `amortization` | REAL_ESTATE | ≥ 0 |
|
||||
| `interestRate` | REAL_ESTATE – Hypothekarzins %/Jahr | 0–20 |
|
||||
| `interestHandling` | REAL_ESTATE – Doppelzählungs-Schalter | `INCLUDED` (Default) \| `ADD` |
|
||||
| `valueGrowth` | REAL_ESTATE – Wertsteigerung %/Jahr auf die Liegenschaft | −20 bis 20 |
|
||||
| `annualRepayment` | OTHER_DEBT | ≥ 0 |
|
||||
|
||||
### 5.4.4 JSON-Payload `TransitionData`
|
||||
@@ -1800,8 +1893,8 @@ Zielumgebung: Hetzner CX23, Traefik als Reverse Proxy, Domain `fpt.aicds.ch`, Gi
|
||||
## 8.1 Teststrategie
|
||||
|
||||
Getestet wird ausschliesslich der Berechnungskern – bewusst, da dort die Fachlogik und das
|
||||
Regressionsrisiko liegen. `src/lib/calculations.test.ts` enthält 30 Tests (AHV-Rentenformel,
|
||||
AHV einkommensabhängig, „V5 Golden Tests"),
|
||||
Regressionsrisiko liegen. `src/lib/calculations.test.ts` enthält 36 Tests (AHV-Rentenformel,
|
||||
Immobilie, AHV einkommensabhängig, „V5 Golden Tests"),
|
||||
ausgeführt mit Vitest in der Node-Umgebung (`vitest.config.ts`, Include `src/**/*.test.ts`).
|
||||
Es gibt **keine** Komponenten-, API- oder E2E-Tests.
|
||||
|
||||
@@ -1818,6 +1911,12 @@ Es gibt **keine** Komponenten-, API- oder E2E-Tests.
|
||||
| **AHV ohne Prüfung** | ohne erfassten Wert gilt der geplante Durchschnitt (nicht 0) |
|
||||
| **AHV bereits pensioniert** | Karriere aus der Phasenzelle der ersten Phase |
|
||||
| **AHV Plafonierung** | zwei Maximalrenten im Paar-Plan → gekappt auf `32'760 × 1.5` |
|
||||
| **AHV Netto → Brutto** | mdJE = `70'000 × 1.12`, nicht 70'000 |
|
||||
| **Immobilie: Hebel** | 1 Mio / 900k Hypothek / 1 % / 10 J. → Endwert 304'622; deutlich mehr als eine Wertsteigerung auf das Eigenkapital ergäbe |
|
||||
| **Immobilie: ohne Wertsteigerung** | Verhalten unverändert (1 Mio − 800k = 200'000) |
|
||||
| **Immobilie: Zins-Schalter** | `INCLUDED` → kein Cash-Abzug; `ADD` → 10 × 1 % von 900'000 = 90'000 |
|
||||
| **Immobilie: Zins sinkt** | Jahr 1: 10'000, Jahr 10: 9'100; schlägt auf die Quote durch |
|
||||
| **Immobilie: Verkauf** | Grundstückgewinnsteuer auf `Verkaufspreis − Kaufpreis`, nicht auf den Verkehrswert |
|
||||
| Test 1 – Ansparen | Einkommen +2 % nominal, Ausgaben real flach: Endvermögen 761'654 nominal / 565'928 real (±1 %), kein negatives Cash |
|
||||
| Test 2 – Verzehr/Ruin | Rente nominal fix 60k, Ausgaben real 100k, Vermögen 900k @3 %: `ruinAge === 94` |
|
||||
| Test 3 – Cash-Ausgleich | Sparrate 6'364: `cashEnd === 5472`, nie negativ |
|
||||
@@ -1878,13 +1977,18 @@ liefert `personByRole` dann `null`:
|
||||
Es gibt keine Bereinigung und keine Warnung im UI. Da die Haushaltsform normalerweise nicht
|
||||
nachträglich geändert wird, ist der Fall selten – er ist aber erreichbar.
|
||||
|
||||
## 9.3 Immobilien ohne Wertentwicklung
|
||||
## 9.3 Immobilien: was noch fehlt
|
||||
|
||||
Der Immobilienwert ist in jeder Phase der aus den Phasendaten gelesene `purchasePrice`. Der
|
||||
Kaufpreis wird beim Anlegen einer Folgephase mitkopiert (`buildCarryData`). Eine Wertsteigerung
|
||||
lässt sich nur indirekt abbilden, indem man den `purchasePrice` in einer späteren Phase
|
||||
manuell erhöht – was dann allerdings auch die Berechnung der Grundstückgewinnsteuer beim Verkauf
|
||||
beeinflusst (`gain = salePrice − purchase` liest den `purchasePrice` der Verkaufsphase).
|
||||
Seit Version 0.5 kennt das Modul Hypothekarzins und Wertsteigerung. Nicht modelliert sind:
|
||||
|
||||
- **Nebenkosten und Unterhalt** (Faustregel ~1 % des Werts). Lassen sich heute als normales
|
||||
Ausgaben-Element erfassen.
|
||||
- **Eigenmietwert** – ohne echte Steuerlogik (Roadmap Nr. 23) nur halb wirksam.
|
||||
- **Mieteinnahmen** (Renditeliegenschaften) – anderer Anwendungsfall.
|
||||
- **Wertvermehrende Investitionen** und die **Haltedauer-Abstufung** der
|
||||
Grundstückgewinnsteuer (kantonal, teils stark degressiv).
|
||||
- **Zinsänderungsrisiko**: Der Zinssatz gilt für die ganze Phase. Ein Zinsschock lässt sich
|
||||
nur abbilden, indem man an dieser Stelle eine Phasengrenze zieht und den Satz neu setzt.
|
||||
|
||||
## 9.4 Kein CSRF-Token
|
||||
|
||||
@@ -1959,7 +2063,41 @@ grösste verbliebene Hebel im AHV-Modell.
|
||||
Das Feld ist per `max`-Prop hart geklammert. Das Zod-Schema kennt für `annualContribution` nur
|
||||
`≥ 0` – ein direkter API-Aufruf kann die Obergrenze überschreiten.
|
||||
|
||||
## 9.13 Kleinere Beobachtungen
|
||||
## 9.13 Verhaltensänderung: Nettolohn-Klarstellung und AHV
|
||||
|
||||
Bis Version 0.4 war nirgends definiert, ob `INCOME` netto oder brutto ist – der Hilfetext sagte
|
||||
nur „die Zahl auf dem Lohnausweis", und dort stehen beide. Für den Cash-Fluss war das folgenlos,
|
||||
weil sich beide Konventionen aufheben (`brutto − Ausgaben inkl. Abzüge` ≡ `netto − Ausgaben ohne
|
||||
Abzüge`). Mit der einkommensabhängigen AHV (0.4) hing daran aber plötzlich eine Rente.
|
||||
|
||||
Seit 0.5 gilt: **`amount` ist der Nettolohn**, und die AHV rechnet mit `× 1.12` auf brutto hoch.
|
||||
Konsequenzen:
|
||||
|
||||
- **Wer bisher netto erfasst hat** (die dokumentierte Absicht): Die AHV-Rente **steigt** – sie
|
||||
war zuvor um bis zu ~1'900/Jahr zu tief, am stärksten bei mittleren Einkommen (bei 80'000
|
||||
brutto: 28'974 statt 30'902). Über 90'720 brutto verschwindet der Effekt, weil beide Werte in
|
||||
die Maximalrente laufen.
|
||||
- **Wer brutto erfasst hat**: Cash-Fluss und AHV sind nun beide zu hoch. Der Einkommensbetrag
|
||||
gehört auf netto korrigiert und die Lohnabzüge aus dem Ausgabenbetrag entfernt.
|
||||
|
||||
## 9.14 Keine Steuerschätzung
|
||||
|
||||
Bewusst **nicht** umgesetzt: eine automatische Schätzung von Einkommens- und Vermögenssteuer.
|
||||
|
||||
Die Bemessungsgrundlage ist das *steuerbare* Einkommen, nicht der Nettolohn – also brutto minus
|
||||
PK, 3a, Berufsauslagen, Versicherungs- und Kinderabzüge. Darauf kommen drei Ebenen (Bund,
|
||||
Kanton, Gemeinde); allein der Gemeindesteuerfuss variiert innerhalb eines Kantons um rund den
|
||||
Faktor zwei. Dazu Zivilstand, Kinder, Konfession und für die Vermögenssteuer 26 kantonale
|
||||
Tarife mit eigenen Freibeträgen. Das Tool kennt weder Wohnort noch Kinder.
|
||||
|
||||
Eine Schätzung daraus läge im Einzelfall schnell 30–50 % daneben – bei vielen Haushalten dem
|
||||
grössten Ausgabenposten. Eine selbst berechnete Zahl wirkt zudem autoritativ und wird nicht
|
||||
hinterfragt. Der Benutzer kennt seine Steuerrechnung dagegen exakt aus der letzten Veranlagung.
|
||||
|
||||
**Heutiger Weg:** ein normales Ausgaben-Element „Steuern" – dafür braucht es kein neues Feld.
|
||||
Echte Steuerlogik ist Roadmap Nr. 23, mit Kanton und Gemeinde als Eingabe.
|
||||
|
||||
## 9.15 Kleinere Beobachtungen
|
||||
|
||||
- `planToCsv(plan, computed)` erhält den `plan`-Parameter, verwendet ihn aber nicht.
|
||||
- Der Typ `Selection` in `PlanView` hat nur eine Variante (`{ type: "phase" }`) – ein Rest der
|
||||
|
||||
Reference in New Issue
Block a user