From 6940700a1bf4ecfd374f305f32311a29731fac73 Mon Sep 17 00:00:00 2001 From: Thomas Kaul <4159106+dtslvr@users.noreply.github.com> Date: Tue, 8 Jul 2025 19:44:30 +0200 Subject: [PATCH 1/5] Feature/extend asset sub classes by collectible (#5126) * Extend asset sub classes by collectible * Update changelog --- CHANGELOG.md | 5 +++++ .../admin-market-data.component.ts | 14 ++++---------- libs/ui/src/lib/i18n.ts | 4 +++- .../migration.sql | 2 ++ .../migration.sql | 2 ++ prisma/schema.prisma | 2 ++ 6 files changed, 18 insertions(+), 11 deletions(-) create mode 100644 prisma/migrations/20250708090630_added_alternative_investment_to_asset_class/migration.sql create mode 100644 prisma/migrations/20250708090631_added_collectible_to_asset_sub_class/migration.sql diff --git a/CHANGELOG.md b/CHANGELOG.md index 98db3b039..ff8e3a8bc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## Unreleased +### Added + +- Added alternative investment as an asset class +- Added collectible as an asset sub class + ### Changed - Improved the language localization for Catalan (`ca`) diff --git a/apps/client/src/app/components/admin-market-data/admin-market-data.component.ts b/apps/client/src/app/components/admin-market-data/admin-market-data.component.ts index e50d2545f..30ab1aa04 100644 --- a/apps/client/src/app/components/admin-market-data/admin-market-data.component.ts +++ b/apps/client/src/app/components/admin-market-data/admin-market-data.component.ts @@ -68,16 +68,10 @@ export class AdminMarketDataComponent @ViewChild(MatSort) sort: MatSort; public activeFilters: Filter[] = []; - public allFilters: Filter[] = [ - AssetSubClass.BOND, - AssetSubClass.COMMODITY, - AssetSubClass.CRYPTOCURRENCY, - AssetSubClass.ETF, - AssetSubClass.MUTUALFUND, - AssetSubClass.PRECIOUS_METAL, - AssetSubClass.PRIVATE_EQUITY, - AssetSubClass.STOCK - ] + public allFilters: Filter[] = Object.keys(AssetSubClass) + .filter((assetSubClass) => { + return assetSubClass !== 'CASH'; + }) .map((assetSubClass) => { return { id: assetSubClass.toString(), diff --git a/libs/ui/src/lib/i18n.ts b/libs/ui/src/lib/i18n.ts index 62b7d162a..749509f82 100644 --- a/libs/ui/src/lib/i18n.ts +++ b/libs/ui/src/lib/i18n.ts @@ -41,7 +41,7 @@ const locales = { SELL: $localize`Sell`, // AssetClass (enum) - CASH: $localize`Cash`, + ALTERNATIVE_INVESTMENT: $localize`Alternative Investment`, COMMODITY: $localize`Commodity`, EQUITY: $localize`Equity`, FIXED_INCOME: $localize`Fixed Income`, @@ -50,6 +50,8 @@ const locales = { // AssetSubClass (enum) BOND: $localize`Bond`, + CASH: $localize`Cash`, + COLLECTIBLE: $localize`Collectible`, CRYPTOCURRENCY: $localize`Cryptocurrency`, ETF: $localize`ETF`, MUTUALFUND: $localize`Mutual Fund`, diff --git a/prisma/migrations/20250708090630_added_alternative_investment_to_asset_class/migration.sql b/prisma/migrations/20250708090630_added_alternative_investment_to_asset_class/migration.sql new file mode 100644 index 000000000..dceb644ff --- /dev/null +++ b/prisma/migrations/20250708090630_added_alternative_investment_to_asset_class/migration.sql @@ -0,0 +1,2 @@ +-- AlterEnum +ALTER TYPE "AssetClass" ADD VALUE 'ALTERNATIVE_INVESTMENT'; diff --git a/prisma/migrations/20250708090631_added_collectible_to_asset_sub_class/migration.sql b/prisma/migrations/20250708090631_added_collectible_to_asset_sub_class/migration.sql new file mode 100644 index 000000000..ddfa2d9ee --- /dev/null +++ b/prisma/migrations/20250708090631_added_collectible_to_asset_sub_class/migration.sql @@ -0,0 +1,2 @@ +-- AlterEnum +ALTER TYPE "AssetSubClass" ADD VALUE 'COLLECTIBLE'; diff --git a/prisma/schema.prisma b/prisma/schema.prisma index bddb0f78c..6080a1aa6 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -278,6 +278,7 @@ enum AccessPermission { } enum AssetClass { + ALTERNATIVE_INVESTMENT COMMODITY EQUITY FIXED_INCOME @@ -288,6 +289,7 @@ enum AssetClass { enum AssetSubClass { BOND CASH + COLLECTIBLE COMMODITY CRYPTOCURRENCY ETF From c34e3a943468f6b942a8202aa0a1f1d94a9c0fb8 Mon Sep 17 00:00:00 2001 From: Thomas Kaul <4159106+dtslvr@users.noreply.github.com> Date: Tue, 8 Jul 2025 19:45:22 +0200 Subject: [PATCH 2/5] Feature/reuse internal routes in assistant list item component (#5122) * Reuse internal routes --- .../assistant-list-item/assistant-list-item.component.ts | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/libs/ui/src/lib/assistant/assistant-list-item/assistant-list-item.component.ts b/libs/ui/src/lib/assistant/assistant-list-item/assistant-list-item.component.ts index 457df01ff..fe8530d7b 100644 --- a/libs/ui/src/lib/assistant/assistant-list-item/assistant-list-item.component.ts +++ b/libs/ui/src/lib/assistant/assistant-list-item/assistant-list-item.component.ts @@ -1,4 +1,5 @@ import { GfSymbolModule } from '@ghostfolio/client/pipes/symbol/symbol.module'; +import { internalRoutes } from '@ghostfolio/common/routes/routes'; import { SearchMode } from '@ghostfolio/ui/assistant/enums/search-mode'; import { IAssetSearchResultItem, @@ -54,13 +55,16 @@ export class GfAssistantListItemComponent dataSource: this.item?.dataSource, symbol: this.item?.symbol }; - this.routerLink = ['/admin', 'market-data']; + + this.routerLink = + internalRoutes.adminControl.subRoutes.marketData.routerLink; } else if (this.item?.mode === SearchMode.HOLDING) { this.queryParams = { dataSource: this.item?.dataSource, holdingDetailDialog: true, symbol: this.item?.symbol }; + this.routerLink = []; } else if (this.item?.mode === SearchMode.QUICK_LINK) { this.queryParams = {}; From 96b4e11126169396b48c74b5e15544ab3c08ccfe Mon Sep 17 00:00:00 2001 From: Thomas Kaul <4159106+dtslvr@users.noreply.github.com> Date: Tue, 8 Jul 2025 20:23:49 +0200 Subject: [PATCH 3/5] Feature/improve label in treemap chart for asset profiles with MANUAL data source (#5128) * Improve label * Update changelog --- CHANGELOG.md | 1 + .../treemap-chart/treemap-chart.component.ts | 42 ++++++++++--------- 2 files changed, 23 insertions(+), 20 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ff8e3a8bc..ed320057a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Changed +- Improved the label for asset profiles with `MANUAL` data source in the chart of the holdings tab on the home page - Improved the language localization for Catalan (`ca`) ## 2.179.0 - 2025-07-07 diff --git a/libs/ui/src/lib/treemap-chart/treemap-chart.component.ts b/libs/ui/src/lib/treemap-chart/treemap-chart.component.ts index 8c5ee94de..4e06d49cc 100644 --- a/libs/ui/src/lib/treemap-chart/treemap-chart.component.ts +++ b/libs/ui/src/lib/treemap-chart/treemap-chart.component.ts @@ -29,6 +29,7 @@ import { ChartConfiguration } from 'chart.js'; import { LinearScale } from 'chart.js'; import { Chart, Tooltip } from 'chart.js'; import { TreemapController, TreemapElement } from 'chartjs-chart-treemap'; +import { isUUID } from 'class-validator'; import { differenceInDays, max } from 'date-fns'; import { orderBy } from 'lodash'; import { NgxSkeletonLoaderModule } from 'ngx-skeleton-loader'; @@ -199,18 +200,18 @@ export class GfTreemapChartComponent const data: ChartConfiguration<'treemap'>['data'] = { datasets: [ { - backgroundColor: (ctx) => { + backgroundColor: (context) => { let annualizedNetPerformancePercent = getAnnualizedPerformancePercent({ daysInMarket: differenceInDays( endDate, max([ - ctx.raw._data.dateOfFirstActivity ?? new Date(0), + context.raw._data.dateOfFirstActivity ?? new Date(0), startDate ]) ), netPerformancePercentage: new Big( - ctx.raw._data.netPerformancePercentWithCurrencyEffect + context.raw._data.netPerformancePercentWithCurrencyEffect ) }).toNumber(); @@ -230,18 +231,18 @@ export class GfTreemapChartComponent key: 'allocationInPercentage', labels: { align: 'left', - color: (ctx) => { + color: (context) => { let annualizedNetPerformancePercent = getAnnualizedPerformancePercent({ daysInMarket: differenceInDays( endDate, max([ - ctx.raw._data.dateOfFirstActivity ?? new Date(0), + context.raw._data.dateOfFirstActivity ?? new Date(0), startDate ]) ), netPerformancePercentage: new Big( - ctx.raw._data.netPerformancePercentWithCurrencyEffect + context.raw._data.netPerformancePercentWithCurrencyEffect ) }).toNumber(); @@ -259,11 +260,11 @@ export class GfTreemapChartComponent }, display: true, font: [{ size: 16 }, { lineHeight: 1.5, size: 14 }], - formatter: (ctx) => { + formatter: ({ raw }) => { // Round to 4 decimal places let netPerformancePercentWithCurrencyEffect = Math.round( - ctx.raw._data.netPerformancePercentWithCurrencyEffect * 10000 + raw._data.netPerformancePercentWithCurrencyEffect * 10000 ) / 10000; if (Math.abs(netPerformancePercentWithCurrencyEffect) === 0) { @@ -272,8 +273,11 @@ export class GfTreemapChartComponent ); } + const name = raw._data.name; + const symbol = raw._data.symbol; + return [ - ctx.raw._data.symbol, + isUUID(symbol) ? (name ?? symbol) : symbol, `${netPerformancePercentWithCurrencyEffect > 0 ? '+' : ''}${(netPerformancePercentWithCurrencyEffect * 100).toFixed(2)}%` ]; }, @@ -341,19 +345,17 @@ export class GfTreemapChartComponent locale: this.locale }), callbacks: { - label: (context) => { - const allocationInPercentage = `${((context.raw._data.allocationInPercentage as number) * 100).toFixed(2)}%`; - const name = context.raw._data.name; + label: ({ raw }) => { + const allocationInPercentage = `${((raw._data.allocationInPercentage as number) * 100).toFixed(2)}%`; + const name = raw._data.name; const sign = - context.raw._data.netPerformancePercentWithCurrencyEffect > 0 - ? '+' - : ''; - const symbol = context.raw._data.symbol; + raw._data.netPerformancePercentWithCurrencyEffect > 0 ? '+' : ''; + const symbol = raw._data.symbol; - const netPerformanceInPercentageWithSign = `${sign}${(context.raw._data.netPerformancePercentWithCurrencyEffect * 100).toFixed(2)}%`; + const netPerformanceInPercentageWithSign = `${sign}${(raw._data.netPerformancePercentWithCurrencyEffect * 100).toFixed(2)}%`; - if (context.raw._data.valueInBaseCurrency !== null) { - const value = context.raw._data.valueInBaseCurrency as number; + if (raw._data.valueInBaseCurrency !== null) { + const value = raw._data.valueInBaseCurrency as number; return [ `${name ?? symbol} (${allocationInPercentage})`, @@ -363,7 +365,7 @@ export class GfTreemapChartComponent })} ${this.baseCurrency}`, '', $localize`Change` + ' (' + $localize`Performance` + ')', - `${sign}${context.raw._data.netPerformanceWithCurrencyEffect.toLocaleString( + `${sign}${raw._data.netPerformanceWithCurrencyEffect.toLocaleString( this.locale, { maximumFractionDigits: 2, From 0addf645b03766cb8aa5e2fdc5f7f4874849d086 Mon Sep 17 00:00:00 2001 From: Attila Cseh <77381875+csehatt741@users.noreply.github.com> Date: Tue, 8 Jul 2025 20:32:36 +0200 Subject: [PATCH 4/5] Bugfix/fix export functionality for accounts without activities (#5116) * Fix export functionality for accounts without activities * Update changelog --- CHANGELOG.md | 5 +++++ apps/api/src/app/export/export.service.ts | 22 ++++++++++++++++++---- 2 files changed, 23 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ed320057a..75327ae9c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,9 +14,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Changed +- Respected the filter by account for accounts when exporting activities on the portfolio activities page - Improved the label for asset profiles with `MANUAL` data source in the chart of the holdings tab on the home page - Improved the language localization for Catalan (`ca`) +### Fixed + +- Fixed the export functionality for accounts without activities + ## 2.179.0 - 2025-07-07 ### Added diff --git a/apps/api/src/app/export/export.service.ts b/apps/api/src/app/export/export.service.ts index 565dd1340..f0f52bfe2 100644 --- a/apps/api/src/app/export/export.service.ts +++ b/apps/api/src/app/export/export.service.ts @@ -5,7 +5,8 @@ import { TagService } from '@ghostfolio/api/services/tag/tag.service'; import { Filter, Export } from '@ghostfolio/common/interfaces'; import { Injectable } from '@nestjs/common'; -import { Platform } from '@prisma/client'; +import { Platform, Prisma } from '@prisma/client'; +import { groupBy } from 'lodash'; @Injectable() export class ExportService { @@ -26,6 +27,9 @@ export class ExportService { userCurrency: string; userId: string; }): Promise { + const { ACCOUNT: filtersByAccount } = groupBy(filters, ({ type }) => { + return type; + }); const platformsMap: { [platformId: string]: Platform } = {}; let { activities } = await this.orderService.getOrders({ @@ -44,20 +48,30 @@ export class ExportService { }); } + const where: Prisma.AccountWhereInput = { userId }; + + if (filtersByAccount?.length > 0) { + where.id = { + in: filtersByAccount.map(({ id }) => { + return id; + }) + }; + } + const accounts = ( await this.accountService.accounts({ + where, include: { balances: true, platform: true }, orderBy: { name: 'asc' - }, - where: { userId } + } }) ) .filter(({ id }) => { - return activities.length > 0 + return activityIds?.length > 0 ? activities.some(({ accountId }) => { return accountId === id; }) From 114b14507bc76c110af3b8fb9e1da1ec914b9018 Mon Sep 17 00:00:00 2001 From: Georgine Forner Date: Wed, 9 Jul 2025 00:33:18 +0600 Subject: [PATCH 5/5] Feature/improve language localization for ES 20250708 (#5125) * Improve language localization for ES * Update changelog --- CHANGELOG.md | 1 + apps/client/src/locales/messages.es.xlf | 16 ++++++++-------- 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 75327ae9c..226f81fb2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,6 +17,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Respected the filter by account for accounts when exporting activities on the portfolio activities page - Improved the label for asset profiles with `MANUAL` data source in the chart of the holdings tab on the home page - Improved the language localization for Catalan (`ca`) +- Improved the language localization for Spanish (`es`) ### Fixed diff --git a/apps/client/src/locales/messages.es.xlf b/apps/client/src/locales/messages.es.xlf index e0ef4dd37..a00d6d054 100644 --- a/apps/client/src/locales/messages.es.xlf +++ b/apps/client/src/locales/messages.es.xlf @@ -6527,7 +6527,7 @@ Italy - Italy + Italia libs/ui/src/lib/i18n.ts 85 @@ -6535,7 +6535,7 @@ Netherlands - Netherlands + Países Bajos libs/ui/src/lib/i18n.ts 87 @@ -6543,7 +6543,7 @@ New Zealand - New Zealand + Nueva Zelanda libs/ui/src/lib/i18n.ts 88 @@ -6551,7 +6551,7 @@ Poland - Poland + Polonia libs/ui/src/lib/i18n.ts 89 @@ -6559,7 +6559,7 @@ Romania - Romania + Rumanía libs/ui/src/lib/i18n.ts 90 @@ -6567,7 +6567,7 @@ South Africa - South Africa + Sudáfrica libs/ui/src/lib/i18n.ts 92 @@ -6575,7 +6575,7 @@ Thailand - Thailand + Tailandia libs/ui/src/lib/i18n.ts 94 @@ -6583,7 +6583,7 @@ United States - United States + Estados Unidos libs/ui/src/lib/i18n.ts 97