mirror of
https://github.com/actualbudget/actual.git
synced 2026-05-06 15:12:35 -05:00
Fix updateTransaction corrupting split parents with partial updates (#7242)
* [AI] Fix updateTransaction corrupting split parents with partial updates
When `api.updateTransaction(id, { notes: '...' })` is called on a split
parent, the `updateTransaction` helper replaces the parent with the
sparse update object (`{ id, notes }`) instead of merging it with
the existing transaction data. This causes `recalculateSplit` to see
`amount` as `undefined` (→ 0), which doesn't match the children's
total and sets a `SplitTransactionError` on the parent. `makeChild`
also inherits undefined `account`, `date`, and `cleared` values,
potentially creating broken child rows.
Fix: merge the incoming partial fields (`{ ...trans, ...transaction }`)
so all existing properties are preserved.
Add a test that performs a notes-only update on a split parent and
asserts no error is set and the amount stays intact.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* [AI] Add release notes for PR #7242
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* Address review feedback: remove verbose comment and simplify release note
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
---------
Co-authored-by: L. Warren Thompson <lwarrenthompson@Warren-MBP.local>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
committed by
Matt Fiddaman
parent
c75a94e8b0
commit
d262f7d8b2
@@ -201,6 +201,40 @@ describe('Transactions', () => {
|
||||
expect(data.length).toBe(5);
|
||||
});
|
||||
|
||||
test('partially updating a split parent preserves amount and does not set error', () => {
|
||||
const transactions = [
|
||||
makeTransaction({ amount: 2001 }),
|
||||
...makeSplitTransaction({ id: 't1', amount: 2500 }, [
|
||||
{ id: 't2', amount: 2000 },
|
||||
{ id: 't3', amount: 500 },
|
||||
]),
|
||||
makeTransaction({ amount: 3002 }),
|
||||
];
|
||||
|
||||
// Simulate a partial update (only `notes`) on the parent — this is
|
||||
// how `api.updateTransaction(id, { notes: '...' })` calls it in
|
||||
// `api.ts`: `updateTransaction(transactions, { id, ...fields })`.
|
||||
const { data, diff } = updateTransaction(transactions, {
|
||||
id: 't1',
|
||||
notes: 'updated note',
|
||||
} as TransactionEntity);
|
||||
|
||||
// The parent should get the updated notes without an error
|
||||
const parent = data.find(d => d.id === 't1');
|
||||
expect(parent?.notes).toBe('updated note');
|
||||
expect(parent?.amount).toBe(2500);
|
||||
expect(parent?.error).toBeNull();
|
||||
|
||||
// Children should be unchanged
|
||||
expect(data.filter(t => t.parent_id === 't1').length).toBe(2);
|
||||
|
||||
expect(diff).toEqual({
|
||||
added: [],
|
||||
deleted: [],
|
||||
updated: [expect.objectContaining({ id: 't1', notes: 'updated note' })],
|
||||
});
|
||||
});
|
||||
|
||||
test('deleting a split transaction works', () => {
|
||||
const transactions = [
|
||||
makeTransaction({ amount: 2001 }),
|
||||
|
||||
@@ -262,7 +262,8 @@ export function updateTransaction(
|
||||
) {
|
||||
return replaceTransactions(transactions, transaction.id, trans => {
|
||||
if (trans.is_parent) {
|
||||
const parent = trans.id === transaction.id ? transaction : trans;
|
||||
const parent =
|
||||
trans.id === transaction.id ? { ...trans, ...transaction } : trans;
|
||||
const originalSubtransactions =
|
||||
parent.subtransactions ?? trans.subtransactions;
|
||||
const sub = originalSubtransactions?.map(t => {
|
||||
|
||||
6
upcoming-release-notes/7242.md
Normal file
6
upcoming-release-notes/7242.md
Normal file
@@ -0,0 +1,6 @@
|
||||
---
|
||||
category: Bugfixes
|
||||
authors: [lwarrenthompson]
|
||||
---
|
||||
|
||||
Fix `api.updateTransaction()` corrupting split parent transactions when doing partial updates
|
||||
Reference in New Issue
Block a user