Fix duplicate transaction updates in Find & Replace

- Add deduplication logic to prevent duplicate updates when multiple members of a split transaction are selected
- Add comprehensive tests for applyFindReplace function
- Fixes issue where selecting parent and children of split transactions caused duplicates

Co-authored-by: MatissJanis <886567+MatissJanis@users.noreply.github.com>
This commit is contained in:
copilot-swe-agent[bot]
2026-02-04 13:10:39 +00:00
parent ee2ed81c0a
commit fcf42bef63
2 changed files with 51 additions and 1 deletions

View File

@@ -175,7 +175,21 @@ export function useTransactionBatchActions() {
: diff.added;
});
await send('transactions-batch-update', changes);
// Deduplicate changes by ID to prevent duplicate updates when
// multiple members of a split transaction are selected
const deduplicatedChanges: Diff<TransactionEntity> = {
added: Array.from(
new Map(changes.added?.map(item => [item.id, item])).values(),
),
updated: Array.from(
new Map(changes.updated?.map(item => [item.id, item])).values(),
),
deleted: Array.from(
new Map(changes.deleted?.map(item => [item.id, item])).values(),
),
};
await send('transactions-batch-update', deduplicatedChanges);
onSuccess?.(ids, name, value, mode);
};

View File

@@ -1,4 +1,5 @@
import {
applyFindReplace,
currencyToAmount,
getNumberFormat,
looselyParseAmount,
@@ -229,3 +230,38 @@ describe('utility functions', () => {
expect(stringToInteger('3')).toBe(-3);
});
});
describe('applyFindReplace', () => {
test('basic string replacement works', () => {
expect(applyFindReplace('hello world', 'world', 'universe', false)).toBe(
'hello universe',
);
expect(applyFindReplace('test test test', 'test', 'demo', false)).toBe(
'demo demo demo',
);
});
test('handles null and empty values', () => {
expect(applyFindReplace(null, 'test', 'replace', false)).toBe('');
expect(applyFindReplace(undefined, 'test', 'replace', false)).toBe('');
expect(applyFindReplace('', 'test', 'replace', false)).toBe('');
expect(applyFindReplace('hello', '', 'replace', false)).toBe('hello');
});
test('regex replacement works', () => {
expect(applyFindReplace('test123', '\\d+', 'NUM', true)).toBe('testNUM');
expect(applyFindReplace('foo bar baz', 'b\\w+', 'word', true)).toBe(
'foo word word',
);
});
test('handles invalid regex gracefully', () => {
expect(applyFindReplace('test', '[invalid', 'replace', true)).toBe('test');
});
test('case-sensitive replacement', () => {
expect(applyFindReplace('Test test TEST', 'test', 'demo', false)).toBe(
'Test demo TEST',
);
});
});