|
@ -11,7 +11,10 @@ import { PortfolioService } from '@ghostfolio/api/app/portfolio/portfolio.servic |
|
|
import { DataProviderService } from '@ghostfolio/api/services/data-provider/data-provider.service'; |
|
|
import { DataProviderService } from '@ghostfolio/api/services/data-provider/data-provider.service'; |
|
|
import { ExchangeRateDataService } from '@ghostfolio/api/services/exchange-rate-data/exchange-rate-data.service'; |
|
|
import { ExchangeRateDataService } from '@ghostfolio/api/services/exchange-rate-data/exchange-rate-data.service'; |
|
|
import { SymbolProfileService } from '@ghostfolio/api/services/symbol-profile/symbol-profile.service'; |
|
|
import { SymbolProfileService } from '@ghostfolio/api/services/symbol-profile/symbol-profile.service'; |
|
|
import { parseDate } from '@ghostfolio/common/helper'; |
|
|
import { |
|
|
|
|
|
getAssetProfileIdentifier, |
|
|
|
|
|
parseDate |
|
|
|
|
|
} from '@ghostfolio/common/helper'; |
|
|
import { UniqueAsset } from '@ghostfolio/common/interfaces'; |
|
|
import { UniqueAsset } from '@ghostfolio/common/interfaces'; |
|
|
import { |
|
|
import { |
|
|
AccountWithPlatform, |
|
|
AccountWithPlatform, |
|
@ -21,6 +24,7 @@ import { Injectable } from '@nestjs/common'; |
|
|
import { DataSource, Prisma, SymbolProfile } from '@prisma/client'; |
|
|
import { DataSource, Prisma, SymbolProfile } from '@prisma/client'; |
|
|
import Big from 'big.js'; |
|
|
import Big from 'big.js'; |
|
|
import { endOfToday, isAfter, isSameDay, parseISO } from 'date-fns'; |
|
|
import { endOfToday, isAfter, isSameDay, parseISO } from 'date-fns'; |
|
|
|
|
|
import { uniqBy } from 'lodash'; |
|
|
import { v4 as uuidv4 } from 'uuid'; |
|
|
import { v4 as uuidv4 } from 'uuid'; |
|
|
|
|
|
|
|
|
@Injectable() |
|
|
@Injectable() |
|
@ -220,8 +224,7 @@ export class ImportService { |
|
|
|
|
|
|
|
|
const assetProfiles = await this.validateActivities({ |
|
|
const assetProfiles = await this.validateActivities({ |
|
|
activitiesDto, |
|
|
activitiesDto, |
|
|
maxActivitiesToImport, |
|
|
maxActivitiesToImport |
|
|
userId |
|
|
|
|
|
}); |
|
|
}); |
|
|
|
|
|
|
|
|
const activitiesExtendedWithErrors = await this.extendActivitiesWithErrors({ |
|
|
const activitiesExtendedWithErrors = await this.extendActivitiesWithErrors({ |
|
@ -295,7 +298,12 @@ export class ImportService { |
|
|
symbolMapping: assetProfile.symbolMapping, |
|
|
symbolMapping: assetProfile.symbolMapping, |
|
|
updatedAt: assetProfile.updatedAt, |
|
|
updatedAt: assetProfile.updatedAt, |
|
|
url: assetProfile.url, |
|
|
url: assetProfile.url, |
|
|
...assetProfiles[assetProfile.symbol] |
|
|
...assetProfiles[ |
|
|
|
|
|
getAssetProfileIdentifier({ |
|
|
|
|
|
dataSource: assetProfile.dataSource, |
|
|
|
|
|
symbol: assetProfile.symbol |
|
|
|
|
|
}) |
|
|
|
|
|
] |
|
|
}, |
|
|
}, |
|
|
Account: validatedAccount, |
|
|
Account: validatedAccount, |
|
|
symbolProfileId: undefined, |
|
|
symbolProfileId: undefined, |
|
@ -446,25 +454,30 @@ export class ImportService { |
|
|
|
|
|
|
|
|
private async validateActivities({ |
|
|
private async validateActivities({ |
|
|
activitiesDto, |
|
|
activitiesDto, |
|
|
maxActivitiesToImport, |
|
|
maxActivitiesToImport |
|
|
userId |
|
|
|
|
|
}: { |
|
|
}: { |
|
|
activitiesDto: Partial<CreateOrderDto>[]; |
|
|
activitiesDto: Partial<CreateOrderDto>[]; |
|
|
maxActivitiesToImport: number; |
|
|
maxActivitiesToImport: number; |
|
|
userId: string; |
|
|
|
|
|
}) { |
|
|
}) { |
|
|
if (activitiesDto?.length > maxActivitiesToImport) { |
|
|
if (activitiesDto?.length > maxActivitiesToImport) { |
|
|
throw new Error(`Too many activities (${maxActivitiesToImport} at most)`); |
|
|
throw new Error(`Too many activities (${maxActivitiesToImport} at most)`); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
const assetProfiles: { |
|
|
const assetProfiles: { |
|
|
[symbol: string]: Partial<SymbolProfile>; |
|
|
[assetProfileIdentifier: string]: Partial<SymbolProfile>; |
|
|
} = {}; |
|
|
} = {}; |
|
|
|
|
|
|
|
|
|
|
|
const uniqueActivitiesDto = uniqBy( |
|
|
|
|
|
activitiesDto, |
|
|
|
|
|
({ dataSource, symbol }) => { |
|
|
|
|
|
return getAssetProfileIdentifier({ dataSource, symbol }); |
|
|
|
|
|
} |
|
|
|
|
|
); |
|
|
|
|
|
|
|
|
for (const [ |
|
|
for (const [ |
|
|
index, |
|
|
index, |
|
|
{ currency, dataSource, symbol } |
|
|
{ currency, dataSource, symbol } |
|
|
] of activitiesDto.entries()) { |
|
|
] of uniqueActivitiesDto.entries()) { |
|
|
if (dataSource !== 'MANUAL') { |
|
|
if (dataSource !== 'MANUAL') { |
|
|
const assetProfile = ( |
|
|
const assetProfile = ( |
|
|
await this.dataProviderService.getAssetProfiles([ |
|
|
await this.dataProviderService.getAssetProfiles([ |
|
@ -484,7 +497,8 @@ export class ImportService { |
|
|
); |
|
|
); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
assetProfiles[symbol] = assetProfile; |
|
|
assetProfiles[getAssetProfileIdentifier({ dataSource, symbol })] = |
|
|
|
|
|
assetProfile; |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|