Files
accounted/lib/bookkeeping/account-descriptions.ts
T
Jakob WennbergandClaude Opus 4.6 91e2c1705a feat: per-line VAT, invoice document types, ledger-based VAT declaration, bank reconciliation, and pagination
Per-line VAT rates:
- Add generatePerRateLines() to group invoice items by vat_rate with separate
  revenue + VAT lines per rate group (invoice-entries.ts)
- Add getAvailableVatRates() and getVatTreatmentForRate() (vat-rules.ts)
- PDF template shows per-line VAT column and per-rate totals for mixed-rate invoices
- Invoice create/review UI supports per-line rate selection
- Types: add vat_rate/vat_amount to InvoiceItem, vat_rate to CreateInvoiceItemInput

Invoice document types (proforma, delivery note):
- Add InvoiceDocumentType, document_type and converted_from_id to Invoice type
- PDF hides prices for delivery notes, adds proforma notice
- Email templates support all document types
- mark-paid skips journal entries for non-invoice document types
- Migration 031: invoice_document_type

Accounting method support:
- Add AccountingMethod type (accrual/cash)
- Migration 032: add_accounting_method column to company_settings

VAT declaration rewrite:
- Rewrite to read directly from general ledger (26xx/3xxx account lines)
  instead of aggregating invoices/transactions/receipts
- ACCOUNT_RUTA mapping drives momsdeklaration boxes from GL balances

Bank reconciliation:
- Transaction ingest now pre-fetches unlinked GL lines and attempts
  auto-reconciliation during import
- Add transaction.reconciled event type
- Add ReconciliationMethod type and reconciliation_method on Transaction
- Migration 030: bank_reconciliation
- New reconciliation engine, API routes, and BankReconciliationView component

Pagination (fetchAllRows):
- New lib/supabase/fetch-all.ts overcomes PostgREST 1000-row limit
- Adopted in all report generators, SIE/SRU export, account list APIs

Fiscal period validation:
- New validate-period-duration.ts enforces max 18 months per BFL 3 kap.
- Applied in period-service.ts and fiscal-periods API

Account mapper simplification:
- Remove Levenshtein/fuzzy matching, use exact account number match only

Swedbank parser improvements:
- Support abbreviated headers (Clnr, Bokfdag, Radnr)
- Use Referens column as counterparty

Chart of accounts management:
- Add DELETE endpoint with system account and usage protection
- PUT uses partial updates
- New AccountCombobox, AddAccountDialog, EditAccountDialog, ChartOfAccountsManager

Tax deadline corrections:
- Rewrite inkomstdeklaration_ab using Skatteverket lookup table
- Rewrite arsredovisning deadline to 7 months after FY end per ÅRL 8:3

Onboarding first fiscal year:
- Add first fiscal year toggle with date pickers and 18-month validation

UI terminology:
- Change "okategoriserad/kategorisera" to "obokförd/bokföra" throughout

Report column fix:
- Fix start_date/end_date to period_start/period_end in report queries

Supplier invoice input:
- CreateSupplierInvoiceItemInput uses amount field (legacy quantity/unit_price kept)

Misc:
- SIE import uses upsert for idempotent account creation
- account-descriptions.ts falls back to BAS reference data
- Add invoice_default_notes to CompanySettings
- Update CLAUDE.md to reflect current project state

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-21 14:57:15 +01:00

381 lines
12 KiB
TypeScript

export type AccountType = 'asset' | 'liability' | 'equity' | 'revenue' | 'expense'
export interface AccountDescription {
name: string
classLabel: string
type: AccountType
explanation: string
}
const ACCOUNT_DESCRIPTIONS: Record<string, AccountDescription> = {
// Class 1: Assets (Tillgångar)
'1510': {
name: 'Kundfordringar',
classLabel: 'Tillgångar',
type: 'asset',
explanation: 'Pengar som kunder är skyldiga dig för skickade fakturor som inte betalats ännu.',
},
'1580': {
name: 'Fordran för skatt',
classLabel: 'Tillgångar',
type: 'asset',
explanation: 'Pengar du har att fordra från Skatteverket, t.ex. överskjutande moms.',
},
'1630': {
name: 'Skattekonto',
classLabel: 'Tillgångar',
type: 'asset',
explanation: 'Ditt skattekonto hos Skatteverket, visar saldo för inbetalda skatter och avgifter.',
},
'1710': {
name: 'Förutbetalda hyreskostnader',
classLabel: 'Tillgångar',
type: 'asset',
explanation: 'Hyra som betalats i förskott men som avser kommande perioder.',
},
'1910': {
name: 'Kassa',
classLabel: 'Tillgångar',
type: 'asset',
explanation: 'Kontanta pengar i företagets kassa.',
},
'1920': {
name: 'PlusGiro',
classLabel: 'Tillgångar',
type: 'asset',
explanation: 'Pengar på företagets PlusGiro-konto.',
},
'1930': {
name: 'Företagskonto',
classLabel: 'Tillgångar',
type: 'asset',
explanation: 'Företagets huvudsakliga bankkonto. Hit kommer inbetalningar och härifrån görs utbetalningar.',
},
'1940': {
name: 'Övriga bankkonton',
classLabel: 'Tillgångar',
type: 'asset',
explanation: 'Ytterligare bankkonton utöver huvudkontot.',
},
// Class 2: Equity & Liabilities (Eget kapital och skulder)
'2010': {
name: 'Eget kapital',
classLabel: 'Eget kapital och skulder',
type: 'equity',
explanation: 'Ägarens insatta kapital i enskild firma. Visar vad ägaren har investerat i verksamheten.',
},
'2013': {
name: 'Egna uttag',
classLabel: 'Eget kapital och skulder',
type: 'equity',
explanation: 'Pengar som ägaren av en enskild firma tar ut privat ur företaget.',
},
'2018': {
name: 'Egna insättningar',
classLabel: 'Eget kapital och skulder',
type: 'equity',
explanation: 'Pengar som ägaren sätter in privat i företaget (enskild firma).',
},
'2081': {
name: 'Aktiekapital',
classLabel: 'Eget kapital och skulder',
type: 'equity',
explanation: 'Det registrerade aktiekapitalet i ett aktiebolag.',
},
'2091': {
name: 'Balanserat resultat',
classLabel: 'Eget kapital och skulder',
type: 'equity',
explanation: 'Ackumulerade vinster/förluster från tidigare år som inte delats ut.',
},
'2099': {
name: 'Årets resultat',
classLabel: 'Eget kapital och skulder',
type: 'equity',
explanation: 'Vinst eller förlust för innevarande räkenskapsår.',
},
'2440': {
name: 'Leverantörsskulder',
classLabel: 'Eget kapital och skulder',
type: 'liability',
explanation: 'Pengar du är skyldig leverantörer för mottagna fakturor som inte betalats ännu.',
},
'2510': {
name: 'Skatteskulder',
classLabel: 'Eget kapital och skulder',
type: 'liability',
explanation: 'Skulder till Skatteverket för preliminär skatt och andra skattebetalningar.',
},
'2611': {
name: 'Utgående moms 25%',
classLabel: 'Eget kapital och skulder',
type: 'liability',
explanation: 'Moms du tar ut på försäljning med 25% momssats. Ska betalas in till Skatteverket.',
},
'2614': {
name: 'Utgående moms omvänd skattskyldighet',
classLabel: 'Eget kapital och skulder',
type: 'liability',
explanation: 'Utgående moms vid omvänd skattskyldighet (reverse charge). Köparen redovisar momsen.',
},
'2621': {
name: 'Utgående moms 12%',
classLabel: 'Eget kapital och skulder',
type: 'liability',
explanation: 'Moms du tar ut på försäljning med 12% momssats (t.ex. livsmedel, hotell).',
},
'2631': {
name: 'Utgående moms 6%',
classLabel: 'Eget kapital och skulder',
type: 'liability',
explanation: 'Moms du tar ut på försäljning med 6% momssats (t.ex. böcker, kollektivtrafik).',
},
'2641': {
name: 'Ingående moms',
classLabel: 'Eget kapital och skulder',
type: 'liability',
explanation: 'Moms du betalat på inköp som du har rätt att dra av. Minskar din momsskuld.',
},
'2645': {
name: 'Beräknad ingående moms på EU-förvärv',
classLabel: 'Eget kapital och skulder',
type: 'liability',
explanation: 'Ingående moms du beräknar själv vid inköp från andra EU-länder (omvänd skattskyldighet).',
},
'2893': {
name: 'Lån från aktieägare',
classLabel: 'Eget kapital och skulder',
type: 'liability',
explanation: 'Pengar som aktiebolaget lånat av sina ägare. Vanligt i mindre AB.',
},
'2898': {
name: 'Outtagen vinstutdelning',
classLabel: 'Eget kapital och skulder',
type: 'liability',
explanation: 'Beslutad men ännu ej utbetald aktieutdelning.',
},
'2920': {
name: 'Upplupna semesterlöner',
classLabel: 'Eget kapital och skulder',
type: 'liability',
explanation: 'Skuld för intjänade men ej uttagna semesterdagar.',
},
'2940': {
name: 'Upplupna arbetsgivaravgifter',
classLabel: 'Eget kapital och skulder',
type: 'liability',
explanation: 'Arbetsgivaravgifter som hänför sig till redovisade löner men ännu inte betalats.',
},
// Class 3: Revenue (Intäkter)
'3001': {
name: 'Försäljning varor/tjänster 25%',
classLabel: 'Intäkter',
type: 'revenue',
explanation: 'Intäkter från försäljning med 25% moms — den vanligaste intäktsraden.',
},
'3002': {
name: 'Försäljning varor/tjänster 12%',
classLabel: 'Intäkter',
type: 'revenue',
explanation: 'Intäkter från försäljning med 12% moms (t.ex. livsmedel).',
},
'3003': {
name: 'Försäljning varor/tjänster 6%',
classLabel: 'Intäkter',
type: 'revenue',
explanation: 'Intäkter från försäljning med 6% moms (t.ex. böcker, tidningar).',
},
'3305': {
name: 'Försäljning export utanför EU',
classLabel: 'Intäkter',
type: 'revenue',
explanation: 'Intäkter från försäljning till kunder utanför EU. Momsfritt.',
},
'3308': {
name: 'Försäljning tjänster EU',
classLabel: 'Intäkter',
type: 'revenue',
explanation: 'Intäkter från försäljning av tjänster till företag i andra EU-länder. Omvänd skattskyldighet.',
},
'3900': {
name: 'Övriga rörelseintäkter',
classLabel: 'Intäkter',
type: 'revenue',
explanation: 'Andra intäkter som inte hör till kärnverksamheten, t.ex. uthyrning av lokaler.',
},
'3960': {
name: 'Valutakursvinster',
classLabel: 'Intäkter',
type: 'revenue',
explanation: 'Vinster som uppstår vid valutaväxling eller betalningar i utländsk valuta.',
},
// Class 4: Cost of goods (Varor och material)
'4010': {
name: 'Varuinköp',
classLabel: 'Varor och material',
type: 'expense',
explanation: 'Kostnader för inköp av varor som säljs vidare.',
},
// Class 5: External expenses (Övriga externa kostnader)
'5010': {
name: 'Lokalkostnader',
classLabel: 'Övriga externa kostnader',
type: 'expense',
explanation: 'Kostnader för kontorslokal, lager eller annan arbetsplats (hyra, el, städning).',
},
'5410': {
name: 'Förbrukningsinventarier',
classLabel: 'Övriga externa kostnader',
type: 'expense',
explanation: 'Inköp av mindre inventarier, t.ex. kontorsmöbler, dator (under halva prisbasbeloppet).',
},
'5420': {
name: 'Programvaror',
classLabel: 'Övriga externa kostnader',
type: 'expense',
explanation: 'Kostnader för mjukvara, prenumerationer och licenser.',
},
'5800': {
name: 'Resekostnader',
classLabel: 'Övriga externa kostnader',
type: 'expense',
explanation: 'Tjänsteresor: tåg, flyg, hotell, taxi.',
},
'5910': {
name: 'Annonsering och reklam',
classLabel: 'Övriga externa kostnader',
type: 'expense',
explanation: 'Kostnader för marknadsföring, annonser och reklamkampanjer.',
},
// Class 6: Other external expenses
'6530': {
name: 'Redovisningstjänster',
classLabel: 'Övriga externa kostnader',
type: 'expense',
explanation: 'Avgifter till bokföringsbyrå eller redovisningskonsult.',
},
'6570': {
name: 'Bankkostnader',
classLabel: 'Övriga externa kostnader',
type: 'expense',
explanation: 'Avgifter för banktjänster, betalförmedling och transaktionsavgifter.',
},
'6991': {
name: 'Övriga externa kostnader',
classLabel: 'Övriga externa kostnader',
type: 'expense',
explanation: 'Diverse externa kostnader som inte passar in under andra konton.',
},
// Class 7: Personnel & depreciation
'7010': {
name: 'Löner tjänstemän',
classLabel: 'Personal och avskrivningar',
type: 'expense',
explanation: 'Bruttolöner (före skatt) till anställda tjänstemän.',
},
'7210': {
name: 'Löner kollektiv',
classLabel: 'Personal och avskrivningar',
type: 'expense',
explanation: 'Bruttolöner till arbetare och kollektivanställda.',
},
'7510': {
name: 'Arbetsgivaravgifter',
classLabel: 'Personal och avskrivningar',
type: 'expense',
explanation: 'Lagstadgade sociala avgifter som arbetsgivaren betalar (ca 31% av bruttolönen).',
},
'7820': {
name: 'Avskrivningar inventarier',
classLabel: 'Personal och avskrivningar',
type: 'expense',
explanation: 'Årlig värdeminskning på inventarier och maskiner. Fördelas över tillgångens livslängd.',
},
'7960': {
name: 'Valutakursförluster',
classLabel: 'Personal och avskrivningar',
type: 'expense',
explanation: 'Förluster som uppstår vid valutaväxling eller betalningar i utländsk valuta.',
},
// Class 8: Financial items
'8310': {
name: 'Ränteintäkter',
classLabel: 'Finansiella poster',
type: 'revenue',
explanation: 'Ränta du får på bankkontosaldo eller utlånade pengar.',
},
'8410': {
name: 'Räntekostnader',
classLabel: 'Finansiella poster',
type: 'expense',
explanation: 'Ränta du betalar på lån och krediter.',
},
'8999': {
name: 'Årets resultat',
classLabel: 'Finansiella poster',
type: 'equity',
explanation: 'Slutresultatkonto som visar vinst eller förlust efter alla intäkter och kostnader.',
},
}
export function getAccountDescription(accountNumber: string): AccountDescription | undefined {
// Check hardcoded descriptions first (most detailed explanations)
const hardcoded = ACCOUNT_DESCRIPTIONS[accountNumber]
if (hardcoded) return hardcoded
// Fall back to BAS reference data for accounts not in the hardcoded list
try {
// Dynamic import avoided — use lazy require pattern
const { getBASReference, ACCOUNT_CLASS_LABELS } = require('./bas-reference')
const ref = getBASReference(accountNumber)
if (ref) {
const classLabel = ACCOUNT_CLASS_LABELS[ref.account_class] || ''
return {
name: ref.account_name,
classLabel,
type: ref.account_type,
explanation: ref.description,
}
}
} catch {
// BAS reference not available — that's fine
}
return undefined
}
/**
* Get a human-readable account class name for BAS account classes.
*/
export function getAccountClassName(accountClass: number): string {
switch (accountClass) {
case 1:
return '1xxx - Tillgångar'
case 2:
return '2xxx - Eget kapital & Skulder'
case 3:
return '3xxx - Intäkter'
case 4:
return '4xxx - Varuinköp'
case 5:
return '5xxx - Externa kostnader'
case 6:
return '6xxx - Övriga externa kostnader'
case 7:
return '7xxx - Personal'
case 8:
return '8xxx - Finansiella poster'
default:
return `${accountClass}xxx - Övrigt`
}
}
export { ACCOUNT_DESCRIPTIONS }