Browse Source

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.
pull/5749/head
Sven Günther 3 weeks ago
parent
commit
9b6e2ff302
  1. 4
      CHANGELOG.md
  2. 1
      apps/client/src/app/pages/portfolio/activities/import-activities-dialog/import-activities-dialog.component.ts
  3. 46
      apps/client/src/app/services/import-activities.service.ts

4
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 - Optimized the get quotes functionality by utilizing the asset profile resolutions in the _Financial Modeling Prep_ service
- Extracted the footer to a component - Extracted the footer to a component
### Fixed
- Import of activity with MANUAL data source fails for CSV file
## 2.208.0 - 2025-10-11 ## 2.208.0 - 2025-10-11
### Added ### Added

1
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 userAccounts: this.data.user.accounts
}); });
this.activities = data.activities; this.activities = data.activities;
this.assetProfiles = data.assetProfiles;
this.dataSource = new MatTableDataSource(data.activities.reverse()); this.dataSource = new MatTableDataSource(data.activities.reverse());
this.pageIndex = 0; this.pageIndex = 0;
this.totalItems = data.activities.length; this.totalItems = data.activities.length;

46
apps/client/src/app/services/import-activities.service.ts

@ -45,6 +45,7 @@ export class ImportActivitiesService {
userAccounts: Account[]; userAccounts: Account[];
}): Promise<{ }): Promise<{
activities: Activity[]; activities: Activity[];
assetProfiles: CreateAssetProfileWithMarketDataDto[];
}> { }> {
const content = csvToJson(fileContent, { const content = csvToJson(fileContent, {
dynamicTyping: true, dynamicTyping: true,
@ -53,23 +54,60 @@ export class ImportActivitiesService {
}).data; }).data;
const activities: CreateOrderDto[] = []; const activities: CreateOrderDto[] = [];
const assetProfiles: CreateAssetProfileWithMarketDataDto[] = [];
for (const [index, item] of content.entries()) { 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({ activities.push({
accountId: this.parseAccount({ item, userAccounts }), accountId: this.parseAccount({ item, userAccounts }),
comment: this.parseComment({ item }), comment: this.parseComment({ item }),
currency: this.parseCurrency({ content, index, item }), currency,
dataSource: this.parseDataSource({ item }), dataSource,
date: this.parseDate({ content, index, item }), date: this.parseDate({ content, index, item }),
fee: this.parseFee({ content, index, item }), fee: this.parseFee({ content, index, item }),
quantity: this.parseQuantity({ content, index, item }), quantity: this.parseQuantity({ content, index, item }),
symbol: this.parseSymbol({ content, index, item }), symbol,
type: this.parseType({ content, index, item }), type: this.parseType({ content, index, item }),
unitPrice: this.parseUnitPrice({ content, index, item }), unitPrice: this.parseUnitPrice({ content, index, item }),
updateAccountBalance: false 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({ public importJson({

Loading…
Cancel
Save