fix: historical import backend routes and service adjustments
This commit is contained in:
parent
d2d3045afe
commit
19421c06fc
|
|
@ -13,7 +13,7 @@ const {
|
||||||
const { amortizationSchedule, debtAprSnapshot } = require('../services/aprService');
|
const { amortizationSchedule, debtAprSnapshot } = require('../services/aprService');
|
||||||
const { standardizeError } = require('../middleware/errorFormatter');
|
const { standardizeError } = require('../middleware/errorFormatter');
|
||||||
const { validatePaymentInput } = require('../services/paymentValidation');
|
const { validatePaymentInput } = require('../services/paymentValidation');
|
||||||
const { addMerchantRule, syncBillPaymentsFromSimplefin } = require('../services/billMerchantRuleService');
|
const { addMerchantRule, syncBillPaymentsFromSimplefin, merchantMatches } = require('../services/billMerchantRuleService');
|
||||||
const { normalizeMerchant } = require('../services/subscriptionService');
|
const { normalizeMerchant } = require('../services/subscriptionService');
|
||||||
const { decorateTransaction } = require('../services/transactionService');
|
const { decorateTransaction } = require('../services/transactionService');
|
||||||
|
|
||||||
|
|
@ -914,7 +914,7 @@ function previewMatchCount(db, userId, normalized) {
|
||||||
`).all(userId);
|
`).all(userId);
|
||||||
return txRows.filter(tx => {
|
return txRows.filter(tx => {
|
||||||
const txMerchant = normalizeMerchant(tx.payee || tx.description || tx.memo || '');
|
const txMerchant = normalizeMerchant(tx.payee || tx.description || tx.memo || '');
|
||||||
return txMerchant && (txMerchant.includes(normalized) || normalized.includes(txMerchant));
|
return txMerchant && merchantMatches(txMerchant, normalized);
|
||||||
}).length;
|
}).length;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1071,7 +1071,7 @@ router.get('/:id/merchant-rules/candidates', (req, res) => {
|
||||||
for (const tx of txRows) {
|
for (const tx of txRows) {
|
||||||
const txMerchant = normalizeMerchant(tx.payee || tx.description || tx.memo || '');
|
const txMerchant = normalizeMerchant(tx.payee || tx.description || tx.memo || '');
|
||||||
if (!txMerchant) continue;
|
if (!txMerchant) continue;
|
||||||
const matches = rules.some(r => txMerchant.includes(r) || r.includes(txMerchant));
|
const matches = rules.some(r => merchantMatches(txMerchant, r));
|
||||||
if (!matches) continue;
|
if (!matches) continue;
|
||||||
|
|
||||||
const paidDate = tx.posted_date || (tx.transacted_at ? String(tx.transacted_at).slice(0, 10) : null);
|
const paidDate = tx.posted_date || (tx.transacted_at ? String(tx.transacted_at).slice(0, 10) : null);
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,18 @@
|
||||||
const { normalizeMerchant } = require('./subscriptionService');
|
const { normalizeMerchant } = require('./subscriptionService');
|
||||||
const { computeBalanceDelta } = require('./billsService');
|
const { computeBalanceDelta } = require('./billsService');
|
||||||
|
|
||||||
|
// Word-boundary merchant match — requires the rule to appear as complete word(s)
|
||||||
|
// within the transaction string (or vice versa), not just as a substring.
|
||||||
|
// Prevents "suno" matching "sunoco", "prime" matching "prime video", etc.
|
||||||
|
function merchantMatches(txMerchant, ruleMerchant) {
|
||||||
|
if (!txMerchant || !ruleMerchant) return false;
|
||||||
|
if (txMerchant === ruleMerchant) return true;
|
||||||
|
const esc = s => s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
||||||
|
const wordBoundary = s => new RegExp(`(^|\\s)${esc(s)}(\\s|$)`);
|
||||||
|
return wordBoundary(ruleMerchant).test(txMerchant) ||
|
||||||
|
wordBoundary(txMerchant).test(ruleMerchant);
|
||||||
|
}
|
||||||
|
|
||||||
// Persist a merchant→bill rule so future synced transactions auto-match.
|
// Persist a merchant→bill rule so future synced transactions auto-match.
|
||||||
function addMerchantRule(db, userId, billId, merchant) {
|
function addMerchantRule(db, userId, billId, merchant) {
|
||||||
const normalized = normalizeMerchant(merchant);
|
const normalized = normalizeMerchant(merchant);
|
||||||
|
|
@ -90,7 +102,7 @@ function applyMerchantRules(db, userId) {
|
||||||
if (!txMerchant) continue;
|
if (!txMerchant) continue;
|
||||||
|
|
||||||
const rule = rules.find(r =>
|
const rule = rules.find(r =>
|
||||||
txMerchant.includes(r.merchant) || r.merchant.includes(txMerchant)
|
merchantMatches(txMerchant, r.merchant)
|
||||||
);
|
);
|
||||||
if (!rule) continue;
|
if (!rule) continue;
|
||||||
|
|
||||||
|
|
@ -210,7 +222,7 @@ function syncBillPaymentsFromSimplefin(db, userId, billId) {
|
||||||
for (const tx of txRows) {
|
for (const tx of txRows) {
|
||||||
const txMerchant = normalizeMerchant(tx.payee || tx.description || tx.memo || '');
|
const txMerchant = normalizeMerchant(tx.payee || tx.description || tx.memo || '');
|
||||||
if (!txMerchant) continue;
|
if (!txMerchant) continue;
|
||||||
const matches = rules.some(r => txMerchant.includes(r) || r.includes(txMerchant));
|
const matches = rules.some(r => merchantMatches(txMerchant, r));
|
||||||
if (!matches) continue;
|
if (!matches) continue;
|
||||||
const paidDate = tx.posted_date || (tx.transacted_at ? String(tx.transacted_at).slice(0, 10) : null);
|
const paidDate = tx.posted_date || (tx.transacted_at ? String(tx.transacted_at).slice(0, 10) : null);
|
||||||
if (!paidDate) continue;
|
if (!paidDate) continue;
|
||||||
|
|
@ -249,4 +261,4 @@ function syncBillPaymentsFromSimplefin(db, userId, billId) {
|
||||||
return { added, late_attributions: lateAttributions };
|
return { added, late_attributions: lateAttributions };
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = { addMerchantRule, applyMerchantRules, syncBillPaymentsFromSimplefin };
|
module.exports = { addMerchantRule, applyMerchantRules, syncBillPaymentsFromSimplefin, merchantMatches };
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue