Browse Source

Update cash positions and respect permission to update user settings

pull/749/head
Thomas 3 years ago
parent
commit
96f4fa9e28
  1. 1
      apps/api/src/app/portfolio/portfolio.controller.ts
  2. 33
      apps/api/src/app/portfolio/portfolio.service-new.ts
  3. 34
      apps/api/src/app/portfolio/portfolio.service.ts
  4. 17
      apps/client/src/app/components/home-summary/home-summary.component.ts
  5. 1
      apps/client/src/app/components/home-summary/home-summary.html
  6. 9
      apps/client/src/app/components/portfolio-summary/portfolio-summary.component.html
  7. 1
      apps/client/src/app/components/portfolio-summary/portfolio-summary.component.ts
  8. 2
      apps/client/src/app/components/positions-table/positions-table.component.ts
  9. 2
      libs/common/src/lib/interfaces/portfolio-position.interface.ts
  10. 2
      libs/ui/src/lib/portfolio-proportion-chart/portfolio-proportion-chart.component.ts

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

@ -334,6 +334,7 @@ export class PortfolioController {
'currentNetPerformance', 'currentNetPerformance',
'currentValue', 'currentValue',
'dividend', 'dividend',
'emergencyFund',
'fees', 'fees',
'items', 'items',
'netWorth', 'netWorth',

33
apps/api/src/app/portfolio/portfolio.service-new.ts

@ -26,6 +26,7 @@ import {
Accounts, Accounts,
PortfolioDetails, PortfolioDetails,
PortfolioPerformanceResponse, PortfolioPerformanceResponse,
PortfolioPosition,
PortfolioReport, PortfolioReport,
PortfolioSummary, PortfolioSummary,
Position, Position,
@ -297,6 +298,10 @@ export class PortfolioServiceNew {
): Promise<PortfolioDetails & { hasErrors: boolean }> { ): Promise<PortfolioDetails & { hasErrors: boolean }> {
const userId = await this.getUserId(aImpersonationId, aUserId); 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 userCurrency = this.request.user?.Settings?.currency ?? baseCurrency;
const { orders, portfolioOrders, transactionPoints } = const { orders, portfolioOrders, transactionPoints } =
@ -394,6 +399,7 @@ export class PortfolioServiceNew {
const cashPositions = await this.getCashPositions({ const cashPositions = await this.getCashPositions({
cashDetails, cashDetails,
emergencyFund,
userCurrency, userCurrency,
investment: totalInvestment, investment: totalInvestment,
value: totalValue value: totalValue
@ -897,7 +903,7 @@ export class PortfolioServiceNew {
}); });
const dividend = this.getDividend(orders).toNumber(); const dividend = this.getDividend(orders).toNumber();
const emergencyFund = const emergencyFund =
(this.request.user?.Settings?.settings as UserSettings).emergencyFund ?? (this.request.user?.Settings?.settings as UserSettings)?.emergencyFund ??
0; 0;
const fees = this.getFees(orders).toNumber(); const fees = this.getFees(orders).toNumber();
const firstOrderDate = orders[0]?.date; const firstOrderDate = orders[0]?.date;
@ -950,16 +956,20 @@ export class PortfolioServiceNew {
private async getCashPositions({ private async getCashPositions({
cashDetails, cashDetails,
emergencyFund,
investment, investment,
userCurrency, userCurrency,
value value
}: { }: {
cashDetails: CashDetails; cashDetails: CashDetails;
emergencyFund: Big;
investment: Big; investment: Big;
value: Big; value: Big;
userCurrency: string; userCurrency: string;
}) { }) {
const cashPositions = {}; const cashPositions: {
[symbol: string]: Partial<PortfolioPosition>;
} = {};
for (const account of cashDetails.accounts) { for (const account of cashDetails.accounts) {
const convertedBalance = this.exchangeRateDataService.toCurrency( 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)) { for (const symbol of Object.keys(cashPositions)) {
// Calculate allocations for each currency // Calculate allocations for each currency
cashPositions[symbol].allocationCurrent = new Big( cashPositions[symbol].allocationCurrent = new Big(

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

@ -27,6 +27,7 @@ import {
Accounts, Accounts,
PortfolioDetails, PortfolioDetails,
PortfolioPerformanceResponse, PortfolioPerformanceResponse,
PortfolioPosition,
PortfolioReport, PortfolioReport,
PortfolioSummary, PortfolioSummary,
Position, Position,
@ -288,6 +289,10 @@ export class PortfolioService {
): Promise<PortfolioDetails & { hasErrors: boolean }> { ): Promise<PortfolioDetails & { hasErrors: boolean }> {
const userId = await this.getUserId(aImpersonationId, aUserId); 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 userCurrency = this.request.user?.Settings?.currency ?? baseCurrency;
const portfolioCalculator = new PortfolioCalculator( const portfolioCalculator = new PortfolioCalculator(
this.currentRateService, this.currentRateService,
@ -382,6 +387,7 @@ export class PortfolioService {
const cashPositions = await this.getCashPositions({ const cashPositions = await this.getCashPositions({
cashDetails, cashDetails,
emergencyFund,
userCurrency, userCurrency,
investment: totalInvestment, investment: totalInvestment,
value: totalValue value: totalValue
@ -875,7 +881,7 @@ export class PortfolioService {
}); });
const dividend = this.getDividend(orders).toNumber(); const dividend = this.getDividend(orders).toNumber();
const emergencyFund = const emergencyFund =
(this.request.user?.Settings?.settings as UserSettings).emergencyFund ?? (this.request.user?.Settings?.settings as UserSettings)?.emergencyFund ??
0; 0;
const fees = this.getFees(orders).toNumber(); const fees = this.getFees(orders).toNumber();
const firstOrderDate = orders[0]?.date; const firstOrderDate = orders[0]?.date;
@ -914,16 +920,20 @@ export class PortfolioService {
private async getCashPositions({ private async getCashPositions({
cashDetails, cashDetails,
emergencyFund,
investment, investment,
userCurrency, userCurrency,
value value
}: { }: {
cashDetails: CashDetails; cashDetails: CashDetails;
emergencyFund: Big;
investment: Big; investment: Big;
userCurrency: string; userCurrency: string;
value: Big; value: Big;
}) { }) {
const cashPositions = {}; const cashPositions: {
[symbol: string]: Partial<PortfolioPosition>;
} = {};
for (const account of cashDetails.accounts) { for (const account of cashDetails.accounts) {
const convertedBalance = this.exchangeRateDataService.toCurrency( 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)) { for (const symbol of Object.keys(cashPositions)) {
// Calculate allocations for each currency // Calculate allocations for each currency
cashPositions[symbol].allocationCurrent = new Big( cashPositions[symbol].allocationCurrent = new Big(

17
apps/client/src/app/components/home-summary/home-summary.component.ts

@ -1,7 +1,9 @@
import { ChangeDetectorRef, Component, OnDestroy, OnInit } from '@angular/core'; import { ChangeDetectorRef, Component, OnDestroy, OnInit } from '@angular/core';
import { DataService } from '@ghostfolio/client/services/data.service'; 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 { UserService } from '@ghostfolio/client/services/user/user.service';
import { PortfolioSummary, User } from '@ghostfolio/common/interfaces'; import { PortfolioSummary, User } from '@ghostfolio/common/interfaces';
import { hasPermission, permissions } from '@ghostfolio/common/permissions';
import { Subject } from 'rxjs'; import { Subject } from 'rxjs';
import { takeUntil } from 'rxjs/operators'; import { takeUntil } from 'rxjs/operators';
@ -11,6 +13,8 @@ import { takeUntil } from 'rxjs/operators';
templateUrl: './home-summary.html' templateUrl: './home-summary.html'
}) })
export class HomeSummaryComponent implements OnDestroy, OnInit { export class HomeSummaryComponent implements OnDestroy, OnInit {
public hasImpersonationId: boolean;
public hasPermissionToUpdateUserSettings: boolean;
public isLoading = true; public isLoading = true;
public summary: PortfolioSummary; public summary: PortfolioSummary;
public user: User; public user: User;
@ -23,6 +27,7 @@ export class HomeSummaryComponent implements OnDestroy, OnInit {
public constructor( public constructor(
private changeDetectorRef: ChangeDetectorRef, private changeDetectorRef: ChangeDetectorRef,
private dataService: DataService, private dataService: DataService,
private impersonationStorageService: ImpersonationStorageService,
private userService: UserService private userService: UserService
) { ) {
this.userService.stateChanged this.userService.stateChanged
@ -31,6 +36,11 @@ export class HomeSummaryComponent implements OnDestroy, OnInit {
if (state?.user) { if (state?.user) {
this.user = state.user; this.user = state.user;
this.hasPermissionToUpdateUserSettings = hasPermission(
this.user.permissions,
permissions.updateUserSettings
);
this.changeDetectorRef.markForCheck(); this.changeDetectorRef.markForCheck();
} }
}); });
@ -40,6 +50,13 @@ export class HomeSummaryComponent implements OnDestroy, OnInit {
* Initializes the controller * Initializes the controller
*/ */
public ngOnInit() { public ngOnInit() {
this.impersonationStorageService
.onChangeHasImpersonation()
.pipe(takeUntil(this.unsubscribeSubject))
.subscribe((aId) => {
this.hasImpersonationId = !!aId;
});
this.update(); this.update();
} }

1
apps/client/src/app/components/home-summary/home-summary.html

@ -8,6 +8,7 @@
<mat-card-content> <mat-card-content>
<gf-portfolio-summary <gf-portfolio-summary
[baseCurrency]="user?.settings?.baseCurrency" [baseCurrency]="user?.settings?.baseCurrency"
[hasPermissionToUpdateUserSettings]="!hasImpersonationId && hasPermissionToUpdateUserSettings"
[isLoading]="isLoading" [isLoading]="isLoading"
[locale]="user?.settings?.locale" [locale]="user?.settings?.locale"
[summary]="summary" [summary]="summary"

9
apps/client/src/app/components/portfolio-summary/portfolio-summary.component.html

@ -133,13 +133,14 @@
<div class="row px-3 py-1"> <div class="row px-3 py-1">
<div class="d-flex flex-grow-1" i18n>Emergency Fund</div> <div class="d-flex flex-grow-1" i18n>Emergency Fund</div>
<div <div
class="align-items-center cursor-pointer d-flex justify-content-end" class="align-items-center d-flex justify-content-end"
(click)="onEditEmergencyFund()" [ngClass]="{ 'cursor-pointer': hasPermissionToUpdateUserSettings }"
(click)="hasPermissionToUpdateUserSettings && onEditEmergencyFund()"
> >
<ion-icon <ion-icon
*ngIf="!isLoading" *ngIf="hasPermissionToUpdateUserSettings && !isLoading"
class="mr-1 text-muted" class="mr-1 text-muted"
name="create-outline" name="pencil-outline"
></ion-icon> ></ion-icon>
<gf-value <gf-value
class="justify-content-end" class="justify-content-end"

1
apps/client/src/app/components/portfolio-summary/portfolio-summary.component.ts

@ -18,6 +18,7 @@ import { formatDistanceToNow } from 'date-fns';
}) })
export class PortfolioSummaryComponent implements OnChanges, OnInit { export class PortfolioSummaryComponent implements OnChanges, OnInit {
@Input() baseCurrency: string; @Input() baseCurrency: string;
@Input() hasPermissionToUpdateUserSettings: boolean;
@Input() isLoading: boolean; @Input() isLoading: boolean;
@Input() locale: string; @Input() locale: string;
@Input() summary: PortfolioSummary; @Input() summary: PortfolioSummary;

2
apps/client/src/app/components/positions-table/positions-table.component.ts

@ -39,7 +39,7 @@ export class PositionsTableComponent implements OnChanges, OnDestroy, OnInit {
public dataSource: MatTableDataSource<PortfolioPosition> = public dataSource: MatTableDataSource<PortfolioPosition> =
new MatTableDataSource(); new MatTableDataSource();
public displayedColumns = []; public displayedColumns = [];
public ignoreAssetSubClasses = [AssetClass.CASH.toString()]; public ignoreAssetSubClasses = [AssetClass.CASH.toString(), 'EMERGENCY_FUND'];
public isLoading = true; public isLoading = true;
public pageSize = 7; public pageSize = 7;
public routeQueryParams: Subscription; public routeQueryParams: Subscription;

2
libs/common/src/lib/interfaces/portfolio-position.interface.ts

@ -8,7 +8,7 @@ export interface PortfolioPosition {
allocationCurrent: number; allocationCurrent: number;
allocationInvestment: number; allocationInvestment: number;
assetClass?: AssetClass; assetClass?: AssetClass;
assetSubClass?: AssetSubClass | 'CASH'; assetSubClass?: AssetSubClass | 'CASH' | 'EMERGENCY_FUND';
countries: Country[]; countries: Country[];
currency: string; currency: string;
dataSource: DataSource; dataSource: DataSource;

2
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]]] = { chartData[this.positions[symbol][this.keys[0]]] = {
name: this.positions[symbol].name, name: this.positions[symbol].name,
subCategory: {}, subCategory: {},
value: new Big(this.positions[symbol].value) value: new Big(this.positions[symbol].value ?? 0)
}; };
if (this.positions[symbol][this.keys[1]]) { if (this.positions[symbol][this.keys[1]]) {

Loading…
Cancel
Save