money: count real Joint recurring items before the move instead of a flat €900/mo
Some checks failed
Deploy production / deploy (push) Has been cancelled
Some checks failed
Deploy production / deploy (push) Has been cancelled
This commit is contained in:
@@ -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%
|
||||
|
||||
@@ -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] = (
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user