feat: BAS data overhaul, currency revaluation, expenses, UI polish, and cleanup
- Update BAS account catalog with comprehensive SRU codes and K2 flags - Add currency revaluation service with tests and API route - Add expenses page and account deletion API - Enhance booking templates with new patterns and improved tests - Improve transaction categorization with template picker and description matching - Polish dashboard, onboarding, import, and transaction UIs - Refactor year-end service for multi-step closing - Move SRU generator to ne-bilaga, remove standalone SRU export - Remove unused dev docs, mock data, and extension hooks - Add invoice delivery note sequences migration Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.6
parent
a9bf24ce0b
commit
66a4027f1e
@@ -126,7 +126,7 @@ const CATEGORY_DEFAULT_TEMPLATES: Record<string, string> = {
|
||||
expense_consumables: 'office_supplies_general',
|
||||
expense_vehicle: 'vehicle_fuel',
|
||||
expense_telecom: 'telecom_mobile',
|
||||
expense_marketing: 'marketing_online_ads',
|
||||
expense_marketing: 'marketing_online_ads_eu',
|
||||
expense_education: 'education_course',
|
||||
expense_professional_services: 'prof_accounting',
|
||||
}
|
||||
|
||||
@@ -100,37 +100,37 @@ function buildSystemPrompt(entityType: EntityType): string {
|
||||
const privateAccount = entityType === 'aktiebolag' ? '2893' : '2013'
|
||||
const entityLabel = entityType === 'aktiebolag' ? 'Aktiebolag (AB)' : 'Enskild firma (EF)'
|
||||
|
||||
return `Du ar expert pa svensk bokforing enligt BAS-kontoplanen. Analysera anvandarens beskrivning av en banktransaktion och returnera ett bokforingsforslag.
|
||||
return `Du är expert på svensk bokföring enligt BAS-kontoplanen. Analysera användarens beskrivning av en banktransaktion och returnera ett bokföringsförslag.
|
||||
|
||||
VANLIGA BAS-KONTON:
|
||||
Utgifter: 5010 Lokalhyra | 5410 Forbrukningsinventarier | 5420 Programvara | 5460 Forbrukningsvaror | 5611 Bil/drivmedel | 5800 Resekostnader | 5910 Annonsering | 6071 Representation mat | 6200 Telefon/internet | 6530 Redovisning/konsult | 6570 Bankavgifter | 6991 Ovriga kostnader | ${entityType === 'aktiebolag' ? '7610' : '6991'} Utbildning
|
||||
Intakter: 3001 Forsaljning 25% | 3002 Forsaljning 12% | 3003 Forsaljning 6% | 3305 Export | 3308 EU-tjanster | 3900 Ovriga intakter
|
||||
Moms: 2611 Utg moms 25% | 2621 Utg moms 12% | 2631 Utg moms 6% | 2641 Ing moms | 2645 Beraknad ing moms
|
||||
Skulder: 2350 Skulder till kreditinstitut (banklan, Almi) | 2440 Leverantorsskulder (ENBART for leverantorsfakturor)
|
||||
Ovrigt: 1510 Kundfordringar | 1930 Foretagskonto | 8410 Rantekostnader | ${privateAccount} Privat
|
||||
Utgifter: 5010 Lokalhyra | 5410 Förbrukningsinventarier | 5420 Programvara | 5460 Förbrukningsvaror | 5611 Bil/drivmedel | 5800 Resekostnader | 5910 Annonsering | 6071 Representation mat | 6200 Telefon/internet | 6530 Redovisning/konsult | 6570 Bankavgifter | 6991 Övriga kostnader | ${entityType === 'aktiebolag' ? '7610' : '6991'} Utbildning
|
||||
Intäkter: 3001 Försäljning 25% | 3002 Försäljning 12% | 3003 Försäljning 6% | 3305 Export | 3308 EU-tjänster | 3900 Övriga intäkter
|
||||
Moms: 2611 Utg moms 25% | 2621 Utg moms 12% | 2631 Utg moms 6% | 2641 Ing moms | 2645 Beräknad ing moms
|
||||
Skulder: 2350 Skulder till kreditinstitut (banklån, Almi) | 2440 Leverantörsskulder (ENBART för leverantörsfakturor)
|
||||
Övrigt: 1510 Kundfordringar | 1930 Företagskonto | 8410 Räntekostnader | ${privateAccount} Privat
|
||||
|
||||
MOMSREGLER:
|
||||
- standard_25: Normala varor/tjanster (25%)
|
||||
- standard_25: Normala varor/tjänster (25%)
|
||||
- reduced_12: Livsmedel, hotell, konstverk (12%)
|
||||
- reduced_6: Bocker, tidningar, kollektivtrafik, kultur (6%)
|
||||
- reverse_charge: Tjanstekop fran utlandet/EU
|
||||
- export: Forsaljning utanfor Sverige
|
||||
- exempt: Momsfritt (bank, forsakring, sjukvard, utbildning)
|
||||
- reduced_6: Böcker, tidningar, kollektivtrafik, kultur (6%)
|
||||
- reverse_charge: Tjänsteköp från utlandet/EU
|
||||
- export: Försäljning utanför Sverige
|
||||
- exempt: Momsfritt (bank, försäkring, sjukvård, utbildning)
|
||||
|
||||
VARNINGSREGLER:
|
||||
- Representation/maltider: Max 300 kr/person exkl moms for avdragsratt (IL 16 kap 2§)
|
||||
- Gavor: Reklamgavor max 300 kr, representationsgavor max 180 kr
|
||||
- Blandad anvandning (telefon/dator): Bara yrkesmassig del avdragsgill
|
||||
- Bankavgifter, kortavgifter, valutavaxling: MOMSFRIA (exempt)
|
||||
- Representation/måltider: Max 300 kr/person exkl moms för avdragsrätt (IL 16 kap 2§)
|
||||
- Gåvor: Reklamgåvor max 300 kr, representationsgåvor max 180 kr
|
||||
- Blandad användning (telefon/dator): Bara yrkesmässig del avdragsgill
|
||||
- Bankavgifter, kortavgifter, valutaväxling: MOMSFRIA (exempt)
|
||||
|
||||
Foretagsform: ${entityLabel}
|
||||
Företagsform: ${entityLabel}
|
||||
Privatkonto: ${privateAccount}
|
||||
|
||||
REGLER:
|
||||
1. Negativt belopp = utgift: debitera kostnadskonto, kreditera 1930
|
||||
2. Positivt belopp = intakt: debitera 1930, kreditera intaktskonto
|
||||
3. Ge ett klart reasoning pa svenska som forklarar valet
|
||||
4. Lagg till warnings for avdragsbegransningar eller speciella regler
|
||||
2. Positivt belopp = intäkt: debitera 1930, kreditera intäktskonto
|
||||
3. Ge ett klart reasoning på svenska som förklarar valet
|
||||
4. Lägg till warnings för avdragsbegränsningar eller speciella regler
|
||||
5. templateId: null (vi matchar mallar separat)`
|
||||
}
|
||||
|
||||
@@ -145,12 +145,12 @@ export async function analyzeDescription(
|
||||
const isExpense = input.transactionAmount < 0
|
||||
|
||||
const userPrompt = `Transaktion:
|
||||
- Anvandarens beskrivning: "${input.description}"
|
||||
- Användarens beskrivning: "${input.description}"
|
||||
- Banktext: "${input.transactionDescription}"
|
||||
- Belopp: ${input.transactionAmount} ${input.currency}
|
||||
- Datum: ${input.transactionDate}${input.merchantName ? `\n- Handlare: ${input.merchantName}` : ''}
|
||||
|
||||
Analysera och returnera bokforingsforslag med analyze_description-verktyget.`
|
||||
Analysera och returnera bokföringsförslag med analyze_description-verktyget.`
|
||||
|
||||
let lastError: Error | null = null
|
||||
|
||||
@@ -248,7 +248,7 @@ function validateResult(
|
||||
// Reasoning — must be a non-empty string
|
||||
const reasoning = typeof raw.reasoning === 'string' && raw.reasoning.length > 0
|
||||
? raw.reasoning
|
||||
: (isExpense ? 'Utgift bokford pa standardkonto' : 'Intakt bokford pa standardkonto')
|
||||
: (isExpense ? 'Utgift bokförd på standardkonto' : 'Intäkt bokförd på standardkonto')
|
||||
|
||||
// Warnings
|
||||
const warnings = Array.isArray(raw.warnings)
|
||||
|
||||
@@ -17,6 +17,8 @@ import * as path from 'path'
|
||||
import * as crypto from 'crypto'
|
||||
|
||||
// Configuration
|
||||
// NOTE: The ai_knowledge_base directory must be created and populated before running ingestion.
|
||||
// Create dev_docs/ai_knowledge_base/ and add markdown files to ingest.
|
||||
const DOCS_DIR = path.join(process.cwd(), 'dev_docs', 'ai_knowledge_base')
|
||||
const CHUNK_SIZE = 1000
|
||||
const CHUNK_OVERLAP = 200
|
||||
@@ -255,6 +257,12 @@ async function ingest() {
|
||||
console.log('Starting knowledge base ingestion...')
|
||||
console.log(`Reading files from: ${DOCS_DIR}`)
|
||||
|
||||
if (!fs.existsSync(DOCS_DIR)) {
|
||||
console.error(`Error: Knowledge base directory not found: ${DOCS_DIR}`)
|
||||
console.error('Create dev_docs/ai_knowledge_base/ and add markdown files before running ingestion.')
|
||||
process.exit(1)
|
||||
}
|
||||
|
||||
// Get all markdown files
|
||||
const files = fs
|
||||
.readdirSync(DOCS_DIR)
|
||||
|
||||
@@ -37,8 +37,8 @@ export class ResendEmailService implements EmailService {
|
||||
}
|
||||
|
||||
const from = fromName
|
||||
? `${fromName} via ERP Base <${DEFAULT_FROM_EMAIL}>`
|
||||
: `ERP Base <${DEFAULT_FROM_EMAIL}>`
|
||||
? `${fromName} via Gnubok <${DEFAULT_FROM_EMAIL}>`
|
||||
: `Gnubok <${DEFAULT_FROM_EMAIL}>`
|
||||
|
||||
try {
|
||||
const resend = getResendClient()
|
||||
@@ -53,8 +53,8 @@ export class ResendEmailService implements EmailService {
|
||||
filename: att.filename,
|
||||
content: typeof att.content === 'string'
|
||||
? Buffer.from(att.content, 'base64')
|
||||
: att.content,
|
||||
content_type: att.contentType,
|
||||
: Buffer.from(att.content),
|
||||
contentType: att.contentType,
|
||||
})),
|
||||
})
|
||||
|
||||
|
||||
@@ -15,6 +15,11 @@ vi.mock('../lib/supplier-matcher', () => ({
|
||||
matchSupplier: vi.fn(),
|
||||
}))
|
||||
|
||||
// Mock api-routes to avoid transitive server-only import from document-analyzer
|
||||
vi.mock('../api-routes', () => ({
|
||||
invoiceInboxApiRoutes: [],
|
||||
}))
|
||||
|
||||
import { createClient } from '@/lib/supabase/server'
|
||||
import { invoiceInboxExtension, getSettings, saveSettings } from '../index'
|
||||
|
||||
|
||||
@@ -121,7 +121,7 @@ export function createReceiptMatchedPayload(
|
||||
export function createMissingUnderlagPayload(count: number): NotificationPayload {
|
||||
return {
|
||||
title: 'Saknade underlag',
|
||||
body: `${count} verifikation(er) saknar underlag. Bifoga for att uppfylla bokforingslagen.`,
|
||||
body: `${count} verifikation(er) saknar underlag. Bifoga för att uppfylla bokföringslagen.`,
|
||||
icon: '/icons/icon-192.png',
|
||||
badge: '/icons/badge-72.png',
|
||||
tag: 'missing-underlag-weekly',
|
||||
|
||||
Reference in New Issue
Block a user