fix(skattekonto): route AGI draw back to 2731 to match salary module (#1905)

* fix(skattekonto): route AGI draw back to 2731 to match salary module (#1870)

Migration 20260519160000 moved the skattekonto AGI seed to 2730 while the
salary module kept crediting 2731, splitting the employer-contribution
liability across two accounts that never net at account level (both carry
SRU 7231, so only huvudbok reconciliation exposes the drift). Revert the
system seed to 2731: BAS 2026 defines 2731 as the reported-but-unpaid
arbetsgivaravgift liability (the accrual account is 2940), and the salary
ore-residual logic is built around 2731.

Historical 2730 debits since 2026-05-19 are left for per-company reclass
verifikat; the migration touches the system seed only.

Fixes #1870

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* fix(skattekonto): bump migration version to avoid collision with 20260825120000_create_company_for_user

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* fix(payroll): align remaining 2730 guidance surfaces on 2731 (#1870)

Skeptic regression finding: companies booking salary manually were taught
7510/2730 by in-product guidance, so the seed revert alone would re-create
the #1870 split mirrored for them. Align every guidance surface on 2731:

- packs/loneutbetalning.yaml legal_note
- MCP payroll-monthly skill (booking recipe and rate notes)
- swedish-payroll SKILL.md + references/bas-7xxx.md (2731 convention, 2730
  group-account alternative, never mixed; accrual is 2940) + regenerated
  agent atom seed (skills:generate -> 20260825180001)
- public/docs/systemdokumentation-mall.md

Also addresses the compliance review finding that the swedish-payroll skill
contradicted the migration.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Mattsson
2026-08-25 19:30:39 +02:00
committed by GitHub
co-authored by Claude Fable 5
parent a82328da84
commit 436cbf5304
10 changed files with 17619 additions and 16 deletions
@@ -52,7 +52,7 @@ Variable lines (overtime, weekend supplement, milage, traktamente, förmåner) a
- **Skatteavdrag**: tax-table lookup (skattetabell + kolumn → table column for the gross level)
- **Nettolön** (net): bruttolön − skatteavdrag
- **Sociala avgifter (arbetsgivaravgifter)**: 31.42 % of bruttolön (standard 2025). Reduced rates apply to specific age groups: always check the current statutory rates before relying on these:
- **Born 1937 or earlier**: **0 %**: no avgifter at all (oldest cohort, never paid into the modern pension system). Easy to miss; the BAS journal entries for 7510/2730 simply don't apply for these employees.
- **Born 1937 or earlier**: **0 %**: no avgifter at all (oldest cohort, never paid into the modern pension system). Easy to miss; the BAS journal entries for 7510/2731 simply don't apply for these employees.
- **Age 66+ on 1 January of the income year (67+ from income year 2026)**: 10.21 % (only ålderspensionsavgift). The threshold rises with the riktålder; verify the cohort year for the current run rather than hard-coding a birth year.
- **växa-stöd / temporary youth reduction**: ages 19-23, salary ≤ 25 000 SEK/month, capped duration. The exact rate and window vary year-over-year (e.g. 20.81 % during 1 Apr 2026: 30 Sep 2027 per Prop. 2025/26:34): confirm against Skatteverket's current published table before applying.
- **Semesterlöneskuld** (vacation accrual): 12 % of bruttolön (default). Booked monthly to 2920.
@@ -73,11 +73,11 @@ Errors at this stage usually mean missing tax-table data: fall back to \`getDefa
- Debit **7210** (lön tjänstemän) or **7010** (lön arbetare): bruttolön
- Debit **7510** (sociala avgifter): \`avgift_base × applicable_rate\`: **per employee**, using the rate from Step 4 (default 31.42 %, or a reduced rate when applicable: 10.21 % for 66+, växa-stöd, etc.)
- Credit **2710** (källskatt): skatteavdrag
- Credit **2730** (lagstadgade arbetsgivaravgifter): same amount as the 7510 debit (the avgift cost is the same number as the avgift liability)
- Credit **2731** (avräkning lagstadgade sociala avgifter): same amount as the 7510 debit (the avgift cost is the same number as the avgift liability). 2731 is what the engine books and what the skattekonto AGI draw debits; crediting the group account 2730 instead splits the liability across two accounts (issue #1870)
- Credit **2920** (semesterlöneskuld): 12 % × bruttolön (debit 7290 to balance)
- Credit **1930** (bank): nettolön (when paid)
When a run mixes full-rate and reduced-rate employees, the 7510/2730 lines are summed across all employees: the *total* avgift line equals \`Σ(per-employee avgift_base × per-employee rate)\`, **not** \`Σ bruttolön × 31.42 %\`. \`gnubok_calculate_salary_run\` already does this aggregation.
When a run mixes full-rate and reduced-rate employees, the 7510/2731 lines are summed across all employees: the *total* avgift line equals \`Σ(per-employee avgift_base × per-employee rate)\`, **not** \`Σ bruttolön × 31.42 %\`. \`gnubok_calculate_salary_run\` already does this aggregation.
### Step 7: Generate AGI
@@ -31,7 +31,7 @@ const SEED_RULES = [
{
id: 'sys-4', priority: 20, pattern: 'arbetsgivaravgift,sociala avgifter,agi',
amount_min: null, amount_max: null, company_type: 'all',
counter_account: '2730', counter_account_ef: null,
counter_account: '2731', counter_account_ef: null,
label: 'Arbetsgivaravgifter', active: true, requires_employer: false,
},
{
@@ -135,17 +135,19 @@ describe('guessCounterAccount', () => {
).toBe('2013')
})
it('routes employer payroll taxes to 2730 (clearing/redovisningskonto, not 2731 accrual)', async () => {
// 2731 matches the salary module's AVGIFTER_LIABILITY credit so the SKV draw
// clears the same account (issue #1870); the accrual account is 2940, not 2731.
it('routes employer payroll taxes to 2731 (same account the salary module credits)', async () => {
const { supabase, enqueue } = makeSupabase()
enqueue({ data: SEED_RULES })
expect(
(await guessCounterAccount(supabase as unknown as SupabaseClient, 'company-1', 'Arbetsgivaravgifter januari', 'aktiebolag'))?.account,
).toBe('2730')
).toBe('2731')
enqueue({ data: SEED_RULES })
expect(
(await guessCounterAccount(supabase as unknown as SupabaseClient, 'company-1', 'Sociala avgifter Q1', 'aktiebolag'))?.account,
).toBe('2730')
).toBe('2731')
})
it('routes deducted income tax to 2710 for an aktiebolag', async () => {