From 603ce4fb8d13bc069d9e3d134cd360c1b6d1ad01 Mon Sep 17 00:00:00 2001 From: Thomas <4159106+dtslvr@users.noreply.github.com> Date: Thu, 10 Mar 2022 21:09:06 +0100 Subject: [PATCH] Add support for emergency fund --- .../app/portfolio/portfolio.service-new.ts | 8 +++++++- .../src/app/portfolio/portfolio.service.ts | 8 +++++++- .../interfaces/user-settings.interface.ts | 2 ++ .../src/app/user/update-user-setting.dto.ts | 6 +++++- apps/api/src/app/user/user.controller.ts | 1 - .../home-summary/home-summary.component.ts | 9 +++++++++ .../components/home-summary/home-summary.html | 1 + .../portfolio-summary.component.html | 19 +++++++++++++++++++ .../portfolio-summary.component.ts | 18 +++++++++++++++++- .../portfolio-summary.module.ts | 4 ++-- .../interfaces/portfolio-summary.interface.ts | 1 + 11 files changed, 70 insertions(+), 7 deletions(-) diff --git a/apps/api/src/app/portfolio/portfolio.service-new.ts b/apps/api/src/app/portfolio/portfolio.service-new.ts index b34a9206f..4c02fd0d4 100644 --- a/apps/api/src/app/portfolio/portfolio.service-new.ts +++ b/apps/api/src/app/portfolio/portfolio.service-new.ts @@ -5,6 +5,7 @@ import { CurrentRateService } from '@ghostfolio/api/app/portfolio/current-rate.s import { PortfolioOrder } from '@ghostfolio/api/app/portfolio/interfaces/portfolio-order.interface'; import { TimelineSpecification } from '@ghostfolio/api/app/portfolio/interfaces/timeline-specification.interface'; import { TransactionPoint } from '@ghostfolio/api/app/portfolio/interfaces/transaction-point.interface'; +import { UserSettings } from '@ghostfolio/api/app/user/interfaces/user-settings.interface'; import { AccountClusterRiskCurrentInvestment } from '@ghostfolio/api/models/rules/account-cluster-risk/current-investment'; import { AccountClusterRiskInitialInvestment } from '@ghostfolio/api/models/rules/account-cluster-risk/initial-investment'; import { AccountClusterRiskSingleAccount } from '@ghostfolio/api/models/rules/account-cluster-risk/single-account'; @@ -895,6 +896,9 @@ export class PortfolioServiceNew { userId }); const dividend = this.getDividend(orders).toNumber(); + const emergencyFund = + (this.request.user?.Settings?.settings as UserSettings).emergencyFund ?? + 0; const fees = this.getFees(orders).toNumber(); const firstOrderDate = orders[0]?.date; const items = this.getItems(orders).toNumber(); @@ -902,6 +906,7 @@ export class PortfolioServiceNew { const totalBuy = this.getTotalByType(orders, userCurrency, 'BUY'); const totalSell = this.getTotalByType(orders, userCurrency, 'SELL'); + const cash = new Big(balanceInBaseCurrency).minus(emergencyFund).toNumber(); const committedFunds = new Big(totalBuy).minus(totalSell); const netWorth = new Big(balanceInBaseCurrency) @@ -927,14 +932,15 @@ export class PortfolioServiceNew { return { ...performanceInformation.performance, annualizedPerformancePercent, + cash, dividend, + emergencyFund, fees, firstOrderDate, items, netWorth, totalBuy, totalSell, - cash: balanceInBaseCurrency, committedFunds: committedFunds.toNumber(), ordersCount: orders.filter((order) => { return order.type === 'BUY' || order.type === 'SELL'; diff --git a/apps/api/src/app/portfolio/portfolio.service.ts b/apps/api/src/app/portfolio/portfolio.service.ts index f3df18c30..7192594ef 100644 --- a/apps/api/src/app/portfolio/portfolio.service.ts +++ b/apps/api/src/app/portfolio/portfolio.service.ts @@ -6,6 +6,7 @@ import { PortfolioOrder } from '@ghostfolio/api/app/portfolio/interfaces/portfol import { TimelineSpecification } from '@ghostfolio/api/app/portfolio/interfaces/timeline-specification.interface'; import { TransactionPoint } from '@ghostfolio/api/app/portfolio/interfaces/transaction-point.interface'; import { PortfolioCalculator } from '@ghostfolio/api/app/portfolio/portfolio-calculator'; +import { UserSettings } from '@ghostfolio/api/app/user/interfaces/user-settings.interface'; import { AccountClusterRiskCurrentInvestment } from '@ghostfolio/api/models/rules/account-cluster-risk/current-investment'; import { AccountClusterRiskInitialInvestment } from '@ghostfolio/api/models/rules/account-cluster-risk/initial-investment'; import { AccountClusterRiskSingleAccount } from '@ghostfolio/api/models/rules/account-cluster-risk/single-account'; @@ -873,6 +874,9 @@ export class PortfolioService { userId }); const dividend = this.getDividend(orders).toNumber(); + const emergencyFund = + (this.request.user?.Settings?.settings as UserSettings).emergencyFund ?? + 0; const fees = this.getFees(orders).toNumber(); const firstOrderDate = orders[0]?.date; const items = this.getItems(orders).toNumber(); @@ -880,6 +884,7 @@ export class PortfolioService { const totalBuy = this.getTotalByType(orders, userCurrency, 'BUY'); const totalSell = this.getTotalByType(orders, userCurrency, 'SELL'); + const cash = new Big(balanceInBaseCurrency).minus(emergencyFund).toNumber(); const committedFunds = new Big(totalBuy).minus(totalSell); const netWorth = new Big(balanceInBaseCurrency) @@ -889,7 +894,9 @@ export class PortfolioService { return { ...performanceInformation.performance, + cash, dividend, + emergencyFund, fees, firstOrderDate, items, @@ -898,7 +905,6 @@ export class PortfolioService { totalSell, annualizedPerformancePercent: performanceInformation.performance.annualizedPerformancePercent, - cash: balanceInBaseCurrency, committedFunds: committedFunds.toNumber(), ordersCount: orders.filter((order) => { return order.type === 'BUY' || order.type === 'SELL'; diff --git a/apps/api/src/app/user/interfaces/user-settings.interface.ts b/apps/api/src/app/user/interfaces/user-settings.interface.ts index 7870abee9..fb4b2af35 100644 --- a/apps/api/src/app/user/interfaces/user-settings.interface.ts +++ b/apps/api/src/app/user/interfaces/user-settings.interface.ts @@ -1,3 +1,5 @@ export interface UserSettings { + emergencyFund?: number; + isNewCalculationEngine?: boolean; isRestrictedView?: boolean; } diff --git a/apps/api/src/app/user/update-user-setting.dto.ts b/apps/api/src/app/user/update-user-setting.dto.ts index 38dc9cafb..5af2f5f8d 100644 --- a/apps/api/src/app/user/update-user-setting.dto.ts +++ b/apps/api/src/app/user/update-user-setting.dto.ts @@ -1,6 +1,10 @@ -import { IsBoolean, IsOptional } from 'class-validator'; +import { IsBoolean, IsNumber, IsOptional } from 'class-validator'; export class UpdateUserSettingDto { + @IsNumber() + @IsOptional() + emergencyFund?: number; + @IsBoolean() @IsOptional() isNewCalculationEngine?: boolean; diff --git a/apps/api/src/app/user/user.controller.ts b/apps/api/src/app/user/user.controller.ts index a615d2f2b..97cf25b6e 100644 --- a/apps/api/src/app/user/user.controller.ts +++ b/apps/api/src/app/user/user.controller.ts @@ -23,7 +23,6 @@ import { import { REQUEST } from '@nestjs/core'; import { JwtService } from '@nestjs/jwt'; import { AuthGuard } from '@nestjs/passport'; -import { Provider, Role } from '@prisma/client'; import { User as UserModel } from '@prisma/client'; import { StatusCodes, getReasonPhrase } from 'http-status-codes'; 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 b2d8d91f4..976aa7549 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 @@ -43,6 +43,15 @@ export class HomeSummaryComponent implements OnDestroy, OnInit { this.update(); } + public onChangeEmergencyFund(emergencyFund: number) { + this.dataService + .putUserSetting({ emergencyFund }) + .pipe(takeUntil(this.unsubscribeSubject)) + .subscribe(() => { + this.update(); + }); + } + public ngOnDestroy() { this.unsubscribeSubject.next(); this.unsubscribeSubject.complete(); 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 fe0b99a2b..516925fb6 100644 --- a/apps/client/src/app/components/home-summary/home-summary.html +++ b/apps/client/src/app/components/home-summary/home-summary.html @@ -11,6 +11,7 @@ [isLoading]="isLoading" [locale]="user?.settings?.locale" [summary]="summary" + (emergencyFundChanged)="onChangeEmergencyFund($event)" > diff --git a/apps/client/src/app/components/portfolio-summary/portfolio-summary.component.html b/apps/client/src/app/components/portfolio-summary/portfolio-summary.component.html index 1fc5e1f85..a1a6eaffa 100644 --- a/apps/client/src/app/components/portfolio-summary/portfolio-summary.component.html +++ b/apps/client/src/app/components/portfolio-summary/portfolio-summary.component.html @@ -130,6 +130,25 @@ > +