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',
'currentValue',
'dividend',
'emergencyFund',
'fees',
'items',
'netWorth',

33
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<PortfolioDetails & { hasErrors: boolean }> {
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<PortfolioPosition>;
} = {};
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(

34
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<PortfolioDetails & { hasErrors: boolean }> {
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<PortfolioPosition>;
} = {};
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(

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 { 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();
}

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

@ -8,6 +8,7 @@
<mat-card-content>
<gf-portfolio-summary
[baseCurrency]="user?.settings?.baseCurrency"
[hasPermissionToUpdateUserSettings]="!hasImpersonationId && hasPermissionToUpdateUserSettings"
[isLoading]="isLoading"
[locale]="user?.settings?.locale"
[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="d-flex flex-grow-1" i18n>Emergency Fund</div>
<div
class="align-items-center cursor-pointer d-flex justify-content-end"
(click)="onEditEmergencyFund()"
class="align-items-center d-flex justify-content-end"
[ngClass]="{ 'cursor-pointer': hasPermissionToUpdateUserSettings }"
(click)="hasPermissionToUpdateUserSettings && onEditEmergencyFund()"
>
<ion-icon
*ngIf="!isLoading"
*ngIf="hasPermissionToUpdateUserSettings && !isLoading"
class="mr-1 text-muted"
name="create-outline"
name="pencil-outline"
></ion-icon>
<gf-value
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 {
@Input() baseCurrency: string;
@Input() hasPermissionToUpdateUserSettings: boolean;
@Input() isLoading: boolean;
@Input() locale: string;
@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> =
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;

2
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;

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]]] = {
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]]) {

Loading…
Cancel
Save