fix(arcim): count already-linked invoices as done in the registration-link row (#2276)

The registration-link result row in ArcimMigrationWorkspace showed
`linked` over `scanned`, while its `unlinked` remainder subtracted both
`linked` and `alreadyLinked`. On a rerun where an earlier run had linked
every invoice (scanned 2, linked 0, alreadyLinked 2) the row read "0 av 2"
and, since unlinked was 0, carried no detail line to explain it: the step
looked failed when there was nothing left to do.

lib/invoices/link-migrated-registration-vouchers.ts reports each scanned
invoice into exactly one of seven buckets, so alreadyLinked is a subset of
scanned and is "done" in the same sense as linked. The row now shows
`linked + alreadyLinked` over `scanned` and, when alreadyLinked > 0, adds
a detail sentence ("2 var redan länkade sedan tidigare" / "2 were already
linked earlier") ahead of the existing unlinked breakdown, so the value
and the details agree. New key in both sv.json and en.json.

Other result rows checked: the documents import shows four separate
counts (no fraction) and the payment reconciliation result is not
rendered as a row, so neither has the same shape.

Fixes #2045


Claude-Session: https://claude.ai/code/session_015qgLgdt4mLmha1ZLFMwq1u

Co-authored-by: Jakob Wennberg <311770904+jakobwennberg-oss@users.noreply.github.com>
Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
Jakob Wennberg
2026-09-04 17:18:22 +02:00
committed by GitHub
co-authored by Jakob Wennberg Claude Fable 5.1
parent 1976129478
commit 88a3b5fb0f
3 changed files with 23 additions and 11 deletions
@@ -1967,19 +1967,29 @@ function ResultStep({
}
if (results.registrationLinks && results.registrationLinks.scanned > 0) {
const links = results.registrationLinks
const unlinked = links.scanned - links.linked - links.alreadyLinked
// An invoice that already carried its link (a rerun over invoices an
// earlier run linked) is done, not failed: it counts towards the
// displayed total and gets its own detail line, so the value and the
// details agree. Without this a rerun read "0 av 2" with no explanation.
const done = links.linked + links.alreadyLinked
const unlinked = links.scanned - done
const details: string[] = []
if (links.alreadyLinked > 0) {
details.push(t('ext_arcim_registration_links_already_linked', { count: links.alreadyLinked }))
}
if (unlinked > 0) {
details.push(t('ext_arcim_registration_links_detail', {
unlinked,
noRef: links.noRef,
refNotFetched: links.refNotFetched ?? 0,
unresolved: links.unresolved + links.ambiguous,
amountMismatch: links.amountMismatch,
}))
}
entityLines.push({
label: t('ext_arcim_registration_links_label'),
value: t('ext_arcim_registration_links_value', { linked: links.linked, scanned: links.scanned }),
detail: unlinked > 0
? t('ext_arcim_registration_links_detail', {
unlinked,
noRef: links.noRef,
refNotFetched: links.refNotFetched ?? 0,
unresolved: links.unresolved + links.ambiguous,
amountMismatch: links.amountMismatch,
})
: undefined,
value: t('ext_arcim_registration_links_value', { linked: done, scanned: links.scanned }),
detail: details.length > 0 ? details.join('. ') : undefined,
failed: false,
})
}