fix(reconciliation): secondary-account scoping, dialog clipping, and N:1 matching (#624)
* fix(reconciliation): secondary-account scoping, dialog clipping, and N:1 matching Three follow-ups to per-account bank reconciliation (PR #623): - Secondary same-currency accounts (e.g. a 1931 savings account) double-counted the company's unassigned (NULL cash_account_id) transactions, inflating their bank total and showing a large bogus difference while 1930 still reconciled. Only the primary cash account now claims NULL rows; every other account scopes strictly to its own id. `includeUnassigned` is threaded through all status/run/list call sites from cash_accounts.is_primary. - The "Matcha mot befintlig verifikation" picker's dropdown was absolutely positioned inside the dialog's overflow-y-auto container and got clipped. Add an `inline` mode that renders the candidate list in normal flow; the dialog uses it, the reconciliation view keeps the compact overlay. - N:1 matching: several bank transactions can now settle one verifikat (a salary run paid in multiple transfers, an invoice paid in instalments). New get_account_gl_lines_for_matching RPC surfaces already-matched vouchers with a linked_transaction_count behind a "Visa även matchade verifikationer" toggle; manualLink's 1:1 guard is relaxed (the aggregate difference still catches mis-links). Tests: extended bank-reconciliation unit tests (strict scope + N:1), rewrote the cash_account_id isolation pg test to prove NULL rows land on the primary account only, and added a pg test for the new RPC. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(reconciliation): address PR review — accurate "att matcha mot" count - BankReconciliationView: the "N verifikationer att matcha mot" hint counted glLines (which includes already-matched vouchers when "Visa matchade" is on), overcounting the vouchers that still need a transaction. Use unmatchedGlLines so the label is correct regardless of the toggle (matches the table below). - MatchVerifikationPicker: document that `open` is overlay-only; the setOpen() writes are intentional no-ops in inline mode. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.8
parent
b5c3c3ec04
commit
094bd85e81
@@ -123,7 +123,7 @@ export const POST = withApiV1<{ params: Promise<{ companyId: string }> }>(
|
||||
const accountNumber = body.account_number ?? '1930'
|
||||
const { data: cashAccount } = await ctx.supabase
|
||||
.from('cash_accounts')
|
||||
.select('id, currency')
|
||||
.select('id, currency, is_primary')
|
||||
.eq('company_id', ctx.companyId!)
|
||||
.eq('ledger_account', accountNumber)
|
||||
.maybeSingle()
|
||||
@@ -144,6 +144,8 @@ export const POST = withApiV1<{ params: Promise<{ companyId: string }> }>(
|
||||
accountNumber,
|
||||
currency: (cashAccount?.currency as string | undefined) ?? 'SEK',
|
||||
cashAccountId: cashAccount?.id as string | undefined,
|
||||
// Only the primary account claims unassigned (NULL cash_account_id) rows.
|
||||
includeUnassigned: Boolean(cashAccount?.is_primary),
|
||||
dryRun: ctx.dryRun,
|
||||
})
|
||||
} catch (err) {
|
||||
|
||||
@@ -89,7 +89,7 @@ export const GET = withApiV1<{ params: Promise<{ companyId: string }> }>(
|
||||
const accountNumber = parsed.data.account_number ?? '1930'
|
||||
const { data: cashAccount } = await ctx.supabase
|
||||
.from('cash_accounts')
|
||||
.select('id, currency')
|
||||
.select('id, currency, is_primary')
|
||||
.eq('company_id', ctx.companyId!)
|
||||
.eq('ledger_account', accountNumber)
|
||||
.maybeSingle()
|
||||
@@ -111,6 +111,8 @@ export const GET = withApiV1<{ params: Promise<{ companyId: string }> }>(
|
||||
accountNumber,
|
||||
(cashAccount?.currency as string | undefined) ?? 'SEK',
|
||||
cashAccount?.id as string | undefined,
|
||||
// Only the primary account claims unassigned (NULL cash_account_id) rows.
|
||||
Boolean(cashAccount?.is_primary),
|
||||
)
|
||||
return ok(status, { requestId: ctx.requestId })
|
||||
} catch (err) {
|
||||
|
||||
Reference in New Issue
Block a user