Compare commits

...

5 Commits

Author SHA1 Message Date
copilot-swe-agent[bot]
32498b0026 Address code review feedback: improve tests and clarify logic
- Add tests for empty string replacement and multiple occurrences
- Simplify split processing logic (no unnecessary null checks)
- All 496 tests pass

Co-authored-by: MatissJanis <886567+MatissJanis@users.noreply.github.com>
2026-02-04 13:20:38 +00:00
copilot-swe-agent[bot]
f5896fe770 Use parent_id field for more reliable split parent identification
- Replace backward search with parent_id field lookup
- More robust and efficient solution
- Addresses code review feedback

Co-authored-by: MatissJanis <886567+MatissJanis@users.noreply.github.com>
2026-02-04 13:16:27 +00:00
copilot-swe-agent[bot]
dfb6612333 Improve fix: prevent split transaction processing duplicates at source
- Track processed splits to avoid duplicate updates when multiple members are selected
- Remove deduplication workaround in favor of preventing duplicates at source
- Add helper to find split parent ID
- More robust solution that prevents the accumulation issue entirely

Co-authored-by: MatissJanis <886567+MatissJanis@users.noreply.github.com>
2026-02-04 13:13:59 +00:00
copilot-swe-agent[bot]
fcf42bef63 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>
2026-02-04 13:10:39 +00:00
copilot-swe-agent[bot]
ee2ed81c0a Initial plan 2026-02-04 13:03:59 +00:00
2 changed files with 73 additions and 0 deletions

View File

@@ -106,6 +106,20 @@ export function useTransactionBatchActions() {
}
const idSet = new Set(ids);
// Track which split transactions have been processed to avoid
// processing the same split multiple times when multiple members are selected
const processedSplits = new Set<string>();
// Helper to find the parent ID of a split transaction
const findSplitParentId = (trans: TransactionEntity): string => {
if (trans.is_parent) {
return trans.id;
}
if (trans.is_child && trans.parent_id) {
return trans.parent_id;
}
return trans.id; // Non-split transaction
};
transactionsToChange.forEach(trans => {
if (name === 'cleared' && trans.reconciled) {
@@ -120,6 +134,16 @@ export function useTransactionBatchActions() {
return;
}
// For split transactions, skip if we've already processed this split
// to avoid duplicate updates when multiple members are selected
if (trans.is_parent || trans.is_child) {
const splitParentId = findSplitParentId(trans);
if (processedSplits.has(splitParentId)) {
return;
}
processedSplits.add(splitParentId);
}
let valueToSet = value;
if (name === 'notes') {

View File

@@ -1,4 +1,5 @@
import {
applyFindReplace,
currencyToAmount,
getNumberFormat,
looselyParseAmount,
@@ -229,3 +230,51 @@ 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',
);
});
test('empty string replacement', () => {
expect(applyFindReplace('test', 'test', '', false)).toBe('');
expect(applyFindReplace('hello test world', 'test ', '', false)).toBe(
'hello world',
);
});
test('replaces multiple occurrences', () => {
expect(applyFindReplace('foo foo foo', 'foo', 'bar', false)).toBe(
'bar bar bar',
);
});
});