From 682bef004b9125cca22299a7a8f557cde60d30e1 Mon Sep 17 00:00:00 2001 From: Thomas Kaul <4159106+dtslvr@users.noreply.github.com> Date: Wed, 29 Jul 2026 22:14:46 +0200 Subject: [PATCH] Improve symbol validation for assets with manual data source --- apps/api/src/app/import/import.service.ts | 22 +++++++----- .../app/services/import-activities.service.ts | 10 ++++-- package-lock.json | 34 ++++++++++++++++--- package.json | 1 + 4 files changed, 52 insertions(+), 15 deletions(-) diff --git a/apps/api/src/app/import/import.service.ts b/apps/api/src/app/import/import.service.ts index c179598ed..344ba8770 100644 --- a/apps/api/src/app/import/import.service.ts +++ b/apps/api/src/app/import/import.service.ts @@ -386,6 +386,19 @@ export class ImportService { } } + for (const [index, assetProfileWithMarketData] of ( + assetProfilesWithMarketDataDto ?? [] + ).entries()) { + if ( + assetProfileWithMarketData.dataSource === DataSource.MANUAL && + !isValidCustomAssetProfileSymbol(assetProfileWithMarketData.symbol) + ) { + throw new Error( + `assetProfiles.${index}.symbol ("${assetProfileWithMarketData.symbol}") must be a UUID or start with the prefix "${ghostfolioPrefix}_" for the data source ("${DataSource.MANUAL}")` + ); + } + } + if (!isDryRun && assetProfilesWithMarketDataDto?.length) { const existingAssetProfiles = await this.symbolProfileService.getSymbolProfiles( @@ -395,15 +408,6 @@ export class ImportService { ); for (const assetProfileWithMarketData of assetProfilesWithMarketDataDto) { - if ( - !isValidCustomAssetProfileSymbol(assetProfileWithMarketData.symbol) - ) { - // Skip synthetic asset profiles (e.g. of the csv import), where the - // symbol is used as the name of the asset profile created in - // createActivity() - continue; - } - // Check if there is any existing asset profile const existingAssetProfile = existingAssetProfiles.find( ({ dataSource, symbol }) => { diff --git a/apps/client/src/app/services/import-activities.service.ts b/apps/client/src/app/services/import-activities.service.ts index 1fe067b3c..7d4baef33 100644 --- a/apps/client/src/app/services/import-activities.service.ts +++ b/apps/client/src/app/services/import-activities.service.ts @@ -17,6 +17,7 @@ import { Account, DataSource, Type as ActivityType } from '@prisma/client'; import { isFinite, isNumber, isString } from 'lodash'; import { parse as csvToJson } from 'papaparse'; import { firstValueFrom } from 'rxjs'; +import { v4 as uuidv4 } from 'uuid'; @Injectable({ providedIn: 'root' @@ -64,18 +65,23 @@ export class ImportActivitiesService { for (const [index, item] of content.entries()) { const currency = this.parseCurrency({ content, index, item }); - const dataSource = this.parseDataSource({ item }); const type = this.parseType({ content, index, item }); + let dataSource = this.parseDataSource({ item }); let symbol = this.parseSymbol({ content, index, item }); + if (!dataSource && ['FEE', 'INTEREST', 'LIABILITY'].includes(type)) { + // Apply the same data source as the import service + dataSource = DataSource.MANUAL; + } + if (dataSource === DataSource.MANUAL) { const name = symbol; if (!isValidCustomAssetProfileSymbol(symbol)) { // Generate a symbol and keep the free text as the name assetProfileSymbolMapping[name] = - assetProfileSymbolMapping[name] ?? crypto.randomUUID(); + assetProfileSymbolMapping[name] ?? uuidv4(); symbol = assetProfileSymbolMapping[name]; } diff --git a/package-lock.json b/package-lock.json index 87cad3a36..51ee52ea1 100644 --- a/package-lock.json +++ b/package-lock.json @@ -97,6 +97,7 @@ "tablemark": "4.1.0", "twitter-api-v2": "1.29.0", "undici": "8.5.0", + "uuid": "14.0.1", "yahoo-finance2": "4.0.0", "zod": "4.4.3", "zone.js": "0.16.1" @@ -15985,6 +15986,16 @@ "node": ">=12" } }, + "node_modules/bull/node_modules/uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", + "deprecated": "uuid@10 and below is no longer supported. For ESM codebases, update to uuid@latest. For CommonJS codebases, use uuid@11 (but be aware this version will likely be deprecated in 2028).", + "license": "MIT", + "bin": { + "uuid": "dist/bin/uuid" + } + }, "node_modules/bundle-name": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/bundle-name/-/bundle-name-4.1.0.tgz", @@ -32154,6 +32165,17 @@ "websocket-driver": "^0.7.4" } }, + "node_modules/sockjs/node_modules/uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", + "deprecated": "uuid@10 and below is no longer supported. For ESM codebases, update to uuid@latest. For CommonJS codebases, use uuid@11 (but be aware this version will likely be deprecated in 2028).", + "dev": true, + "license": "MIT", + "bin": { + "uuid": "dist/bin/uuid" + } + }, "node_modules/socks": { "version": "2.8.7", "resolved": "https://registry.npmjs.org/socks/-/socks-2.8.7.tgz", @@ -34664,12 +34686,16 @@ } }, "node_modules/uuid": { - "version": "8.3.2", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", - "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", + "version": "14.0.1", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-14.0.1.tgz", + "integrity": "sha512-6ZxzVpzDXDa3bJWaHilVayA+BH/1zmxCJoVgvmqJnid/gPoKHxUrS/aC/T6LGQtNHT+XHG9fXPJB4d+IrU30Ew==", + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], "license": "MIT", "bin": { - "uuid": "dist/bin/uuid" + "uuid": "dist-node/bin/uuid" } }, "node_modules/v8-compile-cache-lib": { diff --git a/package.json b/package.json index 9ace2cbe8..61bf1c0d4 100644 --- a/package.json +++ b/package.json @@ -141,6 +141,7 @@ "tablemark": "4.1.0", "twitter-api-v2": "1.29.0", "undici": "8.5.0", + "uuid": "14.0.1", "yahoo-finance2": "4.0.0", "zod": "4.4.3", "zone.js": "0.16.1"