feat: unified document inbox, full BAS 2026, and document-transaction matching

- Expand BAS reference from ~180 to ~1,276 accounts (full BAS Kontoplan 2026)
  with K2 exclusion flags, per-class data files, and computed SRU codes
- Evolve invoice inbox into unified document inbox handling invoices, receipts,
  and government letters with AI-powered classification (Claude Haiku Vision)
- Add multi-pass document-to-transaction matching engine with greedy assignment
  for both supplier invoices (reference/amount/date/name) and receipts
  (weighted amount/merchant/date scoring)
- Add supplier invoice matching in transaction ingest pipeline
- Inject booking template suggestions into AI extraction prompts
- Surface matched documents in swipe categorization UI with one-tap booking
- Auto-activate missing BAS accounts during SIE import against full reference
- Add K2 filter toggle in Chart of Accounts manager
- Add receipt confirmation route with BFNAR representation fields
- Add database migrations for K2 support and document matching columns
- Remove obsolete extension migration scripts

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Jakob Wennberg
2026-02-25 16:59:02 +01:00
co-authored by Claude Opus 4.6
parent 6956a757f3
commit 39e407644d
68 changed files with 19234 additions and 2412 deletions
@@ -0,0 +1,165 @@
-- Migration 42: Full BAS 2026 support
-- Adds k2_excluded column and backfills K2-excluded account numbers.
-- =============================================================================
-- 1. Add k2_excluded column
-- =============================================================================
ALTER TABLE public.chart_of_accounts
ADD COLUMN IF NOT EXISTS k2_excluded boolean DEFAULT false;
-- =============================================================================
-- 2. Backfill k2_excluded = true for BAS 2026 K2-excluded accounts
-- These accounts are marked with # in BAS Kontoplan 2026 v1.0 and should
-- not be used when K2 accounting framework is applied.
-- =============================================================================
UPDATE public.chart_of_accounts
SET k2_excluded = true, updated_at = now()
WHERE account_number IN (
'1010', '1011', '1012', '1018', '1019',
'1370',
'1518',
'2092', '2096',
'2240',
'2448',
'3940',
'7940',
'8290', '8291', '8295',
'8320', '8321', '8325',
'8450', '8451', '8455',
'8480',
'8940'
)
AND k2_excluded = false;
-- =============================================================================
-- 3. Re-run SRU code backfill for any accounts with sru_code IS NULL
-- Uses the same range logic as migration 021.
-- =============================================================================
-- NE: R1 - Försäljning med moms (3000-3499 excl 3100)
UPDATE public.chart_of_accounts
SET sru_code = '7310', updated_at = now()
WHERE sru_code IS NULL
AND account_number >= '3000' AND account_number <= '3499'
AND account_number != '3100';
-- NE: R2 - Momsfria intäkter (3100, 3900, 3970-3980)
UPDATE public.chart_of_accounts
SET sru_code = '7311', updated_at = now()
WHERE sru_code IS NULL
AND (
account_number = '3100'
OR account_number = '3900'
OR (account_number >= '3970' AND account_number <= '3980')
);
-- NE: R4 - Ränteintäkter (8310-8330)
UPDATE public.chart_of_accounts
SET sru_code = '7313', updated_at = now()
WHERE sru_code IS NULL
AND account_number >= '8310' AND account_number <= '8330';
-- NE: R5 - Varuinköp (4000-4990)
UPDATE public.chart_of_accounts
SET sru_code = '7320', updated_at = now()
WHERE sru_code IS NULL
AND account_number >= '4000' AND account_number <= '4990';
-- NE: R6 - Övriga kostnader (5000-6990, 7970)
UPDATE public.chart_of_accounts
SET sru_code = '7321', updated_at = now()
WHERE sru_code IS NULL
AND (
(account_number >= '5000' AND account_number <= '6990')
OR account_number = '7970'
);
-- NE: R7 - Lönekostnader (7000-7699)
UPDATE public.chart_of_accounts
SET sru_code = '7322', updated_at = now()
WHERE sru_code IS NULL
AND account_number >= '7000' AND account_number <= '7699';
-- NE: R8 - Räntekostnader (8400-8499)
UPDATE public.chart_of_accounts
SET sru_code = '7323', updated_at = now()
WHERE sru_code IS NULL
AND account_number >= '8400' AND account_number <= '8499';
-- NE: R9 - Avskrivningar fastighet (7820)
UPDATE public.chart_of_accounts
SET sru_code = '7324', updated_at = now()
WHERE sru_code IS NULL
AND account_number = '7820';
-- NE: R10 - Avskrivningar övrigt (7700-7899 excl 7820)
UPDATE public.chart_of_accounts
SET sru_code = '7325', updated_at = now()
WHERE sru_code IS NULL
AND account_number >= '7700' AND account_number <= '7899'
AND account_number != '7820';
-- INK2: Balance sheet fallbacks
UPDATE public.chart_of_accounts SET sru_code = '7201', updated_at = now()
WHERE sru_code IS NULL AND account_number >= '1000' AND account_number <= '1099';
UPDATE public.chart_of_accounts SET sru_code = '7202', updated_at = now()
WHERE sru_code IS NULL AND account_number >= '1100' AND account_number <= '1299';
UPDATE public.chart_of_accounts SET sru_code = '7203', updated_at = now()
WHERE sru_code IS NULL AND account_number >= '1300' AND account_number <= '1399';
UPDATE public.chart_of_accounts SET sru_code = '7210', updated_at = now()
WHERE sru_code IS NULL AND account_number >= '1400' AND account_number <= '1499';
UPDATE public.chart_of_accounts SET sru_code = '7211', updated_at = now()
WHERE sru_code IS NULL AND account_number >= '1500' AND account_number <= '1599';
UPDATE public.chart_of_accounts SET sru_code = '7212', updated_at = now()
WHERE sru_code IS NULL AND account_number >= '1600' AND account_number <= '1999';
UPDATE public.chart_of_accounts SET sru_code = '7220', updated_at = now()
WHERE sru_code IS NULL AND account_number = '2081';
UPDATE public.chart_of_accounts SET sru_code = '7221', updated_at = now()
WHERE sru_code IS NULL AND account_number >= '2085' AND account_number <= '2098';
UPDATE public.chart_of_accounts SET sru_code = '7222', updated_at = now()
WHERE sru_code IS NULL AND account_number = '2099';
UPDATE public.chart_of_accounts SET sru_code = '7230', updated_at = now()
WHERE sru_code IS NULL AND account_number >= '2100' AND account_number <= '2499';
UPDATE public.chart_of_accounts SET sru_code = '7231', updated_at = now()
WHERE sru_code IS NULL AND account_number >= '2500' AND account_number <= '2999';
-- INK2: Remaining income statement fallbacks
UPDATE public.chart_of_accounts SET sru_code = '7310', updated_at = now()
WHERE sru_code IS NULL AND account_number >= '3000' AND account_number <= '3999';
UPDATE public.chart_of_accounts SET sru_code = '7320', updated_at = now()
WHERE sru_code IS NULL AND account_number >= '4000' AND account_number <= '4999';
UPDATE public.chart_of_accounts SET sru_code = '7330', updated_at = now()
WHERE sru_code IS NULL AND account_number >= '5000' AND account_number <= '6999';
UPDATE public.chart_of_accounts SET sru_code = '7340', updated_at = now()
WHERE sru_code IS NULL AND account_number >= '7000' AND account_number <= '7699';
UPDATE public.chart_of_accounts SET sru_code = '7350', updated_at = now()
WHERE sru_code IS NULL AND account_number >= '7700' AND account_number <= '7899';
UPDATE public.chart_of_accounts SET sru_code = '7360', updated_at = now()
WHERE sru_code IS NULL AND account_number >= '7900' AND account_number <= '7999';
UPDATE public.chart_of_accounts SET sru_code = '7370', updated_at = now()
WHERE sru_code IS NULL AND account_number >= '8000' AND account_number <= '8499';
UPDATE public.chart_of_accounts SET sru_code = '7380', updated_at = now()
WHERE sru_code IS NULL AND account_number >= '8500' AND account_number <= '8999';
-- Equity accounts not covered above (2000-2084)
UPDATE public.chart_of_accounts SET sru_code = '7221', updated_at = now()
WHERE sru_code IS NULL AND account_number >= '2000' AND account_number <= '2084';
@@ -0,0 +1,23 @@
-- Document matching: add columns to invoice_inbox_items for transaction matching
-- and AI template suggestions.
-- Suggested booking template from AI extraction
ALTER TABLE public.invoice_inbox_items
ADD COLUMN suggested_template_id TEXT,
ADD COLUMN suggested_template_confidence NUMERIC;
-- Matched bank transaction
ALTER TABLE public.invoice_inbox_items
ADD COLUMN matched_transaction_id UUID REFERENCES public.transactions(id) ON DELETE SET NULL,
ADD COLUMN match_confidence NUMERIC,
ADD COLUMN match_method TEXT CHECK (match_method IN ('payment_reference', 'amount_date', 'amount_merchant', 'receipt_match'));
-- Index for looking up which inbox item is matched to a transaction
CREATE INDEX idx_inbox_items_matched_transaction
ON public.invoice_inbox_items (user_id, matched_transaction_id)
WHERE matched_transaction_id IS NOT NULL;
-- Index for finding unmatched ready items for sweep
CREATE INDEX idx_inbox_items_unmatched_ready
ON public.invoice_inbox_items (user_id, status)
WHERE matched_transaction_id IS NULL AND status IN ('ready', 'processing');