diff --git a/apps/api/src/app/import/import.service.ts b/apps/api/src/app/import/import.service.ts index 14bdd4c50..8ab695ddf 100644 --- a/apps/api/src/app/import/import.service.ts +++ b/apps/api/src/app/import/import.service.ts @@ -1,7 +1,10 @@ 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 { + Activity, + ActivityError +} from '@ghostfolio/api/app/order/interfaces/activities.interface'; import { OrderService } from '@ghostfolio/api/app/order/order.service'; import { PlatformService } from '@ghostfolio/api/app/platform/platform.service'; import { PortfolioService } from '@ghostfolio/api/app/portfolio/portfolio.service'; @@ -80,11 +83,11 @@ export class ImportService { comment: undefined, createdAt: undefined, date: parseDate(dateString), + // TODO: Add evaluated error state fee: 0, feeInBaseCurrency: 0, id: assetProfile.id, isDraft: false, - isDuplicate: false, // TODO: Use evaluated state SymbolProfile: (assetProfile), symbolProfileId: assetProfile.id, type: 'DIVIDEND', @@ -228,8 +231,8 @@ export class ImportService { accountId, comment, date, + error, fee, - isDuplicate, quantity, SymbolProfile: assetProfile, type, @@ -283,7 +286,7 @@ export class ImportService { updatedAt: new Date() }; } else { - if (isDuplicate) { + if (error) { continue; } @@ -321,7 +324,7 @@ export class ImportService { //@ts-ignore activities.push({ ...order, - isDuplicate, + error, value, feeInBaseCurrency: this.exchangeRateDataService.toCurrency( fee, @@ -389,12 +392,16 @@ export class ImportService { ); }); + const error: ActivityError = isDuplicate + ? { code: 'IS_DUPLICATE' } + : undefined; + return { accountId, comment, date, + error, fee, - isDuplicate, quantity, type, unitPrice, diff --git a/apps/api/src/app/order/interfaces/activities.interface.ts b/apps/api/src/app/order/interfaces/activities.interface.ts index f10417f07..bc2c35a50 100644 --- a/apps/api/src/app/order/interfaces/activities.interface.ts +++ b/apps/api/src/app/order/interfaces/activities.interface.ts @@ -5,9 +5,14 @@ export interface Activities { } export interface Activity extends OrderWithAccount { + error?: ActivityError; feeInBaseCurrency: number; - isDuplicate: boolean; updateAccountBalance?: boolean; value: number; valueInBaseCurrency: number; } + +export interface ActivityError { + code: 'IS_DUPLICATE'; + message?: string; +} diff --git a/apps/api/src/app/order/order.service.ts b/apps/api/src/app/order/order.service.ts index b3f27c221..696f5442e 100644 --- a/apps/api/src/app/order/order.service.ts +++ b/apps/api/src/app/order/order.service.ts @@ -333,7 +333,6 @@ export class OrderService { order.SymbolProfile.currency, userCurrency ), - isDuplicate: false, valueInBaseCurrency: this.exchangeRateDataService.toCurrency( value, order.SymbolProfile.currency, 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 04efb7a56..afdf11a9a 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 @@ -236,8 +236,8 @@ export class ImportActivitiesDialog implements OnDestroy { } public updateSelection(activities: Activity[]) { - this.selectedActivities = activities.filter(({ isDuplicate }) => { - return !isDuplicate; + this.selectedActivities = activities.filter(({ error }) => { + return !error; }); } diff --git a/libs/ui/src/lib/activities-table/activities-table.component.html b/libs/ui/src/lib/activities-table/activities-table.component.html index b4dc5c404..4c9712483 100644 --- a/libs/ui/src/lib/activities-table/activities-table.component.html +++ b/libs/ui/src/lib/activities-table/activities-table.component.html @@ -78,11 +78,9 @@ @@ -90,10 +88,8 @@ @@ -101,6 +97,23 @@ + + + + + +
+ +
+ + +
+ + Date diff --git a/libs/ui/src/lib/activities-table/activities-table.component.ts b/libs/ui/src/lib/activities-table/activities-table.component.ts index 979262853..2797b035e 100644 --- a/libs/ui/src/lib/activities-table/activities-table.component.ts +++ b/libs/ui/src/lib/activities-table/activities-table.component.ts @@ -19,6 +19,7 @@ import { DEFAULT_PAGE_SIZE } from '@ghostfolio/common/config'; import { getDateFormatString } from '@ghostfolio/common/helper'; import { Filter, UniqueAsset } from '@ghostfolio/common/interfaces'; import { OrderWithAccount } from '@ghostfolio/common/types'; +import { translate } from '@ghostfolio/ui/i18n'; import Big from 'big.js'; import { isUUID } from 'class-validator'; import { endOfToday, format, isAfter } from 'date-fns'; @@ -66,7 +67,7 @@ export class ActivitiesTableComponent implements OnChanges, OnDestroy, OnInit { public endOfToday = endOfToday(); public filters$ = new Subject(); public hasDrafts = false; - public hasDuplicateActivity = false; + public hasErrors = false; public isAfter = isAfter; public isLoading = true; public isUUID = isUUID; @@ -109,6 +110,7 @@ export class ActivitiesTableComponent implements OnChanges, OnDestroy, OnInit { public ngOnChanges() { this.displayedColumns = [ 'select', + 'importStatus', 'count', 'date', 'type', @@ -130,7 +132,7 @@ export class ActivitiesTableComponent implements OnChanges, OnDestroy, OnInit { }); } else { this.displayedColumns = this.displayedColumns.filter((column) => { - return column !== 'select'; + return column !== 'importStatus' && column !== 'select'; }); } @@ -143,6 +145,20 @@ export class ActivitiesTableComponent implements OnChanges, OnDestroy, OnInit { this.defaultDateFormat = getDateFormatString(this.locale); if (this.activities) { + this.activities = this.activities.map((activity) => { + return { + ...activity, + error: activity.error + ? { + ...activity.error, + message: translate( + `IMPORT_ACTIVITY_ERROR_${activity.error.code}` + ) + } + : undefined + }; + }); + this.allFilters = this.getSearchableFieldValues(this.activities); this.dataSource = new MatTableDataSource(this.activities); @@ -167,11 +183,11 @@ export class ActivitiesTableComponent implements OnChanges, OnDestroy, OnInit { this.updateFilters(); - this.hasDuplicateActivity = this.activities.some(({ isDuplicate }) => { - return isDuplicate; + this.hasErrors = this.activities.some(({ error }) => { + return !!error; }); } else { - this.hasDuplicateActivity = false; + this.hasErrors = false; } } @@ -184,7 +200,7 @@ export class ActivitiesTableComponent implements OnChanges, OnDestroy, OnInit { public onClickActivity(activity: Activity) { if (this.showCheckbox) { - if (!activity.isDuplicate) { + if (!activity.error) { this.selectedRows.toggle(activity); } } else if ( diff --git a/libs/ui/src/lib/activities-table/activities-table.module.ts b/libs/ui/src/lib/activities-table/activities-table.module.ts index 4d5951644..82d09b44e 100644 --- a/libs/ui/src/lib/activities-table/activities-table.module.ts +++ b/libs/ui/src/lib/activities-table/activities-table.module.ts @@ -6,6 +6,7 @@ import { MatMenuModule } from '@angular/material/menu'; import { MatPaginatorModule } from '@angular/material/paginator'; import { MatSortModule } from '@angular/material/sort'; import { MatTableModule } from '@angular/material/table'; +import { MatTooltipModule } from '@angular/material/tooltip'; import { RouterModule } from '@angular/router'; import { GfSymbolIconModule } from '@ghostfolio/client/components/symbol-icon/symbol-icon.module'; import { GfSymbolModule } from '@ghostfolio/client/pipes/symbol/symbol.module'; @@ -32,6 +33,7 @@ import { ActivitiesTableComponent } from './activities-table.component'; MatPaginatorModule, MatSortModule, MatTableModule, + MatTooltipModule, NgxSkeletonLoaderModule, RouterModule ], diff --git a/libs/ui/src/lib/i18n.ts b/libs/ui/src/lib/i18n.ts index 2dd01e16a..f4b9893aa 100644 --- a/libs/ui/src/lib/i18n.ts +++ b/libs/ui/src/lib/i18n.ts @@ -11,6 +11,7 @@ const locales = { EMERGENCY_FUND: $localize`Emergency Fund`, GRANT: $localize`Grant`, HIGHER_RISK: $localize`Higher Risk`, + IMPORT_ACTIVITY_ERROR_IS_DUPLICATE: $localize`This activity already exists.`, LOWER_RISK: $localize`Lower Risk`, OTHER: $localize`Other`, RETIREMENT_PROVISION: $localize`Retirement Provision`,