diff --git a/apps/api/src/app/portfolio/portfolio.controller.ts b/apps/api/src/app/portfolio/portfolio.controller.ts index 66cd408cc..b808f0bea 100644 --- a/apps/api/src/app/portfolio/portfolio.controller.ts +++ b/apps/api/src/app/portfolio/portfolio.controller.ts @@ -65,11 +65,19 @@ export class PortfolioController { @UseGuards(AuthGuard('jwt')) public async getChart( @Headers('impersonation-id') impersonationId: string, - @Query('range') range + @Query('accounts') filterByAccounts?: string, + @Query('assetClasses') filterByAssetClasses?: string, + @Query('range') range?: DateRange, + @Query('tags') filterByTags?: string ): Promise { const historicalDataContainer = await this.portfolioService.getChart( impersonationId, - range + range, + this.buildFiltersFromQueries( + filterByAccounts, + filterByAssetClasses, + filterByTags + ) ); let chartData = historicalDataContainer.items; @@ -278,11 +286,19 @@ export class PortfolioController { @UseInterceptors(TransformDataSourceInResponseInterceptor) public async getPerformance( @Headers('impersonation-id') impersonationId: string, - @Query('range') range + @Query('accounts') filterByAccounts?: string, + @Query('assetClasses') filterByAssetClasses?: string, + @Query('range') range?: DateRange, + @Query('tags') filterByTags?: string ): Promise { const performanceInformation = await this.portfolioService.getPerformance( impersonationId, - range + range, + this.buildFiltersFromQueries( + filterByAccounts, + filterByAssetClasses, + filterByTags + ) ); if ( @@ -333,11 +349,19 @@ export class PortfolioController { @UseInterceptors(TransformDataSourceInResponseInterceptor) public async getPositions( @Headers('impersonation-id') impersonationId: string, - @Query('range') range + @Query('accounts') filterByAccounts?: string, + @Query('assetClasses') filterByAssetClasses?: string, + @Query('range') range?: DateRange, + @Query('tags') filterByTags?: string ): Promise { const result = await this.portfolioService.getPositions( impersonationId, - range + range, + this.buildFiltersFromQueries( + filterByAccounts, + filterByAssetClasses, + filterByTags + ) ); if ( @@ -423,7 +447,10 @@ export class PortfolioController { @Get('summary') @UseGuards(AuthGuard('jwt')) public async getSummary( - @Headers('impersonation-id') impersonationId + @Headers('impersonation-id') impersonationId, + @Query('accounts') filterByAccounts?: string, + @Query('assetClasses') filterByAssetClasses?: string, + @Query('tags') filterByTags?: string ): Promise { if ( this.configurationService.get('ENABLE_FEATURE_SUBSCRIPTION') && @@ -435,7 +462,15 @@ export class PortfolioController { ); } - let summary = await this.portfolioService.getSummary(impersonationId); + const filters = this.buildFiltersFromQueries( + filterByAccounts, + filterByAssetClasses, + filterByTags + ); + let summary = await this.portfolioService.getSummary( + impersonationId, + filters + ); if ( impersonationId || @@ -516,4 +551,37 @@ export class PortfolioController { return await this.portfolioService.getReport(impersonationId); } + + private buildFiltersFromQueries( + filterByAccounts?: string, + filterByAssetClasses?: string, + filterByTags?: string + ) { + const accountIds = filterByAccounts?.split(',') ?? []; + const assetClasses = filterByAssetClasses?.split(',') ?? []; + const tagIds = filterByTags?.split(',') ?? []; + + const filters: Filter[] = [ + ...accountIds.map((accountId) => { + return { + id: accountId, + type: 'ACCOUNT' + }; + }), + ...assetClasses.map((assetClass) => { + return { + id: assetClass, + type: 'ASSET_CLASS' + }; + }), + ...tagIds.map((tagId) => { + return { + id: tagId, + type: 'TAG' + }; + }) + ]; + + return filters; + } } diff --git a/apps/api/src/app/portfolio/portfolio.service.ts b/apps/api/src/app/portfolio/portfolio.service.ts index c38e2a8d0..36d21586a 100644 --- a/apps/api/src/app/portfolio/portfolio.service.ts +++ b/apps/api/src/app/portfolio/portfolio.service.ts @@ -268,13 +268,15 @@ export class PortfolioService { public async getChart( aImpersonationId: string, - aDateRange: DateRange = 'max' + aDateRange: DateRange = 'max', + aFilters?: Filter[] ): Promise { const userId = await this.getUserId(aImpersonationId, this.request.user.id); const { portfolioOrders, transactionPoints } = await this.getTransactionPoints({ - userId + userId, + filters: aFilters }); const portfolioCalculator = new PortfolioCalculator({ @@ -845,13 +847,15 @@ export class PortfolioService { public async getPositions( aImpersonationId: string, - aDateRange: DateRange = 'max' + aDateRange: DateRange = 'max', + aFilters?: Filter[] ): Promise<{ hasErrors: boolean; positions: Position[] }> { const userId = await this.getUserId(aImpersonationId, this.request.user.id); const { portfolioOrders, transactionPoints } = await this.getTransactionPoints({ - userId + userId, + filters: aFilters }); const portfolioCalculator = new PortfolioCalculator({ @@ -921,12 +925,14 @@ export class PortfolioService { public async getPerformance( aImpersonationId: string, - aDateRange: DateRange = 'max' + aDateRange: DateRange = 'max', + aFilters?: Filter[] ): Promise { const userId = await this.getUserId(aImpersonationId, this.request.user.id); const { portfolioOrders, transactionPoints } = await this.getTransactionPoints({ + filters: aFilters, userId }); @@ -1181,20 +1187,29 @@ export class PortfolioService { }; } - public async getSummary(aImpersonationId: string): Promise { + public async getSummary( + aImpersonationId: string, + aFilters?: Filter[] + ): Promise { const userCurrency = this.request.user.Settings.settings.baseCurrency; const userId = await this.getUserId(aImpersonationId, this.request.user.id); const user = await this.userService.user({ id: userId }); - const performanceInformation = await this.getPerformance(aImpersonationId); + const performanceInformation = await this.getPerformance( + aImpersonationId, + undefined, + aFilters + ); const { balanceInBaseCurrency } = await this.accountService.getCashDetails({ userId, - currency: userCurrency + currency: userCurrency, + filters: aFilters }); const orders = await this.orderService.getOrders({ userCurrency, - userId + userId, + filters: aFilters }); const dividend = this.getDividend(orders).toNumber(); const emergencyFund = new Big( diff --git a/apps/client/src/app/pages/portfolio/allocations/allocations-page.component.ts b/apps/client/src/app/pages/portfolio/allocations/allocations-page.component.ts index b1f600575..bf4116134 100644 --- a/apps/client/src/app/pages/portfolio/allocations/allocations-page.component.ts +++ b/apps/client/src/app/pages/portfolio/allocations/allocations-page.component.ts @@ -136,9 +136,7 @@ export class AllocationsPageComponent implements OnDestroy, OnInit { ? $localize`Filter by account or tag...` : ''; - return this.dataService.fetchPortfolioDetails({ - filters: this.activeFilters - }); + return this.dataService.fetchPortfolioDetails(this.activeFilters); }), takeUntil(this.unsubscribeSubject) ) diff --git a/apps/client/src/app/pages/portfolio/holdings/holdings-page.component.ts b/apps/client/src/app/pages/portfolio/holdings/holdings-page.component.ts index 4711d79f0..17f042419 100644 --- a/apps/client/src/app/pages/portfolio/holdings/holdings-page.component.ts +++ b/apps/client/src/app/pages/portfolio/holdings/holdings-page.component.ts @@ -87,9 +87,7 @@ export class HoldingsPageComponent implements OnDestroy, OnInit { ? $localize`Filter by account or tag...` : ''; - return this.dataService.fetchPortfolioDetails({ - filters: this.activeFilters - }); + return this.dataService.fetchPortfolioDetails(this.activeFilters); }), takeUntil(this.unsubscribeSubject) ) diff --git a/apps/client/src/app/services/data.service.ts b/apps/client/src/app/services/data.service.ts index d33faeb74..a846e4f1e 100644 --- a/apps/client/src/app/services/data.service.ts +++ b/apps/client/src/app/services/data.service.ts @@ -75,60 +75,21 @@ export class DataService { }: { filters?: Filter[]; }): Observable { - let params = new HttpParams(); - - if (filters?.length > 0) { - const { - ACCOUNT: filtersByAccount, - ASSET_CLASS: filtersByAssetClass, - TAG: filtersByTag - } = groupBy(filters, (filter) => { - return filter.type; - }); - - if (filtersByAccount) { - params = params.append( - 'accounts', - filtersByAccount - .map(({ id }) => { - return id; - }) - .join(',') - ); - } - - if (filtersByAssetClass) { - params = params.append( - 'assetClasses', - filtersByAssetClass - .map(({ id }) => { - return id; - }) - .join(',') - ); - } - - if (filtersByTag) { - params = params.append( - 'tags', - filtersByTag - .map(({ id }) => { - return id; - }) - .join(',') - ); - } - } - - return this.http.get('/api/v1/order', { params }).pipe( - map(({ activities }) => { - for (const activity of activities) { - activity.createdAt = parseISO(activity.createdAt); - activity.date = parseISO(activity.date); + return this.http + .get('/api/v1/order', { + params: { + ...this.buildParamsFilter(filters) } - return { activities }; }) - ); + .pipe( + map(({ activities }) => { + for (const activity of activities) { + activity.createdAt = parseISO(activity.createdAt); + activity.date = parseISO(activity.date); + } + return { activities }; + }) + ); } public fetchAdminData() { @@ -202,9 +163,20 @@ export class DataService { return this.http.get('/api/v1/benchmark'); } - public fetchChart({ range, version }: { range: DateRange; version: number }) { + public fetchChart({ + range, + version, + filters + }: { + range: DateRange; + version: number; + filters?: Filter[]; + }) { return this.http.get(`/api/v${version}/portfolio/chart`, { - params: { range } + params: { + range, + ...this.buildParamsFilter(filters) + } }); } @@ -283,12 +255,14 @@ export class DataService { } public fetchPositions({ - range + range, + filters }: { range: DateRange; + filters?: Filter[]; }): Observable { return this.http.get('/api/v1/portfolio/positions', { - params: { range } + params: { range, ...this.buildParamsFilter(filters) } }); } @@ -302,67 +276,24 @@ export class DataService { ); } - public fetchPortfolioDetails({ filters }: { filters?: Filter[] }) { - let params = new HttpParams(); - - if (filters?.length > 0) { - const { - ACCOUNT: filtersByAccount, - ASSET_CLASS: filtersByAssetClass, - TAG: filtersByTag - } = groupBy(filters, (filter) => { - return filter.type; - }); - - if (filtersByAccount) { - params = params.append( - 'accounts', - filtersByAccount - .map(({ id }) => { - return id; - }) - .join(',') - ); - } - - if (filtersByAssetClass) { - params = params.append( - 'assetClasses', - filtersByAssetClass - .map(({ id }) => { - return id; - }) - .join(',') - ); - } - - if (filtersByTag) { - params = params.append( - 'tags', - filtersByTag - .map(({ id }) => { - return id; - }) - .join(',') - ); - } - } - + public fetchPortfolioDetails(filters?: Filter[]) { return this.http.get('/api/v1/portfolio/details', { - params + params: { ...this.buildParamsFilter(filters) } }); } public fetchPortfolioPerformance({ range, - version + version, + filters }: { range: DateRange; version: number; + filters?: Filter[]; }) { return this.http.get( `/api/v${version}/portfolio/performance`, - { params: { range } } + { params: { range, ...this.buildParamsFilter(filters) } } ); } @@ -376,16 +307,22 @@ export class DataService { return this.http.get('/api/v1/portfolio/report'); } - public fetchPortfolioSummary(): Observable { - return this.http.get('/api/v1/portfolio/summary').pipe( - map((summary) => { - if (summary.firstOrderDate) { - summary.firstOrderDate = parseISO(summary.firstOrderDate); - } - - return summary; + public fetchPortfolioSummary( + filters?: Filter[] + ): Observable { + return this.http + .get('/api/v1/portfolio/summary', { + params: { ...this.buildParamsFilter(filters) } }) - ); + .pipe( + map((summary) => { + if (summary.firstOrderDate) { + summary.firstOrderDate = parseISO(summary.firstOrderDate); + } + + return summary; + }) + ); } public fetchPositionDetail({ @@ -456,4 +393,46 @@ export class DataService { couponCode }); } + + private buildParamsFilter(filters: Filter[]) { + const paramsFilter: { + accounts?: string; + assetClasses?: string; + tags?: string; + } = {}; + if (filters?.length > 0) { + const { + ACCOUNT: filtersByAccount, + ASSET_CLASS: filtersByAssetClass, + TAG: filtersByTag + } = groupBy(filters, (filter) => { + return filter.type; + }); + + if (filtersByAccount) { + paramsFilter.accounts = filtersByAccount + .map(({ id }) => { + return id; + }) + .join(','); + } + + if (filtersByAssetClass) { + paramsFilter.assetClasses = filtersByAssetClass + .map(({ id }) => { + return id; + }) + .join(','); + } + + if (filtersByTag) { + paramsFilter.tags = filtersByTag + .map(({ id }) => { + return id; + }) + .join(','); + } + } + return paramsFilter; + } }