Browse Source

Improve filtering (#901)

pull/904/head
Thomas Kaul 3 years ago
committed by GitHub
parent
commit
c1d460cead
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 34
      apps/api/src/app/account/account.service.ts
  2. 2
      apps/api/src/app/portfolio/portfolio.controller.ts
  3. 81
      apps/api/src/app/portfolio/portfolio.service.ts
  4. 29
      apps/client/src/app/pages/portfolio/allocations/allocations-page.component.ts
  5. 4
      libs/ui/src/lib/activities-filter/activities-filter.component.ts

34
apps/api/src/app/account/account.service.ts

@ -1,5 +1,6 @@
import { ExchangeRateDataService } from '@ghostfolio/api/services/exchange-rate-data.service'; import { ExchangeRateDataService } from '@ghostfolio/api/services/exchange-rate-data.service';
import { PrismaService } from '@ghostfolio/api/services/prisma.service'; import { PrismaService } from '@ghostfolio/api/services/prisma.service';
import { Filter } from '@ghostfolio/common/interfaces';
import { Injectable } from '@nestjs/common'; import { Injectable } from '@nestjs/common';
import { Account, Order, Platform, Prisma } from '@prisma/client'; import { Account, Order, Platform, Prisma } from '@prisma/client';
import Big from 'big.js'; import Big from 'big.js';
@ -102,22 +103,39 @@ export class AccountService {
}); });
} }
public async getCashDetails( public async getCashDetails({
aUserId: string, currency,
aCurrency: string filters = [],
): Promise<CashDetails> { userId
}: {
currency: string;
filters?: Filter[];
userId: string;
}): Promise<CashDetails> {
let totalCashBalanceInBaseCurrency = new Big(0); let totalCashBalanceInBaseCurrency = new Big(0);
const accounts = await this.accounts({ const where: Prisma.AccountWhereInput = { userId };
where: { userId: aUserId }
}); 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) { for (const account of accounts) {
totalCashBalanceInBaseCurrency = totalCashBalanceInBaseCurrency.plus( totalCashBalanceInBaseCurrency = totalCashBalanceInBaseCurrency.plus(
this.exchangeRateDataService.toCurrency( this.exchangeRateDataService.toCurrency(
account.balance, account.balance,
account.currency, account.currency,
aCurrency currency
) )
); );
} }

2
apps/api/src/app/portfolio/portfolio.controller.ts

@ -182,8 +182,8 @@ export class PortfolioController {
this.request.user.subscription.type === 'Basic'; this.request.user.subscription.type === 'Basic';
return { return {
accounts,
hasError, hasError,
accounts: filters.length === 0 ? accounts : {},
holdings: isBasicUser ? {} : holdings holdings: isBasicUser ? {} : holdings
}; };
} }

81
apps/api/src/app/portfolio/portfolio.service.ts

@ -68,7 +68,7 @@ import {
subDays, subDays,
subYears subYears
} from 'date-fns'; } from 'date-fns';
import { isEmpty, sortBy, uniqBy } from 'lodash'; import { isEmpty, sortBy, uniq, uniqBy } from 'lodash';
import { import {
HistoricalDataContainer, HistoricalDataContainer,
@ -344,10 +344,11 @@ export class PortfolioService {
startDate startDate
); );
const cashDetails = await this.accountService.getCashDetails( const cashDetails = await this.accountService.getCashDetails({
userId, userId,
userCurrency currency: userCurrency,
); filters: aFilters
});
const holdings: PortfolioDetails['holdings'] = {}; const holdings: PortfolioDetails['holdings'] = {};
const totalInvestment = currentPositions.totalInvestment.plus( 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) { if (aFilters?.length === 0) {
const cashPositions = await this.getCashPositions({
cashDetails,
emergencyFund,
userCurrency,
investment: totalInvestment,
value: totalValue
});
for (const symbol of Object.keys(cashPositions)) { for (const symbol of Object.keys(cashPositions)) {
holdings[symbol] = cashPositions[symbol]; holdings[symbol] = cashPositions[symbol];
} }
} }
const accounts = await this.getValueOfAccounts( const accounts = await this.getValueOfAccounts({
orders, orders,
userId,
portfolioItemsNow, portfolioItemsNow,
userCurrency, filters: aFilters
userId });
);
return { accounts, holdings, hasErrors: currentPositions.hasErrors }; return { accounts, holdings, hasErrors: currentPositions.hasErrors };
} }
@ -890,12 +891,11 @@ export class PortfolioService {
for (const position of currentPositions.positions) { for (const position of currentPositions.positions) {
portfolioItemsNow[position.symbol] = position; portfolioItemsNow[position.symbol] = position;
} }
const accounts = await this.getValueOfAccounts( const accounts = await this.getValueOfAccounts({
orders, orders,
portfolioItemsNow, portfolioItemsNow,
currency,
userId userId
); });
return { return {
rules: { rules: {
accountClusterRisk: await this.rulesService.evaluate( accountClusterRisk: await this.rulesService.evaluate(
@ -957,10 +957,10 @@ export class PortfolioService {
const performanceInformation = await this.getPerformance(aImpersonationId); const performanceInformation = await this.getPerformance(aImpersonationId);
const { balanceInBaseCurrency } = await this.accountService.getCashDetails( const { balanceInBaseCurrency } = await this.accountService.getCashDetails({
userId, userId,
userCurrency currency: userCurrency
); });
const orders = await this.orderService.getOrders({ const orders = await this.orderService.getOrders({
userCurrency, userCurrency,
userId userId
@ -1253,21 +1253,40 @@ export class PortfolioService {
portfolioCalculator.computeTransactionPoints(); portfolioCalculator.computeTransactionPoints();
return { return {
transactionPoints: portfolioCalculator.getTransactionPoints(),
orders, orders,
portfolioOrders portfolioOrders,
transactionPoints: portfolioCalculator.getTransactionPoints()
}; };
} }
private async getValueOfAccounts( private async getValueOfAccounts({
orders: OrderWithAccount[], filters = [],
portfolioItemsNow: { [p: string]: TimelinePosition }, orders,
userCurrency: string, portfolioItemsNow,
userId: string userId
) { }: {
filters?: Filter[];
orders: OrderWithAccount[];
portfolioItemsNow: { [p: string]: TimelinePosition };
userId: string;
}) {
const accounts: PortfolioDetails['accounts'] = {}; 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) { for (const account of currentAccounts) {
const ordersByAccount = orders.filter(({ accountId }) => { const ordersByAccount = orders.filter(({ accountId }) => {

29
apps/client/src/app/pages/portfolio/allocations/allocations-page.component.ts

@ -155,15 +155,17 @@ export class AllocationsPageComponent implements OnDestroy, OnInit {
if (state?.user) { if (state?.user) {
this.user = state.user; this.user = state.user;
const accountFilters: Filter[] = this.user.accounts.map( const accountFilters: Filter[] = this.user.accounts
({ id, name }) => { .filter(({ accountType }) => {
return accountType === 'SECURITIES';
})
.map(({ id, name }) => {
return { return {
id: id, id,
label: name, label: name,
type: 'account' type: 'account'
}; };
} });
);
const tagFilters: Filter[] = this.user.tags.map(({ id, name }) => { const tagFilters: Filter[] = this.user.tags.map(({ id, name }) => {
return { return {
@ -347,17 +349,12 @@ export class AllocationsPageComponent implements OnDestroy, OnInit {
} }
} }
if ( this.symbols[prettifySymbol(symbol)] = {
this.activeFilters?.length === 0 || dataSource: position.dataSource,
position.assetSubClass !== AssetClass.CASH name: position.name,
) { symbol: prettifySymbol(symbol),
this.symbols[prettifySymbol(symbol)] = { value: aPeriod === 'original' ? position.investment : position.value
dataSource: position.dataSource, };
name: position.name,
symbol: prettifySymbol(symbol),
value: aPeriod === 'original' ? position.investment : position.value
};
}
} }
const marketsTotal = const marketsTotal =

4
libs/ui/src/lib/activities-filter/activities-filter.component.ts

@ -48,7 +48,7 @@ export class ActivitiesFilterComponent implements OnChanges, OnDestroy {
public constructor() { public constructor() {
this.searchControl.valueChanges this.searchControl.valueChanges
.pipe(takeUntil(this.unsubscribeSubject)) .pipe(takeUntil(this.unsubscribeSubject))
.subscribe((currentFilter: string) => { .subscribe((currentFilter: Filter) => {
if (currentFilter) { if (currentFilter) {
this.filters$.next( this.filters$.next(
this.allFilters this.allFilters
@ -61,7 +61,7 @@ export class ActivitiesFilterComponent implements OnChanges, OnDestroy {
.filter((filter) => { .filter((filter) => {
return filter.label return filter.label
.toLowerCase() .toLowerCase()
.startsWith(currentFilter?.toLowerCase()); .startsWith(currentFilter.label.toLowerCase());
}) })
.sort((a, b) => a.label.localeCompare(b.label)) .sort((a, b) => a.label.localeCompare(b.label))
); );

Loading…
Cancel
Save