fix(reconciliation): direction-aware NULL-link settlement for transfer legs (#2018)
* fix(reconciliation): direction-aware NULL-link settlement for transfer legs A linked transaction with no resolvable cash account counted as settling its voucher for every account. For an own-account transfer on a non-primary bankavstamning card this hid the near leg while the far leg stayed listed, producing a false oforklarat (reported: momskonto card with differens 0 kr but oforklarat -2 593,75). get_account_gl_lines_for_matching now discounts a NULL-attributed link (pointer or junction) only when ALL of: the card is a non-primary account, the voucher touches >= 2 of the company's cash-account ledgers, and the row's sign contradicts the voucher's net leg on the account. All other shapes keep byte-identical legacy behavior, protecting unbackfilled single-leg rows (measured -37 000 kr false-alarm risk under the naive primary-only rule, simulated per-card against prod before choosing this rule; see DECISIONS.md). Footprint measured on prod: 24 vouchers on 7 cards in 6 companies; 5 cards improve (3 to exactly 0,00), 2 surface a real user-fixable mislink that was previously hidden. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HwDxhfhYbywQNr8y8a7pUo * test(reconciliation): isolate the primary-card exemption in pg coverage The existing primary-card assertion also passes via sign match; a reverse transfer (1931 -> 1930) whose NULL row contradicts the primary's +net leg pins condition 1 (legacy_null_ok) on its own. (CodeRabbit nitpick.) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HwDxhfhYbywQNr8y8a7pUo --------- Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Fable 5
parent
7f0f25b558
commit
1a8fe36bc4
@@ -957,7 +957,12 @@ export async function getReconciliationStatus(
|
||||
// Account-scoped since 20260723160000: a voucher whose links all sit on another
|
||||
// cash account (a transfer's other leg) counts as unmatched HERE, keeping this
|
||||
// number in agreement with the "Omatchade verifikationer" table the
|
||||
// reconciliation view derives from the same RPC.
|
||||
// reconciliation view derives from the same RPC. Direction-aware since
|
||||
// 20260828220000: on a NON-primary account, an own-account-transfer voucher
|
||||
// whose only link is a NULL-cash_account transaction with a contradicting
|
||||
// sign counts as unmatched here too (that row is the transfer's OTHER leg),
|
||||
// so both legs of a transfer land in this list symmetrically instead of one
|
||||
// leg polluting unexplained_difference.
|
||||
// effectiveFrom, NOT the caller's dateFrom: countedLines and countedTx are both
|
||||
// clamped to the IB floor above, and this list has to describe the SAME window
|
||||
// or the card contradicts itself. With the raw dateFrom, a window that opens
|
||||
@@ -1518,8 +1523,10 @@ export async function fetchJunctionLinkedTxIds(
|
||||
/** A match candidate that carries how many transactions already point at it. */
|
||||
export interface GLLineForMatching extends UnlinkedGLLine {
|
||||
/** Transactions settling this entry ON THE REQUESTED ACCOUNT (plus legacy
|
||||
* rows with no cash_account_id, which count everywhere). A transaction on
|
||||
* another cash account, e.g. the outgoing leg of an own-account transfer,
|
||||
* rows with no cash_account_id, which count everywhere EXCEPT on a
|
||||
* non-primary account when the voucher is an own-account transfer and the
|
||||
* row's sign contradicts this account's leg; 20260828220000). A transaction
|
||||
* on another cash account, e.g. the outgoing leg of an own-account transfer,
|
||||
* does not mark the voucher as matched here (issue #1026). */
|
||||
linked_transaction_count: number
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user