|  |  | @ -84,6 +84,7 @@ export class ImportService { | 
			
		
	
		
			
				
					|  |  |  |           feeInBaseCurrency: 0, | 
			
		
	
		
			
				
					|  |  |  |           id: assetProfile.id, | 
			
		
	
		
			
				
					|  |  |  |           isDraft: false, | 
			
		
	
		
			
				
					|  |  |  |           isDuplicate: false, // TODO: Use evaluated state
 | 
			
		
	
		
			
				
					|  |  |  |           SymbolProfile: <SymbolProfile>(<unknown>assetProfile), | 
			
		
	
		
			
				
					|  |  |  |           symbolProfileId: assetProfile.id, | 
			
		
	
		
			
				
					|  |  |  |           type: 'DIVIDEND', | 
			
		
	
	
		
			
				
					|  |  | @ -204,9 +205,14 @@ export class ImportService { | 
			
		
	
		
			
				
					|  |  |  |       userId | 
			
		
	
		
			
				
					|  |  |  |     }); | 
			
		
	
		
			
				
					|  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |     const activitiesMarkedAsDuplicates = await this.markActivitiesAsDuplicates({ | 
			
		
	
		
			
				
					|  |  |  |       activitiesDto, | 
			
		
	
		
			
				
					|  |  |  |       userId | 
			
		
	
		
			
				
					|  |  |  |     }); | 
			
		
	
		
			
				
					|  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |     const accounts = (await this.accountService.getAccounts(userId)).map( | 
			
		
	
		
			
				
					|  |  |  |       (account) => { | 
			
		
	
		
			
				
					|  |  |  |         return { id: account.id, name: account.name }; | 
			
		
	
		
			
				
					|  |  |  |       ({ id, name }) => { | 
			
		
	
		
			
				
					|  |  |  |         return { id, name }; | 
			
		
	
		
			
				
					|  |  |  |       } | 
			
		
	
		
			
				
					|  |  |  |     ); | 
			
		
	
		
			
				
					|  |  |  | 
 | 
			
		
	
	
		
			
				
					|  |  | @ -221,16 +227,14 @@ export class ImportService { | 
			
		
	
		
			
				
					|  |  |  |     for (const { | 
			
		
	
		
			
				
					|  |  |  |       accountId, | 
			
		
	
		
			
				
					|  |  |  |       comment, | 
			
		
	
		
			
				
					|  |  |  |       currency, | 
			
		
	
		
			
				
					|  |  |  |       dataSource, | 
			
		
	
		
			
				
					|  |  |  |       date: dateString, | 
			
		
	
		
			
				
					|  |  |  |       date, | 
			
		
	
		
			
				
					|  |  |  |       fee, | 
			
		
	
		
			
				
					|  |  |  |       isDuplicate, | 
			
		
	
		
			
				
					|  |  |  |       quantity, | 
			
		
	
		
			
				
					|  |  |  |       symbol, | 
			
		
	
		
			
				
					|  |  |  |       SymbolProfile: assetProfile, | 
			
		
	
		
			
				
					|  |  |  |       type, | 
			
		
	
		
			
				
					|  |  |  |       unitPrice | 
			
		
	
		
			
				
					|  |  |  |     } of activitiesDto) { | 
			
		
	
		
			
				
					|  |  |  |       const date = parseISO(<string>(<unknown>dateString)); | 
			
		
	
		
			
				
					|  |  |  |     } of activitiesMarkedAsDuplicates) { | 
			
		
	
		
			
				
					|  |  |  |       const validatedAccount = accounts.find(({ id }) => { | 
			
		
	
		
			
				
					|  |  |  |         return id === accountId; | 
			
		
	
		
			
				
					|  |  |  |       }); | 
			
		
	
	
		
			
				
					|  |  | @ -256,29 +260,33 @@ export class ImportService { | 
			
		
	
		
			
				
					|  |  |  |           id: uuidv4(), | 
			
		
	
		
			
				
					|  |  |  |           isDraft: isAfter(date, endOfToday()), | 
			
		
	
		
			
				
					|  |  |  |           SymbolProfile: { | 
			
		
	
		
			
				
					|  |  |  |             currency, | 
			
		
	
		
			
				
					|  |  |  |             dataSource, | 
			
		
	
		
			
				
					|  |  |  |             symbol, | 
			
		
	
		
			
				
					|  |  |  |             assetClass: null, | 
			
		
	
		
			
				
					|  |  |  |             assetSubClass: null, | 
			
		
	
		
			
				
					|  |  |  |             comment: null, | 
			
		
	
		
			
				
					|  |  |  |             countries: null, | 
			
		
	
		
			
				
					|  |  |  |             createdAt: undefined, | 
			
		
	
		
			
				
					|  |  |  |             id: undefined, | 
			
		
	
		
			
				
					|  |  |  |             isin: null, | 
			
		
	
		
			
				
					|  |  |  |             name: null, | 
			
		
	
		
			
				
					|  |  |  |             scraperConfiguration: null, | 
			
		
	
		
			
				
					|  |  |  |             sectors: null, | 
			
		
	
		
			
				
					|  |  |  |             symbolMapping: null, | 
			
		
	
		
			
				
					|  |  |  |             updatedAt: undefined, | 
			
		
	
		
			
				
					|  |  |  |             url: null, | 
			
		
	
		
			
				
					|  |  |  |             ...assetProfiles[symbol] | 
			
		
	
		
			
				
					|  |  |  |             assetClass: assetProfile.assetClass, | 
			
		
	
		
			
				
					|  |  |  |             assetSubClass: assetProfile.assetSubClass, | 
			
		
	
		
			
				
					|  |  |  |             comment: assetProfile.comment, | 
			
		
	
		
			
				
					|  |  |  |             countries: assetProfile.countries, | 
			
		
	
		
			
				
					|  |  |  |             createdAt: assetProfile.createdAt, | 
			
		
	
		
			
				
					|  |  |  |             currency: assetProfile.currency, | 
			
		
	
		
			
				
					|  |  |  |             dataSource: assetProfile.dataSource, | 
			
		
	
		
			
				
					|  |  |  |             id: assetProfile.id, | 
			
		
	
		
			
				
					|  |  |  |             isin: assetProfile.isin, | 
			
		
	
		
			
				
					|  |  |  |             name: assetProfile.name, | 
			
		
	
		
			
				
					|  |  |  |             scraperConfiguration: assetProfile.scraperConfiguration, | 
			
		
	
		
			
				
					|  |  |  |             sectors: assetProfile.sectors, | 
			
		
	
		
			
				
					|  |  |  |             symbol: assetProfile.currency, | 
			
		
	
		
			
				
					|  |  |  |             symbolMapping: assetProfile.symbolMapping, | 
			
		
	
		
			
				
					|  |  |  |             updatedAt: assetProfile.updatedAt, | 
			
		
	
		
			
				
					|  |  |  |             url: assetProfile.url, | 
			
		
	
		
			
				
					|  |  |  |             ...assetProfiles[assetProfile.symbol] | 
			
		
	
		
			
				
					|  |  |  |           }, | 
			
		
	
		
			
				
					|  |  |  |           Account: validatedAccount, | 
			
		
	
		
			
				
					|  |  |  |           symbolProfileId: undefined, | 
			
		
	
		
			
				
					|  |  |  |           updatedAt: new Date() | 
			
		
	
		
			
				
					|  |  |  |         }; | 
			
		
	
		
			
				
					|  |  |  |       } else { | 
			
		
	
		
			
				
					|  |  |  |         if (isDuplicate) { | 
			
		
	
		
			
				
					|  |  |  |           continue; | 
			
		
	
		
			
				
					|  |  |  |         } | 
			
		
	
		
			
				
					|  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |         order = await this.orderService.createOrder({ | 
			
		
	
		
			
				
					|  |  |  |           comment, | 
			
		
	
		
			
				
					|  |  |  |           date, | 
			
		
	
	
		
			
				
					|  |  | @ -291,14 +299,14 @@ export class ImportService { | 
			
		
	
		
			
				
					|  |  |  |           SymbolProfile: { | 
			
		
	
		
			
				
					|  |  |  |             connectOrCreate: { | 
			
		
	
		
			
				
					|  |  |  |               create: { | 
			
		
	
		
			
				
					|  |  |  |                 currency, | 
			
		
	
		
			
				
					|  |  |  |                 dataSource, | 
			
		
	
		
			
				
					|  |  |  |                 symbol | 
			
		
	
		
			
				
					|  |  |  |                 currency: assetProfile.currency, | 
			
		
	
		
			
				
					|  |  |  |                 dataSource: assetProfile.dataSource, | 
			
		
	
		
			
				
					|  |  |  |                 symbol: assetProfile.symbol | 
			
		
	
		
			
				
					|  |  |  |               }, | 
			
		
	
		
			
				
					|  |  |  |               where: { | 
			
		
	
		
			
				
					|  |  |  |                 dataSource_symbol: { | 
			
		
	
		
			
				
					|  |  |  |                   dataSource, | 
			
		
	
		
			
				
					|  |  |  |                   symbol | 
			
		
	
		
			
				
					|  |  |  |                   dataSource: assetProfile.dataSource, | 
			
		
	
		
			
				
					|  |  |  |                   symbol: assetProfile.symbol | 
			
		
	
		
			
				
					|  |  |  |                 } | 
			
		
	
		
			
				
					|  |  |  |               } | 
			
		
	
		
			
				
					|  |  |  |             } | 
			
		
	
	
		
			
				
					|  |  | @ -313,15 +321,16 @@ export class ImportService { | 
			
		
	
		
			
				
					|  |  |  |       //@ts-ignore
 | 
			
		
	
		
			
				
					|  |  |  |       activities.push({ | 
			
		
	
		
			
				
					|  |  |  |         ...order, | 
			
		
	
		
			
				
					|  |  |  |         isDuplicate, | 
			
		
	
		
			
				
					|  |  |  |         value, | 
			
		
	
		
			
				
					|  |  |  |         feeInBaseCurrency: this.exchangeRateDataService.toCurrency( | 
			
		
	
		
			
				
					|  |  |  |           fee, | 
			
		
	
		
			
				
					|  |  |  |           currency, | 
			
		
	
		
			
				
					|  |  |  |           assetProfile.currency, | 
			
		
	
		
			
				
					|  |  |  |           userCurrency | 
			
		
	
		
			
				
					|  |  |  |         ), | 
			
		
	
		
			
				
					|  |  |  |         valueInBaseCurrency: this.exchangeRateDataService.toCurrency( | 
			
		
	
		
			
				
					|  |  |  |           value, | 
			
		
	
		
			
				
					|  |  |  |           currency, | 
			
		
	
		
			
				
					|  |  |  |           assetProfile.currency, | 
			
		
	
		
			
				
					|  |  |  |           userCurrency | 
			
		
	
		
			
				
					|  |  |  |         ) | 
			
		
	
		
			
				
					|  |  |  |       }); | 
			
		
	
	
		
			
				
					|  |  | @ -340,6 +349,78 @@ export class ImportService { | 
			
		
	
		
			
				
					|  |  |  |     return uniqueAccountIds.size === 1; | 
			
		
	
		
			
				
					|  |  |  |   } | 
			
		
	
		
			
				
					|  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |   private async markActivitiesAsDuplicates({ | 
			
		
	
		
			
				
					|  |  |  |     activitiesDto, | 
			
		
	
		
			
				
					|  |  |  |     userId | 
			
		
	
		
			
				
					|  |  |  |   }: { | 
			
		
	
		
			
				
					|  |  |  |     activitiesDto: Partial<CreateOrderDto>[]; | 
			
		
	
		
			
				
					|  |  |  |     userId: string; | 
			
		
	
		
			
				
					|  |  |  |   }): Promise<Partial<Activity>[]> { | 
			
		
	
		
			
				
					|  |  |  |     const existingActivities = await this.orderService.orders({ | 
			
		
	
		
			
				
					|  |  |  |       include: { SymbolProfile: true }, | 
			
		
	
		
			
				
					|  |  |  |       orderBy: { date: 'desc' }, | 
			
		
	
		
			
				
					|  |  |  |       where: { userId } | 
			
		
	
		
			
				
					|  |  |  |     }); | 
			
		
	
		
			
				
					|  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |     return activitiesDto.map( | 
			
		
	
		
			
				
					|  |  |  |       ({ | 
			
		
	
		
			
				
					|  |  |  |         accountId, | 
			
		
	
		
			
				
					|  |  |  |         comment, | 
			
		
	
		
			
				
					|  |  |  |         currency, | 
			
		
	
		
			
				
					|  |  |  |         dataSource, | 
			
		
	
		
			
				
					|  |  |  |         date: dateString, | 
			
		
	
		
			
				
					|  |  |  |         fee, | 
			
		
	
		
			
				
					|  |  |  |         quantity, | 
			
		
	
		
			
				
					|  |  |  |         symbol, | 
			
		
	
		
			
				
					|  |  |  |         type, | 
			
		
	
		
			
				
					|  |  |  |         unitPrice | 
			
		
	
		
			
				
					|  |  |  |       }) => { | 
			
		
	
		
			
				
					|  |  |  |         const date = parseISO(<string>(<unknown>dateString)); | 
			
		
	
		
			
				
					|  |  |  |         const isDuplicate = existingActivities.some((activity) => { | 
			
		
	
		
			
				
					|  |  |  |           return ( | 
			
		
	
		
			
				
					|  |  |  |             activity.SymbolProfile.currency === currency && | 
			
		
	
		
			
				
					|  |  |  |             activity.SymbolProfile.dataSource === dataSource && | 
			
		
	
		
			
				
					|  |  |  |             isSameDay(activity.date, date) && | 
			
		
	
		
			
				
					|  |  |  |             activity.fee === fee && | 
			
		
	
		
			
				
					|  |  |  |             activity.quantity === quantity && | 
			
		
	
		
			
				
					|  |  |  |             activity.SymbolProfile.symbol === symbol && | 
			
		
	
		
			
				
					|  |  |  |             activity.type === type && | 
			
		
	
		
			
				
					|  |  |  |             activity.unitPrice === unitPrice | 
			
		
	
		
			
				
					|  |  |  |           ); | 
			
		
	
		
			
				
					|  |  |  |         }); | 
			
		
	
		
			
				
					|  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |         return { | 
			
		
	
		
			
				
					|  |  |  |           accountId, | 
			
		
	
		
			
				
					|  |  |  |           comment, | 
			
		
	
		
			
				
					|  |  |  |           date, | 
			
		
	
		
			
				
					|  |  |  |           fee, | 
			
		
	
		
			
				
					|  |  |  |           isDuplicate, | 
			
		
	
		
			
				
					|  |  |  |           quantity, | 
			
		
	
		
			
				
					|  |  |  |           type, | 
			
		
	
		
			
				
					|  |  |  |           unitPrice, | 
			
		
	
		
			
				
					|  |  |  |           SymbolProfile: { | 
			
		
	
		
			
				
					|  |  |  |             currency, | 
			
		
	
		
			
				
					|  |  |  |             dataSource, | 
			
		
	
		
			
				
					|  |  |  |             symbol, | 
			
		
	
		
			
				
					|  |  |  |             assetClass: null, | 
			
		
	
		
			
				
					|  |  |  |             assetSubClass: null, | 
			
		
	
		
			
				
					|  |  |  |             comment: null, | 
			
		
	
		
			
				
					|  |  |  |             countries: null, | 
			
		
	
		
			
				
					|  |  |  |             createdAt: undefined, | 
			
		
	
		
			
				
					|  |  |  |             id: undefined, | 
			
		
	
		
			
				
					|  |  |  |             isin: null, | 
			
		
	
		
			
				
					|  |  |  |             name: null, | 
			
		
	
		
			
				
					|  |  |  |             scraperConfiguration: null, | 
			
		
	
		
			
				
					|  |  |  |             sectors: null, | 
			
		
	
		
			
				
					|  |  |  |             symbolMapping: null, | 
			
		
	
		
			
				
					|  |  |  |             updatedAt: undefined, | 
			
		
	
		
			
				
					|  |  |  |             url: null | 
			
		
	
		
			
				
					|  |  |  |           } | 
			
		
	
		
			
				
					|  |  |  |         }; | 
			
		
	
		
			
				
					|  |  |  |       } | 
			
		
	
		
			
				
					|  |  |  |     ); | 
			
		
	
		
			
				
					|  |  |  |   } | 
			
		
	
		
			
				
					|  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |   private async validateActivities({ | 
			
		
	
		
			
				
					|  |  |  |     activitiesDto, | 
			
		
	
		
			
				
					|  |  |  |     maxActivitiesToImport, | 
			
		
	
	
		
			
				
					|  |  | @ -356,33 +437,11 @@ export class ImportService { | 
			
		
	
		
			
				
					|  |  |  |     const assetProfiles: { | 
			
		
	
		
			
				
					|  |  |  |       [symbol: string]: Partial<SymbolProfile>; | 
			
		
	
		
			
				
					|  |  |  |     } = {}; | 
			
		
	
		
			
				
					|  |  |  |     const existingActivities = await this.orderService.orders({ | 
			
		
	
		
			
				
					|  |  |  |       include: { SymbolProfile: true }, | 
			
		
	
		
			
				
					|  |  |  |       orderBy: { date: 'desc' }, | 
			
		
	
		
			
				
					|  |  |  |       where: { userId } | 
			
		
	
		
			
				
					|  |  |  |     }); | 
			
		
	
		
			
				
					|  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |     for (const [ | 
			
		
	
		
			
				
					|  |  |  |       index, | 
			
		
	
		
			
				
					|  |  |  |       { currency, dataSource, date, fee, quantity, symbol, type, unitPrice } | 
			
		
	
		
			
				
					|  |  |  |       { currency, dataSource, symbol } | 
			
		
	
		
			
				
					|  |  |  |     ] of activitiesDto.entries()) { | 
			
		
	
		
			
				
					|  |  |  |       const duplicateActivity = existingActivities.find((activity) => { | 
			
		
	
		
			
				
					|  |  |  |         return ( | 
			
		
	
		
			
				
					|  |  |  |           activity.SymbolProfile.currency === currency && | 
			
		
	
		
			
				
					|  |  |  |           activity.SymbolProfile.dataSource === dataSource && | 
			
		
	
		
			
				
					|  |  |  |           isSameDay(activity.date, parseISO(<string>(<unknown>date))) && | 
			
		
	
		
			
				
					|  |  |  |           activity.fee === fee && | 
			
		
	
		
			
				
					|  |  |  |           activity.quantity === quantity && | 
			
		
	
		
			
				
					|  |  |  |           activity.SymbolProfile.symbol === symbol && | 
			
		
	
		
			
				
					|  |  |  |           activity.type === type && | 
			
		
	
		
			
				
					|  |  |  |           activity.unitPrice === unitPrice | 
			
		
	
		
			
				
					|  |  |  |         ); | 
			
		
	
		
			
				
					|  |  |  |       }); | 
			
		
	
		
			
				
					|  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |       if (duplicateActivity) { | 
			
		
	
		
			
				
					|  |  |  |         throw new Error(`activities.${index} is a duplicate activity`); | 
			
		
	
		
			
				
					|  |  |  |       } | 
			
		
	
		
			
				
					|  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |       if (dataSource !== 'MANUAL') { | 
			
		
	
		
			
				
					|  |  |  |         const assetProfile = ( | 
			
		
	
		
			
				
					|  |  |  |           await this.dataProviderService.getAssetProfiles([ | 
			
		
	
	
		
			
				
					|  |  | 
 |