Browse Source

Improve symbol validation for assets with manual data source

pull/7467/head
Thomas Kaul 4 weeks ago
parent
commit
53a940f136
  1. 57
      apps/api/src/app/import/import.service.ts
  2. 7
      apps/client/src/app/services/import-activities.service.ts

57
apps/api/src/app/import/import.service.ts

@ -212,6 +212,27 @@ export class ImportService {
} }
} }
// Validate the symbols before any data is persisted. Activities without a
// data source are excluded, since a symbol is generated in
// createActivity() if needed.
for (const [index, activity] of activitiesDto.entries()) {
if (!activity.dataSource) {
if (['FEE', 'INTEREST', 'LIABILITY'].includes(activity.type)) {
activity.dataSource = DataSource.MANUAL;
} else {
activity.dataSource =
this.dataProviderService.getDataSourceForImport();
}
} else if (
activity.dataSource === DataSource.MANUAL &&
!isValidCustomAssetProfileSymbol(activity.symbol)
) {
throw new Error(
`activities.${index}.symbol ("${activity.symbol}") must be a UUID or start with the prefix "${ghostfolioPrefix}_" for the data source ("${DataSource.MANUAL}")`
);
}
}
if (platformsDto?.length) { if (platformsDto?.length) {
const canCreatePlatform = hasPermission( const canCreatePlatform = hasPermission(
user.permissions, user.permissions,
@ -250,26 +271,6 @@ export class ImportService {
} }
} }
for (const [index, activity] of activitiesDto.entries()) {
if (!activity.dataSource) {
if (['FEE', 'INTEREST', 'LIABILITY'].includes(activity.type)) {
activity.dataSource = DataSource.MANUAL;
} else {
activity.dataSource =
this.dataProviderService.getDataSourceForImport();
}
}
if (
activity.dataSource === DataSource.MANUAL &&
!isValidCustomAssetProfileSymbol(activity.symbol)
) {
throw new Error(
`activities.${index}.symbol ("${activity.symbol}") must be a UUID or start with the prefix "${ghostfolioPrefix}_" for the data source ("${DataSource.MANUAL}")`
);
}
}
const existingTagsOfUser = const existingTagsOfUser =
tagsDto?.length || (!isDryRun && accountsWithBalancesDto?.length) tagsDto?.length || (!isDryRun && accountsWithBalancesDto?.length)
? await this.tagService.getTagsForUser(user.id) ? await this.tagService.getTagsForUser(user.id)
@ -456,13 +457,21 @@ export class ImportService {
} }
); );
// If there is no asset profile or if the asset profile belongs to a different user, then reuse the custom asset profile of the user or create a new asset profile // If there is no asset profile or if the asset profile belongs to a
// different user, then reuse the custom asset profile of the user or
// create a new asset profile
if (!existingAssetProfile || existingAssetProfile.userId !== user.id) { if (!existingAssetProfile || existingAssetProfile.userId !== user.id) {
// Check if the user has a custom asset profile with the same name // Check if the user has a custom asset profile with the same name.
// Skip asset profiles with a legacy free-text symbol as they would
// fail the symbol validation on a future import.
const existingCustomAssetProfileOfUser = const existingCustomAssetProfileOfUser =
assetProfileWithMarketData.dataSource === DataSource.MANUAL assetProfileWithMarketData.dataSource === DataSource.MANUAL
? existingCustomAssetProfilesOfUser.find(({ name }) => { ? existingCustomAssetProfilesOfUser.find((customAssetProfile) => {
return name === assetProfileWithMarketData.name; return (
customAssetProfile.name ===
assetProfileWithMarketData.name &&
isValidCustomAssetProfileSymbol(customAssetProfile.symbol)
);
}) })
: undefined; : undefined;

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

@ -61,7 +61,7 @@ export class ImportActivitiesService {
const activities: CreateOrderDto[] = []; const activities: CreateOrderDto[] = [];
const assetProfiles: CreateAssetProfileWithMarketDataDto[] = []; const assetProfiles: CreateAssetProfileWithMarketDataDto[] = [];
const assetProfileSymbolMapping: { [name: string]: string } = {}; const assetProfileSymbolMapping = new Map<string, string>();
for (const [index, item] of content.entries()) { for (const [index, item] of content.entries()) {
const currency = this.parseCurrency({ content, index, item }); const currency = this.parseCurrency({ content, index, item });
@ -80,9 +80,8 @@ export class ImportActivitiesService {
if (!isValidCustomAssetProfileSymbol(symbol)) { if (!isValidCustomAssetProfileSymbol(symbol)) {
// Generate a symbol and keep the free text as the name // Generate a symbol and keep the free text as the name
assetProfileSymbolMapping[name] = symbol = assetProfileSymbolMapping.get(name) ?? uuidv4();
assetProfileSymbolMapping[name] ?? uuidv4(); assetProfileSymbolMapping.set(name, symbol);
symbol = assetProfileSymbolMapping[name];
} }
const isExistingAssetProfile = assetProfiles.some((assetProfile) => { const isExistingAssetProfile = assetProfiles.some((assetProfile) => {

Loading…
Cancel
Save