Swedish personal income tax
The Swedish personal tax pack provides a monthly preliminary-tax cashflow and an annual tax assessment followed by a delayed settlement. swedishPreliminaryIncomeTax books a monthly preliminary-tax expense against the person's cash account. swedishIncomeTax is the year-end assessment cashflow; swedishIncomeTaxSettlement returns its trigger-listening payment cashflow. Both use the pack's 2025 calculation rules for each forecast year.
Use the two cashflows
Open this model in the Calculator.
const { cashflow, currencies, defineModel, entity, input, periods, schedule } = deccf;
const { personalAccountPlan, stdaccount } = modules.accountplan;
const { swedishIncomeTax, swedishIncomeTaxSettlement, swedishPreliminaryIncomeTax } = modules["swedish-income-tax"];
const anna = entity.party({ attributes: { dateOfBirth: "1990-05-21" } });
const salaryGrowth = input.rate(0.03);
const yearEnd = schedule.yearEnd();
const cash = stdaccount({ owner: anna, code: "assets.cash.bank", initial: 25_000 });
const salaryAccount = stdaccount({ owner: anna, code: "income.employment.salary" });
const salary = cashflow({
schedule: schedule.monthly({ from: "2026-01", to: "2028-12" }),
debit: cash,
credit: salaryAccount,
amount: (ctx) => 50_000 * Math.pow(1 + ctx.get(salaryGrowth), Math.floor(ctx.time.index / 12)),
});
const preliminaryTax = swedishPreliminaryIncomeTax({ person: anna });
const tax = swedishIncomeTax({ person: anna });
const taxSettlement = swedishIncomeTaxSettlement(tax);
const closing = personalAccountPlan.bookClosing({ owner: anna, schedule: yearEnd, dependsOn: [tax] });
return defineModel({
currency: currencies.SEK,
// The extra five periods show the final settlement for tax year 2028 in May 2029.
timeline: periods.monthly({ from: "2026-01", count: 41 }),
preconditions: { salaryGrowth },
entities: { anna },
flows: { salary, preliminaryTax, tax, taxSettlement, closing },
});The tax flows find accounts owned by Anna by combining each account's type with its personal-plan category. They automatically add missing expense.income_tax_adjustment and expense.preliminary_income_tax accounts, and use an existing cash asset under assets.cash when one is available. The two tax accounts are siblings; both categories roll up beneath expenses.tax. If the person has several cash accounts, pass the same cashAccount reference to both tax flows. The selected account must exist in the compiled model; a stdaccount reference can be collected automatically from the tax flow.
The example adds only the cash account with an opening balance. The salary account is collected from its posting flow, and the tax and retained-earnings accounts are supplied by the tax and account-plan modules. The model does not need to list those accounts in accounts.
Preliminary payments
The preliminary flow runs in every month of the model's analysis timeline by default, so no schedule needs to be supplied. It looks at the person's mapped employment income for the previous complete calendar year. If that total is zero, it searches backward from the current month to January for the latest month with positive employment income and annualizes that month's amount by multiplying by 12. It calculates tax on that annual service-income estimate using the same tax engine, divides the result by 12, then adds SEK 500.
Each payment debits the expense.preliminary_income_tax account and credits cash, so the payment appears in that month's income statement. No tax bases or tax declaration fields need to be supplied by the model.
Annual settlement
The assessment runs at year-end by default. It calculates the year's full income-tax liability from mapped account postings and compares it with that year's preliminary-tax expense postings. At this point it only emits the signed difference through the engine's ordinary trigger mechanism; it creates no ledger postings, so only monthly preliminary tax has been booked so far. The trigger snapshots that year's amount.
The paired settlement cashflow listens for that trigger and, after the delay, books the captured difference directly between expense.income_tax_adjustment and cash. A positive difference adds final tax expense and pays the remaining tax; a negative difference reduces that adjustment and receives the refund. There is no separate tax-due account: the settlement is booked when the final tax decision is made. settlementDelayDays defaults to SWEDISH_INCOME_TAX_SETTLEMENT_DELAY_DAYS (150 days) after the assessment occurrence, which places the whole final adjustment in May of the following year on a monthly timeline. For example, pass swedishIncomeTax({ person: anna, settlementDelayDays: 120 }) to move it earlier. The amount remains the value captured when that year's assessment ran. Include enough forecast periods to see the settlement; if the following May is outside the model, neither the final adjustment nor the settlement is booked within the forecast.
The personal account plan's bookClosing closes the preliminary and final tax expense accounts at year-end after the assessment. It obtains retained earnings through the personal plan when that account is missing. Add dependsOn: [tax] to book closing so the assessment trigger is emitted before income and expense accounts are closed. The May settlement records the final expense adjustment against cash in that same month.
The preliminary cashflow's schedule and the assessment's schedule option can be overridden with ordinary schedule references. The assessment schedule determines when its trigger captures the tax year's amounts; settlementDelayDays determines the delay from that occurrence. The model should use monthly periods when it needs a payment in every month.
Person attributes and rules
Attach dateOfBirth to the person, rather than to either tax cashflow:
const anna = entity.party({ attributes: { dateOfBirth: "1990-05-21" } });The pack reads and validates that static attribute. The default rules use 12 resident months, a municipal rate of 32.41%, the 2025 state-tax threshold, and the 2025 burial-fee rate. Pass rules to either tax flow to override these values.
Runtime and scope
The allowance table is bundled as an ES module, so the pack can run in Node.js and in the browser. The model supplies income and account definitions; the presentation layer only reads the resulting model output.
The pack applies 2025 rules to every forecast year. It is a planning estimate, not a tax filing calculation. The preliminary payment uses mapped employment income only; annual settlement uses all income and deductions mapped by this pack.