From 4dcf98b1260f7a89d16cf1ae027199af05ad4d0e Mon Sep 17 00:00:00 2001 From: Thomas Kaul <4159106+dtslvr@users.noreply.github.com> Date: Mon, 3 Aug 2026 16:15:04 +0200 Subject: [PATCH] Fix savings rate in impersonation mode --- .../src/app/portfolio/portfolio.controller.ts | 20 ++++++++++--------- .../src/app/portfolio/portfolio.service.ts | 5 +++-- apps/client/src/app/app.component.ts | 4 +++- .../holding-detail-dialog.html | 4 ++-- .../interfaces/interfaces.ts | 2 +- .../analysis/analysis-page.component.ts | 17 ++++++---------- .../portfolio-investments.interface.ts | 1 + libs/common/src/lib/permissions.ts | 4 ++-- 8 files changed, 29 insertions(+), 28 deletions(-) diff --git a/apps/api/src/app/portfolio/portfolio.controller.ts b/apps/api/src/app/portfolio/portfolio.controller.ts index 644764d73..953976a4a 100644 --- a/apps/api/src/app/portfolio/portfolio.controller.ts +++ b/apps/api/src/app/portfolio/portfolio.controller.ts @@ -491,14 +491,14 @@ export class PortfolioController { filterByTags: tags }); - let { investments, streaks } = await this.portfolioService.getInvestments({ - filters, - groupBy, - impersonationId, - dateRange: range, - savingsRate: this.request.user?.settings?.settings.savingsRate, - userId: this.request.user.id - }); + let { investments, savingsRate, streaks } = + await this.portfolioService.getInvestments({ + filters, + groupBy, + impersonationId, + dateRange: range, + userId: this.request.user.id + }); if ( hasReadRestrictedAccessPermission({ @@ -521,6 +521,8 @@ export class PortfolioController { 'currentStreak', 'longestStreak' ]); + + savingsRate = null; } if ( @@ -537,7 +539,7 @@ export class PortfolioController { ]); } - return { investments, streaks }; + return { investments, savingsRate, streaks }; } @Get('performance') diff --git a/apps/api/src/app/portfolio/portfolio.service.ts b/apps/api/src/app/portfolio/portfolio.service.ts index ef79fd717..48ea66dac 100644 --- a/apps/api/src/app/portfolio/portfolio.service.ts +++ b/apps/api/src/app/portfolio/portfolio.service.ts @@ -413,19 +413,18 @@ export class PortfolioService { filters, groupBy, impersonationId, - savingsRate, userId }: { dateRange: DateRange; filters?: Filter[]; groupBy?: GroupBy; impersonationId: string; - savingsRate: number; userId: string; }): Promise { userId = await this.getUserId(impersonationId, userId); const user = await this.userService.user({ id: userId }); const userCurrency = this.getUserCurrency(user); + const savingsRate = (user.settings?.settings as UserSettings)?.savingsRate; const { endDate, startDate } = getIntervalFromDateRange({ dateRange }); @@ -438,6 +437,7 @@ export class PortfolioService { if (activities.length === 0) { return { + savingsRate, investments: [], streaks: { currentStreak: 0, longestStreak: 0 } }; @@ -484,6 +484,7 @@ export class PortfolioService { return { investments, + savingsRate, streaks }; } diff --git a/apps/client/src/app/app.component.ts b/apps/client/src/app/app.component.ts index fd17bcd6e..835423e87 100644 --- a/apps/client/src/app/app.component.ts +++ b/apps/client/src/app/app.component.ts @@ -63,6 +63,7 @@ export class GfAppComponent implements OnInit { public hasPermissionToChangeFilters: boolean; public hasPromotion = false; public hasTabs = false; + public impersonationId: string | null; public info: InfoItem; public pageTitle: string; public routerLinkRegister = publicRoutes.register.routerLink; @@ -117,6 +118,7 @@ export class GfAppComponent implements OnInit { .pipe(takeUntilDestroyed(this.destroyRef)) .subscribe((impersonationId) => { this.hasImpersonationId = !!impersonationId; + this.impersonationId = impersonationId; }); this.router.events @@ -291,7 +293,6 @@ export class GfAppComponent implements OnInit { baseCurrency: this.user?.settings?.baseCurrency, colorScheme: this.user?.settings?.colorScheme, deviceType: this.deviceType, - hasImpersonationId: this.hasImpersonationId, hasPermissionToAccessAdminControl: hasPermission( this.user?.permissions, permissions.accessAdminControl @@ -314,6 +315,7 @@ export class GfAppComponent implements OnInit { permissions.updateActivity ) && !this.user?.settings?.isRestrictedView, + impersonationId: this.impersonationId, locale: this.user?.settings?.locale }, height: this.deviceType === 'mobile' ? '98vh' : '80vh', 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 eccc62b4c..8292ff598 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 @@ -382,7 +382,7 @@ [hasPermissionToCreateActivity]="false" [hasPermissionToDeleteActivity]="false" [hasPermissionToExportActivities]=" - !data.hasImpersonationId && !user?.settings?.isRestrictedView + !data.impersonationId && !user?.settings?.isRestrictedView " [hasPermissionToFilter]="false" [hasPermissionToOpenDetails]="false" @@ -390,8 +390,8 @@ [pageIndex]="pageIndex" [pageSize]="pageSize" [showActions]=" - !data.hasImpersonationId && data.hasPermissionToCreateActivity && + !data.impersonationId && user?.settings?.isExperimentalFeatures && !user?.settings?.isRestrictedView " diff --git a/apps/client/src/app/components/holding-detail-dialog/interfaces/interfaces.ts b/apps/client/src/app/components/holding-detail-dialog/interfaces/interfaces.ts index 40c94ca60..efc10d6ac 100644 --- a/apps/client/src/app/components/holding-detail-dialog/interfaces/interfaces.ts +++ b/apps/client/src/app/components/holding-detail-dialog/interfaces/interfaces.ts @@ -7,11 +7,11 @@ export interface HoldingDetailDialogParams { colorScheme: ColorScheme; dataSource: DataSource; deviceType: string; - hasImpersonationId: boolean; hasPermissionToAccessAdminControl: boolean; hasPermissionToCreateActivity: boolean; hasPermissionToReportDataGlitch: boolean; hasPermissionToUpdateActivity: boolean; + impersonationId: string | null; locale: string; symbol: string; } diff --git a/apps/client/src/app/pages/portfolio/analysis/analysis-page.component.ts b/apps/client/src/app/pages/portfolio/analysis/analysis-page.component.ts index 463493f40..7f586e8fd 100644 --- a/apps/client/src/app/pages/portfolio/analysis/analysis-page.component.ts +++ b/apps/client/src/app/pages/portfolio/analysis/analysis-page.component.ts @@ -83,7 +83,6 @@ export class GfAnalysisPageComponent implements OnInit { protected bottom3: PortfolioPosition[]; protected dividendsByGroup: InvestmentItem[]; protected readonly dividendTimelineDataLabel = $localize`Dividend`; - protected hasImpersonationId: boolean; protected hasPermissionToReadAiPrompt: boolean; protected impersonationId: string | null; protected investments: InvestmentItem[]; @@ -105,6 +104,7 @@ export class GfAnalysisPageComponent implements OnInit { protected performanceDataItemsInPercentage: HistoricalDataItem[]; protected readonly portfolioEvolutionDataLabel = $localize`Investment`; protected precision = 2; + protected savingsRatePerMonth: number; protected streaks: PortfolioInvestmentsResponse['streaks']; protected top3: PortfolioPosition[]; protected unitCurrentStreak: string; @@ -136,18 +136,13 @@ export class GfAnalysisPageComponent implements OnInit { } get savingsRate() { - const savingsRatePerMonth = - this.hasImpersonationId || this.user.settings.isRestrictedView - ? undefined - : this.user?.settings?.savingsRate; - - if (savingsRatePerMonth === undefined) { + if (!this.savingsRatePerMonth) { return undefined; } return this.mode() === 'year' - ? savingsRatePerMonth * 12 - : savingsRatePerMonth; + ? this.savingsRatePerMonth * 12 + : this.savingsRatePerMonth; } public ngOnInit() { @@ -155,7 +150,6 @@ export class GfAnalysisPageComponent implements OnInit { .onChangeHasImpersonation() .pipe(takeUntilDestroyed(this.destroyRef)) .subscribe((impersonationId) => { - this.hasImpersonationId = !!impersonationId; this.impersonationId = impersonationId; this.changeDetectorRef.markForCheck(); @@ -282,8 +276,9 @@ export class GfAnalysisPageComponent implements OnInit { range: this.user?.settings?.dateRange ?? DEFAULT_DATE_RANGE }) .pipe(takeUntilDestroyed(this.destroyRef)) - .subscribe(({ investments, streaks }) => { + .subscribe(({ investments, savingsRate, streaks }) => { this.investmentsByGroup = investments; + this.savingsRatePerMonth = savingsRate; this.streaks = streaks; this.unitCurrentStreak = this.mode() === 'year' diff --git a/libs/common/src/lib/interfaces/responses/portfolio-investments.interface.ts b/libs/common/src/lib/interfaces/responses/portfolio-investments.interface.ts index 6d0d60002..e5ded3d1a 100644 --- a/libs/common/src/lib/interfaces/responses/portfolio-investments.interface.ts +++ b/libs/common/src/lib/interfaces/responses/portfolio-investments.interface.ts @@ -2,5 +2,6 @@ import { InvestmentItem } from '../investment-item.interface'; export interface PortfolioInvestmentsResponse { investments: InvestmentItem[]; + savingsRate: number; streaks: { currentStreak: number; longestStreak: number }; } diff --git a/libs/common/src/lib/permissions.ts b/libs/common/src/lib/permissions.ts index 5ebc20661..96533a9e7 100644 --- a/libs/common/src/lib/permissions.ts +++ b/libs/common/src/lib/permissions.ts @@ -201,14 +201,14 @@ export function hasReadRestrictedAccessPermission({ accesses = [], impersonationId }: { - accesses: Pick[]; + accesses?: Pick[]; impersonationId: string | null; }) { if (!impersonationId) { return false; } - const access = accesses?.find(({ id }) => { + const access = accesses.find(({ id }) => { return id === impersonationId; });