From 48e54f27ce820e6ddd8c32e2511a8e8c000040dc Mon Sep 17 00:00:00 2001 From: Joseph Bao Date: Thu, 26 Jun 2025 12:52:32 +0600 Subject: [PATCH] Feature/add allocation percentage to accounts table and service --- .../src/app/portfolio/portfolio.service.ts | 68 ++++++++++++++++--- .../accounts-table.component.html | 37 ++++++++++ .../accounts-table.component.ts | 1 + .../src/lib/types/account-with-value.type.ts | 1 + 4 files changed, 96 insertions(+), 11 deletions(-) diff --git a/apps/api/src/app/portfolio/portfolio.service.ts b/apps/api/src/app/portfolio/portfolio.service.ts index aed8e0acb..c511eedea 100644 --- a/apps/api/src/app/portfolio/portfolio.service.ts +++ b/apps/api/src/app/portfolio/portfolio.service.ts @@ -73,6 +73,7 @@ import { Order, Platform, Prisma, + SymbolProfile, Tag } from '@prisma/client'; import { Big } from 'big.js'; @@ -159,7 +160,11 @@ export class PortfolioService { this.accountService.accounts({ where, include: { - activities: true, + activities: { + include: { + SymbolProfile: true + } + }, platform: true }, orderBy: { name: 'asc' } @@ -174,38 +179,71 @@ export class PortfolioService { const userCurrency = this.request.user.Settings.settings.baseCurrency; - return accounts.map((account) => { + const accountsWithValue = accounts.map((account) => { let transactionCount = 0; + let valueInBaseCurrency = + details.accounts[account.id]?.valueInBaseCurrency ?? 0; - for (const { isDraft } of account.activities) { - if (!isDraft) { - transactionCount += 1; + if (filterByDataSource && filterBySymbol) { + const holding = details.holdings[filterBySymbol]; + + const activities = ( + account.activities as (Order & { + SymbolProfile: SymbolProfile; + })[] + ).filter((activity) => { + return ( + activity.SymbolProfile.dataSource === filterByDataSource && + activity.SymbolProfile.symbol === filterBySymbol + ); + }); + + let quantity = new Big(0); + for (const activity of activities) { + quantity = quantity.plus( + new Big(getFactor(activity.type)).mul(activity.quantity) + ); } - } - const valueInBaseCurrency = - details.accounts[account.id]?.valueInBaseCurrency ?? 0; + valueInBaseCurrency = quantity.mul(holding.marketPrice ?? 0).toNumber(); + transactionCount = activities.length; + } else { + for (const { isDraft } of account.activities) { + if (!isDraft) { + transactionCount += 1; + } + } + } const result = { ...account, - transactionCount, - valueInBaseCurrency, + allocationInPercentage: 0, balanceInBaseCurrency: this.exchangeRateDataService.toCurrency( account.balance, account.currency, userCurrency ), + transactionCount, value: this.exchangeRateDataService.toCurrency( valueInBaseCurrency, userCurrency, account.currency - ) + ), + valueInBaseCurrency }; delete result.activities; return result; }); + + if (filterByDataSource && filterBySymbol) { + return accountsWithValue.filter((account) => { + return account.transactionCount > 0; + }); + } + + return accountsWithValue; } public async getAccountsWithAggregations({ @@ -236,6 +274,14 @@ export class PortfolioService { transactionCount += account.transactionCount; } + for (const account of accounts) { + account.allocationInPercentage = totalValueInBaseCurrency.gt(0) + ? new Big(account.valueInBaseCurrency) + .div(totalValueInBaseCurrency) + .toNumber() + : 0; + } + return { accounts, transactionCount, diff --git a/apps/client/src/app/components/accounts-table/accounts-table.component.html b/apps/client/src/app/components/accounts-table/accounts-table.component.html index 7e2ac92db..1230a4b3e 100644 --- a/apps/client/src/app/components/accounts-table/accounts-table.component.html +++ b/apps/client/src/app/components/accounts-table/accounts-table.component.html @@ -231,6 +231,43 @@ + + + Allocation + + + + + + + + +