feat(import): support Wise balance statements (#1368)

* feat(import): support Wise balance statements

* fix(import): fail closed on ambiguous Wise rows

* fix(import): guard Wise statement netted-fee assumption with running-balance continuity check

Swedish accounting review asked whether balance-statement Total fees is
netted into Amount. It is: Running Balance moves by exactly the signed
Amount per row, so a separate fee row would double-count the cost. Codify
the assumption with a pairwise continuity warning (order-agnostic, chain
resets across skipped rows) and document the decision.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* fix(api): cap bank-import validation payload and harden issue assertion

CodeRabbit review: bound the VALIDATION_ERROR issues array to 20 entries
with issue_count carrying the full total, so a large malformed file cannot
balloon the response or log sink. Gate stays format-agnostic on purpose:
error severity means do-not-ingest for every parser, and no non-Wise parser
emits per-row errors alongside parsed transactions today.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Mattsson
2026-08-03 17:55:09 +02:00
committed by GitHub
co-authored by Claude Fable 5
parent 0510d4c13f
commit 24911abde0
12 changed files with 741 additions and 23 deletions
+6 -1
View File
@@ -1,6 +1,7 @@
'use client'
import { useState, useCallback } from 'react'
import { useTranslations } from 'next-intl'
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'
import { Badge } from '@/components/ui/badge'
import { Progress } from '@/components/ui/progress'
@@ -54,6 +55,7 @@ export default function BankFileUploadStep({
detectedFormat,
detectedFormatName,
}: BankFileUploadStepProps) {
const t = useTranslations('import')
const [isDragging, setIsDragging] = useState(false)
const [selectedFile, setSelectedFile] = useState<File | null>(null)
const [formatOverride, setFormatOverride] = useState<BankFileFormatId | undefined>(undefined)
@@ -137,6 +139,7 @@ export default function BankFileUploadStep({
<SelectItem value="lunar">Lunar</SelectItem>
<SelectItem value="northmill">Northmill</SelectItem>
<SelectItem value="wise">Wise</SelectItem>
<SelectItem value="wise_statement">{t('bank_format_wise_statement')}</SelectItem>
<SelectItem value="camt053">ISO 20022 camt.053 (XML)</SelectItem>
<SelectItem value="generic_csv">Annan CSV (manuell mappning)</SelectItem>
</SelectContent>
@@ -181,7 +184,9 @@ export default function BankFileUploadStep({
</p>
<Badge variant="secondary" className="mt-2">
<Building2 className="mr-1 h-3 w-3" />
{detectedFormatName || FORMAT_NAMES[detectedFormat] || detectedFormat}
{detectedFormat === 'wise_statement'
? t('bank_format_wise_statement')
: detectedFormatName || FORMAT_NAMES[detectedFormat] || detectedFormat}
</Badge>
</div>
</div>