feat(registry): move community registry source of truth into the public repo (#1458)

* feat(registry): move community registry source of truth into the public repo

The site's registry page says "Lägg till en egen" and links here, but the
MDX entries lived in the private website repo, so an external contributor
had no path to open the PR we were inviting (found by the first person who
tried). This makes the invitation real:

- registry/entries/ + registry/authors/ hold the 20 existing entries and
  2 author profiles, migrated verbatim from the website repo, which now
  syncs FROM this directory instead of owning the content
- registry/README.md documents the frontmatter convention and the flow
- scripts/validate-registry.ts (npm run validate:registry, wired into
  core-build) checks structure and rejects JSX/import/export in bodies:
  the site renders entries through MDX, which would execute those inside
  the website build
- CONTRIBUTING.md points at the registry for listing community work

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Signed-off-by: Jakob Wennberg <311770904+jakobwennberg-oss@users.noreply.github.com>

* fix(registry): close MDX-safety gaps and correct six compliance claims from PR review

Review bot findings on #1458, both verified and addressed:

- The body safety gate only rejected capitalized JSX tags, but MDX also
  evaluates lowercase HTML tags (<div>, <img onerror=...>) and bare {...}
  expressions. The validator now rejects any raw tag and any brace outside
  fenced code and backtick inline code; literal tags in prose go in
  backticks. Verified: a crafted entry with all three bypasses fails, all
  existing content still passes.
- Six factual errors in migrated entries, each checked against the skill
  sources in .claude/skills/ before editing (these were live on the site
  already): traktamente 2026 is 300 kr not 260; employer contributions for
  66+ at year start (67+ from 2026) are 10.21% not "65+: 16.36%", and the
  under-18 0% claim is replaced with the documented 18-22 youth reduction;
  electronics reverse-charge threshold is 100 000 kr excl VAT per invoice
  not 250 000; half prisbasbelopp 2026 is 29 600 not 24 750; kostnadsställe
  is SIE dimension 1 not 7; SRU period suffixes encode the fiscal-year end
  range (P1 jan-apr, P2 maj-aug, P4 sep-dec) not fixed months.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Signed-off-by: Jakob Wennberg <311770904+jakobwennberg-oss@users.noreply.github.com>

---------

Signed-off-by: Jakob Wennberg <311770904+jakobwennberg-oss@users.noreply.github.com>
Co-authored-by: Jakob Wennberg <311770904+jakobwennberg-oss@users.noreply.github.com>
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Jakob Wennberg
2026-08-08 10:54:09 +02:00
committed by GitHub
parent a49d75db77
commit 3d1ed15b6d
30 changed files with 2414 additions and 0 deletions
+8
View File
@@ -68,6 +68,14 @@ jobs:
# only a semantic check catches it.
run: npm run validate:packs
- name: Validate community registry
# registry/ is the public source of truth for gnubok.se/community/registry
# and takes entries by PR from outside the team. The gate checks the
# frontmatter the website build consumes, and rejects JSX/import/export
# in entry bodies: the site renders bodies through MDX, which would
# execute those at build time.
run: npm run validate:registry
- name: Check no core imports from extensions
run: |
VIOLATIONS=$(grep -r "from '@/extensions/" lib/ app/api/ components/ --include="*.ts" --include="*.tsx" \
+4
View File
@@ -77,6 +77,10 @@ Constraints:
- Core must build and run with zero extensions enabled
- Never import from `@/extensions/` in core code
## Community Registry
The registry at [gnubok.se/community/registry](https://www.gnubok.se/community/registry) (skills, MCP servers, workflows and apps built on Accounted) is sourced from the [`registry/`](registry/) directory in this repo. To list something you built, add an MDX entry there by PR; see [registry/README.md](registry/README.md) for the format and run `npm run validate:registry` for the local check. No code changes needed.
## What Not to Do
- **Don't modify enforcement triggers** in migration 017 (legally required for Swedish accounting law)
+1
View File
@@ -827,3 +827,4 @@ One line per decision: `[YYYY-MM-DD] <decision>: <why>`. Appended by agents and
[2026-08-07] Sandbox cleanup repaired via a dedicated gnubok.sandbox_cleanup transaction-local flag (respected by write_audit_log, audit_log_immutable, enforce_dimension_registry_guards, enforce_pending_operations_no_delete) instead of tombstoning sandbox users like the real account-deletion flow: sandbox data is synthetic demo content, not rakenskapsinformation, so full deletion is the correct GDPR/BFL posture and the audit-log DELETE bypass re-verifies per row that the company is a sandbox before letting anything through. Also revoked the default PUBLIC EXECUTE both cleanup RPCs had carried on prod since March (anon could call them via PostgREST), and validated the whole delete chain by replaying it against prod inside aborted transactions (21 sampled users across all seed eras) plus a committed staging run.
[2026-08-07] QuickReviewDialog's 'none'-collapse closed with a deviation test, not a blanket map: 'none' as the SEEDED default (exempt categories) stays off the wire so untouched bank-fee bookings remain byte-identical, while 'none' as a user deviation goes as explicit 'exempt' (books no VAT line, records the classification the momsdeklaration should see, lands income on 3004). The batch dialog's blanket 'none'→'exempt' map from #1443 is correct THERE because its select has no seeded-per-category default. CategoryExpandedDialog carries the same collapse but has zero references (dead code): left untouched, deletion is hygiene for a separate sweep.
[2026-08-08] Ruta 41 unblocked with a static ACCOUNT_RUTA mapping of 3231/3232/3233 (incl. 3232/3233, in the SKV 4700 mapping though not in our shipped BAS 2026 catalog) instead of starting the per-account VAT-treatment picker: the ElvaSmultron/Spiris support case needed omvänd-moms sales in the declaration now, and RC sales deliberately stay out of the ruta 05-08 vs 10-12 pairing checks because the buyer accounts for the VAT. The picker (any custom konto to any ruta, incl. purchase-side 4056-style Visma accounts to ruta 20) is #1457.
[2026-08-08] Community registry source of truth moved into this repo (registry/ with entries + authors, validator wired into core-build): the site button "Lagg till en egen" pointed at erp-mafia/accounted while the registry MDX lived in the private website repo, so an external contributor had no path to PR (found by the first would-be contributor). The website now syncs FROM registry/ instead of owning the content. Entry bodies are gated to plain Markdown because the site renders them through MDX, which would execute JSX and import/export statements inside the website build.
+1
View File
@@ -19,6 +19,7 @@
"lint": "eslint",
"check:guards": "node scripts/checks/no-new-antipatterns.mjs",
"validate:packs": "npx tsx scripts/validate-packs.ts",
"validate:registry": "npx tsx scripts/validate-registry.ts",
"check:lint": "node scripts/checks/no-new-lint-errors.mjs",
"test": "vitest run --project unit",
"test:pg": "vitest run --project pg-real"
+82
View File
@@ -0,0 +1,82 @@
# Community Registry
This directory is the source of truth for the registry shown at
[gnubok.se/community/registry](https://www.gnubok.se/community/registry):
skills, MCP servers, workflows and apps built on Accounted. The website syncs
its registry pages from here, so adding an entry is a normal pull request
against this repo.
## Structure
```
registry/
entries/ one .mdx file per registry entry
authors/ one .mdx file per author profile
```
Files starting with `_` are templates and are ignored by validation and sync.
## Adding an entry
1. Copy `entries/_template.mdx` to `entries/<your-slug>.mdx`. The filename is
the slug and the public URL: `gnubok.se/community/registry/<your-slug>`.
2. If this is your first contribution, copy `authors/_template.mdx` to
`authors/<your-handle>.mdx`. The `author` field in your entry must match an
author file, so first-time contributors add both files in the same PR.
3. Validate locally: `npm run validate:registry` (or
`npx tsx scripts/validate-registry.ts` without installing everything).
4. Open a PR. Commits need a DCO sign-off (`git commit -s`), same as the rest
of the repo; see [CONTRIBUTING.md](../CONTRIBUTING.md).
A maintainer reviews the entry (does it work, does it describe itself
honestly, is the content safe) and merges. After merge the website pulls the
entry in with its registry sync; expect it live on the site within a few days
of merging.
## Entry frontmatter
Required:
| Field | Type | Notes |
|---|---|---|
| `title` | string | Shown as the card and page title |
| `description` | string | Card subtitle, max 500 chars; put detail in the body |
| `slug` | string | Must equal the filename |
| `kind` | `skill` \| `mcp` \| `workflow` \| `app` | Which shelf it goes on |
| `author` | string | Handle of a file in `authors/` |
| `status` | `live` \| `beta` \| `archived` | Be honest; `beta` is fine |
| `lang` | `sv` \| `en` | Language of the entry body |
| `personas` | list | Any of `founder`, `finance`, `byra`, `developer` |
| `publishedAt` | date | `YYYY-MM-DD` |
| `updatedAt` | date | `YYYY-MM-DD`, bump when you edit |
At least one of `installCommand`, `downloadUrl`, `repoUrl`, `externalUrl` is
required so readers can actually get the thing. Optional extras:
`featured` (maintainer-set), `oauthScopes`, `gnubokTools` (which MCP tools it
touches), `requiresWriteScope`, `sieCompatible`, `version`, `faq` (list of
`{q, a}`), `related` (list of slugs), `ogImageEyebrow`.
## Body rules
The body after the frontmatter is **plain Markdown** (headings, lists, tables,
links, fenced code blocks). The website renders bodies through MDX, which
evaluates raw tags (lowercase HTML included), `{...}` expressions and
`import`/`export` statements at build time, so the validator rejects all of
them. Fenced code blocks and backtick inline code are fine; they are
displayed, never executed. Need a literal `<tag>` or `{value}` in prose? Put
it in backticks.
Write the body like documentation, not a landing page: what it does, what it
needs (scopes, API keys), what it will not do, and one honest limitation
beats three superlatives.
## What gets accepted
- It must exist and work against Accounted today (the live app or the
[MCP server](https://www.gnubok.se/community/registry/gnubok-mcp)).
- It must describe its write behavior truthfully. Anything that writes to the
ledger goes through staged operations that a human approves; entries that
work around that will not be listed.
- No fees are charged for listing, and your IP stays yours. Entries are
documentation and are contributed under this repo's license; the thing the
entry points to keeps whatever license you gave it.
+16
View File
@@ -0,0 +1,16 @@
---
# Copy this file to <your-handle>.mdx. The filename is the handle that
# entries reference in their author field.
handle: "your-handle"
name: "Your Name or Company"
# kind: team | byra | founder | partner | community
kind: "community"
# Optional:
# bio: "One line about you."
# url: "https://example.com"
# location: "Stockholm"
# joinedAt: "2026-01-01"
---
Optional longer presentation in plain Markdown. Shown on your author page at
gnubok.se/community/registry/byggt-av/your-handle.
+27
View File
@@ -0,0 +1,27 @@
---
handle: "erp-mafia"
name: "ERP MAFIA"
kind: "community"
bio: "Open-source-kollektiv som underhåller accounted plus en katalog community-skills för svensk bokföringscompliance. Samma grupp, olika scope: accounted är produkten, ERP MAFIA är orget bakom paketen som inte är produktspecifika."
url: "https://github.com/erp-mafia"
location: "Stockholm"
joinedAt: "2026-05-12"
---
ERP MAFIA är open-source-organet på GitHub som underhåller flera
parallella projekt:
* [gnubok](https://github.com/erp-mafia/accounted) — själva huvudboken (AGPL-3.0).
* [swedish-accounting-skills](https://github.com/erp-mafia/swedish-accounting-skills) — Claude Skills för svensk bokföringscompliance (MIT). Inte accounted-specifika.
Posterna nedan är från det andra repot — kunskapspaket som ger en
agent djup förståelse av BFL, BFNAR, BAS, K2/K3, moms, lön, SIE4,
SRU och bokslut. De fungerar med eller utan accounted som huvudbok.
Använd dem som compliance-orakel för en utvecklare eller en
agentmiljö som ska bygga, granska eller köra svensk bokföring. De
ersätter inte revisor eller bokföringskonsult, men ger en
agent stadig grund att stå på innan den ger förslag eller bokför.
Licens: MIT. Hela katalogen klonas direkt, eller skills kopieras
selektivt in i en egen `.claude/skills/`-mapp.
+16
View File
@@ -0,0 +1,16 @@
---
handle: "gnubok"
name: "accounted"
kind: "team"
bio: "Bygger en öppen svensk huvudbok som agenter kan köra. Open source under AGPL-3.0. Byggt i Stockholm."
url: "https://www.accounted.se"
location: "Stockholm"
joinedAt: "2026-01-01"
---
Vi bygger accounted i Stockholm sedan 2026 — en svensk huvudbok med dubbel
bokföring, MCP-server från dag ett och en behandlingshistorik som
Skatteverket kan läsa rakt av.
Det här är teamets egna paket i registry. Allt under AGPL-3.0,
allt installerbart utan avtal eller avgifter.
+36
View File
@@ -0,0 +1,36 @@
---
# Copy this file to <your-slug>.mdx. The filename is the slug and the URL:
# gnubok.se/community/registry/<your-slug>
title: "Human-readable name"
description: "One or two sentences for the card. Max 500 chars; detail goes in the body."
slug: "your-slug"
# kind: skill | mcp | workflow | app
kind: "mcp"
# author: handle of a file in registry/authors/ (add yours in the same PR)
author: "your-handle"
# status: live | beta | archived
status: "beta"
# lang: language of the body, sv | en
lang: "sv"
# personas: any of founder, finance, byra, developer
personas: ["developer"]
publishedAt: "2026-01-01"
updatedAt: "2026-01-01"
# At least one of these four, so readers can actually get the thing:
externalUrl: "https://example.com"
# repoUrl: "https://github.com/you/your-repo"
# installCommand: "npx your-package"
# downloadUrl: "https://example.com/file.json"
# Optional: version, oauthScopes, gnubokTools, requiresWriteScope,
# sieCompatible, faq (list of {q, a}), related (list of slugs)
---
## What it does
Plain Markdown only (the validator rejects JSX and import/export). Describe
what it does, what it needs (scopes, API keys, accounts), and what it will
not do. One honest limitation beats three superlatives.
## Getting started
Steps a reader can follow today.
@@ -0,0 +1,157 @@
---
title: "Daglig runway → Slack"
description: "n8n-workflow med dagligt 08:00-cron som anropar gnubok_get_kpi_report, beräknar runway lokalt från cash_position och 90d-burn, jämför med gårdagens snapshot (i n8n data-store), och postar en Slack-block med trendpil och färgkodning vid runway under 6 mån."
slug: "daglig-runway-till-slack"
kind: "workflow"
author: "gnubok"
status: "live"
lang: "sv"
personas: ["founder"]
publishedAt: "2026-05-12"
updatedAt: "2026-07-18"
downloadUrl: "https://github.com/erp-mafia/accounted/releases/download/workflows-v1/daglig-runway-till-slack.zip"
repoUrl: "https://github.com/erp-mafia/accounted/tree/main/workflows/daglig-runway-till-slack"
gnubokTools:
- "gnubok_get_kpi_report"
- "gnubok_get_balance_sheet"
- "gnubok_query_journal"
oauthScopes:
- "reports:read"
requiresWriteScope: false
version: "1.0.0"
ogImageEyebrow: "Workflow"
faq:
- q: "Vad händer om PSD2-banken är offline och 1930 inte är dagsfärsk?"
a: "Workflowet läser timestamp på senaste 1930-transaktion via `gnubok_query_journal(account: '1930', limit: 1)`. Är den >24 h läggs en notering i Slack-blocket: 'Bank-feed släpar, 1930 baserat på data från X timmar sedan.' Inget larmar förrän feeden är inne igen. Inga falska alarm bara för att banken har problem."
- q: "Kan jag ha olika trösklar per företag (jag har två AB)?"
a: "Ja. Tröskel-värden ligger i n8n Data Store keyed på `company_id`. Lägg till en rad per företag med fälten orange_months (default 6), red_months (default 3), critical_months (default 1). Workflowet läser raden vid varje körning. Inget kod-deploy behövs."
- q: "Hur stänger jag av posten på lördagar?"
a: "Cron-uttrycket i Schedule-noden är `0 8 * * 1-5` som default, alltså bara vardagar. Vill du även lördagar, ändra till `0 8 * * 1-6`."
- q: "Vad om jag inte har Slack, funkar Discord eller Teams?"
a: "Ja. Workflowet är en n8n-mall. Sista noden är 'Slack: Post Message' men kan bytas mot 'Discord: Send Message', 'Microsoft Teams: Post Message' eller HTTP request mot valfri webhook. Innehållet (block-format) anpassas i den noden, allt annat förblir."
- q: "Vad händer med personuppgifter, skickas något känsligt till Slack?"
a: "Nej. Workflowet skickar bara aggregerade tal: kassa, burn, runway. Inga transaktionsdetaljer, inga kundnamn, inga personnummer. Slack-meddelandet är säkert nog för en allmän #finance-kanal."
---
## Vad som händer 08:00
Varje vardag kl 08:00:
1. n8n-cron triggar.
2. Anropar `gnubok_get_kpi_report` via accounted MCP. Returnerar `cash_position`, `total_revenue`, `total_expenses`, `months[]`.
3. Anropar `gnubok_query_journal(account: '1930', limit: 1)` för att kolla bank-feed-färskhet.
4. Beräknar runway lokalt: `runway_months = cash_position / ((total_expenses - total_revenue över senaste 90 dagar) / 90 * 30.4)`.
5. Hämtar gårdagens snapshot från Data Store.
6. Beräknar deltat: kassa-diff, burn-trend, runway-diff i dagar.
7. Bestämmer färg: grön (`>12 mån`), gul (`612 mån`), orange (`36 mån`), röd (`<3 mån`).
8. Bygger Slack-block-message.
9. Postar till konfigurerad kanal.
10. Sparar dagens snapshot i Data Store inför imorgon.
Read-only API-nyckel räcker. Inga skriv-scopes behövs.
## Slack-blocket
```
🟢 Runway: 9.06 månader
Kassa: 1 247 380 SEK ↓ 52 080 (4.0%)
Burn / mån: 137 600 SEK stabil
Trend 7d: 0.4 månader under 12-mån-mål
1930 senast: 06:14, idag
Detaljerad vy → app.gnubok.se/dashboard
```
Vid sjunkande trend till orange/röd nivå:
```
🟠 RUNWAY UNDER 6 MÅNADER
Runway: 5.8 månader (mål: ≥ 6)
Kassa: 843 200 SEK ↓ 47 100 från igår
Burn / mån: 145 600 SEK ↑ 5.8% jämfört förra månaden
Tre största utlopp senaste 7 dagar:
• 134 800 SEK Löneutbetalning april
• 23 400 SEK AWS april
• 19 200 SEK Lokalhyra Q2
Detaljerad vy → app.gnubok.se/dashboard
```
Färgmappningen är emoji plus Slack-attachment-color så det syns både i
notification-vyn och i tråden.
## Importera i n8n
```bash
# Ladda ner zip-paketet, packa upp
n8n import:workflow --input=daglig-runway-till-slack.json
```
Eller via UI: `n8n → Workflows → Import → välj filen`.
Konfigurera credentials:
1. **accounted MCP-anslutning** med scope `reports:read`. För n8n som inte stödjer MCP-OAuth direkt: använd stdio-fallback via API-nyckel (`gnubok_sk_...`) med scopen begränsad till `reports:read`. Skapa nyckeln på app.gnubok.se → Inställningar → API-nycklar.
2. **Slack webhook** för kanalen `#finance` (eller motsvarande).
Aktivera workflowet. Första körningen blir inom 24 timmar. Vill du
testa direkt, klicka "Execute workflow" i n8n.
## Data Store-strukturen
n8n Data Store används för att hålla gårdagens snapshot per företag:
```json
{
"company_id": "ab_123abc",
"ts": "2026-05-11T08:00:00Z",
"kassa": 1299460,
"burn_30d": 142800,
"runway_months": 9.10,
"thresholds": {
"orange_months": 6,
"red_months": 3,
"critical_months": 1
}
}
```
Vid körning skriver workflowet över raden med dagens värden. Behöver
du historik längre tillbaka, kör en parallell n8n-workflow som
exporterar till Google Sheets eller en kolumn-databas.
## Konfiguration via Data Store
Lägg till en rad per företag med dessa fält:
| Fält | Default | Notering |
|---|---|---|
| `company_id` | krävs | Hämtas från accounted-API |
| `slack_channel` | krävs | `#finance` eller annat |
| `orange_months` | 6 | Runway där posten blir gul |
| `red_months` | 3 | Där posten blir orange |
| `critical_months` | 1 | Där posten blir röd plus pingar specifik person |
| `critical_user_id` | optional | Slack user-id att @-mention vid kritisk nivå |
| `weekend_post` | false | Skicka även lör/sön |
Ändra utan att deploya. n8n läser raden vid varje körning.
## Begränsningar
* Workflowet förutspår inte framtida burn. Det jämför dagens läge mot gårdagens.
* Workflowet inkluderar inte commitments, alltså utgående fakturor som inte gått ut än, försenade payouts.
* Workflowet pingar inte vid varje förändring, bara vid tröskel-överskridning.
* En API-nyckel per företag krävs idag. Multi-tenant via `companies:read`-scopen är på roadmap men inte exponerad i MCP än.
## Vad du får
Daglig runway-uppdatering i kanalen där folk faktiskt läser, utan
att grundaren behöver öppna en dashboard. Plus en historisk
snapshot per dag som tillåter "när började det luta?"-analys
vid kvartalsrapporten.
Konfiguration: 5 minuter. Kostnad: 0 SEK/månad i n8n self-hosted, eller
n8n Cloud Starter (200 körningar/mån räcker till 6 företag dagliga).
+180
View File
@@ -0,0 +1,180 @@
---
title: "accounted MCP-server"
description: "MCP-server för svensk dubbel bokföring. Över 120 verktyg över bokföring, fakturor, leverantörer, moms, lön och rapporter. OAuth 2.1 med PKCE och dynamisk klientregistrering. Installeras som connector i Claude.ai, Claude Desktop, Cursor och Continue."
slug: "gnubok-mcp"
kind: "mcp"
author: "gnubok"
status: "live"
lang: "sv"
personas: ["developer", "founder", "byra"]
publishedAt: "2026-05-12"
updatedAt: "2026-07-18"
featured: true
externalUrl: "https://app.gnubok.se/api/extensions/ext/mcp-server/mcp"
repoUrl: "https://github.com/erp-mafia/accounted"
oauthScopes:
- "mcp"
gnubokTools:
- "gnubok_list_uncategorized_transactions"
- "gnubok_categorize_transaction"
- "gnubok_create_invoice"
- "gnubok_get_vat_report"
- "gnubok_auto_match_period"
- "gnubok_export_sie"
requiresWriteScope: true
sieCompatible: true
version: "1.0.1"
ogImageEyebrow: "MCP-server"
faq:
- q: "Vad är accounted MCP-server?"
a: "En HTTP-baserad MCP-server som ger en AI-agent åtkomst till en svensk huvudbok: verifikat, kunder, fakturor, moms, lön, SIE-export. OAuth 2.1 med PKCE och dynamisk klientregistrering. Över 120 verktyg över bokföring, fakturor, leverantörer, moms, lön och rapporter."
- q: "Hur funkar OAuth-flödet?"
a: "MCP-klienten upptäcker accounteds autentiseringsserver via `.well-known/oauth-protected-resource`. Klienten registrerar sig dynamiskt (RFC 7591) på `/api/mcp-oauth/register`, öppnar `/api/mcp-oauth/authorize` i webbläsaren, du loggar in på accounted och godkänner anslutningen, klienten utbyter authorization code mot en access token via `/api/mcp-oauth/token` med PKCE S256."
- q: "Vilka klienter funkar?"
a: "Allt som följer MCP-spec 2026-02 och stödjer HTTP-transport med OAuth: Claude.ai (connectors), Claude Desktop (HTTP), Cursor, Continue, Codex. För stdio-bara klienter finns en lokal bro: `npx gnubok-mcp` med API-nyckel."
- q: "Vilka scopes ger jag bort?"
a: "OAuth-flödet ger en enskild `mcp`-scope: 'denna agent får använda accounteds MCP'. Inom det är agentens åtkomst begränsad till samma rättigheter som ditt eget accounted-konto har. Du kan när som helst återkalla anslutningen i app.gnubok.se → Inställningar."
- q: "Får agenten skriva direkt till huvudboken?"
a: "Bara om ditt eget konto har skrivrättigheter. Och även då stagar varje write en `pending_operation` som du godkänner i app.gnubok.se. Poster är immutabla efter posting; rättelser är nya verifikat via `gnubok_correct_entry`. BFL 5 kap 5§ är arkitektoniskt, inte en policy."
- q: "Vad händer vid Skatteverkets revision?"
a: "Den 1 april 2026 öppnas online-åtkomst för revisorer. `gnubok_audit_package` paketerar SIE-4 + huvudbok + balans- och resultaträkning + audit-logg + voucher-gaps som en zip med 1-timmes signed URL. Det är samma data som UI:t bygger på."
---
## Servern i korthet
Bryggan exponerar över 120 verktyg som en MCP-klient kan kalla. De grupperas
ungefär så här:
| Område | Exempel-verktyg | Server-side scope |
|---|---|---|
| Transaktioner | `gnubok_list_uncategorized_transactions`, `gnubok_categorize_transaction`, `gnubok_match_transaction_to_invoice` | `transactions:read` / `:write` |
| Fakturor | `gnubok_create_invoice`, `gnubok_send_invoice`, `gnubok_credit_invoice` | `invoices:write` |
| Leverantörer | `gnubok_list_suppliers`, `gnubok_create_supplier_invoice_from_inbox`, `gnubok_approve_supplier_invoice` | `suppliers:read` / `:write` |
| Rapporter | `gnubok_get_trial_balance`, `gnubok_get_balance_sheet`, `gnubok_get_income_statement`, `gnubok_get_vat_report`, `gnubok_vat_close_check`, `gnubok_get_kpi_report` | `reports:read` |
| Bokslut | `gnubok_year_end_readiness` (read), `gnubok_run_year_end`, `gnubok_close_period`, `gnubok_explain_voucher_gap` | `reports:read` / `bookkeeping:write` |
| Lön | `gnubok_create_salary_run`, `gnubok_calculate_salary_run`, `gnubok_book_salary_run`, `gnubok_generate_agi` | `payroll:read` / `:write` |
| Audit | `gnubok_audit_package`, `gnubok_export_sie`, `gnubok_query_journal` | `reports:read` |
Skriv-operationer stagar en `pending_operation` som du godkänner i
accounted-appen. Inget skrivs till DB innan en människa klickat.
Server-side scopes är knutna till ditt accounted-konto, inte till
OAuth-grantet. Agenten kan göra allt du själv kan göra inom de
verktyg den får exponera.
## Installera i Claude.ai
1. I Claude → Settings → Connectors → Add custom connector.
2. Ange URL: `https://app.gnubok.se/api/extensions/ext/mcp-server/mcp`.
3. Claude öppnar accounted-OAuth i webbläsaren. Logga in. Godkänn anslutningen.
4. Connectorn dyker upp i listan. Slå på för de chattar där du vill ha den aktiv.
## Installera i Claude Desktop
`claude_desktop_config.json` (`~/Library/Application Support/Claude/` på macOS):
```json
{
"mcpServers": {
"gnubok": {
"url": "https://app.gnubok.se/api/extensions/ext/mcp-server/mcp",
"transport": "http"
}
}
}
```
Starta om Claude Desktop. OAuth-flödet triggas vid första anrop.
## Installera i Cursor
`.cursor/mcp.json`:
```json
{
"mcpServers": {
"gnubok": {
"url": "https://app.gnubok.se/api/extensions/ext/mcp-server/mcp",
"transport": "http"
}
}
}
```
Cmd-Shift-P → MCP: Refresh Servers. OAuth-prompten dyker upp i en
webbläsarflik.
## Installera i Continue
`~/.continue/config.json`:
```json
{
"mcpServers": [
{
"name": "gnubok",
"url": "https://app.gnubok.se/api/extensions/ext/mcp-server/mcp",
"transport": "http"
}
]
}
```
## Stdio-fallback för icke-OAuth-klienter
För klienter som bara stödjer stdio (vissa custom-agentmiljöer,
CI-pipelines) finns en lokal bro via npm:
```json
{
"mcpServers": {
"gnubok": {
"command": "npx",
"args": ["gnubok-mcp"],
"env": {
"GNUBOK_API_KEY": "gnubok_sk_..."
}
}
}
}
```
API-nyckel skapas i app.gnubok.se → Inställningar → API-nycklar.
Här väljer du scopes per nyckel: `transactions:read`,
`transactions:write`, `customers:read`, `customers:write`,
`invoices:read`, `invoices:write`, `suppliers:read`,
`suppliers:write`, `reports:read`, `bookkeeping:write`,
`payroll:read`, `payroll:write`.
API-nyckel-flödet kringgår OAuth-discoveryn men ger samma
verktygsåtkomst. Lämpligt när maskinen som kör agenten inte kan
öppna en webbläsare.
## Audit och behandlingshistorik
För Skatteverkets revision och BFL-spårbarhet använder du
`gnubok_audit_package(period: "...")`. Den producerar en zip med:
* SIE-4-fil för perioden
* Balans- och resultaträkning som PDF
* Huvudboken (per konto, alla rader)
* Audit-loggen som CSV: vem rörde vad när, scope, before/after
* Kvitto-arkivet som referens
* Voucher-gap-rapport per BFNAR 2013:2
Filen ligger i Supabase Storage med en signed URL på 1 timme.
Audit-loggen genereras vid varje skriv-operation och kan inte
stängas av.
## Sånt bryggan inte gör
* Bryggan kör ingen tredjepartskod. Egna skills och workflows körs alltid hos den som skickar anropet, inte hos oss.
* Bryggan stagar alltid skriv-operationer. Ingen agent kan posta ett verifikat utan ett mänskligt godkännande i [app.gnubok.se](https://app.gnubok.se).
* En OAuth-anslutning är bunden till ett accounted-konto, som i sin tur är bunden till ett företag. För byråer som hanterar flera klienter krävs en anslutning per klient idag. Multi-tenant via `companies:read`-scopen är på roadmap för v1 REST API.
## Versionering
OAuth-discoveryn följer RFC 8414 (Authorization Server Metadata),
RFC 9728 (Protected Resource Metadata) och RFC 7591 (Dynamic Client
Registration). Stdio-bron är `gnubok-mcp@1.0.1` på npm. Källkod
under AGPL-3.0 på [github.com/erp-mafia/accounted](https://github.com/erp-mafia/accounted).
@@ -0,0 +1,161 @@
---
title: "Kvartalsmoms på 12 minuter"
description: "SKILL.md som stänger ett kvartals moms från okategoriserat till 'redo att lämna in' i 12 minuter. Identifierar blockers via gnubok_vat_close_check, föreslår kategorisering, returnerar ifyllt SKV 4700 från gnubok_get_vat_report."
slug: "kvartalsmoms-pa-12-minuter"
kind: "skill"
author: "gnubok"
status: "live"
lang: "sv"
personas: ["founder", "finance"]
publishedAt: "2026-05-12"
updatedAt: "2026-07-18"
downloadUrl: "https://github.com/erp-mafia/accounted/releases/download/skills-v1/kvartalsmoms-pa-12-minuter.zip"
repoUrl: "https://github.com/erp-mafia/accounted/tree/main/skills/kvartalsmoms-pa-12-minuter"
gnubokTools:
- "gnubok_vat_close_check"
- "gnubok_get_vat_report"
- "gnubok_list_uncategorized_transactions"
- "gnubok_categorize_transaction"
- "gnubok_query_journal"
- "gnubok_vat_review_widget"
oauthScopes:
- "reports:read"
- "transactions:read"
- "transactions:write"
requiresWriteScope: true
version: "1.0.0"
ogImageEyebrow: "Skill"
faq:
- q: "Vad är skillnaden mellan ruta 30 och ruta 60? Jag har importmoms från Stripe."
a: "Ruta 30 är reverse-charge utgående moms på EU-tjänster (AWS, Linear, Stripe-avgifter, Vercel). Ruta 60 är beräknad importmoms på varor från länder utanför EU. Stripe-avgiften är ett EU-tjänsteinköp om Stripe-bolaget är i Irland, då hamnar det i ruta 30, inte 60. Skillen läser leverantörens land från extracted_data och mappar enligt din kontomappning."
- q: "Min reverse-charge är inte balanserad, vad menas det?"
a: "Reverse-charge ska spegla sig: 2614 (utgående 25%) ska motsvaras av 2645 (beräknad ingående) på samma belopp. Är de inte lika är något bokfört fel. `gnubok_vat_close_check` returnerar blockern `reverse_charge_input_missing` med peklänk till transaktionen."
- q: "Hur ofta råkar man flytta 2641 till 2645 för EU-tjänster?"
a: "Det är den vanligaste momsfel-typen för svenska SaaS-grundare. Generic AI-kategoriserare lär sig inte skillnaden mellan 2641 (vanlig ingående) och 2645 (omvänd skattskyldighet). När du kör skillen för Q1 kommer den retroaktivt flagga rader från tidigare kvartal som hamnat fel via blockern `reverse_charge_input_missing`."
- q: "Behöver jag stänga kvartalet manuellt efteråt?"
a: "Nej. Skillen stagar inte close_period. Den producerar bara SKV 4700-paketet och säger 'redo att lämna in'. Själva inlämningen sker manuellt i Skatteverkets e-tjänst (BankID). Skatteverket har ingen MCP-yta för momsinlämning idag."
- q: "Vad händer om jag har en pågående tvist om kreditfaktura?"
a: "Skillen flaggar omtvistade fakturor som blockers. De räknas inte in i den föreslagna ruta 49-summan. När tvisten löser sig kör du skillen igen och ruta 49 uppdateras. BFL 5 kap 1§: moms får inte rapporteras på något du själv inte håller med om."
---
## Stegen
Skillen stänger ett kvartals moms i ett anrop till
`gnubok_vat_close_check`. Inte fem separata rapporter, utan en
strukturerad förfrågan som returnerar exakt vad du behöver veta
för att fylla i SKV 4700.
Stegen, mestadels parallella:
1. `gnubok_vat_close_check(period: "2026-Q1")`. Drar blocker-rapport.
2. Om `uncategorized_transactions`: `gnubok_list_uncategorized_transactions(period: ...)` följt av `gnubok_categorize_transaction` per post.
3. Om `unapproved_supplier_invoices`: peklänk till godkännandekön i appen.
4. Om `reverse_charge_input_missing`: `gnubok_query_journal(accounts: [2614, 2624, 2634, 2645], period: ...)`. Visar felaktiga rader.
5. Om `missing_high_value_receipts`: peklänk till transaktioner saknande underlag ≥ 4 000 kr.
6. Om `bank_unreconciled`: peklänk till bankavstämningen i appen.
7. När blockers = 0: `gnubok_get_vat_report(period: ...)`. Returnerar fyllt SKV 4700.
8. Skillen visar rutornas värden mot förra kvartalets snitt (drift-check via warnings: `output_vat_ratio_drift`, `input_vat_ratio_drift`, `revenue_drop`, `revenue_spike`).
9. Skillen flaggar ruta 49 = att betala/återfå.
10. Returnerar SKV 4700-värdena. Inlämning sker manuellt i Skatteverkets e-tjänst.
Inget skrivs till huvudboken utan godkännande. Skatteverket-inlämning
sker manuellt; ingen MCP-bro för momsdeklaration finns idag.
## SKV 4700: rutorna skillen fyller i
| Ruta | Vad det är | accounted-konto |
|---|---|---|
| 05 | Försäljning Sverige | 3001, 3002, 3041 |
| 10 | Utgående moms 25% | 2611 |
| 11 | Utgående moms 12% | 2621 |
| 12 | Utgående moms 6% | 2631 |
| 20 | Försäljning av varor till annat EU-land | 3105 |
| 21 | Försäljning av tjänster till annat EU-land | 3106 |
| 22 | Försäljning av varor utanför EU | 3108 |
| 23 | Försäljning av tjänster utanför EU | 3109 |
| 30 | Inköp av tjänster från EU (reverse-charge utg) | 2614 |
| 31 | Inköp av varor från EU (reverse-charge utg) | 2624 |
| 32 | Inköp från annat EU-land (övrigt) | 2634 |
| 35 | Inköp från konsument | 2680 |
| 39 | Vinstmarginalbeskattning utgående moms | 2616 |
| 40 | Vinstmarginalbeskattning beskattningsunderlag | — |
| 48 | Ingående moms att dra av | 2641, 2645 |
| 49 | Att betala / få tillbaka | (beräknad) |
| 60 | Beräknad importmoms 25% (varor icke-EU) | 2615 |
| 61 | Beräknad importmoms 12% | 2625 |
| 62 | Beräknad importmoms 6% | 2635 |
Konton hänvisar till BAS 2026. Om du använder en avvikande kontoplan
läser skillen den via `gnubok_list_accounts` och anpassar mappningen.
## Installera
```bash
git clone https://github.com/erp-mafia/accounted
cd gnubok/skills/kvartalsmoms-pa-12-minuter
```
I Claude Desktop:
```
Cmd-Shift-P → Skills: Add from folder → välj mappen
```
Kräver att [accounted MCP](/community/registry/gnubok-mcp) är ansluten med
scopes `reports:read`, `transactions:read`, `transactions:write`.
## Köra skillen
```
@kvartalsmoms-pa-12-minuter stäng Q1 2026
```
Agenten:
1. Kör `vat_close_check`, listar blockers (säg 12 `uncategorized_transactions`, 2 `reverse_charge_input_missing`, 0 övriga).
2. Föreslår kategoriseringar för okategoriserade.
3. Peklänkar reverse-charge-obalanserna.
4. Du godkänner i appen.
5. Skillen kör om `vat_close_check`. Blockers = 0.
6. Returnerar `get_vat_report` med fyllda rutor.
7. Visar diff mot Q4 2025 om någon av warnings triggar.
8. Frågar: "Filen är klar för upload."
Tar 914 minuter beroende på antal blockers. Snittet vi ser är
12 minuter på en period med 200 transaktioner.
## Blockers och vad de betyder
`uncategorized_transactions`: transaktioner i perioden som inte är
bokförda. Skillen erbjuder en kategoriseringssession.
`unapproved_supplier_invoices`: leverantörsfaktura i status
"registered" men inte "approved". Räknas inte in i ingående moms
förrän approved.
`bank_unreconciled`: bankavstämningen i perioden har diff. Måste
balansera innan moms-snapshot är trovärdig.
`missing_high_value_receipts`: BFL 5 kap 6§ kräver originalverifikat
för utgift ≥ 4 000 kr inkl moms. Saknas det blir det
revisor-anmärkning.
`reverse_charge_input_missing`: en EU-leverantörsfaktura kategoriserades
som 2641 + 4010 istället för split 2614/2645 + 4010. Utgående (2614)
saknar speglande ingående (2645) på samma belopp.
## Begränsningar
* Skillen lämnar inte in moms till Skatteverket. Det sker manuellt i e-tjänsten med BankID.
* Skillen räknar inte om historiska perioder. Den jobbar med en period i taget.
* Skillen försöker inte minska din moms-att-betala. Det är skatteplanering, inte momshantering.
* Skillen hanterar inte vinstmarginalbeskattning (begagnade varor, konst, antikviteter). Använd `gnubok_create_voucher` manuellt för det.
## Vad du får
Momsstängning som tar 12 minuter istället för 90. Och en
audit-logg per anrop, så att när Skatteverket frågar "varför
hamnade den här på ruta 48?", svaret finns på rad i
`gnubok_audit_package`-zippen: actor, scope, tidsstämpel, before/after.
Inte snabbare moms. Moms som är revisionsbar.
@@ -0,0 +1,119 @@
---
title: "Leverantörsfaktura från inbox — OCR till stagad verifikation"
description: "SKILL.md som plockar PDF- eller bildfakturor ur accounted-inbox, kör OCR, matchar leverantör mot organisationsnummer, och stagar gnubok_create_supplier_invoice_from_inbox med rätt momsuppdelning och konto. Du godkänner i appen."
slug: "leverantorsfaktura-fran-inbox"
kind: "skill"
author: "gnubok"
status: "live"
lang: "sv"
personas: ["byra", "finance", "founder"]
publishedAt: "2026-05-12"
updatedAt: "2026-07-18"
downloadUrl: "https://github.com/erp-mafia/accounted/releases/download/skills-v1/leverantorsfaktura-fran-inbox.zip"
repoUrl: "https://github.com/erp-mafia/accounted/tree/main/skills/leverantorsfaktura-fran-inbox"
gnubokTools:
- "gnubok_list_inbox_items"
- "gnubok_get_inbox_item"
- "gnubok_list_suppliers"
- "gnubok_create_supplier_invoice_from_inbox"
- "gnubok_get_counterparty_templates"
- "gnubok_suggest_categories"
oauthScopes:
- "transactions:read"
- "suppliers:read"
- "suppliers:write"
requiresWriteScope: true
version: "1.0.0"
ogImageEyebrow: "Skill"
faq:
- q: "Vilka filformat funkar?"
a: "PDF, JPEG, PNG, HEIC, WebP upp till 20 MB per dokument. Text-baserade PDF:er ger bäst extraktion eftersom OCR kan läsa fältnamn (Belopp, Moms, Förfallodatum). Skannade bilder fungerar men confidence är lägre."
- q: "Vad händer med Peppol-fakturor?"
a: "Peppol BIS Billing 3.0 (XML) stöds inte av `gnubok_create_supplier_invoice_from_inbox` idag. Verktyget kör OCR på dokumentet, inte XML-parsing. Native Peppol-ingestion är på roadmap i takt med EU:s VIDA-direktiv (Council Directive 2025/516, obligatoriskt 1 juli 2030 för gränsöverskridande B2B inom EU)."
- q: "Vad händer om OCR-extraktionen är fel?"
a: "Skillen visar extracted_data innan staging och frågar 'bokar in det här?'. Är något fel (säg moms-procenten lästes som 25% när det skulle vara 12%), backar du och kör en mer manuell väg via accounted-appens UI. Skillen försöker inte rätta OCR-fel automatiskt."
- q: "Vad händer om leverantören inte finns i registret?"
a: "Skillen stagar en kandidat baserat på OCR:ade säljaruppgifter (organisationsnummer om läsbart, annars namn och adress). Du klickar 'skapa leverantör' i appen och fakturan kategoriseras enligt mappingen från `gnubok_suggest_categories`."
- q: "Hur lär sig skillen mina kategoriseringspreferenser?"
a: "Den läser `gnubok_get_counterparty_templates(supplier_id: ...)` per leverantör. Har du bokfört tre fakturor från samma leverantör på konto 6212 (telefoni) kommer förslaget för nästa faktura också säga 6212. Templates uppdateras automatiskt vid varje godkännande."
---
## Stegen
Skillen lyssnar på nya items i accounted-appens inbox och processar
dem så här:
1. `gnubok_list_inbox_items(status: "unprocessed")`. Drar nya fakturor.
2. För varje item: `gnubok_get_inbox_item(item_id: ...)`. Hämtar extracted_data (OCR-resultat).
3. Extraherar säljarens organisationsnummer (om läsbart) eller namn.
4. `gnubok_list_suppliers(orgnr: ...)`. Matchar mot befintlig leverantör.
5. Vid träff: hämtar `gnubok_get_counterparty_templates(supplier_id: ...)` för standardmappning.
6. Vid miss: stagas ny leverantör för godkännande baserat på OCR-uppgifterna.
7. Mappar fakturarader till konto plus moms via `gnubok_suggest_categories`, baserat på templates.
8. `gnubok_create_supplier_invoice_from_inbox(inbox_item_id: ..., supplier_id: ..., line_mappings: [...])`. Stagas hela fakturan för godkännande.
Du klickar 'godkänn' i appen. accounted skapar verifikat, arkiverar
originalfilen som underlag, och skriver in anropet i audit-loggen.
## Vad OCR:n läser
Typiska fält som extraheras från en svensk leverantörsfaktura:
| Fält | Källa i fakturan | OCR-confidence (typisk) |
|---|---|---|
| Säljarens namn | Sidhuvudet | hög |
| Organisationsnummer | Sidfoten eller bredvid moms-info | medel — beror på fontstorlek |
| Fakturanummer | "Fakturanr" / "Invoice no" | hög |
| Fakturadatum | "Datum" / "Date" | hög |
| Förfallodatum | "Förfallodatum" / "Due date" | hög |
| Belopp exkl moms | Beloppsraden | hög |
| Momsbelopp | "Moms 25%" / "VAT" | medel — om procenten är otydlig |
| Total | Sista raden | hög |
| OCR-referens | Streckkod nederst | hög |
`extracted_data` är ett strukturerat objekt. Skillen läser de fält
den behöver för att kunna stagas ett verifikat med rätt belopp och
moms. Är fakturan från ett EU-land utan svensk moms (typ AWS, Linear)
mappar skillen mot 4535/4536 (omvänd skattskyldighet) plus
2614/2645-speglingen.
## Installera
```bash
git clone https://github.com/erp-mafia/accounted
cd gnubok/skills/leverantorsfaktura-fran-inbox
```
I Claude Desktop, Cursor eller Continue, se [accounted MCP-server](/community/registry/gnubok-mcp).
Kräver scopes `transactions:read`, `suppliers:read`, `suppliers:write`.
## Köra skillen
Manuellt:
```
@leverantorsfaktura-fran-inbox processa alla nya
```
Automatiskt via en cron-trigger i n8n eller Make:
```
n8n → Schedule (var 30:e minut) → HTTP-anrop mot gnubok MCP → skillens flöde
```
Workflow-paket finns separat i registry (kommer snart).
## Vad skillen inte gör
* Skillen hanterar inte Peppol XML idag. `gnubok_create_supplier_invoice_from_inbox` är OCR-baserad. När EU:s VIDA-direktiv närmar sig 2030 ramps native Peppol-ingestion upp.
* Skillen postar inte fakturor utan godkännande. Allt stagas, du klickar i appen.
* Skillen autoreservar inte momsavdrag för representation, bilkostnader eller resor. Sådant kräver mänsklig bedömning. Skillen flaggar och låter dig välja.
* Skillen skickar inte fakturan vidare till betalning. Det är en separat process via plusgiro/bankfil.
## Vad du får
Leverantörsfakturahantering på 30 sekunder per faktura istället för
8 minuter. Och en audit-spår där varje OCR-extraktion + bokföringsbeslut
ligger länkat: du kan svara revisorn varför något hamnade på 6541
genom att peka på leverantörstemplaten plus OCR-confidence-värdet.
@@ -0,0 +1,127 @@
---
title: "Månadsbokslut på en fika"
description: "SKILL.md-paket som kör månadsbokslut via accounted MCP. Auto-matchar transaktioner, förklarar verifikatluckor, stagar close-period. ~12 minuter på en månad med 200 transaktioner."
slug: "manadsbokslut-pa-en-fika"
kind: "skill"
author: "gnubok"
status: "live"
lang: "sv"
personas: ["founder", "byra"]
publishedAt: "2026-05-12"
updatedAt: "2026-07-18"
downloadUrl: "https://github.com/erp-mafia/accounted/releases/download/skills-v1/manadsbokslut-pa-en-fika.zip"
repoUrl: "https://github.com/erp-mafia/accounted/tree/main/skills/manadsbokslut-pa-en-fika"
gnubokTools:
- "gnubok_list_uncategorized_transactions"
- "gnubok_suggest_categories"
- "gnubok_categorize_transaction"
- "gnubok_auto_match_period"
- "gnubok_list_voucher_gaps"
- "gnubok_explain_voucher_gap"
- "gnubok_get_reconciliation_status"
- "gnubok_close_period"
oauthScopes:
- "transactions:read"
- "transactions:write"
- "reports:read"
- "bookkeeping:write"
requiresWriteScope: true
version: "1.0.0"
ogImageEyebrow: "Skill"
faq:
- q: "Vad är en SKILL.md?"
a: "Ett paketformat för agent-skills som Anthropic donerade och OpenAI antog i december 2025. En SKILL.md är en markdown-fil med instruktioner och referenser till verktyg som en LLM-agent kan ladda när uppgiften behöver det. Tvärs över Claude, Codex och andra agentmiljöer."
- q: "Hur lång tid tar ett månadsbokslut?"
a: "Beror på volym. En månad med 200 transaktioner går på ~12 minuter, varav agenten gör ~10 och du tittar igenom de svåra fallen. En månad med 30 transaktioner är ~3 minuter. Klippet ligger inte i hastigheten utan i att du inte behöver hålla i tråden."
- q: "Måste jag godkänna varje kategorisering?"
a: "Som default ja, eftersom skillen sätter `confidence_threshold: 0.85`. Allt under godkänner du i en batch i appen. Över: agenten stagar och du kan trycka 'godkänn alla' om du litar på den. Du bestämmer var ribban går."
- q: "Vad händer om bankavstämningen inte balanserar?"
a: "`gnubok_explain_voucher_gap` kallas automatiskt och returnerar en strukturerad förklaring per gap: saknad transaktion, dubblerad inläsning, fel datum. Agenten skickar den till dig som en lista, inte som en lång prosaförklaring."
- q: "Funkar det för byråer med många klienter?"
a: "Idag krävs en MCP-anslutning per klient. En API-nyckel (eller OAuth-grant) är bunden till ett företag. Multi-tenant via `companies:read`-scopen finns på roadmap för v1 REST API men exponeras inte i MCP än. För en byrå med 23 klienter blir det 23 connectors i Claude eller 23 API-nycklar i stdio-fallback."
---
## Stegen i ordning
Skillen är ett SKILL.md-paket plus en liten resurs-bundle. Den
orkestrerar åtta MCP-anrop i ordning, hanterar fallback per steg, och
stagar allt skriv för mänskligt godkännande.
1. `gnubok_list_uncategorized_transactions(period: "2026-04")`. Drar de okategoriserade.
2. `gnubok_suggest_categories(transaction_ids: [...])`. Får förslag med confidence score.
3. För varje förslag över tröskeln: `gnubok_categorize_transaction(...)` (stagas).
4. `gnubok_auto_match_period(period: "2026-04")`. Matchar transaktioner mot fakturor.
5. `gnubok_list_voucher_gaps(period: "2026-04")`. Listar luckor.
6. För varje lucka: `gnubok_explain_voucher_gap(gap_id: ...)`. Strukturerad förklaring.
7. `gnubok_get_reconciliation_status(period: "2026-04")`. Sista checken.
8. Om allt är grönt: `gnubok_close_period(period: "2026-04", dry_run: true)`. Stagas i appen.
Allt skriv stagar `pending_operation`. Inget postas innan du godkänner.
## Installera
Ladda ner ZIP-paketet ovan, eller klona repot:
```bash
git clone https://github.com/erp-mafia/accounted
cd gnubok/skills/manadsbokslut-pa-en-fika
```
I Claude Desktop:
```
Cmd-Shift-P → Skills: Add from folder → välj mappen
```
I Codex:
```
codex skill add ./manadsbokslut-pa-en-fika
```
Skillen kräver att accounted MCP är ansluten, se
[accounted MCP-server](/community/registry/gnubok-mcp).
## Köra skillen
```
@manadsbokslut-pa-en-fika kör för april 2026
```
Agenten kommer:
* Visa antalet okategoriserade poster (säg 47).
* Föreslå kategoriseringar med confidence per post.
* Stagas i appen (du ser dem under "Väntar på godkännande").
* Köra auto-match och visa antalet matchningar.
* Förklara varje lucka i klartext.
* Stagas close-period som dry-run så du kan jämföra resultaträkning mot förra månaden.
Du godkänner allt i ett svep.
## Konfigurera tröskeln
`SKILL.md`-paketet exponerar två parametrar:
```yaml
confidence_threshold: 0.85
explain_voucher_gaps: true
```
Sätter du tröskeln lägre kategoriseras mer automatiskt men du
godkänner fortfarande allt. Tröskeln styr vad agenten *föreslår*, inte
vad som postar utan godkännande.
## Begränsningar
* Skillen postar ingen riktig period-stängning. `dry_run: true` stagar bara förändringarna.
* Skillen rättar inte fel i tidigare perioder. Använd `gnubok_correct_entry` för det. Den skapar en rättelsepost, inte en redigering.
* Skillen tolkar inga kvitton. Använd inbox + [Leverantörsfaktura från inbox](/community/registry/leverantorsfaktura-fran-inbox) för det.
* Skillen jobbar på ett företag i taget. För byrå-volym, kör skillen separat per klient med deras MCP-anslutning.
## Resultatet
En månadsstängning som tar lika lång tid som att dricka kaffet
någon annan brygger åt dig. Och en audit-logg som faller ut i
`gnubok_audit_package`-zippen, samma data du visar revisorn när
hen kommer.
@@ -0,0 +1,161 @@
---
title: "Periodiseringsfond — strategi och bokning för AB:s årsbokslut"
description: "SKILL.md som beräknar maximal periodiseringsfond (25% av skattemässigt resultat före avsättning för AB), beräknar skatteeffekt med 20.6% bolagsskatt, och stager gnubok_create_voucher med 8811 mot 21252127 (sjuårsserien)."
slug: "periodiseringsfond-strategi"
kind: "skill"
author: "gnubok"
status: "live"
lang: "sv"
personas: ["founder"]
publishedAt: "2026-05-12"
updatedAt: "2026-07-18"
downloadUrl: "https://github.com/erp-mafia/accounted/releases/download/skills-v1/periodiseringsfond-strategi.zip"
repoUrl: "https://github.com/erp-mafia/accounted/tree/main/skills/periodiseringsfond-strategi"
gnubokTools:
- "gnubok_get_income_statement"
- "gnubok_year_end_readiness"
- "gnubok_create_voucher"
- "gnubok_query_journal"
oauthScopes:
- "reports:read"
- "bookkeeping:write"
requiresWriteScope: true
version: "1.0.0"
ogImageEyebrow: "Skill"
faq:
- q: "Borde jag verkligen göra avsättning om jag förväntar mig lägre vinst nästa år?"
a: "Beror på likviditeten. Periodiseringsfonden flyttar skatt 6 år framåt. Om du förväntar dig att ha lägre eller ingen vinst nästa år har avsättningen särskilt värde eftersom återföringen då beskattas lägre (eller noll). Skillen visar break-even-året: vid vilken framtida resultatnivå blir avsättningen lönsam eller olönsam."
- q: "Vilken serie kontonummer ska jag använda, 2125, 2126 eller 2127?"
a: "BAS 2026 har sjuårsserie 21252131, ett konto per avsättningsår. För räkenskapsår 2025 (kalenderår 2025) används 2125. För 2026 blir det 2126. Skillen läser fiskalperiodens slutdatum från accounted och väljer rätt konto automatiskt. Återföring sker mot samma konto, så 2125 från 2025 återförs senast år 2031."
- q: "Kan jag återföra periodiseringsfonden frivilligt om jag har förlust?"
a: "Ja, alltid. Återföring i förlustår är en av huvudfördelarna: du minskar förlusten och kan kvitta mot framtida vinster med högre marginal. Skillen detekterar 2125-saldon vid årsbokslut och föreslår återföring om årets resultat är negativt. Bokning: 2125 mot 8811 (i samma år, motsatt riktning)."
- q: "Vad händer med periodiseringsfonden om bolaget likvideras?"
a: "Vid likvidation måste hela kvarvarande periodiseringsfond återföras till beskattning i likvidationsårets resultat. Det är skattepliktigt. Det finns ingen möjlighet att 'rulla över' till ägarna. Sätter du periodiseringsfond bör du ha en plan för hur den ska användas innan likvidation blir aktuell."
- q: "Räknas periodiseringsfond mot K10 eller är det enbart bolagsfråga?"
a: "Periodiseringsfond ligger på bolaget, inte på ägaren. K10 påverkas indirekt: lägre redovisat resultat ett enskilt år ger lägre lönebaserat utrymme för fåmansbolagsutdelning de kommande åren. En faktor i bedömningen, inte ett knockout-skäl. Skillen visar K10-effekten som sidoinformation om bolaget är fåmansbolag."
---
## Sex steg
Periodiseringsfond är AB:s enda återstående tidsförskjutning av
bolagsskatt. Skillen läser årets resultat, beräknar maximal
avsättning, jämför skatteeffekt mellan avsättning och icke-avsättning,
och stager bokningen om du godkänner.
1. `gnubok_year_end_readiness(period: "2025")`. Kollar att perioden är klar för bokslut.
2. `gnubok_get_income_statement(period: "2025")`. Drar årets resultat före skatt.
3. Beräknar `max_avsattning = resultat_före_skatt × 0.25`.
4. Beräknar skatteeffekt vid avsättning vs ej: skillnaden är `max_avsattning × 0.206` SEK i likviditet.
5. Visar break-even-året (vid vilken framtida resultatnivå avsättningen blir olönsam).
6. Stager `gnubok_create_voucher(date: 2025-12-31, lines: [debet 8811 X kr, kredit 2125 X kr])`.
Inget skrivs utan godkännande. Skillen visar konsekvensen, du
bestämmer om den ska göras.
## Beräkningen
För räkenskapsår 2025 med resultat före skatt 480 000 SEK:
```
Max avsättning: 480 000 × 0.25 = 120 000 SEK
Bolagsskatt utan: 480 000 × 0.206 = 98 880 SEK
Bolagsskatt med: 360 000 × 0.206 = 74 160 SEK
Likviditet kvar: 24 720 SEK extra i bolaget
```
24 720 SEK stannar i bolaget i upp till 6 år. Vid återföring år
2031 (om inte tidigare) beskattas det med då gällande skattesats.
Är skattesatsen densamma (20.6%) blir effekten av periodiseringen
en räntefri kredit på 24 720 SEK i 6 år.
För räntenivå 5% är värdet av 6 års räntefri kredit på 24 720 SEK
ungefär 6 600 SEK i nuvärde. Även med konstant skatt är
periodiseringen lönsam, bara mindre dramatisk än "spara 24 720 SEK".
## Sjuårsserien
BAS 2026 har en separat konto per avsättningsår:
| Konto | Avsättningsår | Senaste återföring |
|---|---|---|
| 2125 | 2025 | 2031 |
| 2126 | 2026 | 2032 |
| 2127 | 2027 | 2033 |
| 2128 | 2028 | 2034 |
| 2129 | 2029 | 2035 |
| 2130 | 2030 | 2036 |
| 2131 | 2031 | 2037 |
Detta är inte bara kosmetik. Skatteverket kräver att man kan visa
vilket år en specifik avsättning gjorts, för att kunna räkna
6-årsfönstret. Använder du fel konto blir det rättelseår.
## Installera
```bash
git clone https://github.com/erp-mafia/accounted
cd gnubok/skills/periodiseringsfond-strategi
```
I Claude Desktop:
```
Cmd-Shift-P → Skills: Add from folder → välj mappen
```
Kräver `reports:read` (för läsning) och `bookkeeping:write` (för att
stagas bokningsförslag).
## Köra skillen
```
@periodiseringsfond-strategi för 2025
```
Agenten:
```
Årets resultat: 480 000 SEK
Max avsättning: 120 000 SEK (25%)
Likviditetseffekt: 24 720 SEK
Break-even-vinst: 240 000 SEK år 2031
Föreslagen bokning:
Debet 8811 Avsättning periodiseringsfond 120 000
Kredit 2125 Periodiseringsfond år 2025 120 000
Datum: 2025-12-31
Vill du stagas? (yes / partial / no)
```
Skillen tar emot `partial 60000` om du vill avsätta mindre än maxbeloppet.
## Återföringsbeslut
Skillen detekterar gamla 2125-saldon vid varje årsbokslut. Vid
förlustår föreslår den återföring för att kvitta. Bokning för
återföring:
```
Debet 2125 Periodiseringsfond år 2025 120 000
Kredit 8811 Avsättning periodiseringsfond 120 000
Datum: bokslutsdatum (t.ex. 2027-12-31)
```
Återföring är frivillig. Du kan vänta hela 6-årsfönstret. Men i
år 7 (2031 för 2025-avsättningen) är återföring obligatorisk.
## Begränsningar
* Skillen gör ingen skatteplanering över bolagsstrukturer. Den jobbar bara med ett bolags egen kontoplan.
* Skillen tar inte hänsyn till outnyttjat förlustavdrag. Om bolaget har gamla förluster ska man kanske inte avsätta alls. Det är revisor-bedömning.
* Skillen är inte K10-skattare. Den noterar K10-effekten på fåmansbolag men beräknar inte själva K10-deklarationen.
* Skillen avsätter inte automatiskt. Bara förslag, alltid staged, du godkänner.
## Vad du får
En beräkning på 5 minuter som annars hamnar på revisorns bord till
2 500 kr/timme. Och en bokföring där periodiseringsfondsärenden går
in i samma audit-spår som allt annat. När revisor nästa år frågar
"varför hamnade 120 000 på 2125 i december?", svaret finns på rad i
`gnubok_audit_package`-zippen: vem, när, scope, before/after.
+120
View File
@@ -0,0 +1,120 @@
---
title: "Revisorpaket — SIE4 + audit-logg på schema"
description: "n8n- och Make-workflow som exporterar SIE4 plus audit-logg till revisorn varje månadsslut. Cron, signerad e-post, kvitto i Slack. Read-only, ingen huvudboksmutation."
slug: "revisorpaket-sie"
kind: "workflow"
author: "gnubok"
status: "live"
lang: "sv"
personas: ["byra", "finance"]
publishedAt: "2026-05-12"
updatedAt: "2026-07-18"
downloadUrl: "https://github.com/erp-mafia/accounted/releases/download/workflows-v1/revisorpaket-sie.zip"
repoUrl: "https://github.com/erp-mafia/accounted/tree/main/workflows/revisorpaket-sie"
gnubokTools:
- "gnubok_export_sie"
- "gnubok_audit_package"
- "gnubok_query_journal"
- "gnubok_get_trial_balance"
oauthScopes:
- "reports:read"
requiresWriteScope: false
sieCompatible: true
version: "1.0.0"
ogImageEyebrow: "Workflow"
faq:
- q: "Vad innehåller paketet revisorn får?"
a: "Det som `gnubok_audit_package` paketerar: SIE-4-fil för perioden, balans- och resultaträkning som PDF, huvudboken per konto, audit-logg som CSV (vem rörde vad när, scope, before/after), kvitto-arkiv och voucher-gap-rapport per BFNAR 2013:2. Allt zippat med 1-timmes signed URL från Supabase Storage."
- q: "Varför audit-logg och inte bara SIE?"
a: "Bokföringslagen kräver att åtgärder ska kunna rekonstrueras. SIE4 visar slutresultatet, inte vägen dit. Audit-loggen visar vägen: vilka rättelseposter som skedde när, av vem, med vilken scope. Revisorn behöver båda för en ren granskning."
- q: "Kan jag köra workflowet utan n8n?"
a: "Ja. Make-blueprinten är likvärdig. Båda anropar samma accounted-MCP-verktyg via HTTP. Du kan också köra dem som ett standalone-script om du föredrar det. Kolla repot."
- q: "Hur signeras e-posten?"
a: "Workflowet använder DKIM via Resend (eller din egen SMTP). Om revisorn vill ha S/MIME finns ett tillägg i repot. Det kräver ett signing-cert från till exempel Buypass."
- q: "Vad händer om en period inte är låst?"
a: "Workflowet stoppar och skickar en larm-Slack istället. Det är medvetet: du vill inte skicka en SIE-fil för en period som fortfarande ändras under granskningen."
---
## Workflowet
På schemat (default: första vardagen varje månad kl 08:00):
1. Hämtar föregående periods audit-paket via `gnubok_audit_package(period: "2026-04")`. Returnerar signed URL till en zip i Supabase Storage.
2. Laddar ner zippen via HTTP node.
3. Hämtar balans- och resultaträkning via `gnubok_get_trial_balance` om du vill ha en separat PDF utöver det som ligger i zippen.
4. Räknar SHA256 på SIE-filen och skapar ett kvitto.
5. Skickar paketet till revisorns e-postadress, plus en bekräftelse i din Slack.
Om en period inte är låst stoppas hela kedjan och du får en Slack-larm
istället.
Workflowet är read-only på huvudboken. `gnubok_audit_package` skriver
visserligen en zip-fil till Storage, men det är en sidoeffekt av att
producera rapporten. Ingen huvudboksmutation, inget `pending_operation`
att godkänna.
## Importera i n8n
```bash
# Ladda ner zip från registry, packa upp
n8n import:workflow --input=revisorpaket-sie.json
```
Eller via UI:
```
n8n → Workflows → Import → välj revisorpaket-sie.json
```
Du behöver konfigurera tre credentials:
* **accounted MCP-anslutning** med scope `reports:read`. För n8n som inte ännu stödjer MCP-OAuth direkt, använd stdio-fallback (API-nyckel via `npx gnubok-mcp` eller direkt REST mot accounted-API).
* **SMTP eller Resend API-nyckel**.
* **Slack webhook** (valfritt).
## Importera i Make
Make-blueprinten ligger som `revisorpaket-sie.blueprint.json` i ZIP-paketet.
```
Make → My Blueprints → Import → välj filen
```
Samma tre credentials behövs.
## Konfigurera schema
n8n-cron-noden är konfigurerad till `0 8 1-7 * 1` (första måndagen
varje månad kl 08:00). Justera om du föredrar månadens första vardag
oavsett veckodag.
## Innehållet i paketet
```
gnubok-2026-04.zip
├── 2026-04.sie # SIE4-fil
├── audit-log.csv # Vem rörde vad när
├── huvudbok.pdf # Alla rader per konto
├── balans-och-resultat.pdf
├── voucher-gaps.csv # BFNAR 2013:2 gap-rapport
├── kvitton/ # Bilagor till verifikat
└── README.txt
```
`audit-log.csv` är delen byråer brukar reagera positivt på.
Den ser ut så här:
| ts | actor | scope | verifikation | before | after |
|---|---|---|---|---|---|
| 2026-04-03 09:12:44 | jakob@... | (UI) | V2026-401 | — | created |
| 2026-04-04 14:22:01 | claude-mcp-abc | transactions:write | V2026-411 | uncategorized | konto 5410 |
Tabellen är hur Skatteverket vill ha det när online-revisionen
öppnar 1 april 2026. Ingen revisor i Sverige har sett formatet förr.
Skicka det till din egen revisor först.
## Begränsningar
* Workflowet postar inte filerna till någon publik plats. Du måste konfigurera mottagar-adressen.
* Det räknar inte ut moms eller bokslut åt dig. För det, se [Månadsbokslut på en fika](/community/registry/manadsbokslut-pa-en-fika).
* Det följer inte upp om revisorn faktiskt öppnar paketet. Lägg gärna till en Litmus- eller Mailtrack-pixel om du vill det, men kolla GDPR först.
+142
View File
@@ -0,0 +1,142 @@
---
title: "Runway-dashboard från huvudboken, dagligen"
description: "SKILL.md som beräknar runway varje morgon från cash_position i gnubok_get_kpi_report plus en 90-dagars kostnad/intäkts-summa via gnubok_query_journal. Postar i Slack med veckotrend."
slug: "runway-fran-huvudboken"
kind: "skill"
author: "gnubok"
status: "live"
lang: "sv"
personas: ["founder"]
publishedAt: "2026-05-12"
updatedAt: "2026-07-18"
downloadUrl: "https://github.com/erp-mafia/accounted/releases/download/skills-v1/runway-fran-huvudboken.zip"
repoUrl: "https://github.com/erp-mafia/accounted/tree/main/skills/runway-fran-huvudboken"
gnubokTools:
- "gnubok_get_balance_sheet"
- "gnubok_get_kpi_report"
- "gnubok_query_journal"
- "gnubok_get_income_statement"
oauthScopes:
- "reports:read"
requiresWriteScope: false
version: "1.0.0"
ogImageEyebrow: "Skill"
faq:
- q: "Vad räknas som 'burn', räknar du in periodiseringar?"
a: "Skillen räknar burn som rena utflöden, inte bokföringsmässiga periodiseringar. Konkret: klass 5 (kostnader för varor och tjänster), klass 6 (övriga externa kostnader), klass 7 (personal) minus klass 3 (intäkter). Avskrivningar (7831, 7832) ingår eftersom de speglar kapitalförbrukning. Avsättningar och kalkylmässiga räntor exkluderas, de är inga utflöden av kassa."
- q: "Är runway-siffran dagsfärsk?"
a: "Beroende på bankfeeden. PSD2-feeden via Tink uppdaterar 1930-saldot var 4:e timme. Skillen läser timestamp på senaste tx från `gnubok_query_journal(account: '1930', limit: 1)` och varnar i sin output om feed-datat är äldre än 24 timmar."
- q: "Kan jag få en alert när runway går under 6 månader?"
a: "Ja, men inte i själva skillen. Koppla den till [daglig-runway-till-slack workflow](/community/registry/daglig-runway-till-slack). Workflowet jämför mot tröskel och larmar via Slack-block. Default-tröskel: 6 mån = orange, 3 mån = röd. Konfigurerbart per företag."
- q: "Vad är skillnaden mellan denna och `gnubok_get_kpi_report`?"
a: "`gnubok_get_kpi_report` returnerar bland annat `cash_position`, `total_revenue`, `total_expenses` och `months[]` (rullande snapshots). Skillen läser dessa och beräknar runway själv (cash_position dividerat med snittburn över 90 dagar). Det finns ingen färdig `runway_months`-fält idag, skillen är den som komponerar siffran."
- q: "Funkar det för bolag med flera bankkonton?"
a: "Ja. Skillen läser alla konton i klass 19 (1910 kassa, 1920 plusgiro, 1930 företagskonto, 1940 specialinlåning, 1950 utländsk valuta etc.) via balansräkningen. Du kan via konfiguration exkludera spärrade konton eller utländska konton som inte ska räknas som tillgänglig likviditet."
---
## Anropen
Runway är basal för varje grundare som inte är cash-flow positiv,
men kalkylen kräver dagsfärsk data och korrekt avgränsning.
Skillen kör fyra anrop, alla läsande:
1. `gnubok_get_balance_sheet(date: today)`. Drar saldon för klass 19 (kassa) plus 18 (kortfristiga placeringar).
2. `gnubok_get_kpi_report`. Returnerar `cash_position`, `total_revenue`, `total_expenses`, `months[]`, plus relaterade KPI-fält.
3. `gnubok_get_income_statement(period_start: today - 90d, period_end: today)`. Sammanställd 90-dagars resultaträkning.
4. `gnubok_query_journal(accounts_class: [3, 5, 6, 7], period_start: today - 90d)`. Verifierar att intäkter ligger på 3xxx och kostnader på 5xxx/6xxx/7xxx.
Formeln:
```
kassa = sum(saldo[1910..1979], 1810..1819)
(eller cash_position från kpi_report)
burn_90d = sum(klass_5_7) - sum(klass_3) över senaste 90 dagar
burn_per_manad = burn_90d / 90 * 30.4
runway = kassa / burn_per_manad
```
Skillen beräknar `runway` lokalt. Accounteds KPI-rapport returnerar inte
en färdig `runway_months`-siffra idag, bara råmaterialet.
## Kontoplan-mappning
| Område | BAS-konto | Notering |
|---|---|---|
| Kassa | 1910 (kontant), 1920 (plusgiro), 1930 (företagskonto), 1940 (specialinlåning) | Klass 19 hela |
| Kortfristiga placeringar | 18101819 | Likvida medel, läggs till kassa |
| Försäljning | 3001, 3002, 3041 | Klass 3, dras av från burn |
| Inköp / direkta kostnader | 4xxx | Klass 4 räknas in (varuinköp) |
| Övriga externa kostnader | 5xxx, 6xxx | Klass 56 hela |
| Personal | 7010, 7090, 7210, 7280, 7330, 7385, 7410, 7510 | Klass 7 utom avskrivningar |
| Avskrivningar | 7831, 7832 | Räknas in (kapitalförbrukning) |
| Räntor och finansiella poster | 8313, 8410 | EJ med i burn, finansiering räknas separat |
För enskild firma och kommanditbolag används BAS 2026 sole-trader-mappning.
Skillen detekterar bolagsform via `gnubok_list_accounts` och anpassar.
## Installera
```bash
git clone https://github.com/erp-mafia/accounted
cd gnubok/skills/runway-fran-huvudboken
```
I Claude Desktop:
```
Cmd-Shift-P → Skills: Add from folder → välj mappen
```
Kräver bara `reports:read`. Ingen skriv-scope behövs. En API-nyckel
eller OAuth-grant utan write-scopes räcker.
## Köra skillen
Manuellt:
```
@runway-fran-huvudboken visa läget
```
Agenten svarar:
```
Kassa: 1 247 380 SEK (1930: 1 198 220 + 1810: 49 160)
Burn 90d: - 412 800 SEK (kostnader 612k intäkter 199k)
Burn / månad: 137 600 SEK
Runway: 9.06 månader
Trend: ↓ 0.4 mån sedan förra veckan (kassa -52k, burn ±)
```
Frågar du "varför sjönk det?" går skillen tillbaka till
`gnubok_query_journal` med detaljnivå och returnerar de tre största
posterna sedan förra körningen.
## Automatiserad körning
Schemalägg via [daglig-runway-till-slack workflow](/community/registry/daglig-runway-till-slack).
Samma motor men paketerad som n8n-cron plus Slack-post.
Eller skapa egen cron-trigger som anropar skillen och postar svaret
till valfri kanal.
## Inget data warehouse behövs
Du behöver inte ett data warehouse för det här. Skillen läser
samma data som en revisor läser, huvudboken, med dagsfärska tx
från PSD2. Det är inte aggregerade analytics-data; det är
deterministiska konto-saldon vid ett givet ögonblick.
Därför funkar skillen utan ETL-pipeline, utan månads-batch, utan dbt.
Fyra anrop mot en open ledger med en bankuppdatering i botten.
## Sånt skillen inte gör
* Skillen förutspår inte framtida burn. Den läser historiskt 90-dagars-snitt. Vill du ha forecast-modell, koppla skillen till en separat verktygskedja (t.ex. Vercel AI SDK plus skillens output).
* Skillen jämför inte mot peers eller benchmark. Bara mot din egen historik.
* Skillen tar inte hänsyn till commitments, alltså kommande inflöden från fakturor som inte gått ut än, eller framtida payouts som är konfirmerade men inte träffat banken.
* Skillen skickar inte alerts. Det gör workflowet, eller du själv när du läser siffran.
## Vad du får
En runway-siffra du kan lita på, dagligen, beräknad mot rätt
BAS-konton, med en trend och en orsak. Inte ett estimat. En mätning.
@@ -0,0 +1,120 @@
---
title: "Arbetsgivardeklaration på individnivå — AGI som SKILL"
description: "SKILL.md som tar en godkänd löneberäkning och genererar AGI-fil enligt Skatteverkets schema via gnubok_generate_agi. Producerar XML klar för manuell uppladdning i Skatteverkets e-tjänst."
slug: "skatteverket-arbetsgivardeklaration"
kind: "skill"
author: "gnubok"
status: "live"
lang: "sv"
personas: ["byra", "finance", "founder"]
publishedAt: "2026-05-12"
updatedAt: "2026-07-18"
downloadUrl: "https://github.com/erp-mafia/accounted/releases/download/skills-v1/skatteverket-arbetsgivardeklaration.zip"
repoUrl: "https://github.com/erp-mafia/accounted/tree/main/skills/skatteverket-arbetsgivardeklaration"
gnubokTools:
- "gnubok_list_employees"
- "gnubok_create_salary_run"
- "gnubok_calculate_salary_run"
- "gnubok_get_salary_run"
- "gnubok_get_salary_journal"
- "gnubok_generate_agi"
oauthScopes:
- "payroll:read"
- "payroll:write"
requiresWriteScope: true
version: "1.0.0"
ogImageEyebrow: "Skill"
faq:
- q: "Vilket AGI-format genererar skillen?"
a: "`gnubok_generate_agi` returnerar en download-länk till AGI-XML enligt Skatteverkets schema, sparat under `/api/salary/runs/{id}/agi/xml`. Filen följer formatet för arbetsgivardeklaration på individnivå."
- q: "Hanterar skillen förmåner och traktamenten?"
a: "Skillen orkestrerar `gnubok_get_salary_journal` och `gnubok_generate_agi` på en lönerunde. Lönerundans posttyper (bil, kost, bostad, traktamente, pension) hanteras av accounted-backend, inte av skillen själv. Du verifierar i appen att rätt typ är satt per lönepost innan AGI-genereringen körs."
- q: "Vad krävs för att lämna in?"
a: "Skillen producerar bara XML-filen. Själva uppladdningen sker manuellt i Skatteverkets e-tjänst med BankID eller SUS-certifikat. Skatteverket har ingen officiell MCP-bro idag, och accounteds egen Skatteverket-integration är en intern REST-tjänst (inte MCP-exponerad)."
- q: "Kostar det något extra att köra över byråns klienter?"
a: "Skillen kör mot ett företag i taget via en accounted-MCP-anslutning. För en byrå med flera klienter krävs en anslutning per klient. Multi-tenant via `companies:read` är på roadmap men inte i MCP än."
- q: "Vad händer om Skatteverket avvisar filen?"
a: "Du laddar upp den manuellt och får ett felmeddelande från Skatteverket. Vanligaste avslagen: ofullständiga personnummer, negativa belopp på fält som kräver positiva, validering mot förra periodens uppgifter. Skillen försöker inte rätta automatiskt. Du rättar i accounted och genererar AGI på nytt."
---
## Stegen
Skillen tar en månads godkänd löneberäkning och producerar AGI-XML
i Skatteverkets format. Stegen är idempotenta:
1. `gnubok_get_salary_run(period: "2026-04")`. Drar lönerundans status.
2. Validerar att rundan är godkänd och journalförd. Annars stoppar och säger varför.
3. `gnubok_get_salary_journal(salary_run_id: ...)`. Hämtar alla löneposter per anställd.
4. Visar sammanfattning per anställd: brutto, skatteavdrag, förmåner, traktamenten.
5. `gnubok_generate_agi(salary_run_id: ...)`. Returnerar download-URL till AGI-XML.
6. Returnerar filsökväg plus en kort kontrollista innan manuell upload.
Den producerade filen laddas upp i Skatteverkets e-tjänst:
[skatteverket.se/etjanster/arbetsgivardeklaration](https://skatteverket.se/etjanster/arbetsgivardeklaration).
Skatteverket har ingen officiell MCP-yta för AGI-inlämning. accounteds
egen Skatteverket-extension är aktiv som intern REST-tjänst men
exponerar inte tools över MCP idag.
## Konto- och AGI-mappning
Lönerundan i accounted använder standardvärdena från BAS 2026:
| Lönepost | AGI-fält (typiskt) | Konto-debet | Konto-kredit |
|---|---|---|---|
| Bruttolön | Ersättning | 7210 | 2710 |
| Bilförmån | Bilförmån | 7385 | 2710 |
| Bostadsförmån | Bostadsförmån | 7384 | 2710 |
| Kostförmån | Kostförmån | 7382 | 2710 |
| Traktamente över schablon | Skattepliktig del | 7321 | 2710 |
| Pensionsförsäkringspremie | Pension | 7410 | 2710 |
| Sjuklön | Ersättning | 7282 | 2710 |
| Skatteavdrag | Avdragen skatt | 2710 | 2710 |
| Arbetsgivaravgift 31.42% | (rapporteras separat) | 7510 | 2731 |
Mappningen sker i accounted-backend när lönerundan beräknas, inte i
skillen. Skillen läser bara journalen och triggar AGI-export.
## Installera
```bash
git clone https://github.com/erp-mafia/accounted
cd gnubok/skills/skatteverket-arbetsgivardeklaration
```
I Claude Desktop:
```
Cmd-Shift-P → Skills: Add from folder → välj mappen
```
Kräver att [accounted MCP](/community/registry/gnubok-mcp) är ansluten med
scopes `payroll:read`, `payroll:write`.
## Köra skillen
```
@skatteverket-arbetsgivardeklaration generera AGI för april 2026
```
Agenten:
1. Listar antalet anställda i rundan (säg 8).
2. Visar fördelningen mellan löneslag.
3. Genererar AGI-XML.
4. Returnerar download-URL.
5. Visar sammanfattning per anställd så du kan korsa-checka mot lönespec innan upload.
## Sånt skillen inte gör
* Skillen laddar inte upp filen till Skatteverket. Det sker manuellt i Skatteverkets e-tjänst (BankID eller SUS-certifikat).
* Skillen räknar inte ut själva lönerundan. Använd `gnubok_calculate_salary_run` först.
* Skillen hanterar inte F-skatt-anmälan eller momsregistrering. Andra processer.
* Skillen producerar inte KU-uppgifter (kontrolluppgifter på årsbasis). De är en separat process.
## Om Skatteverket avvisar filen
Vanligaste avslagen och vad de betyder:
* **"Felaktig version på schema"**: AGI-schemat uppdateras med jämna mellanrum. Skillen följer den utgåva som accounted-backend producerar.
* **"Personnummer matchar inte"**: någon i lönerundan har personnummer i fel format. Kör `gnubok_list_employees` för att fånga det innan AGI-genereringen.
* **"Negativt belopp"**: betyder att lönerundan har en återbetalning. AGI tillåter inte negativa belopp på vissa fält. Bokför som korrigering i nästa period istället.
@@ -0,0 +1,76 @@
---
title: "swedish-accounting-compliance — BFL, BFNAR och K2/K3 som SKILL"
description: "Compliance-orakel för svensk bokföringslagstiftning. Täcker BFL, BFNAR, BAS-kontoplan, K2/K3, momsdeklaration, bokföringsskyldighet, verifikationer, löpande bokföring, årsbokslut. Agentkunskap för utvecklare som bygger bokföringsmjukvara mot Sverige."
slug: "swedish-accounting-compliance"
kind: "skill"
author: "erp-mafia"
status: "live"
lang: "sv"
personas: ["developer", "byra", "finance"]
publishedAt: "2026-05-12"
updatedAt: "2026-07-18"
repoUrl: "https://github.com/erp-mafia/swedish-accounting-skills/tree/main/.claude/skills/swedish-accounting-compliance"
externalUrl: "https://github.com/erp-mafia/swedish-accounting-skills"
version: "community"
ogImageEyebrow: "Skill"
faq:
- q: "Vad är detta för skill?"
a: "En SKILL.md som ger en LLM-agent djup kunskap om svensk bokföringslagstiftning. Den triggas automatiskt när agenten ser BFL, BFNAR, BAS, SIE4, K2/K3, momsdeklaration, verifikationer, bokföringsskyldighet eller liknande i prompten. Skillen är ett orakel, inte en slutanvändarguide."
- q: "Hur skiljer den sig från accounted-skills?"
a: "Accounted-skills (typ månadsbokslut-pa-en-fika) orkestrerar verktyg mot en specifik huvudbok. Den här skillen ger agenten kunskap om regelverket innan den fattar beslut. De kompletterar varandra: kör båda för bästa output."
- q: "Använder skillen accounted?"
a: "Nej. Den är compliance-kunskap, inte produktintegration. Funkar lika bra för en utvecklare som bygger eget bokföringssystem, en agent som granskar bokföring i godtyckligt verktyg, eller en byrå som vill ha agentstöd vid lagrumstolkning."
---
## Vad skillen täcker
* Bokföringslagen (BFL, SFS 1999:1078): bokföringsskyldighet, räkenskapsår, verifikationer, löpande bokföring, arkivering 7 år, SFS 2024:342 (digital arkivering).
* Bokföringsnämndens allmänna råd (BFNAR): 2013:2 (Bokföring), 2016:10 (K2 Årsredovisning i mindre företag), 2012:1 (K3 Årsredovisning och koncernredovisning).
* BAS-kontoplanen: klassindelning, standardkonton, branschanpassningar, BAS 2026-uppdateringar.
* Momsdeklaration: rutornas mappning till BAS 26xx, EU-handel, reverse charge.
* Verifikationer: vad som krävs per BFL 5 kap, vilka underlag (kvitto/faktura), original vs digital kopia.
Skillen är ett orakel som agenten konsulterar vid behov, inte ett
batterier-inkluderat-flöde. Den paras vanligen med en mer
operationell skill (typ [månadsbokslut-pa-en-fika](/community/registry/manadsbokslut-pa-en-fika))
som faktiskt utför arbete.
## Installera
```bash
git clone https://github.com/erp-mafia/swedish-accounting-skills.git
cd swedish-accounting-skills
claude
```
Skillen blir tillgänglig automatiskt i Claude Code när du jobbar
inne i mappen.
För att lägga till i ett befintligt projekt:
```bash
cp -r path/to/swedish-accounting-skills/.claude/skills/swedish-accounting-compliance \
./.claude/skills/
```
För Claude Desktop eller claude.ai: Settings → Customize (eller
Settings → Capabilities → Skills) och lägg till skill-filerna där.
Kräver att Code execution är aktiverat under Settings → Capabilities.
## Var skillen pluggar in
Triggar automatiskt på frågor som "är detta compliant?", "vad säger
BFL om X?", "vilka verifikat krävs för Y?", "K2 eller K3?", och
liknande. När agenten ser BFL, BFNAR, BAS-kontoplan, SIE4, K2/K3,
momsdeklaration, verifikationer, löpande bokföring eller
bokföringsskyldighet i kontexten laddar den skillen och svarar mot
regelverket istället för sin generella träningsdata.
Hög träffsäkerhet på saker som annars hamnar i hallucinationszonen:
exakta BFL-paragrafer, BFNAR-rådens scope, BAS-kontonas semantik per
klass, kraven på verifikationsserie och löpande nummerering.
## Licens
MIT. Maintainat av erp-mafia-communityn. Pull requests välkomna i
[swedish-accounting-skills-repot](https://github.com/erp-mafia/swedish-accounting-skills).
@@ -0,0 +1,63 @@
---
title: "swedish-asset-accounting — anläggningstillgångar och avskrivningar"
description: "SKILL.md för svensk anläggningsredovisning. Planenlig avskrivning, överavskrivning (2150/8850), räkenskapsenlig 30%/20% vs restvärdeavskrivning 25%, BAS 10xx12xx/78xx-mapping, inventarieregister per BFL, förbrukningsinventarier (halva PBB), leasing K2/K3/IFRS 16, komponentavskrivning, avyttring med moms."
slug: "swedish-asset-accounting"
kind: "skill"
author: "erp-mafia"
status: "live"
lang: "sv"
personas: ["finance", "byra", "developer"]
publishedAt: "2026-05-12"
updatedAt: "2026-08-08"
repoUrl: "https://github.com/erp-mafia/swedish-accounting-skills/tree/main/.claude/skills/swedish-asset-accounting"
externalUrl: "https://github.com/erp-mafia/swedish-accounting-skills"
version: "community"
ogImageEyebrow: "Skill"
faq:
- q: "När laddas skillen automatiskt?"
a: "Vid frågor om avskrivning, anläggningstillgång, överavskrivning, inventarieregister, förbrukningsinventarier, BAS 10xx12xx eller 78xx, leasing K2/K3, avyttring/utrangering, komponentavskrivning, eller liknande. Skillen flaggar att området har hög felfrekvens i träningsdata och alltid bör konsulteras istället för LLM:ens egen kunskap."
- q: "Vad är skillnaden mellan räkenskapsenlig och restvärdeavskrivning?"
a: "Räkenskapsenlig: skattemässigt 30% av kvarvarande värde + tillkommande inventarier (huvudregeln), eller 20% av anskaffningsvärdet (kompletteringsregeln). Restvärdeavskrivning: 25% av kvarvarande värde, ingen koppling till räkenskaperna. Skillen förklarar när vilken metod är tillåten och hur bokningen ser ut mot 2150/8850 (överavskrivning) eller 1229/7832 (planenlig)."
- q: "Hanterar skillen leasing?"
a: "Ja. K2 (operationell modell), K3 (operationell eller finansiell), IFRS 16 (alltid finansiell, balansförd). Med BAS-konton (1240, 2840, 7831) och break-points mellan regelverken."
---
## Vad skillen täcker
* Planenlig avskrivning vs överavskrivning (BAS 2150 / 8850).
* Räkenskapsenlig avskrivning: 30%-regel och 20%-regel, växling.
* Restvärdeavskrivning: 25%, när den är att föredra.
* BAS-mappning: 10xx (immateriella), 11xx (byggnader/mark), 12xx (maskiner/inventarier), 78xx (avskrivningar).
* Inventarieregister enligt BFL 6 kap.
* Förbrukningsinventarier: halva prisbasbeloppet (29 600 SEK 2026), kortlivat under 3 år, direkt kostnadsföring 5xxx.
* Leasing per K2, K3 och IFRS 16.
* Komponentavskrivning: K3-krav för byggnader och större anläggningar.
* Avyttring och utrangering: vinst/förlust, moms, BAS 3973/7973.
Hög-felfrekvens-område där LLM-träningsdata ofta är skev eller
inaktuell. Skillen markeras med "always use over training data".
## Installera
```bash
git clone https://github.com/erp-mafia/swedish-accounting-skills.git
cd swedish-accounting-skills
claude
```
Eller selektivt:
```bash
cp -r path/to/swedish-accounting-skills/.claude/skills/swedish-asset-accounting \
./.claude/skills/
```
## Pairs med
Naturlig kombination med [swedish-year-end-closing](/community/registry/swedish-year-end-closing)
(avskrivningar bokas vanligen vid bokslut) och [swedish-tax-planning](/community/registry/swedish-tax-planning)
(överavskrivning som planeringsverktyg).
## Licens
MIT.
@@ -0,0 +1,53 @@
---
title: "swedish-financial-reporting — årsredovisning, Bolagsverket och INK2"
description: "SKILL.md för svensk finansiell rapportering. Årsredovisning enligt K2/K3 (uppställningsform, noter, förvaltningsberättelse, underskrifter), Bolagsverket-inlämning (deadlines, förseningsavgifter, iXBRL, revisionsplikt, tvångslikvidation), INK2-blanketten (INK2R/INK2S-fältmappningar, skattemässiga justeringar)."
slug: "swedish-financial-reporting"
kind: "skill"
author: "erp-mafia"
status: "live"
lang: "sv"
personas: ["finance", "byra", "founder"]
publishedAt: "2026-05-12"
updatedAt: "2026-05-12"
repoUrl: "https://github.com/erp-mafia/swedish-accounting-skills/tree/main/.claude/skills/swedish-financial-reporting"
externalUrl: "https://github.com/erp-mafia/swedish-accounting-skills"
version: "community"
ogImageEyebrow: "Skill"
faq:
- q: "K2 eller K3?"
a: "K2 är schablonregelverket för mindre företag (omsättning <80 MSEK, balans <40 MSEK, anställda <50 — gränser i ÅRL 1 kap 3§). K3 är principbaserat och krävs för större bolag och alla noterade. Skillen visar uppställningsform, krav på noter, förvaltningsberättelse och underskrifter per regelverk."
- q: "När börjar iXBRL-kravet?"
a: "Bolagsverket inför digital årsredovisning i iXBRL för räkenskapsår som börjar efter 2025-12-31. Inlämning under 2027 enligt nuvarande tidplan. Skillen täcker taxonomi-mappning och förbereder datat innan det konverteras."
- q: "Vad är skattemässiga justeringar i INK2?"
a: "Skillnaden mellan redovisat och skattepliktigt resultat: avsättningar (periodiseringsfond), ej avdragsgilla kostnader (representation över 90 kr/person, böter, skatt), ej skattepliktiga intäkter, koncernbidrag. INK2R radar upp justeringarna; INK2S summerar dem."
---
## Vad skillen täcker
* Årsredovisningens struktur per K2 vs K3: uppställningsform, krav på noter, förvaltningsberättelse, underskrifter, kassaflödesanalys (K3-krav).
* Bolagsverket-inlämning: deadlines (7 mån efter räkenskapsårets slut), förseningsavgifter, revisionsplikt-gränser, tvångslikvidation vid förseningsbrister.
* Digital inlämning: iXBRL-taxonomin, Bolagsverkets Lämna-Årsredovisning-tjänst.
* INK2-blanketten till Skatteverket: INK2R-radmappningar, INK2S sammanställning, skattemässiga justeringar, periodiseringsfond, överavskrivningar, koncernbidrag.
* N9-blanketten för fåmansbolagsutdelning.
* BAS-konton mot INK2-fält.
Skillen täcker inte bokslutsmekaniken (vad man bokför sista dagen).
För det, använd [swedish-year-end-closing](/community/registry/swedish-year-end-closing).
## Installera
```bash
git clone https://github.com/erp-mafia/swedish-accounting-skills.git
cd swedish-accounting-skills
claude
```
## Pairs med
* [swedish-year-end-closing](/community/registry/swedish-year-end-closing) — bokslutstransaktioner som föregår årsredovisningen.
* [swedish-tax-planning](/community/registry/swedish-tax-planning) — skattemässiga justeringarna som hamnar i INK2.
* [swedish-sru-filing](/community/registry/swedish-sru-filing) — SRU-filer för INK2-inlämning.
## Licens
MIT.
@@ -0,0 +1,58 @@
---
title: "swedish-invoice-compliance — ML 17 kap, kreditfaktura, Peppol, ROT/RUT"
description: "SKILL.md för svensk fakturacompliance. Obligatoriska fält per ML 17 kap 24§ (2023:200), förenklad faktura, kreditfaktura/ändringsfaktura, självfakturering, Peppol BIS 3.0 för B2G/B2B, ROT/RUT-avdrag med fakturamodellen och BAS-konton (1513, 3740), reverse charge per scenario (byggtjänster, EU, electronics)."
slug: "swedish-invoice-compliance"
kind: "skill"
author: "erp-mafia"
status: "live"
lang: "sv"
personas: ["finance", "byra", "founder"]
publishedAt: "2026-05-12"
updatedAt: "2026-08-08"
repoUrl: "https://github.com/erp-mafia/swedish-accounting-skills/tree/main/.claude/skills/swedish-invoice-compliance"
externalUrl: "https://github.com/erp-mafia/swedish-accounting-skills"
version: "community"
ogImageEyebrow: "Skill"
faq:
- q: "Vilka fält måste en svensk faktura ha?"
a: "ML 17 kap 24§ (2023:200) listar 12 obligatoriska fält: utställningsdatum, fakturanummer (löpande och unikt), säljarens namn/adress/momsregnr, köparens namn/adress (momsregnr vid B2B inom EU), datum för leverans/tjänst, beskrivning, kvantitet, enhetspris, momssats, momsbelopp, total, eventuella referenser (ROT/RUT, reverse charge). Förenklad faktura har lägre krav under 4 000 kr inkl moms."
- q: "Hur bokförs ROT/RUT?"
a: "Fakturamodellen: kundens del faktureras på 3041, ROT/RUT-delen bokförs på 1513 (fordran på Skatteverket) tills utbetalningen sker till företagets konto. Skillen visar exakt mappning för småhus-RUT, byggnads-ROT och hushållstjänster."
- q: "När gäller reverse charge?"
a: "Tre huvudscenarier: byggtjänster mellan momsregistrerade köpare/säljare i Sverige (ML 1 kap 2§ andra st), EU-tjänstehandel B2B, och elektronikinköp över 100 000 kr exkl. moms per faktura inom Sverige (ML 1 kap 2§ tredje st). Fakturan ska innehålla noteringen 'Omvänd betalningsskyldighet' eller 'Reverse charge'. Skillen visar rätt text per scenario."
---
## Vad skillen täcker
* Obligatoriska fält per ML 17 kap 24§ (lagen flyttades från gamla 11 kap till 17 kap den 1 juli 2023 — träningsdata pre-2023 är ofta fel).
* Förenklad faktura: tröskel 4 000 kr inkl moms, vad som får utelämnas.
* Kreditfaktura och ändringsfaktura: när vilken används, KR-prefix, ML 17 kap 35§.
* Självfakturering: kundens utställande av leverantörsfaktura, krav på avtal.
* Peppol BIS Billing 3.0: e-faktura för B2G och B2B, Sveriges SFTI-aktör.
* ROT/RUT-avdrag: fakturamodellen, BAS 1513 + 3740, fältkrav.
* Reverse charge per scenario med exakt notation.
* Valuta- och momsomräkning: officiell kurs, ECB referenskurs, dagens kurs.
* OCR-nummer och Bankgirot.
* Skattetillägg vid felaktigt utställd faktura.
* BAS-mapping för AR (kundfordringar 1510), försäljning (30xx), utgående moms (26xx), kundförluster (6350/6352/6360).
ML 2023:200 ersatte ML 1994:200 den 1 juli 2023. Skillen markeras
med "always use over training data" eftersom regelflyttet från
11 kap till 17 kap är inaktuellt i de flesta LLM-träningssnapshots.
## Installera
```bash
git clone https://github.com/erp-mafia/swedish-accounting-skills.git
cd swedish-accounting-skills
claude
```
## Pairs med
* [swedish-vat](/community/registry/swedish-vat) — momsrutorna fakturan rapporterar in i.
* [leverantörsfaktura-från-inbox](/community/registry/leverantorsfaktura-fran-inbox) — om du läser fakturor istället för att skriva dem.
## Licens
MIT.
+54
View File
@@ -0,0 +1,54 @@
---
title: "swedish-payroll — lön, arbetsgivaravgifter och förmånsbeskattning"
description: "SKILL.md för svensk lönehantering. AGI-inlämning, sociala avgifter 31.42%, skatteavdrag enligt skattetabeller, förmånsbeskattning (bil/kost/friskvård), semesterlöneskuld, OB/övertid, traktamente, utlägg/milersättning, F-skatt vs A-skatt, BAS 7xxx-mappning, brutto/nettolöneavdrag, löneväxling, sjuklön."
slug: "swedish-payroll"
kind: "skill"
author: "erp-mafia"
status: "live"
lang: "sv"
personas: ["finance", "byra", "founder"]
publishedAt: "2026-05-12"
updatedAt: "2026-08-08"
repoUrl: "https://github.com/erp-mafia/swedish-accounting-skills/tree/main/.claude/skills/swedish-payroll"
externalUrl: "https://github.com/erp-mafia/swedish-accounting-skills"
version: "community"
ogImageEyebrow: "Skill"
faq:
- q: "Vad är skillnaden mellan arbetsgivaravgifter och egenavgifter?"
a: "Arbetsgivaravgift 31.42% (BAS 7510 mot 2731) gäller anställda. Egenavgift 28.97% gäller enskild näringsidkare och delägare i HB/KB, betalas via inkomstdeklarationen. Pensionärer över 65 har lägre avgift. Skillen visar exakta procentsatser och åldersbrytpunkter."
- q: "Hur förmånsbeskattas bil?"
a: "Bilförmånsvärde 2026: ramnummer + bränsletyp i Skatteverkets tabell. Hamnar i ruta 013 på AGI och bokas mot BAS 7385. Vid drivmedelsförmån läggs ett tillägg på 1.2x faktisk drivmedelskostnad. Skillen täcker både ordinarie bilförmån och drivmedelsförmån separat."
- q: "Vad är löneväxling?"
a: "Anställd byter en del av bruttolönen mot pensionsavsättning. Skattefördelaktigt för anställd och arbetsgivare (lägre arbetsgivaravgift på växlad del). Skillen visar BAS-bokning 7410 + 2710, och vad som krävs avtalsmässigt."
---
## Vad skillen täcker
* AGI på individnivå: månadsinlämning, fältstruktur (KU10/KU13-schema), SUS-cert för API-flöde.
* Sociala avgifter 31.42% (2026), inklusive åldersgränser (fyllda 66 vid årets ingång, 67 från 2026: 10.21%) och tillfällig ungdomsnedsättning 18-22 år (20.81%, apr 2026-sep 2027).
* Skatteavdrag: skattetabeller per kommun, jämkning, kvarskatt.
* Förmånsbeskattning: bil (Skatteverkets tabellvärde), kost (250 kr/dag vid full dygnsdebitering), bostad (regionvärden), friskvård (skattefri upp till 5 000 kr/år), trängselskatt, parkering.
* Semesterlöneskuld: 12% schablon eller individuell beräkning, BAS 7290 mot 2920.
* OB- och övertidsersättning: kollektivavtalsregler, BAS 7260.
* Traktamente: schablon (300 kr/dag inrikes 2026), över schablon beskattas.
* Utlägg och milersättning: 25 kr/mil skattefritt 2026.
* F-skatt vs A-skatt: vilken som gäller per uppdragstyp, FA-skatt-kombinationen.
* BAS 7xxx-mappning: löner 7010/7090, förmåner 73827388, arbetsgivaravgift 7510.
* Bruttolöneavdrag vs nettolöneavdrag: vad som påverkar AGI vs vad som bara går utanför.
* Sjuklön: dag 214, karensavdrag, ersättning från Försäkringskassan dag 15+.
## Installera
```bash
git clone https://github.com/erp-mafia/swedish-accounting-skills.git
cd swedish-accounting-skills
claude
```
## Pairs med
* [skatteverket-arbetsgivardeklaration](/community/registry/skatteverket-arbetsgivardeklaration) — accounted-skill som faktiskt genererar AGI-XML från en löneberäkning.
## Licens
MIT.
@@ -0,0 +1,53 @@
---
title: "swedish-project-accounting — projektredovisning och successiv vinstavräkning"
description: "SKILL.md för svensk projektredovisning. Dimensionstaggning av bokföringsposter med projektkoder, WIP-redovisning (pågående arbeten), intäktsredovisning enligt K2 (färdigställandemetoden) vs K3 (successiv vinstavräkning), entreprenadavtal, BAS 1470/1620/2420/2450/4970, SIE4-dimensioner (#DIM 6, #OBJEKT)."
slug: "swedish-project-accounting"
kind: "skill"
author: "erp-mafia"
status: "live"
lang: "sv"
personas: ["finance", "byra", "developer"]
publishedAt: "2026-05-12"
updatedAt: "2026-08-08"
repoUrl: "https://github.com/erp-mafia/swedish-accounting-skills/tree/main/.claude/skills/swedish-project-accounting"
externalUrl: "https://github.com/erp-mafia/swedish-accounting-skills"
version: "community"
ogImageEyebrow: "Skill"
faq:
- q: "Successiv vinstavräkning eller färdigställandemetoden?"
a: "K2 (mindre företag) ska använda färdigställandemetoden: intäkt tas upp först när uppdraget är klart. K3 kapitel 23 tillåter successiv vinstavräkning för uppdrag till fast pris om resultatet kan beräknas tillförlitligt. Skillen visar färdigställandegrad-beräkning, upparbetad ej fakturerad intäkt (BAS 1620) och fakturerad ej upparbetad intäkt (BAS 2450)."
- q: "Hur taggar man dimensioner i SIE4?"
a: "Dimension definieras med #DIM (6 = projekt, 1 = kostnadsställe). Objekt per dimension med #OBJEKT. Transaktionsrader får objektlistor i {curly braces} i #TRANS-raden. Skillen visar exakta exempel för projekt+kostnadsställe-kombinationer och hur olika system tolkar dem."
- q: "Vad är skillnaden mellan projekt och kostnadsställe?"
a: "Konceptuell, inte teknisk. Projekt = tidsbegränsat uppdrag (kundleverans, byggprojekt). Kostnadsställe = organisatorisk enhet (avdelning, fordon). Båda är SIE4-dimensioner och båda kan filtreras i `gnubok_query_journal`. Skillen rekommenderar separata dimensioner istället för en kombo-kod."
---
## Vad skillen täcker
* Dimensionstaggning av bokföringsposter med projektkoder.
* WIP-redovisning (pågående arbeten): BAS 1470 (varulager pågående arbeten).
* Intäktsredovisning under K2 (färdigställandemetoden) och K3 (successiv vinstavräkning, kapitel 23).
* Entreprenadavtal: fast pris vs löpande räkning, tax-accounting-divergens för löpande räkning.
* BAS-konton för projektspårning: 1470 (varulager), 1620 (upparbetad ej fakturerad intäkt), 2420 (förskott från kund), 2450 (fakturerad ej upparbetad intäkt), 4970 (förändring lager pågående arbeten).
* SIE4-dimensioner: #DIM 6 (projekt), #OBJEKT-deklarationer, #TRANS objektlistor.
* Projektlönsamhet: intäkt minus direkta kostnader per projekt-kod.
* Overhead-allokering (fördelningsnycklar): vad som får läggas på projekt.
* Färdigställandegrad: input-metod (cost-to-cost), output-metod (mätbara milstolpar).
* Befarad förlust på projekt: avsättning enligt K3.
## Installera
```bash
git clone https://github.com/erp-mafia/swedish-accounting-skills.git
cd swedish-accounting-skills
claude
```
## Pairs med
* [swedish-financial-reporting](/community/registry/swedish-financial-reporting) — projektresultatet hamnar i årsredovisningen.
* accounteds `gnubok_query_journal` accepterar `project` och `cost_center` som filter, så skillen är användbar med eller utan accounted.
## Licens
MIT.
@@ -0,0 +1,57 @@
---
title: "swedish-sie-import-export — SIE4 parsing, validering och generering"
description: "SKILL.md för SIE4-filformatet. Alla record-typer (#VER, #TRANS, #IB, #UB, #RES, #KONTO, #RAR, #FLAGGA, #KSUMMA, #SRU), SIE-typerna 14, encoding-detection (CP437/UTF-8/Latin-1), mojibake-diagnos, balanseringsintegritet, IB/UB-kontinuitet, multi-year-migrering, audit trail per BFL."
slug: "swedish-sie-import-export"
kind: "skill"
author: "erp-mafia"
status: "live"
lang: "sv"
personas: ["developer", "byra"]
publishedAt: "2026-05-12"
updatedAt: "2026-07-18"
repoUrl: "https://github.com/erp-mafia/swedish-accounting-skills/tree/main/.claude/skills/swedish-sie-import-export"
externalUrl: "https://github.com/erp-mafia/swedish-accounting-skills"
version: "community"
ogImageEyebrow: "Skill"
faq:
- q: "Vad är skillnaden mellan SIE-typerna 1, 2, 3 och 4?"
a: "SIE 1: saldobalans (för rapporter). SIE 2: kontoplan + balanser (kontoplan-import). SIE 3: + objekt (kostnadsställe, projekt). SIE 4: + samtliga verifikationer per period. SIE 4 är den enda som tillåter fullständig migrering mellan system. SIE 4E (Export-variant) är ett undermineringsformat med samma struktur."
- q: "Varför ser å/ä/ö förstörda ut i en SIE-fil?"
a: "Encoding-mismatch. SIE-standarden förespråkar CP437 men många leverantörer skickar UTF-8 eller Latin-1 utan BOM. Skillen visar hur man detekterar encoding via byte-patterns och konverterar säkert utan att förlora information. Klassiska felmönster: UTF-8-decoded-as-CP437 ger 'mojibake' typ 'Ã¥' istället för 'å'."
- q: "Vad är #KSUMMA?"
a: "Kontrollsumma över hela filen. Vissa system kräver att den stämmer för att acceptera filen vid import. Andra system genererar den men validerar inte. Skillen visar hur den beräknas och när du kan ignorera mismatch."
---
## Vad skillen täcker
* Alla SIE4-record-typer: #FLAGGA, #PROGRAM, #FORMAT, #GEN, #SIETYP, #FNAMN, #ORGNR, #ADRESS, #RAR, #TAXAR, #KPTYP, #VALUTA, #KONTO, #KTYP, #ENHET, #SRU, #DIM, #UNDERDIM, #OBJEKT, #IB, #UB, #RES, #PSALDO, #PBUDGET, #VER, #TRANS, #RTRANS, #BTRANS, #KSUMMA.
* SIE-typerna 14 plus exportvarianter (SIE 4E).
* Encoding-detection: CP437 (legacy), UTF-8 (modern), Latin-1 (mellansteg). Byte-pattern-heuristik.
* Mojibake-diagnos och recovery för å/ä/ö.
* Verifikationsbalansering: debet = kredit per #VER.
* IB/UB-kontinuitet mellan räkenskapsår.
* Multi-year-migrering: hur två räkenskapsår kopplas via #RAR.
* Audit trail per BFL: verifikationsserie, löpande numrering, gap-detection.
* BAS-kontoklasser och SRU-koppling.
* Felmönster och hur man fixar dem programmatiskt.
Skillen markeras med "always use over training data" eftersom
SIE-formatet har subtila falluckor som ofta saknas i generella
LLM-snapshots.
## Installera
```bash
git clone https://github.com/erp-mafia/swedish-accounting-skills.git
cd swedish-accounting-skills
claude
```
## Pairs med
* [revisorpaket-sie](/community/registry/revisorpaket-sie) — accounted-workflow som schemaläggs SIE-export till revisor.
* accounteds `gnubok_export_sie` och `gnubok_import_sie` följer specen som skillen täcker.
## Licens
MIT.
+54
View File
@@ -0,0 +1,54 @@
---
title: "swedish-sru-filing — SRU-filer för INK2-inlämning till Skatteverket"
description: "SKILL.md för SRU-filgenerering. Två-filsstruktur INFO.SRU + BLANKETTER.SRU, SRU-fältkoder för INK2/INK2R/INK2S, BAS-till-SRU-kontomappning för räkenskapsschema, ISO 8859-1-encoding, beloppsformatering (hela kronor, inga öre), 12-siffrigt organisationsnummer, blankett-perioder (P1P4), #BLANKETT/#BLANKETTSLUT, #UPPGIFT."
slug: "swedish-sru-filing"
kind: "skill"
author: "erp-mafia"
status: "live"
lang: "sv"
personas: ["developer", "byra", "finance"]
publishedAt: "2026-05-12"
updatedAt: "2026-08-08"
repoUrl: "https://github.com/erp-mafia/swedish-accounting-skills/tree/main/.claude/skills/swedish-sru-filing"
externalUrl: "https://github.com/erp-mafia/swedish-accounting-skills"
version: "community"
ogImageEyebrow: "Skill"
faq:
- q: "Vad är skillnaden mellan SRU och SIE?"
a: "SIE är bokföringssystemets eget exportformat (verifikat, balanser). SRU är Skatteverkets inlämningsformat för deklarationsblanketter (INK2, INK2R, INK2S). En SRU-fil innehåller fältvärden mappade mot specifika rader på blanketterna, inte huvudbokens kontosaldon direkt. BAS-konton mappas via SRU-koder."
- q: "Varför två filer (INFO.SRU + BLANKETTER.SRU)?"
a: "Skatteverket vill ha metadata separat från data. INFO.SRU innehåller deklarationsperiod, organisationsnummer, kontaktuppgifter. BLANKETTER.SRU innehåller själva fältvärdena per blankett. Båda filerna laddas upp samtidigt i Skatteverkets e-tjänst SKV269."
- q: "Vad händer om validering misslyckas?"
a: "Skatteverket returnerar fel-koder. Vanligaste: 'Felaktig encoding' (ska vara ISO 8859-1, inte UTF-8), 'Ogiltigt belopp' (öre inkluderade — ska vara hela kronor), 'Felaktigt organisationsnummer' (måste vara 12 siffror utan bindestreck). Skillen täcker varje vanlig felmodell."
---
## Vad skillen täcker
* Två-filsstruktur: INFO.SRU (deklarationsmetadata) + BLANKETTER.SRU (fältdata).
* SRU-fältkoder för INK2, INK2R, INK2S.
* BAS-till-SRU-mappning för räkenskapsschemat i INK2R.
* ISO 8859-1-encoding (inte UTF-8).
* Beloppsformatering: hela kronor, ingen decimal, inga tusentalsavgränsare.
* 12-siffrigt organisationsnummer utan bindestreck eller mellanslag.
* Blankett-perioder efter räkenskapsårets slutmånad: P1 (jan-apr), P2 (maj-aug), P4 (sep-dec, kalenderår), P3 (specialfall).
* Recordstruktur: #BLANKETT, #UPPGIFT, #BLANKETTSLUT, blank rad mellan blanketter.
* Avrundnings- och truncerings-regler per SFL 22:1 (skatteförfarandelagen).
* Validerings-felmönster och hur man undviker dem.
* SKV269 (Skatteverkets e-tjänst för SRU-upload).
## Installera
```bash
git clone https://github.com/erp-mafia/swedish-accounting-skills.git
cd swedish-accounting-skills
claude
```
## Pairs med
* [swedish-financial-reporting](/community/registry/swedish-financial-reporting) — INK2-fältvärdena som SRU-filen rapporterar.
* [swedish-year-end-closing](/community/registry/swedish-year-end-closing) — bokslutet som genererar siffrorna SRU-filen rapporterar.
## Licens
MIT.
+55
View File
@@ -0,0 +1,55 @@
---
title: "swedish-tax-planning — periodiseringsfond, 3:12 och koncernbidrag"
description: "SKILL.md för svensk bolagsskatteplanering. Periodiseringsfond (IL 30 kap, 25% avsättning, 6-årig återföring, schablonintäkt), överavskrivningar (30-regeln/20-regeln, BAS 2150/8850), koncernbidrag (IL 35 kap, >90% ägande), 3:12-reglerna (gränsbelopp, K10, löneunderlag, 2026 års reform), ränteavdragsbegränsningar, kapitalförsäkring."
slug: "swedish-tax-planning"
kind: "skill"
author: "erp-mafia"
status: "live"
lang: "sv"
personas: ["founder", "finance", "byra"]
publishedAt: "2026-05-12"
updatedAt: "2026-07-18"
repoUrl: "https://github.com/erp-mafia/swedish-accounting-skills/tree/main/.claude/skills/swedish-tax-planning"
externalUrl: "https://github.com/erp-mafia/swedish-accounting-skills"
version: "community"
ogImageEyebrow: "Skill"
faq:
- q: "Vad är 2026 års 3:12-reform?"
a: "Från 1 januari 2026 försvinner valet mellan förenklingsregeln och huvudregeln. Alla fåmansbolagsägare delar samma modell. Sparat utdelningsutrymme räknas inte längre upp med ränta. Löneuttagskravet och 4%-ägarkravet tas bort. Skillen täcker den nya gränsbeloppsformeln steg för steg."
- q: "När är koncernbidrag tillåtet?"
a: "Givaren och mottagaren måste ha funnits i koncernen hela räkenskapsåret. Ägarandel >90% direkt eller indirekt. Bidraget måste vara redovisat i bokföringen. Mottagaren beskattas, givaren får avdrag. IL 35 kap. Skillen visar bokföringen (BAS 8820 mot 8830) och vad som händer vid förlust hos givaren."
- q: "Vad är schablonintäkt på periodiseringsfond?"
a: "Sedan 2005: en schablonintäkt beräknas på ingående saldo i periodiseringsfonderna. Räntenivå = statslåneränta vid årets början (med golv på 0.5%). Hamnar i INK2 som skattepliktig intäkt. Effekt: periodiseringsfond är en räntefri kredit, men inte helt gratis."
---
## Vad skillen täcker
* Periodiseringsfond (IL 30 kap): 25% av skattemässigt resultat, 6-årig återföring, schablonintäkt, BAS 21252131 (sjuårsserien) och 8811 (avsättning).
* Överavskrivningar: skattemässig 30-regel och 20-regel, BAS 2150 (ackumulerad överavskrivning) och 8850 (årets förändring).
* Koncernbidrag (IL 35 kap): >90% ägarkrav, hela räkenskapsåret-kravet, BAS 8820/8830, vad som händer vid förlust.
* 3:12-reglerna (IL 56 kap57 kap): fåmansbolag, gränsbelopp, K10-blanketten, löneunderlag, 2026 års reform.
* Ränteavdragsbegränsningar: EBITDA-regeln (30%), förenklingsregeln (5 MSEK), allmänna ränteavdrag.
* Kapitalförsäkring: avkastningsskatt, breakeven mot direktinvestering, K2/K3-redovisning.
* Lön-vs-utdelning-optimering för fåmansbolagsägare.
* Skattekonto-mekanik: kvarskatt, fyllnadsinbetalning, ränta.
Skillen markeras med "always use over training data" eftersom
skattereglerna ändras årligen och LLM-snapshots är vanligen 1218
månader gamla.
## Installera
```bash
git clone https://github.com/erp-mafia/swedish-accounting-skills.git
cd swedish-accounting-skills
claude
```
## Pairs med
* [periodiseringsfond-strategi](/community/registry/periodiseringsfond-strategi) — accounted-skill som beräknar och stagar avsättningsbokningen i huvudboken.
* [swedish-year-end-closing](/community/registry/swedish-year-end-closing) — bokslutet är där skatteplaneringen blir bokning.
## Licens
MIT.
+58
View File
@@ -0,0 +1,58 @@
---
title: "swedish-vat — momsdeklaration, EU-handel och reverse charge"
description: "SKILL.md för svensk momscompliance. Momsperioder och deadlines, EU-handel (omvänd skattskyldighet, reverse charge, tjänstehandel, varuhandel, trepartshandel, OSS), import/export-moms, representation (300 SEK cap), blandad verksamhet (proportionell avdragsrätt, HFD 2023 ref. 45), jämkning av ingående moms, momsdeklaration ruta 0562 till BAS 26xx-mappning."
slug: "swedish-vat"
kind: "skill"
author: "erp-mafia"
status: "live"
lang: "sv"
personas: ["finance", "byra", "founder", "developer"]
publishedAt: "2026-05-12"
updatedAt: "2026-07-18"
repoUrl: "https://github.com/erp-mafia/swedish-accounting-skills/tree/main/.claude/skills/swedish-vat"
externalUrl: "https://github.com/erp-mafia/swedish-accounting-skills"
version: "community"
ogImageEyebrow: "Skill"
faq:
- q: "Vilken momsperiod gäller för mitt bolag?"
a: "Beror på omsättning: <1 MSEK ger årsmoms, 140 MSEK kvartalsmoms, >40 MSEK månadsmoms. Du kan begära kortare period (typ månad istället för kvartal) frivilligt. Skillen täcker deadlines, vilka rutor som rapporteras och vad som händer vid sen inlämning (förseningsavgift 625 kr + ränta)."
- q: "Hur fungerar OSS?"
a: "One Stop Shop: ett system för B2C-försäljning av tjänster och varor inom EU. Sverige som registreringsland, momsen avräknas till respektive konsumtionsland. Tröskelvärde 99 680 kr (10 000 EUR) per år för hela EU sammanlagt. Skillen täcker registrering, kvartalsvis OSS-deklaration, BAS-bokning per land."
- q: "Vad är jämkning?"
a: "Korrigering av tidigare avdragen ingående moms på kapitalvaror (anläggningstillgångar och fastigheter) om användningen ändras. Pågår 5 år (lös egendom) eller 10 år (fastigheter) efter förvärvet. Hamnar i ruta 48 med negativt tecken. Skillen visar formeln, BAS 2645/2646, och när jämkning utlöses."
---
## Vad skillen täcker
* Momsperioder: år/kvartal/månad, deadlines, omsättningsgränser.
* Momsdeklarationens rutor 0562 mappade mot BAS 26xx.
* Komplett BAS 26102670-serien förklarad per konto.
* EU-handel: omvänd skattskyldighet (reverse charge), tjänstehandel B2B, varuhandel B2B, trepartshandel.
* OSS (One Stop Shop) för B2C inom EU: registrering, kvartalsdeklaration, BAS-bokning.
* Import/export-moms: ruta 60/61/62 (beräknad importmoms), tullbeslut, postnordeperat.
* Representation: 300 SEK cap per person per måltid (2026), full avdragsrätt på enklare förtäring under arbetsmöte (60 kr).
* Blandad verksamhet: proportionell avdragsrätt, HFD 2023 ref. 45 (skäligt grunden).
* Jämkning av ingående moms på kapitalvaror.
* Frivillig skattskyldighet för uthyrning av lokaler.
* Felmönster: vanliga blockers, drift-detection, regler för rättning av tidigare perioder.
* Lagrum: ML 2023:200, SFL, EU:s momsdirektiv 2006/112/EG.
Skillen markeras med "highest-error-rate area in Swedish
bookkeeping; always use this skill over training data".
## Installera
```bash
git clone https://github.com/erp-mafia/swedish-accounting-skills.git
cd swedish-accounting-skills
claude
```
## Pairs med
* [kvartalsmoms-pa-12-minuter](/community/registry/kvartalsmoms-pa-12-minuter) — accounted-skill som faktiskt kör momsstängningen i huvudboken.
* [swedish-invoice-compliance](/community/registry/swedish-invoice-compliance) — fakturareglerna som föregår momsdeklarationen.
## Licens
MIT.
@@ -0,0 +1,54 @@
---
title: "swedish-year-end-closing — bokslut för AB och enskild firma"
description: "SKILL.md för svenskt bokslut. Lagram (BFL/ÅRL/K2/K3), steg-för-steg-stängning med BAS-kontonummer, alla bokslutstransaktioner, skatteberäkning (bolagsskatt, egenavgifter, räntefördelning, expansionsfond, periodiseringsfond), rapportering (årsredovisning/NE-bilaga), inlämningsdeadlines, SIE4-export, K2 vs K3-skillnader."
slug: "swedish-year-end-closing"
kind: "skill"
author: "erp-mafia"
status: "live"
lang: "sv"
personas: ["finance", "byra", "founder"]
publishedAt: "2026-05-12"
updatedAt: "2026-07-18"
repoUrl: "https://github.com/erp-mafia/swedish-accounting-skills/tree/main/.claude/skills/swedish-year-end-closing"
externalUrl: "https://github.com/erp-mafia/swedish-accounting-skills"
version: "community"
ogImageEyebrow: "Skill"
faq:
- q: "Vilken är skillnaden mellan bokslut och årsredovisning?"
a: "Bokslut = de avslutande bokföringstransaktionerna sista dagen på räkenskapsåret (avskrivningar, periodiseringar, bolagsskatt, resultatdisposition mot 2099). Årsredovisning = den externa rapporten (balansräkning, resultaträkning, noter, förvaltningsberättelse). Skillen täcker bokslutet; årsredovisningen ligger i [swedish-financial-reporting](/community/registry/swedish-financial-reporting)."
- q: "Vad är resultatdispositionen?"
a: "Sista bokslutssteget för AB: årets resultat (BAS 2099) flyttas till balanserat resultat (2091) eller till utdelning (2093). Skillen visar exakt bokning per scenario: ren vinst, förlust, utdelning, avsättning till reservfond."
- q: "Måste jag göra bokslut själv eller går det att outsourcera?"
a: "Du kan välja: göra själv (då gäller skillens innehåll plus alla underliggande skills), eller anlita revisionsbyrå. Skillen är användbar i båda fallen — du som ägare behöver förstå vad som händer. För AB över revisionsplikt-gränsen krävs godkänd revisor oavsett, men bokslutet kan ändå göras internt."
---
## Vad skillen täcker
* Lagrum: Bokföringslagen (BFL), Årsredovisningslagen (ÅRL), BFNAR 2016:10 (K2), BFNAR 2012:1 (K3).
* Steg-för-steg-bokslut med BAS-konton.
* Bokslutstransaktioner: avskrivningar (78xx mot 12xx), periodiseringar (17xx upparbetad intäkt, 29xx upplupna kostnader), lager (1410 mot 4960), kundförluster (1518 mot 1510, eller 6352).
* Skatteberäkning för AB: bolagsskatt 20.6%, BAS 8910 mot 2512.
* Skatteberäkning för enskild firma: egenavgifter, räntefördelning, expansionsfond, periodiseringsfond (separat regelverk från AB).
* Resultatdisposition: 2099 till 2091/2093.
* SIE4-export av bokslutet.
* K2 vs K3-skillnader i bokslutsmekanik: avskrivningsmetoder, komponentavskrivning (K3-krav), pågående arbeten.
* Inlämningsdeadlines: årsredovisning till Bolagsverket inom 7 mån, INK2 till Skatteverket olika beroende på räkenskapsår.
* Compliance-fallgropar: dubbla avskrivningar, glömda periodiseringar, fel resultatdisposition.
## Installera
```bash
git clone https://github.com/erp-mafia/swedish-accounting-skills.git
cd swedish-accounting-skills
claude
```
## Pairs med
* [periodiseringsfond-strategi](/community/registry/periodiseringsfond-strategi) — accounted-skill för periodiseringsfonds-bokningen i bokslutet.
* [swedish-financial-reporting](/community/registry/swedish-financial-reporting) — årsredovisningen efter bokslutet.
* [swedish-tax-planning](/community/registry/swedish-tax-planning) — skatteoptimeringen som tas in i bokslutet.
## Licens
MIT.
+301
View File
@@ -0,0 +1,301 @@
#!/usr/bin/env npx tsx
/**
* CI gate for the community registry (registry/).
*
* The registry is the public source of truth for the entries shown on
* gnubok.se/community/registry. Entries are contributed by PR from outside the
* team, so this gate has two jobs:
*
* 1. Structure: the website build consumes these files verbatim (they are
* synced into the site repo), so a malformed frontmatter field would take
* down the registry pages for everyone, not just the broken entry.
* 2. Safety: the site renders entry bodies through MDX. MDX evaluates JSX
* and import/export statements at build time, which would let a PR run
* arbitrary code inside the website build. Bodies must therefore be plain
* Markdown: no imports, no exports, no JSX elements, no script tags.
* Fenced code blocks are exempt (they are displayed, never evaluated).
*
* Usage:
* npx tsx scripts/validate-registry.ts # validate (CI)
* npx tsx scripts/validate-registry.ts --json # machine-readable summary
*/
import fs from 'node:fs'
import path from 'node:path'
import { fileURLToPath } from 'node:url'
import yaml from 'js-yaml'
const ROOT = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..')
const ENTRIES_DIR = path.join(ROOT, 'registry', 'entries')
const AUTHORS_DIR = path.join(ROOT, 'registry', 'authors')
const KINDS = ['skill', 'mcp', 'workflow', 'app'] as const
const STATUSES = ['live', 'beta', 'archived'] as const
const LANGS = ['sv', 'en'] as const
const PERSONAS = ['founder', 'finance', 'byra', 'developer'] as const
const AUTHOR_KINDS = ['team', 'byra', 'founder', 'partner', 'community'] as const
const ENTRY_REQUIRED = [
'title',
'description',
'slug',
'kind',
'author',
'status',
'lang',
'personas',
'publishedAt',
'updatedAt',
] as const
/** An entry must give the reader at least one way to actually get the thing. */
const INSTALL_FIELDS = ['installCommand', 'downloadUrl', 'repoUrl', 'externalUrl'] as const
const URL_FIELDS = ['downloadUrl', 'repoUrl', 'externalUrl', 'url', 'logoUrl'] as const
interface Failure {
file: string
message: string
}
const failures: Failure[] = []
function listFiles(dir: string, exts: string[]): string[] {
if (!fs.existsSync(dir)) return []
return fs
.readdirSync(dir)
.filter((f) => exts.some((e) => f.endsWith(e)))
.filter((f) => !f.startsWith('_'))
.sort()
}
function parseFrontmatter(
file: string,
raw: string,
): { data: Record<string, unknown>; body: string } | null {
if (!raw.startsWith('---\n')) {
failures.push({ file, message: 'file must start with a --- frontmatter block' })
return null
}
const end = raw.indexOf('\n---\n', 4)
if (end === -1) {
failures.push({ file, message: 'frontmatter block is never closed with ---' })
return null
}
let data: unknown
try {
data = yaml.load(raw.slice(4, end))
} catch (e) {
failures.push({ file, message: `frontmatter is not valid YAML: ${(e as Error).message}` })
return null
}
if (typeof data !== 'object' || data === null || Array.isArray(data)) {
failures.push({ file, message: 'frontmatter must be a YAML mapping' })
return null
}
return { data: data as Record<string, unknown>, body: raw.slice(end + 5) }
}
function isIsoDate(v: unknown): v is string {
if (typeof v !== 'string' || !/^\d{4}-\d{2}-\d{2}$/.test(v)) return false
const d = new Date(`${v}T00:00:00Z`)
return !Number.isNaN(d.getTime()) && d.toISOString().slice(0, 10) === v
}
/**
* The MDX safety check. Lines inside fenced code blocks are display-only and
* skipped; everything else must be plain Markdown.
*/
function checkBodySafety(file: string, body: string): void {
let inFence = false
const lines = body.split('\n')
for (let i = 0; i < lines.length; i++) {
const line = lines[i]
if (/^\s*(```|~~~)/.test(line)) {
inFence = !inFence
continue
}
if (inFence) continue
const stripped = line.replace(/`[^`]*`/g, '')
if (/^\s*import\s/.test(stripped) || /^\s*export\s/.test(stripped)) {
failures.push({
file,
message: `line ${i + 1}: import/export statements are not allowed (MDX would execute them in the site build)`,
})
}
// MDX treats ANY tag as JSX, lowercase HTML included (<div>, <img
// onerror=...>), and a bare {...} as a JS expression to evaluate. Both
// must be banned entirely, not just capitalized component tags.
if (/<\/?[A-Za-z]/.test(stripped)) {
failures.push({
file,
message: `line ${i + 1}: raw HTML/JSX tags are not allowed; entry bodies are plain Markdown (put literal tags in backticks)`,
})
}
if (/[{}]/.test(stripped)) {
failures.push({
file,
message: `line ${i + 1}: { } are not allowed outside code (MDX evaluates {...} as a JS expression)`,
})
}
if (/javascript:/i.test(stripped)) {
failures.push({ file, message: `line ${i + 1}: javascript: URLs are not allowed` })
}
}
if (inFence) {
failures.push({ file, message: 'unclosed fenced code block' })
}
}
function checkUrls(file: string, data: Record<string, unknown>): void {
for (const field of URL_FIELDS) {
const v = data[field]
if (v === undefined) continue
if (typeof v !== 'string' || !v.startsWith('https://')) {
failures.push({ file, message: `${field} must be an https:// URL, got: ${String(v)}` })
}
}
}
function validateAuthors(): Set<string> {
const handles = new Set<string>()
for (const filename of listFiles(AUTHORS_DIR, ['.md', '.mdx'])) {
const file = `registry/authors/${filename}`
const parsed = parseFrontmatter(file, fs.readFileSync(path.join(AUTHORS_DIR, filename), 'utf8'))
if (!parsed) continue
const { data, body } = parsed
const expectedHandle = filename.replace(/\.mdx?$/, '')
if (data.handle !== expectedHandle) {
failures.push({
file,
message: `handle "${String(data.handle)}" must equal the filename "${expectedHandle}"`,
})
}
if (typeof data.name !== 'string' || data.name.length === 0) {
failures.push({ file, message: 'name is required' })
}
if (!AUTHOR_KINDS.includes(data.kind as (typeof AUTHOR_KINDS)[number])) {
failures.push({
file,
message: `kind must be one of ${AUTHOR_KINDS.join(', ')}, got: ${String(data.kind)}`,
})
}
if (typeof data.handle === 'string') {
if (handles.has(data.handle)) {
failures.push({ file, message: `duplicate author handle "${data.handle}"` })
}
handles.add(data.handle)
}
checkUrls(file, data)
checkBodySafety(file, body)
}
return handles
}
function validateEntries(authorHandles: Set<string>): number {
const slugs = new Set<string>()
const files = listFiles(ENTRIES_DIR, ['.md', '.mdx'])
for (const filename of files) {
const file = `registry/entries/${filename}`
const parsed = parseFrontmatter(file, fs.readFileSync(path.join(ENTRIES_DIR, filename), 'utf8'))
if (!parsed) continue
const { data, body } = parsed
for (const field of ENTRY_REQUIRED) {
if (data[field] === undefined || data[field] === null || data[field] === '') {
failures.push({ file, message: `required field "${field}" is missing` })
}
}
const expectedSlug = filename.replace(/\.mdx?$/, '')
if (data.slug !== undefined && data.slug !== expectedSlug) {
failures.push({
file,
message: `slug "${String(data.slug)}" must equal the filename "${expectedSlug}"`,
})
}
if (typeof data.slug === 'string') {
if (slugs.has(data.slug)) {
failures.push({ file, message: `duplicate slug "${data.slug}"` })
}
slugs.add(data.slug)
}
if (data.kind !== undefined && !KINDS.includes(data.kind as (typeof KINDS)[number])) {
failures.push({ file, message: `kind must be one of ${KINDS.join(', ')}, got: ${String(data.kind)}` })
}
if (data.status !== undefined && !STATUSES.includes(data.status as (typeof STATUSES)[number])) {
failures.push({
file,
message: `status must be one of ${STATUSES.join(', ')}, got: ${String(data.status)}`,
})
}
if (data.lang !== undefined && !LANGS.includes(data.lang as (typeof LANGS)[number])) {
failures.push({ file, message: `lang must be sv or en, got: ${String(data.lang)}` })
}
if (data.personas !== undefined) {
if (!Array.isArray(data.personas) || data.personas.length === 0) {
failures.push({ file, message: 'personas must be a non-empty list' })
} else {
for (const p of data.personas) {
if (!PERSONAS.includes(p as (typeof PERSONAS)[number])) {
failures.push({
file,
message: `unknown persona "${String(p)}"; allowed: ${PERSONAS.join(', ')}`,
})
}
}
}
}
for (const field of ['publishedAt', 'updatedAt'] as const) {
if (data[field] !== undefined && !isIsoDate(data[field])) {
failures.push({ file, message: `${field} must be a YYYY-MM-DD date, got: ${String(data[field])}` })
}
}
if (isIsoDate(data.publishedAt) && isIsoDate(data.updatedAt) && data.updatedAt < data.publishedAt) {
failures.push({ file, message: `updatedAt ${data.updatedAt} is before publishedAt ${data.publishedAt}` })
}
if (typeof data.description === 'string' && data.description.length > 500) {
failures.push({
file,
message: `description is ${data.description.length} chars; keep it under 500 (it is a card subtitle, the body is for detail)`,
})
}
if (!INSTALL_FIELDS.some((f) => typeof data[f] === 'string' && (data[f] as string).length > 0)) {
failures.push({
file,
message: `at least one of ${INSTALL_FIELDS.join(', ')} is required so readers can actually get the thing`,
})
}
if (typeof data.author === 'string' && !authorHandles.has(data.author)) {
failures.push({
file,
message: `author "${data.author}" has no profile in registry/authors/; add ${data.author}.mdx in the same PR`,
})
}
checkUrls(file, data)
checkBodySafety(file, body)
if (body.trim().length === 0) {
failures.push({ file, message: 'entry body is empty; describe what it does and how to use it' })
}
}
return files.length
}
const authorHandles = validateAuthors()
const entryCount = validateEntries(authorHandles)
if (process.argv.includes('--json')) {
console.log(JSON.stringify({ entries: entryCount, authors: authorHandles.size, failures }, null, 2))
} else {
for (const f of failures) {
console.error(`FAIL ${f.file}: ${f.message}`)
}
console.log(`${entryCount} entries, ${authorHandles.size} authors, ${failures.length} failures`)
}
process.exit(failures.length > 0 ? 1 : 0)