expense_metadata: every reader resolves the row a write lands on
getTransactions' provenance LATERAL, merchantForTransaction and the Slack details modal all select on transaction_id = $1 OR matched_transaction_id = $1 with a bare LIMIT 1, so they could read a different row than replaceLineItems writes. Same ORDER BY as the match, the write and the order-detail GET. Defensive, not a live bug: measured 2026-08-21, expense_metadata holds 667 rows, 147 of them pointing-only, and ZERO transactions resolve to more than one row by either link. This changes nothing today. It is here so the readers still agree on the day multiplicity appears, which is the only condition under which any of them was ever wrong.
This commit is contained in:
@@ -234,6 +234,16 @@ async function buildDetailsModal(transactionId: number, participantId: number) {
|
|||||||
`SELECT merchant_normalized AS merchant, line_items
|
`SELECT merchant_normalized AS merchant, line_items
|
||||||
FROM expense_metadata
|
FROM expense_metadata
|
||||||
WHERE transaction_id = $1 OR matched_transaction_id = $1
|
WHERE transaction_id = $1 OR matched_transaction_id = $1
|
||||||
|
-- Ordered the same way as replaceLineItems (line-items.ts) and
|
||||||
|
-- matchTransaction (intake-router.ts), so every reader of this row
|
||||||
|
-- resolves the same one a write lands on. NULLS LAST because DESC is
|
||||||
|
-- NULLS FIRST and the row that merely POINTS at the transaction has a
|
||||||
|
-- NULL transaction_id.
|
||||||
|
-- Measured 2026-08-21: 667 expense_metadata rows, 147 of them
|
||||||
|
-- pointing-only, and ZERO transactions resolving to more than one row by
|
||||||
|
-- either link — so this changes nothing today. It is here so the readers
|
||||||
|
-- still agree on the day multiplicity appears.
|
||||||
|
ORDER BY (transaction_id = $1) DESC NULLS LAST, id
|
||||||
LIMIT 1`,
|
LIMIT 1`,
|
||||||
[transactionId]
|
[transactionId]
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -119,6 +119,16 @@ export async function merchantForTransaction(
|
|||||||
const row = await queryRow<{ merchant_normalized: string | null }>(
|
const row = await queryRow<{ merchant_normalized: string | null }>(
|
||||||
`SELECT merchant_normalized FROM expense_metadata
|
`SELECT merchant_normalized FROM expense_metadata
|
||||||
WHERE transaction_id = $1 OR matched_transaction_id = $1
|
WHERE transaction_id = $1 OR matched_transaction_id = $1
|
||||||
|
-- Ordered the same way as replaceLineItems (line-items.ts) and
|
||||||
|
-- matchTransaction (intake-router.ts), so every reader of this row
|
||||||
|
-- resolves the same one a write lands on. NULLS LAST because DESC is
|
||||||
|
-- NULLS FIRST and the row that merely POINTS at the transaction has a
|
||||||
|
-- NULL transaction_id.
|
||||||
|
-- Measured 2026-08-21: 667 expense_metadata rows, 147 of them
|
||||||
|
-- pointing-only, and ZERO transactions resolving to more than one row by
|
||||||
|
-- either link — so this changes nothing today. It is here so the readers
|
||||||
|
-- still agree on the day multiplicity appears.
|
||||||
|
ORDER BY (transaction_id = $1) DESC NULLS LAST, id
|
||||||
LIMIT 1`,
|
LIMIT 1`,
|
||||||
[transactionId]
|
[transactionId]
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -367,6 +367,16 @@ export async function getTransactions(ownerId: number, filters: TransactionFilte
|
|||||||
SELECT em.route, em.platform
|
SELECT em.route, em.platform
|
||||||
FROM expense_metadata em
|
FROM expense_metadata em
|
||||||
WHERE em.transaction_id = t.id OR em.matched_transaction_id = t.id
|
WHERE em.transaction_id = t.id OR em.matched_transaction_id = t.id
|
||||||
|
-- Ordered the same way as replaceLineItems (line-items.ts) and
|
||||||
|
-- matchTransaction (intake-router.ts), so every reader of this row
|
||||||
|
-- resolves the same one a write lands on. NULLS LAST because DESC is
|
||||||
|
-- NULLS FIRST and the row that merely POINTS at the transaction has a
|
||||||
|
-- NULL transaction_id.
|
||||||
|
-- Measured 2026-08-21: 667 expense_metadata rows, 147 of them
|
||||||
|
-- pointing-only, and ZERO transactions resolving to more than one row by
|
||||||
|
-- either link — so this changes nothing today. It is here so the readers
|
||||||
|
-- still agree on the day multiplicity appears.
|
||||||
|
ORDER BY (em.transaction_id = t.id) DESC NULLS LAST, em.id
|
||||||
LIMIT 1
|
LIMIT 1
|
||||||
) order_ctx ON true
|
) order_ctx ON true
|
||||||
LEFT JOIN LATERAL (
|
LEFT JOIN LATERAL (
|
||||||
|
|||||||
Reference in New Issue
Block a user