From c30a9f306b4ff44308999639d779a90d0f782f39 Mon Sep 17 00:00:00 2001 From: Varun Jain Date: Tue, 4 Aug 2026 12:49:28 +0530 Subject: [PATCH] Exclude cash balances from account values filtered by a holding The accounts section of the holding detail dialog requests the accounts filtered by the data source and the symbol of the holding. In getValueOfAccountsAndPlatforms() the value of every account was seeded with the cash balance of that account, regardless of the filters, so the allocation percentages were computed from the cash balance plus the holding instead of the holding alone. Skip the cash balance if the request is filtered by a holding. Co-Authored-By: Claude Opus 5 --- CHANGELOG.md | 4 +++ .../app/portfolio/portfolio.service.spec.ts | 31 +++++++++++++++++++ .../src/app/portfolio/portfolio.service.ts | 13 +++++++- 3 files changed, 47 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index fb6b3ef37..ed7d5f14f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Added the platform logo to the account selector in the create or update activity dialog +### Fixed + +- Fixed the allocation percentages in the accounts section of the holding detail dialog by excluding the cash balances + ## 3.42.0 - 2026-08-04 ### Changed diff --git a/apps/api/src/app/portfolio/portfolio.service.spec.ts b/apps/api/src/app/portfolio/portfolio.service.spec.ts index 97635553f..20d7c4c9d 100644 --- a/apps/api/src/app/portfolio/portfolio.service.spec.ts +++ b/apps/api/src/app/portfolio/portfolio.service.spec.ts @@ -509,5 +509,36 @@ describe('PortfolioService', () => { expect(accounts[UNKNOWN_KEY]).toBeUndefined(); expect(platforms[UNKNOWN_KEY]).toBeUndefined(); }); + + it('should exclude the cash balance when filtering by a holding', async () => { + jest + .spyOn(accountService, 'accounts') + .mockResolvedValue([account] as unknown as Account[]); + + const { accounts, platforms } = await getValueOfAccountsAndPlatforms({ + activities: [ + { + account, + accountId: account.id, + assetProfile: { symbol: 'AAPL' }, + quantity: 1, + type: 'BUY' + } + ], + filters: [ + { id: DataSource.YAHOO, type: 'DATA_SOURCE' }, + { id: 'AAPL', type: 'SYMBOL' } + ], + portfolioItemsNow: { + AAPL: { marketPriceInBaseCurrency: 10 } + }, + userCurrency: 'USD', + userId: userDummyData.id + }); + + // 1 * 10 (activity), without the balance of 100 + expect(accounts[account.id].valueInBaseCurrency).toBe(10); + expect(platforms[account.platformId].valueInBaseCurrency).toBe(10); + }); }); }); diff --git a/apps/api/src/app/portfolio/portfolio.service.ts b/apps/api/src/app/portfolio/portfolio.service.ts index 70106bdc1..784749505 100644 --- a/apps/api/src/app/portfolio/portfolio.service.ts +++ b/apps/api/src/app/portfolio/portfolio.service.ts @@ -2142,6 +2142,15 @@ export class PortfolioService { const accounts: PortfolioDetails['accounts'] = {}; const platforms: PortfolioDetails['platforms'] = {}; + const { + DATA_SOURCE: [filterByDataSource] = [], + SYMBOL: [filterBySymbol] = [] + } = groupBy(filters, ({ type }) => { + return type; + }); + + const isFilteredByHolding = !!(filterByDataSource && filterBySymbol); + let currentAccounts: (Account & { Order?: Order[]; platform?: Platform; @@ -2185,7 +2194,9 @@ export class PortfolioService { return account ? accountId === account.id : !accountId; }); - if (account) { + // Skip the cash balance if the request is filtered by a holding, so that + // the values of the accounts and platforms reflect this holding only + if (account && !isFilteredByHolding) { accounts[account.id] = { balance: account.balance, currency: account.currency,