Files
accounted/dev_docs/07-FUTURE-FEATURES.md
T
2026-02-13 14:00:46 +01:00

16 KiB
Raw Blame History

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

// 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

// 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.

// 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
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)

// 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

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.

// 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
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:

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
// 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)
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
}