feat(import): add Wise (TransferWise) CSV import format (#1018)
* feat(import): add Wise (TransferWise) CSV import format Wise exports a single multi-currency transaction history (one row per balance movement). Add it as a bank-file format plugin so it flows through the existing upload -> preview -> confirm -> execute wizard. - lib/import/bank-file/formats/wise.ts: quote-aware parse (dates contain a space), Direction IN/OUT drives the sign, booked on the moved side (target for IN, source for OUT). Native currency preserved; SEK conversion is left to the downstream FX/booking pipeline (Riksbanken). - Non-zero Wise fees become their own negative "Wise avgift" row (source and target), so the fee books separately and the balance ties out. - Only COMPLETED rows import. external_id keys on the stable Wise ID (TRANSFER-/PLAN_ORDER-, -fee suffix for fee rows) via a new 'wise' branch in generateExternalId, so re-imports dedup exactly. - Register the format (types, parser list), add it to the manual-format picker and the v1 /imports/bank format enum. Tests cover detection, IN/OUT signing + currency, fee splitting, stable external_id, and COMPLETED-only filtering. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Signed-off-by: Alexander Reinthal <email@reinthal.me> * fix(import): harden Wise parser against malformed rows (CodeRabbit #1018) - Strict amount parsing: reject "12abc"/"1,234" instead of parseFloat coercing them to 12/1 and silently corrupting the imported amount. - Require Status to be exactly COMPLETED: a blank/missing status no longer slips through the completed-only filter. - Fail hard on an unsupported Direction: a blank or non-IN/OUT value (e.g. NEUTRAL for a balance conversion) throws instead of being guessed as income; the parse route surfaces it as BANK_FILE_PARSE_FAILED. Proper conversion support is tracked in #1019. - Never invent currencies: a missing movement currency skips the row with a warning (no SEK default), and a fee with no currency of its own is dropped with a warning rather than inheriting the movement currency. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Signed-off-by: Alexander Reinthal <email@reinthal.me> --------- Signed-off-by: Alexander Reinthal <email@reinthal.me> Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Co-authored-by: Jakob Wennberg <jakob.wennberg@gmail.com>
This commit is contained in:
co-authored by
Claude Opus 4.8
Jakob Wennberg
parent
f8033cb32d
commit
1dc85736d8
@@ -60,7 +60,7 @@ registerEndpoint({
|
||||
'SIE bookkeeping import (use /imports/sie). Auto-bank sync (use the enable-banking extension). Single-transaction creation (use POST /transactions/ingest with a 1-element array).',
|
||||
pitfalls: [
|
||||
'File size cap: 10 MB. Larger files require splitting client-side.',
|
||||
'`format` query parameter is optional; auto-detection works for all supported banks. Pass `format` only to force a specific format. Accepted values: seb, swedbank, handelsbanken, nordea, nordea_business, lansforsakringar, ica_banken, skandia, lunar, northmill, generic_csv, camt053.',
|
||||
'`format` query parameter is optional; auto-detection works for all supported banks. Pass `format` only to force a specific format. Accepted values: seb, swedbank, handelsbanken, nordea, nordea_business, lansforsakringar, ica_banken, skandia, lunar, northmill, wise, generic_csv, camt053.',
|
||||
'Duplicate detection is by external_id (composed from date + amount + counterparty); a re-import of the same file with the same flag set typically deduplicates rather than creating doubles.',
|
||||
'BFL 5 kap 6-7 §§ note: this endpoint creates `transactions` rows (the underlag for a verifikation), NOT verifikationer themselves. The verifikation content requirements are in BFL 5 kap 6-7 §§; until each transaction is matched to an invoice/supplier-invoice (POST /transactions/{id}/match-*) or categorised (POST /transactions/{id}/categorize), the bookkeeping obligation isn\'t discharged. A successful import here means the data is ingested: not booked.',
|
||||
'A successful import returns operation_id; poll /operations/{id} for the final ingested/duplicates/errors counts.',
|
||||
@@ -135,6 +135,7 @@ export const POST = withApiV1<{ params: Promise<{ companyId: string }> }>(
|
||||
'skandia',
|
||||
'lunar',
|
||||
'northmill',
|
||||
'wise',
|
||||
'generic_csv',
|
||||
'camt053',
|
||||
])
|
||||
|
||||
Reference in New Issue
Block a user