From 2e8658bf33f60cfbe41264193b9a8af5c05f45fe Mon Sep 17 00:00:00 2001 From: Stan Runge Date: Mon, 26 Oct 2026 07:34:49 +0000 Subject: [PATCH] =?UTF-8?q?money:=20count=20real=20Joint=20recurring=20ite?= =?UTF-8?q?ms=20before=20the=20move=20instead=20of=20a=20flat=20=E2=82=AC9?= =?UTF-8?q?00/mo?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/lib/money-tracker/ScenarioDetails.svelte | 4 ++-- src/lib/money-tracker/scenarios.test.ts | 19 ++++++++----------- src/lib/money-tracker/scenarios.ts | 7 +++---- 3 files changed, 13 insertions(+), 17 deletions(-) diff --git a/src/lib/money-tracker/ScenarioDetails.svelte b/src/lib/money-tracker/ScenarioDetails.svelte index 51c9d73..8251936 100644 --- a/src/lib/money-tracker/ScenarioDetails.svelte +++ b/src/lib/money-tracker/ScenarioDetails.svelte @@ -177,8 +177,8 @@ Edits recalculate the chart right away and are not saved. Fixed for every scenario: contract gross {cents(scenarioFacts.grossCents)}/mo until 30 Sep 2027, net salary and all costs from the recurring list, cash buffer {cents(scenarioFacts.bufferCents)} then everything is invested, - Joint contribution {cents(scenarioFacts.preMoveHouseholdCents)}/mo until the rent starts, - your partner’s income pays her share, DUO {cents(scenarioFacts.duoCents)}/mo from {scenarioFacts.duoFrom} + Joint items from the recurring list in full until the rent starts, then your share, while your + partner’s income pays hers, DUO {cents(scenarioFacts.duoCents)}/mo from {scenarioFacts.duoFrom} to {scenarioFacts.duoUntil}. Prices, benefits, pension, the buffer and the salary plateau grow with inflation; spending grows at the spending rate for ten years, then with inflation. Box 3 keeps taxing gains (losses carried forward). Salary bands (base per year, excl. 8% diff --git a/src/lib/money-tracker/scenarios.test.ts b/src/lib/money-tracker/scenarios.test.ts index d9aef42..5c63cac 100644 --- a/src/lib/money-tracker/scenarios.test.ts +++ b/src/lib/money-tracker/scenarios.test.ts @@ -98,23 +98,23 @@ describe('runScenario', () => { }); it('pays salary on payday, sweeps the excess the day after and counts recurring income and costs', () => { const points = runScenario(inputs, flat).points; - // Without history the contract payday (the 25th) is used; €900 pre-move household is - // spread at €30 a day in November. + // Without history the contract payday (the 25th) is used. Before the move only the Joint + // recurring items count: €50 groceries (living with parents), spread at €1.67 a day. expect(on(points, '2026-11-25').total - on(points, '2026-11-24').total).toBeCloseTo( - 300000 + 51250 - 3000, + 300000 + 51250 - 5000 / 30, 0 ); // The benefit (paid on the 1st, a Sunday) moves to Monday. expect(on(points, '2026-11-02').total - on(points, '2026-11-01').total).toBeCloseTo( - 10000 - 3000, + 10000 - 5000 / 30, 0 ); expect(on(points, '2026-11-25').cash).toBeGreaterThan(500000); expect(on(points, '2026-11-26').cash).toBe(500000); - // Rest of October: 7/31 of the €900 pre-move household. November: 300k salary + 10k benefit - // + 28k one-off + 13th month 3/12 − 3k gym − 90k pre-move household. + // Rest of October: 7/31 of the €50 groceries. November: 300k salary + 10k benefit + // + 28k one-off + 13th month 3/12 − 3k gym − 5k groceries. No flat Joint contribution. expect(on(points, '2026-11-30').total).toBeCloseTo( - 350000 - (90000 * 7) / 31 + 300000 + 10000 + 28000 + 51250 - 3000 - 90000, + 350000 - (5000 * 7) / 31 + 300000 + 10000 + 28000 + 51250 - 3000 - 5000, 0 ); }); @@ -149,10 +149,7 @@ describe('runScenario', () => { const points = runScenario(withHistory, flat).points; // 21 November 2026 is a Saturday, so the salary lands on Friday the 20th. expect(on(points, '2026-11-20').total - on(points, '2026-11-19').total).toBeGreaterThan(300000); - expect(on(points, '2026-12-21').total - on(points, '2026-12-20').total).toBeCloseTo( - 300000 - 90000 / 31, - 0 - ); + expect(on(points, '2026-12-21').total - on(points, '2026-12-20').total).toBeCloseTo(300000, 0); }); it('orders the default scenarios from pessimistic to optimistic', () => { const [pessimistic, realistic, optimistic] = ( diff --git a/src/lib/money-tracker/scenarios.ts b/src/lib/money-tracker/scenarios.ts index 6b332f2..6984965 100644 --- a/src/lib/money-tracker/scenarios.ts +++ b/src/lib/money-tracker/scenarios.ts @@ -220,8 +220,6 @@ export const scenarioFacts = { sideTaxRate: 0.4, /** Cash buffer in today's euros (grows with inflation); everything above it is invested. */ bufferCents: 500000, - /** Joint household contribution before the move, per month (Apr–Oct 2026 average). */ - preMoveHouseholdCents: 90000, /** DUO study-debt repayment, per month, for at most 35 years from 2029. */ duoFrom: 2029, duoUntil: 2063, @@ -691,8 +689,9 @@ export function runScenario(inputs: ScenarioInputs, params: ScenarioParams): Sce } } if (first < moveMonth) { - spending += facts.preMoveHouseholdCents * remaining; - spreadMonth(-facts.preMoveHouseholdCents); + // Before the move the Joint account only pays the Joint items on the recurring list + // (Netflix, groceries while living with my parents, …), in full: no partner income yet. + for (const item of inputs.household) addItem(item, spendGrowth, false); } else { const share = pct(params.householdSharePercent) * spendGrowth; const extra = params.householdExtraEuros * 100 * share;