From 96f4fa9e2831d311ae73345d813e8d0ecd56d6b6 Mon Sep 17 00:00:00 2001 From: Thomas <4159106+dtslvr@users.noreply.github.com> Date: Fri, 11 Mar 2022 21:06:56 +0100 Subject: [PATCH] Update cash positions and respect permission to update user settings --- .../src/app/portfolio/portfolio.controller.ts | 1 + .../app/portfolio/portfolio.service-new.ts | 33 ++++++++++++++++-- .../src/app/portfolio/portfolio.service.ts | 34 +++++++++++++++++-- .../home-summary/home-summary.component.ts | 17 ++++++++++ .../components/home-summary/home-summary.html | 1 + .../portfolio-summary.component.html | 9 ++--- .../portfolio-summary.component.ts | 1 + .../positions-table.component.ts | 2 +- .../portfolio-position.interface.ts | 2 +- .../portfolio-proportion-chart.component.ts | 2 +- 10 files changed, 91 insertions(+), 11 deletions(-) diff --git a/apps/api/src/app/portfolio/portfolio.controller.ts b/apps/api/src/app/portfolio/portfolio.controller.ts index fd11334d9..2d7993859 100644 --- a/apps/api/src/app/portfolio/portfolio.controller.ts +++ b/apps/api/src/app/portfolio/portfolio.controller.ts @@ -334,6 +334,7 @@ export class PortfolioController { 'currentNetPerformance', 'currentValue', 'dividend', + 'emergencyFund', 'fees', 'items', 'netWorth', diff --git a/apps/api/src/app/portfolio/portfolio.service-new.ts b/apps/api/src/app/portfolio/portfolio.service-new.ts index 4c02fd0d4..c8c71c207 100644 --- a/apps/api/src/app/portfolio/portfolio.service-new.ts +++ b/apps/api/src/app/portfolio/portfolio.service-new.ts @@ -26,6 +26,7 @@ import { Accounts, PortfolioDetails, PortfolioPerformanceResponse, + PortfolioPosition, PortfolioReport, PortfolioSummary, Position, @@ -297,6 +298,10 @@ export class PortfolioServiceNew { ): Promise { const userId = await this.getUserId(aImpersonationId, aUserId); + const emergencyFund = new Big( + (this.request.user?.Settings?.settings as UserSettings)?.emergencyFund ?? + 0 + ); const userCurrency = this.request.user?.Settings?.currency ?? baseCurrency; const { orders, portfolioOrders, transactionPoints } = @@ -394,6 +399,7 @@ export class PortfolioServiceNew { const cashPositions = await this.getCashPositions({ cashDetails, + emergencyFund, userCurrency, investment: totalInvestment, value: totalValue @@ -897,7 +903,7 @@ export class PortfolioServiceNew { }); const dividend = this.getDividend(orders).toNumber(); const emergencyFund = - (this.request.user?.Settings?.settings as UserSettings).emergencyFund ?? + (this.request.user?.Settings?.settings as UserSettings)?.emergencyFund ?? 0; const fees = this.getFees(orders).toNumber(); const firstOrderDate = orders[0]?.date; @@ -950,16 +956,20 @@ export class PortfolioServiceNew { private async getCashPositions({ cashDetails, + emergencyFund, investment, userCurrency, value }: { cashDetails: CashDetails; + emergencyFund: Big; investment: Big; value: Big; userCurrency: string; }) { - const cashPositions = {}; + const cashPositions: { + [symbol: string]: Partial; + } = {}; for (const account of cashDetails.accounts) { const convertedBalance = this.exchangeRateDataService.toCurrency( @@ -1000,6 +1010,25 @@ export class PortfolioServiceNew { } } + cashPositions['EMERGENCY_FUND'] = { + ...cashPositions[userCurrency], + assetSubClass: 'EMERGENCY_FUND', + investment: emergencyFund.toNumber(), + name: 'EMERGENCY_FUND', + symbol: 'EMERGENCY_FUND', + value: emergencyFund.toNumber() + }; + cashPositions[userCurrency].investment = new Big( + cashPositions[userCurrency].investment + ) + .minus(emergencyFund) + .toNumber(); + cashPositions[userCurrency].value = new Big( + cashPositions[userCurrency].value + ) + .minus(emergencyFund) + .toNumber(); + for (const symbol of Object.keys(cashPositions)) { // Calculate allocations for each currency cashPositions[symbol].allocationCurrent = new Big( diff --git a/apps/api/src/app/portfolio/portfolio.service.ts b/apps/api/src/app/portfolio/portfolio.service.ts index 7192594ef..6d87d0dc5 100644 --- a/apps/api/src/app/portfolio/portfolio.service.ts +++ b/apps/api/src/app/portfolio/portfolio.service.ts @@ -27,6 +27,7 @@ import { Accounts, PortfolioDetails, PortfolioPerformanceResponse, + PortfolioPosition, PortfolioReport, PortfolioSummary, Position, @@ -288,6 +289,10 @@ export class PortfolioService { ): Promise { const userId = await this.getUserId(aImpersonationId, aUserId); + const emergencyFund = new Big( + (this.request.user?.Settings?.settings as UserSettings)?.emergencyFund ?? + 0 + ); const userCurrency = this.request.user?.Settings?.currency ?? baseCurrency; const portfolioCalculator = new PortfolioCalculator( this.currentRateService, @@ -382,6 +387,7 @@ export class PortfolioService { const cashPositions = await this.getCashPositions({ cashDetails, + emergencyFund, userCurrency, investment: totalInvestment, value: totalValue @@ -875,7 +881,7 @@ export class PortfolioService { }); const dividend = this.getDividend(orders).toNumber(); const emergencyFund = - (this.request.user?.Settings?.settings as UserSettings).emergencyFund ?? + (this.request.user?.Settings?.settings as UserSettings)?.emergencyFund ?? 0; const fees = this.getFees(orders).toNumber(); const firstOrderDate = orders[0]?.date; @@ -914,16 +920,20 @@ export class PortfolioService { private async getCashPositions({ cashDetails, + emergencyFund, investment, userCurrency, value }: { cashDetails: CashDetails; + emergencyFund: Big; investment: Big; userCurrency: string; value: Big; }) { - const cashPositions = {}; + const cashPositions: { + [symbol: string]: Partial; + } = {}; for (const account of cashDetails.accounts) { const convertedBalance = this.exchangeRateDataService.toCurrency( @@ -964,6 +974,26 @@ export class PortfolioService { } } + cashPositions['EMERGENCY_FUND'] = { + ...cashPositions[userCurrency], + assetSubClass: 'EMERGENCY_FUND', + investment: emergencyFund.toNumber(), + name: 'EMERGENCY_FUND', + symbol: 'EMERGENCY_FUND', + value: emergencyFund.toNumber() + }; + + cashPositions[userCurrency].investment = new Big( + cashPositions[userCurrency].investment + ) + .minus(emergencyFund) + .toNumber(); + cashPositions[userCurrency].value = new Big( + cashPositions[userCurrency].value + ) + .minus(emergencyFund) + .toNumber(); + for (const symbol of Object.keys(cashPositions)) { // Calculate allocations for each currency cashPositions[symbol].allocationCurrent = new Big( diff --git a/apps/client/src/app/components/home-summary/home-summary.component.ts b/apps/client/src/app/components/home-summary/home-summary.component.ts index 976aa7549..09bcb0454 100644 --- a/apps/client/src/app/components/home-summary/home-summary.component.ts +++ b/apps/client/src/app/components/home-summary/home-summary.component.ts @@ -1,7 +1,9 @@ import { ChangeDetectorRef, Component, OnDestroy, OnInit } from '@angular/core'; import { DataService } from '@ghostfolio/client/services/data.service'; +import { ImpersonationStorageService } from '@ghostfolio/client/services/impersonation-storage.service'; import { UserService } from '@ghostfolio/client/services/user/user.service'; import { PortfolioSummary, User } from '@ghostfolio/common/interfaces'; +import { hasPermission, permissions } from '@ghostfolio/common/permissions'; import { Subject } from 'rxjs'; import { takeUntil } from 'rxjs/operators'; @@ -11,6 +13,8 @@ import { takeUntil } from 'rxjs/operators'; templateUrl: './home-summary.html' }) export class HomeSummaryComponent implements OnDestroy, OnInit { + public hasImpersonationId: boolean; + public hasPermissionToUpdateUserSettings: boolean; public isLoading = true; public summary: PortfolioSummary; public user: User; @@ -23,6 +27,7 @@ export class HomeSummaryComponent implements OnDestroy, OnInit { public constructor( private changeDetectorRef: ChangeDetectorRef, private dataService: DataService, + private impersonationStorageService: ImpersonationStorageService, private userService: UserService ) { this.userService.stateChanged @@ -31,6 +36,11 @@ export class HomeSummaryComponent implements OnDestroy, OnInit { if (state?.user) { this.user = state.user; + this.hasPermissionToUpdateUserSettings = hasPermission( + this.user.permissions, + permissions.updateUserSettings + ); + this.changeDetectorRef.markForCheck(); } }); @@ -40,6 +50,13 @@ export class HomeSummaryComponent implements OnDestroy, OnInit { * Initializes the controller */ public ngOnInit() { + this.impersonationStorageService + .onChangeHasImpersonation() + .pipe(takeUntil(this.unsubscribeSubject)) + .subscribe((aId) => { + this.hasImpersonationId = !!aId; + }); + this.update(); } diff --git a/apps/client/src/app/components/home-summary/home-summary.html b/apps/client/src/app/components/home-summary/home-summary.html index 516925fb6..7905130bd 100644 --- a/apps/client/src/app/components/home-summary/home-summary.html +++ b/apps/client/src/app/components/home-summary/home-summary.html @@ -8,6 +8,7 @@
Emergency Fund
= new MatTableDataSource(); public displayedColumns = []; - public ignoreAssetSubClasses = [AssetClass.CASH.toString()]; + public ignoreAssetSubClasses = [AssetClass.CASH.toString(), 'EMERGENCY_FUND']; public isLoading = true; public pageSize = 7; public routeQueryParams: Subscription; diff --git a/libs/common/src/lib/interfaces/portfolio-position.interface.ts b/libs/common/src/lib/interfaces/portfolio-position.interface.ts index 1145d98ff..87ee917d4 100644 --- a/libs/common/src/lib/interfaces/portfolio-position.interface.ts +++ b/libs/common/src/lib/interfaces/portfolio-position.interface.ts @@ -8,7 +8,7 @@ export interface PortfolioPosition { allocationCurrent: number; allocationInvestment: number; assetClass?: AssetClass; - assetSubClass?: AssetSubClass | 'CASH'; + assetSubClass?: AssetSubClass | 'CASH' | 'EMERGENCY_FUND'; countries: Country[]; currency: string; dataSource: DataSource; diff --git a/libs/ui/src/lib/portfolio-proportion-chart/portfolio-proportion-chart.component.ts b/libs/ui/src/lib/portfolio-proportion-chart/portfolio-proportion-chart.component.ts index 4fe51497d..9fee13d0e 100644 --- a/libs/ui/src/lib/portfolio-proportion-chart/portfolio-proportion-chart.component.ts +++ b/libs/ui/src/lib/portfolio-proportion-chart/portfolio-proportion-chart.component.ts @@ -122,7 +122,7 @@ export class PortfolioProportionChartComponent chartData[this.positions[symbol][this.keys[0]]] = { name: this.positions[symbol].name, subCategory: {}, - value: new Big(this.positions[symbol].value) + value: new Big(this.positions[symbol].value ?? 0) }; if (this.positions[symbol][this.keys[1]]) {