From 9b6e2ff3022f8cc708338dcf3398b69f522b8b9d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sven=20G=C3=BCnther?= Date: Sun, 12 Oct 2025 19:07:57 +0200 Subject: [PATCH] Fix CSV import for MANUAL data source assets (#5740) Generate synthetic asset profiles during CSV parsing to fix import failures for custom assets like real estate and collectibles. --- CHANGELOG.md | 4 ++ .../import-activities-dialog.component.ts | 1 + .../app/services/import-activities.service.ts | 46 +++++++++++++++++-- 3 files changed, 47 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9a555fa34..7a0c4c266 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,6 +17,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Optimized the get quotes functionality by utilizing the asset profile resolutions in the _Financial Modeling Prep_ service - Extracted the footer to a component +### Fixed + +- Import of activity with MANUAL data source fails for CSV file + ## 2.208.0 - 2025-10-11 ### Added diff --git a/apps/client/src/app/pages/portfolio/activities/import-activities-dialog/import-activities-dialog.component.ts b/apps/client/src/app/pages/portfolio/activities/import-activities-dialog/import-activities-dialog.component.ts index 2439a4b65..d3b0bdc1e 100644 --- a/apps/client/src/app/pages/portfolio/activities/import-activities-dialog/import-activities-dialog.component.ts +++ b/apps/client/src/app/pages/portfolio/activities/import-activities-dialog/import-activities-dialog.component.ts @@ -366,6 +366,7 @@ export class GfImportActivitiesDialogComponent implements OnDestroy { userAccounts: this.data.user.accounts }); this.activities = data.activities; + this.assetProfiles = data.assetProfiles; this.dataSource = new MatTableDataSource(data.activities.reverse()); this.pageIndex = 0; this.totalItems = data.activities.length; diff --git a/apps/client/src/app/services/import-activities.service.ts b/apps/client/src/app/services/import-activities.service.ts index 27a34652b..16e0a8f4d 100644 --- a/apps/client/src/app/services/import-activities.service.ts +++ b/apps/client/src/app/services/import-activities.service.ts @@ -45,6 +45,7 @@ export class ImportActivitiesService { userAccounts: Account[]; }): Promise<{ activities: Activity[]; + assetProfiles: CreateAssetProfileWithMarketDataDto[]; }> { const content = csvToJson(fileContent, { dynamicTyping: true, @@ -53,23 +54,60 @@ export class ImportActivitiesService { }).data; const activities: CreateOrderDto[] = []; + const assetProfiles: CreateAssetProfileWithMarketDataDto[] = []; + for (const [index, item] of content.entries()) { + const dataSource = this.parseDataSource({ item }); + const symbol = this.parseSymbol({ content, index, item }); + const currency = this.parseCurrency({ content, index, item }); + activities.push({ accountId: this.parseAccount({ item, userAccounts }), comment: this.parseComment({ item }), - currency: this.parseCurrency({ content, index, item }), - dataSource: this.parseDataSource({ item }), + currency, + dataSource, date: this.parseDate({ content, index, item }), fee: this.parseFee({ content, index, item }), quantity: this.parseQuantity({ content, index, item }), - symbol: this.parseSymbol({ content, index, item }), + symbol, type: this.parseType({ content, index, item }), unitPrice: this.parseUnitPrice({ content, index, item }), updateAccountBalance: false }); + + // Create synthetic asset profile for MANUAL data source + if (dataSource === DataSource.MANUAL) { + assetProfiles.push({ + assetClass: null, + assetSubClass: null, + comment: null, + countries: [], + currency, + cusip: null, + dataSource: DataSource.MANUAL, + figi: null, + figiComposite: null, + figiShareClass: null, + holdings: [], + isActive: true, + isin: null, + marketData: [], + name: symbol, + scraperConfiguration: null, + sectors: [], + symbol, + symbolMapping: {}, + url: null + }); + } } - return await this.importJson({ activities, isDryRun }); + const result = await this.importJson({ + activities, + assetProfiles, + isDryRun + }); + return { ...result, assetProfiles }; } public importJson({