fix(reconciliation): exclude ignored transactions from the bank total and bridge whitespace-drifted duplicate descriptions (#1705)

Bank reconciliation counted ignored transactions in bank_transaction_total
while excluding them from the unmatched count, so after the sanctioned
duplicate cleanup (ignore one twin) the differens showed the ignored sum
forever and is_reconciled was unreachable: observed live as a permanent
116 367 kr differens on a fully booked enskild firma (78 867 kr ignored
reconnect duplicates + 37 500 kr genuinely unbooked). The ignore toast
already promised 'försvinner från avstämningen'; now the engine keeps
that promise. Ignored rows are surfaced separately (count + sum) in the
status object, the UI card, and the v1 API, mirroring the IB pattern.

The duplicates themselves came from a PSD2 reconnect: the new connection
re-rendered identical transactions with drifted whitespace (CRLF vs
space, and a DROPPED space), so the prefix-containment content bridge
missed every twin. descriptionsBridge now strips all whitespace before
comparing: char-filtering preserves existing prefix relations, and the
compare stays confined to a (date, öre) bucket.

Co-authored-by: Jakob Wennberg <311770904+jakobwennberg-oss@users.noreply.github.com>
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Jakob Wennberg
2026-08-19 20:19:26 +02:00
committed by GitHub
co-authored by Jakob Wennberg Claude Fable 5
parent 6b9338f17e
commit 506d030bb1
10 changed files with 195 additions and 13 deletions
@@ -113,7 +113,11 @@ const QUICK_BOOK_TEMPLATES: {
// ============================================================
interface ReconciliationStatus {
/** Bank-feed total EXCLUDING ignored rows: the reconciling bank side. */
bank_transaction_total: number
/** Sum of ignored rows in the window; informational, not in the difference. */
ignored_transaction_total: number
ignored_transaction_count: number
/**
* @deprecated Kept on the server response for back-compat. The UI no longer
* reads it: `gl_1930_period_movement` is required.
@@ -1061,6 +1065,14 @@ export function BankReconciliationView({ periodId, periodBounds, autoRun }: Bank
{formatCurrency(status.difference)}
</span>
</div>
{status.ignored_transaction_count > 0 && (
<p className="pt-2 text-xs text-muted-foreground">
{t('recon_ignored_note', {
count: status.ignored_transaction_count,
amount: formatCurrency(status.ignored_transaction_total, accountCurrency),
})}
</p>
)}
{status.gl_1930_opening_balance !== 0 && (
<p className="pt-2 text-xs text-muted-foreground">
Ingående balans (IB) på <AccountNumber number={accountNumber} />:{' '}