Files
accounted/lib/import/bank-file/currency-summary.ts
T
Jakob WennbergandClaude Fable 5 bb78f8fce8 fix(import): per-currency totals and row currency in bank-file preview (#1178)
* fix(import): per-currency totals and row currency in bank-file preview

Fixes #1170. ParsedBankTransaction carries a per-row currency (Wise
emits genuinely mixed rows; camt.053 reads Ccy per entry), but the
preview and confirm steps formatted every amount as kr and rendered
parser-level income/expense totals that sum across currencies.

Adds summarizeByCurrency() (income positive / expenses negative, ore
rounding, SEK default) and renders one total line per currency on both
steps; preview table rows format amount and balance with the row's own
currency.

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

* fix(import): use roundOre from lib/money (antipattern ratchet)

The naive Math.round(x * 100) / 100 form is blocked by check:guards
(subtly wrong on exact-half values); lib/money.roundOre is canonical.

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

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-07-25 12:58:47 +02:00

37 lines
1.3 KiB
TypeScript

import { roundOre } from '@/lib/money'
import type { ParsedBankTransaction } from './types'
export interface BankFileCurrencyTotals {
currency: string
total_income: number
total_expenses: number
}
/**
* Per-currency income/expense totals for a parsed bank file.
*
* The parser-level stats (`stats.total_income` / `stats.total_expenses`) sum
* every row regardless of currency, which is meaningless for multi-currency
* exports (Wise emits per-row currencies, camt.053 reads Ccy per entry).
* Sign convention matches the parsers: income positive, expenses negative.
*/
export function summarizeByCurrency(
transactions: ParsedBankTransaction[],
): BankFileCurrencyTotals[] {
const perCurrency = new Map<string, { total_income: number; total_expenses: number }>()
for (const tx of transactions) {
const currency = tx.currency || 'SEK'
const row = perCurrency.get(currency) ?? { total_income: 0, total_expenses: 0 }
if (tx.amount > 0) row.total_income += tx.amount
else row.total_expenses += tx.amount
perCurrency.set(currency, row)
}
return [...perCurrency.entries()]
.map(([currency, totals]) => ({
currency,
total_income: roundOre(totals.total_income),
total_expenses: roundOre(totals.total_expenses),
}))
.sort((a, b) => a.currency.localeCompare(b.currency))
}