From 5a9e2785f051bd834ae9f3c1645a0ad34870fd08 Mon Sep 17 00:00:00 2001 From: Nathan Nguyen <146415969+NathanDrake2406@users.noreply.github.com> Date: Wed, 18 Mar 2026 02:36:17 +1100 Subject: [PATCH] fix: resolve updateAccountBalance checkbox disabled for past dates MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The date valueChanges handler unconditionally disabled the "Update Cash Balance" checkbox when the date was not today, conflicting with the type handler which enabled it for BUY/SELL/DIVIDEND types. Switching types re-triggered the type handler, masking the bug. Extract the enable/disable decision into a pure helper function (shouldEnableUpdateAccountBalance) and call it from the type, accountId, and date subscribers — removing the date-based restriction entirely. Closes #6327 --- ...ate-or-update-activity-dialog.component.ts | 45 +++---- ...e-or-update-activity-dialog.helper.spec.ts | 117 ++++++++++++++++++ ...create-or-update-activity-dialog.helper.ts | 21 ++++ 3 files changed, 158 insertions(+), 25 deletions(-) create mode 100644 apps/client/src/app/pages/portfolio/activities/create-or-update-activity-dialog/create-or-update-activity-dialog.helper.spec.ts create mode 100644 apps/client/src/app/pages/portfolio/activities/create-or-update-activity-dialog/create-or-update-activity-dialog.helper.ts diff --git a/apps/client/src/app/pages/portfolio/activities/create-or-update-activity-dialog/create-or-update-activity-dialog.component.ts b/apps/client/src/app/pages/portfolio/activities/create-or-update-activity-dialog/create-or-update-activity-dialog.component.ts index c7cd63191..5ac7d55ae 100644 --- a/apps/client/src/app/pages/portfolio/activities/create-or-update-activity-dialog/create-or-update-activity-dialog.component.ts +++ b/apps/client/src/app/pages/portfolio/activities/create-or-update-activity-dialog/create-or-update-activity-dialog.component.ts @@ -49,6 +49,7 @@ import { calendarClearOutline, refreshOutline } from 'ionicons/icons'; import { EMPTY, Subject } from 'rxjs'; import { catchError, delay, takeUntil } from 'rxjs/operators'; +import { shouldEnableUpdateAccountBalance } from './create-or-update-activity-dialog.helper'; import { CreateOrUpdateActivityDialogParams } from './interfaces/interfaces'; import { ActivityType } from './types/activity-type.type'; @@ -272,12 +273,7 @@ export class GfCreateOrUpdateActivityDialogComponent implements OnDestroy { this.activityForm.get('currencyOfUnitPrice').setValue(currency); if (['FEE', 'INTEREST'].includes(type)) { - if (this.activityForm.get('accountId').value) { - this.activityForm.get('updateAccountBalance').enable(); - } else { - this.activityForm.get('updateAccountBalance').disable(); - this.activityForm.get('updateAccountBalance').setValue(false); - } + this.updateAccountBalanceState(); } } }); @@ -303,13 +299,6 @@ export class GfCreateOrUpdateActivityDialogComponent implements OnDestroy { }); this.activityForm.get('date').valueChanges.subscribe(() => { - if (isToday(this.activityForm.get('date').value)) { - this.activityForm.get('updateAccountBalance').enable(); - } else { - this.activityForm.get('updateAccountBalance').disable(); - this.activityForm.get('updateAccountBalance').setValue(false); - } - this.changeDetectorRef.markForCheck(); }); @@ -388,8 +377,7 @@ export class GfCreateOrUpdateActivityDialogComponent implements OnDestroy { .get('searchSymbol') .removeValidators(Validators.required); this.activityForm.get('searchSymbol').updateValueAndValidity(); - this.activityForm.get('updateAccountBalance').disable(); - this.activityForm.get('updateAccountBalance').setValue(false); + this.updateAccountBalanceState(); } else if (['FEE', 'INTEREST', 'LIABILITY'].includes(type)) { const currency = this.data.accounts.find(({ id }) => { @@ -426,15 +414,7 @@ export class GfCreateOrUpdateActivityDialogComponent implements OnDestroy { this.activityForm.get('unitPrice').setValue(0); } - if ( - ['FEE', 'INTEREST'].includes(type) && - this.activityForm.get('accountId').value - ) { - this.activityForm.get('updateAccountBalance').enable(); - } else { - this.activityForm.get('updateAccountBalance').disable(); - this.activityForm.get('updateAccountBalance').setValue(false); - } + this.updateAccountBalanceState(); } else { this.activityForm .get('dataSource') @@ -446,7 +426,7 @@ export class GfCreateOrUpdateActivityDialogComponent implements OnDestroy { .get('searchSymbol') .setValidators(Validators.required); this.activityForm.get('searchSymbol').updateValueAndValidity(); - this.activityForm.get('updateAccountBalance').enable(); + this.updateAccountBalanceState(); } this.changeDetectorRef.markForCheck(); @@ -562,6 +542,21 @@ export class GfCreateOrUpdateActivityDialogComponent implements OnDestroy { this.unsubscribeSubject.complete(); } + private updateAccountBalanceState() { + if ( + shouldEnableUpdateAccountBalance({ + accountId: this.activityForm.get('accountId').value, + dataSource: this.activityForm.get('dataSource').value, + type: this.activityForm.get('type').value + }) + ) { + this.activityForm.get('updateAccountBalance').enable(); + } else { + this.activityForm.get('updateAccountBalance').disable(); + this.activityForm.get('updateAccountBalance').setValue(false); + } + } + private updateAssetProfile() { this.isLoading = true; this.changeDetectorRef.markForCheck(); diff --git a/apps/client/src/app/pages/portfolio/activities/create-or-update-activity-dialog/create-or-update-activity-dialog.helper.spec.ts b/apps/client/src/app/pages/portfolio/activities/create-or-update-activity-dialog/create-or-update-activity-dialog.helper.spec.ts new file mode 100644 index 000000000..f82d58b51 --- /dev/null +++ b/apps/client/src/app/pages/portfolio/activities/create-or-update-activity-dialog/create-or-update-activity-dialog.helper.spec.ts @@ -0,0 +1,117 @@ +import { shouldEnableUpdateAccountBalance } from './create-or-update-activity-dialog.helper'; + +describe('shouldEnableUpdateAccountBalance', () => { + describe('BUY type', () => { + it('should enable regardless of date', () => { + expect( + shouldEnableUpdateAccountBalance({ + accountId: 'account-1', + dataSource: 'YAHOO', + type: 'BUY' + }) + ).toBe(true); + }); + + it('should disable when dataSource is MANUAL (transitional from VALUABLE)', () => { + expect( + shouldEnableUpdateAccountBalance({ + accountId: 'account-1', + dataSource: 'MANUAL', + type: 'BUY' + }) + ).toBe(false); + }); + }); + + describe('SELL type', () => { + it('should enable', () => { + expect( + shouldEnableUpdateAccountBalance({ + accountId: 'account-1', + dataSource: 'YAHOO', + type: 'SELL' + }) + ).toBe(true); + }); + }); + + describe('DIVIDEND type', () => { + it('should enable', () => { + expect( + shouldEnableUpdateAccountBalance({ + accountId: 'account-1', + dataSource: 'YAHOO', + type: 'DIVIDEND' + }) + ).toBe(true); + }); + }); + + describe('FEE type', () => { + it('should enable when accountId is set', () => { + expect( + shouldEnableUpdateAccountBalance({ + accountId: 'account-1', + dataSource: 'MANUAL', + type: 'FEE' + }) + ).toBe(true); + }); + + it('should disable when accountId is empty', () => { + expect( + shouldEnableUpdateAccountBalance({ + accountId: null, + dataSource: 'MANUAL', + type: 'FEE' + }) + ).toBe(false); + }); + }); + + describe('INTEREST type', () => { + it('should enable when accountId is set', () => { + expect( + shouldEnableUpdateAccountBalance({ + accountId: 'account-1', + dataSource: 'MANUAL', + type: 'INTEREST' + }) + ).toBe(true); + }); + + it('should disable when accountId is empty', () => { + expect( + shouldEnableUpdateAccountBalance({ + accountId: null, + dataSource: 'MANUAL', + type: 'INTEREST' + }) + ).toBe(false); + }); + }); + + describe('VALUABLE type', () => { + it('should always disable', () => { + expect( + shouldEnableUpdateAccountBalance({ + accountId: 'account-1', + dataSource: 'MANUAL', + type: 'VALUABLE' + }) + ).toBe(false); + }); + }); + + describe('LIABILITY type', () => { + it('should always disable', () => { + expect( + shouldEnableUpdateAccountBalance({ + accountId: 'account-1', + dataSource: 'MANUAL', + type: 'LIABILITY' + }) + ).toBe(false); + }); + }); +}); diff --git a/apps/client/src/app/pages/portfolio/activities/create-or-update-activity-dialog/create-or-update-activity-dialog.helper.ts b/apps/client/src/app/pages/portfolio/activities/create-or-update-activity-dialog/create-or-update-activity-dialog.helper.ts new file mode 100644 index 000000000..27b2fb2c2 --- /dev/null +++ b/apps/client/src/app/pages/portfolio/activities/create-or-update-activity-dialog/create-or-update-activity-dialog.helper.ts @@ -0,0 +1,21 @@ +export function shouldEnableUpdateAccountBalance({ + accountId, + dataSource, + type +}: { + accountId: string | null; + dataSource: string | null; + type: string; +}): boolean { + const isManualBuy = dataSource === 'MANUAL' && type === 'BUY'; + + if (['VALUABLE', 'LIABILITY'].includes(type) || isManualBuy) { + return false; + } + + if (['FEE', 'INTEREST'].includes(type)) { + return !!accountId; + } + + return true; +}