+
Date
@@ -11,7 +11,7 @@
[matDatepicker]="date"
[(ngModel)]="data.date"
/>
-
+
-
diff --git a/apps/client/src/app/components/admin-market-data-detail/market-data-detail-dialog/market-data-detail-dialog.module.ts b/apps/client/src/app/components/admin-market-data-detail/market-data-detail-dialog/market-data-detail-dialog.module.ts
index 14aa2199e..b46158505 100644
--- a/apps/client/src/app/components/admin-market-data-detail/market-data-detail-dialog/market-data-detail-dialog.module.ts
+++ b/apps/client/src/app/components/admin-market-data-detail/market-data-detail-dialog/market-data-detail-dialog.module.ts
@@ -2,10 +2,10 @@ import { CommonModule } from '@angular/common';
import { CUSTOM_ELEMENTS_SCHEMA, NgModule } from '@angular/core';
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import { MatDatepickerModule } from '@angular/material/datepicker';
-import { MatLegacyButtonModule as MatButtonModule } from '@angular/material/legacy-button';
+import { MatButtonModule } from '@angular/material/button';
import { MatLegacyDialogModule as MatDialogModule } from '@angular/material/legacy-dialog';
-import { MatLegacyFormFieldModule as MatFormFieldModule } from '@angular/material/legacy-form-field';
-import { MatLegacyInputModule as MatInputModule } from '@angular/material/legacy-input';
+import { MatFormFieldModule } from '@angular/material/form-field';
+import { MatInputModule } from '@angular/material/input';
import { MarketDataDetailDialog } from './market-data-detail-dialog.component';
diff --git a/apps/client/src/app/components/admin-market-data-detail/market-data-detail-dialog/market-data-detail-dialog.scss b/apps/client/src/app/components/admin-market-data-detail/market-data-detail-dialog/market-data-detail-dialog.scss
index 91922978c..c94c65c54 100644
--- a/apps/client/src/app/components/admin-market-data-detail/market-data-detail-dialog/market-data-detail-dialog.scss
+++ b/apps/client/src/app/components/admin-market-data-detail/market-data-detail-dialog/market-data-detail-dialog.scss
@@ -4,19 +4,9 @@
.mat-dialog-content {
max-height: unset;
- .mat-form-field-appearance-outline {
- ::ng-deep {
- .mat-form-field-suffix {
- top: -0.3rem;
- }
-
- .mat-form-field-wrapper {
- padding-bottom: 0;
- }
- }
-
- ion-icon {
- font-size: 130%;
+ .mat-mdc-button {
+ &.apply-current-market-price {
+ height: 56px;
}
}
}
diff --git a/apps/client/src/app/pages/portfolio/activities/activities-page.html b/apps/client/src/app/pages/portfolio/activities/activities-page.html
index cec456c53..d958598c1 100644
--- a/apps/client/src/app/pages/portfolio/activities/activities-page.html
+++ b/apps/client/src/app/pages/portfolio/activities/activities-page.html
@@ -33,7 +33,7 @@
[queryParams]="{ createDialog: true }"
[routerLink]="[]"
>
-
+
diff --git a/apps/client/src/app/pages/portfolio/activities/activities-page.module.ts b/apps/client/src/app/pages/portfolio/activities/activities-page.module.ts
index 1f69f60f3..28d4437a9 100644
--- a/apps/client/src/app/pages/portfolio/activities/activities-page.module.ts
+++ b/apps/client/src/app/pages/portfolio/activities/activities-page.module.ts
@@ -1,7 +1,7 @@
import { CommonModule } from '@angular/common';
import { CUSTOM_ELEMENTS_SCHEMA, NgModule } from '@angular/core';
-import { MatLegacyButtonModule as MatButtonModule } from '@angular/material/legacy-button';
-import { MatLegacySnackBarModule as MatSnackBarModule } from '@angular/material/legacy-snack-bar';
+import { MatButtonModule } from '@angular/material/button';
+import { MatSnackBarModule } from '@angular/material/snack-bar';
import { RouterModule } from '@angular/router';
import { ImportActivitiesService } from '@ghostfolio/client/services/import-activities.service';
import { GfActivitiesTableModule } from '@ghostfolio/ui/activities-table/activities-table.module';
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 cbe740adc..0fa6ae580 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
@@ -10,7 +10,7 @@ import {
} from '@angular/core';
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
import { DateAdapter, MAT_DATE_LOCALE } from '@angular/material/core';
-import { MatLegacyAutocompleteSelectedEvent as MatAutocompleteSelectedEvent } from '@angular/material/legacy-autocomplete';
+import { MatAutocompleteSelectedEvent } from '@angular/material/autocomplete';
import {
MAT_LEGACY_DIALOG_DATA as MAT_DIALOG_DATA,
MatLegacyDialogRef as MatDialogRef
diff --git a/apps/client/src/app/pages/portfolio/activities/create-or-update-activity-dialog/create-or-update-activity-dialog.html b/apps/client/src/app/pages/portfolio/activities/create-or-update-activity-dialog/create-or-update-activity-dialog.html
index e5b215fbc..1b46ecf69 100644
--- a/apps/client/src/app/pages/portfolio/activities/create-or-update-activity-dialog/create-or-update-activity-dialog.html
+++ b/apps/client/src/app/pages/portfolio/activities/create-or-update-activity-dialog/create-or-update-activity-dialog.html
@@ -6,7 +6,7 @@
>
Update activity
Add activity
-
+
Type
@@ -76,7 +76,7 @@
Currency
-
+
{{ currency }}
@@ -93,7 +93,7 @@
Date
-
+
-
+
@@ -121,20 +121,20 @@
- {{ activityForm.controls['currency'].value }}
-
+
@@ -142,7 +142,7 @@
@@ -157,7 +157,7 @@
Fee
- {{ activityForm.controls['currency'].value }}
@@ -207,8 +207,8 @@
Tags
-
-
+
{{ tag.name }}
-
+
-
+
Date: Wed, 25 Jan 2023 12:12:11 +0530
Subject: [PATCH 02/12] Add accounts to activities export
---
apps/api/src/app/export/export.service.ts | 14 ++++++++++++++
libs/common/src/lib/interfaces/export.interface.ts | 6 +++++-
2 files changed, 19 insertions(+), 1 deletion(-)
diff --git a/apps/api/src/app/export/export.service.ts b/apps/api/src/app/export/export.service.ts
index c4655e7d8..ac6a861be 100644
--- a/apps/api/src/app/export/export.service.ts
+++ b/apps/api/src/app/export/export.service.ts
@@ -14,6 +14,19 @@ export class ExportService {
activityIds?: string[];
userId: string;
}): Promise {
+ const accounts = await this.prismaService.account.findMany({
+ select: {
+ accountType: true,
+ balance: true,
+ currency: true,
+ id: true,
+ isDefault: true,
+ isExcluded: true,
+ name: true
+ },
+ where: { userId }
+ });
+
let activities = await this.prismaService.order.findMany({
orderBy: { date: 'desc' },
select: {
@@ -38,6 +51,7 @@ export class ExportService {
return {
meta: { date: new Date().toISOString(), version: environment.version },
+ accounts,
activities: activities.map(
({
accountId,
diff --git a/libs/common/src/lib/interfaces/export.interface.ts b/libs/common/src/lib/interfaces/export.interface.ts
index 37dbfba79..cd47a8f0b 100644
--- a/libs/common/src/lib/interfaces/export.interface.ts
+++ b/libs/common/src/lib/interfaces/export.interface.ts
@@ -1,10 +1,14 @@
-import { Order } from '@prisma/client';
+import { Order, Account } from '@prisma/client';
export interface Export {
meta: {
date: string;
version: string;
};
+ accounts: Omit<
+ Account,
+ 'createdAt' | 'platformId' | 'updatedAt' | 'userId'
+ >[];
activities: (Omit<
Order,
| 'accountUserId'
From 4469114bb862bc0554a2d86bd22b5adc7d958dcb Mon Sep 17 00:00:00 2001
From: yksolanki9
Date: Wed, 25 Jan 2023 12:59:49 +0530
Subject: [PATCH 03/12] Export platformId with account details
---
apps/api/src/app/export/export.service.ts | 3 ++-
libs/common/src/lib/interfaces/export.interface.ts | 5 +----
2 files changed, 3 insertions(+), 5 deletions(-)
diff --git a/apps/api/src/app/export/export.service.ts b/apps/api/src/app/export/export.service.ts
index ac6a861be..ebf0845cc 100644
--- a/apps/api/src/app/export/export.service.ts
+++ b/apps/api/src/app/export/export.service.ts
@@ -22,7 +22,8 @@ export class ExportService {
id: true,
isDefault: true,
isExcluded: true,
- name: true
+ name: true,
+ platformId: true
},
where: { userId }
});
diff --git a/libs/common/src/lib/interfaces/export.interface.ts b/libs/common/src/lib/interfaces/export.interface.ts
index cd47a8f0b..7b7470717 100644
--- a/libs/common/src/lib/interfaces/export.interface.ts
+++ b/libs/common/src/lib/interfaces/export.interface.ts
@@ -5,10 +5,7 @@ export interface Export {
date: string;
version: string;
};
- accounts: Omit<
- Account,
- 'createdAt' | 'platformId' | 'updatedAt' | 'userId'
- >[];
+ accounts: Omit[];
activities: (Omit<
Order,
| 'accountUserId'
From 3e9df51bf751e75dfb00cc16fda96c91568c2937 Mon Sep 17 00:00:00 2001
From: yksolanki9
Date: Wed, 25 Jan 2023 20:32:12 +0530
Subject: [PATCH 04/12] Add logic for importing accounts
---
.../api/src/app/account/create-account.dto.ts | 8 ++
apps/api/src/app/import/import-data.dto.ts | 9 ++-
apps/api/src/app/import/import.controller.ts | 11 ++-
apps/api/src/app/import/import.service.ts | 73 +++++++++++++++++++
.../import-activities-dialog.component.ts | 19 +++--
.../app/services/import-activities.service.ts | 43 +++++++----
6 files changed, 142 insertions(+), 21 deletions(-)
diff --git a/apps/api/src/app/account/create-account.dto.ts b/apps/api/src/app/account/create-account.dto.ts
index 3ea13e20a..3c32163e0 100644
--- a/apps/api/src/app/account/create-account.dto.ts
+++ b/apps/api/src/app/account/create-account.dto.ts
@@ -8,6 +8,14 @@ import {
} from 'class-validator';
export class CreateAccountDto {
+ @IsOptional()
+ @IsString()
+ id: string;
+
+ @IsOptional()
+ @IsBoolean()
+ isDefault: boolean;
+
@IsString()
accountType: AccountType;
diff --git a/apps/api/src/app/import/import-data.dto.ts b/apps/api/src/app/import/import-data.dto.ts
index f3a0ba8fe..aa141aab5 100644
--- a/apps/api/src/app/import/import-data.dto.ts
+++ b/apps/api/src/app/import/import-data.dto.ts
@@ -1,8 +1,15 @@
import { CreateOrderDto } from '@ghostfolio/api/app/order/create-order.dto';
import { Type } from 'class-transformer';
-import { IsArray, ValidateNested } from 'class-validator';
+import { IsArray, ValidateNested, IsOptional } from 'class-validator';
+import { CreateAccountDto } from '../account/create-account.dto';
export class ImportDataDto {
+ @IsOptional()
+ @IsArray()
+ @Type(() => CreateAccountDto)
+ @ValidateNested({ each: true })
+ accounts: CreateAccountDto[];
+
@IsArray()
@Type(() => CreateOrderDto)
@ValidateNested({ each: true })
diff --git a/apps/api/src/app/import/import.controller.ts b/apps/api/src/app/import/import.controller.ts
index 2591ab638..a1a1d4b1d 100644
--- a/apps/api/src/app/import/import.controller.ts
+++ b/apps/api/src/app/import/import.controller.ts
@@ -2,6 +2,7 @@ import { TransformDataSourceInRequestInterceptor } from '@ghostfolio/api/interce
import { TransformDataSourceInResponseInterceptor } from '@ghostfolio/api/interceptors/transform-data-source-in-response.interceptor';
import { ConfigurationService } from '@ghostfolio/api/services/configuration.service';
import { ImportResponse } from '@ghostfolio/common/interfaces';
+import { hasPermission, permissions } from '@ghostfolio/common/permissions';
import type { RequestWithUser } from '@ghostfolio/common/types';
import {
Body,
@@ -38,7 +39,14 @@ export class ImportController {
@Body() importData: ImportDataDto,
@Query('dryRun') isDryRun?: boolean
): Promise {
- if (!this.configurationService.get('ENABLE_FEATURE_IMPORT')) {
+ if (
+ !this.configurationService.get('ENABLE_FEATURE_IMPORT') ||
+ (importData.accounts?.length > 0 &&
+ !hasPermission(
+ this.request.user.permissions,
+ permissions.createAccount
+ ))
+ ) {
throw new HttpException(
getReasonPhrase(StatusCodes.FORBIDDEN),
StatusCodes.FORBIDDEN
@@ -64,6 +72,7 @@ export class ImportController {
isDryRun,
userCurrency,
activitiesDto: importData.activities,
+ accountsDto: importData.accounts || [],
userId: this.request.user.id
});
diff --git a/apps/api/src/app/import/import.service.ts b/apps/api/src/app/import/import.service.ts
index d3be33bbc..2d09ca761 100644
--- a/apps/api/src/app/import/import.service.ts
+++ b/apps/api/src/app/import/import.service.ts
@@ -17,6 +17,7 @@ import { SymbolProfile } from '@prisma/client';
import Big from 'big.js';
import { endOfToday, isAfter, isSameDay, parseISO } from 'date-fns';
import { v4 as uuidv4 } from 'uuid';
+import { CreateAccountDto } from '../account/create-account.dto';
@Injectable()
export class ImportService {
@@ -101,17 +102,84 @@ export class ImportService {
public async import({
activitiesDto,
+ accountsDto,
isDryRun = false,
maxActivitiesToImport,
userCurrency,
userId
}: {
activitiesDto: Partial[];
+ accountsDto: Partial[];
isDryRun?: boolean;
maxActivitiesToImport: number;
userCurrency: string;
userId: string;
}): Promise {
+ const accountMappings = {};
+ //Validate accounts
+ if (accountsDto?.length) {
+ for (let account of accountsDto) {
+ const existingAccounts = await this.accountService.accounts({
+ where: { id: account.id }
+ });
+
+ const oldAccountId = account.id;
+ const platformId = account.platformId;
+
+ delete account.id;
+ delete account.platformId;
+ delete account.isDefault;
+
+ //If account id does not exist, then create a new one
+ if (existingAccounts.length === 0) {
+ const newAccountConfig = {
+ ...account,
+ User: { connect: { id: userId } }
+ };
+
+ if (platformId) {
+ Object.assign(newAccountConfig, {
+ Platform: { connect: { id: platformId } }
+ });
+ }
+
+ const newAccount = await this.accountService.createAccount(
+ newAccountConfig,
+ userId
+ );
+
+ accountMappings[oldAccountId] = newAccount.id;
+ continue;
+ }
+
+ //If account id is used, then check if it belongs to the same user
+ //Yes -> Merge the accounts and don't create a new one
+ if (existingAccounts[0].userId === userId) {
+ continue;
+ }
+
+ //No -> Replace the account id with a new account id as well as update all the activities when looping
+
+ const newAccountConfig = {
+ ...account,
+ User: { connect: { id: userId } }
+ };
+
+ if (platformId) {
+ Object.assign(newAccountConfig, {
+ Platform: { connect: { id: platformId } }
+ });
+ }
+
+ const newAccount = await this.accountService.createAccount(
+ newAccountConfig,
+ userId
+ );
+
+ accountMappings[oldAccountId] = newAccount.id;
+ }
+ }
+
for (const activity of activitiesDto) {
if (!activity.dataSource) {
if (activity.type === 'ITEM') {
@@ -120,6 +188,11 @@ export class ImportService {
activity.dataSource = this.dataProviderService.getPrimaryDataSource();
}
}
+
+ //If we updated the account id, then update the account id in the activity as well
+ if (Object.keys(accountMappings).includes(activity.accountId)) {
+ activity.accountId = accountMappings[activity.accountId];
+ }
}
const assetProfiles = await this.validateActivities({
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 b7c143c37..b5d7b0a83 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
@@ -11,6 +11,7 @@ import {
MatLegacyDialogRef as MatDialogRef
} from '@angular/material/legacy-dialog';
import { MatLegacySnackBar as MatSnackBar } from '@angular/material/legacy-snack-bar';
+import { CreateAccountDto } from '@ghostfolio/api/app/account/create-account.dto';
import { Activity } from '@ghostfolio/api/app/order/interfaces/activities.interface';
import { DataService } from '@ghostfolio/client/services/data.service';
import { ImportActivitiesService } from '@ghostfolio/client/services/import-activities.service';
@@ -28,6 +29,7 @@ import { ImportActivitiesDialogParams } from './interfaces/interfaces';
templateUrl: 'import-activities-dialog.html'
})
export class ImportActivitiesDialog implements OnDestroy {
+ public accounts: CreateAccountDto[] = [];
public activities: Activity[] = [];
public details: any[] = [];
public errorMessages: string[] = [];
@@ -91,9 +93,10 @@ export class ImportActivitiesDialog implements OnDestroy {
try {
this.snackBar.open('⏳ ' + $localize`Importing data...`);
- await this.importActivitiesService.importSelectedActivities(
- this.selectedActivities
- );
+ await this.importActivitiesService.importSelectedActivities({
+ accounts: this.accounts,
+ activities: this.selectedActivities
+ });
this.snackBar.open(
'✅ ' + $localize`Import has been completed`,
@@ -180,10 +183,13 @@ export class ImportActivitiesDialog implements OnDestroy {
}
try {
- this.activities = await this.importActivitiesService.importJson({
- content: content.activities,
+ const data = await this.importActivitiesService.importJson({
+ activities: content.activities,
+ accounts: content.accounts,
isDryRun: true
});
+ this.activities = data.activities;
+ this.accounts = data.accounts;
} catch (error) {
console.error(error);
this.handleImportError({ error, activities: content.activities });
@@ -192,11 +198,12 @@ export class ImportActivitiesDialog implements OnDestroy {
return;
} else if (file.name.endsWith('.csv')) {
try {
- this.activities = await this.importActivitiesService.importCsv({
+ const data = await this.importActivitiesService.importCsv({
fileContent,
isDryRun: true,
userAccounts: this.data.user.accounts
});
+ this.activities = data.activities;
} catch (error) {
console.error(error);
this.handleImportError({
diff --git a/apps/client/src/app/services/import-activities.service.ts b/apps/client/src/app/services/import-activities.service.ts
index 2e15f367f..c2b0bbe05 100644
--- a/apps/client/src/app/services/import-activities.service.ts
+++ b/apps/client/src/app/services/import-activities.service.ts
@@ -1,5 +1,6 @@
import { HttpClient } from '@angular/common/http';
import { Injectable } from '@angular/core';
+import { CreateAccountDto } from '@ghostfolio/api/app/account/create-account.dto';
import { CreateOrderDto } from '@ghostfolio/api/app/order/create-order.dto';
import { Activity } from '@ghostfolio/api/app/order/interfaces/activities.interface';
import { Account, DataSource, Type } from '@prisma/client';
@@ -33,7 +34,10 @@ export class ImportActivitiesService {
fileContent: string;
isDryRun?: boolean;
userAccounts: Account[];
- }): Promise {
+ }): Promise<{
+ accounts?: CreateAccountDto[];
+ activities: Activity[];
+ }> {
const content = csvToJson(fileContent, {
dynamicTyping: true,
header: true,
@@ -55,20 +59,26 @@ export class ImportActivitiesService {
});
}
- return await this.importJson({ isDryRun, content: activities });
+ return await this.importJson({ isDryRun, activities });
}
public importJson({
- content,
+ activities,
+ accounts,
isDryRun = false
}: {
- content: CreateOrderDto[];
+ activities: CreateOrderDto[];
+ accounts?: CreateAccountDto[];
isDryRun?: boolean;
- }): Promise {
+ }): Promise<{
+ accounts?: CreateAccountDto[];
+ activities: Activity[];
+ }> {
return new Promise((resolve, reject) => {
this.postImport(
{
- activities: content
+ activities,
+ accounts
},
isDryRun
)
@@ -80,22 +90,29 @@ export class ImportActivitiesService {
)
.subscribe({
next: (data) => {
- resolve(data.activities);
+ resolve(data);
}
});
});
}
- public importSelectedActivities(
- selectedActivities: Activity[]
- ): Promise {
+ public importSelectedActivities({
+ accounts,
+ activities
+ }: {
+ accounts?: CreateAccountDto[];
+ activities: Activity[];
+ }): Promise<{
+ accounts?: CreateAccountDto[];
+ activities: Activity[];
+ }> {
const importData: CreateOrderDto[] = [];
- for (const activity of selectedActivities) {
+ for (const activity of activities) {
importData.push(this.convertToCreateOrderDto(activity));
}
- return this.importJson({ content: importData });
+ return this.importJson({ activities: importData, accounts });
}
private convertToCreateOrderDto({
@@ -347,7 +364,7 @@ export class ImportActivitiesService {
}
private postImport(
- aImportData: { activities: CreateOrderDto[] },
+ aImportData: { activities: CreateOrderDto[]; accounts: CreateAccountDto[] },
aIsDryRun = false
) {
return this.http.post<{ activities: Activity[] }>(
From e104ae17f8a5918913883eac0c4b4cd9b70fa369 Mon Sep 17 00:00:00 2001
From: yksolanki9
Date: Wed, 25 Jan 2023 22:59:37 +0530
Subject: [PATCH 05/12] Refactoring and bug fixes
---
apps/api/src/app/account/account.service.ts | 7 ++
apps/api/src/app/import/import.service.ts | 76 +++++++------------
.../import-activities-dialog.component.ts | 3 +-
3 files changed, 37 insertions(+), 49 deletions(-)
diff --git a/apps/api/src/app/account/account.service.ts b/apps/api/src/app/account/account.service.ts
index 7c10fc31f..d144faa8b 100644
--- a/apps/api/src/app/account/account.service.ts
+++ b/apps/api/src/app/account/account.service.ts
@@ -104,6 +104,13 @@ export class AccountService {
});
}
+ public async getAccountById(accountId: string) {
+ const accounts = await this.accounts({
+ where: { id: accountId }
+ });
+ return accounts.length ? accounts[0] : null;
+ }
+
public async getCashDetails({
currency,
filters = [],
diff --git a/apps/api/src/app/import/import.service.ts b/apps/api/src/app/import/import.service.ts
index 2d09ca761..2e4f1044b 100644
--- a/apps/api/src/app/import/import.service.ts
+++ b/apps/api/src/app/import/import.service.ts
@@ -115,68 +115,48 @@ export class ImportService {
userCurrency: string;
userId: string;
}): Promise {
- const accountMappings = {};
- //Validate accounts
- if (accountsDto?.length) {
+ const accountIdMapping = {};
+
+ if (!isDryRun && accountsDto?.length) {
for (let account of accountsDto) {
- const existingAccounts = await this.accountService.accounts({
- where: { id: account.id }
- });
+ //Check if there is any existing account with the same id
+ const accountWithSameId = await this.accountService.getAccountById(
+ account.id
+ );
- const oldAccountId = account.id;
- const platformId = account.platformId;
+ //If there is no account or if the account belongs to a different user then create a new account
+ if (!accountWithSameId || accountWithSameId.userId !== userId) {
+ let oldAccountId: string;
+ const platformId = account.platformId;
- delete account.id;
- delete account.platformId;
- delete account.isDefault;
+ delete account.platformId;
+ delete account.isDefault;
+
+ if (accountWithSameId) {
+ oldAccountId = account.id;
+ delete account.id;
+ }
- //If account id does not exist, then create a new one
- if (existingAccounts.length === 0) {
- const newAccountConfig = {
+ const newAccountObj = {
...account,
User: { connect: { id: userId } }
};
if (platformId) {
- Object.assign(newAccountConfig, {
+ Object.assign(newAccountObj, {
Platform: { connect: { id: platformId } }
});
}
const newAccount = await this.accountService.createAccount(
- newAccountConfig,
+ newAccountObj,
userId
);
- accountMappings[oldAccountId] = newAccount.id;
- continue;
- }
-
- //If account id is used, then check if it belongs to the same user
- //Yes -> Merge the accounts and don't create a new one
- if (existingAccounts[0].userId === userId) {
- continue;
- }
-
- //No -> Replace the account id with a new account id as well as update all the activities when looping
-
- const newAccountConfig = {
- ...account,
- User: { connect: { id: userId } }
- };
-
- if (platformId) {
- Object.assign(newAccountConfig, {
- Platform: { connect: { id: platformId } }
- });
+ if (accountWithSameId && oldAccountId) {
+ accountIdMapping[oldAccountId] = newAccount.id;
+ }
}
-
- const newAccount = await this.accountService.createAccount(
- newAccountConfig,
- userId
- );
-
- accountMappings[oldAccountId] = newAccount.id;
}
}
@@ -189,9 +169,11 @@ export class ImportService {
}
}
- //If we updated the account id, then update the account id in the activity as well
- if (Object.keys(accountMappings).includes(activity.accountId)) {
- activity.accountId = accountMappings[activity.accountId];
+ if (!isDryRun) {
+ //If a new account is created, then update the accountId in all activities
+ if (Object.keys(accountIdMapping).includes(activity.accountId)) {
+ activity.accountId = accountIdMapping[activity.accountId];
+ }
}
}
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 b5d7b0a83..fd8685c9a 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
@@ -183,13 +183,12 @@ export class ImportActivitiesDialog implements OnDestroy {
}
try {
+ this.accounts = content.accounts;
const data = await this.importActivitiesService.importJson({
activities: content.activities,
- accounts: content.accounts,
isDryRun: true
});
this.activities = data.activities;
- this.accounts = data.accounts;
} catch (error) {
console.error(error);
this.handleImportError({ error, activities: content.activities });
From 912c10e6baffe7bbc9df6d23be0ec11a4ec7d67a Mon Sep 17 00:00:00 2001
From: yksolanki9
Date: Wed, 1 Feb 2023 23:18:49 +0530
Subject: [PATCH 06/12] Fix issue with activities for new accounts
---
apps/api/src/app/import/import.service.ts | 5 +++--
.../import-activities-dialog.component.ts | 3 ++-
2 files changed, 5 insertions(+), 3 deletions(-)
diff --git a/apps/api/src/app/import/import.service.ts b/apps/api/src/app/import/import.service.ts
index 2e4f1044b..c672f5bbc 100644
--- a/apps/api/src/app/import/import.service.ts
+++ b/apps/api/src/app/import/import.service.ts
@@ -117,7 +117,8 @@ export class ImportService {
}): Promise {
const accountIdMapping = {};
- if (!isDryRun && accountsDto?.length) {
+ //Create new accounts during dryRun so that new account Ids don't get invalidated
+ if (isDryRun && accountsDto?.length) {
for (let account of accountsDto) {
//Check if there is any existing account with the same id
const accountWithSameId = await this.accountService.getAccountById(
@@ -169,7 +170,7 @@ export class ImportService {
}
}
- if (!isDryRun) {
+ if (isDryRun) {
//If a new account is created, then update the accountId in all activities
if (Object.keys(accountIdMapping).includes(activity.accountId)) {
activity.accountId = accountIdMapping[activity.accountId];
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 fd8685c9a..80b20df2d 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
@@ -186,7 +186,8 @@ export class ImportActivitiesDialog implements OnDestroy {
this.accounts = content.accounts;
const data = await this.importActivitiesService.importJson({
activities: content.activities,
- isDryRun: true
+ isDryRun: true,
+ accounts: this.accounts
});
this.activities = data.activities;
} catch (error) {
From ab7b98630b0fa45b5a0a19125759804685110418 Mon Sep 17 00:00:00 2001
From: yksolanki9
Date: Wed, 1 Feb 2023 23:42:46 +0530
Subject: [PATCH 07/12] minor refactoring
---
apps/api/src/app/account/account.service.ts | 14 +++++++-------
apps/api/src/app/account/create-account.dto.ts | 16 ++++++++--------
apps/api/src/app/import/import-data.dto.ts | 2 +-
apps/api/src/app/import/import.controller.ts | 6 +++---
apps/api/src/app/import/import.service.ts | 13 +++++++------
.../import-activities-dialog.component.ts | 4 ++--
.../app/services/import-activities.service.ts | 11 +++++------
.../src/lib/interfaces/export.interface.ts | 2 +-
8 files changed, 34 insertions(+), 34 deletions(-)
diff --git a/apps/api/src/app/account/account.service.ts b/apps/api/src/app/account/account.service.ts
index d144faa8b..32bf9f875 100644
--- a/apps/api/src/app/account/account.service.ts
+++ b/apps/api/src/app/account/account.service.ts
@@ -80,6 +80,13 @@ export class AccountService {
});
}
+ public async getAccountById(accountId: string) {
+ const accounts = await this.accounts({
+ where: { id: accountId }
+ });
+ return accounts.length ? accounts[0] : null;
+ }
+
public async getAccounts(aUserId: string) {
const accounts = await this.accounts({
include: { Order: true, Platform: true },
@@ -104,13 +111,6 @@ export class AccountService {
});
}
- public async getAccountById(accountId: string) {
- const accounts = await this.accounts({
- where: { id: accountId }
- });
- return accounts.length ? accounts[0] : null;
- }
-
public async getCashDetails({
currency,
filters = [],
diff --git a/apps/api/src/app/account/create-account.dto.ts b/apps/api/src/app/account/create-account.dto.ts
index 3c32163e0..69377bdd2 100644
--- a/apps/api/src/app/account/create-account.dto.ts
+++ b/apps/api/src/app/account/create-account.dto.ts
@@ -8,14 +8,6 @@ import {
} from 'class-validator';
export class CreateAccountDto {
- @IsOptional()
- @IsString()
- id: string;
-
- @IsOptional()
- @IsBoolean()
- isDefault: boolean;
-
@IsString()
accountType: AccountType;
@@ -25,6 +17,14 @@ export class CreateAccountDto {
@IsString()
currency: string;
+ @IsOptional()
+ @IsString()
+ id: string;
+
+ @IsOptional()
+ @IsBoolean()
+ isDefault: boolean;
+
@IsBoolean()
@IsOptional()
isExcluded?: boolean;
diff --git a/apps/api/src/app/import/import-data.dto.ts b/apps/api/src/app/import/import-data.dto.ts
index aa141aab5..cfd866120 100644
--- a/apps/api/src/app/import/import-data.dto.ts
+++ b/apps/api/src/app/import/import-data.dto.ts
@@ -1,6 +1,6 @@
import { CreateOrderDto } from '@ghostfolio/api/app/order/create-order.dto';
import { Type } from 'class-transformer';
-import { IsArray, ValidateNested, IsOptional } from 'class-validator';
+import { IsArray, IsOptional, ValidateNested } from 'class-validator';
import { CreateAccountDto } from '../account/create-account.dto';
export class ImportDataDto {
diff --git a/apps/api/src/app/import/import.controller.ts b/apps/api/src/app/import/import.controller.ts
index a1a1d4b1d..0de1c0b54 100644
--- a/apps/api/src/app/import/import.controller.ts
+++ b/apps/api/src/app/import/import.controller.ts
@@ -68,11 +68,11 @@ export class ImportController {
try {
const activities = await this.importService.import({
- maxActivitiesToImport,
+ accountsDto: importData.accounts || [],
+ activitiesDto: importData.activities,
isDryRun,
+ maxActivitiesToImport,
userCurrency,
- activitiesDto: importData.activities,
- accountsDto: importData.accounts || [],
userId: this.request.user.id
});
diff --git a/apps/api/src/app/import/import.service.ts b/apps/api/src/app/import/import.service.ts
index c672f5bbc..c72d00eca 100644
--- a/apps/api/src/app/import/import.service.ts
+++ b/apps/api/src/app/import/import.service.ts
@@ -1,4 +1,5 @@
import { AccountService } from '@ghostfolio/api/app/account/account.service';
+import { CreateAccountDto } from '@ghostfolio/api/app/account/create-account.dto';
import { CreateOrderDto } from '@ghostfolio/api/app/order/create-order.dto';
import { Activity } from '@ghostfolio/api/app/order/interfaces/activities.interface';
import { OrderService } from '@ghostfolio/api/app/order/order.service';
@@ -17,7 +18,6 @@ import { SymbolProfile } from '@prisma/client';
import Big from 'big.js';
import { endOfToday, isAfter, isSameDay, parseISO } from 'date-fns';
import { v4 as uuidv4 } from 'uuid';
-import { CreateAccountDto } from '../account/create-account.dto';
@Injectable()
export class ImportService {
@@ -101,15 +101,15 @@ export class ImportService {
}
public async import({
- activitiesDto,
accountsDto,
+ activitiesDto,
isDryRun = false,
maxActivitiesToImport,
userCurrency,
userId
}: {
- activitiesDto: Partial[];
accountsDto: Partial[];
+ activitiesDto: Partial[];
isDryRun?: boolean;
maxActivitiesToImport: number;
userCurrency: string;
@@ -117,10 +117,10 @@ export class ImportService {
}): Promise {
const accountIdMapping = {};
- //Create new accounts during dryRun so that new account Ids don't get invalidated
+ //Create new accounts during dryRun so that new account IDs don't get invalidated
if (isDryRun && accountsDto?.length) {
for (let account of accountsDto) {
- //Check if there is any existing account with the same id
+ //Check if there is any existing account with the same ID
const accountWithSameId = await this.accountService.getAccountById(
account.id
);
@@ -154,6 +154,7 @@ export class ImportService {
userId
);
+ //Store the new to old account ID mappings for updating activities
if (accountWithSameId && oldAccountId) {
accountIdMapping[oldAccountId] = newAccount.id;
}
@@ -170,8 +171,8 @@ export class ImportService {
}
}
+ //If a new account is created, then update the accountId in all activities
if (isDryRun) {
- //If a new account is created, then update the accountId in all activities
if (Object.keys(accountIdMapping).includes(activity.accountId)) {
activity.accountId = accountIdMapping[activity.accountId];
}
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 80b20df2d..045cee17f 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
@@ -183,12 +183,12 @@ export class ImportActivitiesDialog implements OnDestroy {
}
try {
- this.accounts = content.accounts;
const data = await this.importActivitiesService.importJson({
activities: content.activities,
isDryRun: true,
- accounts: this.accounts
+ accounts: content.accounts
});
+ this.accounts = content.accounts;
this.activities = data.activities;
} catch (error) {
console.error(error);
diff --git a/apps/client/src/app/services/import-activities.service.ts b/apps/client/src/app/services/import-activities.service.ts
index c2b0bbe05..357ad196a 100644
--- a/apps/client/src/app/services/import-activities.service.ts
+++ b/apps/client/src/app/services/import-activities.service.ts
@@ -35,7 +35,6 @@ export class ImportActivitiesService {
isDryRun?: boolean;
userAccounts: Account[];
}): Promise<{
- accounts?: CreateAccountDto[];
activities: Activity[];
}> {
const content = csvToJson(fileContent, {
@@ -71,14 +70,14 @@ export class ImportActivitiesService {
accounts?: CreateAccountDto[];
isDryRun?: boolean;
}): Promise<{
- accounts?: CreateAccountDto[];
activities: Activity[];
+ accounts?: CreateAccountDto[];
}> {
return new Promise((resolve, reject) => {
this.postImport(
{
- activities,
- accounts
+ accounts,
+ activities
},
isDryRun
)
@@ -100,11 +99,11 @@ export class ImportActivitiesService {
accounts,
activities
}: {
- accounts?: CreateAccountDto[];
activities: Activity[];
- }): Promise<{
accounts?: CreateAccountDto[];
+ }): Promise<{
activities: Activity[];
+ accounts?: CreateAccountDto[];
}> {
const importData: CreateOrderDto[] = [];
diff --git a/libs/common/src/lib/interfaces/export.interface.ts b/libs/common/src/lib/interfaces/export.interface.ts
index 7b7470717..dcd7aa60d 100644
--- a/libs/common/src/lib/interfaces/export.interface.ts
+++ b/libs/common/src/lib/interfaces/export.interface.ts
@@ -1,4 +1,4 @@
-import { Order, Account } from '@prisma/client';
+import { Account, Order } from '@prisma/client';
export interface Export {
meta: {
From 665e5ad6cdd44f2ff6844529ad58c1be1adca801 Mon Sep 17 00:00:00 2001
From: yksolanki9
Date: Sat, 4 Feb 2023 23:33:03 +0530
Subject: [PATCH 08/12] Resolve comments
---
apps/api/src/app/account/create-account.dto.ts | 4 ----
apps/api/src/app/export/export.service.ts | 1 -
apps/api/src/app/import/import-data.dto.ts | 2 +-
apps/api/src/app/import/import.service.ts | 3 +--
.../import-activities-dialog.component.ts | 17 +++++++----------
.../app/services/import-activities.service.ts | 11 ++++-------
.../src/lib/interfaces/export.interface.ts | 2 +-
7 files changed, 14 insertions(+), 26 deletions(-)
diff --git a/apps/api/src/app/account/create-account.dto.ts b/apps/api/src/app/account/create-account.dto.ts
index 69377bdd2..43e2584b7 100644
--- a/apps/api/src/app/account/create-account.dto.ts
+++ b/apps/api/src/app/account/create-account.dto.ts
@@ -21,10 +21,6 @@ export class CreateAccountDto {
@IsString()
id: string;
- @IsOptional()
- @IsBoolean()
- isDefault: boolean;
-
@IsBoolean()
@IsOptional()
isExcluded?: boolean;
diff --git a/apps/api/src/app/export/export.service.ts b/apps/api/src/app/export/export.service.ts
index ebf0845cc..219b022cb 100644
--- a/apps/api/src/app/export/export.service.ts
+++ b/apps/api/src/app/export/export.service.ts
@@ -20,7 +20,6 @@ export class ExportService {
balance: true,
currency: true,
id: true,
- isDefault: true,
isExcluded: true,
name: true,
platformId: true
diff --git a/apps/api/src/app/import/import-data.dto.ts b/apps/api/src/app/import/import-data.dto.ts
index cfd866120..fa954a2c9 100644
--- a/apps/api/src/app/import/import-data.dto.ts
+++ b/apps/api/src/app/import/import-data.dto.ts
@@ -1,7 +1,7 @@
+import { CreateAccountDto } from '@ghostfolio/api/app/account/create-account.dto';
import { CreateOrderDto } from '@ghostfolio/api/app/order/create-order.dto';
import { Type } from 'class-transformer';
import { IsArray, IsOptional, ValidateNested } from 'class-validator';
-import { CreateAccountDto } from '../account/create-account.dto';
export class ImportDataDto {
@IsOptional()
diff --git a/apps/api/src/app/import/import.service.ts b/apps/api/src/app/import/import.service.ts
index c72d00eca..9210d9da8 100644
--- a/apps/api/src/app/import/import.service.ts
+++ b/apps/api/src/app/import/import.service.ts
@@ -115,7 +115,7 @@ export class ImportService {
userCurrency: string;
userId: string;
}): Promise {
- const accountIdMapping = {};
+ const accountIdMapping: { [oldAccountId: string]: string } = {};
//Create new accounts during dryRun so that new account IDs don't get invalidated
if (isDryRun && accountsDto?.length) {
@@ -131,7 +131,6 @@ export class ImportService {
const platformId = account.platformId;
delete account.platformId;
- delete account.isDefault;
if (accountWithSameId) {
oldAccountId = account.id;
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 045cee17f..b4e118a30 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
@@ -11,7 +11,6 @@ import {
MatLegacyDialogRef as MatDialogRef
} from '@angular/material/legacy-dialog';
import { MatLegacySnackBar as MatSnackBar } from '@angular/material/legacy-snack-bar';
-import { CreateAccountDto } from '@ghostfolio/api/app/account/create-account.dto';
import { Activity } from '@ghostfolio/api/app/order/interfaces/activities.interface';
import { DataService } from '@ghostfolio/client/services/data.service';
import { ImportActivitiesService } from '@ghostfolio/client/services/import-activities.service';
@@ -29,7 +28,6 @@ import { ImportActivitiesDialogParams } from './interfaces/interfaces';
templateUrl: 'import-activities-dialog.html'
})
export class ImportActivitiesDialog implements OnDestroy {
- public accounts: CreateAccountDto[] = [];
public activities: Activity[] = [];
public details: any[] = [];
public errorMessages: string[] = [];
@@ -94,7 +92,6 @@ export class ImportActivitiesDialog implements OnDestroy {
this.snackBar.open('⏳ ' + $localize`Importing data...`);
await this.importActivitiesService.importSelectedActivities({
- accounts: this.accounts,
activities: this.selectedActivities
});
@@ -183,13 +180,13 @@ export class ImportActivitiesDialog implements OnDestroy {
}
try {
- const data = await this.importActivitiesService.importJson({
- activities: content.activities,
- isDryRun: true,
- accounts: content.accounts
- });
- this.accounts = content.accounts;
- this.activities = data.activities;
+ const { activities } =
+ await this.importActivitiesService.importJson({
+ accounts: content.accounts,
+ activities: content.activities,
+ isDryRun: true
+ });
+ this.activities = activities;
} catch (error) {
console.error(error);
this.handleImportError({ error, activities: content.activities });
diff --git a/apps/client/src/app/services/import-activities.service.ts b/apps/client/src/app/services/import-activities.service.ts
index 357ad196a..d3ac9b2fb 100644
--- a/apps/client/src/app/services/import-activities.service.ts
+++ b/apps/client/src/app/services/import-activities.service.ts
@@ -58,12 +58,12 @@ export class ImportActivitiesService {
});
}
- return await this.importJson({ isDryRun, activities });
+ return await this.importJson({ activities, isDryRun });
}
public importJson({
- activities,
accounts,
+ activities,
isDryRun = false
}: {
activities: CreateOrderDto[];
@@ -96,14 +96,11 @@ export class ImportActivitiesService {
}
public importSelectedActivities({
- accounts,
activities
}: {
activities: Activity[];
- accounts?: CreateAccountDto[];
}): Promise<{
activities: Activity[];
- accounts?: CreateAccountDto[];
}> {
const importData: CreateOrderDto[] = [];
@@ -111,7 +108,7 @@ export class ImportActivitiesService {
importData.push(this.convertToCreateOrderDto(activity));
}
- return this.importJson({ activities: importData, accounts });
+ return this.importJson({ activities: importData });
}
private convertToCreateOrderDto({
@@ -363,7 +360,7 @@ export class ImportActivitiesService {
}
private postImport(
- aImportData: { activities: CreateOrderDto[]; accounts: CreateAccountDto[] },
+ aImportData: { accounts: CreateAccountDto[]; activities: CreateOrderDto[] },
aIsDryRun = false
) {
return this.http.post<{ activities: Activity[] }>(
diff --git a/libs/common/src/lib/interfaces/export.interface.ts b/libs/common/src/lib/interfaces/export.interface.ts
index dcd7aa60d..b142cb2f8 100644
--- a/libs/common/src/lib/interfaces/export.interface.ts
+++ b/libs/common/src/lib/interfaces/export.interface.ts
@@ -5,7 +5,7 @@ export interface Export {
date: string;
version: string;
};
- accounts: Omit[];
+ accounts: Omit[];
activities: (Omit<
Order,
| 'accountUserId'
From dcc857a2ba6f8827552eaa2741d3a2b375bb8abe Mon Sep 17 00:00:00 2001
From: yksolanki9
Date: Sun, 5 Feb 2023 13:40:42 +0530
Subject: [PATCH 09/12] Remove getAccountById method and its usage
---
apps/api/src/app/account/account.service.ts | 7 -------
apps/api/src/app/import/import.service.ts | 12 ++++++++++--
2 files changed, 10 insertions(+), 9 deletions(-)
diff --git a/apps/api/src/app/account/account.service.ts b/apps/api/src/app/account/account.service.ts
index 32bf9f875..7c10fc31f 100644
--- a/apps/api/src/app/account/account.service.ts
+++ b/apps/api/src/app/account/account.service.ts
@@ -80,13 +80,6 @@ export class AccountService {
});
}
- public async getAccountById(accountId: string) {
- const accounts = await this.accounts({
- where: { id: accountId }
- });
- return accounts.length ? accounts[0] : null;
- }
-
public async getAccounts(aUserId: string) {
const accounts = await this.accounts({
include: { Order: true, Platform: true },
diff --git a/apps/api/src/app/import/import.service.ts b/apps/api/src/app/import/import.service.ts
index 9210d9da8..5c3aa9e59 100644
--- a/apps/api/src/app/import/import.service.ts
+++ b/apps/api/src/app/import/import.service.ts
@@ -117,12 +117,20 @@ export class ImportService {
}): Promise {
const accountIdMapping: { [oldAccountId: string]: string } = {};
+ const existingAccounts = await this.accountService.accounts({
+ where: {
+ id: {
+ in: accountsDto.map((account) => account.id)
+ }
+ }
+ });
+
//Create new accounts during dryRun so that new account IDs don't get invalidated
if (isDryRun && accountsDto?.length) {
for (let account of accountsDto) {
//Check if there is any existing account with the same ID
- const accountWithSameId = await this.accountService.getAccountById(
- account.id
+ const accountWithSameId = existingAccounts.find(
+ (existingAccount) => existingAccount.id === account.id
);
//If there is no account or if the account belongs to a different user then create a new account
From f3157414a9151543bc7eb9b7f55923a89e4c71f6 Mon Sep 17 00:00:00 2001
From: Thomas <4159106+dtslvr@users.noreply.github.com>
Date: Sun, 5 Feb 2023 10:07:43 +0100
Subject: [PATCH 10/12] Refactoring
---
apps/api/src/app/export/export.service.ts | 3 +++
apps/api/src/app/import/import.controller.ts | 4 ++--
apps/api/src/app/import/import.service.ts | 22 +++++++++++---------
3 files changed, 17 insertions(+), 12 deletions(-)
diff --git a/apps/api/src/app/export/export.service.ts b/apps/api/src/app/export/export.service.ts
index 219b022cb..c57f1df4e 100644
--- a/apps/api/src/app/export/export.service.ts
+++ b/apps/api/src/app/export/export.service.ts
@@ -15,6 +15,9 @@ export class ExportService {
userId: string;
}): Promise {
const accounts = await this.prismaService.account.findMany({
+ orderBy: {
+ name: 'asc'
+ },
select: {
accountType: true,
balance: true,
diff --git a/apps/api/src/app/import/import.controller.ts b/apps/api/src/app/import/import.controller.ts
index 0de1c0b54..06d9ad93d 100644
--- a/apps/api/src/app/import/import.controller.ts
+++ b/apps/api/src/app/import/import.controller.ts
@@ -68,11 +68,11 @@ export class ImportController {
try {
const activities = await this.importService.import({
- accountsDto: importData.accounts || [],
- activitiesDto: importData.activities,
isDryRun,
maxActivitiesToImport,
userCurrency,
+ accountsDto: importData.accounts ?? [],
+ activitiesDto: importData.activities,
userId: this.request.user.id
});
diff --git a/apps/api/src/app/import/import.service.ts b/apps/api/src/app/import/import.service.ts
index 5c3aa9e59..b9ca5eccd 100644
--- a/apps/api/src/app/import/import.service.ts
+++ b/apps/api/src/app/import/import.service.ts
@@ -120,20 +120,22 @@ export class ImportService {
const existingAccounts = await this.accountService.accounts({
where: {
id: {
- in: accountsDto.map((account) => account.id)
+ in: accountsDto.map(({ id }) => {
+ return id;
+ })
}
}
});
- //Create new accounts during dryRun so that new account IDs don't get invalidated
+ // Create new accounts during dryRun so that new account IDs don't get invalidated
if (isDryRun && accountsDto?.length) {
- for (let account of accountsDto) {
- //Check if there is any existing account with the same ID
+ for (const account of accountsDto) {
+ // Check if there is any existing account with the same ID
const accountWithSameId = existingAccounts.find(
(existingAccount) => existingAccount.id === account.id
);
- //If there is no account or if the account belongs to a different user then create a new account
+ // If there is no account or if the account belongs to a different user then create a new account
if (!accountWithSameId || accountWithSameId.userId !== userId) {
let oldAccountId: string;
const platformId = account.platformId;
@@ -145,23 +147,23 @@ export class ImportService {
delete account.id;
}
- const newAccountObj = {
+ const newAccountObject = {
...account,
User: { connect: { id: userId } }
};
if (platformId) {
- Object.assign(newAccountObj, {
+ Object.assign(newAccountObject, {
Platform: { connect: { id: platformId } }
});
}
const newAccount = await this.accountService.createAccount(
- newAccountObj,
+ newAccountObject,
userId
);
- //Store the new to old account ID mappings for updating activities
+ // Store the new to old account ID mappings for updating activities
if (accountWithSameId && oldAccountId) {
accountIdMapping[oldAccountId] = newAccount.id;
}
@@ -178,7 +180,7 @@ export class ImportService {
}
}
- //If a new account is created, then update the accountId in all activities
+ // If a new account is created, then update the accountId in all activities
if (isDryRun) {
if (Object.keys(accountIdMapping).includes(activity.accountId)) {
activity.accountId = accountIdMapping[activity.accountId];
From 56307036fac716997a92bc58999c0e502d26df9a Mon Sep 17 00:00:00 2001
From: Thomas <4159106+dtslvr@users.noreply.github.com>
Date: Sun, 5 Feb 2023 10:07:53 +0100
Subject: [PATCH 11/12] Update changelog
---
CHANGELOG.md | 5 +++++
1 file changed, 5 insertions(+)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 2cd26e71e..75eae6230 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -7,6 +7,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## Unreleased
+### Added
+
+- Added support to export accounts
+- Added suport to import accounts
+
### Changed
- Migrated the style of `ActivitiesPageModule` to `@angular/material` `15` (mdc)
From a56859d64bf4eebe6e283ffa7d39a0801f611594 Mon Sep 17 00:00:00 2001
From: Thomas <4159106+dtslvr@users.noreply.github.com>
Date: Sun, 5 Feb 2023 10:56:00 +0100
Subject: [PATCH 12/12] Extend import test files
---
test/import/ok-without-accounts.json | 48 ++++++++++++++++++++++++++++
test/import/ok.json | 21 +++++++++++-
2 files changed, 68 insertions(+), 1 deletion(-)
create mode 100644 test/import/ok-without-accounts.json
diff --git a/test/import/ok-without-accounts.json b/test/import/ok-without-accounts.json
new file mode 100644
index 000000000..63961be74
--- /dev/null
+++ b/test/import/ok-without-accounts.json
@@ -0,0 +1,48 @@
+{
+ "meta": {
+ "date": "2022-04-01T00:00:00.000Z",
+ "version": "dev"
+ },
+ "activities": [
+ {
+ "fee": 0,
+ "quantity": 0,
+ "type": "BUY",
+ "unitPrice": 0,
+ "currency": "USD",
+ "dataSource": "YAHOO",
+ "date": "2050-06-05T22:00:00.000Z",
+ "symbol": "MSFT"
+ },
+ {
+ "fee": 0,
+ "quantity": 1,
+ "type": "ITEM",
+ "unitPrice": 500000,
+ "currency": "USD",
+ "dataSource": "MANUAL",
+ "date": "2021-12-31T22:00:00.000Z",
+ "symbol": "Penthouse Apartment"
+ },
+ {
+ "fee": 0,
+ "quantity": 5,
+ "type": "DIVIDEND",
+ "unitPrice": 0.62,
+ "currency": "USD",
+ "dataSource": "YAHOO",
+ "date": "2021-11-16T22:00:00.000Z",
+ "symbol": "MSFT"
+ },
+ {
+ "fee": 19,
+ "quantity": 5,
+ "type": "BUY",
+ "unitPrice": 298.58,
+ "currency": "USD",
+ "dataSource": "YAHOO",
+ "date": "2021-09-15T22:00:00.000Z",
+ "symbol": "MSFT"
+ }
+ ]
+}
diff --git a/test/import/ok.json b/test/import/ok.json
index 63961be74..335d2cd8a 100644
--- a/test/import/ok.json
+++ b/test/import/ok.json
@@ -1,10 +1,23 @@
{
"meta": {
- "date": "2022-04-01T00:00:00.000Z",
+ "date": "2023-02-05T00:00:00.000Z",
"version": "dev"
},
+ "accounts": [
+ {
+ "accountType": "SECURITIES",
+ "balance": 2000,
+ "currency": "USD",
+ "id": "b2d3fe1d-d6a8-41a3-be39-07ef5e9480f0",
+ "isExcluded": false,
+ "name": "My Online Trading Account",
+ "platformId": null
+ }
+ ],
"activities": [
{
+ "accountId": "b2d3fe1d-d6a8-41a3-be39-07ef5e9480f0",
+ "comment": null,
"fee": 0,
"quantity": 0,
"type": "BUY",
@@ -15,6 +28,8 @@
"symbol": "MSFT"
},
{
+ "accountId": null,
+ "comment": null,
"fee": 0,
"quantity": 1,
"type": "ITEM",
@@ -25,6 +40,8 @@
"symbol": "Penthouse Apartment"
},
{
+ "accountId": "b2d3fe1d-d6a8-41a3-be39-07ef5e9480f0",
+ "comment": null,
"fee": 0,
"quantity": 5,
"type": "DIVIDEND",
@@ -35,6 +52,8 @@
"symbol": "MSFT"
},
{
+ "accountId": "b2d3fe1d-d6a8-41a3-be39-07ef5e9480f0",
+ "comment": "My first order",
"fee": 19,
"quantity": 5,
"type": "BUY",