Files
accounted/extensions/general/mcp-server/skills/payroll-monthly.ts
T
MattssonandClaude Fable 5 436cbf5304 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>
2026-08-25 19:30:39 +02:00

124 lines
7.3 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import type { Skill } from './types'
const body = `# Monthly Payroll: Accounted
Salary run + AGI filing for one calendar month.
## When to use
- "Run payroll for [month]"
- "Lönekörning [månad]"
- "Generate AGI"
- Once per month, **before payment_date**
## Statutory deadlines
- **AGI (arbetsgivardeklaration):** 12th of the **next month** (17th in January and August). E.g. payroll for March → file AGI by 12 April.
- **Skatt + sociala avgifter payment:** same deadline as AGI.
- Skattekontot must be in funds by deadline (SFL 62 kap. 3 §).
## Workflow
### Step 1: Verify employees are set up
\`gnubok_list_employees\` returns all active employees. For each, the system needs:
- **Personnummer** (last 4 stored, full encrypted)
- **monthly_salary** (or hourly_rate + estimated hours)
- **employment_degree** (1-100 %)
- **tax_table_number** + **tax_column** (skattetabell + kolumn from Skatteverket)
- **employment_type** (\`tjänsteman\`, \`arbetare\`, etc.): drives BAS account choice (7210 vs 7010)
If anything is missing, the user fixes it in the web UI before running payroll.
### Step 2: Create the salary run
\`gnubok_create_salary_run({ period_year, period_month, payment_date })\`
- Creates a \`salary_runs\` row with status \`draft\`
- Adds **all active employees** with their base salary line (item_type \`monthly_salary\` or \`hourly_salary\`)
- Returns the run ID + employee count
- Idempotent on \`(company_id, period_year, period_month)\`: re-calling errors with "Salary run already exists for this period"
### Step 3: Add OB-tillägg, traktamente, förmåner (if any)
Variable lines (overtime, weekend supplement, milage, traktamente, förmåner) are added in the web UI per-employee. There's no MCP tool yet for these: guide the user there.
### Step 4: Calculate
\`gnubok_calculate_salary_run({ salary_run_id })\`. Computes per employee:
- **Bruttolön** (gross): sum of taxable salary lines
- **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/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.
- **Förmåner** (benefits): employer-paid taxable amounts (bilförmån, kostförmån, etc.): added to skattegrundande lön but not to nettolön payment.
Errors at this stage usually mean missing tax-table data: fall back to \`getDefaultTaxColumn(personnummer, year)\` heuristics or prompt user.
### Step 5: Review
\`gnubok_get_salary_run({ salary_run_id })\`: full breakdown including \`calculation_breakdown\` showing step-by-step formulas. The user reviews per-employee in web UI.
\`gnubok_get_salary_journal({ year })\`: annual rollup for sanity check.
### Step 6: Approve & book
\`gnubok_book_salary_run({ salary_run_id })\` stages the booking (high-risk: commit needs \`confirmed=true\` on approval). On commit it walks the run \`review\` → \`approved\` → \`paid\` → \`booked\`; the staged approval is the authorization act. Alternatively the user clicks through the same statuses in the web UI. Booking creates the JE:
- 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 **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/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
\`gnubok_generate_agi({ salary_run_id })\`. Run must be in \`review\`/\`approved\`/\`paid\`/\`booked\` status (past draft).
Returns \`{ message, period, employee_count, download_url }\`. The XML conforms to Skatteverket's AGI format and is stored 7 years per BFL. Download from \`/api/salary/runs/{id}/agi/xml\` and upload to Skatteverket e-tjänst.
## Critical rules
- **Skatteavdrag is mandatory.** Never pay gross. Skatteverket charges 100% penalty for missing avdrag.
- **Sociala avgifter are 31.42 % even if salary is in EUR.** Convert to SEK at payment date for the avgift base.
- **Semesterlöneskuld** must be reserved monthly, not at year-end. 2920 grows by 12 % of every month's bruttolön.
- **Förmånsbeskattning** (benefit tax) is required even if not in cash. Bilförmån, kostförmån, sjukvårdsförsäkring all count.
- **Karensavdrag** (sick day deduction): first day of sickness is generally without pay; 80 % from day 2. Specific rules: fall through to \`swedish-payroll\` reference if unsure.
## Common errors
- **Run already exists**: idempotency on (company, year, month). Find the existing run with \`gnubok_get_salary_run\`.
- **Tax table column wrong**: defaults to column 1 if not set, which is too high for most employees. Fix on employee record.
- **AGI before booking**: works (status check is past-draft, not booked), but you should book first so the JE matches what AGI reports.
## Tools
- \`gnubok_list_employees\`: verify setup
- \`gnubok_create_salary_run\`: stage new monthly run
- \`gnubok_calculate_salary_run\`: compute tax + avgifter + accrual
- \`gnubok_get_salary_run\`: review breakdown
- \`gnubok_book_salary_run\`: stage booking (statuses + verifikat)
- \`gnubok_get_salary_journal\`: annual rollup
- \`gnubok_generate_agi\`: produce AGI XML for filing
`
export const payrollMonthlySkill: Skill = {
slug: 'payroll-monthly',
name: 'Monthly Payroll',
summary: 'Monthly salary run + AGI: employee setup, calculation, sociala avgifter, semesterlöneskuld, booking, AGI XML.',
tags: ['monthly', 'payroll', 'agi', 'compliance'],
body,
tier: 'workflow',
// Only relevant when the company actually has employees. EF without payroll
// (most sole traders) shouldn't see this in the discovery list.
applicability: { entity_type: 'both', requires: ['employees'] },
}