diff --git a/CHANGELOG.md b/CHANGELOG.md index 5e196fb88..538815f1d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,9 +9,15 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Changed +- Extended the support of the _Exclude from Analysis_ tag from accounts to activities +- Optimized the performance of the search in the assistant by reusing the cached portfolio snapshot - Improved the validation of the import functionality when referencing an asset profile with the data source `MANUAL` - Improved the validation of the endpoint to add a custom asset profile in the admin control panel +### Fixed + +- Fixed the fuzzy search for the holdings in the assistant + ## 3.41.0 - 2026-08-03 ### Added diff --git a/apps/api/src/app/portfolio/portfolio.service.ts b/apps/api/src/app/portfolio/portfolio.service.ts index 0a69cf32d..70106bdc1 100644 --- a/apps/api/src/app/portfolio/portfolio.service.ts +++ b/apps/api/src/app/portfolio/portfolio.service.ts @@ -165,6 +165,10 @@ export class PortfolioService { }; } + const filtersWithoutSearchQueryFilter = filters?.filter(({ type }) => { + return type !== 'SEARCH_QUERY'; + }); + const [accounts, details, user] = await Promise.all([ this.accountService.accounts({ where, @@ -176,8 +180,8 @@ export class PortfolioService { orderBy: { name: 'asc' } }), this.getDetails({ - filters, withExcludedAccounts, + filters: filtersWithoutSearchQueryFilter, impersonationId: userId, userId: this.request.user.id }), @@ -369,14 +373,6 @@ export class PortfolioService { userId: string; }) { userId = await this.getUserId(impersonationId, userId); - const { holdings: holdingsMap } = await this.getDetails({ - dateRange, - filters, - impersonationId, - userId - }); - - let holdings = Object.values(holdingsMap); const { SEARCH_QUERY: [filterBySearchQuery] = [] } = groupBy( filters, @@ -385,9 +381,22 @@ export class PortfolioService { } ); + const filtersWithoutSearchQueryFilter = filters?.filter(({ type }) => { + return type !== 'SEARCH_QUERY'; + }); + + const { holdings: holdingsMap } = await this.getDetails({ + dateRange, + impersonationId, + userId, + filters: filtersWithoutSearchQueryFilter + }); + + let holdings = Object.values(holdingsMap); + if (filterBySearchQuery) { const fuse = new Fuse(holdings, { - keys: ['isin', 'name', 'symbol'], + keys: ['assetProfile.isin', 'assetProfile.name', 'assetProfile.symbol'], threshold: 0.3 }); @@ -651,6 +660,7 @@ export class PortfolioService { }; } ), + isin: assetProfile.isin, name: assetProfile.name, sectors: assetProfile.sectors, symbol: assetProfile.symbol, diff --git a/apps/api/src/app/user/user.service.ts b/apps/api/src/app/user/user.service.ts index 5d60c571d..10e9c5240 100644 --- a/apps/api/src/app/user/user.service.ts +++ b/apps/api/src/app/user/user.service.ts @@ -186,15 +186,15 @@ export class UserService { systemMessage = systemMessageProperty; } - let tags = tagsForUser.filter((tag) => { - return tag.id !== TAG_ID_EXCLUDE_FROM_ANALYSIS; - }); + let tags = tagsForUser; if ( this.configurationService.get('ENABLE_FEATURE_SUBSCRIPTION') && subscription.type === SubscriptionType.Basic ) { - tags = []; + tags = tags.filter(({ id }) => { + return id === TAG_ID_EXCLUDE_FROM_ANALYSIS; + }); } return { diff --git a/apps/client/src/app/pages/accounts/create-or-update-account-dialog/create-or-update-account-dialog.component.ts b/apps/client/src/app/pages/accounts/create-or-update-account-dialog/create-or-update-account-dialog.component.ts index 2157ea5d3..86fc84711 100644 --- a/apps/client/src/app/pages/accounts/create-or-update-account-dialog/create-or-update-account-dialog.component.ts +++ b/apps/client/src/app/pages/accounts/create-or-update-account-dialog/create-or-update-account-dialog.component.ts @@ -1,5 +1,4 @@ import { UserService } from '@ghostfolio/client/services/user/user.service'; -import { TAG_ID_EXCLUDE_FROM_ANALYSIS } from '@ghostfolio/common/config'; import { CreateAccountDto, UpdateAccountDto } from '@ghostfolio/common/dtos'; import { getStringOrNull } from '@ghostfolio/common/helper'; import { hasPermission, permissions } from '@ghostfolio/common/permissions'; @@ -86,19 +85,13 @@ export class GfCreateOrUpdateAccountDialogComponent { permissions.createOwnTag ); - this.tagsAvailable = [ - ...(this.data.user?.tags ?? []), - { - id: TAG_ID_EXCLUDE_FROM_ANALYSIS, - name: 'EXCLUDE_FROM_ANALYSIS', - userId: null - } - ].map((tag) => { - return { - ...tag, - name: translate(tag.name) - }; - }); + this.tagsAvailable = + this.data.user?.tags?.map((tag) => { + return { + ...tag, + name: translate(tag.name) + }; + }) ?? []; this.accountForm = this.formBuilder.group({ accountId: [{ disabled: true, value: this.data.account.id }], diff --git a/libs/common/src/lib/interfaces/portfolio-position.interface.ts b/libs/common/src/lib/interfaces/portfolio-position.interface.ts index cf71b20ca..388e661f7 100644 --- a/libs/common/src/lib/interfaces/portfolio-position.interface.ts +++ b/libs/common/src/lib/interfaces/portfolio-position.interface.ts @@ -15,6 +15,7 @@ export interface PortfolioPosition { | 'currency' | 'dataSource' | 'holdings' + | 'isin' | 'name' | 'sectors' | 'symbol' diff --git a/libs/ui/src/lib/portfolio-filter-form/portfolio-filter-form.util.ts b/libs/ui/src/lib/portfolio-filter-form/portfolio-filter-form.util.ts index ec3114d41..193624c76 100644 --- a/libs/ui/src/lib/portfolio-filter-form/portfolio-filter-form.util.ts +++ b/libs/ui/src/lib/portfolio-filter-form/portfolio-filter-form.util.ts @@ -1,3 +1,4 @@ +import { TAG_ID_EXCLUDE_FROM_ANALYSIS } from '@ghostfolio/common/config'; import { getAssetProfileIdentifier } from '@ghostfolio/common/helper'; import { Filter, PortfolioPosition } from '@ghostfolio/common/interfaces'; @@ -105,8 +106,8 @@ export function getTagFilters( ): Filter[] { return ( tags - ?.filter(({ isUsed }) => { - return isUsed; + ?.filter(({ id, isUsed }) => { + return id !== TAG_ID_EXCLUDE_FROM_ANALYSIS && isUsed; }) ?.map(({ id, name }) => { return {