Browse Source

Add error messages if import include duplicates

pull/1984/head
Thomas 2 years ago
parent
commit
95901368eb
  1. 19
      apps/api/src/app/import/import.service.ts
  2. 7
      apps/api/src/app/order/interfaces/activities.interface.ts
  3. 1
      apps/api/src/app/order/order.service.ts
  4. 4
      apps/client/src/app/pages/portfolio/activities/import-activities-dialog/import-activities-dialog.component.ts
  5. 30
      libs/ui/src/lib/activities-table/activities-table.component.html
  6. 28
      libs/ui/src/lib/activities-table/activities-table.component.ts
  7. 2
      libs/ui/src/lib/activities-table/activities-table.module.ts
  8. 1
      libs/ui/src/lib/i18n.ts

19
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: <SymbolProfile>(<unknown>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,

7
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;
}

1
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,

4
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;
});
}

30
libs/ui/src/lib/activities-table/activities-table.component.html

@ -78,11 +78,9 @@
<mat-checkbox
color="primary"
[checked]="
areAllRowsSelected() &&
!hasDuplicateActivity &&
selectedRows.hasValue()
areAllRowsSelected() && !hasErrors && selectedRows.hasValue()
"
[disabled]="hasDuplicateActivity"
[disabled]="hasErrors"
[indeterminate]="selectedRows.hasValue() && !areAllRowsSelected()"
(change)="$event ? toggleAllRows() : null"
></mat-checkbox>
@ -90,10 +88,8 @@
<td *matCellDef="let element" class="px-1" mat-cell>
<mat-checkbox
color="primary"
[checked]="
element.isDuplicate ? false : selectedRows.isSelected(element)
"
[disabled]="element.isDuplicate"
[checked]="element.error ? false : selectedRows.isSelected(element)"
[disabled]="element.error"
(change)="$event ? selectedRows.toggle(element) : null"
(click)="$event.stopPropagation()"
></mat-checkbox>
@ -101,6 +97,23 @@
<td *matFooterCellDef class="px-1" mat-footer-cell></td>
</ng-container>
<ng-container matColumnDef="importStatus">
<th *matHeaderCellDef class="px-1" mat-header-cell>
<ng-container i18n></ng-container>
</th>
<td *matCellDef="let element" class="px-1" mat-cell>
<div
*ngIf="element.error"
class="d-flex"
matTooltipPosition="above"
[matTooltip]="element.error.message"
>
<ion-icon class="text-danger" name="alert-circle-outline"></ion-icon>
</div>
</td>
<td *matFooterCellDef class="px-1" mat-footer-cell></td>
</ng-container>
<ng-container matColumnDef="count">
<th
*matHeaderCellDef
@ -125,6 +138,7 @@
mat-footer-cell
></td>
</ng-container>
<ng-container matColumnDef="date">
<th *matHeaderCellDef class="px-1" mat-header-cell mat-sort-header>
<ng-container i18n>Date</ng-container>

28
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<Filter[]>();
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 (

2
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
],

1
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`,

Loading…
Cancel
Save