Files
accounted/extensions/general/enable-banking/lib/sync.ts
T
Jakob WennbergandClaude Opus 4.6 ef5a84a5d5 feat: make extension system packageable via enriched ExtensionContext
Enrich ExtensionContext with supabase, emit(), settings, storage, log,
and services so extensions can receive everything through dependency
injection instead of importing core modules directly.

- Add context factory and inject context into event handlers via registry
- Move supplier invoice journal entry creation to core event handler
- Add services.ingestTransactions to ExtensionContext for enable-banking
- Create catch-all API route for extension-declared apiRoutes
- Migrate 5 extensions to accept context with dynamic import fallbacks

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-23 09:24:21 +01:00

76 lines
2.4 KiB
TypeScript

import type { SupabaseClient } from '@supabase/supabase-js'
import { getTransactions, getAccountBalance } from './api-client'
import { ingestTransactions as defaultIngest } from '@/lib/transactions/ingest'
import type { RawTransaction, IngestResult } from '@/types'
import type { StoredAccount } from '../types'
/** Ingest function signature — matches lib/transactions/ingest */
export type IngestFn = (
supabase: SupabaseClient,
userId: string,
raw: RawTransaction[]
) => Promise<IngestResult>
export interface SyncResult {
imported: number
duplicates: number
errors: number
}
/**
* Sync transactions for a single bank account via Enable Banking PSD2.
*
* Fetches transactions from the Enable Banking API, converts to RawTransaction
* format, and delegates to the shared ingestion pipeline.
*
* @param ingest - Optional ingest function override (defaults to core ingestTransactions).
* When called from an extension handler with ctx.services.ingestTransactions,
* pass that function to avoid direct @/lib imports.
*/
export async function syncAccountTransactions(
supabase: SupabaseClient,
userId: string,
connectionId: string,
account: StoredAccount,
fromDate: string,
toDate: string,
ingest: IngestFn = defaultIngest
): Promise<SyncResult> {
const bankTransactions = await getTransactions(
account.uid,
fromDate,
toDate,
account.currency
)
// Convert Enable Banking format to generic RawTransaction
const rawTransactions: RawTransaction[] = bankTransactions.map((tx) => ({
date: tx.booking_date || tx.date,
description: tx.description || tx.counterparty_name || 'Unknown',
amount: tx.amount,
currency: tx.currency || account.currency,
external_id: `${connectionId}_${tx.id}`,
mcc_code: tx.merchant_category_code ? parseInt(tx.merchant_category_code, 10) : null,
merchant_name: tx.counterparty_name || null,
reference: tx.reference || null,
bank_connection_id: connectionId,
import_source: 'enable_banking',
}))
const ingestResult = await ingest(supabase, userId, rawTransactions)
// Update account balance
try {
const balance = await getAccountBalance(account.uid)
account.balance = balance.amount
} catch {
// Ignore balance fetch errors
}
return {
imported: ingestResult.imported,
duplicates: ingestResult.duplicates,
errors: ingestResult.errors,
}
}