fix: preserve schedule link when merging transactions (#7177)

* [AI] fix: preserve schedule link when merging transactions

When merging two transactions where one is linked to a schedule,
the schedule field was not included in the merge update, causing
the schedule association to be silently dropped. This resulted in
duplicate transactions and incorrect "Due" status for scheduled
transactions.

Add `schedule: keep.schedule || drop.schedule` to both the normal
merge path and the subtransaction merge path, matching the existing
fallback pattern used for payee, category, notes, etc.

Add three test cases covering:
- Schedule preserved from dropped transaction when kept has none
- Kept transaction's schedule takes priority when both have one
- Schedule preserved when merging manual scheduled with banksynced

Fixes #6997

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* Add release notes for PR #7177

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
okxint
2026-03-17 10:44:16 +05:30
committed by GitHub
parent f73c5e9210
commit 1d0281025d
3 changed files with 61 additions and 0 deletions

View File

@@ -284,6 +284,59 @@ describe('Merging success', () => {
});
});
it('preserves schedule link from dropped transaction when kept transaction has none', async () => {
const t1 = await db.insertTransaction({
account: 'one',
amount: 5,
date: '2025-01-01',
imported_id: 'imported_1',
});
const t2 = await db.insertTransaction({
...transaction2,
schedule: 'schedule-1',
});
expect(await mergeTransactions([{ id: t1 }, { id: t2 }])).toBe(t1);
const transactions = await getAllTransactions();
expect(transactions.length).toBe(1);
expect(transactions[0].schedule).toBe('schedule-1');
});
it('preserves schedule link from kept transaction when both have schedules', async () => {
const t1 = await db.insertTransaction({
...transaction1,
imported_id: 'imported_1',
schedule: 'schedule-keep',
});
const t2 = await db.insertTransaction({
...transaction2,
schedule: 'schedule-drop',
});
expect(await mergeTransactions([{ id: t1 }, { id: t2 }])).toBe(t1);
const transactions = await getAllTransactions();
expect(transactions.length).toBe(1);
expect(transactions[0].schedule).toBe('schedule-keep');
});
it('preserves schedule link when merging manual scheduled with banksynced', async () => {
// Manual transaction linked to a schedule
const t1 = await db.insertTransaction({
...transaction1,
schedule: 'schedule-1',
});
// Bank-synced transaction (kept due to imported_id priority)
const t2 = await db.insertTransaction({
...transaction2,
imported_id: 'imported_2',
});
expect(await mergeTransactions([{ id: t1 }, { id: t2 }])).toBe(t2);
const transactions = await getAllTransactions();
expect(transactions.length).toBe(1);
expect(transactions[0].schedule).toBe('schedule-1');
});
it('preserves split categories when merging split transaction with uncategorized imported transaction', async () => {
// Create a manual transaction with splits
const manualParent = await db.insertTransaction({

View File

@@ -81,6 +81,7 @@ export async function mergeTransactions(
notes: keep.notes || drop.notes,
cleared: keep.cleared || drop.cleared,
reconciled: keep.reconciled || drop.reconciled,
schedule: keep.schedule || drop.schedule,
} as unknown as TransactionEntity);
} else {
// Normal merge without subtransactions
@@ -91,6 +92,7 @@ export async function mergeTransactions(
notes: keep.notes || drop.notes,
cleared: keep.cleared || drop.cleared,
reconciled: keep.reconciled || drop.reconciled,
schedule: keep.schedule || drop.schedule,
} as TransactionEntity);
}

View File

@@ -0,0 +1,6 @@
---
category: Bugfixes
authors: [okxint]
---
Fix schedule link being lost when merging transactions