feat: add bank file import as core, move Enable Banking to extension

Replace PSD2 bank integration as the default with file-based bank
import (CSV/XML), which better suits Swedish sole traders and small
companies. Enable Banking is now an opt-in extension.

- Phase 1: Extract generic transaction ingestion service (ingest.ts)
  with dedup, auto-categorization, and OCR-based invoice matching
- Phase 2: Bank file parser library supporting Nordea, SEB, Swedbank,
  Handelsbanken CSV formats and ISO 20022 camt.053 XML
- Phase 3: Database migration adding import_source, reference columns
  and bank_file_imports tracking table
- Phase 4: Import wizard UI (5-step flow) and API routes for parse/execute
- Phase 5: Move Enable Banking to extensions/enable-banking/ with
  commented-out loader entry for opt-in activation
- Phase 6: 104 new tests (ingestion + all parser formats), fixing
  Nordea detection overlap and camt.053 XML tag collision bugs

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Jakob Wennberg
2026-02-20 14:21:28 +01:00
co-authored by Claude Opus 4.6
parent 838dc6b8b5
commit 885f362a29
60 changed files with 8232 additions and 853 deletions
@@ -5,7 +5,7 @@ import {
getRevenueAccount,
getOutputVatAccount,
} from '@/lib/bookkeeping/invoice-entries'
import type { Transaction, Invoice, CreateJournalEntryInput, VatTreatment } from '@/types'
import type { Transaction, Invoice, CreateJournalEntryInput, EntityType, VatTreatment } from '@/types'
interface MatchInvoiceRequest {
invoice_id: string
@@ -139,11 +139,12 @@ export async function POST(
// Fetch accounting method
const { data: settings } = await supabase
.from('company_settings')
.select('accounting_method')
.select('accounting_method, entity_type')
.eq('user_id', user.id)
.single()
const accountingMethod = settings?.accounting_method || 'accrual'
const entityType = (settings?.entity_type as EntityType) || 'enskild_firma'
// Create journal entry for payment receipt (method-aware)
let journalEntryId: string | null = null
@@ -158,7 +159,7 @@ export async function POST(
if (accountingMethod === 'cash') {
// Kontantmetoden: combined revenue entry at payment
// Debit 1930 Företagskonto, Credit 30xx Försäljning, Credit 26xx Utgående moms
const revenueAccount = getRevenueAccount(invoice.vat_treatment as VatTreatment)
const revenueAccount = getRevenueAccount(invoice.vat_treatment as VatTreatment, entityType)
const lines: CreateJournalEntryInput['lines'] = [
{
account_number: '1930',