From 7613b5451429b0162a8afb786be4d73d95a6a2ad Mon Sep 17 00:00:00 2001 From: Attila Cseh <77381875+csehatt741@users.noreply.github.com> Date: Thu, 7 Aug 2025 19:23:40 +0200 Subject: [PATCH] Feature/extend activities import by tags (#5287) * Extend activities import by tags * Update changelog --- CHANGELOG.md | 6 ++ .../src/app/endpoints/tags/create-tag.dto.ts | 4 + apps/api/src/app/import/import-data.dto.ts | 7 ++ apps/api/src/app/import/import.controller.ts | 1 + apps/api/src/app/import/import.module.ts | 2 + apps/api/src/app/import/import.service.ts | 96 ++++++++++++++++++- apps/api/src/app/order/create-order.dto.ts | 10 +- .../order/interfaces/activities.interface.ts | 1 + apps/api/src/app/order/order.controller.ts | 6 ++ apps/api/src/app/order/order.service.ts | 12 +-- apps/api/src/app/order/update-order.dto.ts | 10 +- .../roai/portfolio-calculator-btceur.spec.ts | 4 + .../roai/portfolio-calculator-btcusd.spec.ts | 4 + ...ulator-novn-buy-and-sell-partially.spec.ts | 4 + ...folio-calculator-novn-buy-and-sell.spec.ts | 4 + ...ate-or-update-activity-dialog.component.ts | 4 +- .../import-activities-dialog.component.ts | 9 +- .../app/services/import-activities.service.ts | 26 ++++- 18 files changed, 173 insertions(+), 37 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 838d5350e..5f0267a53 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,12 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## Unreleased + +### Changed + +- Extended the import functionality by tags + ## 2.189.0 - 2025-08-05 ### Changed diff --git a/apps/api/src/app/endpoints/tags/create-tag.dto.ts b/apps/api/src/app/endpoints/tags/create-tag.dto.ts index 09c29d25a..b41c37a50 100644 --- a/apps/api/src/app/endpoints/tags/create-tag.dto.ts +++ b/apps/api/src/app/endpoints/tags/create-tag.dto.ts @@ -1,6 +1,10 @@ import { IsOptional, IsString } from 'class-validator'; export class CreateTagDto { + @IsOptional() + @IsString() + id?: string; + @IsString() name: string; diff --git a/apps/api/src/app/import/import-data.dto.ts b/apps/api/src/app/import/import-data.dto.ts index 138d16961..330bc52f6 100644 --- a/apps/api/src/app/import/import-data.dto.ts +++ b/apps/api/src/app/import/import-data.dto.ts @@ -3,6 +3,7 @@ import { CreateOrderDto } from '@ghostfolio/api/app/order/create-order.dto'; import { Type } from 'class-transformer'; import { IsArray, IsOptional, ValidateNested } from 'class-validator'; +import { CreateTagDto } from '../endpoints/tags/create-tag.dto'; import { CreateAccountWithBalancesDto } from './create-account-with-balances.dto'; import { CreateAssetProfileWithMarketDataDto } from './create-asset-profile-with-market-data.dto'; @@ -23,4 +24,10 @@ export class ImportDataDto { @Type(() => CreateAssetProfileWithMarketDataDto) @ValidateNested({ each: true }) assetProfiles?: CreateAssetProfileWithMarketDataDto[]; + + @IsArray() + @IsOptional() + @Type(() => CreateTagDto) + @ValidateNested({ each: true }) + tags?: CreateTagDto[]; } diff --git a/apps/api/src/app/import/import.controller.ts b/apps/api/src/app/import/import.controller.ts index 8c06bdc6d..2681444df 100644 --- a/apps/api/src/app/import/import.controller.ts +++ b/apps/api/src/app/import/import.controller.ts @@ -74,6 +74,7 @@ export class ImportController { accountsWithBalancesDto: importData.accounts ?? [], activitiesDto: importData.activities, assetProfilesWithMarketDataDto: importData.assetProfiles ?? [], + tagsDto: importData.tags ?? [], user: this.request.user }); diff --git a/apps/api/src/app/import/import.module.ts b/apps/api/src/app/import/import.module.ts index fb6c29fc5..88990af2e 100644 --- a/apps/api/src/app/import/import.module.ts +++ b/apps/api/src/app/import/import.module.ts @@ -13,6 +13,7 @@ import { MarketDataModule } from '@ghostfolio/api/services/market-data/market-da import { PrismaModule } from '@ghostfolio/api/services/prisma/prisma.module'; import { DataGatheringModule } from '@ghostfolio/api/services/queues/data-gathering/data-gathering.module'; import { SymbolProfileModule } from '@ghostfolio/api/services/symbol-profile/symbol-profile.module'; +import { TagModule } from '@ghostfolio/api/services/tag/tag.module'; import { Module } from '@nestjs/common'; @@ -35,6 +36,7 @@ import { ImportService } from './import.service'; PrismaModule, RedisCacheModule, SymbolProfileModule, + TagModule, TransformDataSourceInRequestModule, TransformDataSourceInResponseModule ], diff --git a/apps/api/src/app/import/import.service.ts b/apps/api/src/app/import/import.service.ts index ef9d25f53..a9fe898b9 100644 --- a/apps/api/src/app/import/import.service.ts +++ b/apps/api/src/app/import/import.service.ts @@ -13,12 +13,14 @@ import { DataProviderService } from '@ghostfolio/api/services/data-provider/data import { MarketDataService } from '@ghostfolio/api/services/market-data/market-data.service'; import { DataGatheringService } from '@ghostfolio/api/services/queues/data-gathering/data-gathering.service'; import { SymbolProfileService } from '@ghostfolio/api/services/symbol-profile/symbol-profile.service'; +import { TagService } from '@ghostfolio/api/services/tag/tag.service'; import { DATA_GATHERING_QUEUE_PRIORITY_HIGH } from '@ghostfolio/common/config'; import { getAssetProfileIdentifier, parseDate } from '@ghostfolio/common/helper'; import { AssetProfileIdentifier } from '@ghostfolio/common/interfaces'; +import { hasPermission, permissions } from '@ghostfolio/common/permissions'; import { AccountWithPlatform, OrderWithAccount, @@ -46,7 +48,8 @@ export class ImportService { private readonly orderService: OrderService, private readonly platformService: PlatformService, private readonly portfolioService: PortfolioService, - private readonly symbolProfileService: SymbolProfileService + private readonly symbolProfileService: SymbolProfileService, + private readonly tagService: TagService ) {} public async getDividends({ @@ -154,6 +157,7 @@ export class ImportService { assetProfilesWithMarketDataDto, isDryRun = false, maxActivitiesToImport, + tagsDto, user }: { accountsWithBalancesDto: ImportDataDto['accounts']; @@ -161,10 +165,12 @@ export class ImportService { assetProfilesWithMarketDataDto: ImportDataDto['assetProfiles']; isDryRun?: boolean; maxActivitiesToImport: number; + tagsDto: ImportDataDto['tags']; user: UserWithSettings; }): Promise { const accountIdMapping: { [oldAccountId: string]: string } = {}; const assetProfileSymbolMapping: { [oldSymbol: string]: string } = {}; + const tagIdMapping: { [oldTagId: string]: string } = {}; const userCurrency = user.settings.settings.baseCurrency; if (!isDryRun && accountsWithBalancesDto?.length) { @@ -293,6 +299,50 @@ export class ImportService { } } + if (tagsDto?.length) { + const existingTagsOfUser = await this.tagService.getTagsForUser(user.id); + + const canCreateOwnTag = hasPermission( + user.permissions, + permissions.createOwnTag + ); + + for (const tag of tagsDto) { + const existingTagOfUser = existingTagsOfUser.find(({ id }) => { + return id === tag.id; + }); + + if (!existingTagOfUser || existingTagOfUser.userId !== null) { + if (!canCreateOwnTag) { + throw new Error( + `Insufficient permissions to create custom tag ("${tag.name}")` + ); + } + + if (!isDryRun) { + const existingTag = await this.tagService.getTag({ id: tag.id }); + let oldTagId: string; + + if (existingTag) { + oldTagId = tag.id; + delete tag.id; + } + + const tagObject: Prisma.TagCreateInput = { + ...tag, + user: { connect: { id: user.id } } + }; + + const newTag = await this.tagService.createTag(tagObject); + + if (existingTag && oldTagId) { + tagIdMapping[oldTagId] = newTag.id; + } + } + } + } + } + for (const activity of activitiesDto) { if (!activity.dataSource) { if (['FEE', 'INTEREST', 'LIABILITY'].includes(activity.type)) { @@ -313,6 +363,11 @@ export class ImportService { if (assetProfileSymbolMapping[activity.symbol]) { activity.symbol = assetProfileSymbolMapping[activity.symbol]; } + + // If a new tag is created, then update the tag ID in all activities + activity.tags = (activity.tags ?? []).map((tagId) => { + return tagIdMapping[tagId] ?? tagId; + }); } } @@ -340,6 +395,24 @@ export class ImportService { }); } + const tags = (await this.tagService.getTagsForUser(user.id)).map( + ({ id, name }) => { + return { id, name }; + } + ); + + if (isDryRun) { + tagsDto + .filter(({ id }) => { + return !tags.some(({ id: tagId }) => { + return tagId === id; + }); + }) + .forEach(({ id, name }) => { + tags.push({ id, name }); + }); + } + const activities: Activity[] = []; for (const activity of activitiesExtendedWithErrors) { @@ -351,6 +424,7 @@ export class ImportService { const fee = activity.fee; const quantity = activity.quantity; const SymbolProfile = activity.SymbolProfile; + const tagIds = activity.tagIds ?? []; const type = activity.type; const unitPrice = activity.unitPrice; @@ -388,11 +462,17 @@ export class ImportService { const validatedAccount = accounts.find(({ id }) => { return id === accountId; }); + const validatedTags = tags.filter(({ id: tagId }) => { + return tagIds.some((activityTagId) => { + return activityTagId === tagId; + }); + }); let order: | OrderWithAccount - | (Omit & { - Account?: { id: string; name: string }; + | (Omit & { + account?: { id: string; name: string }; + tags?: { id: string; name: string }[]; }); if (isDryRun) { @@ -404,7 +484,7 @@ export class ImportService { quantity, type, unitPrice, - Account: validatedAccount, + account: validatedAccount, accountId: validatedAccount?.id, accountUserId: undefined, createdAt: new Date(), @@ -436,6 +516,7 @@ export class ImportService { userId: dataSource === 'MANUAL' ? user.id : undefined }, symbolProfileId: undefined, + tags: validatedTags, updatedAt: new Date(), userId: user.id }; @@ -469,6 +550,9 @@ export class ImportService { } } }, + tags: validatedTags.map(({ id }) => { + return { id }; + }), updateAccountBalance: false, user: { connect: { id: user.id } }, userId: user.id @@ -546,6 +630,7 @@ export class ImportService { fee, quantity, symbol, + tags, type, unitPrice }) => { @@ -594,7 +679,8 @@ export class ImportService { isActive: true, sectors: undefined, updatedAt: undefined - } + }, + tagIds: tags }; } ); diff --git a/apps/api/src/app/order/create-order.dto.ts b/apps/api/src/app/order/create-order.dto.ts index c2b10fd81..af87fd93e 100644 --- a/apps/api/src/app/order/create-order.dto.ts +++ b/apps/api/src/app/order/create-order.dto.ts @@ -1,13 +1,7 @@ import { IsCurrencyCode } from '@ghostfolio/api/validators/is-currency-code'; import { IsAfter1970Constraint } from '@ghostfolio/common/validator-constraints/is-after-1970'; -import { - AssetClass, - AssetSubClass, - DataSource, - Tag, - Type -} from '@prisma/client'; +import { AssetClass, AssetSubClass, DataSource, Type } from '@prisma/client'; import { Transform, TransformFnParams } from 'class-transformer'; import { IsArray, @@ -70,7 +64,7 @@ export class CreateOrderDto { @IsArray() @IsOptional() - tags?: Tag[]; + tags?: string[]; @IsEnum(Type, { each: true }) type: Type; diff --git a/apps/api/src/app/order/interfaces/activities.interface.ts b/apps/api/src/app/order/interfaces/activities.interface.ts index 99dd87fea..01a5a60f0 100644 --- a/apps/api/src/app/order/interfaces/activities.interface.ts +++ b/apps/api/src/app/order/interfaces/activities.interface.ts @@ -14,6 +14,7 @@ export interface Activity extends Order { feeInAssetProfileCurrency: number; feeInBaseCurrency: number; SymbolProfile?: EnhancedSymbolProfile; + tagIds?: string[]; tags?: Tag[]; unitPriceInAssetProfileCurrency: number; updateAccountBalance?: boolean; diff --git a/apps/api/src/app/order/order.controller.ts b/apps/api/src/app/order/order.controller.ts index 9bd45050e..ffed8ac2e 100644 --- a/apps/api/src/app/order/order.controller.ts +++ b/apps/api/src/app/order/order.controller.ts @@ -217,6 +217,9 @@ export class OrderController { } } }, + tags: data.tags?.map((id) => { + return { id }; + }), user: { connect: { id: this.request.user.id } }, userId: this.request.user.id }); @@ -293,6 +296,9 @@ export class OrderController { name: data.symbol } }, + tags: data.tags?.map((id) => { + return { id }; + }), user: { connect: { id: this.request.user.id } } }, where: { diff --git a/apps/api/src/app/order/order.service.ts b/apps/api/src/app/order/order.service.ts index e9d72233e..fd50f5e74 100644 --- a/apps/api/src/app/order/order.service.ts +++ b/apps/api/src/app/order/order.service.ts @@ -97,7 +97,7 @@ export class OrderService { assetSubClass?: AssetSubClass; currency?: string; symbol?: string; - tags?: Tag[]; + tags?: { id: string }[]; updateAccountBalance?: boolean; userId: string; } @@ -201,9 +201,7 @@ export class OrderService { account, isDraft, tags: { - connect: tags.map(({ id }) => { - return { id }; - }) + connect: tags } }, include: { SymbolProfile: true } @@ -658,7 +656,7 @@ export class OrderService { assetSubClass?: AssetSubClass; currency?: string; symbol?: string; - tags?: Tag[]; + tags?: { id: string }[]; type?: ActivityType; }; where: Prisma.OrderWhereUniqueInput; @@ -720,9 +718,7 @@ export class OrderService { ...data, isDraft, tags: { - connect: tags.map(({ id }) => { - return { id }; - }) + connect: tags } } }); diff --git a/apps/api/src/app/order/update-order.dto.ts b/apps/api/src/app/order/update-order.dto.ts index eabd1f418..25c92532a 100644 --- a/apps/api/src/app/order/update-order.dto.ts +++ b/apps/api/src/app/order/update-order.dto.ts @@ -1,13 +1,7 @@ import { IsCurrencyCode } from '@ghostfolio/api/validators/is-currency-code'; import { IsAfter1970Constraint } from '@ghostfolio/common/validator-constraints/is-after-1970'; -import { - AssetClass, - AssetSubClass, - DataSource, - Tag, - Type -} from '@prisma/client'; +import { AssetClass, AssetSubClass, DataSource, Type } from '@prisma/client'; import { Transform, TransformFnParams } from 'class-transformer'; import { IsArray, @@ -71,7 +65,7 @@ export class UpdateOrderDto { @IsArray() @IsOptional() - tags?: Tag[]; + tags?: string[]; @IsString() type: Type; diff --git a/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-btceur.spec.ts b/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-btceur.spec.ts index 3594b4427..ad64cb383 100644 --- a/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-btceur.spec.ts +++ b/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-btceur.spec.ts @@ -18,6 +18,7 @@ import { PortfolioSnapshotServiceMock } from '@ghostfolio/api/services/queues/po import { parseDate } from '@ghostfolio/common/helper'; import { PerformanceCalculationType } from '@ghostfolio/common/types/performance-calculation-type.type'; +import { Tag } from '@prisma/client'; import { Big } from 'big.js'; import { join } from 'path'; @@ -108,6 +109,9 @@ describe('PortfolioCalculator', () => { name: 'Bitcoin', symbol: activity.symbol }, + tags: activity.tags?.map((id) => { + return { id } as Tag; + }), unitPriceInAssetProfileCurrency: 44558.42 })); diff --git a/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-btcusd.spec.ts b/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-btcusd.spec.ts index d1d9fbd93..d17fd028a 100644 --- a/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-btcusd.spec.ts +++ b/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-btcusd.spec.ts @@ -18,6 +18,7 @@ import { PortfolioSnapshotServiceMock } from '@ghostfolio/api/services/queues/po import { parseDate } from '@ghostfolio/common/helper'; import { PerformanceCalculationType } from '@ghostfolio/common/types/performance-calculation-type.type'; +import { Tag } from '@prisma/client'; import { Big } from 'big.js'; import { join } from 'path'; @@ -108,6 +109,9 @@ describe('PortfolioCalculator', () => { name: 'Bitcoin', symbol: activity.symbol }, + tags: activity.tags?.map((id) => { + return { id } as Tag; + }), unitPriceInAssetProfileCurrency: 44558.42 })); diff --git a/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-novn-buy-and-sell-partially.spec.ts b/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-novn-buy-and-sell-partially.spec.ts index 96bbbc757..18455477e 100644 --- a/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-novn-buy-and-sell-partially.spec.ts +++ b/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-novn-buy-and-sell-partially.spec.ts @@ -18,6 +18,7 @@ import { PortfolioSnapshotServiceMock } from '@ghostfolio/api/services/queues/po import { parseDate } from '@ghostfolio/common/helper'; import { PerformanceCalculationType } from '@ghostfolio/common/types/performance-calculation-type.type'; +import { Tag } from '@prisma/client'; import { Big } from 'big.js'; import { join } from 'path'; @@ -111,6 +112,9 @@ describe('PortfolioCalculator', () => { name: 'Novartis AG', symbol: activity.symbol }, + tags: activity.tags?.map((id) => { + return { id } as Tag; + }), unitPriceInAssetProfileCurrency: activity.unitPrice })); diff --git a/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-novn-buy-and-sell.spec.ts b/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-novn-buy-and-sell.spec.ts index c19f6917e..e20400cb7 100644 --- a/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-novn-buy-and-sell.spec.ts +++ b/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-novn-buy-and-sell.spec.ts @@ -18,6 +18,7 @@ import { PortfolioSnapshotServiceMock } from '@ghostfolio/api/services/queues/po import { parseDate } from '@ghostfolio/common/helper'; import { PerformanceCalculationType } from '@ghostfolio/common/types/performance-calculation-type.type'; +import { Tag } from '@prisma/client'; import { Big } from 'big.js'; import { join } from 'path'; @@ -111,6 +112,9 @@ describe('PortfolioCalculator', () => { name: 'Novartis AG', symbol: activity.symbol }, + tags: activity.tags?.map((id) => { + return { id } as Tag; + }), unitPriceInAssetProfileCurrency: activity.unitPrice })); diff --git a/apps/client/src/app/pages/portfolio/activities/create-or-update-activity-dialog/create-or-update-activity-dialog.component.ts b/apps/client/src/app/pages/portfolio/activities/create-or-update-activity-dialog/create-or-update-activity-dialog.component.ts index 656e4a88f..f2da26246 100644 --- a/apps/client/src/app/pages/portfolio/activities/create-or-update-activity-dialog/create-or-update-activity-dialog.component.ts +++ b/apps/client/src/app/pages/portfolio/activities/create-or-update-activity-dialog/create-or-update-activity-dialog.component.ts @@ -495,7 +495,9 @@ export class GfCreateOrUpdateActivityDialog implements OnDestroy { ? undefined : this.activityForm.get('searchSymbol')?.value?.symbol) ?? this.activityForm.get('name')?.value, - tags: this.activityForm.get('tags').value, + tags: this.activityForm.get('tags').value?.map(({ id }) => { + return id; + }), type: this.activityForm.get('type').value, unitPrice: this.activityForm.get('unitPrice').value }; diff --git a/apps/client/src/app/pages/portfolio/activities/import-activities-dialog/import-activities-dialog.component.ts b/apps/client/src/app/pages/portfolio/activities/import-activities-dialog/import-activities-dialog.component.ts index 660e7265e..41e366253 100644 --- a/apps/client/src/app/pages/portfolio/activities/import-activities-dialog/import-activities-dialog.component.ts +++ b/apps/client/src/app/pages/portfolio/activities/import-activities-dialog/import-activities-dialog.component.ts @@ -1,3 +1,4 @@ +import { CreateTagDto } from '@ghostfolio/api/app/endpoints/tags/create-tag.dto'; import { CreateAccountWithBalancesDto } from '@ghostfolio/api/app/import/create-account-with-balances.dto'; import { CreateAssetProfileWithMarketDataDto } from '@ghostfolio/api/app/import/create-asset-profile-with-market-data.dto'; import { Activity } from '@ghostfolio/api/app/order/interfaces/activities.interface'; @@ -94,6 +95,7 @@ export class GfImportActivitiesDialog implements OnDestroy { public sortColumn = 'date'; public sortDirection: SortDirection = 'desc'; public stepperOrientation: StepperOrientation; + public tags: CreateTagDto[] = []; public totalItems: number; private unsubscribeSubject = new Subject(); @@ -169,7 +171,8 @@ export class GfImportActivitiesDialog implements OnDestroy { await this.importActivitiesService.importSelectedActivities({ accounts: this.accounts, activities: this.selectedActivities, - assetProfiles: this.assetProfiles + assetProfiles: this.assetProfiles, + tags: this.tags }); this.snackBar.open( @@ -297,6 +300,7 @@ export class GfImportActivitiesDialog implements OnDestroy { this.accounts = content.accounts; this.assetProfiles = content.assetProfiles; + this.tags = content.tags; if (!isArray(content.activities)) { if (isArray(content.orders)) { @@ -328,7 +332,8 @@ export class GfImportActivitiesDialog implements OnDestroy { accounts: content.accounts, activities: content.activities, assetProfiles: content.assetProfiles, - isDryRun: true + isDryRun: true, + tags: content.tags }); this.activities = activities; this.dataSource = new MatTableDataSource(activities.reverse()); diff --git a/apps/client/src/app/services/import-activities.service.ts b/apps/client/src/app/services/import-activities.service.ts index 033ae7e24..27a34652b 100644 --- a/apps/client/src/app/services/import-activities.service.ts +++ b/apps/client/src/app/services/import-activities.service.ts @@ -1,3 +1,4 @@ +import { CreateTagDto } from '@ghostfolio/api/app/endpoints/tags/create-tag.dto'; import { CreateAccountWithBalancesDto } from '@ghostfolio/api/app/import/create-account-with-balances.dto'; import { CreateAssetProfileWithMarketDataDto } from '@ghostfolio/api/app/import/create-asset-profile-with-market-data.dto'; import { CreateOrderDto } from '@ghostfolio/api/app/order/create-order.dto'; @@ -75,12 +76,14 @@ export class ImportActivitiesService { accounts, activities, assetProfiles, - isDryRun = false + isDryRun = false, + tags }: { activities: CreateOrderDto[]; accounts?: CreateAccountWithBalancesDto[]; assetProfiles?: CreateAssetProfileWithMarketDataDto[]; isDryRun?: boolean; + tags?: CreateTagDto[]; }): Promise<{ activities: Activity[]; }> { @@ -89,7 +92,8 @@ export class ImportActivitiesService { { accounts, activities, - assetProfiles + assetProfiles, + tags }, isDryRun ) @@ -110,11 +114,13 @@ export class ImportActivitiesService { public importSelectedActivities({ accounts, activities, - assetProfiles + assetProfiles, + tags }: { accounts?: CreateAccountWithBalancesDto[]; activities: Activity[]; assetProfiles?: CreateAssetProfileWithMarketDataDto[]; + tags?: CreateTagDto[]; }): Promise<{ activities: Activity[]; }> { @@ -124,7 +130,12 @@ export class ImportActivitiesService { importData.push(this.convertToCreateOrderDto(activity)); } - return this.importJson({ accounts, assetProfiles, activities: importData }); + return this.importJson({ + accounts, + assetProfiles, + tags, + activities: importData + }); } private convertToCreateOrderDto({ @@ -135,6 +146,7 @@ export class ImportActivitiesService { fee, quantity, SymbolProfile, + tags, type, unitPrice, updateAccountBalance @@ -150,7 +162,10 @@ export class ImportActivitiesService { currency: currency ?? SymbolProfile.currency, dataSource: SymbolProfile.dataSource, date: date.toString(), - symbol: SymbolProfile.symbol + symbol: SymbolProfile.symbol, + tags: tags?.map(({ id }) => { + return id; + }) }; } @@ -391,6 +406,7 @@ export class ImportActivitiesService { accounts?: CreateAccountWithBalancesDto[]; activities: CreateOrderDto[]; assetProfiles?: CreateAssetProfileWithMarketDataDto[]; + tags?: CreateTagDto[]; }, aIsDryRun = false ) {