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
- 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

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

46
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({

Loading…
Cancel
Save