53 lines
2.3 KiB
JavaScript
53 lines
2.3 KiB
JavaScript
|
|
'use strict';
|
||
|
|
|
||
|
|
// Canonical writers for a transaction's match state.
|
||
|
|
//
|
||
|
|
// A transaction's match state lives in three columns that must move together:
|
||
|
|
// match_status 'unmatched' | 'matched' | 'ignored'
|
||
|
|
// matched_bill_id the bill when matched, else NULL
|
||
|
|
// ignored 1 when explicitly ignored, else 0
|
||
|
|
//
|
||
|
|
// These were previously updated by copy-pasted inline UPDATEs across routes and
|
||
|
|
// services, which is exactly how they drift out of sync — e.g. QA-B5-04, where a
|
||
|
|
// bill purge left match_status='matched' with a NULL bill. Routing every
|
||
|
|
// single-transaction transition through these helpers keeps the columns
|
||
|
|
// consistent by construction. All are ownership-scoped (user_id) and return the
|
||
|
|
// number of rows changed. Bulk transitions (auto-match sweep, retention purge)
|
||
|
|
// stay in their own services where the WHERE clause is fundamentally different.
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Mark a transaction matched to a bill.
|
||
|
|
* @param {boolean} [opts.resetIgnored] also clear the ignored flag (used when
|
||
|
|
* matching a transaction directly, which implicitly un-ignores it).
|
||
|
|
*/
|
||
|
|
function markMatched(db, userId, transactionId, billId, { resetIgnored = false } = {}) {
|
||
|
|
return db.prepare(`
|
||
|
|
UPDATE transactions
|
||
|
|
SET matched_bill_id = ?, match_status = 'matched'${resetIgnored ? ', ignored = 0' : ''}, updated_at = datetime('now')
|
||
|
|
WHERE id = ? AND user_id = ?
|
||
|
|
`).run(billId, transactionId, userId).changes;
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Clear a transaction's match — back to 'unmatched' with no bill.
|
||
|
|
* @param {boolean} [opts.resetIgnored] also clear the ignored flag (un-ignore).
|
||
|
|
*/
|
||
|
|
function markUnmatched(db, userId, transactionId, { resetIgnored = false } = {}) {
|
||
|
|
return db.prepare(`
|
||
|
|
UPDATE transactions
|
||
|
|
SET matched_bill_id = NULL, match_status = 'unmatched'${resetIgnored ? ', ignored = 0' : ''}, updated_at = datetime('now')
|
||
|
|
WHERE id = ? AND user_id = ?
|
||
|
|
`).run(transactionId, userId).changes;
|
||
|
|
}
|
||
|
|
|
||
|
|
/** Mark a transaction ignored — dropped from matching, no bill. */
|
||
|
|
function markIgnored(db, userId, transactionId) {
|
||
|
|
return db.prepare(`
|
||
|
|
UPDATE transactions
|
||
|
|
SET ignored = 1, match_status = 'ignored', matched_bill_id = NULL, updated_at = datetime('now')
|
||
|
|
WHERE id = ? AND user_id = ?
|
||
|
|
`).run(transactionId, userId).changes;
|
||
|
|
}
|
||
|
|
|
||
|
|
module.exports = { markMatched, markUnmatched, markIgnored };
|