From c1d460cead4b33f0c9e13cbde6c9cd2b1694295a Mon Sep 17 00:00:00 2001
From: Thomas Kaul <4159106+dtslvr@users.noreply.github.com>
Date: Tue, 10 May 2022 21:24:36 +0200
Subject: [PATCH] Improve filtering (#901)

---
 apps/api/src/app/account/account.service.ts   | 34 ++++++--
 .../src/app/portfolio/portfolio.controller.ts |  2 +-
 .../src/app/portfolio/portfolio.service.ts    | 81 ++++++++++++-------
 .../allocations/allocations-page.component.ts | 29 +++----
 .../activities-filter.component.ts            |  4 +-
 5 files changed, 92 insertions(+), 58 deletions(-)

diff --git a/apps/api/src/app/account/account.service.ts b/apps/api/src/app/account/account.service.ts
index ec1678131..34c2f4a15 100644
--- a/apps/api/src/app/account/account.service.ts
+++ b/apps/api/src/app/account/account.service.ts
@@ -1,5 +1,6 @@
 import { ExchangeRateDataService } from '@ghostfolio/api/services/exchange-rate-data.service';
 import { PrismaService } from '@ghostfolio/api/services/prisma.service';
+import { Filter } from '@ghostfolio/common/interfaces';
 import { Injectable } from '@nestjs/common';
 import { Account, Order, Platform, Prisma } from '@prisma/client';
 import Big from 'big.js';
@@ -102,22 +103,39 @@ export class AccountService {
     });
   }
 
-  public async getCashDetails(
-    aUserId: string,
-    aCurrency: string
-  ): Promise<CashDetails> {
+  public async getCashDetails({
+    currency,
+    filters = [],
+    userId
+  }: {
+    currency: string;
+    filters?: Filter[];
+    userId: string;
+  }): Promise<CashDetails> {
     let totalCashBalanceInBaseCurrency = new Big(0);
 
-    const accounts = await this.accounts({
-      where: { userId: aUserId }
-    });
+    const where: Prisma.AccountWhereInput = { userId };
+
+    if (filters?.length > 0) {
+      where.id = {
+        in: filters
+          .filter(({ type }) => {
+            return type === 'account';
+          })
+          .map(({ id }) => {
+            return id;
+          })
+      };
+    }
+
+    const accounts = await this.accounts({ where });
 
     for (const account of accounts) {
       totalCashBalanceInBaseCurrency = totalCashBalanceInBaseCurrency.plus(
         this.exchangeRateDataService.toCurrency(
           account.balance,
           account.currency,
-          aCurrency
+          currency
         )
       );
     }
diff --git a/apps/api/src/app/portfolio/portfolio.controller.ts b/apps/api/src/app/portfolio/portfolio.controller.ts
index 25021492e..68cd03c0a 100644
--- a/apps/api/src/app/portfolio/portfolio.controller.ts
+++ b/apps/api/src/app/portfolio/portfolio.controller.ts
@@ -182,8 +182,8 @@ export class PortfolioController {
       this.request.user.subscription.type === 'Basic';
 
     return {
+      accounts,
       hasError,
-      accounts: filters.length === 0 ? accounts : {},
       holdings: isBasicUser ? {} : holdings
     };
   }
diff --git a/apps/api/src/app/portfolio/portfolio.service.ts b/apps/api/src/app/portfolio/portfolio.service.ts
index 314cedf60..3ecb60d41 100644
--- a/apps/api/src/app/portfolio/portfolio.service.ts
+++ b/apps/api/src/app/portfolio/portfolio.service.ts
@@ -68,7 +68,7 @@ import {
   subDays,
   subYears
 } from 'date-fns';
-import { isEmpty, sortBy, uniqBy } from 'lodash';
+import { isEmpty, sortBy, uniq, uniqBy } from 'lodash';
 
 import {
   HistoricalDataContainer,
@@ -344,10 +344,11 @@ export class PortfolioService {
       startDate
     );
 
-    const cashDetails = await this.accountService.getCashDetails(
+    const cashDetails = await this.accountService.getCashDetails({
       userId,
-      userCurrency
-    );
+      currency: userCurrency,
+      filters: aFilters
+    });
 
     const holdings: PortfolioDetails['holdings'] = {};
     const totalInvestment = currentPositions.totalInvestment.plus(
@@ -440,26 +441,26 @@ export class PortfolioService {
       };
     }
 
-    const cashPositions = await this.getCashPositions({
-      cashDetails,
-      emergencyFund,
-      userCurrency,
-      investment: totalInvestment,
-      value: totalValue
-    });
-
     if (aFilters?.length === 0) {
+      const cashPositions = await this.getCashPositions({
+        cashDetails,
+        emergencyFund,
+        userCurrency,
+        investment: totalInvestment,
+        value: totalValue
+      });
+
       for (const symbol of Object.keys(cashPositions)) {
         holdings[symbol] = cashPositions[symbol];
       }
     }
 
-    const accounts = await this.getValueOfAccounts(
+    const accounts = await this.getValueOfAccounts({
       orders,
+      userId,
       portfolioItemsNow,
-      userCurrency,
-      userId
-    );
+      filters: aFilters
+    });
 
     return { accounts, holdings, hasErrors: currentPositions.hasErrors };
   }
@@ -890,12 +891,11 @@ export class PortfolioService {
     for (const position of currentPositions.positions) {
       portfolioItemsNow[position.symbol] = position;
     }
-    const accounts = await this.getValueOfAccounts(
+    const accounts = await this.getValueOfAccounts({
       orders,
       portfolioItemsNow,
-      currency,
       userId
-    );
+    });
     return {
       rules: {
         accountClusterRisk: await this.rulesService.evaluate(
@@ -957,10 +957,10 @@ export class PortfolioService {
 
     const performanceInformation = await this.getPerformance(aImpersonationId);
 
-    const { balanceInBaseCurrency } = await this.accountService.getCashDetails(
+    const { balanceInBaseCurrency } = await this.accountService.getCashDetails({
       userId,
-      userCurrency
-    );
+      currency: userCurrency
+    });
     const orders = await this.orderService.getOrders({
       userCurrency,
       userId
@@ -1253,21 +1253,40 @@ export class PortfolioService {
     portfolioCalculator.computeTransactionPoints();
 
     return {
-      transactionPoints: portfolioCalculator.getTransactionPoints(),
       orders,
-      portfolioOrders
+      portfolioOrders,
+      transactionPoints: portfolioCalculator.getTransactionPoints()
     };
   }
 
-  private async getValueOfAccounts(
-    orders: OrderWithAccount[],
-    portfolioItemsNow: { [p: string]: TimelinePosition },
-    userCurrency: string,
-    userId: string
-  ) {
+  private async getValueOfAccounts({
+    filters = [],
+    orders,
+    portfolioItemsNow,
+    userId
+  }: {
+    filters?: Filter[];
+    orders: OrderWithAccount[];
+    portfolioItemsNow: { [p: string]: TimelinePosition };
+    userId: string;
+  }) {
     const accounts: PortfolioDetails['accounts'] = {};
 
-    const currentAccounts = await this.accountService.getAccounts(userId);
+    let currentAccounts = [];
+
+    if (filters.length === 0) {
+      currentAccounts = await this.accountService.getAccounts(userId);
+    } else {
+      const accountIds = uniq(
+        orders.map(({ accountId }) => {
+          return accountId;
+        })
+      );
+
+      currentAccounts = await this.accountService.accounts({
+        where: { id: { in: accountIds } }
+      });
+    }
 
     for (const account of currentAccounts) {
       const ordersByAccount = orders.filter(({ accountId }) => {
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 01e9a7cef..b2c63664f 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
@@ -155,15 +155,17 @@ export class AllocationsPageComponent implements OnDestroy, OnInit {
         if (state?.user) {
           this.user = state.user;
 
-          const accountFilters: Filter[] = this.user.accounts.map(
-            ({ id, name }) => {
+          const accountFilters: Filter[] = this.user.accounts
+            .filter(({ accountType }) => {
+              return accountType === 'SECURITIES';
+            })
+            .map(({ id, name }) => {
               return {
-                id: id,
+                id,
                 label: name,
                 type: 'account'
               };
-            }
-          );
+            });
 
           const tagFilters: Filter[] = this.user.tags.map(({ id, name }) => {
             return {
@@ -347,17 +349,12 @@ export class AllocationsPageComponent implements OnDestroy, OnInit {
         }
       }
 
-      if (
-        this.activeFilters?.length === 0 ||
-        position.assetSubClass !== AssetClass.CASH
-      ) {
-        this.symbols[prettifySymbol(symbol)] = {
-          dataSource: position.dataSource,
-          name: position.name,
-          symbol: prettifySymbol(symbol),
-          value: aPeriod === 'original' ? position.investment : position.value
-        };
-      }
+      this.symbols[prettifySymbol(symbol)] = {
+        dataSource: position.dataSource,
+        name: position.name,
+        symbol: prettifySymbol(symbol),
+        value: aPeriod === 'original' ? position.investment : position.value
+      };
     }
 
     const marketsTotal =
diff --git a/libs/ui/src/lib/activities-filter/activities-filter.component.ts b/libs/ui/src/lib/activities-filter/activities-filter.component.ts
index b114317e5..bf8c5878b 100644
--- a/libs/ui/src/lib/activities-filter/activities-filter.component.ts
+++ b/libs/ui/src/lib/activities-filter/activities-filter.component.ts
@@ -48,7 +48,7 @@ export class ActivitiesFilterComponent implements OnChanges, OnDestroy {
   public constructor() {
     this.searchControl.valueChanges
       .pipe(takeUntil(this.unsubscribeSubject))
-      .subscribe((currentFilter: string) => {
+      .subscribe((currentFilter: Filter) => {
         if (currentFilter) {
           this.filters$.next(
             this.allFilters
@@ -61,7 +61,7 @@ export class ActivitiesFilterComponent implements OnChanges, OnDestroy {
               .filter((filter) => {
                 return filter.label
                   .toLowerCase()
-                  .startsWith(currentFilter?.toLowerCase());
+                  .startsWith(currentFilter.label.toLowerCase());
               })
               .sort((a, b) => a.label.localeCompare(b.label))
           );