[PR #6855] [CLOSED] Fix Find & Replace duplicate updates for split transactions #84457

Closed
opened 2026-05-19 09:34:06 -05:00 by GiteaMirror · 0 comments
Owner

📋 Pull Request Information

Original PR: https://github.com/actualbudget/actual/pull/6855
Author: @Copilot
Created: 2/4/2026
Status: Closed

Base: masterHead: copilot/fix-find-replace-bug


📝 Commits (5)

  • ee2ed81 Initial plan
  • fcf42be Fix duplicate transaction updates in Find & Replace
  • dfb6612 Improve fix: prevent split transaction processing duplicates at source
  • f5896fe Use parent_id field for more reliable split parent identification
  • 32498b0 Address code review feedback: improve tests and clarify logic

📊 Changes

2 files changed (+73 additions, -0 deletions)

View changed files

📝 packages/desktop-client/src/hooks/useTransactionBatchActions.ts (+24 -0)
📝 packages/loot-core/src/shared/util.test.ts (+49 -0)

📄 Description

Problem

Find & Replace operations on split transactions cause duplicates and data corruption when multiple members of the same split are selected. Each selected member triggers updateTransaction() which returns a diff for the entire split, leading to:

  • Duplicate entries in the changes array (same transaction updated N times)
  • Non-idempotent operations applied multiple times ("test""testtest""testtesttest")

Solution

Track processed splits to ensure each split is only updated once, regardless of how many members are selected:

const processedSplits = new Set<string>();

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;
};

// In the forEach loop:
if (trans.is_parent || trans.is_child) {
  const splitParentId = findSplitParentId(trans);
  if (processedSplits.has(splitParentId)) return;
  processedSplits.add(splitParentId);
}

Changes

  • useTransactionBatchActions.ts: Add split deduplication logic
  • util.test.ts: Add test coverage for applyFindReplace (basic replacement, regex, null handling, edge cases)
Original prompt

This section details on the original issue you should resolve

<issue_title>[Bug]: Find & Replace not working correctly all the time</issue_title>
<issue_description>### Verified issue does not already exist?

  • I have searched and found no existing issue

What happened?

After a few large find/replace operations, I found a few duplicated transactions and some missing transactions. Previously reconciled accounts with matching running balances no longer matched.

One transaction, in a closed account, was duplicated twice.

I had to revert to a backup.

How can we reproduce the issue?

How can we reproduce the issue? I don't believe I can reproduce it exactly, nor do I want to mess with my data again. Also, the find/replace works strangely and has no "Apply", you have to press "Enter". Not intuitive at all.

Where are you hosting Actual?

None

What browsers are you seeing the problem on?

No response

Operating System

None</issue_description>

Comments on the Issue (you are @copilot in this section)

@youngcw I wonder if rules are getting run after and causing the problems. @youngcw I looked at the code for the find an replace again. Nothing in there seems like it would change amounts or create transactions. I would check your rules to see if anything jumps out. @youngcw If you could do it again and try to figure out edit caused the issue that would be useful. You should be able to CTRL-Z the changes once you find the one that is breaking things @youngcw I wonder if it isn't because of merged transactions, but transfers in general. I wonder if new transfers are getting made @MatissJanis Which PR introduced find&replace? We (or I) can ask an AI agent to do a preliminary analysis there to see if it finds any problems. @MatissJanis @copilot please investigate what could cause the problem reported in this issue. It was possibly introduced in actualbudget/actual#6282

Let Copilot coding agent set things up for you — coding agent works faster and does higher quality work when set up for your repo.


Bundle Stats

Bundle Files count Total bundle size % Changed
desktop-client 28 14.47 MB → 14.47 MB (+3.97 kB) +0.03%
loot-core 1 5.84 MB 0%
api 1 4.38 MB 0%
View detailed bundle stats

desktop-client

Total

Files count Total bundle size % Changed
28 14.47 MB → 14.47 MB (+3.97 kB) +0.03%
Changeset
File Δ Size
src/hooks/useTransactionBatchActions.ts 📈 +559 B (+4.24%) 12.87 kB → 13.42 kB
locale/es.json 📈 +2.71 kB (+1.58%) 171.21 kB → 173.92 kB
locale/de.json 📈 +734 B (+0.40%) 177.78 kB → 178.5 kB
View detailed bundle breakdown

Added
No assets were added

Removed
No assets were removed

Bigger

Asset File Size % Changed
static/js/es.js 171.21 kB → 173.92 kB (+2.71 kB) +1.58%
static/js/de.js 177.78 kB → 178.5 kB (+734 B) +0.40%
static/js/useTransactionBatchActions.js 13.23 kB → 13.77 kB (+559 B) +4.13%

Smaller
No assets were smaller

Unchanged

Asset File Size % Changed
static/js/index.js 9.22 MB 0%
static/js/indexeddb-main-thread-worker-e59fee74.js 12.94 kB 0%
static/js/workbox-window.prod.es5.js 5.64 kB 0%
static/js/da.js 106.62 kB 0%
static/js/en-GB.js 7.18 kB 0%
static/js/en.js 162.91 kB 0%
static/js/fr.js 179.72 kB 0%
static/js/it.js 171.54 kB 0%
static/js/nb-NO.js 157.23 kB 0%
static/js/nl.js 106.65 kB 0%
static/js/pl.js 88.64 kB 0%
static/js/pt-BR.js 146.62 kB 0%
static/js/ru.js 106.97 kB 0%
static/js/sv.js 78.2 kB 0%
static/js/th.js 182.35 kB 0%
static/js/uk.js 215.11 kB 0%
static/js/resize-observer.js 18.37 kB 0%
static/js/BackgroundImage.js 120.54 kB 0%
static/js/ReportRouter.js 1.11 MB 0%
static/js/narrow.js 641.19 kB 0%
static/js/TransactionList.js 105.97 kB 0%
static/js/wide.js 160.07 kB 0%
static/js/AppliedFilters.js 9.71 kB 0%
static/js/usePayeeRuleCounts.js 11.79 kB 0%
static/js/FormulaEditor.js 1.04 MB 0%

loot-core

Total

Files count Total bundle size % Changed
1 5.84 MB 0%
View detailed bundle breakdown

Added
No assets were added

Removed
No assets were removed

Bigger
No assets were bigger

Smaller
No assets were smaller

Unchanged

Asset File Size % Changed
kcab.worker.DE5uAdQe.js 5.84 MB 0%

api

Total

Files count Total bundle size % Changed
1 4.38 MB 0%
View detailed bundle breakdown

Added
No assets were added

Removed
No assets were removed

Bigger
No assets were bigger

Smaller
No assets were smaller

Unchanged

Asset File Size % Changed
bundle.api.js 4.38 MB 0%

🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.

## 📋 Pull Request Information **Original PR:** https://github.com/actualbudget/actual/pull/6855 **Author:** [@Copilot](https://github.com/apps/copilot-swe-agent) **Created:** 2/4/2026 **Status:** ❌ Closed **Base:** `master` ← **Head:** `copilot/fix-find-replace-bug` --- ### 📝 Commits (5) - [`ee2ed81`](https://github.com/actualbudget/actual/commit/ee2ed81c0a704de14da6e4fbc2ec05540fe98fe8) Initial plan - [`fcf42be`](https://github.com/actualbudget/actual/commit/fcf42bef637a173a65bc4b7aa023c1d5dd823c49) Fix duplicate transaction updates in Find & Replace - [`dfb6612`](https://github.com/actualbudget/actual/commit/dfb6612333b4da58cb0c9f9419cc46360b376589) Improve fix: prevent split transaction processing duplicates at source - [`f5896fe`](https://github.com/actualbudget/actual/commit/f5896fe7705b090c3ec46027adf36f8108758114) Use parent_id field for more reliable split parent identification - [`32498b0`](https://github.com/actualbudget/actual/commit/32498b00267f057420fa9b6f87a8ee476ea3f5a9) Address code review feedback: improve tests and clarify logic ### 📊 Changes **2 files changed** (+73 additions, -0 deletions) <details> <summary>View changed files</summary> 📝 `packages/desktop-client/src/hooks/useTransactionBatchActions.ts` (+24 -0) 📝 `packages/loot-core/src/shared/util.test.ts` (+49 -0) </details> ### 📄 Description ## Problem Find & Replace operations on split transactions cause duplicates and data corruption when multiple members of the same split are selected. Each selected member triggers `updateTransaction()` which returns a diff for the entire split, leading to: - Duplicate entries in the changes array (same transaction updated N times) - Non-idempotent operations applied multiple times (`"test"` → `"testtest"` → `"testtesttest"`) ## Solution Track processed splits to ensure each split is only updated once, regardless of how many members are selected: ```typescript const processedSplits = new Set<string>(); 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; }; // In the forEach loop: if (trans.is_parent || trans.is_child) { const splitParentId = findSplitParentId(trans); if (processedSplits.has(splitParentId)) return; processedSplits.add(splitParentId); } ``` ## Changes - **`useTransactionBatchActions.ts`**: Add split deduplication logic - **`util.test.ts`**: Add test coverage for `applyFindReplace` (basic replacement, regex, null handling, edge cases) <!-- START COPILOT ORIGINAL PROMPT --> <details> <summary>Original prompt</summary> > > ---- > > *This section details on the original issue you should resolve* > > <issue_title>[Bug]: Find & Replace not working correctly all the time</issue_title> > <issue_description>### Verified issue does not already exist? > > - [x] I have searched and found no existing issue > > ### What happened? > > After a few large find/replace operations, I found a few duplicated transactions and some missing transactions. Previously reconciled accounts with matching running balances no longer matched. > > One transaction, in a closed account, was duplicated twice. > > I had to revert to a backup. > > ### How can we reproduce the issue? > > How can we reproduce the issue? I don't believe I can reproduce it exactly, nor do I want to mess with my data again. Also, the find/replace works strangely and has no "Apply", you have to press "Enter". Not intuitive at all. > > ### Where are you hosting Actual? > > None > > ### What browsers are you seeing the problem on? > > _No response_ > > ### Operating System > > None</issue_description> > > ## Comments on the Issue (you are @copilot in this section) > > <comments> > <comment_new><author>@youngcw</author><body> > I wonder if rules are getting run after and causing the problems. </body></comment_new> > <comment_new><author>@youngcw</author><body> > I looked at the code for the find an replace again. Nothing in there seems like it would change amounts or create transactions. I would check your rules to see if anything jumps out. </body></comment_new> > <comment_new><author>@youngcw</author><body> > If you could do it again and try to figure out edit caused the issue that would be useful. You should be able to CTRL-Z the changes once you find the one that is breaking things</body></comment_new> > <comment_new><author>@youngcw</author><body> > I wonder if it isn't because of merged transactions, but transfers in general. I wonder if new transfers are getting made</body></comment_new> > <comment_new><author>@MatissJanis</author><body> > Which PR introduced find&replace? We (or I) can ask an AI agent to do a preliminary analysis there to see if it finds any problems. </body></comment_new> > <comment_new><author>@MatissJanis</author><body> > @copilot please investigate what could cause the problem reported in this issue. It was possibly introduced in actualbudget/actual#6282 > > </body></comment_new> > </comments> > </details> <!-- START COPILOT CODING AGENT SUFFIX --> - Fixes actualbudget/actual#6847 <!-- START COPILOT CODING AGENT TIPS --> --- ✨ Let Copilot coding agent [set things up for you](https://github.com/actualbudget/actual/issues/new?title=✨+Set+up+Copilot+instructions&body=Configure%20instructions%20for%20this%20repository%20as%20documented%20in%20%5BBest%20practices%20for%20Copilot%20coding%20agent%20in%20your%20repository%5D%28https://gh.io/copilot-coding-agent-tips%29%2E%0A%0A%3COnboard%20this%20repo%3E&assignees=copilot) — coding agent works faster and does higher quality work when set up for your repo. <!--- actual-bot-sections ---> <hr /> <!--- bundlestats-action-comment key:combined start ---> ### Bundle Stats Bundle | Files count | Total bundle size | % Changed ------ | ----------- | ----------------- | --------- desktop-client | 28 | 14.47 MB → 14.47 MB (+3.97 kB) | +0.03% loot-core | 1 | 5.84 MB | 0% api | 1 | 4.38 MB | 0% <details> <summary>View detailed bundle stats</summary> #### desktop-client **Total** Files count | Total bundle size | % Changed ----------- | ----------------- | --------- 28 | 14.47 MB → 14.47 MB (+3.97 kB) | +0.03% <details> <summary>Changeset</summary> File | Δ | Size ---- | - | ---- `src/hooks/useTransactionBatchActions.ts` | 📈 +559 B (+4.24%) | 12.87 kB → 13.42 kB `locale/es.json` | 📈 +2.71 kB (+1.58%) | 171.21 kB → 173.92 kB `locale/de.json` | 📈 +734 B (+0.40%) | 177.78 kB → 178.5 kB </details> <details> <summary>View detailed bundle breakdown</summary> <div> **Added** No assets were added **Removed** No assets were removed **Bigger** Asset | File Size | % Changed ----- | --------- | --------- static/js/es.js | 171.21 kB → 173.92 kB (+2.71 kB) | +1.58% static/js/de.js | 177.78 kB → 178.5 kB (+734 B) | +0.40% static/js/useTransactionBatchActions.js | 13.23 kB → 13.77 kB (+559 B) | +4.13% **Smaller** No assets were smaller **Unchanged** Asset | File Size | % Changed ----- | --------- | --------- static/js/index.js | 9.22 MB | 0% static/js/indexeddb-main-thread-worker-e59fee74.js | 12.94 kB | 0% static/js/workbox-window.prod.es5.js | 5.64 kB | 0% static/js/da.js | 106.62 kB | 0% static/js/en-GB.js | 7.18 kB | 0% static/js/en.js | 162.91 kB | 0% static/js/fr.js | 179.72 kB | 0% static/js/it.js | 171.54 kB | 0% static/js/nb-NO.js | 157.23 kB | 0% static/js/nl.js | 106.65 kB | 0% static/js/pl.js | 88.64 kB | 0% static/js/pt-BR.js | 146.62 kB | 0% static/js/ru.js | 106.97 kB | 0% static/js/sv.js | 78.2 kB | 0% static/js/th.js | 182.35 kB | 0% static/js/uk.js | 215.11 kB | 0% static/js/resize-observer.js | 18.37 kB | 0% static/js/BackgroundImage.js | 120.54 kB | 0% static/js/ReportRouter.js | 1.11 MB | 0% static/js/narrow.js | 641.19 kB | 0% static/js/TransactionList.js | 105.97 kB | 0% static/js/wide.js | 160.07 kB | 0% static/js/AppliedFilters.js | 9.71 kB | 0% static/js/usePayeeRuleCounts.js | 11.79 kB | 0% static/js/FormulaEditor.js | 1.04 MB | 0% </div> </details> --- #### loot-core **Total** Files count | Total bundle size | % Changed ----------- | ----------------- | --------- 1 | 5.84 MB | 0% <details> <summary>View detailed bundle breakdown</summary> <div> **Added** No assets were added **Removed** No assets were removed **Bigger** No assets were bigger **Smaller** No assets were smaller **Unchanged** Asset | File Size | % Changed ----- | --------- | --------- kcab.worker.DE5uAdQe.js | 5.84 MB | 0% </div> </details> --- #### api **Total** Files count | Total bundle size | % Changed ----------- | ----------------- | --------- 1 | 4.38 MB | 0% <details> <summary>View detailed bundle breakdown</summary> <div> **Added** No assets were added **Removed** No assets were removed **Bigger** No assets were bigger **Smaller** No assets were smaller **Unchanged** Asset | File Size | % Changed ----- | --------- | --------- bundle.api.js | 4.38 MB | 0% </div> </details> </details> <!--- bundlestats-action-comment key:combined end ---> --- <sub>🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.</sub>
GiteaMirror added the pull-request label 2026-05-19 09:34:06 -05:00
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: github-starred/actual#84457