feat: enable PSD2 bank integration via settings
- Uncomment Enable Banking extension in loader (now registered at runtime) - Add subscriptionNotice field to ExtensionDefinition type - Show confirmation dialog when enabling extensions with subscription requirements - Fix Settings banking tab: toggle-aware visibility, URL-addressable tabs, BankSelector widget, correct API paths (/api/extensions/ext/enable-banking/*) - Replace inline bank connection cards with BankConnectionStatus component - Add actionable link to Settings from EnableBankingWorkspace - Update CLAUDE.md with latest architecture docs Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.6
parent
4163b46eae
commit
9d684d7e9f
@@ -14,11 +14,7 @@ import { intrastatExtension } from '@/extensions/export/intrastat'
|
||||
import { currencyReceivablesExtension } from '@/extensions/export/currency-receivables'
|
||||
import type { Extension } from './types'
|
||||
|
||||
// ── Enable Banking (PSD2) — opt-in extension ───────────────────────────
|
||||
// Uncomment the following line to enable automatic PSD2 bank transaction sync.
|
||||
// Requires ENABLE_BANKING_APP_ID and ENABLE_BANKING_PRIVATE_KEY env vars.
|
||||
//
|
||||
// import { enableBankingExtension } from '@/extensions/general/enable-banking'
|
||||
import { enableBankingExtension } from '@/extensions/general/enable-banking'
|
||||
|
||||
/**
|
||||
* Explicit list of first-party extensions.
|
||||
@@ -36,7 +32,7 @@ const FIRST_PARTY_EXTENSIONS: Extension[] = [
|
||||
invoiceInboxExtension,
|
||||
calendarExtension,
|
||||
userDescriptionMatchExtension,
|
||||
// enableBankingExtension, // Uncomment to activate PSD2 bank sync
|
||||
enableBankingExtension,
|
||||
|
||||
// ── Export sector ──────────────────────────────────────────
|
||||
euSalesListExtension,
|
||||
|
||||
@@ -117,6 +117,8 @@ export const SECTORS: Sector[] = [
|
||||
description: 'Automatisk banktransaktionssynk via PSD2',
|
||||
longDescription:
|
||||
'Koppla ditt bankkonto direkt och synka transaktioner automatiskt via säker PSD2-bankintegration. Stöder de flesta svenska banker.',
|
||||
subscriptionNotice:
|
||||
'Denna integration kräver ett aktivt Enable Banking-abonnemang. Utan abonnemang kommer bankintegration inte att fungera.',
|
||||
},
|
||||
],
|
||||
},
|
||||
|
||||
@@ -39,6 +39,8 @@ export interface ExtensionDefinition {
|
||||
readsCoreTables?: string[]
|
||||
hasOwnData?: boolean
|
||||
quickAction?: QuickActionDefinition
|
||||
/** Notice shown when enabling — e.g. external subscription requirement */
|
||||
subscriptionNotice?: string
|
||||
}
|
||||
|
||||
/** Sector definition with its extensions */
|
||||
|
||||
@@ -166,8 +166,8 @@ function accountToCategory(account: string, amount: number): string | null {
|
||||
}
|
||||
|
||||
/**
|
||||
* Source priority for sorting — higher-quality sources rank first.
|
||||
* AI and mapping rules always beat history-based guesses.
|
||||
* Source priority for tiebreaking — used when two suggestions
|
||||
* have the same confidence score.
|
||||
*/
|
||||
const SOURCE_PRIORITY: Record<SuggestedCategory['source'], number> = {
|
||||
mapping_rule: 3,
|
||||
@@ -187,19 +187,28 @@ export function mergeAiSuggestions(
|
||||
aiSuggestions: { category: string; basAccount: string; confidence: number; reasoning: string }[],
|
||||
transactionAmount?: number
|
||||
): SuggestedCategory[] {
|
||||
const seen = new Set<string>(existing.map((s) => s.category))
|
||||
const merged = [...existing]
|
||||
|
||||
for (const ai of aiSuggestions) {
|
||||
if (seen.has(ai.category)) continue
|
||||
|
||||
// Skip suggestions that don't match transaction direction
|
||||
if (transactionAmount !== undefined) {
|
||||
if (transactionAmount > 0 && ai.category.startsWith('expense_')) continue
|
||||
if (transactionAmount < 0 && ai.category.startsWith('income_')) continue
|
||||
}
|
||||
|
||||
seen.add(ai.category)
|
||||
const existingIdx = merged.findIndex((s) => s.category === ai.category)
|
||||
if (existingIdx !== -1) {
|
||||
// Upgrade existing entry if the AI has higher confidence
|
||||
if (ai.confidence > merged[existingIdx].confidence) {
|
||||
merged[existingIdx] = {
|
||||
...merged[existingIdx],
|
||||
account: ai.basAccount || merged[existingIdx].account,
|
||||
confidence: ai.confidence,
|
||||
source: 'ai',
|
||||
}
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
merged.push({
|
||||
category: ai.category as TransactionCategory,
|
||||
@@ -210,12 +219,12 @@ export function mergeAiSuggestions(
|
||||
})
|
||||
}
|
||||
|
||||
// Sort by source priority first, then by confidence within the same tier
|
||||
// Sort by confidence first, then by source priority as tiebreaker
|
||||
return merged
|
||||
.sort((a, b) => {
|
||||
const priorityDiff = SOURCE_PRIORITY[b.source] - SOURCE_PRIORITY[a.source]
|
||||
if (priorityDiff !== 0) return priorityDiff
|
||||
return b.confidence - a.confidence
|
||||
const confidenceDiff = b.confidence - a.confidence
|
||||
if (confidenceDiff !== 0) return confidenceDiff
|
||||
return SOURCE_PRIORITY[b.source] - SOURCE_PRIORITY[a.source]
|
||||
})
|
||||
.slice(0, 5)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user