import type { TransactionCategory, MappingResult, VatJournalLine, Transaction, EntityType, VatTreatment, RiskLevel, } from '@/types' import { getVatRate, generateReverseChargeLines, generateInputVatLine } from './vat-entries' // ============================================================ // Types // ============================================================ export type TemplateGroup = | 'premises' | 'vehicle' | 'it_software' | 'office_supplies' | 'marketing' | 'travel' | 'representation' | 'insurance' | 'professional_services' | 'bank_finance' | 'telecom' | 'education' | 'personnel' | 'revenue' | 'financial' | 'private_transfers' | 'equipment' | 'subscriptions' export interface BookingTemplate { id: string name_sv: string name_en: string group: TemplateGroup direction: 'expense' | 'income' | 'transfer' entity_applicability: 'all' | 'enskild_firma' | 'aktiebolag' debit_account: string credit_account: string debit_account_ab?: string credit_account_ab?: string vat_treatment: VatTreatment | null vat_rate: number deductibility: 'full' | 'non_deductible' | 'conditional' deductibility_note_sv?: string special_rules_sv?: string mcc_codes: number[] keywords: string[] risk_level: RiskLevel requires_review: boolean impact_score: number auto_match_confidence: number default_private: boolean fallback_category: TransactionCategory description_sv: string } export interface TemplateGroupInfo { group: TemplateGroup label_sv: string label_en: string templates: BookingTemplate[] } export interface TemplateMatch { template: BookingTemplate confidence: number } // ============================================================ // Template Group Labels // ============================================================ const GROUP_LABELS: Record = { premises: { sv: 'Lokalkostnader', en: 'Premises' }, vehicle: { sv: 'Fordon', en: 'Vehicle' }, it_software: { sv: 'IT & Programvara', en: 'IT & Software' }, office_supplies: { sv: 'Kontorsmaterial', en: 'Office Supplies' }, marketing: { sv: 'Marknadsföring', en: 'Marketing' }, travel: { sv: 'Resor & Transport', en: 'Travel & Transport' }, representation: { sv: 'Representation', en: 'Representation' }, insurance: { sv: 'Försäkringar', en: 'Insurance' }, professional_services: { sv: 'Professionella tjänster', en: 'Professional Services' }, bank_finance: { sv: 'Bank & Finans', en: 'Banking & Finance' }, telecom: { sv: 'Telekom & Internet', en: 'Telecom & Internet' }, education: { sv: 'Utbildning', en: 'Education' }, personnel: { sv: 'Personal', en: 'Personnel' }, revenue: { sv: 'Intäkter', en: 'Revenue' }, financial: { sv: 'Finansiella poster', en: 'Financial Items' }, private_transfers: { sv: 'Privata transaktioner', en: 'Private Transfers' }, equipment: { sv: 'Inventarier & Utrustning', en: 'Equipment' }, subscriptions: { sv: 'Prenumerationer & Medlemskap', en: 'Subscriptions & Memberships' }, } // ============================================================ // Template Data (100 templates) // ============================================================ export const BOOKING_TEMPLATES: readonly BookingTemplate[] = [ // --- PREMISES (1-8) --- { id: 'premises_rent', name_sv: 'Lokalhyra', name_en: 'Office rent', group: 'premises', direction: 'expense', entity_applicability: 'all', debit_account: '5010', credit_account: '1930', vat_treatment: 'exempt', vat_rate: 0, deductibility: 'full', special_rules_sv: 'Lokalhyra är momsfri om hyresvärden inte är frivilligt momsregistrerad', mcc_codes: [], keywords: ['hyra', 'lokal', 'kontor', 'rent', 'office space', 'kontorslokal'], risk_level: 'NONE', requires_review: false, impact_score: 10, auto_match_confidence: 0.85, default_private: false, fallback_category: 'expense_office', description_sv: 'Månadshyra för kontors- eller affärslokal', }, { id: 'premises_rent_vat', name_sv: 'Lokalhyra (momsbelagd)', name_en: 'Office rent (with VAT)', group: 'premises', direction: 'expense', entity_applicability: 'all', debit_account: '5010', credit_account: '1930', vat_treatment: 'standard_25', vat_rate: 0.25, deductibility: 'full', special_rules_sv: 'Gäller när hyresvärden är frivilligt momsregistrerad', mcc_codes: [], keywords: ['hyra', 'lokal', 'moms', 'kontorshyra'], risk_level: 'NONE', requires_review: false, impact_score: 8, auto_match_confidence: 0.80, default_private: false, fallback_category: 'expense_office', description_sv: 'Lokalhyra med moms (frivilligt momsregistrerad hyresvärd)', }, { id: 'premises_electricity', name_sv: 'El', name_en: 'Electricity', group: 'premises', direction: 'expense', entity_applicability: 'all', debit_account: '5020', credit_account: '1930', vat_treatment: 'standard_25', vat_rate: 0.25, deductibility: 'full', mcc_codes: [4900], keywords: ['el', 'electricity', 'vattenfall', 'eon', 'fortum', 'ellevio', 'kraftbolag', 'elnät'], risk_level: 'NONE', requires_review: false, impact_score: 8, auto_match_confidence: 0.85, default_private: false, fallback_category: 'expense_office', description_sv: 'Elkostnad för kontors- eller affärslokal', }, { id: 'premises_heating', name_sv: 'Uppvärmning', name_en: 'Heating', group: 'premises', direction: 'expense', entity_applicability: 'all', debit_account: '5030', credit_account: '1930', vat_treatment: 'standard_25', vat_rate: 0.25, deductibility: 'full', mcc_codes: [], keywords: ['värme', 'fjärrvärme', 'uppvärmning', 'heating'], risk_level: 'NONE', requires_review: false, impact_score: 5, auto_match_confidence: 0.80, default_private: false, fallback_category: 'expense_office', description_sv: 'Uppvärmningskostnad för lokal', }, { id: 'premises_water', name_sv: 'Vatten & Avlopp', name_en: 'Water & Sewage', group: 'premises', direction: 'expense', entity_applicability: 'all', debit_account: '5040', credit_account: '1930', vat_treatment: 'standard_25', vat_rate: 0.25, deductibility: 'full', mcc_codes: [], keywords: ['vatten', 'avlopp', 'water', 'va-avgift'], risk_level: 'NONE', requires_review: false, impact_score: 4, auto_match_confidence: 0.80, default_private: false, fallback_category: 'expense_office', description_sv: 'Vatten- och avloppskostnader', }, { id: 'premises_cleaning', name_sv: 'Städning', name_en: 'Cleaning services', group: 'premises', direction: 'expense', entity_applicability: 'all', debit_account: '5050', credit_account: '1930', vat_treatment: 'standard_25', vat_rate: 0.25, deductibility: 'full', mcc_codes: [7349], keywords: ['städ', 'cleaning', 'rengöring', 'städfirma', 'lokalvård'], risk_level: 'NONE', requires_review: false, impact_score: 6, auto_match_confidence: 0.85, default_private: false, fallback_category: 'expense_office', description_sv: 'Städning av kontors- eller affärslokal', }, { id: 'premises_repairs', name_sv: 'Reparation & Underhåll av lokal', name_en: 'Premises repairs & maintenance', group: 'premises', direction: 'expense', entity_applicability: 'all', debit_account: '5070', credit_account: '1930', vat_treatment: 'standard_25', vat_rate: 0.25, deductibility: 'full', mcc_codes: [1520, 1711, 1731, 1740, 1750, 1761, 1771], keywords: ['reparation', 'underhåll', 'renovering', 'repair', 'maintenance', 'lokal'], risk_level: 'LOW', requires_review: false, impact_score: 5, auto_match_confidence: 0.75, default_private: false, fallback_category: 'expense_office', description_sv: 'Reparation och underhåll av kontors- eller affärslokal', }, { id: 'premises_security', name_sv: 'Bevakning & Larm', name_en: 'Security & Alarms', group: 'premises', direction: 'expense', entity_applicability: 'all', debit_account: '5060', credit_account: '1930', vat_treatment: 'standard_25', vat_rate: 0.25, deductibility: 'full', mcc_codes: [7382], keywords: ['securitas', 'larm', 'bevakning', 'security', 'alarm', 'verisure'], risk_level: 'NONE', requires_review: false, impact_score: 4, auto_match_confidence: 0.85, default_private: false, fallback_category: 'expense_office', description_sv: 'Bevakning, larm och säkerhetstjänster', }, // --- VEHICLE (9-15) --- { id: 'vehicle_fuel', name_sv: 'Drivmedel', name_en: 'Fuel', group: 'vehicle', direction: 'expense', entity_applicability: 'all', debit_account: '5611', credit_account: '1930', vat_treatment: 'standard_25', vat_rate: 0.25, deductibility: 'full', special_rules_sv: 'Drivmedel till firmabil, ej privat körning', mcc_codes: [5541, 5542, 5983], keywords: ['bensin', 'diesel', 'drivmedel', 'fuel', 'tank', 'okq8', 'circle k', 'preem', 'st1', 'shell', 'ingo'], risk_level: 'LOW', requires_review: false, impact_score: 8, auto_match_confidence: 0.85, default_private: false, fallback_category: 'expense_travel', description_sv: 'Drivmedel (bensin/diesel) för tjänstefordon', }, { id: 'vehicle_leasing', name_sv: 'Billeasing', name_en: 'Car leasing', group: 'vehicle', direction: 'expense', entity_applicability: 'all', debit_account: '5615', credit_account: '1930', vat_treatment: 'standard_25', vat_rate: 0.25, deductibility: 'conditional', deductibility_note_sv: 'Max 50% momsavdrag för personbil', special_rules_sv: 'Personbil: halvt momsavdrag. Lastbil/lätt lastbil: fullt avdrag.', mcc_codes: [7512, 7513], keywords: ['leasing', 'billeasing', 'car lease', 'leasingavgift'], risk_level: 'LOW', requires_review: false, impact_score: 7, auto_match_confidence: 0.85, default_private: false, fallback_category: 'expense_travel', description_sv: 'Leasingavgift för tjänstefordon', }, { id: 'vehicle_insurance', name_sv: 'Fordonsförsäkring', name_en: 'Vehicle insurance', group: 'vehicle', direction: 'expense', entity_applicability: 'all', debit_account: '5612', credit_account: '1930', vat_treatment: 'exempt', vat_rate: 0, deductibility: 'full', special_rules_sv: 'Försäkringar är momsfria', mcc_codes: [6300], keywords: ['bilförsäkring', 'fordonsförsäkring', 'car insurance', 'vehicle insurance', 'if', 'trygg-hansa', 'folksam'], risk_level: 'NONE', requires_review: false, impact_score: 5, auto_match_confidence: 0.80, default_private: false, fallback_category: 'expense_other', description_sv: 'Försäkring för tjänstefordon', }, { id: 'vehicle_repairs', name_sv: 'Reparation & Service fordon', name_en: 'Vehicle repairs & service', group: 'vehicle', direction: 'expense', entity_applicability: 'all', debit_account: '5613', credit_account: '1930', vat_treatment: 'standard_25', vat_rate: 0.25, deductibility: 'full', mcc_codes: [5511, 5521, 5531, 5532, 5533, 7531, 7534, 7535, 7538, 7542], keywords: ['bilverkstad', 'service', 'reparation', 'däck', 'mekonomen', 'autoexperten', 'bilprovning', 'besiktning'], risk_level: 'NONE', requires_review: false, impact_score: 6, auto_match_confidence: 0.85, default_private: false, fallback_category: 'expense_travel', description_sv: 'Reparation, service och underhåll av fordon', }, { id: 'vehicle_parking', name_sv: 'Parkering', name_en: 'Parking', group: 'vehicle', direction: 'expense', entity_applicability: 'all', debit_account: '5614', credit_account: '1930', vat_treatment: 'standard_25', vat_rate: 0.25, deductibility: 'full', mcc_codes: [7521, 7523], keywords: ['parkering', 'parking', 'p-avgift', 'easypark', 'apcoa', 'q-park', 'aimo'], risk_level: 'NONE', requires_review: false, impact_score: 7, auto_match_confidence: 0.90, default_private: false, fallback_category: 'expense_travel', description_sv: 'Parkeringsavgift vid tjänsteärende', }, { id: 'vehicle_toll', name_sv: 'Trängselskatt & Vägtull', name_en: 'Congestion tax & Road tolls', group: 'vehicle', direction: 'expense', entity_applicability: 'all', debit_account: '5616', credit_account: '1930', vat_treatment: null, vat_rate: 0, deductibility: 'full', special_rules_sv: 'Trängselskatt är inte momsbelagd', mcc_codes: [4784], keywords: ['trängselskatt', 'vägtull', 'toll', 'brobizz', 'congestion'], risk_level: 'NONE', requires_review: false, impact_score: 5, auto_match_confidence: 0.85, default_private: false, fallback_category: 'expense_travel', description_sv: 'Trängselskatt och vägtullar', }, { id: 'vehicle_ev_charging', name_sv: 'Laddning elbil', name_en: 'EV charging', group: 'vehicle', direction: 'expense', entity_applicability: 'all', debit_account: '5611', credit_account: '1930', vat_treatment: 'standard_25', vat_rate: 0.25, deductibility: 'full', mcc_codes: [5552], keywords: ['laddning', 'elbil', 'charging', 'charge', 'tesla', 'ionity', 'recharge', 'mer'], risk_level: 'NONE', requires_review: false, impact_score: 6, auto_match_confidence: 0.85, default_private: false, fallback_category: 'expense_travel', description_sv: 'Laddning av elbil för tjänstebruk', }, // --- IT & SOFTWARE (16-25) --- { id: 'it_saas_subscription', name_sv: 'Programvara / SaaS-prenumeration', name_en: 'Software / SaaS subscription', group: 'it_software', direction: 'expense', entity_applicability: 'all', debit_account: '5420', credit_account: '1930', vat_treatment: 'standard_25', vat_rate: 0.25, deductibility: 'full', mcc_codes: [5734, 5817, 5818], keywords: ['software', 'saas', 'programvara', 'licens', 'subscription', 'app', 'microsoft', 'google', 'adobe', 'slack', 'notion', 'figma', 'github', 'atlassian', 'jira'], risk_level: 'NONE', requires_review: false, impact_score: 10, auto_match_confidence: 0.90, default_private: false, fallback_category: 'expense_software', description_sv: 'Programvarulicens eller SaaS-prenumeration', }, { id: 'it_saas_eu', name_sv: 'Programvara / SaaS (EU, omvänd moms)', name_en: 'Software / SaaS (EU, reverse charge)', group: 'it_software', direction: 'expense', entity_applicability: 'all', debit_account: '5420', credit_account: '1930', vat_treatment: 'reverse_charge', vat_rate: 0, deductibility: 'full', special_rules_sv: 'EU-tjänst med omvänd skattskyldighet', mcc_codes: [5734, 5817, 5818], keywords: ['software', 'saas', 'eu', 'ireland', 'reverse charge', 'omvänd moms'], risk_level: 'LOW', requires_review: false, impact_score: 8, auto_match_confidence: 0.75, default_private: false, fallback_category: 'expense_software', description_sv: 'Programvara från EU-land med omvänd skattskyldighet', }, { id: 'it_cloud_hosting', name_sv: 'Molntjänster / Hosting', name_en: 'Cloud services / Hosting', group: 'it_software', direction: 'expense', entity_applicability: 'all', debit_account: '5421', credit_account: '1930', vat_treatment: 'reverse_charge', vat_rate: 0, deductibility: 'full', special_rules_sv: 'Ofta EU/utländsk leverantör med omvänd skattskyldighet', mcc_codes: [4816], keywords: ['aws', 'azure', 'google cloud', 'gcp', 'hosting', 'server', 'cloud', 'vercel', 'heroku', 'digitalocean', 'cloudflare', 'hetzner'], risk_level: 'NONE', requires_review: false, impact_score: 9, auto_match_confidence: 0.85, default_private: false, fallback_category: 'expense_software', description_sv: 'Molnbaserade tjänster, webbhotell och serverhosting', }, { id: 'it_domain_name', name_sv: 'Domännamn', name_en: 'Domain name', group: 'it_software', direction: 'expense', entity_applicability: 'all', debit_account: '5421', credit_account: '1930', vat_treatment: 'standard_25', vat_rate: 0.25, deductibility: 'full', mcc_codes: [], keywords: ['domän', 'domain', 'dns', 'loopia', 'binero', 'godaddy', 'namecheap'], risk_level: 'NONE', requires_review: false, impact_score: 5, auto_match_confidence: 0.85, default_private: false, fallback_category: 'expense_software', description_sv: 'Registrering och förnyelse av domännamn', }, { id: 'it_hardware_small', name_sv: 'IT-tillbehör (förbrukningsinventarie)', name_en: 'IT accessories (consumable)', group: 'it_software', direction: 'expense', entity_applicability: 'all', debit_account: '5410', credit_account: '1930', vat_treatment: 'standard_25', vat_rate: 0.25, deductibility: 'full', special_rules_sv: 'Under halva prisbasbeloppet = förbrukningsinventarie', mcc_codes: [5045, 5065, 5732], keywords: ['tangentbord', 'mus', 'headset', 'adapter', 'kabel', 'usb', 'monitor', 'skärm', 'dockningsstation', 'keyboard', 'mouse', 'webcam'], risk_level: 'NONE', requires_review: false, impact_score: 7, auto_match_confidence: 0.80, default_private: false, fallback_category: 'expense_equipment', description_sv: 'Mindre IT-tillbehör (under halva prisbasbeloppet)', }, { id: 'it_computer', name_sv: 'Dator / Laptop', name_en: 'Computer / Laptop', group: 'it_software', direction: 'expense', entity_applicability: 'all', debit_account: '5410', credit_account: '1930', vat_treatment: 'standard_25', vat_rate: 0.25, deductibility: 'full', special_rules_sv: 'Över halva prisbasbeloppet = aktivering på konto 1250. Blandad användning → bara företagsdelen avdragsgill.', mcc_codes: [5045, 5732], keywords: ['dator', 'laptop', 'macbook', 'thinkpad', 'computer', 'pc', 'apple', 'dell', 'lenovo', 'hp'], risk_level: 'LOW', requires_review: true, impact_score: 7, auto_match_confidence: 0.80, default_private: false, fallback_category: 'expense_equipment', description_sv: 'Dator eller laptop (granska om aktivering krävs)', }, { id: 'it_phone', name_sv: 'Mobiltelefon', name_en: 'Mobile phone', group: 'it_software', direction: 'expense', entity_applicability: 'all', debit_account: '5410', credit_account: '1930', vat_treatment: 'standard_25', vat_rate: 0.25, deductibility: 'conditional', deductibility_note_sv: 'Blandad användning: bara yrkesmässig del avdragsgill', mcc_codes: [5065, 4812], keywords: ['mobil', 'telefon', 'iphone', 'samsung', 'phone', 'mobiltelefon'], risk_level: 'MEDIUM', requires_review: true, impact_score: 6, auto_match_confidence: 0.75, default_private: false, fallback_category: 'expense_equipment', description_sv: 'Mobiltelefon (granska yrkesmässig andel)', }, { id: 'it_ai_services', name_sv: 'AI-tjänster', name_en: 'AI services', group: 'it_software', direction: 'expense', entity_applicability: 'all', debit_account: '5420', credit_account: '1930', vat_treatment: 'reverse_charge', vat_rate: 0, deductibility: 'full', special_rules_sv: 'Ofta utländsk leverantör (USA) med omvänd skattskyldighet', mcc_codes: [], keywords: ['openai', 'chatgpt', 'anthropic', 'claude', 'ai', 'midjourney', 'copilot', 'mistral', 'claude', 'gemini'], risk_level: 'NONE', requires_review: false, impact_score: 7, auto_match_confidence: 0.85, default_private: false, fallback_category: 'expense_software', description_sv: 'AI-plattformar och tjänster (ChatGPT, Claude, etc.)', }, { id: 'it_antivirus', name_sv: 'Antivirus / Säkerhet', name_en: 'Antivirus / Security', group: 'it_software', direction: 'expense', entity_applicability: 'all', debit_account: '5420', credit_account: '1930', vat_treatment: 'standard_25', vat_rate: 0.25, deductibility: 'full', mcc_codes: [], keywords: ['antivirus', 'security', 'vpn', 'norton', 'kaspersky', '1password', 'lastpass', 'bitwarden'], risk_level: 'NONE', requires_review: false, impact_score: 4, auto_match_confidence: 0.80, default_private: false, fallback_category: 'expense_software', description_sv: 'Säkerhetsprogramvara och VPN-tjänster', }, // --- OFFICE SUPPLIES (26-31) --- { id: 'office_supplies_general', name_sv: 'Kontorsmaterial', name_en: 'Office supplies', group: 'office_supplies', direction: 'expense', entity_applicability: 'all', debit_account: '6110', credit_account: '1930', vat_treatment: 'standard_25', vat_rate: 0.25, deductibility: 'full', mcc_codes: [5111, 5112, 5943, 5944], keywords: ['kontorsmaterial', 'pennor', 'papper', 'office supplies', 'staples', 'kontorsvaror', 'kontor'], risk_level: 'NONE', requires_review: false, impact_score: 7, auto_match_confidence: 0.80, default_private: false, fallback_category: 'expense_office', description_sv: 'Kontorsmaterial och förbrukningsvaror', }, { id: 'office_printing', name_sv: 'Tryck & Kopiering', name_en: 'Printing & Copying', group: 'office_supplies', direction: 'expense', entity_applicability: 'all', debit_account: '6120', credit_account: '1930', vat_treatment: 'standard_25', vat_rate: 0.25, deductibility: 'full', mcc_codes: [2741, 2791], keywords: ['tryck', 'print', 'trycksaker', 'kopiering', 'visitkort', 'broschyr'], risk_level: 'NONE', requires_review: false, impact_score: 4, auto_match_confidence: 0.80, default_private: false, fallback_category: 'expense_office', description_sv: 'Trycksaker, kopior och kontorsutskrifter', }, { id: 'office_postage', name_sv: 'Porto & Frakt', name_en: 'Postage & Shipping', group: 'office_supplies', direction: 'expense', entity_applicability: 'all', debit_account: '6250', credit_account: '1930', vat_treatment: 'exempt', vat_rate: 0, deductibility: 'full', special_rules_sv: 'Posttjänster är momsfria, frakttjänster kan ha moms', mcc_codes: [4215, 4211], keywords: ['porto', 'postnord', 'frakt', 'shipping', 'dhl', 'ups', 'fedex', 'bring', 'paket'], risk_level: 'NONE', requires_review: false, impact_score: 6, auto_match_confidence: 0.85, default_private: false, fallback_category: 'expense_office', description_sv: 'Porto och fraktkostnader', }, { id: 'office_postage_vat', name_sv: 'Frakt (momsbelagd)', name_en: 'Shipping (with VAT)', group: 'office_supplies', direction: 'expense', entity_applicability: 'all', debit_account: '6250', credit_account: '1930', vat_treatment: 'standard_25', vat_rate: 0.25, deductibility: 'full', mcc_codes: [4215], keywords: ['frakt', 'shipping', 'dhl', 'ups', 'schenker', 'transport'], risk_level: 'NONE', requires_review: false, impact_score: 5, auto_match_confidence: 0.75, default_private: false, fallback_category: 'expense_office', description_sv: 'Frakttjänster med moms', }, { id: 'office_furniture_small', name_sv: 'Kontorsmöbler (förbrukningsinventarie)', name_en: 'Office furniture (consumable)', group: 'office_supplies', direction: 'expense', entity_applicability: 'all', debit_account: '5410', credit_account: '1930', vat_treatment: 'standard_25', vat_rate: 0.25, deductibility: 'full', mcc_codes: [5712, 5021], keywords: ['möbler', 'stol', 'skrivbord', 'ikea', 'furniture', 'kontorsstol', 'hyllor'], risk_level: 'LOW', requires_review: false, impact_score: 5, auto_match_confidence: 0.75, default_private: false, fallback_category: 'expense_equipment', description_sv: 'Kontorsmöbler under halva prisbasbeloppet', }, { id: 'office_books_lit', name_sv: 'Facklitteratur', name_en: 'Professional literature', group: 'office_supplies', direction: 'expense', entity_applicability: 'all', debit_account: '6970', credit_account: '1930', vat_treatment: 'reduced_6', vat_rate: 0.06, deductibility: 'full', special_rules_sv: 'Böcker har reducerad moms 6%', mcc_codes: [5192, 5942], keywords: ['bok', 'böcker', 'book', 'facklitteratur', 'adlibris', 'bokus', 'amazon'], risk_level: 'LOW', requires_review: false, impact_score: 4, auto_match_confidence: 0.75, default_private: false, fallback_category: 'expense_education', description_sv: 'Yrkesrelaterad facklitteratur (6% moms)', }, // --- MARKETING (32-38) --- { id: 'marketing_online_ads', name_sv: 'Digital annonsering', name_en: 'Online advertising', group: 'marketing', direction: 'expense', entity_applicability: 'all', debit_account: '5910', credit_account: '1930', vat_treatment: 'reverse_charge', vat_rate: 0, deductibility: 'full', special_rules_sv: 'Google/Meta fakturerar ofta från Irland → omvänd skattskyldighet', mcc_codes: [7311], keywords: ['google ads', 'facebook ads', 'meta ads', 'instagram ads', 'linkedin ads', 'annons', 'advertising', 'adwords', 'kampanj'], risk_level: 'NONE', requires_review: false, impact_score: 9, auto_match_confidence: 0.90, default_private: false, fallback_category: 'expense_marketing', description_sv: 'Digital annonsering (Google, Meta, LinkedIn etc.)', }, { id: 'marketing_print_ads', name_sv: 'Tryckt annonsering', name_en: 'Print advertising', group: 'marketing', direction: 'expense', entity_applicability: 'all', debit_account: '5910', credit_account: '1930', vat_treatment: 'standard_25', vat_rate: 0.25, deductibility: 'full', mcc_codes: [], keywords: ['annons', 'tidning', 'press', 'print ad', 'reklam'], risk_level: 'NONE', requires_review: false, impact_score: 3, auto_match_confidence: 0.75, default_private: false, fallback_category: 'expense_marketing', description_sv: 'Annonsering i tidningar och trycksaker', }, { id: 'marketing_design', name_sv: 'Grafisk design / Webbdesign', name_en: 'Graphic / Web design', group: 'marketing', direction: 'expense', entity_applicability: 'all', debit_account: '5920', credit_account: '1930', vat_treatment: 'standard_25', vat_rate: 0.25, deductibility: 'full', mcc_codes: [7333], keywords: ['design', 'grafisk', 'logotyp', 'webb', 'website', 'logo', 'branding', 'grafiker'], risk_level: 'NONE', requires_review: false, impact_score: 5, auto_match_confidence: 0.80, default_private: false, fallback_category: 'expense_marketing', description_sv: 'Grafisk design, logotyper och webbdesign', }, { id: 'marketing_promotional', name_sv: 'Reklamgåvor & Profilmaterial', name_en: 'Promotional items', group: 'marketing', direction: 'expense', entity_applicability: 'all', debit_account: '5930', credit_account: '1930', vat_treatment: 'standard_25', vat_rate: 0.25, deductibility: 'conditional', deductibility_note_sv: 'Max 300 kr/mottagare exkl moms för reklamgåvor', mcc_codes: [], keywords: ['reklamgåva', 'profilmaterial', 'promotional', 'giveaway', 'tröjor', 'pennor'], risk_level: 'MEDIUM', requires_review: true, impact_score: 3, auto_match_confidence: 0.70, default_private: false, fallback_category: 'expense_marketing', description_sv: 'Reklamgåvor och profilprodukter (max 300 kr/mottagare)', }, { id: 'marketing_seo_sem', name_sv: 'SEO / SEM-tjänster', name_en: 'SEO / SEM services', group: 'marketing', direction: 'expense', entity_applicability: 'all', debit_account: '5910', credit_account: '1930', vat_treatment: 'standard_25', vat_rate: 0.25, deductibility: 'full', mcc_codes: [], keywords: ['seo', 'sem', 'sökmotoroptimering', 'search engine'], risk_level: 'NONE', requires_review: false, impact_score: 4, auto_match_confidence: 0.80, default_private: false, fallback_category: 'expense_marketing', description_sv: 'Sökmotoroptimering och sökmarknadsföring', }, { id: 'marketing_social_media_tools', name_sv: 'Verktyg för sociala medier', name_en: 'Social media tools', group: 'marketing', direction: 'expense', entity_applicability: 'all', debit_account: '5920', credit_account: '1930', vat_treatment: 'standard_25', vat_rate: 0.25, deductibility: 'full', mcc_codes: [], keywords: ['hootsuite', 'buffer', 'later', 'canva', 'social media', 'sociala medier'], risk_level: 'NONE', requires_review: false, impact_score: 4, auto_match_confidence: 0.80, default_private: false, fallback_category: 'expense_marketing', description_sv: 'Verktyg för hantering av sociala medier', }, { id: 'marketing_photography', name_sv: 'Fotografering / Videoproduktion', name_en: 'Photography / Video production', group: 'marketing', direction: 'expense', entity_applicability: 'all', debit_account: '5920', credit_account: '1930', vat_treatment: 'standard_25', vat_rate: 0.25, deductibility: 'full', mcc_codes: [7332, 7829], keywords: ['foto', 'fotograf', 'video', 'film', 'photography', 'videoproduktion'], risk_level: 'NONE', requires_review: false, impact_score: 4, auto_match_confidence: 0.80, default_private: false, fallback_category: 'expense_marketing', description_sv: 'Professionell fotografering och videoproduktion', }, // --- TRAVEL (39-46) --- { id: 'travel_domestic_flight', name_sv: 'Inrikesflyg', name_en: 'Domestic flight', group: 'travel', direction: 'expense', entity_applicability: 'all', debit_account: '5810', credit_account: '1930', vat_treatment: 'reduced_6', vat_rate: 0.06, deductibility: 'full', special_rules_sv: 'Inrikesflyg har 6% moms', mcc_codes: [3000, 3001, 3002, 3003, 4511], keywords: ['flyg', 'sas', 'norwegian', 'bra', 'flight', 'boarding', 'flygbiljett'], risk_level: 'LOW', requires_review: false, impact_score: 6, auto_match_confidence: 0.80, default_private: false, fallback_category: 'expense_travel', description_sv: 'Inrikesflyg i tjänsten (6% moms)', }, { id: 'travel_international_flight', name_sv: 'Utrikesflyg', name_en: 'International flight', group: 'travel', direction: 'expense', entity_applicability: 'all', debit_account: '5810', credit_account: '1930', vat_treatment: 'export', vat_rate: 0, deductibility: 'full', special_rules_sv: 'Utrikesflyg är momsfritt', mcc_codes: [3000, 3001, 3002, 3003, 4511], keywords: ['flyg', 'flight', 'international', 'utrikes', 'airport'], risk_level: 'LOW', requires_review: false, impact_score: 5, auto_match_confidence: 0.75, default_private: false, fallback_category: 'expense_travel', description_sv: 'Utrikesflyg i tjänsten (momsfritt)', }, { id: 'travel_train', name_sv: 'Tåg & Kollektivtrafik', name_en: 'Train & Public transport', group: 'travel', direction: 'expense', entity_applicability: 'all', debit_account: '5810', credit_account: '1930', vat_treatment: 'reduced_6', vat_rate: 0.06, deductibility: 'full', special_rules_sv: 'Persontransport har 6% moms', mcc_codes: [4011, 4111, 4112, 4131], keywords: ['sj', 'tåg', 'train', 'sl', 'västtrafik', 'skånetrafiken', 'kollektivtrafik', 'pendeltåg', 'tunnelbana'], risk_level: 'NONE', requires_review: false, impact_score: 7, auto_match_confidence: 0.85, default_private: false, fallback_category: 'expense_travel', description_sv: 'Tåg och kollektivtrafik i tjänsten (6% moms)', }, { id: 'travel_taxi', name_sv: 'Taxi', name_en: 'Taxi', group: 'travel', direction: 'expense', entity_applicability: 'all', debit_account: '5810', credit_account: '1930', vat_treatment: 'reduced_6', vat_rate: 0.06, deductibility: 'full', mcc_codes: [4121], keywords: ['taxi', 'uber', 'bolt', 'cab', 'taxikurir', 'taxi stockholm', 'sverigetaxi'], risk_level: 'LOW', requires_review: false, impact_score: 6, auto_match_confidence: 0.90, default_private: false, fallback_category: 'expense_travel', description_sv: 'Taxi i tjänsten (6% moms)', }, { id: 'travel_hotel', name_sv: 'Hotell', name_en: 'Hotel', group: 'travel', direction: 'expense', entity_applicability: 'all', debit_account: '5820', credit_account: '1930', vat_treatment: 'reduced_12', vat_rate: 0.12, deductibility: 'full', special_rules_sv: 'Logi har 12% moms. Frukost särredovisas med 12% moms.', mcc_codes: [3501, 3502, 3503, 3504, 7011], keywords: ['hotell', 'hotel', 'logi', 'övernattning', 'scandic', 'elite', 'best western', 'booking', 'airbnb'], risk_level: 'LOW', requires_review: false, impact_score: 6, auto_match_confidence: 0.85, default_private: false, fallback_category: 'expense_travel', description_sv: 'Hotellövernattning i tjänsten (12% moms)', }, { id: 'travel_mileage', name_sv: 'Milersättning', name_en: 'Mileage allowance', group: 'travel', direction: 'expense', entity_applicability: 'aktiebolag', debit_account: '5811', credit_account: '1930', vat_treatment: null, vat_rate: 0, deductibility: 'full', special_rules_sv: 'Skattefritt 25 kr/mil (2024). Gäller egen bil i tjänsten.', mcc_codes: [], keywords: ['milersättning', 'mileage', 'körjournal', 'bilersättning'], risk_level: 'LOW', requires_review: true, impact_score: 5, auto_match_confidence: 0.70, default_private: false, fallback_category: 'expense_travel', description_sv: 'Milersättning vid användning av egen bil i tjänsten', }, { id: 'travel_per_diem', name_sv: 'Traktamente', name_en: 'Per diem', group: 'travel', direction: 'expense', entity_applicability: 'aktiebolag', debit_account: '5830', credit_account: '1930', vat_treatment: null, vat_rate: 0, deductibility: 'full', special_rules_sv: 'Skattefritt traktamente vid tjänsteresa med övernattning', mcc_codes: [], keywords: ['traktamente', 'per diem', 'dagtraktamente'], risk_level: 'LOW', requires_review: true, impact_score: 4, auto_match_confidence: 0.70, default_private: false, fallback_category: 'expense_travel', description_sv: 'Traktamente vid tjänsteresa med övernattning', }, { id: 'travel_rental_car', name_sv: 'Hyrbil', name_en: 'Rental car', group: 'travel', direction: 'expense', entity_applicability: 'all', debit_account: '5810', credit_account: '1930', vat_treatment: 'standard_25', vat_rate: 0.25, deductibility: 'full', mcc_codes: [7512], keywords: ['hyrbil', 'rental car', 'europcar', 'hertz', 'avis', 'sixt'], risk_level: 'LOW', requires_review: false, impact_score: 4, auto_match_confidence: 0.85, default_private: false, fallback_category: 'expense_travel', description_sv: 'Hyrbil i tjänsten', }, // --- REPRESENTATION (47-50) --- { id: 'representation_external', name_sv: 'Extern representation (måltid)', name_en: 'External representation (meal)', group: 'representation', direction: 'expense', entity_applicability: 'all', debit_account: '6071', credit_account: '1930', vat_treatment: 'reduced_12', vat_rate: 0.12, deductibility: 'conditional', deductibility_note_sv: 'Max 300 kr/person exkl moms (IL 16 kap 2§)', special_rules_sv: 'Dokumentera: syfte, deltagare, företag. Momsavdrag max 300 kr/person.', mcc_codes: [5812, 5813, 5814], keywords: ['representation', 'lunch', 'middag', 'restaurang', 'restaurant', 'kund', 'kundmöte'], risk_level: 'HIGH', requires_review: true, impact_score: 7, auto_match_confidence: 0.70, default_private: false, fallback_category: 'expense_other', description_sv: 'Representation med kund/affärspartner (dokumentera noggrant)', }, { id: 'representation_internal', name_sv: 'Intern representation (personalaktivitet)', name_en: 'Internal representation (staff event)', group: 'representation', direction: 'expense', entity_applicability: 'aktiebolag', debit_account: '6072', credit_account: '1930', vat_treatment: 'reduced_12', vat_rate: 0.12, deductibility: 'conditional', deductibility_note_sv: 'Max 300 kr/person exkl moms', special_rules_sv: 'Gäller personalfester, kick-offs etc. Max 2 tillfällen/år för fullt avdrag.', mcc_codes: [], keywords: ['personalfest', 'kick-off', 'teambuilding', 'julbord', 'sommarfest'], risk_level: 'MEDIUM', requires_review: true, impact_score: 3, auto_match_confidence: 0.70, default_private: false, fallback_category: 'expense_other', description_sv: 'Intern representation för personal', }, { id: 'representation_gift', name_sv: 'Representationsgåva', name_en: 'Business gift', group: 'representation', direction: 'expense', entity_applicability: 'all', debit_account: '6071', credit_account: '1930', vat_treatment: 'standard_25', vat_rate: 0.25, deductibility: 'conditional', deductibility_note_sv: 'Max 180 kr/mottagare för representationsgåvor', mcc_codes: [], keywords: ['gåva', 'present', 'gift', 'representationsgåva'], risk_level: 'HIGH', requires_review: true, impact_score: 2, auto_match_confidence: 0.70, default_private: false, fallback_category: 'expense_other', description_sv: 'Gåva till kund/affärspartner (max 180 kr/mottagare)', }, { id: 'representation_conference', name_sv: 'Konferens & Mässa', name_en: 'Conference & Trade show', group: 'representation', direction: 'expense', entity_applicability: 'all', debit_account: '5990', credit_account: '1930', vat_treatment: 'standard_25', vat_rate: 0.25, deductibility: 'full', mcc_codes: [7941, 7922], keywords: ['konferens', 'mässa', 'conference', 'trade show', 'event', 'utställning', 'biljett'], risk_level: 'LOW', requires_review: false, impact_score: 4, auto_match_confidence: 0.75, default_private: false, fallback_category: 'expense_education', description_sv: 'Avgifter för konferenser och mässor', }, // --- INSURANCE (51-54) --- { id: 'insurance_business', name_sv: 'Företagsförsäkring', name_en: 'Business insurance', group: 'insurance', direction: 'expense', entity_applicability: 'all', debit_account: '6310', credit_account: '1930', vat_treatment: 'exempt', vat_rate: 0, deductibility: 'full', special_rules_sv: 'Försäkringspremier är momsfria', mcc_codes: [6300], keywords: ['försäkring', 'insurance', 'företagsförsäkring', 'ansvarsförsäkring', 'if', 'trygg-hansa', 'länsförsäkringar'], risk_level: 'NONE', requires_review: false, impact_score: 6, auto_match_confidence: 0.85, default_private: false, fallback_category: 'expense_other', description_sv: 'Företagsförsäkring (egendoms-, ansvars- eller avbrottsförsäkring)', }, { id: 'insurance_liability', name_sv: 'Ansvarsförsäkring / Konsultförsäkring', name_en: 'Liability / Professional insurance', group: 'insurance', direction: 'expense', entity_applicability: 'all', debit_account: '6310', credit_account: '1930', vat_treatment: 'exempt', vat_rate: 0, deductibility: 'full', mcc_codes: [], keywords: ['ansvarsförsäkring', 'konsultförsäkring', 'liability', 'professional insurance'], risk_level: 'NONE', requires_review: false, impact_score: 5, auto_match_confidence: 0.80, default_private: false, fallback_category: 'expense_other', description_sv: 'Yrkes- eller ansvarsförsäkring', }, { id: 'insurance_pension_ef', name_sv: 'Pensionsförsäkring (EF)', name_en: 'Pension insurance (EF)', group: 'insurance', direction: 'expense', entity_applicability: 'enskild_firma', debit_account: '6530', credit_account: '1930', vat_treatment: 'exempt', vat_rate: 0, deductibility: 'conditional', deductibility_note_sv: 'Avdragsgill i NE-deklarationen, inte i bokföringen direkt', special_rules_sv: 'EF: Pensionssparande dras av i NE-blanketten, inte som kostnad i rörelsen.', mcc_codes: [], keywords: ['pension', 'pensionsförsäkring', 'itp', 'avanza pension', 'spp'], risk_level: 'MEDIUM', requires_review: true, impact_score: 4, auto_match_confidence: 0.75, default_private: false, fallback_category: 'expense_other', description_sv: 'Pensionssparande för enskild firma (granska avdragsregel)', }, { id: 'insurance_pension_ab', name_sv: 'Pensionsförsäkring (AB)', name_en: 'Pension insurance (AB)', group: 'insurance', direction: 'expense', entity_applicability: 'aktiebolag', debit_account: '7410', credit_account: '1930', vat_treatment: 'exempt', vat_rate: 0, deductibility: 'full', special_rules_sv: 'AB: Pensionskostnad är en avdragsgill personalkostnad', mcc_codes: [], keywords: ['pension', 'pensionsförsäkring', 'itp', 'tjänstepension'], risk_level: 'NONE', requires_review: false, impact_score: 5, auto_match_confidence: 0.80, default_private: false, fallback_category: 'expense_other', description_sv: 'Tjänstepension för anställda i aktiebolag', }, // --- PROFESSIONAL SERVICES (55-60) --- { id: 'prof_accounting', name_sv: 'Redovisningstjänster', name_en: 'Accounting services', group: 'professional_services', direction: 'expense', entity_applicability: 'all', debit_account: '6530', credit_account: '1930', vat_treatment: 'standard_25', vat_rate: 0.25, deductibility: 'full', mcc_codes: [8931], keywords: ['redovisning', 'bokföring', 'revisor', 'accounting', 'bokföringstjänst', 'redovisningsbyrå'], risk_level: 'NONE', requires_review: false, impact_score: 8, auto_match_confidence: 0.85, default_private: false, fallback_category: 'expense_professional_services', description_sv: 'Redovisning, bokföring och revisionstjänster', }, { id: 'prof_legal', name_sv: 'Juridiska tjänster', name_en: 'Legal services', group: 'professional_services', direction: 'expense', entity_applicability: 'all', debit_account: '6530', credit_account: '1930', vat_treatment: 'standard_25', vat_rate: 0.25, deductibility: 'full', mcc_codes: [8111], keywords: ['advokat', 'juridisk', 'legal', 'lawyer', 'jurist', 'avtal', 'kontrakt'], risk_level: 'NONE', requires_review: false, impact_score: 5, auto_match_confidence: 0.85, default_private: false, fallback_category: 'expense_professional_services', description_sv: 'Advokattjänster och juridisk rådgivning', }, { id: 'prof_consulting', name_sv: 'Konsulttjänster', name_en: 'Consulting services', group: 'professional_services', direction: 'expense', entity_applicability: 'all', debit_account: '6550', credit_account: '1930', vat_treatment: 'standard_25', vat_rate: 0.25, deductibility: 'full', mcc_codes: [7392], keywords: ['konsult', 'consulting', 'rådgivning', 'advisory', 'management'], risk_level: 'NONE', requires_review: false, impact_score: 6, auto_match_confidence: 0.80, default_private: false, fallback_category: 'expense_professional_services', description_sv: 'Konsultarvoden och rådgivningstjänster', }, { id: 'prof_bolagsverket', name_sv: 'Bolagsverket-avgifter', name_en: 'Companies Registration Office fees', group: 'professional_services', direction: 'expense', entity_applicability: 'all', debit_account: '6990', credit_account: '1930', vat_treatment: null, vat_rate: 0, deductibility: 'full', special_rules_sv: 'Myndighetsavgifter är inte momspliktiga', mcc_codes: [], keywords: ['bolagsverket', 'registrering', 'årsredovisning', 'companies registration'], risk_level: 'NONE', requires_review: false, impact_score: 4, auto_match_confidence: 0.90, default_private: false, fallback_category: 'expense_other', description_sv: 'Avgifter till Bolagsverket (registrering, årsredovisning etc.)', }, { id: 'prof_patent', name_sv: 'Patent & Varumärken', name_en: 'Patents & Trademarks', group: 'professional_services', direction: 'expense', entity_applicability: 'all', debit_account: '6990', credit_account: '1930', vat_treatment: null, vat_rate: 0, deductibility: 'full', mcc_codes: [], keywords: ['patent', 'varumärke', 'trademark', 'prv', 'immaterialrätt'], risk_level: 'NONE', requires_review: false, impact_score: 2, auto_match_confidence: 0.80, default_private: false, fallback_category: 'expense_other', description_sv: 'Patent- och varumärkesregistrering', }, { id: 'prof_recruitment', name_sv: 'Rekryteringstjänster', name_en: 'Recruitment services', group: 'professional_services', direction: 'expense', entity_applicability: 'aktiebolag', debit_account: '6540', credit_account: '1930', vat_treatment: 'standard_25', vat_rate: 0.25, deductibility: 'full', mcc_codes: [7361], keywords: ['rekrytering', 'recruitment', 'headhunting', 'personalförmedling'], risk_level: 'NONE', requires_review: false, impact_score: 3, auto_match_confidence: 0.80, default_private: false, fallback_category: 'expense_professional_services', description_sv: 'Rekrytering och personalförmedlingstjänster', }, // --- BANK & FINANCE (61-66) --- { id: 'bank_fees', name_sv: 'Bankavgifter', name_en: 'Bank fees', group: 'bank_finance', direction: 'expense', entity_applicability: 'all', debit_account: '6570', credit_account: '1930', vat_treatment: 'exempt', vat_rate: 0, deductibility: 'full', special_rules_sv: 'Banktjänster är momsfria', mcc_codes: [6010, 6011, 6012], keywords: ['bankavgift', 'bank fee', 'kontoavgift', 'årsavgift', 'månadsavgift'], risk_level: 'NONE', requires_review: false, impact_score: 9, auto_match_confidence: 0.90, default_private: false, fallback_category: 'expense_bank_fees', description_sv: 'Bankavgifter och kontoavgifter', }, { id: 'bank_card_fees', name_sv: 'Kortavgifter / Betalterminal', name_en: 'Card fees / Payment terminal', group: 'bank_finance', direction: 'expense', entity_applicability: 'all', debit_account: '6570', credit_account: '1930', vat_treatment: 'exempt', vat_rate: 0, deductibility: 'full', mcc_codes: [], keywords: ['kortavgift', 'zettle', 'izettle', 'stripe', 'klarna', 'swish', 'betalterminal', 'card fee', 'nets'], risk_level: 'NONE', requires_review: false, impact_score: 8, auto_match_confidence: 0.85, default_private: false, fallback_category: 'expense_card_fees', description_sv: 'Kortavgifter och betalterminalavgifter', }, { id: 'bank_interest_income', name_sv: 'Ränteintäkt', name_en: 'Interest income', group: 'bank_finance', direction: 'income', entity_applicability: 'all', debit_account: '1930', credit_account: '8310', vat_treatment: null, vat_rate: 0, deductibility: 'full', mcc_codes: [], keywords: ['ränta', 'ränteinkomst', 'interest income', 'sparränta'], risk_level: 'NONE', requires_review: false, impact_score: 5, auto_match_confidence: 0.80, default_private: false, fallback_category: 'income_other', description_sv: 'Ränteintäkter på bankkonto eller placeringar', }, { id: 'bank_interest_expense', name_sv: 'Räntekostnad', name_en: 'Interest expense', group: 'bank_finance', direction: 'expense', entity_applicability: 'all', debit_account: '8410', credit_account: '1930', vat_treatment: null, vat_rate: 0, deductibility: 'full', mcc_codes: [], keywords: ['ränta', 'räntekostnad', 'interest expense', 'låneränta'], risk_level: 'NONE', requires_review: false, impact_score: 5, auto_match_confidence: 0.80, default_private: false, fallback_category: 'expense_other', description_sv: 'Räntekostnad på lån eller kredit', }, { id: 'bank_currency_loss', name_sv: 'Valutakursförlust', name_en: 'Currency exchange loss', group: 'bank_finance', direction: 'expense', entity_applicability: 'all', debit_account: '7960', credit_account: '1930', vat_treatment: null, vat_rate: 0, deductibility: 'full', mcc_codes: [6051], keywords: ['valuta', 'currency', 'växling', 'kursförlust', 'exchange'], risk_level: 'NONE', requires_review: false, impact_score: 4, auto_match_confidence: 0.80, default_private: false, fallback_category: 'expense_currency_exchange', description_sv: 'Valutakursförluster vid betalning i utländsk valuta', }, { id: 'bank_currency_gain', name_sv: 'Valutakursvinst', name_en: 'Currency exchange gain', group: 'bank_finance', direction: 'income', entity_applicability: 'all', debit_account: '1930', credit_account: '3960', vat_treatment: null, vat_rate: 0, deductibility: 'full', mcc_codes: [], keywords: ['valuta', 'currency', 'kursvinst', 'exchange gain'], risk_level: 'NONE', requires_review: false, impact_score: 3, auto_match_confidence: 0.75, default_private: false, fallback_category: 'income_other', description_sv: 'Valutakursvinster vid betalning i utländsk valuta', }, // --- TELECOM (67-70) --- { id: 'telecom_mobile', name_sv: 'Mobilabonnemang', name_en: 'Mobile subscription', group: 'telecom', direction: 'expense', entity_applicability: 'all', debit_account: '6211', credit_account: '1930', vat_treatment: 'standard_25', vat_rate: 0.25, deductibility: 'conditional', deductibility_note_sv: 'Blandad användning: bara yrkesmässig del avdragsgill', mcc_codes: [4812, 4814], keywords: ['mobil', 'tele2', 'telia', 'tre', 'telenor', 'hallon', 'comviq', 'mobilabonnemang', 'telefon'], risk_level: 'LOW', requires_review: false, impact_score: 8, auto_match_confidence: 0.85, default_private: false, fallback_category: 'expense_other', description_sv: 'Mobilabonnemang (granska yrkesmässig andel)', }, { id: 'telecom_internet', name_sv: 'Internetanslutning', name_en: 'Internet connection', group: 'telecom', direction: 'expense', entity_applicability: 'all', debit_account: '6230', credit_account: '1930', vat_treatment: 'standard_25', vat_rate: 0.25, deductibility: 'full', mcc_codes: [4816], keywords: ['internet', 'bredband', 'fiber', 'broadband', 'bahnhof', 'telia', 'comhem'], risk_level: 'NONE', requires_review: false, impact_score: 7, auto_match_confidence: 0.85, default_private: false, fallback_category: 'expense_other', description_sv: 'Internetanslutning för kontor', }, { id: 'telecom_landline', name_sv: 'Fast telefoni', name_en: 'Landline phone', group: 'telecom', direction: 'expense', entity_applicability: 'all', debit_account: '6212', credit_account: '1930', vat_treatment: 'standard_25', vat_rate: 0.25, deductibility: 'full', mcc_codes: [4813], keywords: ['fast telefon', 'landline', 'växel', 'ip-telefoni', 'voip'], risk_level: 'NONE', requires_review: false, impact_score: 3, auto_match_confidence: 0.80, default_private: false, fallback_category: 'expense_other', description_sv: 'Fast telefoni och IP-telefoni', }, { id: 'telecom_coworking', name_sv: 'Coworking / Kontorshotell', name_en: 'Coworking space', group: 'telecom', direction: 'expense', entity_applicability: 'all', debit_account: '5010', credit_account: '1930', vat_treatment: 'standard_25', vat_rate: 0.25, deductibility: 'full', mcc_codes: [], keywords: ['coworking', 'kontorshotell', 'wework', 'norrsken', 'epicenter', 'united spaces', 'regus'], risk_level: 'NONE', requires_review: false, impact_score: 6, auto_match_confidence: 0.85, default_private: false, fallback_category: 'expense_office', description_sv: 'Coworking-plats eller kontorshotell', }, // --- EDUCATION (71-73) --- { id: 'education_course', name_sv: 'Kurs / Utbildning', name_en: 'Course / Training', group: 'education', direction: 'expense', entity_applicability: 'all', debit_account: '6991', credit_account: '1930', debit_account_ab: '7610', vat_treatment: 'exempt', vat_rate: 0, deductibility: 'full', special_rules_sv: 'Utbildningstjänster är momsfria. AB: konto 7610, EF: konto 6991.', mcc_codes: [8220, 8241, 8244, 8249, 8299], keywords: ['kurs', 'utbildning', 'course', 'training', 'workshop', 'certifiering', 'certification'], risk_level: 'NONE', requires_review: false, impact_score: 6, auto_match_confidence: 0.80, default_private: false, fallback_category: 'expense_education', description_sv: 'Yrkesrelaterade kurser och utbildningar', }, { id: 'education_online_course', name_sv: 'Onlinekurs (EU/utländsk)', name_en: 'Online course (EU/foreign)', group: 'education', direction: 'expense', entity_applicability: 'all', debit_account: '6991', credit_account: '1930', debit_account_ab: '7610', vat_treatment: 'reverse_charge', vat_rate: 0, deductibility: 'full', special_rules_sv: 'Utländska onlinekurser kan vara tjänst med omvänd skattskyldighet', mcc_codes: [5815, 5816], keywords: ['udemy', 'coursera', 'pluralsight', 'linkedin learning', 'skillshare', 'online course'], risk_level: 'NONE', requires_review: false, impact_score: 5, auto_match_confidence: 0.80, default_private: false, fallback_category: 'expense_education', description_sv: 'Onlinekurser från utländska plattformar', }, { id: 'education_membership', name_sv: 'Branschförening / Medlemskap', name_en: 'Trade association / Membership', group: 'education', direction: 'expense', entity_applicability: 'all', debit_account: '6980', credit_account: '1930', vat_treatment: 'exempt', vat_rate: 0, deductibility: 'full', special_rules_sv: 'Föreningsavgifter är normalt momsfria', mcc_codes: [8641, 8651, 8661, 8699], keywords: ['medlemskap', 'membership', 'förening', 'branschorganisation', 'förbund', 'svenskt näringsliv', 'företagarna'], risk_level: 'NONE', requires_review: false, impact_score: 4, auto_match_confidence: 0.80, default_private: false, fallback_category: 'expense_education', description_sv: 'Medlemsavgift i branschorganisation eller yrkesförening', }, // --- PERSONNEL (74-78) --- { id: 'personnel_salary', name_sv: 'Lön (netto)', name_en: 'Salary (net)', group: 'personnel', direction: 'expense', entity_applicability: 'aktiebolag', debit_account: '7210', credit_account: '1930', vat_treatment: null, vat_rate: 0, deductibility: 'full', mcc_codes: [], keywords: ['lön', 'salary', 'nettolön', 'löneutbetalning'], risk_level: 'NONE', requires_review: true, impact_score: 8, auto_match_confidence: 0.80, default_private: false, fallback_category: 'expense_other', description_sv: 'Nettolön till anställd', }, { id: 'personnel_employer_tax', name_sv: 'Arbetsgivaravgifter', name_en: 'Employer social contributions', group: 'personnel', direction: 'expense', entity_applicability: 'aktiebolag', debit_account: '7510', credit_account: '1930', vat_treatment: null, vat_rate: 0, deductibility: 'full', mcc_codes: [], keywords: ['arbetsgivaravgift', 'sociala avgifter', 'employer tax', 'skattekonto'], risk_level: 'NONE', requires_review: true, impact_score: 7, auto_match_confidence: 0.75, default_private: false, fallback_category: 'expense_other', description_sv: 'Arbetsgivaravgifter (31.42%)', }, { id: 'personnel_preliminary_tax', name_sv: 'Preliminärskatt (AB)', name_en: 'Preliminary tax (AB)', group: 'personnel', direction: 'expense', entity_applicability: 'aktiebolag', debit_account: '2510', credit_account: '1930', vat_treatment: null, vat_rate: 0, deductibility: 'full', mcc_codes: [], keywords: ['preliminärskatt', 'skatt', 'tax', 'skatteverket', 'skattekonto'], risk_level: 'NONE', requires_review: true, impact_score: 7, auto_match_confidence: 0.75, default_private: false, fallback_category: 'expense_other', description_sv: 'Preliminärskatt till Skatteverket', }, { id: 'personnel_friskvard', name_sv: 'Friskvårdsbidrag', name_en: 'Wellness benefit', group: 'personnel', direction: 'expense', entity_applicability: 'aktiebolag', debit_account: '7690', credit_account: '1930', vat_treatment: null, vat_rate: 0, deductibility: 'conditional', deductibility_note_sv: 'Skattefritt max 5 000 kr/anställd/år (2024)', special_rules_sv: 'Friskvård är momsfri. Max 5 000 kr/år per anställd för skattefrihet.', mcc_codes: [7941, 7991, 7992, 7997], keywords: ['friskvård', 'gym', 'träning', 'wellness', 'friskvårdsbidrag'], risk_level: 'MEDIUM', requires_review: true, impact_score: 5, auto_match_confidence: 0.75, default_private: false, fallback_category: 'expense_other', description_sv: 'Friskvårdsbidrag till anställd (max 5 000 kr/år)', }, { id: 'personnel_staff_food', name_sv: 'Personalmat / Fika', name_en: 'Staff meals / Coffee', group: 'personnel', direction: 'expense', entity_applicability: 'aktiebolag', debit_account: '7690', credit_account: '1930', vat_treatment: 'reduced_12', vat_rate: 0.12, deductibility: 'conditional', deductibility_note_sv: 'Enklare förtäring (kaffe, frukt) avdragsgillt utan beloppsgräns. Mat = förmånsbeskattning.', mcc_codes: [5812, 5814], keywords: ['fika', 'kaffe', 'frukt', 'personalmat', 'staff food'], risk_level: 'LOW', requires_review: false, impact_score: 4, auto_match_confidence: 0.70, default_private: false, fallback_category: 'expense_other', description_sv: 'Enklare förtäring för personal (kaffe, frukt etc.)', }, // --- REVENUE (79-85) --- { id: 'revenue_services_25', name_sv: 'Tjänsteförsäljning 25% moms', name_en: 'Service revenue 25% VAT', group: 'revenue', direction: 'income', entity_applicability: 'all', debit_account: '1930', credit_account: '3001', vat_treatment: 'standard_25', vat_rate: 0.25, deductibility: 'full', mcc_codes: [], keywords: ['konsult', 'tjänst', 'service', 'arvode', 'fee', 'faktura'], risk_level: 'NONE', requires_review: false, impact_score: 10, auto_match_confidence: 0.80, default_private: false, fallback_category: 'income_services', description_sv: 'Intäkter från tjänsteförsäljning med 25% moms', }, { id: 'revenue_products_25', name_sv: 'Varuförsäljning 25% moms', name_en: 'Product revenue 25% VAT', group: 'revenue', direction: 'income', entity_applicability: 'all', debit_account: '1930', credit_account: '3001', vat_treatment: 'standard_25', vat_rate: 0.25, deductibility: 'full', mcc_codes: [], keywords: ['försäljning', 'vara', 'produkt', 'product', 'sale'], risk_level: 'NONE', requires_review: false, impact_score: 9, auto_match_confidence: 0.75, default_private: false, fallback_category: 'income_products', description_sv: 'Intäkter från varuförsäljning med 25% moms', }, { id: 'revenue_products_12', name_sv: 'Varuförsäljning 12% moms (livsmedel)', name_en: 'Product revenue 12% VAT (food)', group: 'revenue', direction: 'income', entity_applicability: 'all', debit_account: '1930', credit_account: '3002', vat_treatment: 'reduced_12', vat_rate: 0.12, deductibility: 'full', mcc_codes: [], keywords: ['livsmedel', 'mat', 'food', 'restaurang'], risk_level: 'NONE', requires_review: false, impact_score: 6, auto_match_confidence: 0.75, default_private: false, fallback_category: 'income_products', description_sv: 'Intäkter från livsmedelsförsäljning med 12% moms', }, { id: 'revenue_products_6', name_sv: 'Försäljning 6% moms (kultur/böcker)', name_en: 'Revenue 6% VAT (culture/books)', group: 'revenue', direction: 'income', entity_applicability: 'all', debit_account: '1930', credit_account: '3003', vat_treatment: 'reduced_6', vat_rate: 0.06, deductibility: 'full', mcc_codes: [], keywords: ['kultur', 'bok', 'tidning', 'evenemang', 'culture', 'book'], risk_level: 'NONE', requires_review: false, impact_score: 4, auto_match_confidence: 0.75, default_private: false, fallback_category: 'income_products', description_sv: 'Intäkter med 6% moms (böcker, kultur, evenemang)', }, { id: 'revenue_eu_services', name_sv: 'Tjänsteförsäljning EU (omvänd moms)', name_en: 'Service revenue EU (reverse charge)', group: 'revenue', direction: 'income', entity_applicability: 'all', debit_account: '1930', credit_account: '3308', vat_treatment: 'reverse_charge', vat_rate: 0, deductibility: 'full', special_rules_sv: 'Tjänsteförsäljning till EU-företag → omvänd skattskyldighet, rapportera i ruta 39', mcc_codes: [], keywords: ['eu', 'export', 'eu service', 'reverse charge', 'utlandsförsäljning'], risk_level: 'LOW', requires_review: false, impact_score: 6, auto_match_confidence: 0.75, default_private: false, fallback_category: 'income_services', description_sv: 'Tjänsteförsäljning till EU-företag (momsfritt, ruta 39)', }, { id: 'revenue_export', name_sv: 'Export utanför EU', name_en: 'Export outside EU', group: 'revenue', direction: 'income', entity_applicability: 'all', debit_account: '1930', credit_account: '3305', vat_treatment: 'export', vat_rate: 0, deductibility: 'full', special_rules_sv: 'Export utanför EU → momsfritt, rapportera i ruta 40', mcc_codes: [], keywords: ['export', 'utland', 'usa', 'utanför eu', 'non-eu'], risk_level: 'LOW', requires_review: false, impact_score: 5, auto_match_confidence: 0.75, default_private: false, fallback_category: 'income_services', description_sv: 'Export av varor/tjänster utanför EU (momsfritt, ruta 40)', }, { id: 'revenue_other', name_sv: 'Övrig intäkt', name_en: 'Other revenue', group: 'revenue', direction: 'income', entity_applicability: 'all', debit_account: '1930', credit_account: '3900', vat_treatment: null, vat_rate: 0, deductibility: 'full', mcc_codes: [], keywords: ['övrigt', 'miscellaneous', 'other income', 'övrig intäkt'], risk_level: 'NONE', requires_review: false, impact_score: 5, auto_match_confidence: 0.70, default_private: false, fallback_category: 'income_other', description_sv: 'Övriga rörelseintäkter', }, // --- FINANCIAL (86-88) --- { id: 'financial_loan_repayment', name_sv: 'Amortering lån', name_en: 'Loan repayment', group: 'financial', direction: 'transfer', entity_applicability: 'all', debit_account: '2350', credit_account: '1930', vat_treatment: null, vat_rate: 0, deductibility: 'full', mcc_codes: [], keywords: ['amortering', 'lån', 'loan', 'repayment', 'avbetalning'], risk_level: 'NONE', requires_review: true, impact_score: 5, auto_match_confidence: 0.75, default_private: false, fallback_category: 'expense_other', description_sv: 'Amortering av banklån', }, { id: 'financial_tax_account', name_sv: 'Insättning skattekonto', name_en: 'Tax account deposit', group: 'financial', direction: 'transfer', entity_applicability: 'all', debit_account: '1630', credit_account: '1930', vat_treatment: null, vat_rate: 0, deductibility: 'full', mcc_codes: [], keywords: ['skattekonto', 'skatteverket', 'tax account', 'f-skatt', 'moms inbetalning'], risk_level: 'NONE', requires_review: true, impact_score: 8, auto_match_confidence: 0.85, default_private: false, fallback_category: 'expense_other', description_sv: 'Insättning på skattekonto hos Skatteverket', }, { id: 'financial_savings_transfer', name_sv: 'Överföring till sparkonto', name_en: 'Transfer to savings account', group: 'financial', direction: 'transfer', entity_applicability: 'all', debit_account: '1940', credit_account: '1930', vat_treatment: null, vat_rate: 0, deductibility: 'full', mcc_codes: [], keywords: ['sparkonto', 'savings', 'överföring', 'transfer'], risk_level: 'NONE', requires_review: false, impact_score: 4, auto_match_confidence: 0.70, default_private: false, fallback_category: 'expense_other', description_sv: 'Överföring mellan företagets bankkonton', }, // --- PRIVATE TRANSFERS (89-92) --- { id: 'private_withdrawal_ef', name_sv: 'Eget uttag (EF)', name_en: 'Owner withdrawal (EF)', group: 'private_transfers', direction: 'transfer', entity_applicability: 'enskild_firma', debit_account: '2013', credit_account: '1930', vat_treatment: null, vat_rate: 0, deductibility: 'non_deductible', mcc_codes: [], keywords: ['eget uttag', 'privat', 'withdrawal', 'egen insättning'], risk_level: 'NONE', requires_review: false, impact_score: 10, auto_match_confidence: 0.85, default_private: true, fallback_category: 'private', description_sv: 'Privat uttag från företagskonto (enskild firma)', }, { id: 'private_deposit_ef', name_sv: 'Egen insättning (EF)', name_en: 'Owner deposit (EF)', group: 'private_transfers', direction: 'transfer', entity_applicability: 'enskild_firma', debit_account: '1930', credit_account: '2018', vat_treatment: null, vat_rate: 0, deductibility: 'non_deductible', mcc_codes: [], keywords: ['egen insättning', 'insättning', 'deposit', 'tillskott'], risk_level: 'NONE', requires_review: false, impact_score: 8, auto_match_confidence: 0.80, default_private: true, fallback_category: 'private', description_sv: 'Egen insättning till företagskonto (enskild firma)', }, { id: 'private_shareholder_loan', name_sv: 'Skuld till aktieägare (AB)', name_en: 'Shareholder loan (AB)', group: 'private_transfers', direction: 'transfer', entity_applicability: 'aktiebolag', debit_account: '2893', credit_account: '1930', vat_treatment: null, vat_rate: 0, deductibility: 'non_deductible', mcc_codes: [], keywords: ['aktieägare', 'lån', 'shareholder', 'skuld till ägare'], risk_level: 'LOW', requires_review: true, impact_score: 6, auto_match_confidence: 0.75, default_private: true, fallback_category: 'private', description_sv: 'Utbetalning bokförd som skuld till aktieägare', }, { id: 'private_expense', name_sv: 'Privat kostnad', name_en: 'Private expense', group: 'private_transfers', direction: 'expense', entity_applicability: 'all', debit_account: '2013', credit_account: '1930', debit_account_ab: '2893', vat_treatment: null, vat_rate: 0, deductibility: 'non_deductible', mcc_codes: [], keywords: ['privat', 'private', 'personlig', 'personal'], risk_level: 'NONE', requires_review: false, impact_score: 9, auto_match_confidence: 0.85, default_private: true, fallback_category: 'private', description_sv: 'Privat kostnad betald från företagskonto', }, // --- EQUIPMENT (93-95) --- { id: 'equipment_tools', name_sv: 'Verktyg & Maskiner (förbrukning)', name_en: 'Tools & Machinery (consumable)', group: 'equipment', direction: 'expense', entity_applicability: 'all', debit_account: '5410', credit_account: '1930', vat_treatment: 'standard_25', vat_rate: 0.25, deductibility: 'full', mcc_codes: [5200, 5251, 5261, 5072], keywords: ['verktyg', 'maskin', 'tool', 'machine', 'biltema', 'jula', 'bauhaus', 'hornbach'], risk_level: 'NONE', requires_review: false, impact_score: 5, auto_match_confidence: 0.80, default_private: false, fallback_category: 'expense_equipment', description_sv: 'Verktyg och maskiner under halva prisbasbeloppet', }, { id: 'equipment_capital', name_sv: 'Inventarie (aktivering)', name_en: 'Capital equipment (capitalize)', group: 'equipment', direction: 'expense', entity_applicability: 'all', debit_account: '1250', credit_account: '1930', vat_treatment: 'standard_25', vat_rate: 0.25, deductibility: 'full', special_rules_sv: 'Över halva prisbasbeloppet: aktivera och skriv av. Avskrivning konto 7832.', mcc_codes: [], keywords: ['inventarie', 'anläggningstillgång', 'capital', 'aktivering', 'avskrivning'], risk_level: 'LOW', requires_review: true, impact_score: 4, auto_match_confidence: 0.70, default_private: false, fallback_category: 'expense_equipment', description_sv: 'Inventarie som ska aktiveras och skrivas av', }, { id: 'equipment_work_clothes', name_sv: 'Arbetskläder / Skyddsutrustning', name_en: 'Work clothes / Safety equipment', group: 'equipment', direction: 'expense', entity_applicability: 'all', debit_account: '5480', credit_account: '1930', vat_treatment: 'standard_25', vat_rate: 0.25, deductibility: 'conditional', deductibility_note_sv: 'Normala kläder ej avdragsgilla. Skyddskläder/uniformer med logotyp OK.', special_rules_sv: 'Vardagskläder ej avdragsgilla (RÅ 1988 ref. 35). Skyddskläder och arbetskläder med tydlig företagsprofil OK.', mcc_codes: [5651, 5691, 5699], keywords: ['arbetskläder', 'skyddsutrustning', 'uniform', 'safety', 'skyddsskor'], risk_level: 'HIGH', requires_review: true, impact_score: 4, auto_match_confidence: 0.70, default_private: false, fallback_category: 'expense_other', description_sv: 'Arbetskläder och skyddsutrustning (ej vardagskläder)', }, // --- SUBSCRIPTIONS (96-100) --- { id: 'subscription_accounting_software', name_sv: 'Bokföringsprogram', name_en: 'Accounting software', group: 'subscriptions', direction: 'expense', entity_applicability: 'all', debit_account: '6530', credit_account: '1930', vat_treatment: 'standard_25', vat_rate: 0.25, deductibility: 'full', mcc_codes: [], keywords: ['bokföring', 'fortnox', 'visma', 'bokio', 'dooer', 'accounting software'], risk_level: 'NONE', requires_review: false, impact_score: 8, auto_match_confidence: 0.90, default_private: false, fallback_category: 'expense_professional_services', description_sv: 'Prenumeration på bokföringsprogramvara', }, { id: 'subscription_project_tools', name_sv: 'Projektverktyg', name_en: 'Project management tools', group: 'subscriptions', direction: 'expense', entity_applicability: 'all', debit_account: '5420', credit_account: '1930', vat_treatment: 'reverse_charge', vat_rate: 0, deductibility: 'full', special_rules_sv: 'Ofta utländsk leverantör med omvänd skattskyldighet', mcc_codes: [], keywords: ['asana', 'monday', 'trello', 'basecamp', 'clickup', 'linear', 'project management'], risk_level: 'NONE', requires_review: false, impact_score: 5, auto_match_confidence: 0.85, default_private: false, fallback_category: 'expense_software', description_sv: 'Projekthanteringsverktyg', }, { id: 'subscription_email_marketing', name_sv: 'E-postmarknadsföring', name_en: 'Email marketing', group: 'subscriptions', direction: 'expense', entity_applicability: 'all', debit_account: '5920', credit_account: '1930', vat_treatment: 'reverse_charge', vat_rate: 0, deductibility: 'full', mcc_codes: [], keywords: ['mailchimp', 'sendgrid', 'mailgun', 'brevo', 'email marketing', 'nyhetsbrev', 'newsletter'], risk_level: 'NONE', requires_review: false, impact_score: 4, auto_match_confidence: 0.85, default_private: false, fallback_category: 'expense_marketing', description_sv: 'Verktyg för e-postmarknadsföring och nyhetsbrev', }, { id: 'subscription_music_streaming', name_sv: 'Musik-/Mediestreaming (företag)', name_en: 'Music/Media streaming (business)', group: 'subscriptions', direction: 'expense', entity_applicability: 'all', debit_account: '5420', credit_account: '1930', vat_treatment: 'standard_25', vat_rate: 0.25, deductibility: 'conditional', deductibility_note_sv: 'Avdragsgillt bara om det används yrkesmässigt (t.ex. bakgrundsmusik i butik)', mcc_codes: [5815, 4899], keywords: ['spotify', 'apple music', 'streaming', 'musik'], risk_level: 'MEDIUM', requires_review: true, impact_score: 3, auto_match_confidence: 0.70, default_private: true, fallback_category: 'expense_other', description_sv: 'Musikstreaming (ofta privat, granska yrkesmässig användning)', }, { id: 'subscription_crm', name_sv: 'CRM-system', name_en: 'CRM system', group: 'subscriptions', direction: 'expense', entity_applicability: 'all', debit_account: '5420', credit_account: '1930', vat_treatment: 'reverse_charge', vat_rate: 0, deductibility: 'full', special_rules_sv: 'Ofta utländsk leverantör med omvänd skattskyldighet', mcc_codes: [], keywords: ['hubspot', 'salesforce', 'pipedrive', 'crm', 'kundhantering'], risk_level: 'NONE', requires_review: false, impact_score: 5, auto_match_confidence: 0.85, default_private: false, fallback_category: 'expense_software', description_sv: 'CRM-system för kundhantering', }, { id: 'subscription_storage', name_sv: 'Molnlagring', name_en: 'Cloud storage', group: 'subscriptions', direction: 'expense', entity_applicability: 'all', debit_account: '5420', credit_account: '1930', vat_treatment: 'reverse_charge', vat_rate: 0, deductibility: 'full', special_rules_sv: 'Ofta utländsk leverantör med omvänd skattskyldighet', mcc_codes: [], keywords: ['dropbox', 'google drive', 'onedrive', 'icloud', 'storage', 'lagring', 'backup'], risk_level: 'NONE', requires_review: false, impact_score: 5, auto_match_confidence: 0.85, default_private: false, fallback_category: 'expense_software', description_sv: 'Molnlagring och backup-tjänster', }, ] // ============================================================ // Lookup Indexes (built once at module load) // ============================================================ const templateById = new Map() const templatesByGroup = new Map() const templatesByMcc = new Map() for (const t of BOOKING_TEMPLATES) { templateById.set(t.id, t) const groupList = templatesByGroup.get(t.group) || [] groupList.push(t) templatesByGroup.set(t.group, groupList) for (const mcc of t.mcc_codes) { const mccList = templatesByMcc.get(mcc) || [] mccList.push(t) templatesByMcc.set(mcc, mccList) } } // ============================================================ // Public API // ============================================================ /** O(1) lookup by template ID */ export function getTemplateById(id: string): BookingTemplate | undefined { return templateById.get(id) } /** Get all templates in a group */ export function getTemplatesByGroup(group: TemplateGroup): BookingTemplate[] { return templatesByGroup.get(group) || [] } /** Get templates matching a specific MCC code */ export function getTemplatesByMcc(mcc: number): BookingTemplate[] { return templatesByMcc.get(mcc) || [] } /** Get all groups with labels and their templates */ export function getTemplateGroups(): TemplateGroupInfo[] { const groups: TemplateGroupInfo[] = [] for (const [group, labels] of Object.entries(GROUP_LABELS)) { const g = group as TemplateGroup groups.push({ group: g, label_sv: labels.sv, label_en: labels.en, templates: templatesByGroup.get(g) || [], }) } return groups } /** * Fuzzy search templates by name, keywords, or description. * Optionally filter by entity type. */ export function searchTemplates(query: string, entityType?: EntityType): BookingTemplate[] { if (!query.trim()) return [] const q = query.toLowerCase() const tokens = q.split(/\s+/).filter(Boolean) return BOOKING_TEMPLATES.filter((t) => { // Filter by entity applicability if (entityType && t.entity_applicability !== 'all' && t.entity_applicability !== entityType) { return false } // Check if all tokens match somewhere return tokens.every((token) => t.name_sv.toLowerCase().includes(token) || t.name_en.toLowerCase().includes(token) || t.description_sv.toLowerCase().includes(token) || t.keywords.some((kw) => kw.toLowerCase().includes(token)) || t.id.includes(token) ) }) } /** * Multi-signal matching against a transaction. * Returns top 5 matches sorted by confidence descending. */ export function findMatchingTemplates( transaction: Transaction, entityType?: EntityType ): TemplateMatch[] { const results: TemplateMatch[] = [] const isExpense = transaction.amount < 0 const isIncome = transaction.amount > 0 const descLower = (transaction.description || '').toLowerCase() const merchantLower = (transaction.merchant_name || '').toLowerCase() const searchText = `${descLower} ${merchantLower}` for (const t of BOOKING_TEMPLATES) { // Filter entity applicability if (entityType && t.entity_applicability !== 'all' && t.entity_applicability !== entityType) { continue } // Filter direction if (t.direction === 'expense' && !isExpense) continue if (t.direction === 'income' && !isIncome) continue // 'transfer' templates match both directions let score = 0 // MCC exact match: +0.4 if (transaction.mcc_code && t.mcc_codes.includes(transaction.mcc_code)) { score += 0.4 } // Keyword matches in description + merchant: +0.3 (proportional) if (t.keywords.length > 0) { let matchedKeywords = 0 for (const kw of t.keywords) { if (searchText.includes(kw.toLowerCase())) { matchedKeywords++ } } if (matchedKeywords > 0) { score += 0.3 * Math.min(matchedKeywords / Math.min(t.keywords.length, 3), 1) } } // Direction match bonus: +0.1 (only if there's already a signal) if (score > 0 && ((t.direction === 'expense' && isExpense) || (t.direction === 'income' && isIncome))) { score += 0.1 } if (score > 0) { const confidence = Math.round(score * t.auto_match_confidence * 100) / 100 results.push({ template: t, confidence }) } } return results .sort((a, b) => b.confidence - a.confidence) .slice(0, 20) } /** * Convert a booking template into a MappingResult. * Follows the same pattern as buildMappingResultFromCategory in category-mapping.ts. */ export function buildMappingResultFromTemplate( template: BookingTemplate, transaction: Transaction, entityType: EntityType = 'enskild_firma' ): MappingResult { const isExpense = transaction.amount < 0 const isBusiness = !template.default_private // Resolve entity-specific accounts let debitAccount = template.debit_account let creditAccount = template.credit_account if (entityType === 'aktiebolag') { if (template.debit_account_ab) debitAccount = template.debit_account_ab if (template.credit_account_ab) creditAccount = template.credit_account_ab } // Generate VAT lines const vatLines: VatJournalLine[] = [] if (isBusiness && template.vat_treatment && template.deductibility !== 'non_deductible') { const vatRate = getVatRate(template.vat_treatment) if (template.vat_treatment === 'reverse_charge' && isExpense) { // EU reverse charge: fiktiv moms (offsetting entries) const absAmount = Math.abs(transaction.amount) const rcLines = generateReverseChargeLines(absAmount) for (const rcl of rcLines) { vatLines.push({ account_number: rcl.account_number, debit_amount: rcl.debit_amount, credit_amount: rcl.credit_amount, description: rcl.line_description || '', }) } } else if (vatRate > 0 && isExpense) { // Input VAT deduction const absAmount = Math.abs(transaction.amount) const vatLine = generateInputVatLine(absAmount, vatRate) if (vatLine) { vatLines.push({ account_number: vatLine.account_number, debit_amount: vatLine.debit_amount, credit_amount: vatLine.credit_amount, description: vatLine.line_description || '', }) } } else if (vatRate > 0 && !isExpense) { // Output VAT (income) const grossAmount = Math.abs(transaction.amount) const vatAmount = Math.round((grossAmount * vatRate / (1 + vatRate)) * 100) / 100 let vatAccount: string switch (template.vat_treatment) { case 'standard_25': vatAccount = '2611'; break case 'reduced_12': vatAccount = '2621'; break case 'reduced_6': vatAccount = '2631'; break default: vatAccount = '2611' } vatLines.push({ account_number: vatAccount, debit_amount: 0, credit_amount: vatAmount, description: `Utgående moms ${vatRate * 100}%`, }) } } // Build description const description = `${template.name_sv}: ${transaction.description}` return { rule: null, template_id: template.id, debit_account: debitAccount, credit_account: creditAccount, risk_level: template.risk_level, confidence: 1.0, // User explicitly selected template requires_review: template.requires_review, default_private: template.default_private, vat_lines: vatLines, description, } }