feat: add foreign currency support, refactor bookkeeping engine, and improve invoice inbox document classification

- Add currency-utils module for SEK conversion with exchange rates
- Refactor createJournalEntry to use draft+commit flow preventing voucher number gaps (BFL 5 kap. 7§)
- Add foreign currency support to invoice entries with per-line SEK conversion
- Centralize category-to-account mapping into single source of truth
- Refactor invoice inbox to use shared document analyzer with document type classification (receipt, supplier invoice, government letter)
- Update mapping engine, supplier invoice entries, and transaction entries
- Fix report component rendering issues
- Add new validation schemas and tests

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Jakob Wennberg
2026-02-27 09:50:06 +01:00
co-authored by Claude Opus 4.6
parent 547fd053ec
commit 6f4573f380
33 changed files with 1365 additions and 497 deletions
@@ -29,6 +29,7 @@ function makeClient() {
vi.mock('@/lib/bookkeeping/engine', () => ({
validateBalance: vi.fn().mockReturnValue({ valid: true, totalDebit: 1000, totalCredit: 1000 }),
getNextVoucherNumber: vi.fn(async () => ++resultIdx), // just increment
getSwedishLocalDate: vi.fn().mockReturnValue('2024-06-15'),
}))
import { correctEntry } from '../storno-service'
+3 -3
View File
@@ -5,7 +5,7 @@ import type {
JournalEntry,
JournalEntryLine,
} from '@/types'
import { validateBalance, getNextVoucherNumber } from '@/lib/bookkeeping/engine'
import { validateBalance, getNextVoucherNumber, getSwedishLocalDate } from '@/lib/bookkeeping/engine'
/**
* Storno Service - 3-step correction flow per Bokföringslagen
@@ -69,7 +69,7 @@ export async function correctEntry(
fiscal_period_id: original.fiscal_period_id,
voucher_number: reversalVoucherNumber,
voucher_series: original.voucher_series || 'A',
entry_date: new Date().toISOString().split('T')[0],
entry_date: getSwedishLocalDate(),
description: `Storno: ${original.description}`,
source_type: 'storno',
reverses_id: originalEntryId,
@@ -172,7 +172,7 @@ export async function correctEntry(
fiscal_period_id: original.fiscal_period_id,
voucher_number: correctedVoucherNumber,
voucher_series: original.voucher_series || 'A',
entry_date: new Date().toISOString().split('T')[0],
entry_date: getSwedishLocalDate(),
description: `Rättelse: ${original.description}`,
source_type: 'correction',
correction_of_id: originalEntryId,