diff --git a/apps/api/src/app/user/user.service.ts b/apps/api/src/app/user/user.service.ts index 1dae9d45b..bf9f016fb 100644 --- a/apps/api/src/app/user/user.service.ts +++ b/apps/api/src/app/user/user.service.ts @@ -333,7 +333,10 @@ export class UserService { currentPermissions, permissions.accessHoldingsChart, permissions.createAccess, - permissions.readAiPrompt + permissions.createMarketDataOfOwnAssetProfile, + permissions.readAiPrompt, + permissions.readMarketDataOfOwnAssetProfile, + permissions.updateMarketDataOfOwnAssetProfile ); // Reset benchmark diff --git a/apps/client/src/app/components/holding-detail-dialog/holding-detail-dialog.component.ts b/apps/client/src/app/components/holding-detail-dialog/holding-detail-dialog.component.ts index c992a7659..4b6b06057 100644 --- a/apps/client/src/app/components/holding-detail-dialog/holding-detail-dialog.component.ts +++ b/apps/client/src/app/components/holding-detail-dialog/holding-detail-dialog.component.ts @@ -3,6 +3,7 @@ import { GfAccountsTableModule } from '@ghostfolio/client/components/accounts-ta import { GfDialogFooterModule } from '@ghostfolio/client/components/dialog-footer/dialog-footer.module'; import { GfDialogHeaderModule } from '@ghostfolio/client/components/dialog-header/dialog-header.module'; 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 { NUMERICAL_PRECISION_THRESHOLD } from '@ghostfolio/common/config'; import { DATE_FORMAT, downloadAsFile } from '@ghostfolio/common/helper'; @@ -13,6 +14,7 @@ import { LineChartItem, User } from '@ghostfolio/common/interfaces'; +import { hasPermission, permissions } from '@ghostfolio/common/permissions'; import { GfActivitiesTableComponent } from '@ghostfolio/ui/activities-table'; import { GfDataProviderCreditsComponent } from '@ghostfolio/ui/data-provider-credits'; import { GfHistoricalMarketDataEditorComponent } from '@ghostfolio/ui/historical-market-data-editor'; @@ -97,6 +99,7 @@ export class GfHoldingDetailDialogComponent implements OnDestroy, OnInit { public dividendYieldPercentWithCurrencyEffect: number; public feeInBaseCurrency: number; public firstBuyDate: string; + public hasPermissionToReadMarketDataOfOwnAssetProfile: boolean; public historicalDataItems: LineChartItem[]; public investment: number; public investmentPrecision = 2; @@ -126,6 +129,7 @@ export class GfHoldingDetailDialogComponent implements OnDestroy, OnInit { public user: User; public value: number; + private hasImpersonationId: boolean; private unsubscribeSubject = new Subject(); public constructor( @@ -134,6 +138,7 @@ export class GfHoldingDetailDialogComponent implements OnDestroy, OnInit { public dialogRef: MatDialogRef, @Inject(MAT_DIALOG_DATA) public data: HoldingDetailDialogParams, private formBuilder: FormBuilder, + private impersonationStorageService: ImpersonationStorageService, private router: Router, private userService: UserService ) {} @@ -234,6 +239,15 @@ export class GfHoldingDetailDialogComponent implements OnDestroy, OnInit { this.feeInBaseCurrency = feeInBaseCurrency; this.firstBuyDate = firstBuyDate; + this.hasPermissionToReadMarketDataOfOwnAssetProfile = + !this.hasImpersonationId && + hasPermission( + this.user?.permissions, + permissions.readMarketDataOfOwnAssetProfile + ) && + SymbolProfile?.dataSource === 'MANUAL' && + SymbolProfile?.userId === this.user?.id; + this.historicalDataItems = historicalData.map( ({ averagePrice, date, marketPrice }) => { this.benchmarkDataItems.push({ @@ -395,11 +409,22 @@ export class GfHoldingDetailDialogComponent implements OnDestroy, OnInit { }; } ); - this.fetchMarketData(); + + if (this.hasPermissionToReadMarketDataOfOwnAssetProfile) { + this.fetchMarketData(); + } + this.changeDetectorRef.markForCheck(); } ); + this.impersonationStorageService + .onChangeHasImpersonation() + .pipe(takeUntil(this.unsubscribeSubject)) + .subscribe((impersonationId) => { + this.hasImpersonationId = !!impersonationId; + }); + this.userService.stateChanged .pipe(takeUntil(this.unsubscribeSubject)) .subscribe((state) => { @@ -451,26 +476,7 @@ export class GfHoldingDetailDialogComponent implements OnDestroy, OnInit { }); } - public fetchMarketData() { - this.dataService - .fetchMarketDataBySymbol({ - dataSource: this.data.dataSource, - symbol: this.data.symbol - }) - .pipe(takeUntil(this.unsubscribeSubject)) - .subscribe(({ marketData }) => { - this.marketDataItems = marketData; - this.historicalDataItems = marketData.map(({ date, marketPrice }) => { - return { - date: format(date, DATE_FORMAT), - value: marketPrice - }; - }); - this.changeDetectorRef.markForCheck(); - }); - } - - public onMarketDataChanged(withRefresh: boolean = false) { + public onMarketDataChanged(withRefresh = false) { if (withRefresh) { this.fetchMarketData(); } @@ -492,4 +498,27 @@ export class GfHoldingDetailDialogComponent implements OnDestroy, OnInit { this.unsubscribeSubject.next(); this.unsubscribeSubject.complete(); } + + private fetchMarketData() { + this.dataService + .fetchMarketDataBySymbol({ + dataSource: this.data.dataSource, + symbol: this.data.symbol + }) + .pipe(takeUntil(this.unsubscribeSubject)) + .subscribe(({ marketData }) => { + this.marketDataItems = marketData; + + this.historicalDataItems = this.marketDataItems.map( + ({ date, marketPrice }) => { + return { + date: format(date, DATE_FORMAT), + value: marketPrice + }; + } + ); + + this.changeDetectorRef.markForCheck(); + }); + } } diff --git a/apps/client/src/app/components/holding-detail-dialog/holding-detail-dialog.html b/apps/client/src/app/components/holding-detail-dialog/holding-detail-dialog.html index 10914c4fa..38d00571e 100644 --- a/apps/client/src/app/components/holding-detail-dialog/holding-detail-dialog.html +++ b/apps/client/src/app/components/holding-detail-dialog/holding-detail-dialog.html @@ -364,22 +364,24 @@ [showValueInBaseCurrency]="false" /> - - - -
Market Data
-
- -
+ @if (hasPermissionToReadMarketDataOfOwnAssetProfile) { + + + +
Market Data
+
+ +
+ }