feat: INK2 declaration improvements, invoice delivery date & Swedish compliance skills (#204)

* feat: add INK2 declaration improvements, invoice delivery date, and Swedish compliance skills

Expand INK2 engine with full INK2S/INK2R support and improved SRU generation.
Add delivery_date field to invoices and corresponding PDF/migration support.
Add Claude skills for Swedish asset accounting, invoice compliance, SIE import/export, SRU filing, and tax planning.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: address PR review — map BAS 4500–4899, strip CRLF in SRU, document P3

- Map BAS accounts 4500–4599 (legoarbeten), 4700–4899 (diverse
  varuinköpskostnader) to SRU 7512 so they are not silently dropped
  from INK2R declarations
- Strip \r\n in sanitizeString to prevent CRLF injection in SRU fields
- Document P3 period suffix limitation for brutet räkenskapsår

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: correct BAS 4500-4599, 4700-4899 mapping from 7512 to 7511

Per the official BAS-to-SRU mapping, these account ranges are cost of
goods (legoarbeten, inkurans, svinn) and belong under 7511 (Råvaror
och förnödenheter), not 7512 (Handelsvaror). 7512 remains 4600-4699.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Jakob Wennberg
2026-04-09 13:16:10 +02:00
committed by GitHub
co-authored by Claude Opus 4.6
parent bf5a8d9195
commit 7a18d89c70
37 changed files with 5481 additions and 658 deletions
@@ -0,0 +1,65 @@
---
name: swedish-asset-accounting
description: >
Swedish fixed asset accounting (anläggningsredovisning) reference. Covers planenlig avskrivning, överavskrivning (2150/8850),
räkenskapsenlig (30%/20%) vs restvärdeavskrivning (25%), BAS 10xx-12xx/78xx mapping, inventarieregister per BFL,
förbrukningsinventarier (half PBB), leasing K2/K3/IFRS 16, komponentavskrivning, avyttring/utrangering with VAT.
Trigger on ANY Swedish avskrivning, anläggningstillgång, överavskrivning, inventarieregister, förbrukningsinventarie,
BAS 10xx-12xx/78xx, leasing K2/K3, asset disposal, komponentavskrivning, or fixed-asset question.
High-error-rate area; always use over training data.
---
# Swedish Asset Accounting (Anläggningsredovisning)
Developer compliance reference for Swedish fixed asset accounting. Core laws: ÅRL (1995:1554), IL (1999:1229), BFL (1999:1078). Frameworks: K2 (BFNAR 2016:10), K3 (BFNAR 2012:1), IFRS 16, RFR 2.
**Before answering any question, read the appropriate reference file:**
- For depreciation methods, överavskrivning, or tax depreciation rules: `cat references/depreciation.md`
- For BAS account mapping, anläggningsregister, or förbrukningsinventarier: `cat references/accounts-and-registry.md`
- For leasing treatment or disposal/scrapping: `cat references/leasing-and-disposal.md`
If the question spans multiple areas, read all relevant files.
## Quick Decision Tree
```
Is the asset below half prisbasbelopp (29,600 kr 2026) or useful life ≤ 3 years?
├─ YES → Expense immediately via 54xx (förbrukningsinventarie)
└─ NO → Capitalize to 10xx/11xx/12xx
Is it a building?
├─ YES → IL 19 kap rates (2-5% straight-line on cost)
│ K2: no components, use SKV standard rates
│ K3: mandatory component depreciation
└─ NO → Machinery/inventory: IL 18 kap
├─ Räkenskapsenlig: 30% declining or 20% straight-line (choose yearly)
└─ Restvärdeavskrivning: 25% declining (fallback)
Book ≠ Tax? → Bridge via 2150/8850 (överavskrivning)
```
## Critical K2 vs K3 Differences (Assets)
| Feature | K2 | K3 |
|---|---|---|
| Component depreciation | Forbidden | Mandatory |
| Useful life | 5-year default allowed | Individual assessment required |
| Residual value | Optional, forbidden with 5-year rule | Required, annual reassessment |
| Depreciation start | Year put into use | When available for use |
| Revaluation | Permitted for buildings/land | Permitted |
| Deferred tax | Forbidden | Required (ch. 29) |
| Leasing on BS | Never | Financial leases (but 20.29 exemption in juridisk person) |
| Development costs | Must expense | May capitalize |
## 2026 Regulatory Change
From fiscal years starting after 2025-12-31, entities with buildings generating ≥75% of net revenue (fastighetsbolag, BRF:er) must use K3. This forces component depreciation adoption. BFN published updated BFNAR 2016:10 and 2012:1 effective for these fiscal years.
## Prisbasbelopp Reference (Half PBB = förbrukningsinventarie threshold)
| Year | PBB | Threshold |
|---|---|---|
| 2024 | 57,300 | 28,650 |
| 2025 | 58,800 | 29,400 |
| 2026 | 59,200 | 29,600 |
@@ -0,0 +1,196 @@
# BAS Account Mapping & Anläggningsregister
## BAS Account Structure for Fixed Assets
Pattern: xx0 = asset, xx8 = accumulated impairment (nedskrivning), xx9 = accumulated depreciation.
### Intangible Assets (Kontogrupp 10)
| Account | Description | Notes |
|---|---|---|
| 1010 | Utvecklingsutgifter | Ej K2 (must expense under K2) |
| 1019 | Ack. avskr. utvecklingsutgifter | |
| 1020 | Koncessioner | |
| 1029 | Ack. avskr. koncessioner | |
| 1030 | Patent | |
| 1039 | Ack. avskr. patent | |
| 1040 | Licenser | |
| 1049 | Ack. avskr. licenser | |
| 1050 | Varumärken | |
| 1059 | Ack. avskr. varumärken | |
| 1060 | Hyresrätter och tomträtter | |
| 1069 | Ack. avskr. hyresrätter | |
| 1070 | Goodwill | |
| 1079 | Ack. avskr. goodwill | |
| 1080 | Pågående projekt/förskott immateriella | |
| 1088 | Ack. nedskrivningar immateriella | |
### Buildings and Land (Kontogrupp 11)
| Account | Description | Notes |
|---|---|---|
| 1110 | Byggnader | 1111 egen mark, 1112 annans mark |
| 1118 | Ack. nedskr. byggnader | |
| 1119 | Ack. avskr. byggnader | |
| 1120 | Förbättringsutgifter annans fastighet | |
| 1129 | Ack. avskr. förbättringsutgifter | |
| 1130 | Mark | Never depreciated |
| 1140 | Tomter och obebyggda markområden | |
| 1150 | Markanläggningar | |
| 1159 | Ack. avskr. markanläggningar | |
| 1180 | Pågående nyanläggningar/förskott | |
### Machinery and Inventory (Kontogrupp 12)
| Account | Description | Notes |
|---|---|---|
| 1210 | Maskiner och andra tekniska anläggningar | |
| 1211 | Maskiner | |
| 1213 | Övriga tekniska anläggningar | |
| 1218 | Ack. nedskr. maskiner | |
| 1219 | Ack. avskr. maskiner | |
| 1220 | Inventarier och verktyg | |
| 1221 | Inventarier | |
| 1222 | Byggnadsinventarier | Tax: follows IL 18 kap, not building rates |
| 1223 | Markinventarier | Tax: follows IL 18 kap |
| 1228 | Ack. nedskr. inventarier | |
| 1229 | Ack. avskr. inventarier | |
| 1230 | Installationer | |
| 1239 | Ack. avskr. installationer | |
| 1240 | Bilar och andra transportmedel | |
| 1241 | Personbilar | |
| 1242 | Lastbilar | |
| 1243 | Truckar | |
| 1244 | Arbetsmaskiner | |
| 1245 | Traktorer | |
| 1246 | Övriga transportmedel | |
| 1249 | Ack. avskr. bilar/transport | |
| 1250 | Datorer | |
| 1259 | Ack. avskr. datorer | |
| 1260 | Leasade tillgångar | Ej K2. K3 financial leases, IFRS 16 ROU. |
| 1269 | Ack. avskr. leasade tillgångar | |
| 1280 | Pågående nyanläggningar/förskott | |
| 1290 | Övriga materiella anläggningstillgångar | |
| 1291 | Konst och liknande tillgångar | Not depreciated |
| 1299 | Ack. avskr. övriga materiella | |
### Impairment Expense (Kontogrupp 77)
| Account | Description |
|---|---|
| 7710 | Nedskrivningar immateriella |
| 7720 | Nedskrivningar byggnader/mark |
| 7730 | Nedskrivningar maskiner/inventarier |
| 7760 | Återföring nedskrivning immateriella |
| 7770 | Återföring nedskrivning byggnader/mark |
| 7780 | Återföring nedskrivning maskiner/inventarier |
### Överavskrivningar (Kontogrupp 21/88)
| Account | Description |
|---|---|
| 2150 | Ackumulerade överavskrivningar |
| 2151 | ...immateriella tillgångar |
| 2152 | ...byggnader |
| 2153 | ...maskiner och inventarier |
| 8850 | Förändring av överavskrivningar |
| 8851 | ...immateriella |
| 8852 | ...byggnader |
| 8853 | ...maskiner och inventarier |
### Disposal Accounts (Kontogrupp 39/79)
| Account | Type | Description |
|---|---|---|
| 3970 | Intäkt | Vinst vid avyttring (övergripande) |
| 3971 | Intäkt | ...immateriella |
| 3972 | Intäkt | ...byggnader/mark |
| 3973 | Intäkt | ...maskiner/inventarier |
| 7970 | Kostnad | Förlust vid avyttring (övergripande) |
| 7971 | Kostnad | ...immateriella |
| 7972 | Kostnad | ...byggnader/mark |
| 7973 | Kostnad | ...maskiner/inventarier |
---
## Anläggningsregister (Fixed Asset Register)
### Legal Basis
BFL 5 kap. 4 § requires subsidiary accounting (sidoordnad bokföring) for fixed assets when needed for satisfactory control. Detailed requirements in **BFNAR 2013:2 chapter 4**.
### Required Data per Asset (BFNAR 2013:2 punkt 4.5)
1. **Identifieringsuppgifter** (serial number, model, description)
2. **Anskaffningsvärde** (acquisition cost)
3. **Anskaffningstidpunkt** (acquisition date)
4. **Beräknat restvärde** (estimated residual value)
5. **Tillämpad avskrivningsmetod** (if non-linear, or note that asset is not depreciated)
6. **Nyttjandeperiod** (useful life)
7. **Ackumulerade avskrivningar** (accumulated depreciation)
8. **Ackumulerade nedskrivningar** (accumulated impairment)
9. **Korrigeringar** (corrections of prior years' depreciation/impairment)
10. **Omklassificeringar** (reclassifications)
### Additional Requirements
- **AB/EF (punkt 4.6):** Must also track uppskrivningar (revaluations)
- **K3 (punkt 4.8):** Must record information per component
- **Grouping (punkt 4.9):** Similar low-value assets acquired simultaneously may be grouped as one entry
- **Disposals (punkt 4.7):** Must note date and proceeds of disposals/scrappings
- **Theft-attractive items (punkt 4.11):** Items expensed as förbrukningsinventarier but stöldbegärliga should still be tracked
### Note Disclosure (ÅRL 5 kap. 8 §, 25 §)
Larger companies must disclose per asset category:
- Opening and closing acquisition values
- Period depreciation, impairment, revaluation
- Acquisitions and disposals during period
---
## Förbrukningsinventarier
### Legal Basis: IL 18 kap. 4 §
Immediate expensing permitted for:
**1. Inventarier av mindre värde** (below half prisbasbelopp):
| Year | Prisbasbelopp | Threshold (half PBB) |
|---|---|---|
| 2024 | 57,300 kr | **28,650 kr** |
| 2025 | 58,800 kr | **29,400 kr** |
| 2026 | 59,200 kr | **29,600 kr** |
Measured excluding deductible VAT.
**2. Korttidsinventarier** (useful life ≤ 3 years, regardless of value).
### Grouping Rule (IL 18 kap. 4 § st. 2)
Items with **naturligt samband** (functional connection) acquired for use together must have combined cost measured against threshold. Example: conference table + chairs = one unit. Items in a larger procurement for similar purposes (e.g., all office computers replaced) evaluated on total cost.
### BAS Accounts for Förbrukningsinventarier (54xx)
| Account | Description |
|---|---|
| 5410 | Förbrukningsinventarier |
| 5411 | Förbrukningsinventarier med > 1 år livslängd |
| 5412 | Förbrukningsinventarier med ≤ 1 år livslängd |
| 5420 | Programvaror |
| 5430 | Transportinventarier |
| 5440 | Förbrukningsemballage |
### Classification Decision
```
Cost < half PBB (excl. deductible VAT)?
├─ YES → Expense to 54xx
└─ NO
└─ Useful life ≤ 3 years?
├─ YES → Expense to 54xx (korttidsinventarie)
└─ NO → Capitalize to 12xx, depreciate via 78xx
```
**Exception for non-VAT entities:** Threshold effectively rises by the non-deductible VAT amount since cost includes it.
@@ -0,0 +1,145 @@
# Depreciation Methods & Tax Rules
## Planenlig avskrivning (Book Depreciation)
ÅRL 4 kap. 4 § requires systematic depreciation of all fixed assets with limited useful life.
### Methods
**Linjär (straight-line):** `(anskaffningsvärde restvärde) / nyttjandeperiod`. Most common.
**Degressiv (declining balance):** Fixed % of remaining book value. Higher charges early. Suitable for vehicles, tech.
**Produktionsberoende (units-of-production):** `(cost residual) × (units produced / total expected)`. K2 additionally allows progressiv for machinery (never buildings).
### Useful Life
- **K3 (17.16):** Individual assessment per asset. Mandatory reassessment on indicators of change.
- **K2 (10.27):** Permits standardized **5-year useful life** for machinery, inventory, intangibles. Buildings may follow SKV standard rates (industrial 4-5%, offices 2-4%, residential 2%).
- **Goodwill/dev costs:** Default 5 years when life cannot be reliably determined (ÅRL 4 kap. 4 §).
### Residual Value
- **K3 (17.14):** Must estimate and reassess annually.
- **K2 (10.25):** Optional. Forbidden with 5-year rule or for buildings.
### Depreciation Start
- **K2 (10.23):** From year asset is *put into use* (tas i bruk). Full annual amount regardless of partial year.
- **K3 (17.19):** From when asset *can* be used (available for use). Buildings under K2 depreciate even before use.
### Component Depreciation (Komponentavskrivning)
- **K3 (17.4-17.5):** MANDATORY. Decompose into components with materially different useful lives.
- **K2 (10.2):** FORBIDDEN.
Typical building components and lives:
| Component | Years |
|---|---|
| Stomme (structure) | 80-160 |
| Stammar (plumbing) | 40-60 |
| Tak (roof) | 30-50 |
| Fasad (facade) | 30-60 |
| Fönster (windows) | 25-40 |
| Hissar (elevators) | 25-40 |
| VVS/ventilation | 20-30 |
| Styr & övervakning | 10-20 |
When a component is replaced under K3 (17.21-17.22): derecognize old component's remaining book value, capitalize new component separately.
### BAS Expense Accounts (78xx)
- 7810: Avskrivningar immateriella tillgångar
- 7820: Avskrivningar byggnader/markanläggningar (7821 byggnader, 7824 markanläggningar)
- 7830: Avskrivningar maskiner/inventarier (7831-7835 specific types)
- 7836: Avskrivningar leasade tillgångar
- 7840: Avskrivningar förbättringsutgifter annans fastighet
---
## Överavskrivning (Excess Tax Depreciation)
When skattemässig avskrivning > planenlig avskrivning, the difference is an **obeskattad reserv** (untaxed reserve).
### BAS Accounts
**Balance sheet:** 2150 Ackumulerade överavskrivningar
- 2151: Immateriella tillgångar
- 2152: Byggnader
- 2153: Maskiner och inventarier
**Income statement (bokslutsdisposition):** 8850 Förändring av överavskrivningar
- 8851-8853: Matching sub-accounts
### Booking Pattern
- Increase överavskrivning: **Debit 8850 / Credit 2150**
- Decrease överavskrivning: **Debit 2150 / Credit 8850**
### Constraint
Book value must NEVER fall below tax value:
`skattemässigt restvärde = konto 12xx (konto 12x9 + konto 2150)`
### K2 vs K3
- **K2:** Often eliminates need for överavskrivning entirely. 5-year rule (20%) matches kompletteringsregeln. Building rates can match SKV. No deferred tax permitted.
- **K3:** Strict separation required. Book depreciation must reflect economic consumption. Deferred tax required (ch. 29). In juridisk person, K3 29.37 permits obeskattade reserver at gross. In consolidated accounts: split into equity (79.4%) and uppskjuten skatteskuld (20.6% at current corporate tax rate).
---
## Skattemässig avskrivning (Tax Depreciation)
### Inventarier (IL 18 kap.)
One method for ALL inventory. No mixing.
#### Räkenskapsenlig avskrivning (IL 18 kap. 13-17 §§)
Requirements: ordnad bokföring + årsbokslut + tax depreciation = book depreciation.
**Huvudregeln (30% declining balance):**
- Deduct up to 30% of avskrivningsunderlag annually
- Underlag = book value at year start + acquisitions sale proceeds for items owned at year start
- Minimum book value = 70% of underlag
- Rate adjusts proportionally for fiscal years ≠ 12 months
**Kompletteringsregeln (20% straight-line):**
- Full depreciation over exactly 5 years at 20%/year on original acquisition cost
- Requires tracking each asset's acquisition year and cost for prior 4 fiscal years
- Can switch between rules each year
**Practical:** Huvudregeln better years 1-2. Kompletteringsregeln superior from year 3 onward.
#### Restvärdeavskrivning (IL 18 kap. 13 § st. 3)
- Max **25% declining balance** on skattemässigt restvärde
- No book/tax match required
- Fallback method when räkenskapsenlig requirements cannot be met
- No supplementary rule: assets theoretically never fully depreciated
| Feature | Räkenskapsenlig | Restvärdeavskrivning |
|---|---|---|
| Legal basis | IL 18:13-17 | IL 18:13 st.3 |
| Max rate | 30% declining / 20% SL | 25% declining |
| Book = tax | Required | Not required |
| Full depr. | Yes (5yr komplettering) | No (asymptotic) |
| Ordnad bokföring | Required | Not required |
### Byggnader (IL 19 kap.)
Straight-line on acquisition cost. SKV standard rates (SKV A 2005:5):
| Building type | Rate |
|---|---|
| Industrial (industri) | 4% |
| Office/residential | 2% |
| Hotel/parking | 3% |
| Kiosk | 5% |
**Primäravdrag (since 2019):** Additional 2%/year for first 6 years on new construction of hyreshus.
**Mark (land):** Never depreciated.
**Missed year:** Cannot be recovered later.
@@ -0,0 +1,192 @@
# Leasing Treatment & Asset Disposal
## Leasing Across Frameworks
### K2 (BFNAR 2016:10)
ALL leases treated as operating. No exceptions.
**Booking:** Expense directly:
- 5210: Hyra av anläggningstillgångar
- 5220: Hyra av inventarier och verktyg
- 5615: Leasing av personbilar
**Förhöjd förstagångshyra (first enhanced payment):** Prepaid via **1720 Förutbetalda leasingavgifter**, allocated linearly over lease term.
No assets or liabilities appear on balance sheet.
### K3 (BFNAR 2012:1, Chapter 20)
Classifies leases as **financial** or **operating** using IAS 17 criteria:
1. Transfer of ownership at end of term
2. Bargain purchase option
3. Lease covers major part of economic life
4. Present value of payments ≈ fair value
5. Specialized asset
**Financial leases are capitalized:**
- Asset: **1260 Leasade tillgångar** (depreciated via 7836 / 1269)
- Liability: **2391** (short-term) / **2350** (long-term leasingskuld)
- Payments split: interest (8410) + amortization via effective interest method
**K3 point 20.29 exemption:** In the **juridisk person** (legal entity), financial leases MAY be accounted for as operating. This preserves the tax connection. In **consolidated accounts**, financial leases MUST go on balance sheet.
**Operating leases under K3:** Same as K2 treatment, expensed directly.
### IFRS 16
All leases on balance sheet via right-of-use model:
**Recognition:**
- **Nyttjanderättstillgång (ROU asset):** 1260, at PV of lease payments
- **Leasingskuld:** 2350 (long-term) / 2391 (short-term)
- Discount rate: implicit rate in lease, or marginal borrowing rate
**Two exemptions only:**
1. Short-term leases (≤ 12 months) - expense directly
2. Low-value assets (< ~USD 5,000 when new) - expense directly
**RFR 2 exemption:** In the juridisk person, IFRS 16 need NOT be applied. Lease payments recorded as straight-line expense, preserving tax connection.
### Tax Treatment of Leasing
Swedish tax law follows **legal form**, not accounting classification:
- **Lessee:** Deducts lease payments
- **Lessor:** Claims depreciation as legal owner
Regardless of whether the lease is capitalized in accounting.
**Key 2024 ruling:** Capitalizing financial leases does NOT forfeit the right to räkenskapsenlig avskrivning on owned assets, provided adjustments are made in the tax return (INK2/INK4).
**VAT on financial leasing (2024 Skatteverket ställningstagande):** Financial leases may be classified as supply of goods rather than services. For **personbilar** this triggers the **full VAT deduction prohibition** on purchases, instead of the usual 50% deduction available for lease services.
### Leasing Summary Table
| Feature | K2 | K3 (juridisk person) | K3 (koncern) | IFRS 16 (koncern) | IFRS 16 (JP via RFR 2) |
|---|---|---|---|---|---|
| Financial on BS | Never | Optional (20.29) | Mandatory | Mandatory | Not required |
| Operating treatment | All leases | All (if 20.29 used) | Operating only | Short-term + low-value only | All (expense) |
| Asset account | N/A | 1260 | 1260 | 1260 | N/A |
| Depreciation account | N/A | 7836 | 7836 | 7836 | N/A |
---
## Avyttring (Disposal/Sale)
### Book Value at Disposal
`Bokfört värde = Anskaffningsvärde Ackumulerade avskrivningar`
Asset must be depreciated up to the disposal date before calculating gain/loss.
### Booking Pattern (Sale of Inventory)
**Step 1 - Record sale with VAT:**
```
Debit 1510 Kundfordran [sale price incl. 25% VAT]
Credit 2611 Utgående moms 25% [VAT amount]
Credit 3973 Vinst avyttring [sale price ex VAT] (or Debit 7973 if loss)
```
**Step 2 - Remove asset from books:**
```
Debit 1229 Ack. avskr. inventarier [accumulated depreciation]
Debit 7973 Förlust avyttring [remaining book value] (or Credit 3973 if already used above)
Credit 1221 Inventarier [original acquisition cost]
```
**Alternative (net method):** Some systems use a combined entry. The gain/loss equals: `försäljningspris ex moms bokfört restvärde`.
### Gain/Loss Accounts
| Account | Direction | Description |
|---|---|---|
| 3971 | Credit (gain) | Vinst avyttring immateriella |
| 3972 | Credit (gain) | Vinst avyttring byggnader/mark |
| 3973 | Credit (gain) | Vinst avyttring maskiner/inventarier |
| 7971 | Debit (loss) | Förlust avyttring immateriella |
| 7972 | Debit (loss) | Förlust avyttring byggnader/mark |
| 7973 | Debit (loss) | Förlust avyttring maskiner/inventarier |
### Practical Example: Sale at Profit
Asset: anskaffningsvärde 50,000 kr, ack. avskr. 10,000 kr (book value 40,000 kr).
Sold for 56,000 kr ex VAT.
```
Debit 1510 70,000 (56,000 + 14,000 VAT)
Credit 2611 14,000 (25% moms)
Credit 3973 16,000 (gain: 56,000 40,000)
Debit 1229 10,000 (remove accumulated depreciation)
Credit 1221 50,000 (remove asset at cost)
Debit 7973 40,000 (book value to loss account)
```
Net effect on income: 3973 16,000 credit + 7973 40,000 debit = 3973 16,000 gain (after netting with book value removal, the two 7973 entries cancel).
**Note:** Many Swedish systems handle this more cleanly by netting directly. The above shows the full debit/credit flow.
---
## Utrangering (Scrapping)
Same as disposal with zero sale proceeds. Remaining book value written off entirely.
**K2 (7.14):** Recognized when asset can no longer be used AND company does not intend to repair or sell.
### Booking Pattern
```
Debit 1229 Ack. avskr. [accumulated depreciation]
Debit 7973 Förlust [remaining book value]
Credit 1221 Inventarier [original acquisition cost]
```
**Fully depreciated assets:** No income statement effect.
```
Debit 1229 [full amount]
Credit 1221 [full amount]
```
---
## VAT on Asset Disposal
**General rule:** 25% moms on sale of movable business assets.
### Exceptions
1. **Verksamhetsöverlåtelse (business transfer, ML 5 kap. 38 §):** No VAT when transferring entire business or independent branch.
2. **No original input VAT deduction:** No output VAT on sale if input VAT was never deducted.
3. **Real property (fastighet):** Generally VAT-exempt sales.
### Jämkning (VAT Adjustment Rules, ML 15 kap.)
Applies to capital goods where significant input VAT was deducted:
| Asset type | Correction period | Threshold (ingående moms) |
|---|---|---|
| Byggnader | 10 years | ≥ 100,000 kr |
| Maskiner/inventarier | 5 years | ≥ 50,000 kr |
If a building is sold outside a business transfer, seller must repay remaining investment VAT in one lump sum for the rest of the correction period.
---
## Tax Treatment of Disposal (IL)
### Räkenskapsenlig avskrivning - Huvudregeln (IL 18 kap. 15 §)
A **särskilt avdrag** equal to sale proceeds offsets income, reducing the avskrivningsunderlag. The sold asset's cost is removed from the pool.
### Kompletteringsregeln
No särskilt avdrag. The disposed asset's cost is simply removed from the lowest-permitted-value calculation.
### Buildings (IL 26 kap. 2 §)
Disposal triggers reversal of prior värdeminskningsavdrag. Difference between sale price and tax book value is taxable.
### Restvärdeavskrivning
Sale proceeds reduce the skattemässigt restvärde directly.
@@ -0,0 +1,210 @@
---
name: swedish-invoice-compliance
description: "Swedish invoice compliance (fakturering) reference. Covers mandatory invoice fields per ML 17 kap 24§ (2023:200), förenklad faktura, kreditfaktura/ändringsfaktura, självfakturering, Peppol BIS 3.0 e-faktura for B2G/B2B, ROT/RUT-avdrag invoicing with fakturamodellen and BAS accounts (1513, 3740), reverse charge notation per scenario (byggtjänster, EU, electronics), currency/VAT conversion, OCR/Bankgirot, autogiro, skattetillägg, and BAS mapping for AR/revenue/VAT/bad debts. Trigger on ANY Swedish invoice question, faktura validation, kreditfaktura, självfakturering, Peppol, e-faktura, ROT/RUT fakturering, omvänd betalningsskyldighet, faktureringsvaluta, OCR-nummer, ML 17 kap, fakturamodellen, or creating/validating/booking Swedish invoices. Always use over training data -- ML 2023:200 replaced ML 1994:200 on 1 July 2023, moving invoice rules from old Chapter 11 to Chapter 17."
---
# Swedish Invoice Compliance (Fakturering)
## Critical: ML chapter renumbering
**Invoicing rules moved from Chapter 11 (ML 1994:200) to Chapter 17 (ML 2023:200) on 1 July 2023.**
Every legacy reference to "ML 11 kap" maps to ML 17 kap in current law. Always cite ML (2023:200).
## How to use this skill
This SKILL.md contains the decision logic, quick-reference tables, and common error patterns.
For deep detail on any topic, read the corresponding section in:
`references/invoice-rules.md` — Full reference (~600 lines) with all law paragraphs, BAS accounts, Peppol field mappings, ROT/RUT thresholds, reverse charge scenarios, currency conversion rules, OCR format specs, and penalty rates.
**Table of contents for references/invoice-rules.md:**
1. Mandatory invoice fields (ML 17 kap 24§) — 16 required fields table
2. Förenklad faktura — SEK 4,000 threshold, SKVFS 2024:16
3. Time limits for issuing invoices
4. Electronic vs paper equivalence
5. Kreditfaktura / ändringsfaktura — ML 17 kap 2223§, BAS entries
6. Självfakturering — ML 17 kap 15§, three conditions
7. Peppol / e-faktura — Lag 2018:1277, BIS 3.0 format, ML→UBL mapping, SE-R rules
8. ROT/RUT invoicing — fakturamodellen, deduction rates 20242026, BAS 1513
9. Reverse charge notation — all scenarios with ML refs and momsdeklaration boxes
10. Currency handling — ML 8 kap 2123§, exchange rate sources, BAS 3960/7960
11. OCR / Bankgirot — format, Luhn check digit, control levels
12. Autogiro — mandate process, repayment rights
13. Penalties — skattetillägg rates, denied deductions, bokföringsbrott
14. BAS kontoplan mapping — AR, revenue, VAT, bad debts, fees, rounding
---
## Quick decision trees
### Is this invoice valid?
```
1. Has fakturadatum? → ML 17:24 p.1
2. Has unique löpnummer from a series? → ML 17:24 p.2
3. Seller's momsreg.nr (SE+10+01)? → ML 17:24 p.3
4. Buyer's momsreg.nr (if RC or intra-EU)? → ML 17:24 p.4
5. Full name+address, both parties? → ML 17:24 p.5
6. Description: quantity+nature of goods/svc? → ML 17:24 p.6
7. Delivery date (if ≠ invoice date)? → ML 17:24 p.7
8. Tax base per rate, unit price excl VAT? → ML 17:24 p.8
9. VAT rate stated (25/12/6%)? → ML 17:24 p.9
10. VAT amount in SEK? → ML 17:24 p.10
11. Special notations where required?
- Reverse charge → "Omvänd betalningsskyldighet"
- Self-billing → "Självfakturering"
- Exempt → ML/Directive reference
- Margin scheme → Scheme notation
```
Missing any of 110 = non-compliant. Missing 11 when applicable = non-compliant.
### Can a simplified invoice be used?
```
Total incl. VAT ≤ SEK 4,000?
AND NOT intra-EU / distance sale / cross-border RC?
→ Yes: förenklad faktura per ML 17:2628, SKVFS 2024:16
→ No: full invoice required
```
### Which reverse charge scenario?
```
Domestic byggtjänster?
→ ML 16:13, seller Box 41, buyer Box 24/30/48, accounts 3231/2614/2647
EU services (B2B main rule)?
→ ML 16:6 + 6:3337, buyer Box 21/3032/48, accounts 4545/2614/2645
Intra-EU goods?
→ ML 10:42, buyer Box 20/3032/48, accounts 4535/2614/2645
Electronics >100k SEK/invoice?
→ ML 16:17, same treatment as byggtjänster
```
### ROT or RUT invoice?
```
1. Company has F-skatt? → Required
2. Invoice shows arbetskostnad separately? → Required
3. Customer personnummer on invoice? → Required
4. ROT: fastighetsbeteckning included? → Required
5. Skattereduktion amount calculated correctly?
ROT: 30% of labor incl. moms (50% MayDec 2025)
RUT: 50% of labor incl. moms
6. Combined max per person/year:
Standard: ROT 50k + RUT 75k, combined cap 75k
2024 H2 temporary: ROT 75k + RUT 75k, separate caps
7. Customer paying electronically? → Required since 2020
8. AR split: 1511 (customer) + 1513 (SKV) → Required
```
### Credit note checklist
```
1. Own unique fakturanummer + fakturadatum? → Required
2. Reference to original invoice number? → Required (ML 17:2223)
3. Negative amounts with VAT per original rate? → Required
4. "Er tillgodo" instead of "Att betala"? → Convention
5. Seller reduces utgående moms this period? → Required
6. Buyer reduces ingående moms this period? → Required
```
---
## Common error patterns (high-frequency in Luka validation)
| Error | Consequence | Fix |
|---|---|---|
| Missing delivery date when ≠ invoice date | Buyer's VAT deduction at risk | Always populate if dates differ |
| Löpnummer gaps or duplicates | BFL 5:6 violation, audit red flag | Enforce sequential numbering in DB |
| No "Omvänd betalningsskyldighet" text on RC invoice | Buyer cannot self-assess VAT | Add text + buyer VAT ID, charge 0% VAT |
| Seller charges VAT when RC applies | Buyer CANNOT deduct the incorrectly charged VAT | Credit note required, then reissue without VAT |
| Kreditfaktura missing reference to original | Invalid credit note per ML 17:22 | Include original löpnummer |
| ROT invoice missing fastighetsbeteckning | SKV will deny claim | Require field when ROT flag is set |
| ROT/RUT not separating labor from materials | Deduction calculated on wrong base | Separate line items: arbetskostnad vs material |
| Foreign currency invoice without SEK VAT | Non-compliant per ML 17:29 | Always show VAT amount in SEK |
| VAT amount only, no tax base per rate | Incomplete per ML 17:24 p.8 | Show beskattningsunderlag per skattesats |
| Self-billing without "Självfakturering" text | Invoice invalid per ML 17:15 | Add notation |
---
## BAS account quick reference
### Accounts receivable
- **1510** Kundfordringar (main)
- **1513** Kundfordringar delad faktura (ROT/RUT SKV portion)
- **1515** Osäkra kundfordringar
- **1519** Nedskrivning av kundfordringar (contra)
### Revenue
- **3001/3002/3003/3004** Domestic sales 25%/12%/6%/exempt
- **3105** Export goods, **3108** EU goods
- **3231** Byggsektorn omvänd betalningsskyldighet
- **3305** Export services, **3308** EU services
### VAT
- **26102615** Utgående moms 25% (domestic/RC/import)
- **26202624** Utgående moms 12%
- **26302634** Utgående moms 6%
- **2640** Ingående moms, **2645** Beräknad ingående moms utlandet
- **2647** Ingående moms omvänd betalningsskyldighet Sverige
### Currency differences
- **3960** Valutakursvinster rörelsefordringar/-skulder
- **7960** Valutakursförluster rörelsefordringar/-skulder
### Invoice extras
- **3540** Faktureringsavgift (25% VAT)
- **3740** Öresavrundning (no VAT)
- **3930** Påminnelseavgift (no VAT)
- **8313** Dröjsmålsränta (no VAT, financial income)
---
## Peppol essentials (for Luka e-invoice generation)
Format: UBL 2.1 XML, profile Peppol BIS Billing 3.0.
TypeCodes: **380** = invoice, **381** = credit note, **389** = self-billing.
Swedish org ID scheme: **0007** + 10-digit orgnr.
SE validation rules: SE-R-001 (VAT ID 14 chars), SE-R-005 (F-skatt text), SE-R-006 (valid rates), SE-R-009 (Bankgiro 78 chars).
Required header: `CustomizationID` + `ProfileID` (exact URNs in reference file).
Required: either `BuyerReference` (BT-10) or `OrderReference` (BT-13).
**B2G mandatory since April 2019.** B2B voluntary; formal inquiry launched Feb 2026, report due Nov 2027. ViDA mandates cross-border B2B e-invoicing by July 2030.
---
## Key law references
| Topic | Current law | Old law |
|---|---|---|
| Invoice content | ML 17 kap 24§ (2023:200) | ML 11 kap 8§ (1994:200) |
| Simplified invoice | ML 17 kap 2628§ | ML 11 kap 9§ |
| Credit note | ML 17 kap 2223§ | ML 11 kap 10§ |
| Self-billing | ML 17 kap 15§ | ML 11 kap 4§ |
| Reverse charge | ML 16 kap 622§§ | ML 1 kap 2§ st.4 |
| Currency conversion | ML 8 kap 2123§ | ML 7 kap 7a§ |
| E-invoice B2G | Lag (2018:1277) | — |
| ROT/RUT | HUSFL (2009:194) 69§§ | — |
| Invoice archiving | BFL 7 kap | — |
| Skattetillägg | SFL 49 kap | — |
---
## Time-dependent parameters
These values change. Always verify against the reference file or search current rates:
- Förenklad faktura threshold: **SEK 4,000** (SKVFS 2024:16)
- ROT deduction %: 30% standard, 50% MayDec 2025
- RUT deduction %: 50%
- ROT max/person/year: 50,000 SEK (75,000 in 2024 H2)
- RUT max/person/year: 75,000 SEK
- Combined max: 75,000 SEK (separated in 2024 H2)
- Electronics RC threshold: 100,000 SEK excl. VAT per invoice
- Skattetillägg VAT: 20% (periodization: 25%)
- Archive retention: 7 years (BFL 7 kap)
@@ -0,0 +1,809 @@
# Swedish Invoice Compliance — Full Reference
## 1. Mandatory invoice fields (ML 17 kap 24§)
A fullständig faktura must contain all fields per **17 kap 24§ ML (2023:200)**, implementing EU VAT Directive Article 226.
| # | Field (Swedish) | Field (English) | ML ref |
|---|---|---|---|
| 1 | Fakturadatum | Invoice issue date | 17:24 p.1 |
| 2 | Löpnummer (unique sequential from one or more series) | Invoice number | 17:24 p.2 |
| 3 | Säljarens momsregistreringsnummer (SE + 10 digits + 01) | Seller VAT ID | 17:24 p.3 |
| 4 | Köparens momsregistreringsnummer (when RC or intra-EU) | Buyer VAT ID | 17:24 p.4 |
| 5 | Fullständigt namn och adress, säljare och köpare | Names and addresses | 17:24 p.5 |
| 6 | Varornas mängd och art / tjänsternas omfattning och art | Quantity and nature | 17:24 p.6 |
| 7 | Leveransdatum eller tillhandahållandedatum (if ≠ invoice date) | Delivery date | 17:24 p.7 |
| 8 | Beskattningsunderlag per skattesats, enhetspris exkl. moms, rabatter | Tax base per rate | 17:24 p.8 |
| 9 | Mervärdesskattesats (25%, 12%, or 6%) | VAT rate | 17:24 p.9 |
| 10 | Mervärdesskattebelopp | VAT amount | 17:24 p.10 |
| 11 | "Omvänd betalningsskyldighet" (if reverse charge) | RC notation | 17:24 |
| 12 | ML/Directive reference (if VAT-exempt) | Exemption ref | 17:24 |
| 13 | "Självfakturering" (if self-billing) | Self-billing text | 17:24 |
| 14 | Margin scheme notation (if applicable) | Margin scheme | 17:24 |
| 15 | Transport media details (new vehicles to EU) | Vehicle specifics | 17:24 |
| 16 | Förskottsbetalning amount (advance payment) | Advance payment | 17:17 |
**Löpnummerserie:** Must enable detection of missing invoices. Multiple series permitted (per unit, POS, etc.). Also required by BFL 5 kap 6§. Gaps or duplicates are a compliance red flag.
## 2. Förenklad faktura (simplified invoice)
**ML 17 kap 2628§**, **SKVFS 2024:16**.
Threshold: total **≤ SEK 4,000 including VAT**.
Also permitted when trade/technical conditions make full invoicing impractical (vending machines, fuel pumps, parking meters).
Reduced content: date, seller ID (VAT/org number), description of goods/services, VAT amount or data to calculate it.
**Cannot** be used for: intra-EU transactions, distance sales, cross-border reverse charge.
## 3. Time limits for issuing invoices
- Domestic: no hard statutory deadline; "without undue delay" per god affärssed.
- Intra-EU goods/services (main rule): **15th of month following delivery/performance** (17 kap 19§).
- Construction services: **end of second month after performance**.
## 4. Electronic vs paper equivalence
Electronic invoices = identical legal standing (2 kap 910§ ML). E-invoicing requires buyer consent (17 kap 20§). PDF by email is a legal faktura but not a structured e-faktura. Archive for **7 years** (BFL 7 kap). Since July 2024, paper originals may be destroyed immediately after digital transfer (amended 7 kap 6§ BFL).
No specific language requirement in ML. VAT amounts must be in SEK if accounting currency is SEK. Skatteverket may request translations.
## 5. Kreditfaktura / ändringsfaktura
Term in ML (2023:200): **ändringsfaktura**. Business terms "kreditfaktura" and "kreditnota" remain in use.
### When to issue
Incorrect original, goods returned, price reduction post-invoicing, partial/full cancellation, erroneous VAT correction.
### Governing law
**17 kap 2223§ ML (2023:200)**.
### Mandatory content
1. The specific change to the original invoice
2. Specific and unambiguous reference to the original invoice (typically löpnummer)
3. What has been changed
4. Own unique fakturanummer and fakturadatum
5. Amounts shown as negative values
6. VAT specified per momssats from the original
Notation: "Er tillgodo" replaces "Att betala".
When citing specific original is impractical (volume rebates): customer number + date range + description is accepted.
### Partial credits
Fully permitted. Must specify which items credited, partial negative amount, proportional VAT per skattesats.
### VAT adjustment mechanics
Seller reduces utgående moms in the credit note period. Buyer must reduce ingående moms in the same period. For felaktigt debiterad mervärdesskatt, a valid kreditfaktura is a prerequisite before seller can adjust VAT return.
### BAS journal entries
**Seller issuing credit note (25% example):**
| Account | Debit | Credit |
|---|---|---|
| 3011 Försäljning tjänster 25% | X | |
| 2611 Utgående moms 25% | X | |
| 1510 Kundfordringar | | X |
12%: 3002/2621. 6%: 3003/2631. Mirror reversal of original.
**Buyer receiving credit note:**
Debit 2440 Leverantörsskulder, Credit purchase account (40xx), Credit 2641 Ingående moms.
## 6. Självfakturering (self-billing)
**ML 17 kap 15§** (old 11 kap 4§).
### Three cumulative conditions
1. **Pre-existing agreement (avtal i förväg).** Written recommended, oral technically sufficient. Should specify scope, transactions, format, approval, duration, termination.
2. **Approval procedure (godkännandeförfarande).** Passive approval accepted: silence within agreed timeframe = approval, provided seller can review and object.
3. **"Självfakturering" notation** on every invoice.
Seller remains responsible for VAT reporting. Both parties archive 7 years per BFL 5 kap 11§.
Peppol: `InvoiceTypeCode` **389**.
## 7. Peppol / e-faktura
### Legal framework
**Lag (2018:1277) om elektroniska fakturor till följd av offentlig upphandling** transposed EU Directive 2014/55/EU. Mandatory for B2G since **1 April 2019**. Format: **EN 16931**, Swedish implementation = **Peppol BIS Billing 3.0**. DIGG is Sweden's Peppol Authority. Over 95% of Swedish public entities registered.
**MDFFS 2019:1** mandates public entities to register as Peppol receivers. SFTI phased out EDIFACT ESAP 6 from recommended formats (July 2025).
### BIS Billing 3.0 format
UBL 2.1 XML. Current version: 3.0.20.
Required identification strings:
```xml
<cbc:CustomizationID>urn:cen.eu:en16931:2017#compliant#urn:fdc:peppol.eu:2017:poacc:billing:3.0</cbc:CustomizationID>
<cbc:ProfileID>urn:fdc:peppol.eu:2017:poacc:billing:01:1.0</cbc:ProfileID>
```
Mandatory header: `cbc:ID`, `cbc:IssueDate`, `cbc:InvoiceTypeCode` (380/381/389), `cbc:DocumentCurrencyCode`, `cac:AccountingSupplierParty`, `cac:AccountingCustomerParty`, `cac:TaxTotal`, `cac:LegalMonetaryTotal`, ≥1 `cac:InvoiceLine`. Either `BuyerReference` (BT-10) or `OrderReference` (BT-13) required.
### Sweden-specific validation rules
| Rule | Requirement |
|---|---|
| SE-R-001 | Swedish VAT numbers must be 14 characters (SE + 10 digits + 01) |
| SE-R-005 | SE sellers must include "Godkänd för F-skatt" |
| SE-R-006 | Valid VAT rates for SE sellers: 6%, 12%, 25% with category S |
| SE-R-009 | Bankgiro Account ID: 78 characters |
| SE-R-011 | Bankgiro uses PaymentMeansCode=30 |
| SE-R-012 | Plusgiro uses PaymentMeansCode=30 |
### Identifiers
Swedish orgs: prefix **0007** + 10-digit organisationsnummer (e.g., `0007:5567321707`).
GLN: prefix **0088** + 13 digits.
VAT in UBL: `cac:PartyTaxScheme/cbc:CompanyID` = `SE556732170701`.
### ML 17 kap → Peppol UBL field mapping
| ML requirement | EN 16931 BT | UBL element |
|---|---|---|
| Fakturadatum | BT-2 | `cbc:IssueDate` |
| Löpnummer | BT-1 | `cbc:ID` |
| Säljarens moms-nr | BT-31 | `cac:AccountingSupplierParty/.../cbc:CompanyID` |
| Köparens moms-nr | BT-48 | `cac:AccountingCustomerParty/.../cbc:CompanyID` |
| Namn+adress (säljare) | BT-27, BT-3539 | `cac:AccountingSupplierParty` |
| Varornas mängd+art | BT-129, BT-153 | `cac:InvoiceLine/cbc:InvoicedQuantity` + `cac:Item/cbc:Name` |
| Leveransdatum | BT-72 | `cac:InvoicePeriod/cbc:StartDate` or `cbc:TaxPointDate` (BT-7) |
| Beskattningsunderlag per skattesats | BT-116 | `cac:TaxSubtotal/cbc:TaxableAmount` |
| Momssats | BT-119 | `cac:TaxCategory/cbc:Percent` |
| Momsbelopp | BT-110 | `cac:TaxTotal/cbc:TaxAmount` |
| "Omvänd betalningsskyldighet" | BT-121 | `cbc:TaxExemptionReason` + TaxCategory code AE |
| "Självfakturering" | BT-3 | `cbc:InvoiceTypeCode` = 389 |
### B2B e-invoicing timeline
B2B voluntary as of April 2026. Ministry of Finance launched formal inquiry **5 February 2026**, report due **30 November 2027**. EU ViDA directive (adopted 11 March 2025) allows member states to mandate domestic B2B e-invoicing without EU approval. Mandatory cross-border B2B e-invoicing by **1 July 2030**.
## 8. ROT/RUT invoicing
### Fakturamodellen
Company invoices full amount, shows ROT/RUT deduction, reduces "att betala." Customer pays reduced amount **electronically** (cash disqualified since 1 Jan 2020). Company applies to Skatteverket via "Rot och rut företag." SKV pays company directly (~10 days). **F-skatt required** per HUSFL 69§§.
### Required invoice fields
1. Company name + F-skatt statement
2. Customer personnummer (ÅÅÅÅMMDD-XXXX)
3. Type of work performed
4. Where and when work performed
5. ROT: fastighetsbeteckning or BRF orgnr + lägenhetsnummer
6. Separate line items: arbetskostnad, materialkostnad, övriga kostnader
7. Total excl/incl moms with moms amount
8. Calculated skattereduktion amount
### Deduction rates and caps (20242026)
| Parameter | ROT | RUT |
|---|---|---|
| Standard % | 30% of labor incl. moms | 50% of labor incl. moms |
| Standard max/person/year | 50,000 SEK | 75,000 SEK |
| Standard combined max | 75,000 SEK (ROT capped at 50k within) | |
| 2024 H2 (JulDec) | Max raised to 75k, caps separated, total possible 150k | |
| 2025 (12 May31 Dec) | ROT % raised to **50%** (Betänkande 2024/25:FiU32) | 50% unchanged |
| 2026 | Standard 30%/50% rules resume | |
### BAS journal entries
Example: 18,000 SEK arbetskostnad = 22,500 inkl. moms (25%), ROT 30% = 6,750 SEK.
**Invoice issued:**
| Account | Debit | Credit |
|---|---|---|
| 1511 Kundfordringar (customer portion) | 23,875 | |
| 1513 Kundfordringar delad faktura (SKV) | 6,750 | |
| 3010 Försäljning | | 24,500 |
| 2610 Utgående moms 25% | | 6,125 |
Customer pays: Debit 1930, Credit 1511.
SKV pays: Debit 1930, Credit 1513.
SKV denies: Debit 1510, Credit 1513 (re-invoice customer).
Account 1513 exists in BAS Kontoplan 1 but not Kontoplan 2. Alternative: 1600 Övriga kortfristiga fordringar.
### Application deadline
**31 January of the year following the payment year.** Payment date (not invoice or work date) determines tax year.
## 9. Reverse charge notation
### Invoice requirements
Per **17 kap 28§ punkt 14 ML**: when buyer is liable for VAT, invoice must include notation. Three accepted forms:
- Swedish: "Omvänd betalningsskyldighet"
- English: "Reverse charge"
- Reference to ML paragraph or EU Directive article
Invoice must include buyer's VAT number and charge **no VAT**.
### Scenario reference
**Domestic construction (byggtjänster) — ML 16 kap 13§:**
Applies when buyer is taxable person who not only temporarily provides construction services.
Seller: Box 41. Buyer: Box 24, output VAT Box 30, input VAT Box 48.
**EU services (B2B main rule) — ML 16 kap 6§ + 6 kap 3337§§:**
Swedish business buys services from EU seller under main rule.
Buyer: Box 21, output VAT Box 3032, input VAT Box 48. Seller: Box 39 + periodisk sammanställning.
**Intra-EU goods — ML 10 kap 42§:**
Text: "Unionsintern leverans" or ref to Article 138 Directive 2006/112/EC. Both VAT numbers required.
Seller: Box 35 + periodisk sammanställning. Buyer: Box 20, output VAT Box 3032, input VAT Box 48.
**Electronics >100k SEK — ML 16 kap 17§:**
Mobile phones, integrated circuits, game consoles, tablets, laptops when invoice excl. VAT > SEK 100,000. In effect since 1 April 2021.
**Other:** Scrap metal/waste (16:14), CO₂ allowances (16:15), gold (16:16). Full scope: 16 kap 622§§.
### BAS accounts for reverse charge
| Account | Purpose |
|---|---|
| 2614 | Utgående moms, omvänd betalningsskyldighet 25% |
| 2624 | Utgående moms, omvänd betalningsskyldighet 12% |
| 2634 | Utgående moms, omvänd betalningsskyldighet 6% |
| 2644/2647 | Ingående moms, omvänd momsskyldighet (domestic) |
| 2645 | Beräknad ingående moms på förvärv från utlandet |
| 4610 | Byggtjänster (cost account) |
| 4535 | Varuförvärv från EU |
| 4545 | Tjänsteförvärv från EU |
| 3231 | Försäljning byggsektorn, omvänd betalningsskyldighet |
| 3048 | EU-försäljning tjänster 0% |
## 10. Currency handling
### Rules
Invoicing in any currency is permitted. Per **ML 17 kap 29§**: VAT amount must be in the company's redovisningsvaluta. For SEK companies: VAT in **both invoice currency and SEK**.
### Exchange rate sources (ML 8 kap 2123§)
Two permitted sources (choose one consistently):
1. Mid-rate (mittkurs) from **Nasdaq OMX Stockholm AB** (published on Riksbanken's website)
2. Latest published **ECB rate**
Rate to use: **at time of taxable event** (delivery/supply date or advance payment date, not invoice date unless same). Continuous supplies: last delivery day of invoiced period. Two non-euro currencies via ECB: route through EUR (8 kap 22§).
### Exchange rate differences
No VAT impact. VAT locked at original transaction rate. Differences are P&L items only. Revalue monetary items to balance sheet date per 4 kap 13§ ÅRL and K2/K3.
### BAS accounts
| Account | Purpose |
|---|---|
| 3960 | Valutakursvinster, rörelsefordringar/-skulder |
| 7960 | Valutakursförluster, rörelsefordringar/-skulder |
| 8230 | Valutakursdifferenser, långfristiga fordringar |
| 8330 | Valutakursdifferenser, kortfristiga fordringar/placeringar |
| 8430 | Valutakursdifferenser, skulder (long-term loans) |
Decision: customer receivables (1510) and supplier payables (2440) → 3960/7960. Financial instruments/loans → 8230/8330/8430. Per K3 Ch. 30: recognize in income statement in the period they arise.
## 11. OCR / Bankgirot
### OCR format
225 digit numeric reference. Last digit = check digit via **Luhn algorithm (Modulus 10)**. Recommended length: 515 digits. Typically encodes invoice number and/or customer number.
### Control levels
| Level | Behavior |
|---|---|
| OCR 1 | Soft — warning only |
| OCR 2 | Hard — payment rejected if wrong |
| OCR 3 | Hard + variable length control |
| OCR 4 | Hard + fixed length |
Hard control → ~100% auto-match rate. Requires bank agreement for "Bankgiro Inbetalningar" with OCR-referenskontroll. Sweden transitioning to ISO 20022 in 2026; file-initiated payments with incorrect OCR will be rejected.
## 12. Autogiro
Direct debit via Bankgirot. Business signs agreement with bank linking Bankgiro number. Customer grants **medgivande** (mandate) via BG600P/BG600F, internet bank, or BankID. Activation: up to 2 banking days.
Business submits payment files to Bankgirot. Customer right to request repayment within **8 weeks** per Betaltjänstlagen if amount unknown or unreasonably high. Business must notify customers in advance of amounts and dates.
## 13. Penalties
### Denied VAT deductions
Skatteverket can deny avdragsrätt when invoices lack mandatory fields per 17 kap 2428§. Per EU case law (C-272/13): purely formal defects are correctable ("healable"). Material requirements (goods/services in VAT-liable business) are primary; formal are secondary but necessary. Missing VAT amount entirely → new corrected invoice required, deduction only from corrected invoice period.
### Skattetillägg (SFL 49 kap)
| Situation | Rate |
|---|---|
| Oriktig uppgift — income tax | 40% |
| Oriktig uppgift — VAT/employer contributions | 20% |
| Periodization error — income tax | 10% |
| Periodization error — VAT (≤4 months, ≤3-month periods) | 2% |
| Periodization error — VAT (annual or >4 months) | 5% |
Deducting ingående moms on non-compliant invoices: **20% skattetillägg**. Felaktigt debiterad moms: buyer gets no deduction; 20% reduced to 1/4 if seller reported and paid. Voluntary correction before investigation normally avoids skattetillägg.
### Criminal consequences
BFL: all transactions require verifikationer (5 kap 7§), archived 7 years (7 kap).
Bokföringsbrott (11 kap 5§ BrB): up to 2 years, grovt 6 months6 years.
False invoices: skattebrott (Skattebrottslagen) up to 2 years, grovt 6 months6 years, plus potential penningtvättsbrott.
## 14. BAS kontoplan mapping
### Accounts receivable (15xx)
| Account | Name |
|---|---|
| 1510 | Kundfordringar (main/group) |
| 1511 | Kundfordringar (sub / customer portion split) |
| 1512 | Belånade kundfordringar (factoring) |
| 1513 | Kundfordringar delad faktura (ROT/RUT SKV) |
| 1515 | Osäkra kundfordringar |
| 1516 | Tvistiga kundfordringar |
| 1518 | Ej reskontraförda kundfordringar |
| 1519 | Nedskrivning av kundfordringar (contra, credit balance) |
### Revenue (30xx34xx)
| Account | Name |
|---|---|
| 3001 | Försäljning Sverige 25% |
| 3002 | Försäljning Sverige 12% |
| 3003 | Försäljning Sverige 6% |
| 3004 | Försäljning Sverige momsfri |
| 3105 | Export varor utanför EU |
| 3108 | Varor till annat EU-land, momsfri |
| 3231 | Byggsektorn omvänd betalningsskyldighet |
| 3305 | Tjänster utanför EU |
| 3308 | Tjänster till annat EU-land |
| 3950 | Återvunna avskrivna kundfordringar |
### VAT (26xx)
| Account | Name |
|---|---|
| 2610/2611 | Utgående moms 25% |
| 2612 | Utgående moms egna uttag 25% |
| 2614 | Utgående moms omvänd skattskyldighet 25% |
| 2615 | Utgående moms import varor 25% |
| 2620/2621 | Utgående moms 12% |
| 2630/2631 | Utgående moms 6% |
| 2640 | Ingående moms (group) |
| 2645 | Beräknad ingående moms utlandet |
| 2647 | Ingående moms omvänd betalningsskyldighet Sverige |
| 2650 | Redovisningskonto moms (settlement) |
### Bad debts flow
1. Transfer doubtful: Debit 1515, Credit 1510
2. Provision: Debit 6352 (befarade förluster), Credit 1519
3. Loss confirmed: Debit 6351 (konstaterade förluster), Credit 1515; reverse provision Debit 1519 Credit 6352; recover VAT by debiting 2610/2620/2630
VAT recovery on bad debts permitted under **ML 7 kap 43§** when loss is konstaterad (bankruptcy, failed enforcement, acknowledged insolvency).
### Invoice extras
| Item | Account | VAT |
|---|---|---|
| Faktureringsavgift | 3540 | 25% VAT |
| Öresavrundning | 3740 | No VAT |
| Påminnelseavgift | 3930 | No VAT |
| Dröjsmålsränta | 8313/8310 | No VAT (financial income) |# Swedish Invoice Compliance — Full Reference
## 1. Mandatory invoice fields (ML 17 kap 24§)
A fullständig faktura must contain all fields per **17 kap 24§ ML (2023:200)**, implementing EU VAT Directive Article 226.
| # | Field (Swedish) | Field (English) | ML ref |
|---|---|---|---|
| 1 | Fakturadatum | Invoice issue date | 17:24 p.1 |
| 2 | Löpnummer (unique sequential from one or more series) | Invoice number | 17:24 p.2 |
| 3 | Säljarens momsregistreringsnummer (SE + 10 digits + 01) | Seller VAT ID | 17:24 p.3 |
| 4 | Köparens momsregistreringsnummer (when RC or intra-EU) | Buyer VAT ID | 17:24 p.4 |
| 5 | Fullständigt namn och adress, säljare och köpare | Names and addresses | 17:24 p.5 |
| 6 | Varornas mängd och art / tjänsternas omfattning och art | Quantity and nature | 17:24 p.6 |
| 7 | Leveransdatum eller tillhandahållandedatum (if ≠ invoice date) | Delivery date | 17:24 p.7 |
| 8 | Beskattningsunderlag per skattesats, enhetspris exkl. moms, rabatter | Tax base per rate | 17:24 p.8 |
| 9 | Mervärdesskattesats (25%, 12%, or 6%) | VAT rate | 17:24 p.9 |
| 10 | Mervärdesskattebelopp | VAT amount | 17:24 p.10 |
| 11 | "Omvänd betalningsskyldighet" (if reverse charge) | RC notation | 17:24 |
| 12 | ML/Directive reference (if VAT-exempt) | Exemption ref | 17:24 |
| 13 | "Självfakturering" (if self-billing) | Self-billing text | 17:24 |
| 14 | Margin scheme notation (if applicable) | Margin scheme | 17:24 |
| 15 | Transport media details (new vehicles to EU) | Vehicle specifics | 17:24 |
| 16 | Förskottsbetalning amount (advance payment) | Advance payment | 17:17 |
**Löpnummerserie:** Must enable detection of missing invoices. Multiple series permitted (per unit, POS, etc.). Also required by BFL 5 kap 6§. Gaps or duplicates are a compliance red flag.
## 2. Förenklad faktura (simplified invoice)
**ML 17 kap 2628§**, **SKVFS 2024:16**.
Threshold: total **≤ SEK 4,000 including VAT**.
Also permitted when trade/technical conditions make full invoicing impractical (vending machines, fuel pumps, parking meters).
Reduced content: date, seller ID (VAT/org number), description of goods/services, VAT amount or data to calculate it.
**Cannot** be used for: intra-EU transactions, distance sales, cross-border reverse charge.
## 3. Time limits for issuing invoices
- Domestic: no hard statutory deadline; "without undue delay" per god affärssed.
- Intra-EU goods/services (main rule): **15th of month following delivery/performance** (17 kap 19§).
- Construction services: **end of second month after performance**.
## 4. Electronic vs paper equivalence
Electronic invoices = identical legal standing (2 kap 910§ ML). E-invoicing requires buyer consent (17 kap 20§). PDF by email is a legal faktura but not a structured e-faktura. Archive for **7 years** (BFL 7 kap). Since July 2024, paper originals may be destroyed immediately after digital transfer (amended 7 kap 6§ BFL).
No specific language requirement in ML. VAT amounts must be in SEK if accounting currency is SEK. Skatteverket may request translations.
## 5. Kreditfaktura / ändringsfaktura
Term in ML (2023:200): **ändringsfaktura**. Business terms "kreditfaktura" and "kreditnota" remain in use.
### When to issue
Incorrect original, goods returned, price reduction post-invoicing, partial/full cancellation, erroneous VAT correction.
### Governing law
**17 kap 2223§ ML (2023:200)**.
### Mandatory content
1. The specific change to the original invoice
2. Specific and unambiguous reference to the original invoice (typically löpnummer)
3. What has been changed
4. Own unique fakturanummer and fakturadatum
5. Amounts shown as negative values
6. VAT specified per momssats from the original
Notation: "Er tillgodo" replaces "Att betala".
When citing specific original is impractical (volume rebates): customer number + date range + description is accepted.
### Partial credits
Fully permitted. Must specify which items credited, partial negative amount, proportional VAT per skattesats.
### VAT adjustment mechanics
Seller reduces utgående moms in the credit note period. Buyer must reduce ingående moms in the same period. For felaktigt debiterad mervärdesskatt, a valid kreditfaktura is a prerequisite before seller can adjust VAT return.
### BAS journal entries
**Seller issuing credit note (25% example):**
| Account | Debit | Credit |
|---|---|---|
| 3011 Försäljning tjänster 25% | X | |
| 2611 Utgående moms 25% | X | |
| 1510 Kundfordringar | | X |
12%: 3002/2621. 6%: 3003/2631. Mirror reversal of original.
**Buyer receiving credit note:**
Debit 2440 Leverantörsskulder, Credit purchase account (40xx), Credit 2641 Ingående moms.
## 6. Självfakturering (self-billing)
**ML 17 kap 15§** (old 11 kap 4§).
### Three cumulative conditions
1. **Pre-existing agreement (avtal i förväg).** Written recommended, oral technically sufficient. Should specify scope, transactions, format, approval, duration, termination.
2. **Approval procedure (godkännandeförfarande).** Passive approval accepted: silence within agreed timeframe = approval, provided seller can review and object.
3. **"Självfakturering" notation** on every invoice.
Seller remains responsible for VAT reporting. Both parties archive 7 years per BFL 5 kap 11§.
Peppol: `InvoiceTypeCode` **389**.
## 7. Peppol / e-faktura
### Legal framework
**Lag (2018:1277) om elektroniska fakturor till följd av offentlig upphandling** transposed EU Directive 2014/55/EU. Mandatory for B2G since **1 April 2019**. Format: **EN 16931**, Swedish implementation = **Peppol BIS Billing 3.0**. DIGG is Sweden's Peppol Authority. Over 95% of Swedish public entities registered.
**MDFFS 2019:1** mandates public entities to register as Peppol receivers. SFTI phased out EDIFACT ESAP 6 from recommended formats (July 2025).
### BIS Billing 3.0 format
UBL 2.1 XML. Current version: 3.0.20.
Required identification strings:
```xml
<cbc:CustomizationID>urn:cen.eu:en16931:2017#compliant#urn:fdc:peppol.eu:2017:poacc:billing:3.0</cbc:CustomizationID>
<cbc:ProfileID>urn:fdc:peppol.eu:2017:poacc:billing:01:1.0</cbc:ProfileID>
```
Mandatory header: `cbc:ID`, `cbc:IssueDate`, `cbc:InvoiceTypeCode` (380/381/389), `cbc:DocumentCurrencyCode`, `cac:AccountingSupplierParty`, `cac:AccountingCustomerParty`, `cac:TaxTotal`, `cac:LegalMonetaryTotal`, ≥1 `cac:InvoiceLine`. Either `BuyerReference` (BT-10) or `OrderReference` (BT-13) required.
### Sweden-specific validation rules
| Rule | Requirement |
|---|---|
| SE-R-001 | Swedish VAT numbers must be 14 characters (SE + 10 digits + 01) |
| SE-R-005 | SE sellers must include "Godkänd för F-skatt" |
| SE-R-006 | Valid VAT rates for SE sellers: 6%, 12%, 25% with category S |
| SE-R-009 | Bankgiro Account ID: 78 characters |
| SE-R-011 | Bankgiro uses PaymentMeansCode=30 |
| SE-R-012 | Plusgiro uses PaymentMeansCode=30 |
### Identifiers
Swedish orgs: prefix **0007** + 10-digit organisationsnummer (e.g., `0007:5567321707`).
GLN: prefix **0088** + 13 digits.
VAT in UBL: `cac:PartyTaxScheme/cbc:CompanyID` = `SE556732170701`.
### ML 17 kap → Peppol UBL field mapping
| ML requirement | EN 16931 BT | UBL element |
|---|---|---|
| Fakturadatum | BT-2 | `cbc:IssueDate` |
| Löpnummer | BT-1 | `cbc:ID` |
| Säljarens moms-nr | BT-31 | `cac:AccountingSupplierParty/.../cbc:CompanyID` |
| Köparens moms-nr | BT-48 | `cac:AccountingCustomerParty/.../cbc:CompanyID` |
| Namn+adress (säljare) | BT-27, BT-3539 | `cac:AccountingSupplierParty` |
| Varornas mängd+art | BT-129, BT-153 | `cac:InvoiceLine/cbc:InvoicedQuantity` + `cac:Item/cbc:Name` |
| Leveransdatum | BT-72 | `cac:InvoicePeriod/cbc:StartDate` or `cbc:TaxPointDate` (BT-7) |
| Beskattningsunderlag per skattesats | BT-116 | `cac:TaxSubtotal/cbc:TaxableAmount` |
| Momssats | BT-119 | `cac:TaxCategory/cbc:Percent` |
| Momsbelopp | BT-110 | `cac:TaxTotal/cbc:TaxAmount` |
| "Omvänd betalningsskyldighet" | BT-121 | `cbc:TaxExemptionReason` + TaxCategory code AE |
| "Självfakturering" | BT-3 | `cbc:InvoiceTypeCode` = 389 |
### B2B e-invoicing timeline
B2B voluntary as of April 2026. Ministry of Finance launched formal inquiry **5 February 2026**, report due **30 November 2027**. EU ViDA directive (adopted 11 March 2025) allows member states to mandate domestic B2B e-invoicing without EU approval. Mandatory cross-border B2B e-invoicing by **1 July 2030**.
## 8. ROT/RUT invoicing
### Fakturamodellen
Company invoices full amount, shows ROT/RUT deduction, reduces "att betala." Customer pays reduced amount **electronically** (cash disqualified since 1 Jan 2020). Company applies to Skatteverket via "Rot och rut företag." SKV pays company directly (~10 days). **F-skatt required** per HUSFL 69§§.
### Required invoice fields
1. Company name + F-skatt statement
2. Customer personnummer (ÅÅÅÅMMDD-XXXX)
3. Type of work performed
4. Where and when work performed
5. ROT: fastighetsbeteckning or BRF orgnr + lägenhetsnummer
6. Separate line items: arbetskostnad, materialkostnad, övriga kostnader
7. Total excl/incl moms with moms amount
8. Calculated skattereduktion amount
### Deduction rates and caps (20242026)
| Parameter | ROT | RUT |
|---|---|---|
| Standard % | 30% of labor incl. moms | 50% of labor incl. moms |
| Standard max/person/year | 50,000 SEK | 75,000 SEK |
| Standard combined max | 75,000 SEK (ROT capped at 50k within) | |
| 2024 H2 (JulDec) | Max raised to 75k, caps separated, total possible 150k | |
| 2025 (12 May31 Dec) | ROT % raised to **50%** (Betänkande 2024/25:FiU32) | 50% unchanged |
| 2026 | Standard 30%/50% rules resume | |
### BAS journal entries
Example: 18,000 SEK arbetskostnad = 22,500 inkl. moms (25%), ROT 30% = 6,750 SEK.
**Invoice issued:**
| Account | Debit | Credit |
|---|---|---|
| 1511 Kundfordringar (customer portion) | 23,875 | |
| 1513 Kundfordringar delad faktura (SKV) | 6,750 | |
| 3010 Försäljning | | 24,500 |
| 2610 Utgående moms 25% | | 6,125 |
Customer pays: Debit 1930, Credit 1511.
SKV pays: Debit 1930, Credit 1513.
SKV denies: Debit 1510, Credit 1513 (re-invoice customer).
Account 1513 exists in BAS Kontoplan 1 but not Kontoplan 2. Alternative: 1600 Övriga kortfristiga fordringar.
### Application deadline
**31 January of the year following the payment year.** Payment date (not invoice or work date) determines tax year.
## 9. Reverse charge notation
### Invoice requirements
Per **17 kap 28§ punkt 14 ML**: when buyer is liable for VAT, invoice must include notation. Three accepted forms:
- Swedish: "Omvänd betalningsskyldighet"
- English: "Reverse charge"
- Reference to ML paragraph or EU Directive article
Invoice must include buyer's VAT number and charge **no VAT**.
### Scenario reference
**Domestic construction (byggtjänster) — ML 16 kap 13§:**
Applies when buyer is taxable person who not only temporarily provides construction services.
Seller: Box 41. Buyer: Box 24, output VAT Box 30, input VAT Box 48.
**EU services (B2B main rule) — ML 16 kap 6§ + 6 kap 3337§§:**
Swedish business buys services from EU seller under main rule.
Buyer: Box 21, output VAT Box 3032, input VAT Box 48. Seller: Box 39 + periodisk sammanställning.
**Intra-EU goods — ML 10 kap 42§:**
Text: "Unionsintern leverans" or ref to Article 138 Directive 2006/112/EC. Both VAT numbers required.
Seller: Box 35 + periodisk sammanställning. Buyer: Box 20, output VAT Box 3032, input VAT Box 48.
**Electronics >100k SEK — ML 16 kap 17§:**
Mobile phones, integrated circuits, game consoles, tablets, laptops when invoice excl. VAT > SEK 100,000. In effect since 1 April 2021.
**Other:** Scrap metal/waste (16:14), CO₂ allowances (16:15), gold (16:16). Full scope: 16 kap 622§§.
### BAS accounts for reverse charge
| Account | Purpose |
|---|---|
| 2614 | Utgående moms, omvänd betalningsskyldighet 25% |
| 2624 | Utgående moms, omvänd betalningsskyldighet 12% |
| 2634 | Utgående moms, omvänd betalningsskyldighet 6% |
| 2644/2647 | Ingående moms, omvänd momsskyldighet (domestic) |
| 2645 | Beräknad ingående moms på förvärv från utlandet |
| 4610 | Byggtjänster (cost account) |
| 4535 | Varuförvärv från EU |
| 4545 | Tjänsteförvärv från EU |
| 3231 | Försäljning byggsektorn, omvänd betalningsskyldighet |
| 3048 | EU-försäljning tjänster 0% |
## 10. Currency handling
### Rules
Invoicing in any currency is permitted. Per **ML 17 kap 29§**: VAT amount must be in the company's redovisningsvaluta. For SEK companies: VAT in **both invoice currency and SEK**.
### Exchange rate sources (ML 8 kap 2123§)
Two permitted sources (choose one consistently):
1. Mid-rate (mittkurs) from **Nasdaq OMX Stockholm AB** (published on Riksbanken's website)
2. Latest published **ECB rate**
Rate to use: **at time of taxable event** (delivery/supply date or advance payment date, not invoice date unless same). Continuous supplies: last delivery day of invoiced period. Two non-euro currencies via ECB: route through EUR (8 kap 22§).
### Exchange rate differences
No VAT impact. VAT locked at original transaction rate. Differences are P&L items only. Revalue monetary items to balance sheet date per 4 kap 13§ ÅRL and K2/K3.
### BAS accounts
| Account | Purpose |
|---|---|
| 3960 | Valutakursvinster, rörelsefordringar/-skulder |
| 7960 | Valutakursförluster, rörelsefordringar/-skulder |
| 8230 | Valutakursdifferenser, långfristiga fordringar |
| 8330 | Valutakursdifferenser, kortfristiga fordringar/placeringar |
| 8430 | Valutakursdifferenser, skulder (long-term loans) |
Decision: customer receivables (1510) and supplier payables (2440) → 3960/7960. Financial instruments/loans → 8230/8330/8430. Per K3 Ch. 30: recognize in income statement in the period they arise.
## 11. OCR / Bankgirot
### OCR format
225 digit numeric reference. Last digit = check digit via **Luhn algorithm (Modulus 10)**. Recommended length: 515 digits. Typically encodes invoice number and/or customer number.
### Control levels
| Level | Behavior |
|---|---|
| OCR 1 | Soft — warning only |
| OCR 2 | Hard — payment rejected if wrong |
| OCR 3 | Hard + variable length control |
| OCR 4 | Hard + fixed length |
Hard control → ~100% auto-match rate. Requires bank agreement for "Bankgiro Inbetalningar" with OCR-referenskontroll. Sweden transitioning to ISO 20022 in 2026; file-initiated payments with incorrect OCR will be rejected.
## 12. Autogiro
Direct debit via Bankgirot. Business signs agreement with bank linking Bankgiro number. Customer grants **medgivande** (mandate) via BG600P/BG600F, internet bank, or BankID. Activation: up to 2 banking days.
Business submits payment files to Bankgirot. Customer right to request repayment within **8 weeks** per Betaltjänstlagen if amount unknown or unreasonably high. Business must notify customers in advance of amounts and dates.
## 13. Penalties
### Denied VAT deductions
Skatteverket can deny avdragsrätt when invoices lack mandatory fields per 17 kap 2428§. Per EU case law (C-272/13): purely formal defects are correctable ("healable"). Material requirements (goods/services in VAT-liable business) are primary; formal are secondary but necessary. Missing VAT amount entirely → new corrected invoice required, deduction only from corrected invoice period.
### Skattetillägg (SFL 49 kap)
| Situation | Rate |
|---|---|
| Oriktig uppgift — income tax | 40% |
| Oriktig uppgift — VAT/employer contributions | 20% |
| Periodization error — income tax | 10% |
| Periodization error — VAT (≤4 months, ≤3-month periods) | 2% |
| Periodization error — VAT (annual or >4 months) | 5% |
Deducting ingående moms on non-compliant invoices: **20% skattetillägg**. Felaktigt debiterad moms: buyer gets no deduction; 20% reduced to 1/4 if seller reported and paid. Voluntary correction before investigation normally avoids skattetillägg.
### Criminal consequences
BFL: all transactions require verifikationer (5 kap 7§), archived 7 years (7 kap).
Bokföringsbrott (11 kap 5§ BrB): up to 2 years, grovt 6 months6 years.
False invoices: skattebrott (Skattebrottslagen) up to 2 years, grovt 6 months6 years, plus potential penningtvättsbrott.
## 14. BAS kontoplan mapping
### Accounts receivable (15xx)
| Account | Name |
|---|---|
| 1510 | Kundfordringar (main/group) |
| 1511 | Kundfordringar (sub / customer portion split) |
| 1512 | Belånade kundfordringar (factoring) |
| 1513 | Kundfordringar delad faktura (ROT/RUT SKV) |
| 1515 | Osäkra kundfordringar |
| 1516 | Tvistiga kundfordringar |
| 1518 | Ej reskontraförda kundfordringar |
| 1519 | Nedskrivning av kundfordringar (contra, credit balance) |
### Revenue (30xx34xx)
| Account | Name |
|---|---|
| 3001 | Försäljning Sverige 25% |
| 3002 | Försäljning Sverige 12% |
| 3003 | Försäljning Sverige 6% |
| 3004 | Försäljning Sverige momsfri |
| 3105 | Export varor utanför EU |
| 3108 | Varor till annat EU-land, momsfri |
| 3231 | Byggsektorn omvänd betalningsskyldighet |
| 3305 | Tjänster utanför EU |
| 3308 | Tjänster till annat EU-land |
| 3950 | Återvunna avskrivna kundfordringar |
### VAT (26xx)
| Account | Name |
|---|---|
| 2610/2611 | Utgående moms 25% |
| 2612 | Utgående moms egna uttag 25% |
| 2614 | Utgående moms omvänd skattskyldighet 25% |
| 2615 | Utgående moms import varor 25% |
| 2620/2621 | Utgående moms 12% |
| 2630/2631 | Utgående moms 6% |
| 2640 | Ingående moms (group) |
| 2645 | Beräknad ingående moms utlandet |
| 2647 | Ingående moms omvänd betalningsskyldighet Sverige |
| 2650 | Redovisningskonto moms (settlement) |
### Bad debts flow
1. Transfer doubtful: Debit 1515, Credit 1510
2. Provision: Debit 6352 (befarade förluster), Credit 1519
3. Loss confirmed: Debit 6351 (konstaterade förluster), Credit 1515; reverse provision Debit 1519 Credit 6352; recover VAT by debiting 2610/2620/2630
VAT recovery on bad debts permitted under **ML 7 kap 43§** when loss is konstaterad (bankruptcy, failed enforcement, acknowledged insolvency).
### Invoice extras
| Item | Account | VAT |
|---|---|---|
| Faktureringsavgift | 3540 | 25% VAT |
| Öresavrundning | 3740 | No VAT |
| Påminnelseavgift | 3930 | No VAT |
| Dröjsmålsränta | 8313/8310 | No VAT (financial income) |
@@ -0,0 +1,148 @@
---
name: swedish-sie-import-export
description: >
Swedish SIE4 file format: parsing, validation, generation, troubleshooting. All record types
(#VER, #TRANS, #IB, #UB, #RES, #KONTO, #RAR, #FLAGGA, #KSUMMA, #SRU, etc.), SIE types 1-4,
encoding detection (CP437/UTF-8/Latin-1), mojibake diagnosis, verification balance integrity,
IB/UB continuity, multi-year migration, diffing, audit trail under BFL, BAS account classes,
SRU tax codes, and error patterns. Trigger on ANY SIE question: import, export, parsing,
validation, encoding issues, garbled å/ä/ö, unbalanced verifications, IB/UB mismatch,
#FLAGGA, SIE migration between Fortnox/Visma/BL/SpeedLedger/Bokio, .SE/.SI files,
verification series, or code reading/writing SIE data. Always use over training data.
---
# Swedish SIE4 Import/Export
Deterministic reference for parsing, validating, generating, and troubleshooting SIE4 files.
## Quick orientation
SIE4 (Standard Import Export, version 4B, 2008) is Sweden's universal accounting data interchange format. Tagged plain-text, one record per line, `#LABEL` prefix, space-delimited fields.
**File extensions**: `.SE` = export, `.SI` = import.
**Five subtypes**:
- **Type 1**: Closing balances + chart of accounts + SRU codes (tax returns)
- **Type 2**: Type 1 + monthly period balances (#PSALDO, #PBUDGET)
- **Type 3**: Type 2 + object-level balances, dimensions
- **Type 4E**: Type 3 + all verifications (#VER/#TRANS) = full audit trail
- **Type 4I**: Verifications only, minimal header = subsystem import (payroll, POS)
## Core invariants (never violate)
1. **Verification balance**: Sum of all #TRANS amounts within a #VER block = 0.00 exactly
2. **IB/UB continuity**: UB(year N, account) = IB(year N+1, account) for all balance sheet accounts (1xxx-2xxx)
3. **Result account reset**: IB for income statement accounts (3xxx-9xxx) must be zero at year start
4. **#FLAGGA idempotency**: 0 = not imported, 1 = already imported. Prevents double-import.
5. **Sequential numbering**: Verifications within a series appear in ascending verno order
6. **Sign convention**: Debit = positive, Credit = negative (in #TRANS, #IB, #UB, #RES)
## Field format rules
- **Quoting**: Double quotes around fields with spaces. Escape internal quotes as `\"`
- **Dates**: YYYYMMDD. Periods: YYYYMM
- **Amounts**: Dot decimal separator, max 2 decimals, no plus sign
- **Block delimiters**: `{` and `}` each on own line around #TRANS entries in #VER
- **Object lists**: `{dimension_no "object_no"}` within balance/transaction items
- **Forward compat**: Readers must ignore unknown labels and unknown trailing fields
## When to read reference files
For the **complete record type specification** (all labels, fields, compulsory/optional rules, #KSUMMA CRC-32 details):
→ Read `references/record-types.md`
For **encoding detection, mojibake diagnosis, and per-software encoding behaviors**:
→ Read `references/encoding.md`
For **all validation rules, error patterns, and remediation procedures**:
→ Read `references/validation-rules.md`
For **BAS account classes, SRU code mappings, and account type behaviors**:
→ Read `references/bas-sru.md`
## Verification structure example
```
#VER A 1 20240115 "Kundfaktura 2024-001"
{
#TRANS 1510 {} 12500.00
#TRANS 2611 {} -2500.00
#TRANS 3010 {1 "100" 6 "P01"} -10000.00
}
```
Sum: 12500 + (-2500) + (-10000) = 0. Valid.
## Verification series conventions (Swedish practice)
- **A** = Huvudserie (main/general)
- **B** = Automatkonteringar (auto-postings)
- **F** = Kundfakturor (customer invoices)
- **I** = Inbetalningar (customer payments)
- **J** = Bokslutsverifikationer (year-end closing)
- **L** = Leverantörsfakturor (supplier invoices)
- **N** = Löner (payroll)
- **U** = Utbetalningar (supplier payments)
Series and numbering restart each fiscal year. 4I import files may have empty series/verno.
## Year-end closing and IB/UB flow
1. Closing entries (J-series) zero out all result accounts (3xxx-8xxx) by transferring net result to account 2099 (Årets resultat)
2. After closing: UB for result accounts = 0, UB for equity reflects accumulated result
3. These UB values become IB for next year
4. #RES records capture what result accounts held during the year before closing
## Multi-year handling
- `#RAR 0 20240101 20241231` = current fiscal year
- `#RAR -1 20230101 20231231` = previous year
- Only one chart of accounts per file (current year's)
- Broken fiscal years supported: `#RAR 0 20240701 20250630`
- #PSALDO/#PBUDGET store monthly change (not cumulative), period = YYYYMM
## Migration between systems
Standard path: export SIE4E per fiscal year from source, import into target starting with current year, verify IB/UB continuity, remap verification series to avoid collisions.
**What SIE does NOT carry**: VAT codes (must be manually configured post-import), customer/supplier subledgers (reskontror), processing history, underlying digital documents/vouchers.
Post-migration validation: compare balance reports, trial balances, verification counts per series, Swedish character rendering.
## Audit trail under BFL
- **7-year retention** after calendar year in which fiscal year ended (BFL 7:1)
- **Storage in Sweden** required (EU/EEA with Skatteverket notification; non-EU requires permission)
- **Immutability**: locked entries cannot be modified. Corrections via separate correction verification only.
- **#FLAGGA**: anti-duplication control. Exporter writes 0, importer sets to 1 after success.
- **SIE is not complete archiving**: lacks processing history and system documentation required by BFL.
- As of July 2024, paper originals may be destroyed after proper digitization.
## Encoding quick reference
The spec mandates CP437 (`#FORMAT PC8`), but modern cloud software exports UTF-8. Many programs write `#FORMAT PC8` regardless of actual encoding.
**Detection priority**: UTF-8 BOM → strict UTF-8 decode → CP437 byte scan (0x84/0x86/0x8E/0x8F/0x94/0x99) → Latin-1 byte scan (0xC4/0xC5/0xD6/0xE4/0xE5/0xF6) → check #PROGRAM (cloud = UTF-8, desktop = CP437) → fall back Latin-1.
**Mojibake signatures**: `Ã¥`/`ä`/`ö` = UTF-8 read as Latin-1. `†`/`„`/`"` = CP437 read as Win-1252. `σ`/`Σ`/`÷` = Latin-1 read as CP437.
**#KSUMMA note**: CRC-32 is calculated on CP437 byte values per spec. Skip KSUMMA validation when non-CP437 encoding is detected.
For full encoding tables and per-software behaviors, read `references/encoding.md`.
## Common errors (quick lookup)
| Error | Cause | Fix |
|-------|-------|-----|
| Unbalanced verification | #TRANS sum ≠ 0 | Add öresutjämning (3741) or fix amounts |
| IB/UB mismatch | Incomplete year-end closing | Create adjustment verification in opening period |
| Garbled å/ä/ö | Encoding mismatch | Detect actual encoding, re-decode |
| Duplicate verno | Series collision on import | Remap to unused series |
| Undeclared account | #TRANS references account not in #KONTO | Add #KONTO or map to existing |
| #FLAGGA 1 | File already imported | Verify not duplicate, manually reset to 0 |
| Missing #RAR | Can't determine fiscal year | Reject file or infer from verification dates |
| Non-zero IB on 3xxx-9xxx | Incomplete closing in source | Run closing entries before export |
| Truncated file | #KSUMMA opening present, closing missing | Reject, re-export from source |
| No VAT codes post-import | SIE carries no moms info | Manually configure in target system |
For the full error catalog with detailed remediation, read `references/validation-rules.md`.
@@ -0,0 +1,135 @@
# BAS Kontoplan and SRU Code Reference for SIE4
## Account class hierarchy
BAS uses a 4-digit decimal system: first digit = class, first two = group, all four = specific account.
| Class | #KTYP | Swedish | English | Report | IB/UB? |
|-------|-------|---------|---------|--------|--------|
| 1xxx | T | Tillgångar | Assets | Balance sheet | Yes (#IB/#UB) |
| 2xxx | S | Eget kapital och skulder | Equity & liabilities | Balance sheet | Yes (#IB/#UB) |
| 3xxx | I | Rörelseintäkter | Operating revenue | Income statement | No (#RES) |
| 4xxx | K | Varuinköp/material | Cost of goods | Income statement | No (#RES) |
| 5xxx | K | Övriga externa kostnader (lokaler, förbrukningsinventarier) | Other external costs | Income statement | No (#RES) |
| 6xxx | K | Övriga externa kostnader (kontorsmaterial, telefon, reklam) | Other external costs | Income statement | No (#RES) |
| 7xxx | K | Personalkostnader, avskrivningar | Personnel, depreciation | Income statement | No (#RES) |
| 8xxx | I/K | Finansiella poster, bokslutsdispositioner, skatt | Financial items, appropriations, tax | Income statement | No (#RES) |
| 9xxx | — | Internredovisning | Internal/management | Off-report | N/A |
## Account type (#KTYP) behavior in SIE4
| Type | Increases on | Decreases on | Normal balance | SIE sign convention |
|------|-------------|--------------|----------------|---------------------|
| T (Asset) | Debit (+) | Credit (-) | Positive | IB/UB positive = normal |
| S (Liability/Equity) | Credit (-) | Debit (+) | Negative | IB/UB negative = normal |
| I (Income) | Credit (-) | Debit (+) | Negative | RES negative = revenue |
| K (Cost) | Debit (+) | Credit (-) | Positive | RES positive = expense |
## Key accounts for SIE validation
These accounts appear frequently in SIE files and have special significance:
### Balance sheet (1xxx-2xxx)
- **1510** Kundfordringar (accounts receivable)
- **1910** Kassa (cash)
- **1920** PlusGiro
- **1930** Företagskonto/bank (business bank account)
- **1940** Övriga bankkonton
- **2081** Aktiekapital (share capital, AB only)
- **2091** Balanserad vinst/förlust (retained earnings)
- **2098** Vinst/förlust föregående år
- **2099** Årets resultat (current year net income, used in closing)
- **2440** Leverantörsskulder (accounts payable)
- **2510** Skatteskulder (tax liabilities)
- **2610** Utgående moms 25% (output VAT 25%)
- **2620** Utgående moms 12%
- **2630** Utgående moms 6%
- **2640** Ingående moms (input VAT)
- **2650** Redovisning av moms (VAT settlement account)
- **2710** Personalens källskatt (employee withholding tax)
- **2730** Lagstadgade sociala avgifter (statutory social charges)
- **2920** Upplupna semesterlöner (accrued vacation pay)
### Income statement (3xxx-8xxx)
- **3010-3099** Försäljning varor/tjänster (sales revenue)
- **3740** Öres-/avrundningsdifferens (rounding differences)
- **3741** Öresutjämning (øre equalization, used to balance rounding)
- **4010** Varuinköp (cost of goods purchased)
- **5010** Lokalhyra (office rent)
- **6110** Kontorsmaterial (office supplies)
- **6212** Mobiltelefon
- **6230** Datakommunikation (internet/data)
- **6570** Bankkostnader (bank charges)
- **7010** Löner (salaries)
- **7210** Arbetsgivaravgifter (employer contributions)
- **7510** Avskrivningar maskiner/inventarier (depreciation)
- **8310** Ränteintäkter (interest income)
- **8410** Räntekostnader (interest expenses)
- **8910** Skatt på årets resultat (income tax on profit, AB)
- **8999** Årets resultat (result for the year, closing account)
## SRU codes
### What SRU codes are
SRU (Standardiserade Räkenskapsutdrag) codes are 4-digit numeric codes that map BAS accounts to specific fields in Swedish income tax declaration forms. They enable automated transfer of account balances to tax returns.
### SRU in SIE files
The `#SRU` record maps an account to its SRU code:
```
#SRU 1510 7214
#SRU 3010 7410
```
Multiple #SRU entries per account are permitted (one account may map to multiple SRU codes). Some amounts map to different SRU codes depending on sign (debit vs credit balance).
### Key relationships
- `#TAXAR` specifies which taxation year's SRU mappings apply
- `#KPTYP` determines which SRU mapping set is appropriate
- The mapping schema is maintained by BAS-kontogruppen and Skatteverket
- Updated annually; published at bas.se as "kopplingsschema"
### Tax forms using SRU
- **INK2** (+ INK2R, INK2S): Aktiebolag (AB) income tax
- **INK3**: Ideella föreningar, stiftelser (non-profits, foundations)
- **INK4** (+ INK4R, INK4S, INK4DU): Handelsbolag, kommanditbolag (HB, KB)
- **NE-bilaga**: Enskild firma (sole proprietorship)
### SRU code ranges (approximate)
- **7000-7399**: Balance sheet items (tillgångar, eget kapital, skulder)
- **7400-7699**: Income statement items (intäkter, kostnader)
- **7700-7999**: Special items (bokslutsdispositioner, skatter)
### Important note
SRU mappings change between taxation years. A file with `#TAXAR 2023` uses 2023's mappings; `#TAXAR 2024` uses 2024's. Always verify the kopplingsschema version matches the taxation year.
## Account class rules for SIE validation
### Balance sheet accounts (1xxx-2xxx)
- Must have #IB and #UB records
- UB(year -1) must equal IB(year 0)
- Carry forward between fiscal years
### Income statement accounts (3xxx-8xxx)
- Must have #RES records (not #IB/#UB)
- IB must be zero at start of each fiscal year
- Zeroed out by year-end closing entries (transferred to 2099)
- #PSALDO records track monthly changes
### Internal accounts (9xxx)
- Not included in external reports
- Some systems (Visma Bokföring) don't support them
- May or may not appear in SIE exports depending on system
- No mandatory #IB/#UB or #RES requirements
## BAS version handling in SIE
The `#KPTYP` record declares the chart of accounts version:
- `BAS95`: Original BAS plan
- `BAS96`: 1996 revision
- `EUBAS97`: EU-adapted version (most common modern usage)
- `NE2007`: Specialized for enskild firma
- `BAS2xxx` (e.g., `BAS2024`): Treated as equivalent to EUBAS97
If `#KPTYP` is missing, assume BAS 95. The account number structure is consistent across versions; differences are mainly in which accounts exist and their SRU mappings.
@@ -0,0 +1,91 @@
# SIE4 Character Encoding Reference
## The spec vs. reality
The SIE4 spec mandates IBM PC Codepage 437 (declared as `#FORMAT PC8`). Modern cloud accounting software exports UTF-8. Many programs write `#FORMAT PC8` regardless of actual encoding. This mismatch is the single largest source of SIE interoperability failures.
## Byte values for Swedish characters
| Char | CP437 (hex) | Latin-1 (hex) | UTF-8 (hex) |
|------|-------------|---------------|-------------|
| å | 0x86 | 0xE5 | 0xC3 0xA5 |
| ä | 0x84 | 0xE4 | 0xC3 0xA4 |
| ö | 0x94 | 0xF6 | 0xC3 0xB6 |
| Å | 0x8F | 0xC5 | 0xC3 0x85 |
| Ä | 0x8E | 0xC4 | 0xC3 0x84 |
| Ö | 0x99 | 0xD6 | 0xC3 0x96 |
| é | 0x82 | 0xE9 | 0xC3 0xA9 |
## Mojibake diagnosis patterns
### UTF-8 bytes misread as Latin-1/CP1252
- å → `Ã¥`, ä → `ä`, ö → `ö`
- Å → `Ã…`, Ä → `Ä`, Ö → `Ö`
- **Telltale**: The `Ã` prefix always indicates UTF-8 content decoded as single-byte
- **Fix**: Re-read the file as UTF-8
### CP437 bytes misread as Latin-1/CP1252
- å → `†`, ä → `„`, ö → `"`
- Å → control char (0x8F), Ä → control char (0x8E), Ö → `™`
- **Telltale**: Typographic characters like `†„"™` in account names
- **Fix**: Re-read the file as CP437
### Latin-1 bytes misread as CP437
- å → `σ`, ä → `Σ`, ö → `÷`
- **Telltale**: Greek letters or math symbols in Swedish text
- **Fix**: Re-read the file as Latin-1 (ISO 8859-1)
### Double-encoding (UTF-8 encoded twice)
- å → `ã¥` or similar multi-byte garbage
- **Telltale**: 3+ byte sequences where 2 were expected
- **Fix**: Decode as UTF-8, then decode the result as UTF-8 again (strip one layer)
## Encoding detection algorithm
Apply in this order, stop at first match:
1. **UTF-8 BOM check**: Bytes 0xEF 0xBB 0xBF at file start → UTF-8
2. **Strict UTF-8 validation**: Attempt full decode. If valid AND multi-byte sequences found → UTF-8. Pure ASCII files are ambiguous (proceed to next step).
3. **CP437 byte scan**: Look for bytes in range 0x80-0x9F in text contexts (account names, descriptions). If bytes 0x84, 0x86, 0x8E, 0x8F, 0x94, 0x99 appear → CP437
4. **Latin-1 byte scan**: Look for bytes in range 0xC0-0xFF in text contexts. If bytes 0xC4, 0xC5, 0xD6, 0xE4, 0xE5, 0xF6 appear → Latin-1
5. **#PROGRAM heuristic**: Extract program name from the file (readable in any encoding since it's typically ASCII):
- Cloud software → assume UTF-8
- Desktop software → assume CP437
6. **Fallback**: Latin-1 (every byte 0x00-0xFF is valid, never fails)
## Per-software encoding behaviors
| Software | Platform | Typical encoding | Notes |
|----------|----------|-----------------|-------|
| Fortnox | Cloud | UTF-8 | May declare `#FORMAT PC8`. Largest market share. |
| Visma Administration | Desktop | CP437 or Win-1252 | Legacy Windows app. Requires non-UTF-8 locale. |
| Visma eEkonomi | Cloud | UTF-8 or Latin-1 | Known garbage character issues in exports. |
| Visma Compact | Desktop | CP437 | Standards-compliant. Referenced in SIE spec examples. |
| BL Administration (Björn Lundén) | Desktop | CP437 | Standards-compliant. |
| Lundify (BL cloud) | Cloud | UTF-8 | Modern cloud version of BL. |
| SpeedLedger | Cloud | UTF-8 (likely) | Web-based Unicode environment. |
| PE Accounting | Cloud | UTF-8 (likely) | Web-based. |
| Bokio | Cloud | UTF-8 | Parser-level normalization documented. |
| Dooer | Cloud | UTF-8 | Parser-level normalization documented. |
| Hogia | Desktop/Cloud | CP437 or UTF-8 | Depends on product version. |
## #KSUMMA and encoding interaction
The CRC-32 checksum (#KSUMMA) is defined to operate on CP437 byte values. When a file is actually encoded in UTF-8:
1. Swedish characters have different byte representations (2 bytes in UTF-8 vs 1 byte in CP437)
2. The checksum computed on UTF-8 bytes will NOT match the expected CP437-based value
3. **Recommendation**: When non-CP437 encoding is detected, skip #KSUMMA validation entirely
4. When generating SIE files, either: (a) write CP437 with correct KSUMMA, or (b) write UTF-8 and omit KSUMMA
## Normalization strategy for import
Best practice for robust SIE import:
1. Read raw bytes from file
2. Detect encoding using the algorithm above
3. Decode to Unicode/UTF-8 internal representation
4. Validate #KSUMMA only if encoding is CP437
5. Parse all records from the Unicode text
6. Store internally as UTF-8
7. When exporting: write CP437 if standards compliance is critical (auditors, Skatteverket), otherwise UTF-8 with a note that KSUMMA is omitted
@@ -0,0 +1,296 @@
# SIE4 Record Type Reference
Complete specification of all SIE4 record types, fields, and rules.
## Table of contents
1. [Flag item](#flag-item)
2. [Identification items](#identification-items)
3. [Chart of accounts items](#chart-of-accounts-items)
4. [Dimension and object items](#dimension-and-object-items)
5. [Balance items](#balance-items)
6. [Verification and transaction items](#verification-and-transaction-items)
7. [Control total](#control-total)
8. [Record ordering](#record-ordering)
9. [Type availability matrix](#type-availability-matrix)
---
## Flag item
### #FLAGGA x
- **Compulsory**: All types
- **Position**: Must be first item in file
- **Values**: `0` = not yet imported (set by exporter), `1` = already imported (set by importer)
- **Purpose**: Prevents double-import of transaction files
- **Workflow**: Exporter creates with 0 → importer sets to 1 after success → re-import attempts detect 1 and refuse
---
## Identification items
### #PROGRAM name version
- **Compulsory**: All types
- **Example**: `#PROGRAM "Fortnox" 3.0`
- **Note**: Useful for encoding detection heuristics (cloud vs desktop)
### #FORMAT PC8
- **Compulsory**: All types
- **Values**: Only `PC8` (Codepage 437) is valid per spec
- **Reality**: Many programs write `PC8` but export UTF-8. Treat as unreliable for encoding detection.
### #GEN date [sign]
- **Compulsory**: All types
- **Fields**: date = YYYYMMDD, sign = optional author signature string
- **Example**: `#GEN 20240115 "JD"`
### #SIETYP n
- **Compulsory**: Types 2-4 (if absent, assume type 1)
- **Values**: 1, 2, 3, 4
- **Note**: No distinction between 4E and 4I in this field; both are type 4
### #FNAMN name
- **Compulsory**: All types
- **Example**: `#FNAMN "Exempelföretag AB"`
### #ORGNR cin [acq_no] [act_no]
- **Optional**
- **Format**: Organisationsnummer with hyphen after 6th digit: `556677-8899`
- **Additional fields**: acquisition number, activity number (rarely used)
### #RAR year_no start end
- **Compulsory**: Types 1-3 and 4E
- **Fields**: year_no (0 = current, -1 = previous, -2 = two years ago), start/end = YYYYMMDD
- **Both current (0) and previous (-1) year should be present**
- **Supports broken fiscal years**: `#RAR 0 20240701 20250630`
- **Example**: `#RAR 0 20240101 20241231`
### #TAXAR year
- **Optional**
- **Purpose**: Taxation year for SRU code interpretation
- **Example**: `#TAXAR 2024`
### #OMFATTN date
- **Compulsory**: Types 2-3; optional for 4E
- **Purpose**: End date of the period scope covered by the file
- **Example**: `#OMFATTN 20241231`
### #KPTYP type
- **Optional** (if missing, assume BAS 95)
- **Valid values**: `BAS95`, `BAS96`, `EUBAS97`, `NE2007`
- **Rule**: Any value starting with `BAS2` (e.g., `BAS2024`) is treated as `EUBAS97`
### #PROSA text
- **Optional**
- **Purpose**: Free-form comment
### #FNR id
- **Optional**
- **Purpose**: Internal company code in multi-company setups
### #ADRESS contact address postal_address tel
- **Optional**
- **Fields**: 4 quoted strings
- **Example**: `#ADRESS "Jan Sansen" "Box 21" "211 20 Malmö" "040-12 34 56"`
### #BKOD sni_code
- **Optional**
- **Purpose**: SNI industry code (Svensk Näringsgrensindelning)
### #FTYP type
- **Optional**
- **Valid values**: AB, E, HB, KB, EK, KHF, BRF, BF, SF, I, S, FL, BAB, MB, SB, BFL, FAB, OFB, SE, SCE, TSF, X
- **Common**: AB (aktiebolag), E (enskild firma), HB (handelsbolag), KB (kommanditbolag)
### #VALUTA code
- **Optional** (if missing, assume SEK)
- **Format**: ISO 4217 currency code
---
## Chart of accounts items
### #KONTO account_no name
- **Compulsory**: Types 1-3, 4E
- **Rule**: Account numbers must be numeric. All accounts used in the file must be declared.
- **Example**: `#KONTO 1510 "Kundfordringar"`
### #KTYP account_no type
- **Optional**
- **Values**: T (Tillgång/Asset), S (Skuld/Liability), K (Kostnad/Cost), I (Intäkt/Income)
- **Example**: `#KTYP 1510 T`
### #ENHET account_no unit
- **Optional**
- **Purpose**: Quantity unit for the account
- **Example**: `#ENHET 4010 "st"`
### #SRU account sru_code
- **Compulsory**: Types 1-2
- **Rule**: Multiple #SRU entries per account are permitted (one account can map to multiple SRU codes)
- **Example**: `#SRU 1510 7214`
---
## Dimension and object items
### #DIM dimension_no name
- **Types**: 3 and 4
- **Reserved dimensions** (need not be explicitly declared):
- 1 = Kostnadsställe (cost centre)
- 2 = Kostnadsbärare (cost bearer, sub-dimension of 1)
- 6 = Projekt (project)
- 7 = Anställd (employee)
- 8 = Kund (customer)
- 9 = Leverantör (supplier)
- 10 = Faktura (invoice)
- **Reserved but unused**: 3-5, 11-19
- **Freely assignable**: 20+
- **Example**: `#DIM 1 "Kostnadsställe"`
### #UNDERDIM dimension_no name superdimension
- **Optional**
- **Purpose**: Hierarchical sub-dimension declaration
- **Example**: `#UNDERDIM 2 "Kostnadsbärare" 1`
### #OBJEKT dimension_no object_no name
- **Types**: 3 and 4
- **Example**: `#OBJEKT 1 "100" "Stockholm"`
- **Example**: `#OBJEKT 6 "P01" "Projekt Alpha"`
---
## Balance items
### #IB year_no account balance [quantity]
- **Purpose**: Opening balance (Ingående Balans) for balance sheet accounts
- **year_no**: 0 = current year, -1 = previous year
- **Sign**: Credit = negative (liabilities/equity carry negative balances)
- **Rule**: Only balance sheet accounts (1xxx-2xxx) should have non-zero IB
- **Example**: `#IB 0 1510 125000.00`
### #UB year_no account balance [quantity]
- **Purpose**: Closing balance (Utgående Balans)
- **Rule**: UB for current year (0) must always be present
- **Continuity rule**: UB(year -1, account) must equal IB(year 0, account)
- **Example**: `#UB 0 1510 98500.00`
### #RES year_no account balance [quantity]
- **Purpose**: Balance for income statement accounts (3xxx-8xxx)
- **Sign**: Income (credit) = negative, Cost (debit) = positive
- **Example**: `#RES 0 3010 -500000.00` (500k in revenue)
### #OIB year_no account {dim_no obj_no} balance [quantity]
- **Purpose**: Opening balance per object (type 3+)
- **Example**: `#OIB 0 1510 {1 "100"} 50000.00`
### #OUB year_no account {dim_no obj_no} balance [quantity]
- **Purpose**: Closing balance per object (type 3+)
### #PSALDO year_no period account {obj_list} balance [quantity]
- **Compulsory**: Types 2-3
- **Purpose**: Monthly change on account (NOT cumulative)
- **Period**: YYYYMM
- **Object list**: Empty `{}` for type 2; object-specific for type 3
- **Example**: `#PSALDO 0 202401 3010 {} -42000.00`
### #PBUDGET year_no period account {obj_list} balance [quantity]
- **Optional**
- **Structure**: Same as #PSALDO but for budgeted values
---
## Verification and transaction items
### #VER series verno verdate [vertext] [regdate] [sign]
- **Type**: 4 only
- **Fields**:
- series = letter (A, B...), number, or alphanumeric string
- verno = sequential number within series
- verdate = YYYYMMDD
- vertext = optional description in quotes
- regdate = optional registration date YYYYMMDD
- sign = optional author signature
- **Rules**:
- Must be followed by `{` on its own line
- Then one or more #TRANS lines
- Then `}` on its own line
- All verifications within a series must be in ascending verno order
- For 4I files, series and verno may be empty (receiver assigns)
- **Example**: `#VER A 1 20240115 "Kundfaktura 2024-001" 20240115 "JD"`
### #TRANS account_no {object_list} amount [transdate] [transtext] [quantity] [sign]
- **Type**: 4 only, as sub-entry of #VER
- **Fields**:
- account_no = numeric account
- object_list = `{}` or `{dim_no "obj_no" ...}` (multiple dim/obj pairs allowed)
- amount = decimal, debit positive, credit negative
- transdate = optional override of verification date
- transtext = optional line-level description
- quantity = optional quantity
- sign = optional author
- **Balance rule**: Sum of all #TRANS amounts in a #VER = 0.00 exactly
- **Example**: `#TRANS 1510 {} 12500.00 20240115 "Faktura 2024-001"`
### #RTRANS (same fields as #TRANS)
- **Purpose**: Supplementary/corrected transaction
- **Rule**: Must be immediately followed by an identical #TRANS for backward compatibility
- **Programs understanding RTRANS**: use RTRANS, ignore following TRANS
- **Programs not understanding RTRANS**: ignore RTRANS, use TRANS
### #BTRANS (same fields as #TRANS)
- **Purpose**: Removed/cancelled transaction
- **Programs not understanding BTRANS**: simply ignore it
---
## Control total
### #KSUMMA [value]
- **Optional**
- **Two instances**:
1. Near file start: `#KSUMMA` (empty, signals active checksumming)
2. At file end: `#KSUMMA 1234567890` (the actual CRC-32 value)
- **Algorithm**: CRC-32 with polynomial EDB88320H, pre-conditioning FFFFFFFF, post-conditioning bit-invert
- **Excluded from calculation**: whitespace, quote characters, brace characters
- **Truncation detection**: If opening KSUMMA exists but closing is missing, file is truncated. Reject.
- **Encoding caveat**: CRC is calculated on CP437 byte values per spec. If file is actually UTF-8, checksum will not match. Skip validation for non-CP437 files.
---
## Record ordering
The SIE spec defines a strict ordering:
1. `#FLAGGA` (always first)
2. Identification items (#PROGRAM, #FORMAT, #GEN, #SIETYP, #FNAMN, #ORGNR, #RAR, etc.)
3. Chart of accounts (#KONTO, #KTYP, #ENHET, #SRU)
4. Dimensions and objects (#DIM, #UNDERDIM, #OBJEKT)
5. Balances (#IB, #UB, #OIB, #OUB, #RES, #PSALDO, #PBUDGET)
6. Verifications (#VER with nested #TRANS)
7. `#KSUMMA` (closing, if active)
Readers should be tolerant of minor ordering deviations but may reject severely out-of-order files.
---
## Type availability matrix
| Record | Type 1 | Type 2 | Type 3 | Type 4E | Type 4I |
|--------|--------|--------|--------|---------|---------|
| #FLAGGA | Required | Required | Required | Required | Required |
| #PROGRAM | Required | Required | Required | Required | Required |
| #FORMAT | Required | Required | Required | Required | Required |
| #GEN | Required | Required | Required | Required | Required |
| #SIETYP | Implied | Required | Required | Required | Required |
| #FNAMN | Required | Required | Required | Required | Optional |
| #RAR | Required | Required | Required | Required | N/A |
| #KONTO | Required | Required | Required | Required | N/A |
| #SRU | Required | Required | Optional | Optional | N/A |
| #DIM/#OBJEKT | N/A | N/A | Required | Optional | Optional |
| #IB/#UB | Required | Required | Required | Required | N/A |
| #RES | Required | Required | Required | Required | N/A |
| #OIB/#OUB | N/A | N/A | Required | Optional | N/A |
| #PSALDO | N/A | Required | Required | Optional | N/A |
| #PBUDGET | N/A | Optional | Optional | Optional | N/A |
| #VER/#TRANS | N/A | N/A | N/A | Required | Required |
| #KSUMMA | Optional | Optional | Optional | Optional | Optional |
@@ -0,0 +1,267 @@
# SIE4 Validation Rules and Error Patterns
## Table of contents
1. [Structural validation](#structural-validation)
2. [Verification integrity](#verification-integrity)
3. [Balance continuity](#balance-continuity)
4. [Account and dimension validation](#account-and-dimension-validation)
5. [Verification numbering](#verification-numbering)
6. [Fiscal year boundaries](#fiscal-year-boundaries)
7. [Encoding errors](#encoding-errors)
8. [Post-import issues](#post-import-issues)
9. [Validation severity levels](#validation-severity-levels)
10. [SIE file diffing](#sie-file-diffing)
---
## Structural validation
### Missing required records
- **Check**: #FLAGGA present and first
- **Check**: #PROGRAM, #FORMAT, #GEN present
- **Check**: #FNAMN present
- **Check**: #SIETYP present for types 2-4
- **Check**: #RAR present for types 1-3 and 4E (with both year 0 and year -1)
- **Check**: #OMFATTN present for types 2-3
- **Severity**: FATAL for missing compulsory records
### Truncated file detection
- **Check**: If opening `#KSUMMA` (empty) exists, closing `#KSUMMA value` must also exist
- **If missing**: File was truncated during export or transfer
- **Severity**: FATAL. Reject the file. Re-export from source system.
### Wrong SIE type
- **Symptom**: Importing .SI (4I) file expecting .SE (4E) format
- **Difference**: 4I lacks balances, results, fiscal year definitions, chart of accounts
- **Check**: Verify #SIETYP matches expected import type
- **Severity**: FATAL for type mismatch
### Malformed records
- **Check**: All lines starting with # have valid label names
- **Check**: Quoted strings properly closed (matching double quotes)
- **Check**: Escaped quotes `\"` within strings handled correctly
- **Check**: VER blocks have matching `{` and `}` on their own lines
- **Severity**: FATAL for unclosed quotes or unmatched braces
---
## Verification integrity
### Unbalanced verifications (most common error)
- **Rule**: Sum of all #TRANS amounts within a #VER = 0.00
- **Detection**: Parse each VER block, sum all TRANS amounts, check for exact zero
- **Common cause**: VAT rounding across multiple line items producing 0.01 SEK discrepancy
- **Remediation**: Add öresutjämning line using account 3741 (Öresutjämning) for the difference
- **Tolerance**: Some parsers (jsisie) offer `AllowUnbalancedVoucher` flag. Use only for analysis, never for production import.
- **Severity**: ERROR. Block import of the specific verification unless tolerance is enabled.
### Missing transaction data
- **Check**: Every #VER block contains at least one #TRANS
- **Check**: #TRANS has valid account_no (numeric) and amount (parseable decimal)
- **Check**: #TRANS amount has max 2 decimal places
- **Severity**: FATAL for empty VER blocks; ERROR for unparseable amounts
### Date validation
- **Check**: verdate in #VER is valid YYYYMMDD
- **Check**: verdate falls within a fiscal year defined by #RAR
- **Check**: If regdate provided, it is a valid date
- **Check**: If transdate in #TRANS provided, it is a valid date
- **Severity**: WARNING for dates outside fiscal year; ERROR for unparseable dates
---
## Balance continuity
### IB/UB cross-year continuity
- **Rule**: For every balance sheet account (1xxx-2xxx): UB(year -1) = IB(year 0)
- **Detection**: Compare `#UB -1 account balance` with `#IB 0 account balance` for all accounts
- **Common cause**: Incomplete year-end closing, post-closing manual adjustments, mixed-date exports
- **Remediation**: Create adjustment verification in opening period to reconcile
- **Severity**: WARNING. Flag discrepancy with exact amounts. Do not silently proceed.
### Result account IB validation
- **Rule**: IB for income statement accounts (3xxx-9xxx) must be zero
- **Detection**: Check `#IB 0 account balance` for all accounts where first digit is 3-9
- **Common cause**: Source system did not complete year-end closing before export
- **Remediation**: Run closing entries in source system and re-export, or create closing entries in target
- **Severity**: WARNING
### UB/RES consistency
- **Check**: For current year, UB values for balance sheet accounts should be derivable from IB + sum of relevant TRANS
- **Check**: RES values for income statement accounts should equal sum of relevant TRANS
- **Severity**: INFO (advisory check, complex to validate fully)
### Missing balances
- **Check**: All accounts referenced in #TRANS have corresponding #IB/#UB or #RES records
- **Check**: #UB for year 0 exists for all balance sheet accounts with activity
- **Severity**: WARNING for missing UB; INFO for missing IB on accounts with no prior activity
---
## Account and dimension validation
### Undeclared accounts
- **Rule**: All accounts used in #TRANS must be declared in #KONTO (for types 1-3, 4E)
- **Exception**: Type 4I files may rely on target system's existing chart
- **Detection**: Collect all account numbers from #TRANS, check against #KONTO declarations
- **Remediation**: Add missing #KONTO declarations or map to existing accounts in target
- **Severity**: ERROR for 4E files; WARNING for 4I files
### Duplicate account declarations
- **Rule**: Each account number may only appear once in #KONTO
- **Detection**: Track seen accounts, flag duplicates
- **Severity**: ERROR
### Invalid account numbers
- **Check**: Account numbers are numeric (no letters)
- **Check**: Account numbers are 4 digits (standard BAS) or valid extended length
- **Severity**: ERROR for non-numeric; WARNING for non-standard length
### Account class 9 handling
- **Note**: Class 9 (internal/management accounting) is unsupported by some programs (e.g., Visma Bokföring & Fakturering)
- **Check**: If target system doesn't support class 9, flag any 9xxx accounts
- **Severity**: WARNING
### Dimension/object mismatches
- **Check**: Dimension numbers in TRANS object lists are declared in #DIM or are standard reserved (1,2,6-10)
- **Check**: Object numbers reference objects declared in #OBJEKT
- **Severity**: WARNING for undeclared dimensions; ERROR for malformed object list syntax
---
## Verification numbering
### Duplicate verification numbers
- **Rule**: Within each series, verification numbers must be unique
- **Detection**: Track (series, verno) pairs, flag duplicates
- **Common cause**: Series collision when importing into system with existing verifications
- **Remediation**: Remap to unused series letter or offset numbering
- **Severity**: ERROR
### Gaps in numbering
- **Detection**: Within each series, check for sequential numbering (1, 2, 3...)
- **Significance**: Gaps may indicate deleted verifications. BFL requires unbroken sequences for customer invoices.
- **Severity**: WARNING
### Non-ascending order
- **Rule**: Verifications within a series must appear in ascending verno order in the file
- **Detection**: Track last seen verno per series, flag if current < previous
- **Severity**: WARNING (some parsers tolerate this but it violates spec)
### Empty series/verno in 4I files
- **Rule**: 4I import files are allowed to have empty series and verno
- **Detection**: If #SIETYP 4 and series/verno empty, this is valid for import
- **Handling**: Receiving program must assign series and verno
---
## Fiscal year boundaries
### Transactions outside fiscal year
- **Check**: All #VER dates fall within a #RAR-defined period
- **Detection**: Parse all #RAR records to build valid date ranges, check each VER date
- **Severity**: WARNING. May indicate wrong fiscal year selected for export.
### Overlapping fiscal year definitions
- **Check**: #RAR records for different year indices should not overlap in date range
- **Severity**: ERROR
### Incomplete fiscal year exports
- **Check**: Compare date range of actual verifications against #RAR 0 period
- **If verifications cover only part of the year**: Flag as potentially incomplete
- **Use #OMFATTN**: This record explicitly declares the scope end date
- **Severity**: INFO
### Broken fiscal year validation
- **Check**: If #RAR shows non-calendar year (e.g., July-June), verify all period-dependent logic handles this
- **Check**: First fiscal year may be up to 18 months (AB) or shorter
- **Severity**: INFO (important for correct period assignment)
---
## Encoding errors
### Garbled Swedish characters
- **Symptom**: å/ä/ö appear as garbage in account names, company name, descriptions
- **Diagnosis**: Use mojibake patterns from encoding.md to identify mismatch type
- **Remediation**: Re-decode file with correct encoding
- **Severity**: WARNING (data is recoverable, not lost)
### #FORMAT PC8 mismatch
- **Symptom**: File declares `#FORMAT PC8` but is actually UTF-8
- **Detection**: Encoding detection algorithm identifies non-CP437
- **Impact**: #KSUMMA validation will fail
- **Handling**: Skip KSUMMA, parse with detected encoding
- **Severity**: INFO
---
## Post-import issues
### Missing VAT codes
- **Issue**: SIE format carries no VAT/moms code information
- **Impact**: Momsdeklaration/momsrapporter cannot be generated automatically
- **Remediation**: Manually configure VAT codes for relevant accounts in target system
- **Accounts affected**: 2610-2650 (output VAT), 2640-2649 (input VAT)
- **Severity**: Operational issue, not a file error
### Missing subledger data
- **Issue**: SIE 1-4 does not carry customer/supplier subledger (reskontra) data
- **Impact**: Aged receivables/payables reports unavailable
- **Remediation**: Import subledger data separately or rebuild from individual verifications
- **Severity**: Operational issue
### Verification series remapping
- **Issue**: Target system uses different series conventions than source
- **Remediation**: Map source series to target series, document the mapping
- **Best practice**: Use unused series letters in target to avoid collisions
---
## Validation severity levels
| Level | Meaning | Action |
|-------|---------|--------|
| **FATAL** | File cannot be processed | Reject file, report error, re-export from source |
| **ERROR** | Specific records invalid | Block affected records, allow rest if possible |
| **WARNING** | Data integrity concern | Import with flag, require manual review |
| **INFO** | Advisory | Log for reference, no action required |
## Recommended validation order
1. Structural checks (required records, truncation, type)
2. Encoding detection and normalization
3. Record parsing (field formats, dates, amounts)
4. Chart of accounts validation (duplicates, undeclared)
5. Balance integrity (IB/UB continuity, result account reset)
6. Verification integrity (balance check, numbering)
7. Fiscal year boundary checks
8. Dimension/object validation
9. KSUMMA verification (only if CP437)
---
## SIE file diffing
### Comparing two SIE files
To detect changes between two exports (e.g., before/after a correction period, or during migration validation):
1. **Balance diff**: Compare #IB/#UB records by account. Flag any amount differences.
2. **Result diff**: Compare #RES records by account.
3. **Verification diff**: Match #VER blocks by (series, verno). Compare:
- Transaction amounts per account
- Transaction dates
- Number of lines
- Total verification count per series
4. **Added/removed verifications**: Identify VER blocks present in one file but not the other.
5. **Period diff**: Compare #PSALDO month-by-month per account.
6. **Chart diff**: Compare #KONTO declarations (added/removed/renamed accounts).
7. **Metadata diff**: Compare #FNAMN, #ORGNR, #RAR, #KPTYP for structural changes.
### Implementation reference
The .NET library `jsisie` provides `SieDocumentComparer.Compare()` returning a structured diff list. For custom implementations, key data structures are:
- Map<(series, verno), VER_block> for verification matching
- Map<(year_no, account), balance> for IB/UB/RES comparison
- Map<(year_no, period, account), balance> for PSALDO comparison
+258
View File
@@ -0,0 +1,258 @@
---
name: swedish-sru-filing
description: >
Swedish SRU file generation for Skatteverket digital tax filing (INK2, INK2R, INK2S declarations for aktiebolag).
Covers the two-file submission structure (INFO.SRU + BLANKETTER.SRU), all SRU field codes for INK2/INK2R/INK2S,
BAS-to-SRU account mappings for räkenskapsschema, ISO 8859-1 encoding rules, amount formatting (hela kronor,
no öre), 12-digit org number formatting, blankett type period suffixes (P1-P4), #BLANKETT/#BLANKETTSLUT
delimiters, #UPPGIFT record format, validation error patterns, and rounding/truncation rules per SFL 22:1.
Trigger on ANY question about SRU files, SRU-koder, fältkoder, filöverföring till Skatteverket, INK2S/INK2R
generation, BAS-to-SRU mapping, "skapa SRU", "generera deklarationsfil", "digital inlämning INK2",
BLANKETTER.SRU, INFO.SRU, SKV269, or any code that produces SRU output. Also trigger when debugging
Skatteverket validation errors on uploaded SRU files. Always use this skill over training data for SRU topics.
---
# Swedish SRU File Generation
This skill contains the complete technical specification for generating valid SRU files accepted by Skatteverket's filöverföringstjänst. The canonical source is Skatteverket's "Teknisk information om filöverföring" page (replaced brochure SKV 269 from Jan 1 2024).
**Before writing any SRU generation code**, read `references/sru-codes.md` for the complete field code tables and BAS-to-SRU mapping.
## Architecture: Two files, always
Every SRU submission consists of exactly two files:
| File | Content | Max size |
|---|---|---|
| `INFO.SRU` | Submitter metadata (who is filing) | — |
| `BLANKETTER.SRU` | All blankett blocks with tax data | 5 MB |
File names are case-insensitive but must not be renamed (browsers appending `(1)` cause rejection).
## INFO.SRU structure
Posts must appear in this exact order. Omit optional posts entirely if not used.
```
#DATABESKRIVNING_START
#PRODUKT SRU
#MEDIAID <free-form ID>
#SKAPAD <YYYYMMDD> <HHMMSS>
#PROGRAM <program name and version>
#FILNAMN BLANKETTER.SRU
#DATABESKRIVNING_SLUT
#MEDIELEV_START
#ORGNR <12-digit org number>
#NAMN <submitter name, max 250 chars>
#ADRESS <postal address>
#POSTNR <5-digit postal code>
#POSTORT <city>
#AVDELNING <department>
#KONTAKT <contact person>
#EMAIL <email>
#TELEFON <max 15 chars>
#FAX <max 15 chars>
#MEDIELEV_SLUT
```
Mandatory posts: `#PRODUKT`, `#FILNAMN`, `#ORGNR`, `#NAMN`, `#POSTNR`, `#POSTORT`.
The `#PRODUKT` value is always `SRU` (post-2013). There is no `#PERIOD` post — period is encoded in each blankett type string.
## BLANKETTER.SRU structure
Contains one or more blankett blocks, terminated by `#FIL_SLUT`:
```
#BLANKETT <BlankettTyp>
#IDENTITET <OrgNr> <YYYYMMDD> <HHMMSS>
#NAMN <taxpayer name>
#UPPGIFT <FältKod> <FältVärde>
... (repeat #UPPGIFT for each field)
#BLANKETTSLUT
... (repeat #BLANKETT blocks for each form section)
#FIL_SLUT
```
### INK2 requires three blankett blocks
An aktiebolag filing INK2 must include three separate blocks in this file:
| Block | BlankettTyp example | Content |
|---|---|---|
| INK2 | `INK2-2024P4` | Huvudblankett (page 1) — summary fields |
| INK2R | `INK2R-2024P4` | Räkenskapsschema (pages 2-3) — balance sheet + income statement |
| INK2S | `INK2S-2024P4` | Skattemässiga justeringar (page 4) — tax adjustments |
### Period suffix rules
The suffix after the hyphen encodes when the fiscal year ENDS:
| Suffix | Fiscal year ends in months |
|---|---|
| P1 | JanuaryApril |
| P2 | MayAugust |
| P3 | Special cases |
| P4 | SeptemberDecember (calendar-year companies) |
The year in the type string is the INCOME YEAR (inkomstår), not the filing year. A company with fiscal year 2024-01-01 to 2024-12-31 uses period `2024P4`.
### Each block is independent
Every blankett block carries its own `#IDENTITET` line. The `DatFramst` timestamp determines version precedence — later timestamps replace earlier submissions for the same org number and blankett type.
## Encoding and formatting rules
### Encoding: ISO 8859-1 (Latin-1)
This is the single most common source of validation failure in programmatic SRU generation. **Never use UTF-8.** Swedish characters (å, ä, ö) will corrupt.
When writing files in code:
- Python: `open(path, 'w', encoding='iso-8859-1')`
- Node.js: Use `iconv-lite` to encode to `iso-8859-1` before writing
- Java: `new OutputStreamWriter(fos, StandardCharsets.ISO_8859_1)`
### Line endings
All three conventions accepted: `\r\n` (Windows), `\r` (classic Mac), `\n` (Unix).
### Amount formatting
- **Integers in hela kronor (whole SEK)**. No öre, no decimals.
- Positive: no sign, no leading zeros. Example: `1000`
- Negative: `-` prefix. Example: `-1000`
- **No thousands separators.** `7 135` with a space WILL fail.
- Truncation rule per SFL 22 kap. 1 §: öre are DROPPED (truncated), not rounded.
- Small rounding differences from öre truncation across multiple posts are accepted by Skatteverket.
### Org number format
Always 12 digits, format `SSÅÅMMDDNNNK`, no hyphens.
- Juridiska personer (companies): century prefix `16`
- Example: org nr `556000-0100` becomes `165560000100`
### Checkbox fields
Value: uppercase `X`. If unchecked, **omit the entire #UPPGIFT line** — never send empty values.
### The `#` character
Reserved for post names. **Forbidden in all string data values.**
## Zero-value handling
**Do not emit `#UPPGIFT` lines for fields with zero value.** Omit them entirely. Including zero-value fields is a common source of validation warnings and in some cases errors.
## Complete example: calendar-year 2024 AB
### INFO.SRU
```
#DATABESKRIVNING_START
#PRODUKT SRU
#SKAPAD 20250401 100000
#PROGRAM Luka 1.0
#FILNAMN BLANKETTER.SRU
#DATABESKRIVNING_SLUT
#MEDIELEV_START
#ORGNR 165590001234
#NAMN Exempelbolaget AB
#POSTNR 11122
#POSTORT Stockholm
#KONTAKT Anna Andersson
#EMAIL anna@exempel.se
#MEDIELEV_SLUT
```
### BLANKETTER.SRU
```
#BLANKETT INK2-2024P4
#IDENTITET 165590001234 20250401 100000
#NAMN Exempelbolaget AB
#UPPGIFT 7011 20240101
#UPPGIFT 7012 20241231
#UPPGIFT 7113 90000
#BLANKETTSLUT
#BLANKETT INK2R-2024P4
#IDENTITET 165590001234 20250401 100001
#NAMN Exempelbolaget AB
#UPPGIFT 7011 20240101
#UPPGIFT 7012 20241231
#UPPGIFT 7201 100000
#UPPGIFT 7281 50000
#UPPGIFT 7301 50000
#UPPGIFT 7302 25000
#UPPGIFT 7410 500000
#UPPGIFT 7513 -200000
#UPPGIFT 7514 -150000
#UPPGIFT 7450 75000
#BLANKETTSLUT
#BLANKETT INK2S-2024P4
#IDENTITET 165590001234 20250401 100002
#NAMN Exempelbolaget AB
#UPPGIFT 7011 20240101
#UPPGIFT 7012 20241231
#UPPGIFT 7650 75000
#UPPGIFT 7651 15000
#UPPGIFT 8020 90000
#BLANKETTSLUT
#FIL_SLUT
```
## Validation errors
Skatteverket validates on upload and returns a mottagningskvittens.
### Level 1 (entire submission rejected)
- Structural errors in INFO.SRU
- Posts in wrong order in any blankett block
- More than 100 level 2 errors total
- Missing `#FIL_SLUT`
- Unknown post types
- `#FILNAMN` referencing nonexistent file
### Level 2 (individual blankett block rejected, others accepted)
- Invalid org number
- Field code not valid for the blankett type
- Value violates field rules (wrong data type, out of range)
- Missing mandatory timestamp
- Invalid blankett type string
### Most frequent real-world failures
1. **Renamed files** — browsers adding `(1)` suffix
2. **Wrong period** — using `2025P4` when income year is 2024
3. **Amounts with decimals or spaces**`7135.50` or `7 135`
4. **UTF-8 encoding** instead of ISO 8859-1
5. **Including #UPPGIFT for zero/empty values**
6. **Duplicate field codes** in same blankett block
7. **10-digit or hyphenated org number** instead of 12-digit
8. **Non-existent SRU codes** — mapping BAS accounts to wrong field codes
## BAS-to-SRU mapping: critical rules
The official mapping is maintained by BAS-kontogruppen + Skatteverket at `bas.se/kontoplaner/sru/`.
**The #1 mapping error**: BAS accounts 5000-6999 (övriga externa kostnader) must ALL aggregate into a single SRU code: **7513**. Do NOT create individual SRU codes per BAS account in this range.
**INK2S codes are NOT auto-derived from BAS accounts.** They represent tax adjustments requiring manual calculation. The bookkeeping result flows from INK2R into INK2S field 7650/7750, then tax adjustments are applied to arrive at 8020/8021 (överskott/underskott).
**For the complete SRU code tables and BAS mapping**, read `references/sru-codes.md`.
## Data type reference
| Type | Format | Range |
|---|---|---|
| Numeriskt_A | Integer | -999,999,999,999 to 999,999,999,999 |
| Numeriskt_B | Integer | 0 to 999,999,999,999 |
| Datum_A | YYYYMMDD | Valid calendar date |
| Tid_A | HHMMSS | 00:00:00 to 23:59:59 |
| Decimal_2 | x.xx | -9,999,999,999.99 to 9,999,999,999.99 |
| Andel_4 | x.xxxx | 0.0000 to 100.0000 |
| STR_250 | String | Max 250 chars, no `#` |
## Key external references
- Skatteverket tech spec: `skatteverket.se/foretag/inkomstdeklaration/forredovisningsbyraer/tekniskinformationomfiloverforing`
- BAS SRU mappings: `bas.se/kontoplaner/sru/`
- Annual field code ZIP packages: download from Skatteverket tech spec page (Excel files per period)
- Validation service: `www1.skatteverket.se/fv/fv_web/start.do`
- Open-source reference implementations: `github.com/thpe/pysru-accounting`, `github.com/aidium/SRU-Maker`
@@ -0,0 +1,295 @@
# SRU Code Tables and BAS-to-SRU Mapping
Complete field code (fältkod) tables for INK2, INK2R, and INK2S blankett types, plus the BAS account to SRU code mapping for INK2R.
## Table of Contents
1. [INK2 — Huvudblankett](#ink2)
2. [INK2R — Räkenskapsschema: Balance Sheet Assets](#ink2r-assets)
3. [INK2R — Räkenskapsschema: Balance Sheet Equity & Liabilities](#ink2r-equity)
4. [INK2R — Räkenskapsschema: Income Statement](#ink2r-income)
5. [INK2S — Skattemässiga justeringar](#ink2s)
6. [BAS-to-SRU Mapping: Balance Sheet](#bas-balance)
7. [BAS-to-SRU Mapping: Income Statement](#bas-income)
8. [Sign conventions](#signs)
---
<a id="ink2"></a>
## 1. INK2 — Huvudblankett (main declaration, page 1)
| SRU | Row | Description |
|---|---|---|
| 7011 | — | Räkenskapsår fr.o.m. (YYYYMMDD) |
| 7012 | — | Räkenskapsår t.o.m. (YYYYMMDD) |
| 7113 | 1.1 | Överskott av näringsverksamhet |
| 7114 | 1.2 | Underskott av näringsverksamhet |
| 7132 | 1.4 | Underlag för särskild löneskatt på pensionskostnader |
| 7133 | 1.5 | Negativt underlag särskild löneskatt |
| 7153 | 1.6a | Avkastningsskatt 15% |
| 7155 | 1.7a | Avkastningsskatt 30% |
Fields 7113/7114 correspond directly to INK2S fields 8020/8021.
---
<a id="ink2r-assets"></a>
## 2. INK2R — Balance Sheet: Assets (Tillgångar)
| SRU | Row | Description |
|---|---|---|
| 7201 | 2.1 | Koncessioner, patent, licenser, varumärken, hyresrätter, goodwill |
| 7202 | 2.2 | Förskott avs. immateriella anläggningstillgångar |
| 7214 | 2.3 | Byggnader och mark |
| 7215 | 2.4 | Maskiner, inventarier, övriga materiella anläggningstillgångar |
| 7216 | 2.5 | Förbättringsutgifter på annans fastighet |
| 7217 | 2.6 | Pågående nyanläggningar, förskott materiella anläggningstillgångar |
| 7230 | 2.7 | Andelar i koncernföretag |
| 7231 | 2.8 | Andelar i intresseföretag och gemensamt styrda företag |
| 7233 | 2.9 | Ägarintressen i övriga företag + andra långfristiga värdepapper |
| 7232 | 2.10 | Fordringar hos koncern-/intresse-/gemensamt styrda företag |
| 7234 | 2.11 | Lån till delägare eller närstående |
| 7235 | 2.12 | Fordringar hos övriga företag med ägarintresse + andra långfristiga fordringar |
| 7241 | 2.13 | Råvaror och förnödenheter |
| 7242 | 2.14 | Varor under tillverkning |
| 7243 | 2.15 | Färdiga varor och handelsvaror |
| 7244 | 2.16 | Övriga lagertillgångar |
| 7245 | 2.17 | Pågående arbeten för annans räkning |
| 7246 | 2.18 | Förskott till leverantörer |
| 7251 | 2.19 | Kundfordringar |
| 7252 | 2.20 | Fordringar hos koncern-/intresse-/gemensamt styrda företag (kortfristiga) |
| 7261 | 2.21 | Fordringar hos övriga företag med ägarintresse + övriga fordringar |
| 7262 | 2.22 | Upparbetad men ej fakturerad intäkt |
| 7263 | 2.23 | Förutbetalda kostnader och upplupna intäkter |
| 7270 | 2.24 | Andelar i koncernföretag (kortfristiga) |
| 7271 | 2.25 | Övriga kortfristiga placeringar |
| 7281 | 2.26 | Kassa, bank och redovisningsmedel |
---
<a id="ink2r-equity"></a>
## 3. INK2R — Balance Sheet: Equity & Liabilities (Eget kapital och skulder)
| SRU | Row | Description |
|---|---|---|
| 7301 | 2.27 | Bundet eget kapital |
| 7302 | 2.28 | Fritt eget kapital |
| 7321 | 2.29 | Periodiseringsfonder |
| 7322 | 2.30 | Ackumulerade överavskrivningar |
| 7323 | 2.31 | Övriga obeskattade reserver |
| 7331 | 2.32 | Avsättningar för pensioner enl. tryggandelagen |
| 7332 | 2.33 | Övriga avsättningar för pensioner |
| 7333 | 2.34 | Övriga avsättningar |
| 7350 | 2.35 | Obligationslån |
| 7351 | 2.36 | Checkräkningskredit (långfristig) |
| 7352 | 2.37 | Övriga skulder till kreditinstitut (långfristiga) |
| 7353 | 2.38 | Skulder till koncern-/intresse-/gemensamt styrda företag (långfristiga) |
| 7354 | 2.39 | Skulder till övriga företag med ägarintresse + övriga skulder (långfristiga) |
| 7360 | 2.40 | Checkräkningskredit (kortfristig) |
| 7361 | 2.41 | Övriga skulder till kreditinstitut (kortfristiga) |
| 7362 | 2.42 | Förskott från kunder |
| 7363 | 2.43 | Pågående arbeten för annans räkning (skuldsida) |
| 7364 | 2.44 | Fakturerad men ej upparbetad intäkt |
| 7365 | 2.45 | Leverantörsskulder |
| 7366 | 2.46 | Växelskulder |
| 7367 | 2.47 | Skulder till koncern-/intresse-/gemensamt styrda företag (kortfristiga) |
| 7369 | 2.48 | Skulder till övriga företag med ägarintresse + övriga skulder (kortfristiga) |
| 7368 | 2.49 | Skatteskulder |
| 7370 | 2.50 | Upplupna kostnader och förutbetalda intäkter |
---
<a id="ink2r-income"></a>
## 4. INK2R — Income Statement (Resultaträkning)
| SRU | Row | Description | Sign |
|---|---|---|---|
| 7410 | 3.1 | Nettoomsättning | * |
| 7411 | 3.2 | Förändring av lager | * |
| 7412 | 3.3 | Aktiverat arbete för egen räkning | * |
| 7413 | 3.4 | Övriga rörelseintäkter | * |
| 7511 | 3.5 | Råvaror och förnödenheter | * |
| 7512 | 3.6 | Handelsvaror | * |
| 7513 | 3.7 | Övriga externa kostnader | * |
| 7514 | 3.8 | Personalkostnader | * |
| 7515 | 3.9 | Av- och nedskrivningar materiella/immateriella | * |
| 7516 | 3.10 | Nedskrivningar omsättningstillgångar | * |
| 7517 | 3.11 | Övriga rörelsekostnader | * |
| 7414 | 3.12 | Resultat från andelar i koncernföretag | * |
| 7415 | 3.13 | Resultat från andelar i intresseföretag | * |
| 7423 | 3.14 | Resultat från övriga företag med ägarintresse | * |
| 7416 | 3.15 | Resultat från övriga finansiella anläggningstillgångar | * |
| 7417 | 3.16 | Övriga ränteintäkter och liknande | * |
| 7521 | 3.17 | Nedskrivningar finansiella anläggningstillgångar | * |
| 7522 | 3.18 | Räntekostnader och liknande | * |
| 7524 | 3.19 | Lämnade koncernbidrag | * |
| 7419 | 3.20 | Mottagna koncernbidrag | * |
| 7420 | 3.21 | Återföring av periodiseringsfond | * |
| 7525 | 3.22 | Avsättning till periodiseringsfond | * |
| 7421 | 3.23 | Förändring av överavskrivningar | * |
| 7422 | 3.24 | Övriga bokslutsdispositioner | * |
| 7528 | 3.25 | Skatt på årets resultat | * |
| 7450 | 3.26 | Årets resultat, vinst | + |
| 7550 | 3.27 | Årets resultat, förlust | - |
**Sign convention (*)**: No pre-printed sign on form. Supply the sign as-is from the accounting. Costs are typically negative. **(+)**: Positive pre-printed; report positive for agreement. **(-)**: Negative pre-printed; report positive for agreement, negative to deviate.
---
<a id="ink2s"></a>
## 5. INK2S — Skattemässiga justeringar (tax adjustments, page 4)
| SRU | Row | Description |
|---|---|---|
| 7650 | 4.1 | Årets resultat, vinst |
| 7750 | 4.2 | Årets resultat, förlust |
| 7651 | 4.3a | Skatt på årets resultat (ej avdragsgill) |
| 7652 | 4.3b | Nedskrivning av finansiella tillgångar |
| 7653 | 4.3c | Andra bokförda kostnader som inte är avdragsgilla |
| 7751 | 4.4a | Lämnade koncernbidrag |
| 7764 | 4.4b | Andra ej bokförda kostnader som ska dras av |
| 7752 | 4.5a | Ackordsvinster (skattefria) |
| 7753 | 4.5b | Utdelning (skattefri) |
| 7754 | 4.5c | Andra bokförda intäkter som inte ska beskattas |
| 7654 | 4.6a | Schablonintäkt på periodiseringsfonder |
| 7668 | 4.6b | Schablonintäkt på fondandelar |
| 7655 | 4.6c | Mottagna koncernbidrag |
| 7656 | 4.6d | Uppräknat belopp vid återföring av periodiseringsfond |
| 7657 | 4.6e | Andra ej bokförda intäkter som ska beskattas |
| 7755 | 4.7a | Bokförd vinst vid avyttring av delägarrätter |
| 7756 | 4.7b | Bokförd förlust vid avyttring av delägarrätter |
| 7658 | 4.7e | Kapitalvinst för beskattningsåret |
| 7757 | 4.7f | Kapitalförlust som ska dras av |
| 7758 | 4.8a | Bokförd intäkt/vinst i handelsbolag |
| 7659 | 4.8b | Skattemässigt överskott enl. N3B |
| 7660 | 4.8c | Bokförd kostnad/förlust i handelsbolag |
| 7759 | 4.8d | Skattemässigt underskott enl. N3B |
| 7663 | 4.13 | Andra skattemässiga justeringar (catch-all) |
| 7763 | 4.14a | Outnyttjat underskott från föregående beskattningsår |
| 7664 | 4.14b | Reduktion av underskott (beloppsspärr/ackord) |
| 7670 | 4.14c | Reduktion pga koncernbidragsspärr/fusionsspärr |
| 8020 | 4.15 | Överskott → överförs till punkt 1.1 (INK2 field 7113) |
| 8021 | 4.16 | Underskott → överförs till punkt 1.2 (INK2 field 7114) |
| 7770 | 4.20 | Lån från aktieägare (fysisk person) vid beskattningsårets utgång |
**Critical**: INK2S codes are NOT auto-derived from BAS accounts. They represent tax adjustments requiring manual calculation. The bookkeeping result (årets resultat from INK2R) flows into 7650/7750, then adjustments produce 8020/8021.
---
<a id="bas-balance"></a>
## 6. BAS-to-SRU Mapping: Balance Sheet
Source: BAS-kontogruppen + Skatteverket joint mapping at bas.se/kontoplaner/sru/. Stable since 2017.
### Assets
| BAS accounts | → SRU | INK2R row | Description |
|---|---|---|---|
| 1010-1079, 1090-1099 | 7201 | 2.1 | Immateriella anläggningstillgångar |
| 1080-1089 | 7202 | 2.2 | Förskott immateriella |
| 1100-1119, 1130-1179, 1190-1199 | 7214 | 2.3 | Byggnader och mark |
| 1200-1299 (most) | 7215 | 2.4 | Maskiner och inventarier |
| 1120-1129 | 7216 | 2.5 | Förbättringsutgifter annans fastighet |
| 1180-1189 | 7217 | 2.6 | Pågående nyanläggningar |
| 1311-1316 | 7230 | 2.7 | Andelar koncernföretag |
| 1330-1338 | 7231 | 2.8 | Andelar intresseföretag |
| 1350-1359, 1380-1389 | 7233 | 2.9 | Ägarintressen övriga |
| 1320-1329, 1340-1349 | 7232 | 2.10 | Fordringar koncern/intresse |
| 1360-1369 | 7234 | 2.11 | Lån till delägare |
| 1370-1379, 1390-1399 | 7235 | 2.12 | Övriga långfristiga fordringar |
| 1410-1419 | 7241 | 2.13 | Råvaror |
| 1440-1449 | 7242 | 2.14 | Varor under tillverkning |
| 1450-1469 | 7243 | 2.15 | Färdiga varor |
| 1470-1489 | 7244 | 2.16 | Övriga lagertillgångar |
| 1490-1499 | 7245 | 2.17 | Pågående arbeten |
| 1400-1409 | 7246 | 2.18 | Förskott till leverantörer |
| 1500-1519 | 7251 | 2.19 | Kundfordringar |
| 1560-1579 | 7252 | 2.20 | Fordringar koncern/intresse (kortfristiga) |
| 1520-1559, 1580-1599, 1600-1699 | 7261 | 2.21 | Övriga fordringar |
| 1620 (specific) | 7262 | 2.22 | Upparbetad ej fakturerad |
| 1700-1799 | 7263 | 2.23 | Förutbetalda kostnader |
| 1860-1869 | 7270 | 2.24 | Kortfristiga andelar koncern |
| 1800-1859, 1870-1899 | 7271 | 2.25 | Övriga kortfristiga placeringar |
| 1900-1999 | 7281 | 2.26 | Kassa och bank |
### Equity & Liabilities
| BAS accounts | → SRU | INK2R row | Description |
|---|---|---|---|
| 2010-2089 | 7301 | 2.27 | Bundet eget kapital |
| 2090-2099 | 7302 | 2.28 | Fritt eget kapital |
| 2110-2129 | 7321 | 2.29 | Periodiseringsfonder |
| 2150-2159 | 7322 | 2.30 | Ackumulerade överavskrivningar |
| 2130-2149, 2160-2199 | 7323 | 2.31 | Övriga obeskattade reserver |
| 2210-2219 | 7331 | 2.32 | Pensionsavsättningar tryggandelagen |
| 2220-2229 | 7332 | 2.33 | Övriga pensionsavsättningar |
| 2230-2299 | 7333 | 2.34 | Övriga avsättningar |
| 2320-2329 | 7350 | 2.35 | Obligationslån |
| 2330-2339 | 7351 | 2.36 | Checkräkningskredit (långfristig) |
| 2340-2359 | 7352 | 2.37 | Övriga skulder kreditinstitut (långfristiga) |
| 2360-2379 | 7353 | 2.38 | Skulder koncern/intresse (långfristiga) |
| 2380-2399 | 7354 | 2.39 | Övriga skulder (långfristiga) |
| 2410-2419 | 7360 | 2.40 | Checkräkningskredit (kortfristig) |
| 2420-2439 | 7361 | 2.41 | Övriga skulder kreditinstitut (kortfristiga) |
| 2400-2409 | 7362 | 2.42 | Förskott från kunder |
| 2450-2459 | 7363 | 2.43 | Pågående arbeten (skuld) |
| 2460-2469 | 7364 | 2.44 | Fakturerad ej upparbetad |
| 2440-2449 | 7365 | 2.45 | Leverantörsskulder |
| 2490 | 7366 | 2.46 | Växelskulder |
| 2470-2479 | 7367 | 2.47 | Skulder koncern/intresse (kortfristiga) |
| 2480-2489, 2491-2499, 2600-2799, 2800-2899 | 7369 | 2.48 | Övriga skulder (kortfristiga) |
| 2500-2599 | 7368 | 2.49 | Skatteskulder |
| 2900-2999 | 7370 | 2.50 | Upplupna kostnader |
---
<a id="bas-income"></a>
## 7. BAS-to-SRU Mapping: Income Statement
**CRITICAL: BAS 5000-6999 ALL map to SRU 7513.** This is the single most common mapping error.
| BAS accounts | → SRU | INK2R row | Description |
|---|---|---|---|
| 3000-3799 | 7410 | 3.1 | Nettoomsättning |
| 3800-3899 | 7412 | 3.3 | Aktiverat arbete |
| 3900-3999 | 7413 | 3.4 | Övriga rörelseintäkter |
| 4900-4999 | 7411 | 3.2 | Förändring av lager |
| 4000-4499 | 7511 | 3.5 | Råvaror och förnödenheter |
| 4600-4699 | 7512 | 3.6 | Handelsvaror |
| **5000-6999** | **7513** | 3.7 | **Övriga externa kostnader (ALL accounts in range)** |
| 7000-7699 | 7514 | 3.8 | Personalkostnader |
| 7700-7799 | 7516 | 3.10 | Nedskrivningar omsättningstillgångar |
| 7800-7899 | 7515 | 3.9 | Avskrivningar |
| 7900-7999 | 7517 | 3.11 | Övriga rörelsekostnader |
| 8000-8099 | 7414 | 3.12 | Resultat koncernföretag |
| 8100-8199 | 7415 | 3.13 | Resultat intresseföretag |
| 8200-8269 | 7423 | 3.14 | Resultat övriga ägarintresse |
| 8270-8299 | 7416 | 3.15 | Övriga finansiella anläggningstillgångar |
| 8300-8399 | 7417 | 3.16 | Ränteintäkter |
| 8400-8499 | 7522 | 3.18 | Räntekostnader |
| 8500-8599 (nedskrivn.) | 7521 | 3.17 | Nedskrivningar finansiella |
| 8810 | 7524 | 3.19 | Lämnade koncernbidrag |
| 8820 | 7419 | 3.20 | Mottagna koncernbidrag |
| 8830 | 7420 | 3.21 | Återföring periodiseringsfond |
| 8840 | 7525 | 3.22 | Avsättning periodiseringsfond |
| 8850 | 7421 | 3.23 | Förändring överavskrivningar |
| 8860-8899 | 7422 | 3.24 | Övriga bokslutsdispositioner |
| 8900-8989 | 7528 | 3.25 | Skatt |
| 8999 (positive balance) | 7450 | 3.26 | Vinst |
| 8999 (negative balance) | 7550 | 3.27 | Förlust |
---
<a id="signs"></a>
## 8. Sign conventions for INK2R
The income statement rows on INK2R use a sign system tied to the physical form:
- **Revenue rows** (7410-7422): Report as the natural sign from accounting. Revenue positive, costs negative.
- **Cost rows** (7511-7528): Report as the natural sign. Costs are negative values.
- **7450 (vinst)**: Pre-printed as positive. Supply positive value if profit.
- **7550 (förlust)**: Pre-printed as negative. Supply positive value to indicate a loss; the minus is implicit in the row definition.
When in doubt: supply the signed value as it appears in the trial balance. The form's physical layout handles presentation.
**Balance sheet rows**: All amounts are positive (assets positive, equity/liabilities positive). A negative balance on a liability account indicates an error or special case.
@@ -0,0 +1,114 @@
# 3:12-reglerna (Fåmansbolag Dividend Taxation)
## Legal basis
IL 56 kap (definition of fåmansföretag), IL 57 kap (taxation of qualified holdings)
## Definitions
**Fåmansföretag**: AB where 4 or fewer delägare control >50% of rösterna, counting närståendes holdings as one person (56 kap. 2 §).
**Kvalificerade andelar**: Shares are qualified if the owner or a närstående has been **verksam i betydande omfattning** (actively participating) in the company during the tax year or any of the 5 preceding years (57 kap. 4 §). Shortened to **4 years** from inkomstår 2027.
**Samma eller likartad verksamhet**: Activity in a related company can make shares qualified even if the owner is not active in the specific company (57 kap. 4 § first paragraph 1).
## Tax rates on dividends from qualified shares
| Zone | Tax rate | Determined by |
|------|----------|---------------|
| Within gränsbelopp | **20%** effective (2/3 x 30%) | K10 calculation |
| Above gränsbelopp | **~32-52%** (tjänstebeskattning) | Marginal income tax |
| Above takbelopp (90 IBB) | **30%** flat | Capital income |
Takbelopp for utdelning: 90 x IBB = 7,254,000 kr (2025), 7,506,000 kr (2026)
## Gränsbelopp calculation (through inkomstår 2025)
### Förenklingsregeln
- Schablonbelopp: **2.75 x IBB (föregående år)**
- Inkomstår 2025: 2.75 x 76,200 = **209,550 kr**
- No salary or ownership threshold required
- Can only be used for ONE fåmansbolag per year
- Cannot be combined with huvudregeln
### Huvudregeln
Formula: Omkostnadsbelopp x (SLR + 9%) + Lönebaserat utrymme + Sparat utdelningsutrymme x (SLR + 3%)
For inkomstår 2025:
- Klyvningsränta: SLR 1.96% + 9% = **10.96%**
- Uppräkning sparat utrymme: SLR 1.96% + 3% = **4.96%**
## Löneunderlag (IL 57 kap. 16-19 §§)
Lönebaserat utrymme = **50% of total löneunderlag** (all kontant bruttolön paid by company + qualifying subsidiaries during calendar year preceding assessment year), proportioned by ownership share.
### Lönekravet (through inkomstår 2025)
Owner or närstående must have drawn kontant bruttolön equal to at least the LOWER of:
- 6 IBB + 5% of total company wages
- 9.6 IBB
For inkomstår 2025 (löner 2024): lower of (457,200 + 5% of total wages) and 731,520 kr.
### What counts as kontant bruttolön
- Cash salary only
- NOT: skattepliktiga förmåner, kostnadsersättningar, state-subsidized wages
### 4%-spärren (through inkomstår 2025)
Must own at least **4% of capital** to use lönebaserat utrymme. Närståendes holdings do NOT count toward this threshold.
### 50x cap
Lönebaserat utrymme is capped at **50 times** the owner's or närstående's own kontant ersättning from the company/subsidiaries.
## Sparat utdelningsutrymme
Unused gränsbelopp carries forward, uppräknat with SLR + 3 procentenheter annually. Filing K10 every year, even without dividends, is essential to preserve accumulation.
## Capital gains on qualified shares
Same split logic as dividends:
- Within remaining sparat utdelningsutrymme: 20%
- Excess: tjänstebeskattning up to takbelopp of **100 IBB** (8,060,000 kr for 2025)
- Above takbelopp: 30% flat
The takbelopp aggregates the shareholder's and närståendes combined gains over current + 5 preceding years.
## K10-deklaration (blankett SKV 2110)
Filed as bilaga to **Inkomstdeklaration 1** by May 2 each year.
Contents:
- (A1) Förenklingsregeln calculation
- (A2) Huvudregeln calculation
- (B) Kapitalvinst vid avyttring
- (C) Omräknat omkostnadsbelopp
- (D) Lönebaserat utrymme beräkning
Must be filed every year to preserve sparat utdelningsutrymme, even if no dividend was taken.
## 2026 Reform (effective January 1, 2026)
Major changes replacing the dual system with a unified calculation:
| Component | New rule (inkomstår 2026) |
|-----------|--------------------------|
| Grundbelopp | **4 x IBB (2025) = 322,400 kr** (replaces förenklingsregeln) |
| Lönebaserat utrymme | 50% x (ägarandel x löneunderlag - 8 IBB) |
| Ränta on omkostnadsbelopp | (Belopp - 100,000) x (SLR + 9%) |
| Sparat utdelningsutrymme | Carried forward WITHOUT ränteuppräkning |
| 4%-spärren | **Abolished** |
| Löneuttagskravet | **Abolished** (50x cap remains) |
| Karenstid | Shortened from 5 to **4 years** |
| Närståendebegreppet | Narrowed: siblings and their families excluded |
| Schablonmässigt löneavdrag | **8 IBB** per delägare subtracted from löneunderlag |
The grundbelopp at 4 IBB is significantly higher than the old förenklingsregeln (2.75 IBB), benefiting small companies. The abolished lönekrav means owners of holding companies can access lönebaserat utrymme from subsidiary wages without meeting a salary threshold.
## Common K10 errors
1. Including förmåner (benefits) in kontant bruttolön
2. Using förenklingsregeln in multiple companies simultaneously
3. Failing to file K10 in years without dividend (losing sparat utrymme)
4. Confusing K10 (kvalificerade) with K12 (okvalificerade)
5. Miscalculating löneunderlag when subsidiaries are involved
6. Missing the 4% ownership threshold (through 2025)
7. Not tracking närståendes holdings for aggregation rules
@@ -0,0 +1,59 @@
# Kapitalförsäkring i bolagskontext
## Key rule: AB cannot open ISK
Investeringssparkonto is restricted to fysiska personer. Kapitalförsäkring (KF) is the sole schablonbeskattad sparform available to AB.
## Tax mechanism for Swedish KF held by AB
The AB does **NOT** report or pay avkastningsskatt. For a Swedish KF, the **livförsäkringsföretaget** (insurance company) is the skattskyldige entity under §2 first paragraph, Lag (1990:661) om avkastningsskatt på pensionsmedel (AvPL).
The insurance company calculates and deducts avkastningsskatt internally, reducing the KF's value. The AB's books reflect only insättningar and uttag. Individual transactions within the KF are invisible to the AB's accounting.
## Avkastningsskatt calculation
1. Kapitalunderlag = KF value January 1 + 100% of premiums Jan-Jun + 50% of premiums Jul-Dec
2. Skatteunderlag = Kapitalunderlag x (SLR + 1 procentenhet), minimum **1.25%**
3. Avkastningsskatt = Skatteunderlag x **30%**
For 2026: effective annual tax = (2.55% + 1%) x 30% = **1.065%** of kapitalunderlag.
Properties:
- No tax on realized gains, dividends, or fund switches within KF
- Uttag (withdrawals) are skattefria
- Insättningar are NOT avdragsgilla
## Utländsk KF
For KF issued by non-Swedish insurance company without fast driftställe in Sweden: the **AB itself becomes skattskyldig** for avkastningsskatt (§2 first paragraph 6-7 AvPL). Must be declared in income declaration. The avkastningsskatt is not avdragsgill.
## BAS accounts
| Account | Use |
|---------|-----|
| 1385 | Värde av kapitalförsäkring (financial asset) |
| 8220 | Resultat vid försäljning (vinst/förlust at uttag) |
Insättning: Debit 1385 / Credit 1930 (bank)
Uttag: Debit 1930 / Credit 1385 (+ 8220 for gain/loss)
## When KF beats direktägande
At 2026 SLR, breakeven annual return is approximately **5.2%**. Above this, KF's flat ~1.065% tax beats 20.6% bolagsskatt on realized gains in a depå.
Additional KF advantages:
- No per-transaction bookkeeping
- Tax-free rebalancing
- Administrative simplicity
## When KF is WRONG
- **Näringsbetingade andelar** (IL 24 kap. 32-34 §): dividends and capital gains are entirely skattefria when held directly. KF would impose unnecessary tax.
- Never use KF for unquoted holdings
- Never use KF for listed shares where AB holds ≥10% of votes for at least one year
- KF losses are **not avdragsgilla**, making it disadvantageous in declining markets
- Placing kvalificerade andelar in KF is actively challenged by Skatteverket as skatteflykt
## Proposed skattefri grundnivå
A lagrådsremiss proposes a tax-free base level for ISK/KF savings. If enacted, this would affect the breakeven calculation. Monitor legislative developments.
@@ -0,0 +1,61 @@
# Koncernbidrag (Group Contributions)
## Legal basis
IL 35 kap
## Core function
Koncernbidrag transfers taxable income between group companies. Avdragsgillt for the giver, skattepliktigt for the receiver. Enables the koncern to pay tax only on net aggregate profit.
## Requirements (35 kap. 2-3 §§)
All of these must be met:
1. Moderföretaget owns **>90% of andelarna** (capital shares, not votes)
2. Ownership has existed for the **entire tax year** of both giver and receiver (or since subsidiary began any business)
3. Both are qualifying Swedish entities (AB, ekonomisk förening, sparbank, etc.)
4. Neither is a privatbostadsföretag or investmentföretag
5. Both **openly report** (öppet redovisa) the bidrag in income declarations filed at the same deadline
6. An actual **value transfer** must occur no later than the giver's declaration filing date
## Directions
- Parent to subsidiary (nedåtriktat) -- most common
- Subsidiary to parent (uppåtriktat)
- Between systerbolag (sidledes) -- requires common parent owning >90% of both
## Öppna vs dolda koncernbidrag
**Öppna**: Explicitly reported, regulated by IL 35 kap. Booked as bokslutsdisposition.
**Dolda**: Through mispriced intra-group transactions, below-market sales, debt forgiveness. Fall outside IL 35 kap. Trigger uttagsbeskattning and transfer pricing scrutiny under 14 kap. 19 § IL (korrigeringsregeln). Avoid.
## BAS accounts
| Account | Name |
|---------|------|
| 8820 | Mottagna/Erhållna koncernbidrag (receiver) |
| 8830 | Lämnade koncernbidrag (giver) |
| 2860 | Kortfristig skuld till koncernföretag (giver's balance sheet) |
| 1660 | Kortfristig fordran på koncernföretag (receiver's balance sheet) |
**CORRECTION**: Account 8840 is NOT for koncernbidrag. It is "Lämnade gottgörelser." The old accounts 8893/8894 were replaced by 8820/8830 in BAS 2009.
## Underskottsspärr (40 kap. 18-19 §§)
After an ownership change (ägarförändring), the acquired company's old tax losses CANNOT be offset against received koncernbidrag during a spärrtid. This prevents abuse of loss-making shell companies.
## Planning considerations
- Instant resultatutjämning across the group. No 6-year deferral like periodiseringsfond.
- Requires formal bolagsstämmobeslut
- No koncernbidragsrätt in the year a subsidiary is acquired (if it had prior operations) or sold. The "hela beskattningsåret" rule is strict.
- Koncernbidrag REDUCES the giver's EBITDA for ränteavdragsbegränsningar (negative interaction)
- The giver must have sufficient free equity (fritt eget kapital) per ABL to make the transfer without triggering "olovlig värdeöverföring"
## Example
Moder AB: taxable income 5 MSEK. Dotter AB: deficit 3 MSEK.
- Moder gives 3 MSEK koncernbidrag
- Moder: Debit 8830 / Credit 2860. Taxable income: 2 MSEK -> tax 412,000 kr
- Dotter: Debit 1660 / Credit 8820. Taxable income: 0
- Without koncernbidrag, group pays 1,030,000 kr. Saving: 618,000 kr.
@@ -0,0 +1,69 @@
# Överavskrivningar (Accelerated Depreciation)
## Legal basis
IL 18 kap (inventarier), IL 19 kap (byggnader)
## Core concept
When skattemässig avskrivning exceeds bokföringsmässig avskrivning (planned depreciation per ÅRL/K2/K3), the difference is an överavskrivning, booked as obeskattad reserv.
## Two methods for inventarier (räkenskapsenlig avskrivning)
### 30-regeln / Restvärdemetoden (IL 18 kap. 13 §)
- Tax book value of all pooled inventarier can be written down to **70%** of avskrivningsunderlag
- Avskrivningsunderlag = prior-year tax residual value + new acquisitions - sales proceeds of items acquired in prior years
- Yields up to 30% deduction annually on declining balance
- Applied on the entire pool collectively
### 20-regeln / Kompletteringsregeln (IL 18 kap. 17 §)
- 20% straight-line depreciation from acquisition year
- Full depreciation in 5 years
- Applied per individual asset, then summed
- Each asset's remaining tax value = max(anskaffningsvärde x (1 - 0.20 x antal år), 0)
### Choosing between methods
- Companies may switch between the two methods each year
- Choose whichever yields the **lowest tax book value** (largest total deduction)
- 20-regeln typically wins for older assets; 30-regeln for newer large acquisitions
## Direktavdrag (IL 18 kap. 4 §)
Assets costing less than **half a prisbasbelopp** or with expected useful life ≤ 3 years:
- 2025: < 29,400 kr
- 2026: < 29,600 kr
## BAS accounts
| Account | Name |
|---------|------|
| 2150 | Ackumulerade överavskrivningar (group) |
| 2153 | Ack. överavskrivningar, maskiner och inventarier |
| 8850 | Förändring av överavskrivningar (group) |
| 8853 | Förändring av överavskrivningar, maskiner och inventarier |
| 7831-7835 | Avskrivningar enligt plan (per asset category) |
| 1220/1229 | Inventarier: anskaffningsvärde / ack. avskrivningar |
## Obeskattade reserver split
Obeskattade reserver from överavskrivningar consist of:
- ~79.4% eget kapital
- ~20.6% latent skatteskuld (deferred tax liability)
Under K3 consolidated accounts, these are split explicitly. Under K2, they appear as gross line item.
## Interaction with periodiseringsfond
Both create obeskattade reserver and can be used simultaneously. Optimal year-end sequence:
1. Maximize överavskrivningar (limited by asset base)
2. Calculate remaining taxable income
3. Set aside up to 25% to periodiseringsfond
## Critical pitfall
Skatteverket ställningstagande 2024-10-15, dnr 8-3120349: If a company systematically (more than one year) books depreciation exceeding the skattemässigt maximum, it **loses the right to räkenskapsenlig avskrivning entirely** and is forced onto restvärdesavskrivning (25% of residual value). A one-time error does not forfeit the right.
## Planning considerations
- Most valuable for capital-intensive businesses with large inventarier bases
- Combined with periodiseringsfond, an asset-heavy AB can defer the vast majority of its tax bill
- Monitor the gap between plan and tax depreciation to avoid unintended over-depreciation
- In a sale scenario, överavskrivningar are reversed (creating taxable income), so plan exit timing carefully
@@ -0,0 +1,60 @@
# Periodiseringsfond (Tax Allocation Reserve)
## Legal basis
IL 30 kap (Inkomstskattelagen 1999:1229)
## Core rules for AB
- Max avsättning: **25% of skattemässigt överskott** (taxable income before the deduction)
- Each year's fund is tracked separately
- Up to **6 parallel funds** at any time
- Each fund must be reversed no later than the **6th tax year** after the avsättning year (30 kap. 7 §)
- Reversal follows **FIFO** (oldest first)
- The avsättning **must be booked in räkenskaperna** as obeskattad reserv (formellt samband, 30 kap. 3 §). Without the booking, the deduction is invalid for AB.
## Schablonintäkt (30 kap. 6a §)
AB must declare a schablonintäkt annually:
- Formula: SLR (per Nov 30 prior year) x total periodiseringsfonder at tax year start
- Minimum SLR floor: **0.5%**
- Reported only on **INK2S punkt 4.6a** (not booked in accounting)
- Annual cost of the tax credit at SLR 2.55%: approximately 2.55% x 20.6% = **0.53%** of fund balance
## Transition uppräkning
Funds set aside when bolagsskatt was 22% (räkenskapsår starting 2018 or earlier) trigger a **6% uppräkning** on the reversed amount, preventing arbitrage from the rate change to 20.6%.
## BAS accounts
| Account | Name | Use |
|---------|------|-----|
| 2110-2139 | Periodiseringsfonder (year-specific) | Balance sheet, obeskattade reserver |
| 8810 | Förändring av periodiseringsfond (group) | Income statement, bokslutsdispositioner |
| 8811 | Avsättning till periodiseringsfond | Debit 8811 / Credit 21XX |
| 8819 | Återföring från periodiseringsfond | Debit 21XX / Credit 8819 |
**CRITICAL**: Account 2150 is NOT periodiseringsfond. It is ackumulerade överavskrivningar. This is one of the most common bookkeeping errors.
## Planning considerations
- **Loss absorption**: When reversed against a loss year, the originally deferred tax becomes a permanent saving, not merely a deferral. This makes periodiseringsfond especially valuable for cyclical businesses.
- **Cash flow**: The deferred tax remains in the company as working capital. If the company earns a return exceeding the SLR before tax on the retained cash, the fund is net beneficial.
- **Year-end sequence**: Calculate periodiseringsfond AFTER maximizing överavskrivningar, since both reduce taxable income.
- **Interaction with EBITDA**: Avsättning to periodiseringsfond INCREASES the skattemässigt EBITDA (avdragsunderlag) for ränteavdragsbegränsningar purposes (IL 24 kap. 25 §). This is beneficial when interest deductions are constrained.
## Common pitfalls
1. Forgetting mandatory 6-year reversal (Skatteverket corrects automatically, plus skattetillägg risk)
2. Neglecting schablonintäkt in the deklaration
3. Failing to book in räkenskaperna (invalidates the deduction for AB)
4. Consuming the cash without reserving for future tax liability upon reversal
5. Not tracking individual funds separately (each has its own 6-year clock)
## Example
AB with 1,000,000 kr taxable income in 2025:
- Max avsättning: 25% x 1,000,000 = 250,000 kr
- Booking: Debit 8811 / Credit 2125
- Taxable income falls to 750,000 kr
- Tax saved in current year: 51,500 kr
- 2026 schablonintäkt: 250,000 x 2.55% = 6,375 kr -> extra tax 1,313 kr
@@ -0,0 +1,81 @@
# Ränteavdragsbegränsningar (Interest Deduction Limitations)
## Legal basis
IL 24 kap. 21-29 §§ (generella regler), IL 24 kap. 16-20 §§ (riktade regler)
Implements the EU Anti-Tax Avoidance Directive (ATAD). Effective January 1, 2019.
## Two alternative ceilings
### EBITDA-regeln (24 kap. 24 § first paragraph)
Deduction of negativt räntenetto up to **30% of skattemässigt EBITDA** (avdragsunderlag).
Avdragsunderlag calculation (24 kap. 25 §):
- Start: skattemässigt resultat
- ADD: värdeminskningsavdrag, negativt räntenetto, avsättning till periodiseringsfond, certain non-deductible items
- SUBTRACT: återförd periodiseringsfond, tax-free dividends on näringsbetingade andelar, positivt räntenetto
Key interaction: periodiseringsfond avsättning INCREASES avdragsunderlag, which is beneficial.
Key interaction: koncernbidrag (lämnat) DECREASES avdragsunderlag, which is harmful.
### Förenklingsregeln (24 kap. 24 § second paragraph)
Deduction of negativt räntenetto up to **5 MSEK**.
Critical constraints:
- Applies per **intressegemenskap**, not per bolag
- If ANY company in the group uses this, the 5 MSEK cap covers the ENTIRE group
- Non-deductible amounts under förenklingsregeln **cannot be carried forward** (permanently lost)
- Cannot be combined with EBITDA-regeln within the same intressegemenskap
## Carry-forward (24 kap. 26 §)
Under EBITDA-regeln only: excess negativt räntenetto becomes **kvarstående negativt räntenetto**, carried forward for **6 years**. Lost upon ägarförändring.
## Definition of ränteutgifter (24 kap. 2 §)
Broad definition includes:
- Traditional ränta
- Financing costs, commitment fees, arrangement fees
- Interest component of financial leasing
- Hedged currency losses on loans
- Capitalized interest
## Riktade regler for koncerninterna lån (24 kap. 16-20 §§)
Additional restrictions BEFORE the generella regler apply:
- Ränteavdrag denied if arrangement "uteslutande eller så gott som uteslutande" (90-100%) arose to generate väsentlig skatteförmån
- Förvärvsregeln (24 kap. 19 §): underlying acquisition must be "väsentligen affärsmässigt motiverat"
- Following HFD 2021 ref. 68 and HFD 2024, partly incompatible with EU law
- Prop. 2025/26:20 adapts rules from January 1, 2026: denial limited to "rent konstlade upplägg" for EES-based lenders
## BAS accounts
| Account | Use |
|---------|-----|
| 8400/8410 | Räntekostnader (general) |
| 8310/8311 | Ränteintäkter |
| Separate sub-accounts | Koncerninterna vs externa räntor (essential for N9) |
## N9-blankett
Filed as bilaga to INK2 when negativt räntenetto exists. Reports:
- Ränteintäkter and ränteutgifter
- Negativt/positivt räntenetto
- Avdragsunderlag (EBITDA)
- Avdragsutrymme
- Kvarstående negativt räntenetto from prior years
- Choice of EBITDA-regeln vs förenklingsregeln
## Practical example
AB Alfa: skattemässigt resultat 10 MSEK, ränteintäkter 1 MSEK, ränteutgifter 6 MSEK, avskrivningar 3 MSEK, periodiseringsfond avsättning 2 MSEK, återföring 1 MSEK.
- Negativt räntenetto: 6 - 1 = 5 MSEK
- EBITDA = 10 + 5 + 3 + 2 - 1 = 19 MSEK
- Avdragsutrymme = 30% x 19 = 5.7 MSEK > 5 MSEK -> full deduction
## Circular dependency warning
Adjusting koncernbidrag after the EBITDA calculation creates circular dependencies. Koncernbidrag changes skattemässigt resultat, which changes EBITDA, which changes avdragsutrymme, which may change the optimal koncernbidrag. Iterative recalculation required.
@@ -0,0 +1,102 @@
# Strategy, Interactions, and Optimization
## Year-end decision sequence
Follow this order for optimal tax planning:
1. **Överavskrivningar**: Calculate max under 30-regeln and 20-regeln. Limited by asset base, so do this first.
2. **Periodiseringsfond**: Calculate remaining taxable income, set aside up to 25%. Remember any återföring in the same year increases the base.
3. **Koncernbidrag**: Equalize remaining profits and losses across the group. Requires bolagsstämmobeslut.
4. **Löneuttag**: Verify owner salary meets lönekrav for 3:12 purposes (through inkomstår 2025). December bonuses are common but draw Skatteverket attention.
5. **Utdelning**: Plan within available gränsbelopp.
6. **Ränteavdragsbegränsningar**: Verify compliance. Periodiseringsfond avsättning increases EBITDA (good), koncernbidrag decreases it (bad for giver).
## Lön vs utdelning: the fundamental trade-off
### Effective combined tax burdens
| Extraction method | Combined burden (company + owner) |
|-------------------|-----------------------------------|
| Utdelning within gränsbelopp | ~36.5% (1 - 0.794 x 0.80) |
| Lön below brytpunkt (no statlig skatt) | ~47-52% (incl. arbetsgivaravgifter 31.42%) |
| Lön above brytpunkt | ~58-63% |
| Utdelning above gränsbelopp (tjänstebeskattning) | ~52-58% |
### Optimal strategy for most fåmansbolagsägare
1. Take lön up to pension ceiling: 8.07 x IBB = ~673,000 kr (2026). This secures SGI and pensionsrätt.
2. Ensure lönekravet is met for 3:12 (through inkomstår 2025)
3. Distribute remaining profits as utdelning within gränsbelopp
4. Excess profits: retain in company, invest via kapitalförsäkring, or use periodiseringsfond to defer
### When lön is preferable despite higher tax
- Building SGI (sjukpenninggrundande inkomst) for social insurance benefits
- Maximizing allmän pension
- When the company has no accumulated gränsbelopp
- When löneunderlag needs to be built (owner's lön feeds into the calculation)
## Interaction matrix
| Tool A | Affects Tool B | How |
|--------|---------------|-----|
| Periodiseringsfond avsättning | EBITDA | Increases avdragsunderlag (beneficial) |
| Periodiseringsfond återföring | EBITDA | Decreases avdragsunderlag (harmful) |
| Koncernbidrag (lämnat) | EBITDA | Decreases avdragsunderlag (harmful for giver) |
| Koncernbidrag (mottaget) | EBITDA | Increases avdragsunderlag (beneficial for receiver) |
| Löneuttag | 3:12 gränsbelopp | Meets lönekrav AND feeds löneunderlag |
| Löneuttag | Company cost | Avdragsgill, reduces taxable income |
| Löneuttag | Arbetsgivaravgifter | 31.42% additional company cost |
| Överavskrivningar | Periodiseringsfond | Reduces taxable income, lowering max avsättning |
| Överavskrivningar | EBITDA | Added back, increases avdragsunderlag |
## Obeskattade reserver: balance sheet effects
Both periodiseringsfonder and ackumulerade överavskrivningar appear as obeskattade reserver:
- ~79.4% equity component
- ~20.6% latent tax component
For bank credit assessments (soliditet), these components are typically split. In K3 consolidated accounts, the split is explicit (uppskjuten skatteskuld and eget kapital).
## Skatteverket audit triggers
Common patterns that invite scrutiny:
1. Discrepancies between momsdeklaration and INK2 turnover
2. Unusually large avdrag for representation, leasing, consulting fees
3. Owner salary patterns that appear primarily tax-motivated (e.g., large December bonuses to meet lönevillkoret)
4. Placing kvalificerade andelar in kapitalförsäkring
5. "Samma eller likartad verksamhet" when activity is split between related companies
6. Rapid corporate restructuring before significant transactions
7. Koncernbidrag patterns lacking business substance
8. Transfer pricing in intra-group transactions without documentation
## Skatteflyktslagen (Lag 1995:575)
Applies when four cumulative conditions are met:
1. Väsentlig skatteförmån
2. Taxpayer participation (directly or indirectly)
3. Tax benefit was the predominant reason
4. Taxation based on the arrangement would contravene the law's purpose
## Documentation requirements
Essential documentation that must be in order:
- Periodiseringsfond: corresponding obeskattade reserver in räkenskaperna
- Koncernbidrag: formal bolagsstämmobeslut
- K10: filed annually (even without dividend)
- Transfer pricing: arm's-length documentation for intra-group transactions
- Löneunderlag: lönespecifikationer supporting the K10 calculation
Missing any of these creates both skattetillägg risk and potential loss of deduction rights.
## Multi-year planning horizon
The most valuable long-term strategy for a fåmansbolagsägare:
1. Maximize sparat utdelningsutrymme through consistent K10 filing
2. Optimize löneunderlag annually (company wages directly feed gränsbelopp growth)
3. Compound growth of the 20%-taxed dividend space reduces lifetime tax burden far more than any single-year maneuver
4. Use periodiseringsfond strategically against anticipated loss years
5. Time major asset acquisitions to maximize överavskrivningar in the acquisition year
## Proposed bolagsskatt reduction
A proposal to reduce bolagsskatt to 20% has been remitted (May 2025) but is not yet enacted. If passed, it would trigger transition effects across periodiseringsfonder (potential uppräkning) and affect the relative attractiveness of all tools. Monitor.
+21 -4
View File
@@ -40,6 +40,7 @@ const schema = z.object({
customer_id: z.string().min(1, 'Välj en kund'),
invoice_date: z.string().min(1, 'Fakturadatum krävs'),
due_date: z.string().min(1, 'Förfallodatum krävs'),
delivery_date: z.string().optional(),
currency: z.enum(['SEK', 'EUR', 'USD', 'GBP', 'NOK', 'DKK']),
document_type: z.enum(['invoice', 'proforma', 'delivery_note']),
your_reference: z.string().optional(),
@@ -678,6 +679,13 @@ export default function NewInvoicePage() {
<Input type="date" {...register('due_date')} />
</div>
{watchDocumentType === 'invoice' && (
<div className="space-y-2">
<Label>Leveransdatum</Label>
<Input type="date" {...register('delivery_date')} placeholder="Om det skiljer sig från fakturadatum" />
</div>
)}
<Separator />
<div className="space-y-2">
@@ -723,12 +731,21 @@ export default function NewInvoicePage() {
<span>{formatCurrency(subtotal, watchCurrency)}</span>
</div>
{Array.from(vatByRate.entries())
.filter(([, group]) => group.vat > 0)
.sort(([a], [b]) => b - a)
.map(([rate, group]) => (
<div key={rate} className="flex justify-between">
<span className="text-muted-foreground">Moms {rate}%</span>
<span>{formatCurrency(group.vat, watchCurrency)}</span>
<div key={rate}>
{vatByRate.size > 1 && (
<div className="flex justify-between">
<span className="text-muted-foreground">Netto {rate}%</span>
<span>{formatCurrency(group.base, watchCurrency)}</span>
</div>
)}
{group.vat > 0 && (
<div className="flex justify-between">
<span className="text-muted-foreground">Moms {rate}%</span>
<span>{formatCurrency(group.vat, watchCurrency)}</span>
</div>
)}
</div>
))}
{vatByRate.size === 0 && (
+2 -1
View File
@@ -24,7 +24,7 @@ export async function POST(request: Request) {
const companyId = await requireCompanyId(supabase, user.id)
const body = await request.json()
const { customer_id, invoice_date, due_date, currency, items, your_reference, our_reference, notes, document_type } = body
const { customer_id, invoice_date, due_date, delivery_date, currency, items, your_reference, our_reference, notes, document_type } = body
if (!customer_id || !items || items.length === 0) {
return NextResponse.json({ error: 'Kunduppgifter och rader krävs' }, { status: 400 })
@@ -93,6 +93,7 @@ export async function POST(request: Request) {
invoice_number: 'FÖRHANDSGRANSKNING',
invoice_date: invoice_date || new Date().toISOString().split('T')[0],
due_date: due_date || new Date().toISOString().split('T')[0],
delivery_date: delivery_date || null,
status: 'draft',
currency: currency || 'SEK',
exchange_rate: null,
+2
View File
@@ -188,6 +188,7 @@ export async function POST(request: Request) {
invoice_number: invoiceNumber,
invoice_date: invoiceInput.invoice_date,
due_date: invoiceInput.due_date,
delivery_date: invoiceInput.delivery_date ?? null,
currency: invoiceInput.currency,
exchange_rate: exchangeRate,
exchange_rate_date: exchangeRateDate,
@@ -312,6 +313,7 @@ async function createCreditNote(
invoice_number: creditNoteNumber,
invoice_date: new Date().toISOString().split('T')[0],
due_date: new Date().toISOString().split('T')[0],
delivery_date: originalInvoice.delivery_date ?? null,
currency: originalInvoice.currency,
exchange_rate: originalInvoice.exchange_rate,
exchange_rate_date: originalInvoice.exchange_rate_date,
+32 -9
View File
@@ -2,11 +2,11 @@ import { createClient } from '@/lib/supabase/server'
import { NextResponse } from 'next/server'
import { generateINK2Declaration } from '@/lib/reports/ink2/ink2-engine'
import {
generateSRUFile,
sruFileToString,
getSRUFilename,
generateSRUSubmission,
getZipFilename,
} from '@/lib/reports/ink2/sru-generator'
import { requireCompanyId } from '@/lib/company/context'
import JSZip from 'jszip'
/**
* GET /api/reports/ink2
@@ -15,7 +15,7 @@ import { requireCompanyId } from '@/lib/company/context'
*
* Query parameters:
* - period_id: Fiscal period ID (required)
* - format: 'json' (default) or 'sru' for SRU file download
* - format: 'json' (default) or 'sru' for SRU file download (ZIP with INFO.SRU + BLANKETTER.SRU)
*/
export async function GET(request: Request) {
const supabase = await createClient()
@@ -42,14 +42,24 @@ export async function GET(request: Request) {
const declaration = await generateINK2Declaration(supabase, companyId, periodId)
if (format === 'sru') {
const sruFile = generateSRUFile(declaration)
const sruContent = sruFileToString(sruFile)
const filename = getSRUFilename(declaration)
const submission = generateSRUSubmission(declaration)
return new NextResponse(sruContent, {
// Encode both files as ISO 8859-1 (Latin-1) — required by Skatteverket
const infoBytes = encodeISO88591(submission.infoSru)
const blanketterBytes = encodeISO88591(submission.blanketterSru)
// Create ZIP with both files
const zip = new JSZip()
zip.file('INFO.SRU', infoBytes)
zip.file('BLANKETTER.SRU', blanketterBytes)
const zipArrayBuffer = await zip.generateAsync({ type: 'arraybuffer' })
const filename = getZipFilename(declaration)
return new NextResponse(zipArrayBuffer, {
status: 200,
headers: {
'Content-Type': 'text/plain; charset=utf-8',
'Content-Type': 'application/zip',
'Content-Disposition': `attachment; filename="${filename}"`,
},
})
@@ -64,3 +74,16 @@ export async function GET(request: Request) {
)
}
}
/**
* Encode a string as ISO 8859-1 (Latin-1) bytes.
* Characters outside the Latin-1 range are replaced with '?'.
*/
function encodeISO88591(str: string): Uint8Array {
const bytes = new Uint8Array(str.length)
for (let i = 0; i < str.length; i++) {
const code = str.charCodeAt(i)
bytes[i] = code <= 0xFF ? code : 0x3F // '?' for unmappable chars
}
return bytes
}
+117 -41
View File
@@ -7,17 +7,18 @@ import { Badge } from '@/components/ui/badge'
import { Download, AlertCircle, Info } from 'lucide-react'
import { AccountNumber } from '@/components/ui/account-number'
import { formatCurrency } from '@/lib/utils'
import type { INK2Declaration, INK2SRUCode } from '@/lib/reports/ink2/types'
import type { INK2Declaration, INK2RSRUCode } from '@/lib/reports/ink2/types'
import {
INK2_RUTA_LABELS,
INK2_ASSET_CODES,
INK2_EQUITY_LIABILITY_CODES,
INK2_INCOME_STATEMENT_CODES,
INK2R_RUTA_LABELS,
INK2R_ASSET_CODES,
INK2R_EQUITY_LIABILITY_CODES,
INK2R_INCOME_CODES,
} from '@/lib/reports/ink2/types'
export function INK2DeclarationView({ periodId }: { periodId: string }) {
const [data, setData] = useState<INK2Declaration | null>(null)
const [loading, setLoading] = useState(false)
const [downloading, setDownloading] = useState(false)
const [error, setError] = useState<string | null>(null)
const fetchDeclaration = async () => {
@@ -38,8 +39,24 @@ export function INK2DeclarationView({ periodId }: { periodId: string }) {
}
}
const downloadSRU = () => {
window.open(`/api/reports/ink2?period_id=${periodId}&format=sru`, '_blank')
const downloadSRU = async () => {
setDownloading(true)
try {
const res = await fetch(`/api/reports/ink2?period_id=${periodId}&format=sru`)
if (!res.ok) throw new Error('Download failed')
const blob = await res.blob()
const filename = res.headers.get('Content-Disposition')?.match(/filename="(.+)"/)?.[1] || 'INK2_SRU.zip'
const url = URL.createObjectURL(blob)
const a = document.createElement('a')
a.href = url
a.download = filename
a.click()
URL.revokeObjectURL(url)
} catch {
setError('Kunde inte ladda ner SRU-filer')
} finally {
setDownloading(false)
}
}
return (
@@ -52,20 +69,34 @@ export function INK2DeclarationView({ periodId }: { periodId: string }) {
<CardContent>
<div className="flex items-start gap-2 mb-4 p-3 bg-primary/10 rounded-md">
<Info className="h-4 w-4 text-primary mt-0.5 shrink-0" />
<p className="text-sm text-primary">
INK2 visar det bokföringsmässiga resultatet baserat din bokföring.
Skattemässiga justeringar (ej avdragsgilla kostnader, periodiseringsfonder m.m.)
hanteras av din revisor/redovisningskonsult.
</p>
<div className="text-sm text-primary space-y-1">
<p>
INK2 visar det bokföringsmässiga resultatet baserat din bokföring.
Skattemässiga justeringar (ej avdragsgilla kostnader, periodiseringsfonder m.m.)
hanteras av din revisor/redovisningskonsult.
</p>
<p>
SRU-filen laddas ner som en ZIP med INFO.SRU och BLANKETTER.SRU.
Ladda upp båda filerna till{' '}
<a
href="https://www1.skatteverket.se/fv/fv_web/start.do"
target="_blank"
rel="noopener noreferrer"
className="underline"
>
Skatteverkets filöverföringstjänst
</a>.
</p>
</div>
</div>
<div className="flex gap-2">
<Button onClick={fetchDeclaration} disabled={loading}>
{loading ? 'Laddar...' : 'Hämta INK2'}
</Button>
{data && (
<Button variant="outline" onClick={downloadSRU}>
<Button variant="outline" onClick={downloadSRU} disabled={downloading}>
<Download className="h-4 w-4 mr-2" />
Ladda ner SRU-fil
{downloading ? 'Laddar ner...' : 'Ladda ner SRU-filer'}
</Button>
)}
</div>
@@ -126,12 +157,12 @@ export function INK2DeclarationView({ periodId }: { periodId: string }) {
<CardContent>
<table className="w-full text-sm">
<tbody>
{INK2_ASSET_CODES.map((code) => (
{INK2R_ASSET_CODES.map((code) => (
<INK2DeclarationRow
key={code}
code={code}
label={INK2_RUTA_LABELS[code]}
amount={data.rutor[code]}
label={INK2R_RUTA_LABELS[code]}
amount={data.ink2r[code]}
accounts={data.breakdown[code]?.accounts || []}
/>
))}
@@ -156,12 +187,12 @@ export function INK2DeclarationView({ periodId }: { periodId: string }) {
<CardContent>
<table className="w-full text-sm">
<tbody>
{INK2_EQUITY_LIABILITY_CODES.map((code) => (
{INK2R_EQUITY_LIABILITY_CODES.map((code) => (
<INK2DeclarationRow
key={code}
code={code}
label={INK2_RUTA_LABELS[code]}
amount={data.rutor[code]}
label={INK2R_RUTA_LABELS[code]}
amount={data.ink2r[code]}
accounts={data.breakdown[code]?.accounts || []}
/>
))}
@@ -186,19 +217,15 @@ export function INK2DeclarationView({ periodId }: { periodId: string }) {
<CardContent>
<table className="w-full text-sm">
<tbody>
{INK2_INCOME_STATEMENT_CODES.map((code) => {
const isExpense = code !== '7310' && code !== '7370' && code !== '7380'
return (
<INK2DeclarationRow
key={code}
code={code}
label={INK2_RUTA_LABELS[code]}
amount={data.rutor[code]}
accounts={data.breakdown[code]?.accounts || []}
isExpense={isExpense}
/>
)
})}
{INK2R_INCOME_CODES.map((code) => (
<INK2DeclarationRow
key={code}
code={code}
label={INK2R_RUTA_LABELS[code]}
amount={data.ink2r[code]}
accounts={data.breakdown[code]?.accounts || []}
/>
))}
</tbody>
<tfoot>
<tr className="border-t font-medium">
@@ -208,7 +235,7 @@ export function INK2DeclarationView({ periodId }: { periodId: string }) {
</td>
</tr>
<tr className="border-t-2 font-semibold">
<td className="py-2">Resultat efter finansiella poster</td>
<td className="py-2">Årets resultat</td>
<td className={`py-2 text-right ${data.totals.resultAfterFinancial >= 0 ? 'text-success' : 'text-destructive'}`}>
{formatCurrency(data.totals.resultAfterFinancial)}
</td>
@@ -217,6 +244,57 @@ export function INK2DeclarationView({ periodId }: { periodId: string }) {
</table>
</CardContent>
</Card>
{/* INK2S summary */}
<Card>
<CardHeader>
<CardTitle className="text-lg">INK2S Skattemässiga justeringar</CardTitle>
</CardHeader>
<CardContent>
<div className="flex items-start gap-2 mb-4 p-3 bg-primary/10 rounded-md">
<Info className="h-4 w-4 text-primary mt-0.5 shrink-0" />
<p className="text-sm text-primary">
Grundläggande justeringar beräknas automatiskt. Manuella justeringar
(periodiseringsfonder, koncernbidrag m.m.) hanteras av din redovisningskonsult.
</p>
</div>
<table className="w-full text-sm">
<tbody>
<tr className="border-b">
<td className="py-2">
<span className="font-mono text-xs bg-muted px-1 rounded mr-2">4.1</span>
Årets resultat (vinst)
</td>
<td className="py-2 text-right">{formatCurrency(data.ink2s['7650'])}</td>
</tr>
<tr className="border-b">
<td className="py-2">
<span className="font-mono text-xs bg-muted px-1 rounded mr-2">4.2</span>
Årets resultat (förlust)
</td>
<td className="py-2 text-right">{formatCurrency(data.ink2s['7750'])}</td>
</tr>
<tr className="border-b">
<td className="py-2">
<span className="font-mono text-xs bg-muted px-1 rounded mr-2">4.3a</span>
Skatt årets resultat (ej avdragsgill)
</td>
<td className="py-2 text-right">{formatCurrency(data.ink2s['7651'])}</td>
</tr>
</tbody>
<tfoot>
<tr className="border-t-2 font-semibold">
<td className="py-2">
{data.ink2s['8020'] > 0 ? 'Överskott (punkt 1.1)' : 'Underskott (punkt 1.2)'}
</td>
<td className={`py-2 text-right ${data.ink2s['8020'] > 0 ? 'text-success' : 'text-destructive'}`}>
{formatCurrency(data.ink2s['8020'] > 0 ? data.ink2s['8020'] : data.ink2s['8021'])}
</td>
</tr>
</tfoot>
</table>
</CardContent>
</Card>
</>
)}
@@ -236,13 +314,11 @@ function INK2DeclarationRow({
label,
amount,
accounts,
isExpense,
}: {
code: INK2SRUCode
code: INK2RSRUCode
label: string
amount: number
accounts: Array<{ accountNumber: string; accountName: string; amount: number }>
isExpense?: boolean
}) {
const [expanded, setExpanded] = useState(false)
@@ -263,8 +339,8 @@ function INK2DeclarationRow({
</span>
)}
</td>
<td className="py-2 text-right">
{isExpense && amount > 0 ? '-' : ''}{formatCurrency(Math.abs(amount))}
<td className="py-2 text-right tabular-nums">
{formatCurrency(amount)}
</td>
</tr>
{expanded && accounts.length > 0 && (
@@ -278,8 +354,8 @@ function INK2DeclarationRow({
<AccountNumber number={acc.accountNumber} name={acc.accountName} size="sm" />
</td>
<td className="py-1">{acc.accountName}</td>
<td className="py-1 text-right">
{isExpense && acc.amount > 0 ? '-' : ''}{formatCurrency(Math.abs(acc.amount))}
<td className="py-1 text-right tabular-nums">
{formatCurrency(acc.amount)}
</td>
</tr>
))}
+1
View File
@@ -157,6 +157,7 @@ export const CreateInvoiceSchema = z.object({
customer_id: uuid,
invoice_date: isoDate,
due_date: isoDate,
delivery_date: isoDate.optional(),
currency: CurrencySchema,
document_type: InvoiceDocumentTypeSchema.optional(),
your_reference: z.string().optional(),
+34 -8
View File
@@ -351,6 +351,12 @@ export function InvoicePDF({ invoice, customer, items, company, originalInvoiceN
<Text style={styles.label}>Förfallodatum:</Text>
<Text style={styles.value}>{formatDate(invoice.due_date)}</Text>
</View>
{invoice.delivery_date && invoice.delivery_date !== invoice.invoice_date && (
<View style={styles.row}>
<Text style={styles.label}>Leveransdatum:</Text>
<Text style={styles.value}>{formatDate(invoice.delivery_date)}</Text>
</View>
)}
{invoice.your_reference && (
<View style={{ marginBottom: 4 }}>
<Text style={styles.label}>Er referens:</Text>
@@ -447,12 +453,19 @@ export function InvoicePDF({ invoice, customer, items, company, originalInvoiceN
</View>
{vatByRate.size > 1 ? (
Array.from(vatByRate.entries())
.filter(([, group]) => group.vat > 0)
.sort(([a], [b]) => b - a)
.map(([rate, group]) => (
<View key={rate} style={styles.totalRow}>
<Text style={styles.totalLabel}>Moms {rate}%:</Text>
<Text style={styles.totalValue}>{formatCurrency(group.vat, invoice.currency)}</Text>
<View key={rate}>
<View style={styles.totalRow}>
<Text style={styles.totalLabel}>Netto {rate}%:</Text>
<Text style={styles.totalValue}>{formatCurrency(group.base, invoice.currency)}</Text>
</View>
{group.vat > 0 && (
<View style={styles.totalRow}>
<Text style={styles.totalLabel}>Moms {rate}%:</Text>
<Text style={styles.totalValue}>{formatCurrency(group.vat, invoice.currency)}</Text>
</View>
)}
</View>
))
) : (
@@ -477,9 +490,17 @@ export function InvoicePDF({ invoice, customer, items, company, originalInvoiceN
</View>
)}
{invoice.currency !== 'SEK' && invoice.total_sek && (
<View style={[styles.totalRow, { marginTop: 8 }]}>
<Text style={[styles.totalLabel, { fontSize: 9 }]}>I SEK (kurs {invoice.exchange_rate}):</Text>
<Text style={[styles.totalValue, { fontSize: 9 }]}>{formatCurrency(invoice.total_sek, 'SEK')}</Text>
<View style={{ marginTop: 8 }}>
{invoice.vat_amount_sek != null && invoice.vat_amount_sek !== 0 && (
<View style={styles.totalRow}>
<Text style={[styles.totalLabel, { fontSize: 9 }]}>Moms i SEK (kurs {invoice.exchange_rate}):</Text>
<Text style={[styles.totalValue, { fontSize: 9 }]}>{formatCurrency(invoice.vat_amount_sek, 'SEK')}</Text>
</View>
)}
<View style={styles.totalRow}>
<Text style={[styles.totalLabel, { fontSize: 9 }]}>Totalt i SEK:</Text>
<Text style={[styles.totalValue, { fontSize: 9 }]}>{formatCurrency(invoice.total_sek, 'SEK')}</Text>
</View>
</View>
)}
</View>
@@ -549,12 +570,17 @@ export function InvoicePDF({ invoice, customer, items, company, originalInvoiceN
</View>
)}
{/* Reverse charge notice */}
{/* Reverse charge / export / exempt notice */}
{invoice.reverse_charge_text && (
<View style={styles.reverseChargeBox}>
<Text style={styles.reverseChargeText}>{invoice.reverse_charge_text}</Text>
</View>
)}
{invoice.vat_treatment === 'exempt' && !invoice.reverse_charge_text && (
<View style={styles.reverseChargeBox}>
<Text style={styles.reverseChargeText}>Undantag från skatteplikt, ML 3 kap.</Text>
</View>
)}
{/* Notes */}
{invoice.notes && (
+1
View File
@@ -103,6 +103,7 @@ export function getVatRules(
treatment: 'export',
rate: 0,
momsRuta: '40',
reverseChargeText: 'Omsättning utanför EU, ML 10 kap.',
}
default:
+191 -158
View File
@@ -1,12 +1,12 @@
import { describe, it, expect } from 'vitest'
import { INK2_ACCOUNT_MAPPINGS, isAccountInMapping, checkBalanceWarning } from '../ink2-engine'
import type { INK2SRUCode } from '../types'
import { INK2R_ACCOUNT_MAPPINGS, isAccountInMapping, checkBalanceWarning } from '../ink2-engine'
import type { INK2RSRUCode } from '../types'
/**
* Helper to find which SRU code an account maps to
*/
function findSRUCodeForAccount(accountNumber: string): INK2SRUCode | null {
for (const mapping of INK2_ACCOUNT_MAPPINGS) {
function findSRUCodeForAccount(accountNumber: string): INK2RSRUCode | null {
for (const mapping of INK2R_ACCOUNT_MAPPINGS) {
if (isAccountInMapping(accountNumber, mapping)) {
return mapping.sruCode
}
@@ -14,244 +14,277 @@ function findSRUCodeForAccount(accountNumber: string): INK2SRUCode | null {
return null
}
describe('INK2 Account Mappings', () => {
describe('INK2R Account Mappings', () => {
describe('completeness', () => {
it('has 19 mappings covering all INK2 fields', () => {
expect(INK2_ACCOUNT_MAPPINGS).toHaveLength(19)
it('has mappings covering all INK2R balance sheet and income statement fields', () => {
// 26 asset + 21 equity/liability + 20 income statement = 67 mappings
expect(INK2R_ACCOUNT_MAPPINGS.length).toBeGreaterThanOrEqual(60)
})
it('covers all SRU codes', () => {
const codes = INK2_ACCOUNT_MAPPINGS.map(m => m.sruCode)
const expectedCodes: INK2SRUCode[] = [
'7201', '7202', '7203', '7210', '7211', '7212',
'7220', '7221', '7222', '7230', '7231',
'7310', '7320', '7330', '7340', '7350', '7360', '7370', '7380',
]
expect(codes).toEqual(expectedCodes)
it('covers all expected SRU codes', () => {
const codes = new Set(INK2R_ACCOUNT_MAPPINGS.map(m => m.sruCode))
// Balance sheet asset codes
for (const code of ['7201', '7202', '7214', '7215', '7216', '7217', '7281']) {
expect(codes.has(code as INK2RSRUCode)).toBe(true)
}
// Equity/liability codes
for (const code of ['7301', '7302', '7321', '7322', '7365', '7368', '7370']) {
expect(codes.has(code as INK2RSRUCode)).toBe(true)
}
// Income statement codes
for (const code of ['7410', '7513', '7514', '7515', '7522', '7528']) {
expect(codes.has(code as INK2RSRUCode)).toBe(true)
}
})
})
describe('Balance sheet - Assets', () => {
it('1000-1099 -> 7201 (Immateriella AT)', () => {
expect(findSRUCodeForAccount('1000')).toBe('7201')
describe('Balance sheet - Assets (per bas.se/kontoplaner/sru/)', () => {
it('1010-1079, 1090-1099 -> 7201 (Immateriella AT excl. förskott)', () => {
expect(findSRUCodeForAccount('1010')).toBe('7201')
expect(findSRUCodeForAccount('1050')).toBe('7201')
expect(findSRUCodeForAccount('1079')).toBe('7201')
expect(findSRUCodeForAccount('1090')).toBe('7201')
expect(findSRUCodeForAccount('1099')).toBe('7201')
})
it('1100-1299 -> 7202 (Materiella AT)', () => {
expect(findSRUCodeForAccount('1100')).toBe('7202')
expect(findSRUCodeForAccount('1210')).toBe('7202')
expect(findSRUCodeForAccount('1299')).toBe('7202')
it('1080-1089 -> 7202 (Förskott immateriella)', () => {
expect(findSRUCodeForAccount('1080')).toBe('7202')
expect(findSRUCodeForAccount('1089')).toBe('7202')
})
it('1300-1399 -> 7203 (Finansiella AT)', () => {
expect(findSRUCodeForAccount('1300')).toBe('7203')
expect(findSRUCodeForAccount('1350')).toBe('7203')
expect(findSRUCodeForAccount('1399')).toBe('7203')
it('1100-1119, 1130-1179, 1190-1199 -> 7214 (Byggnader och mark)', () => {
expect(findSRUCodeForAccount('1100')).toBe('7214')
expect(findSRUCodeForAccount('1110')).toBe('7214')
expect(findSRUCodeForAccount('1130')).toBe('7214')
expect(findSRUCodeForAccount('1190')).toBe('7214')
})
it('1400-1499 -> 7210 (Varulager)', () => {
expect(findSRUCodeForAccount('1400')).toBe('7210')
expect(findSRUCodeForAccount('1460')).toBe('7210')
expect(findSRUCodeForAccount('1499')).toBe('7210')
it('1120-1129 -> 7216 (Förbättringsutgifter annans fastighet)', () => {
expect(findSRUCodeForAccount('1120')).toBe('7216')
expect(findSRUCodeForAccount('1129')).toBe('7216')
})
it('1500-1599 -> 7211 (Kundfordringar)', () => {
expect(findSRUCodeForAccount('1500')).toBe('7211')
expect(findSRUCodeForAccount('1510')).toBe('7211')
expect(findSRUCodeForAccount('1599')).toBe('7211')
it('1180-1189 -> 7217 (Pågående nyanläggningar)', () => {
expect(findSRUCodeForAccount('1180')).toBe('7217')
expect(findSRUCodeForAccount('1189')).toBe('7217')
})
it('1600-1999 -> 7212 (Övriga OT)', () => {
expect(findSRUCodeForAccount('1600')).toBe('7212')
expect(findSRUCodeForAccount('1930')).toBe('7212')
expect(findSRUCodeForAccount('1999')).toBe('7212')
it('1200-1299 -> 7215 (Maskiner och inventarier)', () => {
expect(findSRUCodeForAccount('1200')).toBe('7215')
expect(findSRUCodeForAccount('1250')).toBe('7215')
expect(findSRUCodeForAccount('1299')).toBe('7215')
})
it('1500-1519 -> 7251 (Kundfordringar)', () => {
expect(findSRUCodeForAccount('1500')).toBe('7251')
expect(findSRUCodeForAccount('1510')).toBe('7251')
expect(findSRUCodeForAccount('1519')).toBe('7251')
})
it('1520-1559 -> 7261 (Övriga fordringar, not 7251)', () => {
expect(findSRUCodeForAccount('1520')).toBe('7261')
expect(findSRUCodeForAccount('1550')).toBe('7261')
})
it('1700-1799 -> 7263 (Förutbetalda kostnader)', () => {
expect(findSRUCodeForAccount('1700')).toBe('7263')
expect(findSRUCodeForAccount('1790')).toBe('7263')
})
it('1900-1999 -> 7281 (Kassa och bank)', () => {
expect(findSRUCodeForAccount('1900')).toBe('7281')
expect(findSRUCodeForAccount('1930')).toBe('7281')
expect(findSRUCodeForAccount('1999')).toBe('7281')
})
})
describe('Balance sheet - Equity & Liabilities', () => {
it('2081 -> 7220 (Aktiekapital)', () => {
expect(findSRUCodeForAccount('2081')).toBe('7220')
it('2010-2089 -> 7301 (Bundet EK)', () => {
expect(findSRUCodeForAccount('2010')).toBe('7301')
expect(findSRUCodeForAccount('2081')).toBe('7301')
expect(findSRUCodeForAccount('2089')).toBe('7301')
})
it('2081 does NOT go to 7221', () => {
expect(findSRUCodeForAccount('2081')).not.toBe('7221')
it('2090-2099 -> 7302 (Fritt EK)', () => {
expect(findSRUCodeForAccount('2090')).toBe('7302')
expect(findSRUCodeForAccount('2099')).toBe('7302')
})
it('2000-2080 -> 7221 (Övrigt EK)', () => {
expect(findSRUCodeForAccount('2000')).toBe('7221')
expect(findSRUCodeForAccount('2010')).toBe('7221')
expect(findSRUCodeForAccount('2080')).toBe('7221')
it('2110-2129 -> 7321 (Periodiseringsfonder)', () => {
expect(findSRUCodeForAccount('2110')).toBe('7321')
expect(findSRUCodeForAccount('2120')).toBe('7321')
})
it('2082-2098 -> 7221 (Övrigt EK)', () => {
expect(findSRUCodeForAccount('2082')).toBe('7221')
expect(findSRUCodeForAccount('2090')).toBe('7221')
expect(findSRUCodeForAccount('2098')).toBe('7221')
it('2150-2159 -> 7322 (Ackumulerade överavskrivningar)', () => {
expect(findSRUCodeForAccount('2150')).toBe('7322')
expect(findSRUCodeForAccount('2159')).toBe('7322')
})
it('2099 -> 7222 (Årets resultat)', () => {
expect(findSRUCodeForAccount('2099')).toBe('7222')
it('2440-2449 -> 7365 (Leverantörsskulder)', () => {
expect(findSRUCodeForAccount('2440')).toBe('7365')
expect(findSRUCodeForAccount('2449')).toBe('7365')
})
it('2099 does NOT go to 7221', () => {
expect(findSRUCodeForAccount('2099')).not.toBe('7221')
it('2500-2599 -> 7368 (Skatteskulder)', () => {
expect(findSRUCodeForAccount('2500')).toBe('7368')
expect(findSRUCodeForAccount('2510')).toBe('7368')
})
it('2100-2499 -> 7230 (Obeskattade reserver, avsättningar, skulder)', () => {
expect(findSRUCodeForAccount('2100')).toBe('7230')
expect(findSRUCodeForAccount('2150')).toBe('7230') // Obeskattade reserver
expect(findSRUCodeForAccount('2250')).toBe('7230') // Avsättningar
expect(findSRUCodeForAccount('2440')).toBe('7230') // Leverantörsskulder
expect(findSRUCodeForAccount('2499')).toBe('7230')
it('2600-2799 -> 7369 (Övriga skulder kortfristiga, e.g. moms)', () => {
expect(findSRUCodeForAccount('2611')).toBe('7369')
expect(findSRUCodeForAccount('2710')).toBe('7369')
})
it('2500-2999 -> 7231 (Övriga skulder)', () => {
expect(findSRUCodeForAccount('2500')).toBe('7231')
expect(findSRUCodeForAccount('2611')).toBe('7231') // Utgående moms
expect(findSRUCodeForAccount('2710')).toBe('7231') // Personalens källskatt
expect(findSRUCodeForAccount('2999')).toBe('7231')
it('2900-2999 -> 7370 (Upplupna kostnader)', () => {
expect(findSRUCodeForAccount('2900')).toBe('7370')
expect(findSRUCodeForAccount('2999')).toBe('7370')
})
})
describe('Income statement', () => {
it('3000-3999 -> 7310 (Nettoomsättning)', () => {
expect(findSRUCodeForAccount('3000')).toBe('7310')
expect(findSRUCodeForAccount('3001')).toBe('7310')
expect(findSRUCodeForAccount('3100')).toBe('7310')
expect(findSRUCodeForAccount('3999')).toBe('7310')
describe('Income statement (per bas.se — CRITICAL: 5000-6999 ALL → 7513)', () => {
it('3000-3799 -> 7410 (Nettoomsättning)', () => {
expect(findSRUCodeForAccount('3000')).toBe('7410')
expect(findSRUCodeForAccount('3001')).toBe('7410')
expect(findSRUCodeForAccount('3100')).toBe('7410')
expect(findSRUCodeForAccount('3799')).toBe('7410')
})
it('4000-4999 -> 7320 (Varuinköp)', () => {
expect(findSRUCodeForAccount('4000')).toBe('7320')
expect(findSRUCodeForAccount('4010')).toBe('7320')
expect(findSRUCodeForAccount('4999')).toBe('7320')
it('3900-3999 -> 7413 (Övriga rörelseintäkter)', () => {
expect(findSRUCodeForAccount('3900')).toBe('7413')
expect(findSRUCodeForAccount('3999')).toBe('7413')
})
it('5000-6999 -> 7330 (Övriga externa kostnader)', () => {
expect(findSRUCodeForAccount('5000')).toBe('7330')
expect(findSRUCodeForAccount('5460')).toBe('7330')
expect(findSRUCodeForAccount('6200')).toBe('7330')
expect(findSRUCodeForAccount('6999')).toBe('7330')
it('4000-4499 -> 7511 (Råvaror)', () => {
expect(findSRUCodeForAccount('4000')).toBe('7511')
expect(findSRUCodeForAccount('4010')).toBe('7511')
expect(findSRUCodeForAccount('4499')).toBe('7511')
})
it('7000-7699 -> 7340 (Personalkostnader)', () => {
expect(findSRUCodeForAccount('7000')).toBe('7340')
expect(findSRUCodeForAccount('7210')).toBe('7340')
expect(findSRUCodeForAccount('7699')).toBe('7340')
it('4600-4699 -> 7512 (Handelsvaror)', () => {
expect(findSRUCodeForAccount('4600')).toBe('7512')
expect(findSRUCodeForAccount('4699')).toBe('7512')
})
it('7700-7899 -> 7350 (Avskrivningar)', () => {
expect(findSRUCodeForAccount('7700')).toBe('7350')
expect(findSRUCodeForAccount('7820')).toBe('7350')
expect(findSRUCodeForAccount('7899')).toBe('7350')
it('5000-6999 ALL -> 7513 (Övriga externa kostnader)', () => {
expect(findSRUCodeForAccount('5000')).toBe('7513')
expect(findSRUCodeForAccount('5460')).toBe('7513')
expect(findSRUCodeForAccount('6200')).toBe('7513')
expect(findSRUCodeForAccount('6999')).toBe('7513')
})
it('7900-7999 -> 7360 (Övriga rörelsekostnader)', () => {
expect(findSRUCodeForAccount('7900')).toBe('7360')
expect(findSRUCodeForAccount('7970')).toBe('7360')
expect(findSRUCodeForAccount('7999')).toBe('7360')
it('7000-7699 -> 7514 (Personalkostnader)', () => {
expect(findSRUCodeForAccount('7000')).toBe('7514')
expect(findSRUCodeForAccount('7210')).toBe('7514')
expect(findSRUCodeForAccount('7699')).toBe('7514')
})
it('8000-8499 -> 7370 (Finansiella poster)', () => {
expect(findSRUCodeForAccount('8000')).toBe('7370')
expect(findSRUCodeForAccount('8310')).toBe('7370') // Ränteintäkter
expect(findSRUCodeForAccount('8400')).toBe('7370') // Räntekostnader
expect(findSRUCodeForAccount('8499')).toBe('7370')
it('7800-7899 -> 7515 (Avskrivningar)', () => {
expect(findSRUCodeForAccount('7800')).toBe('7515')
expect(findSRUCodeForAccount('7820')).toBe('7515')
expect(findSRUCodeForAccount('7899')).toBe('7515')
})
it('8500-8999 -> 7380 (Extraordinära poster)', () => {
expect(findSRUCodeForAccount('8500')).toBe('7380')
expect(findSRUCodeForAccount('8910')).toBe('7380') // Skatt
expect(findSRUCodeForAccount('8999')).toBe('7380')
it('7700-7799 -> 7516 (Nedskrivningar OT)', () => {
expect(findSRUCodeForAccount('7700')).toBe('7516')
expect(findSRUCodeForAccount('7799')).toBe('7516')
})
it('7900-7999 -> 7517 (Övriga rörelsekostnader)', () => {
expect(findSRUCodeForAccount('7900')).toBe('7517')
expect(findSRUCodeForAccount('7999')).toBe('7517')
})
it('8300-8399 -> 7417 (Ränteintäkter)', () => {
expect(findSRUCodeForAccount('8300')).toBe('7417')
expect(findSRUCodeForAccount('8310')).toBe('7417')
})
it('8400-8499 -> 7522 (Räntekostnader)', () => {
expect(findSRUCodeForAccount('8400')).toBe('7522')
expect(findSRUCodeForAccount('8499')).toBe('7522')
})
it('8900-8989 -> 7528 (Skatt)', () => {
expect(findSRUCodeForAccount('8900')).toBe('7528')
expect(findSRUCodeForAccount('8910')).toBe('7528')
})
})
describe('no overlap between mappings', () => {
it('each account matches exactly one mapping', () => {
// Test a representative sample across boundaries
it('representative boundary accounts match exactly one mapping', () => {
const testAccounts = [
'1099', '1100', // 7201/7202 boundary
'1299', '1300', // 7202/7203 boundary
'1399', '1400', // 7203/7210 boundary
'1499', '1500', // 7210/7211 boundary
'1599', '1600', // 7211/7212 boundary
'1999', '2000', // 7212/7221 boundary
'2080', '2081', '2082', // 7221/7220/7221
'2098', '2099', '2100', // 7221/7222/7230 boundary
'2499', '2500', // 7230/7231 boundary
'2999', '3000', // 7231/7310 boundary
'3999', '4000', // 7310/7320 boundary
'4999', '5000', // 7320/7330 boundary
'6999', '7000', // 7330/7340 boundary
'7699', '7700', // 7340/7350 boundary
'7899', '7900', // 7350/7360 boundary
'7999', '8000', // 7360/7370 boundary
'8499', '8500', // 7370/7380 boundary
'1079', '1080', // 7201/7202 boundary
'1089', '1090', // 7202/7201 boundary
'1099', '1100', // 7201/7214 boundary
'1119', '1120', // 7214/7216 boundary
'1129', '1130', // 7216/7214 boundary
'1199', '1200', // 7214/7215 boundary
'1299', '1311', // 7215/7230 boundary
'1519', '1520', // 7251/7261 boundary
'1559', '1560', // 7261/7252 boundary
'1930', '1999', // 7281 bank accounts
'2089', '2090', // 7301/7302 boundary
'2099', '2110', // 7302/7321 boundary
'2439', '2440', // 7361/7365 boundary
'2449', '2450', // 7365/7363 boundary
'2499', '2500', // 7369/7368 boundary
'2599', '2600', // 7368/7369 boundary
'2899', '2900', // 7369/7370 boundary
'3799', '3800', // 7410/7412 boundary
'3899', '3900', // 7412/7413 boundary
'3999', '4000', // 7413/7511 boundary
'4499', '4600', // 7511/7512 boundary (4500-4599 unmapped)
'5000', '6999', // 7513 (övriga externa)
'7699', '7700', // 7514/7516 boundary
'7799', '7800', // 7516/7515 boundary
'7899', '7900', // 7515/7517 boundary
'8399', '8400', // 7417/7522 boundary
]
for (const account of testAccounts) {
let matchCount = 0
for (const mapping of INK2_ACCOUNT_MAPPINGS) {
for (const mapping of INK2R_ACCOUNT_MAPPINGS) {
if (isAccountInMapping(account, mapping)) {
matchCount++
}
}
expect(matchCount).toBe(1)
expect(matchCount, `Account ${account} should match exactly one mapping, got ${matchCount}`).toBe(1)
}
})
})
describe('section assignments', () => {
it('asset mappings have section "assets"', () => {
const assetMappings = INK2_ACCOUNT_MAPPINGS.filter(m => m.section === 'assets')
expect(assetMappings.map(m => m.sruCode)).toEqual(['7201', '7202', '7203', '7210', '7211', '7212'])
})
it('equity/liability mappings have section "equity_liabilities"', () => {
const eqMappings = INK2_ACCOUNT_MAPPINGS.filter(m => m.section === 'equity_liabilities')
expect(eqMappings.map(m => m.sruCode)).toEqual(['7220', '7221', '7222', '7230', '7231'])
})
it('income statement mappings have section "income_statement"', () => {
const isMappings = INK2_ACCOUNT_MAPPINGS.filter(m => m.section === 'income_statement')
expect(isMappings.map(m => m.sruCode)).toEqual(['7310', '7320', '7330', '7340', '7350', '7360', '7370', '7380'])
})
})
describe('normal balance assignments', () => {
it('asset accounts are debit-normal', () => {
const assetMappings = INK2_ACCOUNT_MAPPINGS.filter(m => m.section === 'assets')
it('all asset mappings have section "assets"', () => {
const assetMappings = INK2R_ACCOUNT_MAPPINGS.filter(m => m.section === 'assets')
expect(assetMappings.length).toBe(26)
for (const m of assetMappings) {
expect(m.normalBalance).toBe('debit')
}
})
it('equity/liability accounts are credit-normal', () => {
const eqMappings = INK2_ACCOUNT_MAPPINGS.filter(m => m.section === 'equity_liabilities')
it('all equity/liability mappings have section "equity_liabilities"', () => {
const eqMappings = INK2R_ACCOUNT_MAPPINGS.filter(m => m.section === 'equity_liabilities')
expect(eqMappings.length).toBe(24)
for (const m of eqMappings) {
expect(m.normalBalance).toBe('credit')
}
})
it('revenue (7310) is credit-normal', () => {
const revenue = INK2_ACCOUNT_MAPPINGS.find(m => m.sruCode === '7310')
it('income statement mappings have correct normal balance types', () => {
const isMappings = INK2R_ACCOUNT_MAPPINGS.filter(m => m.section === 'income_statement')
expect(isMappings.length).toBeGreaterThanOrEqual(20)
// Revenue accounts are credit-normal
const revenue = isMappings.find(m => m.sruCode === '7410')
expect(revenue?.normalBalance).toBe('credit')
})
it('expense accounts (7320-7360) are debit-normal', () => {
const expenseCodes: INK2SRUCode[] = ['7320', '7330', '7340', '7350', '7360']
for (const code of expenseCodes) {
const mapping = INK2_ACCOUNT_MAPPINGS.find(m => m.sruCode === code)
expect(mapping?.normalBalance).toBe('debit')
}
})
// Cost accounts are debit-normal
const costs = isMappings.find(m => m.sruCode === '7513')
expect(costs?.normalBalance).toBe('debit')
it('financial and extraordinary items (7370, 7380) are net', () => {
const financial = INK2_ACCOUNT_MAPPINGS.find(m => m.sruCode === '7370')
const extraordinary = INK2_ACCOUNT_MAPPINGS.find(m => m.sruCode === '7380')
expect(financial?.normalBalance).toBe('net')
expect(extraordinary?.normalBalance).toBe('net')
// Net items
const net = isMappings.find(m => m.sruCode === '7414')
expect(net?.normalBalance).toBe('net')
})
})
})
+257 -109
View File
@@ -1,8 +1,33 @@
import { describe, it, expect } from 'vitest'
import { generateSRUFile, sruFileToString, validateSRUFile, getSRUFilename } from '../sru-generator'
import {
generateSRUSubmission,
validateBlanketterSru,
getZipFilename,
} from '../sru-generator'
import type { INK2Declaration } from '../types'
function makeDeclaration(overrides?: Partial<INK2Declaration>): INK2Declaration {
const defaultInk2r = {
'7201': 0, '7202': 0, '7214': 0, '7215': 50000, '7216': 0, '7217': 0,
'7230': 0, '7231': 0, '7233': 0, '7232': 0, '7234': 0, '7235': 0,
'7241': 0, '7242': 0, '7243': 0, '7244': 0, '7245': 0, '7246': 0,
'7251': 25000, '7252': 0, '7261': 0, '7262': 0, '7263': 0,
'7270': 0, '7271': 0, '7281': 100000,
'7301': 50000, '7302': 20000,
'7321': 0, '7322': 0, '7323': 0,
'7331': 0, '7332': 0, '7333': 0,
'7350': 0, '7351': 0, '7352': 0, '7353': 0, '7354': 0,
'7360': 0, '7361': 0, '7362': 0, '7363': 0, '7364': 0,
'7365': 30000, '7366': 0, '7367': 0, '7369': 70000, '7368': 0, '7370': 0,
'7410': 500000, '7411': 0, '7412': 0, '7413': 0,
'7511': 0, '7512': 0, '7513': -100000, '7514': -80000, '7515': -10000, '7516': 0, '7517': -5000,
'7414': 0, '7415': 0, '7423': 0, '7416': 0, '7417': 0,
'7521': 0, '7522': -3000,
'7524': 0, '7419': 0, '7420': 0, '7525': 0, '7421': 0, '7422': 0,
'7528': 0,
'7450': 302000, '7550': 0,
} as INK2Declaration['ink2r']
return {
fiscalYear: {
id: 'period-1',
@@ -11,25 +36,36 @@ function makeDeclaration(overrides?: Partial<INK2Declaration>): INK2Declaration
end: '2025-12-31',
isClosed: true,
},
rutor: {
'7201': 0, '7202': 50000, '7203': 0,
'7210': 10000, '7211': 25000, '7212': 100000,
'7220': 50000, '7221': 20000, '7222': 15000,
'7230': 30000, '7231': 70000,
'7310': 500000, '7320': 200000, '7330': 100000,
'7340': 80000, '7350': 10000, '7360': 5000,
'7370': -3000, '7380': 0,
ink2: {
'7011': '20250101',
'7012': '20251231',
'7113': 302000,
'7114': 0,
},
ink2r: defaultInk2r,
ink2s: {
'7011': '20250101',
'7012': '20251231',
'7650': 302000,
'7750': 0,
'7651': 0,
'8020': 302000,
'8021': 0,
},
breakdown: {} as INK2Declaration['breakdown'],
totals: {
totalAssets: 185000,
totalEquityLiabilities: 185000,
operatingResult: 105000,
resultAfterFinancial: 102000,
totalAssets: 175000,
totalEquityLiabilities: 175000,
operatingResult: 305000,
resultAfterFinancial: 302000,
},
companyInfo: {
companyName: 'Test AB',
orgNumber: '556677-8899',
addressLine1: 'Testgatan 1',
postalCode: '11122',
city: 'Stockholm',
email: 'test@example.com',
},
warnings: [],
...overrides,
@@ -37,138 +73,250 @@ function makeDeclaration(overrides?: Partial<INK2Declaration>): INK2Declaration
}
describe('INK2 SRU Generator', () => {
describe('generateSRUFile', () => {
it('produces valid SRU file structure', () => {
describe('generateSRUSubmission', () => {
it('produces valid INFO.SRU', () => {
const declaration = makeDeclaration()
const sruFile = generateSRUFile(declaration)
const validation = validateSRUFile(sruFile)
const submission = generateSRUSubmission(declaration)
expect(submission.infoSru).toContain('#DATABESKRIVNING_START')
expect(submission.infoSru).toContain('#PRODUKT SRU')
expect(submission.infoSru).toContain('#FILNAMN BLANKETTER.SRU')
expect(submission.infoSru).toContain('#DATABESKRIVNING_SLUT')
expect(submission.infoSru).toContain('#MEDIELEV_START')
expect(submission.infoSru).toContain('#ORGNR 165566778899')
expect(submission.infoSru).toContain('#NAMN Test AB')
expect(submission.infoSru).toContain('#POSTNR 11122')
expect(submission.infoSru).toContain('#POSTORT Stockholm')
expect(submission.infoSru).toContain('#MEDIELEV_SLUT')
})
it('formats org number as 12-digit with century prefix 16', () => {
const declaration = makeDeclaration()
const submission = generateSRUSubmission(declaration)
// In INFO.SRU
expect(submission.infoSru).toContain('#ORGNR 165566778899')
// In BLANKETTER.SRU
expect(submission.blanketterSru).toContain('#IDENTITET 165566778899')
})
it('handles org number already in 12-digit format', () => {
const declaration = makeDeclaration({
companyInfo: {
companyName: 'Test AB',
orgNumber: '165566778899',
addressLine1: null,
postalCode: '11122',
city: 'Stockholm',
email: null,
},
})
const submission = generateSRUSubmission(declaration)
expect(submission.infoSru).toContain('#ORGNR 165566778899')
})
it('produces three blankett blocks in BLANKETTER.SRU', () => {
const declaration = makeDeclaration()
const submission = generateSRUSubmission(declaration)
expect(submission.blanketterSru).toContain('#BLANKETT INK2-2025P4')
expect(submission.blanketterSru).toContain('#BLANKETT INK2R-2025P4')
expect(submission.blanketterSru).toContain('#BLANKETT INK2S-2025P4')
expect(submission.blanketterSru).toContain('#FIL_SLUT')
})
it('validates the generated BLANKETTER.SRU', () => {
const declaration = makeDeclaration()
const submission = generateSRUSubmission(declaration)
const validation = validateBlanketterSru(submission.blanketterSru)
expect(validation.isValid).toBe(true)
expect(validation.errors).toEqual([])
})
it('uses #BLANKETT INK2', () => {
it('uses correct period suffix for calendar year', () => {
const declaration = makeDeclaration()
const sruFile = generateSRUFile(declaration)
const blankettRecord = sruFile.records.find(r => r.fieldCode === 'BLANKETT')
expect(blankettRecord?.value).toBe('INK2')
const submission = generateSRUSubmission(declaration)
// Dec = P4
expect(submission.blanketterSru).toContain('INK2-2025P4')
})
it('includes only non-zero field values', () => {
const declaration = makeDeclaration()
const sruFile = generateSRUFile(declaration)
const uppgiftRecords = sruFile.records.filter(r => r.fieldCode === 'UPPGIFT')
// 7000 (fiscal year) + non-zero rutor
// Zero rutor: 7201, 7203, 7380 = 3 zero fields
// Non-zero: 16 fields
// Total UPPGIFT records: 1 (fiscal year) + 16 (non-zero values)
expect(uppgiftRecords).toHaveLength(17)
// Verify zero fields are excluded
const fieldCodes = uppgiftRecords.map(r => String(r.value).split(' ')[0])
expect(fieldCodes).not.toContain('7201')
expect(fieldCodes).not.toContain('7203')
expect(fieldCodes).not.toContain('7380')
it('uses P1 suffix for fiscal year ending in Jan-Apr', () => {
const declaration = makeDeclaration({
fiscalYear: {
id: 'p1',
name: 'FY',
start: '2024-05-01',
end: '2025-04-30',
isClosed: true,
},
})
const submission = generateSRUSubmission(declaration)
expect(submission.blanketterSru).toContain('INK2-2025P1')
})
it('includes fiscal year as field 7000', () => {
const declaration = makeDeclaration()
const sruFile = generateSRUFile(declaration)
const fiscalYearRecord = sruFile.records.find(
r => r.fieldCode === 'UPPGIFT' && String(r.value).startsWith('7000')
)
expect(fiscalYearRecord).toBeDefined()
expect(fiscalYearRecord?.value).toBe('7000 20250101-20251231')
it('uses P2 suffix for fiscal year ending in May-Aug', () => {
const declaration = makeDeclaration({
fiscalYear: {
id: 'p2',
name: 'FY',
start: '2024-09-01',
end: '2025-06-30',
isClosed: true,
},
})
const submission = generateSRUSubmission(declaration)
expect(submission.blanketterSru).toContain('INK2-2025P2')
})
it('handles negative values (financial items)', () => {
it('excludes zero-value #UPPGIFT lines', () => {
const declaration = makeDeclaration()
const sruFile = generateSRUFile(declaration)
const financialRecord = sruFile.records.find(
r => r.fieldCode === 'UPPGIFT' && String(r.value).startsWith('7370')
)
expect(financialRecord?.value).toBe('7370 -3000')
const submission = generateSRUSubmission(declaration)
// 7201 is 0, should not appear
const ink2rBlock = extractBlock(submission.blanketterSru, 'INK2R')
expect(ink2rBlock).not.toContain('#UPPGIFT 7201')
// 7215 is 50000, should appear
expect(ink2rBlock).toContain('#UPPGIFT 7215 50000')
})
it('strips dashes from org number', () => {
it('includes fiscal year fields 7011 and 7012 in each block', () => {
const declaration = makeDeclaration()
const sruFile = generateSRUFile(declaration)
const identityRecord = sruFile.records.find(r => r.fieldCode === 'IDENTITET')
expect(identityRecord?.value).toBe('5566778899')
const submission = generateSRUSubmission(declaration)
// Each block should have 7011 and 7012
const blocks = [
extractBlock(submission.blanketterSru, 'INK2-'),
extractBlock(submission.blanketterSru, 'INK2R'),
extractBlock(submission.blanketterSru, 'INK2S'),
]
for (const block of blocks) {
expect(block).toContain('#UPPGIFT 7011 20250101')
expect(block).toContain('#UPPGIFT 7012 20251231')
}
})
})
describe('sruFileToString', () => {
it('formats records as #FIELD value lines', () => {
it('each blankett block has #IDENTITET and #NAMN', () => {
const declaration = makeDeclaration()
const sruFile = generateSRUFile(declaration)
const content = sruFileToString(sruFile)
const submission = generateSRUSubmission(declaration)
expect(content).toContain('#BLANKETT INK2')
expect(content).toContain('#IDENTITET 5566778899')
expect(content).toContain('#BLANKETTSLUT')
const blocks = submission.blanketterSru.split('#BLANKETT ').slice(1)
expect(blocks).toHaveLength(3)
for (const block of blocks) {
expect(block).toContain('#IDENTITET 165566778899')
expect(block).toContain('#NAMN Test AB')
expect(block).toContain('#BLANKETTSLUT')
}
})
it('handles negative values correctly', () => {
const declaration = makeDeclaration()
const submission = generateSRUSubmission(declaration)
const ink2rBlock = extractBlock(submission.blanketterSru, 'INK2R')
expect(ink2rBlock).toContain('#UPPGIFT 7513 -100000')
expect(ink2rBlock).toContain('#UPPGIFT 7522 -3000')
})
it('includes INK2S with överskott/underskott', () => {
const declaration = makeDeclaration()
const submission = generateSRUSubmission(declaration)
const ink2sBlock = extractBlock(submission.blanketterSru, 'INK2S')
expect(ink2sBlock).toContain('#UPPGIFT 7650 302000')
expect(ink2sBlock).toContain('#UPPGIFT 8020 302000')
// 7750 and 8021 are 0, should not appear
expect(ink2sBlock).not.toContain('#UPPGIFT 7750')
expect(ink2sBlock).not.toContain('#UPPGIFT 8021')
})
it('INK2 block includes överskott', () => {
const declaration = makeDeclaration()
const submission = generateSRUSubmission(declaration)
const ink2Block = extractBlock(submission.blanketterSru, 'INK2-')
expect(ink2Block).toContain('#UPPGIFT 7113 302000')
// 7114 (underskott) is 0, should not appear
expect(ink2Block).not.toContain('#UPPGIFT 7114')
})
it('uses CRLF line endings', () => {
const declaration = makeDeclaration()
const sruFile = generateSRUFile(declaration)
const content = sruFileToString(sruFile)
expect(content).toContain('\r\n')
const submission = generateSRUSubmission(declaration)
expect(submission.infoSru).toContain('\r\n')
expect(submission.blanketterSru).toContain('\r\n')
})
it('ends with newline', () => {
const declaration = makeDeclaration()
const sruFile = generateSRUFile(declaration)
const content = sruFileToString(sruFile)
expect(content.endsWith('\r\n')).toBe(true)
it('sanitizes # from company name', () => {
const declaration = makeDeclaration({
companyInfo: {
companyName: 'Test #1 AB',
orgNumber: '556677-8899',
addressLine1: null,
postalCode: '11122',
city: 'Stockholm',
email: null,
},
})
const submission = generateSRUSubmission(declaration)
expect(submission.infoSru).toContain('#NAMN Test 1 AB')
})
})
describe('getSRUFilename', () => {
describe('validateBlanketterSru', () => {
it('validates a correct BLANKETTER.SRU', () => {
const declaration = makeDeclaration()
const submission = generateSRUSubmission(declaration)
const result = validateBlanketterSru(submission.blanketterSru)
expect(result.isValid).toBe(true)
})
it('detects missing INK2R block', () => {
const result = validateBlanketterSru(
'#BLANKETT INK2-2025P4\r\n#IDENTITET 165566778899 20250101 100000\r\n#NAMN Test\r\n#BLANKETTSLUT\r\n' +
'#BLANKETT INK2S-2025P4\r\n#IDENTITET 165566778899 20250101 100002\r\n#NAMN Test\r\n#BLANKETTSLUT\r\n' +
'#FIL_SLUT\r\n'
)
expect(result.isValid).toBe(false)
expect(result.errors).toContain('Missing INK2R blankett block')
})
it('detects missing #FIL_SLUT', () => {
const result = validateBlanketterSru(
'#BLANKETT INK2-2025P4\r\n#IDENTITET x\r\n#NAMN T\r\n#BLANKETTSLUT\r\n' +
'#BLANKETT INK2R-2025P4\r\n#IDENTITET x\r\n#NAMN T\r\n#BLANKETTSLUT\r\n' +
'#BLANKETT INK2S-2025P4\r\n#IDENTITET x\r\n#NAMN T\r\n#BLANKETTSLUT\r\n'
)
expect(result.isValid).toBe(false)
expect(result.errors).toContain('Missing #FIL_SLUT terminator')
})
})
describe('getZipFilename', () => {
it('returns correct filename format', () => {
const declaration = makeDeclaration()
expect(getSRUFilename(declaration)).toBe('INK2_5566778899_2025.sru')
expect(getZipFilename(declaration)).toBe('INK2_SRU_5566778899_2025.zip')
})
it('handles missing org number', () => {
const declaration = makeDeclaration({
companyInfo: { companyName: 'Test AB', orgNumber: null },
companyInfo: {
companyName: 'Test AB',
orgNumber: null,
addressLine1: null,
postalCode: null,
city: null,
email: null,
},
})
expect(getSRUFilename(declaration)).toBe('INK2_unknown_2025.sru')
})
})
describe('validateSRUFile', () => {
it('validates a correct SRU file', () => {
const declaration = makeDeclaration()
const sruFile = generateSRUFile(declaration)
const result = validateSRUFile(sruFile)
expect(result.isValid).toBe(true)
})
it('detects missing PRODUKT header', () => {
const result = validateSRUFile({
records: [
{ fieldCode: 'BLANKETT', value: 'INK2' },
{ fieldCode: 'BLANKETTSLUT', value: '' },
],
generatedAt: new Date().toISOString(),
})
expect(result.isValid).toBe(false)
expect(result.errors).toContain('Missing PRODUKT header')
})
it('detects wrong blankett type', () => {
const result = validateSRUFile({
records: [
{ fieldCode: 'PRODUKT', value: 'KONTROLLUPPGIFTER' },
{ fieldCode: 'BLANKETT', value: 'NE' },
{ fieldCode: 'BLANKETTSLUT', value: '' },
],
generatedAt: new Date().toISOString(),
})
expect(result.isValid).toBe(false)
expect(result.errors).toContain('Expected BLANKETT INK2, got NE')
expect(getZipFilename(declaration)).toBe('INK2_SRU_unknown_2025.zip')
})
})
})
/** Extract a specific blankett block from BLANKETTER.SRU content */
function extractBlock(content: string, blockPrefix: string): string {
const regex = new RegExp(`#BLANKETT ${blockPrefix}[^\\r\\n]*[\\s\\S]*?#BLANKETTSLUT`)
const match = content.match(regex)
return match ? match[0] : ''
}
File diff suppressed because it is too large Load Diff
+225 -101
View File
@@ -1,97 +1,67 @@
import type { INK2Declaration, INK2SRUCode, SRUFile, SRURecord } from './types'
import type {
INK2Declaration,
INK2RSRUCode,
INK2SRutor,
SRUSubmission,
} from './types'
/**
* SRU File Generator for INK2
* SRU File Generator for INK2 (Aktiebolag)
*
* Generates SRU (Standardiserat Räkenskapsutdrag) files for electronic
* submission to Skatteverket. The SRU format is used for tax declarations.
* Generates a Skatteverket-compliant SRU submission consisting of:
* - INFO.SRU: submitter metadata
* - BLANKETTER.SRU: three blankett blocks (INK2, INK2R, INK2S)
*
* INK2 field codes are the SRU codes directly (7201-7380).
* Encoding: ISO 8859-1 (handled by the API route when writing the response)
* Line endings: CRLF
* Amounts: integers in hela kronor, no decimals, no thousands separators
* Org number: 12 digits with century prefix 16 for juridisk person
*/
/** All INK2 SRU field codes in order */
const INK2_FIELD_CODES: INK2SRUCode[] = [
'7201', '7202', '7203', '7210', '7211', '7212',
'7220', '7221', '7222', '7230', '7231',
'7310', '7320', '7330', '7340', '7350', '7360', '7370', '7380',
]
const CRLF = '\r\n'
const PROGRAM_NAME = 'gnubok'
const PROGRAM_VERSION = '1.0'
/**
* Generate SRU file content from INK2 declaration
* Compute the period suffix for blankett type strings.
* Based on which month the fiscal year ENDS in:
* P1 = Jan-Apr, P2 = May-Aug, P3 = special, P4 = Sep-Dec
*/
export function generateSRUFile(declaration: INK2Declaration): SRUFile {
const records: SRURecord[] = []
const now = new Date()
// File header
records.push({ fieldCode: 'PRODUKT', value: 'KONTROLLUPPGIFTER' })
records.push({ fieldCode: 'SESSION', value: '1' })
records.push({ fieldCode: 'PROGRAMNAMN', value: 'ERPBase' })
records.push({ fieldCode: 'PROGRAMVERSION', value: '1.0' })
records.push({
fieldCode: 'SKAPAT',
value: formatSRUDate(now),
})
// Form declaration
records.push({ fieldCode: 'BLANKETT', value: 'INK2' })
// Company identification
if (declaration.companyInfo.orgNumber) {
const cleanOrgNumber = declaration.companyInfo.orgNumber.replace(/-/g, '')
records.push({
fieldCode: 'IDENTITET',
value: cleanOrgNumber,
})
}
// Fiscal year
records.push({
fieldCode: 'UPPGIFT',
value: `7000 ${formatSRUDateRange(declaration.fiscalYear.start, declaration.fiscalYear.end)}`,
})
// INK2 field values
for (const code of INK2_FIELD_CODES) {
const value = declaration.rutor[code]
if (value !== 0) {
records.push({
fieldCode: 'UPPGIFT',
value: `${code} ${formatSRUAmount(value)}`,
})
}
}
// End of form
records.push({ fieldCode: 'BLANKETTSLUT', value: '' })
return {
records,
generatedAt: now.toISOString(),
}
function computePeriodSuffix(fiscalYearEnd: string): string {
const endMonth = parseInt(fiscalYearEnd.substring(5, 7), 10)
if (endMonth >= 1 && endMonth <= 4) return 'P1'
if (endMonth >= 5 && endMonth <= 8) return 'P2'
// P4 covers Sep-Dec (most common: calendar year companies)
// NOTE: P3 (first/short fiscal year) cannot be derived from end month alone.
// Callers must handle P3 manually for brutet räkenskapsår.
return 'P4'
}
/**
* Convert SRU file to string content
* Get the income year from the fiscal year end date.
* The year in the blankett type string is the income year.
*/
export function sruFileToString(sruFile: SRUFile): string {
const lines: string[] = []
for (const record of sruFile.records) {
if (record.value === '') {
lines.push(`#${record.fieldCode}`)
} else {
lines.push(`#${record.fieldCode} ${record.value}`)
}
}
return lines.join('\r\n') + '\r\n'
function getIncomeYear(fiscalYearEnd: string): string {
return fiscalYearEnd.substring(0, 4)
}
/**
* Format date for SRU: YYYYMMDD
* Format org number as 12-digit with century prefix.
* Swedish juridiska personer use century prefix "16".
* Input: "556677-8899" or "5566778899"
* Output: "165566778899"
*/
function formatSRUDate(date: Date): string {
function formatOrgNumber12(orgNumber: string): string {
const clean = orgNumber.replace(/-/g, '')
if (clean.length === 12) return clean
if (clean.length === 10) return `16${clean}`
return `16${clean}`
}
/**
* Format a Date as YYYYMMDD
*/
function formatDate(date: Date): string {
const y = date.getFullYear()
const m = String(date.getMonth() + 1).padStart(2, '0')
const d = String(date.getDate()).padStart(2, '0')
@@ -99,47 +69,201 @@ function formatSRUDate(date: Date): string {
}
/**
* Format date string (YYYY-MM-DD) to SRU format (YYYYMMDD)
* Format a Date as HHMMSS
*/
function dateStringToSRU(dateStr: string): string {
return dateStr.replace(/-/g, '')
function formatTime(date: Date): string {
const h = String(date.getHours()).padStart(2, '0')
const m = String(date.getMinutes()).padStart(2, '0')
const s = String(date.getSeconds()).padStart(2, '0')
return `${h}${m}${s}`
}
/**
* Format fiscal year date range for SRU
* Format integer amount for SRU. No decimals, no thousands separator.
* Truncated to hela kronor by the engine.
*/
function formatSRUDateRange(startDate: string, endDate: string): string {
return `${dateStringToSRU(startDate)}-${dateStringToSRU(endDate)}`
function formatAmount(amount: number): string {
return Math.trunc(amount).toString()
}
/**
* Format amount for SRU: whole numbers, no thousands separator, negative with minus
* Generate the INFO.SRU file content
*/
function formatSRUAmount(amount: number): string {
return Math.round(amount).toString()
function generateInfoSru(declaration: INK2Declaration, now: Date): string {
const lines: string[] = []
const orgNumber12 = declaration.companyInfo.orgNumber
? formatOrgNumber12(declaration.companyInfo.orgNumber)
: '000000000000'
// DATABESKRIVNING block (required order)
lines.push('#DATABESKRIVNING_START')
lines.push('#PRODUKT SRU')
lines.push(`#SKAPAD ${formatDate(now)} ${formatTime(now)}`)
lines.push(`#PROGRAM ${PROGRAM_NAME} ${PROGRAM_VERSION}`)
lines.push('#FILNAMN BLANKETTER.SRU')
lines.push('#DATABESKRIVNING_SLUT')
// MEDIELEV block
lines.push('#MEDIELEV_START')
lines.push(`#ORGNR ${orgNumber12}`)
lines.push(`#NAMN ${sanitizeString(declaration.companyInfo.companyName)}`)
if (declaration.companyInfo.addressLine1) {
lines.push(`#ADRESS ${sanitizeString(declaration.companyInfo.addressLine1)}`)
}
lines.push(`#POSTNR ${declaration.companyInfo.postalCode || '00000'}`)
lines.push(`#POSTORT ${sanitizeString(declaration.companyInfo.city || 'Okänd')}`)
if (declaration.companyInfo.email) {
lines.push(`#EMAIL ${declaration.companyInfo.email}`)
}
lines.push('#MEDIELEV_SLUT')
return lines.join(CRLF) + CRLF
}
/**
* Validate SRU file content
* Generate the BLANKETTER.SRU file content with three blankett blocks
*/
export function validateSRUFile(sruFile: SRUFile): {
function generateBlanketterSru(declaration: INK2Declaration, now: Date): string {
const lines: string[] = []
const orgNumber12 = declaration.companyInfo.orgNumber
? formatOrgNumber12(declaration.companyInfo.orgNumber)
: '000000000000'
const incomeYear = getIncomeYear(declaration.fiscalYear.end)
const periodSuffix = computePeriodSuffix(declaration.fiscalYear.end)
const companyName = sanitizeString(declaration.companyInfo.companyName)
const dateStr = formatDate(now)
// Each blankett gets a unique timestamp (increment seconds)
const time0 = formatTime(now)
const time1 = formatTime(new Date(now.getTime() + 1000))
const time2 = formatTime(new Date(now.getTime() + 2000))
// ---- Block 1: INK2 (huvudblankett) ----
lines.push(`#BLANKETT INK2-${incomeYear}${periodSuffix}`)
lines.push(`#IDENTITET ${orgNumber12} ${dateStr} ${time0}`)
lines.push(`#NAMN ${companyName}`)
// Fiscal year dates
lines.push(`#UPPGIFT 7011 ${declaration.ink2['7011']}`)
lines.push(`#UPPGIFT 7012 ${declaration.ink2['7012']}`)
// Överskott/underskott
if (declaration.ink2['7113'] > 0) {
lines.push(`#UPPGIFT 7113 ${formatAmount(declaration.ink2['7113'])}`)
}
if (declaration.ink2['7114'] > 0) {
lines.push(`#UPPGIFT 7114 ${formatAmount(declaration.ink2['7114'])}`)
}
lines.push('#BLANKETTSLUT')
// ---- Block 2: INK2R (räkenskapsschema) ----
lines.push(`#BLANKETT INK2R-${incomeYear}${periodSuffix}`)
lines.push(`#IDENTITET ${orgNumber12} ${dateStr} ${time1}`)
lines.push(`#NAMN ${companyName}`)
// Fiscal year dates
lines.push(`#UPPGIFT 7011 ${declaration.ink2['7011']}`)
lines.push(`#UPPGIFT 7012 ${declaration.ink2['7012']}`)
// All INK2R fields — emit non-zero values only
const ink2rCodes: INK2RSRUCode[] = Object.keys(declaration.ink2r) as INK2RSRUCode[]
for (const code of ink2rCodes) {
const value = declaration.ink2r[code]
if (value !== 0) {
lines.push(`#UPPGIFT ${code} ${formatAmount(value)}`)
}
}
lines.push('#BLANKETTSLUT')
// ---- Block 3: INK2S (skattemässiga justeringar) ----
lines.push(`#BLANKETT INK2S-${incomeYear}${periodSuffix}`)
lines.push(`#IDENTITET ${orgNumber12} ${dateStr} ${time2}`)
lines.push(`#NAMN ${companyName}`)
// Fiscal year dates
lines.push(`#UPPGIFT 7011 ${declaration.ink2s['7011']}`)
lines.push(`#UPPGIFT 7012 ${declaration.ink2s['7012']}`)
// INK2S numeric fields — emit non-zero values only
const ink2sNumericFields: (keyof INK2SRutor)[] = ['7650', '7750', '7651', '8020', '8021']
for (const code of ink2sNumericFields) {
const value = declaration.ink2s[code]
if (typeof value === 'number' && value !== 0) {
lines.push(`#UPPGIFT ${code} ${formatAmount(value)}`)
}
}
lines.push('#BLANKETTSLUT')
// Required terminator
lines.push('#FIL_SLUT')
return lines.join(CRLF) + CRLF
}
/**
* Sanitize string for SRU: remove # characters (reserved), limit to 250 chars
*/
function sanitizeString(str: string): string {
return str.replace(/#/g, '').replace(/[\r\n]/g, ' ').substring(0, 250)
}
/**
* Generate complete SRU submission (INFO.SRU + BLANKETTER.SRU)
*/
export function generateSRUSubmission(declaration: INK2Declaration): SRUSubmission {
const now = new Date()
return {
infoSru: generateInfoSru(declaration, now),
blanketterSru: generateBlanketterSru(declaration, now),
generatedAt: now.toISOString(),
}
}
/**
* Validate the generated BLANKETTER.SRU content
*/
export function validateBlanketterSru(content: string): {
isValid: boolean
errors: string[]
} {
const errors: string[] = []
const hasHeader = sruFile.records.some(r => r.fieldCode === 'PRODUKT')
const hasBlankett = sruFile.records.some(r => r.fieldCode === 'BLANKETT')
const hasBlankettslut = sruFile.records.some(r => r.fieldCode === 'BLANKETTSLUT')
// Check for required blankett blocks
const hasINK2 = /^#BLANKETT INK2-/m.test(content)
const hasINK2R = /^#BLANKETT INK2R-/m.test(content)
const hasINK2S = /^#BLANKETT INK2S-/m.test(content)
const hasFilSlut = /^#FIL_SLUT/m.test(content)
if (!hasHeader) errors.push('Missing PRODUKT header')
if (!hasBlankett) errors.push('Missing BLANKETT declaration')
if (!hasBlankettslut) errors.push('Missing BLANKETTSLUT')
if (!hasINK2) errors.push('Missing INK2 blankett block')
if (!hasINK2R) errors.push('Missing INK2R blankett block')
if (!hasINK2S) errors.push('Missing INK2S blankett block')
if (!hasFilSlut) errors.push('Missing #FIL_SLUT terminator')
// Verify it's INK2
const blankettRecord = sruFile.records.find(r => r.fieldCode === 'BLANKETT')
if (blankettRecord && blankettRecord.value !== 'INK2') {
errors.push(`Expected BLANKETT INK2, got ${blankettRecord.value}`)
// Count BLANKETTSLUT — should be exactly 3
const blankettslutCount = (content.match(/^#BLANKETTSLUT/gm) || []).length
if (blankettslutCount !== 3) {
errors.push(`Expected 3 BLANKETTSLUT, found ${blankettslutCount}`)
}
// Check that each blankett has #IDENTITET
const blankettBlocks = content.split(/^#BLANKETT /m).slice(1)
for (const block of blankettBlocks) {
if (!block.includes('#IDENTITET')) {
const type = block.split('\n')[0]?.split('\r')[0] || 'unknown'
errors.push(`Blankett ${type} missing #IDENTITET`)
}
if (!block.includes('#NAMN')) {
const type = block.split('\n')[0]?.split('\r')[0] || 'unknown'
errors.push(`Blankett ${type} missing #NAMN`)
}
}
return {
@@ -149,10 +273,10 @@ export function validateSRUFile(sruFile: SRUFile): {
}
/**
* Get filename for SRU file download
* Get ZIP filename for download
*/
export function getSRUFilename(declaration: INK2Declaration): string {
export function getZipFilename(declaration: INK2Declaration): string {
const year = declaration.fiscalYear.start.substring(0, 4)
const orgNumber = declaration.companyInfo.orgNumber?.replace(/-/g, '') || 'unknown'
return `INK2_${orgNumber}_${year}.sru`
return `INK2_SRU_${orgNumber}_${year}.zip`
}
+245 -62
View File
@@ -1,36 +1,121 @@
// INK2 declaration rutor (fields) keyed by SRU code
export interface INK2DeclarationRutor {
// Balance sheet - Assets
'7201': number // Immateriella anläggningstillgångar
'7202': number // Materiella anläggningstillgångar
'7203': number // Finansiella anläggningstillgångar
'7210': number // Varulager m.m.
'7211': number // Kundfordringar
'7212': number // Övriga omsättningstillgångar
/**
* INK2R — Räkenskapsschema (balance sheet + income statement)
* Field codes per Skatteverket spec and bas.se/kontoplaner/sru/
*/
export interface INK2RRutor {
// Balance sheet - Assets (Tillgångar)
'7201': number // 2.1 Koncessioner, patent, licenser, varumärken, hyresrätter, goodwill
'7202': number // 2.2 Förskott avs. immateriella anläggningstillgångar
'7214': number // 2.3 Byggnader och mark
'7215': number // 2.4 Maskiner, inventarier, övriga materiella anläggningstillgångar
'7216': number // 2.5 Förbättringsutgifter på annans fastighet
'7217': number // 2.6 Pågående nyanläggningar, förskott materiella anläggningstillgångar
'7230': number // 2.7 Andelar i koncernföretag
'7231': number // 2.8 Andelar i intresseföretag och gemensamt styrda företag
'7233': number // 2.9 Ägarintressen i övriga företag + andra långfristiga värdepapper
'7232': number // 2.10 Fordringar hos koncern-/intresse-/gemensamt styrda företag
'7234': number // 2.11 Lån till delägare eller närstående
'7235': number // 2.12 Fordringar hos övriga + andra långfristiga fordringar
'7241': number // 2.13 Råvaror och förnödenheter
'7242': number // 2.14 Varor under tillverkning
'7243': number // 2.15 Färdiga varor och handelsvaror
'7244': number // 2.16 Övriga lagertillgångar
'7245': number // 2.17 Pågående arbeten för annans räkning
'7246': number // 2.18 Förskott till leverantörer
'7251': number // 2.19 Kundfordringar
'7252': number // 2.20 Fordringar koncern/intresse (kortfristiga)
'7261': number // 2.21 Fordringar övriga + övriga fordringar
'7262': number // 2.22 Upparbetad men ej fakturerad intäkt
'7263': number // 2.23 Förutbetalda kostnader och upplupna intäkter
'7270': number // 2.24 Andelar i koncernföretag (kortfristiga)
'7271': number // 2.25 Övriga kortfristiga placeringar
'7281': number // 2.26 Kassa, bank och redovisningsmedel
// Balance sheet - Equity & Liabilities
'7220': number // Aktiekapital
'7221': number // Övrigt eget kapital
'7222': number // Årets resultat
'7230': number // Obeskattade reserver, avsättningar och skulder
'7231': number // Övriga skulder
// Balance sheet - Equity & Liabilities (Eget kapital och skulder)
'7301': number // 2.27 Bundet eget kapital
'7302': number // 2.28 Fritt eget kapital
'7321': number // 2.29 Periodiseringsfonder
'7322': number // 2.30 Ackumulerade överavskrivningar
'7323': number // 2.31 Övriga obeskattade reserver
'7331': number // 2.32 Avsättningar för pensioner enl. tryggandelagen
'7332': number // 2.33 Övriga avsättningar för pensioner
'7333': number // 2.34 Övriga avsättningar
'7350': number // 2.35 Obligationslån
'7351': number // 2.36 Checkräkningskredit (långfristig)
'7352': number // 2.37 Övriga skulder till kreditinstitut (långfristiga)
'7353': number // 2.38 Skulder koncern/intresse (långfristiga)
'7354': number // 2.39 Övriga skulder (långfristiga)
'7360': number // 2.40 Checkräkningskredit (kortfristig)
'7361': number // 2.41 Övriga skulder till kreditinstitut (kortfristiga)
'7362': number // 2.42 Förskott från kunder
'7363': number // 2.43 Pågående arbeten (skuldsida)
'7364': number // 2.44 Fakturerad men ej upparbetad intäkt
'7365': number // 2.45 Leverantörsskulder
'7366': number // 2.46 Växelskulder
'7367': number // 2.47 Skulder koncern/intresse (kortfristiga)
'7369': number // 2.48 Övriga skulder (kortfristiga)
'7368': number // 2.49 Skatteskulder
'7370': number // 2.50 Upplupna kostnader och förutbetalda intäkter
// Income statement
'7310': number // Nettoomsättning
'7320': number // Varuinköp/direkta kostnader
'7330': number // Övriga externa kostnader
'7340': number // Personalkostnader
'7350': number // Avskrivningar
'7360': number // Övriga rörelsekostnader
'7370': number // Finansiella poster (netto)
'7380': number // Extraordinära poster (netto)
// Income statement (Resultaträkning)
'7410': number // 3.1 Nettoomsättning
'7411': number // 3.2 Förändring av lager
'7412': number // 3.3 Aktiverat arbete för egen räkning
'7413': number // 3.4 Övriga rörelseintäkter
'7511': number // 3.5 Råvaror och förnödenheter
'7512': number // 3.6 Handelsvaror
'7513': number // 3.7 Övriga externa kostnader
'7514': number // 3.8 Personalkostnader
'7515': number // 3.9 Av- och nedskrivningar materiella/immateriella
'7516': number // 3.10 Nedskrivningar omsättningstillgångar
'7517': number // 3.11 Övriga rörelsekostnader
'7414': number // 3.12 Resultat från andelar i koncernföretag
'7415': number // 3.13 Resultat från andelar i intresseföretag
'7423': number // 3.14 Resultat från övriga företag med ägarintresse
'7416': number // 3.15 Resultat från övriga finansiella anläggningstillgångar
'7417': number // 3.16 Övriga ränteintäkter och liknande
'7521': number // 3.17 Nedskrivningar finansiella anläggningstillgångar
'7522': number // 3.18 Räntekostnader och liknande
'7524': number // 3.19 Lämnade koncernbidrag
'7419': number // 3.20 Mottagna koncernbidrag
'7420': number // 3.21 Återföring av periodiseringsfond
'7525': number // 3.22 Avsättning till periodiseringsfond
'7421': number // 3.23 Förändring av överavskrivningar
'7422': number // 3.24 Övriga bokslutsdispositioner
'7528': number // 3.25 Skatt på årets resultat
'7450': number // 3.26 Årets resultat, vinst (positive)
'7550': number // 3.27 Årets resultat, förlust (positive = loss)
}
export type INK2SRUCode = keyof INK2DeclarationRutor
export type INK2RSRUCode = keyof INK2RRutor
// Account mapping configuration for INK2 declaration
/**
* INK2 — Huvudblankett (main declaration, page 1)
*/
export interface INK2Rutor {
'7011': string // Räkenskapsår fr.o.m. (YYYYMMDD)
'7012': string // Räkenskapsår t.o.m. (YYYYMMDD)
'7113': number // 1.1 Överskott av näringsverksamhet
'7114': number // 1.2 Underskott av näringsverksamhet
}
/**
* INK2S — Skattemässiga justeringar (page 4)
* Auto-derived fields only. Manual tax adjustments are handled by the accountant.
*/
export interface INK2SRutor {
'7011': string // Räkenskapsår fr.o.m. (YYYYMMDD)
'7012': string // Räkenskapsår t.o.m. (YYYYMMDD)
'7650': number // 4.1 Årets resultat, vinst
'7750': number // 4.2 Årets resultat, förlust
'7651': number // 4.3a Skatt på årets resultat (ej avdragsgill)
'8020': number // 4.15 Överskott → punkt 1.1
'8021': number // 4.16 Underskott → punkt 1.2
}
// Account mapping configuration for INK2R
export interface INK2AccountMapping {
sruCode: INK2SRUCode
sruCode: INK2RSRUCode
description: string
section: 'assets' | 'equity_liabilities' | 'income_statement'
normalBalance: 'debit' | 'credit' | 'net'
@@ -41,7 +126,17 @@ export interface INK2AccountMapping {
}>
}
// INK2 declaration response
// Company info for SRU file generation
export interface INK2CompanyInfo {
companyName: string
orgNumber: string | null
addressLine1: string | null
postalCode: string | null
city: string | null
email: string | null
}
// INK2 declaration response (includes all three blankett sections)
export interface INK2Declaration {
fiscalYear: {
id: string
@@ -50,8 +145,10 @@ export interface INK2Declaration {
end: string
isClosed: boolean
}
rutor: INK2DeclarationRutor
breakdown: Record<INK2SRUCode, {
ink2: INK2Rutor
ink2r: INK2RRutor
ink2s: INK2SRutor
breakdown: Record<INK2RSRUCode, {
accounts: Array<{
accountNumber: string
accountName: string
@@ -65,40 +162,126 @@ export interface INK2Declaration {
operatingResult: number
resultAfterFinancial: number
}
companyInfo: {
companyName: string
orgNumber: string | null
}
companyInfo: INK2CompanyInfo
warnings: string[]
}
// Reuse SRU file types from NE-bilaga
export type { SRURecord, SRUFile } from '@/lib/reports/ne-bilaga/types'
// Labels for INK2 rutor
export const INK2_RUTA_LABELS: Record<INK2SRUCode, string> = {
'7201': 'Immateriella anläggningstillgångar',
'7202': 'Materiella anläggningstillgångar',
'7203': 'Finansiella anläggningstillgångar',
'7210': 'Varulager m.m.',
'7211': 'Kundfordringar',
'7212': 'Övriga omsättningstillgångar',
'7220': 'Aktiekapital',
'7221': 'Övrigt eget kapital',
'7222': 'Årets resultat',
'7230': 'Obeskattade reserver, avsättningar och skulder',
'7231': 'Övriga skulder',
'7310': 'Nettoomsättning',
'7320': 'Varuinköp/direkta kostnader',
'7330': 'Övriga externa kostnader',
'7340': 'Personalkostnader',
'7350': 'Avskrivningar',
'7360': 'Övriga rörelsekostnader',
'7370': 'Finansiella poster (netto)',
'7380': 'Extraordinära poster (netto)',
// SRU file types — no longer shared with NE-bilaga since the structure
// is fundamentally different (INFO.SRU + BLANKETTER.SRU two-file format)
export interface SRUSubmission {
infoSru: string
blanketterSru: string
generatedAt: string
}
// Section groupings for UI display
export const INK2_ASSET_CODES: INK2SRUCode[] = ['7201', '7202', '7203', '7210', '7211', '7212']
export const INK2_EQUITY_LIABILITY_CODES: INK2SRUCode[] = ['7220', '7221', '7222', '7230', '7231']
export const INK2_INCOME_STATEMENT_CODES: INK2SRUCode[] = ['7310', '7320', '7330', '7340', '7350', '7360', '7370', '7380']
// ---- UI display helpers ----
export const INK2R_ASSET_CODES: INK2RSRUCode[] = [
'7201', '7202', '7214', '7215', '7216', '7217',
'7230', '7231', '7233', '7232', '7234', '7235',
'7241', '7242', '7243', '7244', '7245', '7246',
'7251', '7252', '7261', '7262', '7263',
'7270', '7271', '7281',
]
export const INK2R_EQUITY_LIABILITY_CODES: INK2RSRUCode[] = [
'7301', '7302',
'7321', '7322', '7323',
'7331', '7332', '7333',
'7350', '7351', '7352', '7353', '7354',
'7360', '7361', '7362', '7363', '7364', '7365', '7366', '7367', '7369', '7368',
'7370',
]
export const INK2R_INCOME_CODES: INK2RSRUCode[] = [
'7410', '7411', '7412', '7413',
'7511', '7512', '7513', '7514', '7515', '7516', '7517',
'7414', '7415', '7423', '7416', '7417',
'7521', '7522',
'7524', '7419', '7420', '7525', '7421', '7422',
'7528',
'7450', '7550',
]
export const INK2R_RUTA_LABELS: Record<INK2RSRUCode, string> = {
// Assets
'7201': 'Koncessioner, patent, licenser, varumärken, goodwill',
'7202': 'Förskott immateriella anläggningstillgångar',
'7214': 'Byggnader och mark',
'7215': 'Maskiner och inventarier',
'7216': 'Förbättringsutgifter på annans fastighet',
'7217': 'Pågående nyanläggningar och förskott',
'7230': 'Andelar i koncernföretag',
'7231': 'Andelar i intresseföretag',
'7233': 'Ägarintressen i övriga företag',
'7232': 'Fordringar koncern-/intresseföretag',
'7234': 'Lån till delägare eller närstående',
'7235': 'Övriga långfristiga fordringar',
'7241': 'Råvaror och förnödenheter',
'7242': 'Varor under tillverkning',
'7243': 'Färdiga varor och handelsvaror',
'7244': 'Övriga lagertillgångar',
'7245': 'Pågående arbeten för annans räkning',
'7246': 'Förskott till leverantörer',
'7251': 'Kundfordringar',
'7252': 'Fordringar koncern/intresse (kortfristiga)',
'7261': 'Övriga fordringar',
'7262': 'Upparbetad men ej fakturerad intäkt',
'7263': 'Förutbetalda kostnader och upplupna intäkter',
'7270': 'Andelar i koncernföretag (kortfristiga)',
'7271': 'Övriga kortfristiga placeringar',
'7281': 'Kassa, bank och redovisningsmedel',
// Equity & Liabilities
'7301': 'Bundet eget kapital',
'7302': 'Fritt eget kapital',
'7321': 'Periodiseringsfonder',
'7322': 'Ackumulerade överavskrivningar',
'7323': 'Övriga obeskattade reserver',
'7331': 'Pensionsavsättningar (tryggandelagen)',
'7332': 'Övriga pensionsavsättningar',
'7333': 'Övriga avsättningar',
'7350': 'Obligationslån',
'7351': 'Checkräkningskredit (långfristig)',
'7352': 'Övriga skulder kreditinstitut (långfristiga)',
'7353': 'Skulder koncern/intresse (långfristiga)',
'7354': 'Övriga skulder (långfristiga)',
'7360': 'Checkräkningskredit (kortfristig)',
'7361': 'Övriga skulder kreditinstitut (kortfristiga)',
'7362': 'Förskott från kunder',
'7363': 'Pågående arbeten (skuldsida)',
'7364': 'Fakturerad men ej upparbetad intäkt',
'7365': 'Leverantörsskulder',
'7366': 'Växelskulder',
'7367': 'Skulder koncern/intresse (kortfristiga)',
'7369': 'Övriga skulder (kortfristiga)',
'7368': 'Skatteskulder',
'7370': 'Upplupna kostnader och förutbetalda intäkter',
// Income statement
'7410': 'Nettoomsättning',
'7411': 'Förändring av lager',
'7412': 'Aktiverat arbete för egen räkning',
'7413': 'Övriga rörelseintäkter',
'7511': 'Råvaror och förnödenheter',
'7512': 'Handelsvaror',
'7513': 'Övriga externa kostnader',
'7514': 'Personalkostnader',
'7515': 'Av- och nedskrivningar',
'7516': 'Nedskrivningar omsättningstillgångar',
'7517': 'Övriga rörelsekostnader',
'7414': 'Resultat andelar koncernföretag',
'7415': 'Resultat andelar intresseföretag',
'7423': 'Resultat övriga ägarintresse',
'7416': 'Övriga finansiella anläggningstillgångar',
'7417': 'Ränteintäkter',
'7521': 'Nedskrivningar finansiella anläggningstillgångar',
'7522': 'Räntekostnader',
'7524': 'Lämnade koncernbidrag',
'7419': 'Mottagna koncernbidrag',
'7420': 'Återföring av periodiseringsfond',
'7525': 'Avsättning till periodiseringsfond',
'7421': 'Förändring av överavskrivningar',
'7422': 'Övriga bokslutsdispositioner',
'7528': 'Skatt på årets resultat',
'7450': 'Årets resultat (vinst)',
'7550': 'Årets resultat (förlust)',
}
@@ -0,0 +1,2 @@
-- ML 17:24 p.7: leveransdatum when different from fakturadatum
ALTER TABLE public.invoices ADD COLUMN IF NOT EXISTS delivery_date date;
+1
View File
@@ -328,6 +328,7 @@ export function makeInvoice(overrides: Partial<Invoice> = {}): Invoice {
invoice_number: 'F-2024001',
invoice_date: '2024-06-15',
due_date: '2024-07-15',
delivery_date: null,
status: 'draft',
currency: 'SEK',
exchange_rate: null,
+1
View File
@@ -546,6 +546,7 @@ export interface Invoice {
// Dates
invoice_date: string
due_date: string
delivery_date: string | null
// Status
status: InvoiceStatus