mirror of
https://github.com/actualbudget/actual.git
synced 2026-10-12 02:10:19 +00:00
fix: match "is nothing" rule conditions on transactions without a payee or category (#9017)
This commit is contained in:
@@ -406,6 +406,74 @@ describe('Transaction rules', () => {
|
||||
});
|
||||
});
|
||||
|
||||
test('"payee is nothing" matches a transaction without a payee field', async () => {
|
||||
await loadRules();
|
||||
await insertRule({
|
||||
stage: null,
|
||||
conditionsOp: 'and',
|
||||
conditions: [{ op: 'is', field: 'payee', value: null }],
|
||||
actions: [{ op: 'set', field: 'notes', value: 'no payee' }],
|
||||
});
|
||||
|
||||
// Bank sync leaves `payee` out entirely when the import has no payee
|
||||
expect(
|
||||
await runRules({
|
||||
imported_payee: 'kroger',
|
||||
date: '2020-08-11',
|
||||
amount: 50,
|
||||
}),
|
||||
).toEqual({
|
||||
imported_payee: 'kroger',
|
||||
date: '2020-08-11',
|
||||
amount: 50,
|
||||
notes: 'no payee',
|
||||
});
|
||||
|
||||
// A payee set by a rule action is kept
|
||||
await insertRule({
|
||||
stage: null,
|
||||
conditionsOp: 'and',
|
||||
conditions: [{ op: 'is', field: 'payee', value: null }],
|
||||
actions: [{ op: 'set', field: 'payee', value: 'kroger' }],
|
||||
});
|
||||
expect(
|
||||
await runRules({
|
||||
imported_payee: 'kroger',
|
||||
date: '2020-08-11',
|
||||
amount: 50,
|
||||
}),
|
||||
).toEqual({
|
||||
imported_payee: 'kroger',
|
||||
date: '2020-08-11',
|
||||
amount: 50,
|
||||
payee: 'kroger',
|
||||
notes: 'no payee',
|
||||
});
|
||||
});
|
||||
|
||||
test('"category is nothing" matches a transaction without a category field', async () => {
|
||||
await loadRules();
|
||||
await insertRule({
|
||||
stage: null,
|
||||
conditionsOp: 'and',
|
||||
conditions: [{ op: 'is', field: 'category', value: null }],
|
||||
actions: [{ op: 'set', field: 'notes', value: 'no category' }],
|
||||
});
|
||||
|
||||
expect(
|
||||
await runRules({
|
||||
payee: 'kroger',
|
||||
date: '2020-08-11',
|
||||
amount: 50,
|
||||
}),
|
||||
).toEqual({
|
||||
payee: 'kroger',
|
||||
date: '2020-08-11',
|
||||
amount: 50,
|
||||
notes: 'no category',
|
||||
});
|
||||
});
|
||||
|
||||
test('category_group condition matches categories in that group (live)', async () => {
|
||||
await loadRules();
|
||||
const billsGroupId = await db.insertCategoryGroup({ name: 'Bills' });
|
||||
|
||||
@@ -318,6 +318,8 @@ export async function getAllRuleIdsFromSchedules(
|
||||
}
|
||||
|
||||
// Runner
|
||||
const NULLABLE_RULE_FIELDS = ['payee', 'category'] as const;
|
||||
|
||||
export async function runRules(
|
||||
trans,
|
||||
accounts: Map<string, db.DbAccount> | null = null,
|
||||
@@ -340,9 +342,24 @@ export async function runRules(
|
||||
),
|
||||
);
|
||||
|
||||
let finalTrans = await prepareTransactionForRules({ ...trans }, accountsMap, {
|
||||
includeBalance: false,
|
||||
});
|
||||
// Bank sync and the client can leave `payee` or `category` out entirely
|
||||
// when the transaction has none. A condition never matches a missing
|
||||
// field, so a "payee is nothing" rule would silently skip such a
|
||||
// transaction. Match against null instead, and drop the field again
|
||||
// afterwards so the returned shape matches the input.
|
||||
const missingNullableFields = NULLABLE_RULE_FIELDS.filter(
|
||||
field => trans[field] === undefined,
|
||||
);
|
||||
const normalizedTrans = { ...trans };
|
||||
for (const field of missingNullableFields) {
|
||||
normalizedTrans[field] = null;
|
||||
}
|
||||
|
||||
let finalTrans = await prepareTransactionForRules(
|
||||
normalizedTrans,
|
||||
accountsMap,
|
||||
{ includeBalance: false },
|
||||
);
|
||||
let lastCategoryIdForGroup: string | null = finalTrans.category ?? null;
|
||||
|
||||
// The running balance is a query over every earlier transaction in
|
||||
@@ -431,7 +448,13 @@ export async function runRules(
|
||||
}
|
||||
}
|
||||
|
||||
return await finalizeTransactionForRules(finalTrans);
|
||||
const result = await finalizeTransactionForRules(finalTrans);
|
||||
for (const field of missingNullableFields) {
|
||||
if (result[field] === null) {
|
||||
delete result[field];
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
function conditionSpecialCases(cond: Condition | null): Condition | null {
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
---
|
||||
category: Bugfix
|
||||
authors: [YasinzHyper]
|
||||
---
|
||||
|
||||
Fix "payee is nothing" and "category is nothing" rule conditions not matching synced transactions that have no payee or category
|
||||
Reference in New Issue
Block a user