520 lines
16 KiB
Markdown
520 lines
16 KiB
Markdown
# Future Features & Regulatory Integrations
|
||
|
||
## Phase 2: SIE File Import (Migration)
|
||
|
||
### Overview
|
||
|
||
SIE (Standard Import Export) is the Swedish standard for accounting data exchange. Users migrating from Fortnox, Visma, or other systems can export their data as SIE files.
|
||
|
||
### SIE File Types
|
||
|
||
| Type | Content | Use Case |
|
||
|------|---------|----------|
|
||
| SIE1 | Årssaldon | Year-end balances only |
|
||
| SIE2 | Periodsaldon | Monthly/periodic balances |
|
||
| SIE3 | Objektsaldon | Balances with cost centers |
|
||
| SIE4 | Transaktioner | Full transaction history |
|
||
|
||
**Recommended:** Support SIE4 for complete migration, SIE1/2 for simpler imports.
|
||
|
||
### Implementation Notes
|
||
|
||
```typescript
|
||
// lib/import/sie-parser.ts
|
||
|
||
interface SIETransaction {
|
||
verifikationsnummer: string
|
||
datum: Date
|
||
kontonummer: string
|
||
belopp: number
|
||
text: string
|
||
}
|
||
|
||
interface SIEImportResult {
|
||
company: {
|
||
name: string
|
||
orgNumber: string
|
||
fiscalYearStart: Date
|
||
}
|
||
accounts: Array<{ number: string; name: string }>
|
||
transactions: SIETransaction[]
|
||
balances: Array<{ account: string; balance: number }>
|
||
}
|
||
|
||
// SIE files use CP437 encoding and specific format
|
||
// Example line: #VER A 1 20240115 "Faktura 1001"
|
||
```
|
||
|
||
### Mapping Decisions Needed
|
||
|
||
Before implementing, determine mapping for:
|
||
- BAS account numbers → system categories
|
||
- Opening balances → how to represent
|
||
- Customer/supplier data (if present in SIE)
|
||
- Historical invoices → create as "imported" status?
|
||
|
||
---
|
||
|
||
## Phase 2: Benefits & Gifts Module (Förmånshantering)
|
||
|
||
### Problem
|
||
|
||
Influencers receive products ("PR-utskick", "gifted collabs") that may be taxable income. Skatteverket actively audits this. Getting it wrong = skattetillägg.
|
||
|
||
### Decision Tree Logic
|
||
|
||
```typescript
|
||
// lib/benefits/gift-classifier.ts
|
||
|
||
interface GiftClassification {
|
||
taxable: boolean
|
||
marketValue: number
|
||
deductibleAsExpense: boolean
|
||
bookingType: 'income' | 'income_and_expense' | 'tax_free'
|
||
reasoning: string
|
||
}
|
||
|
||
interface GiftInput {
|
||
estimatedValue: number
|
||
hasMotprestation: boolean // Required post/video/mention?
|
||
usedInBusiness: boolean // Used as props/equipment?
|
||
usedPrivately: boolean // Personal use?
|
||
isSimplePromoItem: boolean // Pen, mug, basic merch?
|
||
}
|
||
|
||
const TAX_FREE_PROMO_THRESHOLD = 450 // SEK, Skatteverket guideline
|
||
|
||
export function classifyGift(input: GiftInput): GiftClassification {
|
||
// Rule 1: Simple promotional items under threshold = tax free
|
||
if (input.isSimplePromoItem && input.estimatedValue <= TAX_FREE_PROMO_THRESHOLD && !input.hasMotprestation) {
|
||
return {
|
||
taxable: false,
|
||
marketValue: input.estimatedValue,
|
||
deductibleAsExpense: false,
|
||
bookingType: 'tax_free',
|
||
reasoning: 'Enklare reklamgåva under 450 kr utan krav på motprestation'
|
||
}
|
||
}
|
||
|
||
// Rule 2: Motprestation exists = always taxable
|
||
if (input.hasMotprestation) {
|
||
if (input.usedInBusiness && !input.usedPrivately) {
|
||
return {
|
||
taxable: true,
|
||
marketValue: input.estimatedValue,
|
||
deductibleAsExpense: true,
|
||
bookingType: 'income_and_expense',
|
||
reasoning: 'Skattepliktig förmån (motprestation krävdes). Avdragsgill som rekvisita då den endast används i verksamheten.'
|
||
}
|
||
}
|
||
|
||
return {
|
||
taxable: true,
|
||
marketValue: input.estimatedValue,
|
||
deductibleAsExpense: false,
|
||
bookingType: 'income',
|
||
reasoning: 'Skattepliktig förmån (motprestation krävdes). Ej avdragsgill då produkten används privat.'
|
||
}
|
||
}
|
||
|
||
// Rule 3: High value without motprestation but used privately
|
||
if (input.estimatedValue > TAX_FREE_PROMO_THRESHOLD && input.usedPrivately) {
|
||
return {
|
||
taxable: true,
|
||
marketValue: input.estimatedValue,
|
||
deductibleAsExpense: false,
|
||
bookingType: 'income',
|
||
reasoning: 'Värdet överstiger gränsen för skattefria reklamgåvor och produkten används privat.'
|
||
}
|
||
}
|
||
|
||
// Default: taxable if significant value
|
||
return {
|
||
taxable: input.estimatedValue > TAX_FREE_PROMO_THRESHOLD,
|
||
marketValue: input.estimatedValue,
|
||
deductibleAsExpense: input.usedInBusiness,
|
||
bookingType: input.estimatedValue > TAX_FREE_PROMO_THRESHOLD ? 'income' : 'tax_free',
|
||
reasoning: 'Klassificering baserad på värde och användning'
|
||
}
|
||
}
|
||
```
|
||
|
||
### UI Flow
|
||
|
||
1. User taps "Logga gåva/produkt"
|
||
2. Photo capture or manual entry
|
||
3. System queries price APIs (Prisjakt, Google Shopping) for value estimate
|
||
4. Decision tree questions:
|
||
- "Fanns krav på att du skulle posta om denna produkt?" [Ja/Nej]
|
||
- "Använder du produkten i din content-produktion?" [Ja/Nej]
|
||
- "Använder du produkten privat?" [Ja/Nej]
|
||
5. System shows classification + tax impact
|
||
6. Auto-booking to correct accounts
|
||
|
||
---
|
||
|
||
## Phase 2: NE-bilaga Generation
|
||
|
||
### SRU File Format
|
||
|
||
Skatteverket accepts SRU (Standardiserat RäkenskapsUtdrag) files for tax form import.
|
||
|
||
```typescript
|
||
// lib/tax/sru-generator.ts
|
||
|
||
interface NEBilaga {
|
||
// R1-series: Income
|
||
R1_nettoomsattning: number // Net revenue
|
||
R2_ovriga_intakter: number // Other income
|
||
|
||
// R3-series: Costs
|
||
R3_varor: number // Cost of goods
|
||
R4_ovriga_kostnader: number // Other expenses
|
||
R5_personal: number // Personnel costs (usually 0 for sole proprietor)
|
||
R6_avskrivningar: number // Depreciation
|
||
|
||
// R7-series: Financial
|
||
R7_ranteintakter: number
|
||
R8_rantekostnader: number
|
||
|
||
// Result
|
||
R9_resultat: number // Profit/loss before adjustments
|
||
|
||
// Adjustments
|
||
R10_periodiseringsfond_avsatt: number // Max 30% of profit
|
||
R11_periodiseringsfond_aterforing: number
|
||
R12_expansionsfond: number
|
||
|
||
// Final
|
||
R14_overskott_underskott: number
|
||
}
|
||
|
||
function generateSRUFile(data: NEBilaga, personnummer: string, year: number): string {
|
||
const lines: string[] = []
|
||
|
||
// Header
|
||
lines.push('#DATABESKRIVNING_START')
|
||
lines.push('#PRODUKT SIE')
|
||
lines.push('#FORMAT PC8')
|
||
lines.push('#FILTYP NARINGSBILAGA')
|
||
lines.push(`#UPPGIFTSLAMNARE ${personnummer}`)
|
||
lines.push(`#INKOMSTAR ${year}`)
|
||
lines.push('#DATABESKRIVNING_SLUT')
|
||
|
||
// Data rows (simplified)
|
||
lines.push(`#UPPGIFT 7001 ${data.R1_nettoomsattning}`) // R1
|
||
lines.push(`#UPPGIFT 7002 ${data.R2_ovriga_intakter}`) // R2
|
||
// ... continue for all fields
|
||
|
||
lines.push('#UPPGIFT_SLUT')
|
||
|
||
return lines.join('\n')
|
||
}
|
||
```
|
||
|
||
### BAS Account to NE Mapping
|
||
|
||
| BAS Account | Description | NE Ruta |
|
||
|-------------|-------------|---------|
|
||
| 3000-3999 | Intäkter | R1 |
|
||
| 3900-3999 | Övriga rörelseintäkter | R2 |
|
||
| 4000-4999 | Varuinköp | R3 |
|
||
| 5000-6999 | Övriga externa kostnader | R4 |
|
||
| 7000-7699 | Personalkostnader | R5 |
|
||
| 7800-7899 | Avskrivningar | R6 |
|
||
| 8300-8399 | Ränteintäkter | R7 |
|
||
| 8400-8499 | Räntekostnader | R8 |
|
||
|
||
### Tax Optimization Suggestions
|
||
|
||
Before generating NE-bilaga, system should suggest:
|
||
|
||
1. **Periodiseringsfond** (max 30% of profit)
|
||
- Defer tax to future years
|
||
- Must be reversed within 6 years
|
||
- Good for income smoothing
|
||
|
||
2. **Expansionsfond**
|
||
- Tax profit at 22% corporate rate instead of marginal personal rate
|
||
- Good if marginal rate > 22%
|
||
- More complex rules
|
||
|
||
```typescript
|
||
function suggestTaxOptimization(profit: number, marginalTaxRate: number) {
|
||
const suggestions = []
|
||
|
||
if (profit > 0) {
|
||
const maxPeriodisering = profit * 0.30
|
||
suggestions.push({
|
||
type: 'periodiseringsfond',
|
||
amount: maxPeriodisering,
|
||
taxSaved: maxPeriodisering * marginalTaxRate,
|
||
description: `Sätt av ${formatSEK(maxPeriodisering)} till periodiseringsfond för att skjuta upp ${formatSEK(maxPeriodisering * marginalTaxRate)} i skatt.`
|
||
})
|
||
}
|
||
|
||
if (profit > 100000 && marginalTaxRate > 0.30) {
|
||
suggestions.push({
|
||
type: 'expansionsfond',
|
||
description: 'Din marginalskatt är hög. Överväg expansionsfond för att beskattas med 22% istället.'
|
||
})
|
||
}
|
||
|
||
return suggestions
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## Phase 2b: AB Salary Optimization (3:12-reglerna)
|
||
|
||
### Overview
|
||
|
||
For aktiebolag owners, the split between lön (salary) and utdelning (dividend) significantly impacts total tax. The 3:12 rules (Inkomstskattelagen kapitel 57) determine how much dividend can be taxed at the favorable 20% rate vs marginal income tax rates.
|
||
|
||
### Key Concepts
|
||
|
||
| Term | Description |
|
||
|------|-------------|
|
||
| Gränsbelopp | Annual limit for dividend taxed at 20% |
|
||
| Löneunderlag | Salary base that increases gränsbelopp |
|
||
| Kvalificerade andelar | Shares where owner is "active" in company |
|
||
| Sparat utdelningsutrymme | Unused gränsbelopp carried forward |
|
||
|
||
### Gränsbelopp Calculation (Simplified)
|
||
|
||
```typescript
|
||
// lib/tax/ab-optimization.ts
|
||
|
||
interface GransbeloppCalculation {
|
||
schablonbelopp: number // 2.75 × inkomstbasbelopp
|
||
lonebaserat: number // 50% of löneunderlag (if meets salary requirement)
|
||
sparat: number // Carried forward from previous years
|
||
totalGransbelopp: number
|
||
}
|
||
|
||
const INKOMSTBASBELOPP_2024 = 74300
|
||
|
||
function calculateGransbelopp(
|
||
ownerSalary: number,
|
||
totalCompanySalaries: number,
|
||
sparatUtdelningsutrymme: number
|
||
): GransbeloppCalculation {
|
||
// Schablonbelopp: 2.75 × IBB
|
||
const schablonbelopp = 2.75 * INKOMSTBASBELOPP_2024 // ~204k
|
||
|
||
// Lönebaserat: requires owner salary ≥ 6 IBB (or 9.6% of total + 6 IBB)
|
||
const minOwnerSalary = Math.min(
|
||
6 * INKOMSTBASBELOPP_2024,
|
||
0.096 * totalCompanySalaries + 6 * INKOMSTBASBELOPP_2024
|
||
)
|
||
|
||
const lonebaserat = ownerSalary >= minOwnerSalary
|
||
? totalCompanySalaries * 0.50
|
||
: 0
|
||
|
||
return {
|
||
schablonbelopp,
|
||
lonebaserat,
|
||
sparat: sparatUtdelningsutrymme,
|
||
totalGransbelopp: schablonbelopp + lonebaserat + sparatUtdelningsutrymme
|
||
}
|
||
}
|
||
```
|
||
|
||
### Optimization Engine
|
||
|
||
```typescript
|
||
interface OptimizationRecommendation {
|
||
recommendedSalary: number
|
||
recommendedDividend: number
|
||
taxOnSalary: number
|
||
taxOnDividend: number
|
||
totalTax: number
|
||
savingsVsAllSalary: number
|
||
explanation: string
|
||
}
|
||
|
||
function optimizeSalaryDividendSplit(
|
||
availableProfit: number,
|
||
ownerMarginalTaxRate: number,
|
||
sparatUtdelningsutrymme: number
|
||
): OptimizationRecommendation {
|
||
// Consider:
|
||
// - Minimum salary for lönebaserat gränsbelopp (6 IBB = ~446k)
|
||
// - Arbetsgivaravgifter (31.42%) on salary
|
||
// - 20% tax on dividend within gränsbelopp
|
||
// - Marginal tax on dividend above gränsbelopp
|
||
// - Bolagsskatt (20.6%) already paid on profit
|
||
|
||
// Return optimal split with explanation
|
||
}
|
||
```
|
||
|
||
### UI Concept
|
||
|
||
```
|
||
┌─────────────────────────────────────────────────────────────┐
|
||
│ 💡 OPTIMERINGSFÖRSLAG │
|
||
│ │
|
||
│ Baserat på ditt resultat och skattesituation: │
|
||
│ │
|
||
│ Rekommenderad lön: 445 800 kr/år │
|
||
│ Rekommenderad utdelning: 204 325 kr │
|
||
│ │
|
||
│ Beräknad total skatt: ~142 000 kr │
|
||
│ vs allt som lön: ~185 000 kr │
|
||
│ ───────────────────────────────────────────────────────── │
|
||
│ Potentiell besparing: ~43 000 kr │
|
||
│ │
|
||
│ ⚠️ Detta är en uppskattning. Rådgör med revisor. │
|
||
└─────────────────────────────────────────────────────────────┘
|
||
```
|
||
|
||
### Implementation Requirements
|
||
|
||
- Track sparat utdelningsutrymme (historical data)
|
||
- Input for owner's other income (affects marginal rate)
|
||
- Annual update of inkomstbasbelopp
|
||
- Strong disclaimer: Not tax advice
|
||
|
||
---
|
||
|
||
## Phase 3: Skatteverket API Integrations
|
||
|
||
### Available APIs
|
||
|
||
Skatteverket offers several APIs that can enhance the product:
|
||
|
||
#### 1. Skattekonto API
|
||
|
||
Fetch user's tax account balance and transactions.
|
||
|
||
**Prerequisite:** User must grant "Läsombud" access in Skatteverket's system.
|
||
|
||
```typescript
|
||
// Conceptual - actual implementation requires OAuth2 + organizational agreement
|
||
interface SkattekontoBala {
|
||
saldo: number
|
||
senastInbetalning: Date
|
||
kommandeForfall: Array<{
|
||
datum: Date
|
||
belopp: number
|
||
typ: string // 'F-skatt', 'Moms', etc.
|
||
}>
|
||
}
|
||
|
||
// Use case: "Du har 15 000 kr på skattekontot, men beräknad skatt är 20 000 kr. Sätt in 5 000 kr nu."
|
||
```
|
||
|
||
#### 2. Momsdeklaration API
|
||
|
||
Submit VAT declaration directly from app.
|
||
|
||
**Prerequisites:**
|
||
- Registered as e-tjänstleverantör with Skatteverket
|
||
- User authentication via BankID
|
||
- Signed agreement with Skatteverket
|
||
|
||
```typescript
|
||
interface MomsdeklarationSubmission {
|
||
period: string // '2024-01' for January
|
||
ruta05: number // Momspliktig försäljning
|
||
ruta39: number // Tjänsteförsäljning EU
|
||
ruta40: number // Export
|
||
ruta21: number // Inköp tjänster EU
|
||
ruta48: number // Utgående moms inköp
|
||
// ... all required rutor
|
||
}
|
||
```
|
||
|
||
#### 3. Arbetsgivardeklaration API
|
||
|
||
Not typically needed for sole proprietors without employees, but relevant if they hire.
|
||
|
||
### Integration Roadmap
|
||
|
||
| Phase | Integration | User Value |
|
||
|-------|-------------|------------|
|
||
| MVP | None (manual) | - |
|
||
| v1.5 | VIES VAT validation | Automated reverse charge |
|
||
| v2.0 | Skattekonto (read) | Real-time tax position |
|
||
| v2.5 | Momsdeklaration (submit) | One-click VAT filing |
|
||
| v3.0 | NE-bilaga (submit) | Full declaration automation |
|
||
|
||
---
|
||
|
||
## Phase 2: Reklammärkning Compliance
|
||
|
||
### Marknadsföringslagen Requirement
|
||
|
||
Swedish law requires clear ad disclosure. The "Kissie" case established that marking must be:
|
||
- At the very beginning of content
|
||
- Immediately visible
|
||
- Unambiguous ("Reklam" or "Annons", not just "Samarbete")
|
||
|
||
### Implementation
|
||
|
||
In Deal-to-Invoice flow, add mandatory checkbox:
|
||
|
||
```typescript
|
||
interface DealComplianceCheck {
|
||
reklamMarkerad: boolean
|
||
markeringTyp: 'inlagg' | 'video' | 'story' | 'podcast'
|
||
bekraftelse: string // Timestamp of user confirmation
|
||
}
|
||
|
||
// Before invoice can be marked as complete:
|
||
const complianceText = `Jag intygar att allt publicerat material för detta samarbete har reklammarkerats tydligt i början av varje inlägg/video i enlighet med Marknadsföringslagen (2008:486).`
|
||
```
|
||
|
||
---
|
||
|
||
## Data Retention & GDPR
|
||
|
||
### Conflict Resolution
|
||
|
||
**Bokföringslagen**: 7 years mandatory retention for räkenskapsinformation
|
||
**GDPR**: Right to erasure
|
||
|
||
Resolution:
|
||
- Accounting records (verifikationer, fakturor, kvitton): 7-year mandatory retention
|
||
- Non-accounting data (support chats, preferences, analytics): Can be deleted on request
|
||
- Technical implementation: Separate data stores with different retention policies
|
||
|
||
```typescript
|
||
// Database design consideration
|
||
interface UserDataPolicy {
|
||
accountingData: {
|
||
retention: '7_years_from_fiscal_year_end',
|
||
deletable: false,
|
||
legal_basis: 'Bokföringslagen 7 kap. 2 §'
|
||
},
|
||
operationalData: {
|
||
retention: 'until_deletion_request',
|
||
deletable: true,
|
||
legal_basis: 'Consent / Legitimate interest'
|
||
}
|
||
}
|
||
```
|
||
|
||
### Digital Archive Requirements (July 2024 Law)
|
||
|
||
Receipt photos must be:
|
||
- Stored in immutable format (PDF/A-3, locked JPEG)
|
||
- Backed up regularly
|
||
- Hosted within EU/EES or adequate country
|
||
- Tamper-evident (hash verification)
|
||
|
||
```typescript
|
||
interface ReceiptArchive {
|
||
originalFilename: string
|
||
storedAs: string // UUID.pdf
|
||
format: 'PDF/A-3'
|
||
sha256Hash: string
|
||
capturedAt: Date
|
||
linkedTransactionId: string
|
||
storageLocation: 'eu-north-1' // Must be EU
|
||
}
|
||
```
|