diff --git a/CHANGELOG.md b/CHANGELOG.md index baf1b8564..e73dcff8a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added - Added groups to the activities filter component +- Added support for filtering by asset class on the allocations page ## 1.148.0 - 14.05.2022 diff --git a/apps/api/src/app/account/account.service.ts b/apps/api/src/app/account/account.service.ts index 240045e8e..b9b65716a 100644 --- a/apps/api/src/app/account/account.service.ts +++ b/apps/api/src/app/account/account.service.ts @@ -4,6 +4,7 @@ import { Filter } from '@ghostfolio/common/interfaces'; import { Injectable } from '@nestjs/common'; import { Account, Order, Platform, Prisma } from '@prisma/client'; import Big from 'big.js'; +import { groupBy } from 'lodash'; import { CashDetails } from './interfaces/cash-details.interface'; @@ -116,15 +117,19 @@ export class AccountService { const where: Prisma.AccountWhereInput = { userId }; - if (filters?.length > 0) { + const { + ACCOUNT: filtersByAccount, + ASSET_CLASS: filtersByAssetClass, + TAG: filtersByTag + } = groupBy(filters, (filter) => { + return filter.type; + }); + + if (filtersByAccount?.length > 0) { where.id = { - in: filters - .filter(({ type }) => { - return type === 'ACCOUNT'; - }) - .map(({ id }) => { - return id; - }) + in: filtersByAccount.map(({ id }) => { + return id; + }) }; } diff --git a/apps/api/src/app/order/order.service.ts b/apps/api/src/app/order/order.service.ts index 3e87c5f5e..7b09ec559 100644 --- a/apps/api/src/app/order/order.service.ts +++ b/apps/api/src/app/order/order.service.ts @@ -188,12 +188,13 @@ export class OrderService { }): Promise { const where: Prisma.OrderWhereInput = { userId }; - const { ACCOUNT: filtersByAccount, TAG: filtersByTag } = groupBy( - filters, - (filter) => { - return filter.type; - } - ); + const { + ACCOUNT: filtersByAccount, + ASSET_CLASS: filtersByAssetClass, + TAG: filtersByTag + } = groupBy(filters, (filter) => { + return filter.type; + }); if (filtersByAccount?.length > 0) { where.accountId = { @@ -207,6 +208,34 @@ export class OrderService { where.isDraft = false; } + if (filtersByAssetClass?.length > 0) { + where.SymbolProfile = { + OR: [ + { + AND: [ + { + OR: filtersByAssetClass.map(({ id }) => { + return { assetClass: AssetClass[id] }; + }) + }, + { + SymbolProfileOverrides: { + is: null + } + } + ] + }, + { + SymbolProfileOverrides: { + OR: filtersByAssetClass.map(({ id }) => { + return { assetClass: AssetClass[id] }; + }) + } + } + ] + }; + } + if (filtersByTag?.length > 0) { where.tags = { some: { diff --git a/apps/api/src/app/portfolio/portfolio.controller.ts b/apps/api/src/app/portfolio/portfolio.controller.ts index 5c648378d..5d3c683f1 100644 --- a/apps/api/src/app/portfolio/portfolio.controller.ts +++ b/apps/api/src/app/portfolio/portfolio.controller.ts @@ -107,12 +107,14 @@ export class PortfolioController { public async getDetails( @Headers('impersonation-id') impersonationId: string, @Query('accounts') filterByAccounts?: string, + @Query('assetClasses') filterByAssetClasses?: string, @Query('range') range?: DateRange, @Query('tags') filterByTags?: string ): Promise { let hasError = false; const accountIds = filterByAccounts?.split(',') ?? []; + const assetClasses = filterByAssetClasses?.split(',') ?? []; const tagIds = filterByTags?.split(',') ?? []; const filters: Filter[] = [ @@ -122,6 +124,12 @@ export class PortfolioController { type: 'ACCOUNT' }; }), + ...assetClasses.map((assetClass) => { + return { + id: assetClass, + type: 'ASSET_CLASS' + }; + }), ...tagIds.map((tagId) => { return { id: tagId, diff --git a/apps/api/src/app/portfolio/portfolio.service.ts b/apps/api/src/app/portfolio/portfolio.service.ts index 3ecb60d41..409a40017 100644 --- a/apps/api/src/app/portfolio/portfolio.service.ts +++ b/apps/api/src/app/portfolio/portfolio.service.ts @@ -441,7 +441,12 @@ export class PortfolioService { }; } - if (aFilters?.length === 0) { + if ( + aFilters?.length === 0 || + (aFilters?.length === 1 && + aFilters[0].type === 'ASSET_CLASS' && + aFilters[0].id === 'CASH') + ) { const cashPositions = await this.getCashPositions({ cashDetails, emergencyFund, 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 279d2a499..b8cc651f6 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 @@ -172,6 +172,15 @@ export class AllocationsPageComponent implements OnDestroy, OnInit { }; }); + const assetClassFilters: Filter[] = []; + for (const assetClass of Object.keys(AssetClass)) { + assetClassFilters.push({ + id: assetClass, + label: assetClass, + type: 'ASSET_CLASS' + }); + } + const tagFilters: Filter[] = this.user.tags.map(({ id, name }) => { return { id, @@ -180,7 +189,11 @@ export class AllocationsPageComponent implements OnDestroy, OnInit { }; }); - this.allFilters = [...accountFilters, ...tagFilters]; + this.allFilters = [ + ...accountFilters, + ...assetClassFilters, + ...tagFilters + ]; this.changeDetectorRef.markForCheck(); } diff --git a/apps/client/src/app/services/data.service.ts b/apps/client/src/app/services/data.service.ts index 3edde74e4..58a900a72 100644 --- a/apps/client/src/app/services/data.service.ts +++ b/apps/client/src/app/services/data.service.ts @@ -187,12 +187,13 @@ export class DataService { let params = new HttpParams(); if (filters?.length > 0) { - const { ACCOUNT: filtersByAccount, TAG: filtersByTag } = groupBy( - filters, - (filter) => { - return filter.type; - } - ); + const { + ACCOUNT: filtersByAccount, + ASSET_CLASS: filtersByAssetClass, + TAG: filtersByTag + } = groupBy(filters, (filter) => { + return filter.type; + }); if (filtersByAccount) { params = params.append( @@ -205,6 +206,17 @@ export class DataService { ); } + if (filtersByAssetClass) { + params = params.append( + 'assetClasses', + filtersByAssetClass + .map(({ id }) => { + return id; + }) + .join(',') + ); + } + if (filtersByTag) { params = params.append( 'tags', diff --git a/libs/common/src/lib/interfaces/filter.interface.ts b/libs/common/src/lib/interfaces/filter.interface.ts index a4280f4f4..a3e5163e7 100644 --- a/libs/common/src/lib/interfaces/filter.interface.ts +++ b/libs/common/src/lib/interfaces/filter.interface.ts @@ -1,5 +1,5 @@ export interface Filter { id: string; label?: string; - type: 'ACCOUNT' | 'SYMBOL' | 'TAG'; + type: 'ACCOUNT' | 'ASSET_CLASS' | 'SYMBOL' | 'TAG'; }