Browse Source

Improve validation

pull/1769/head
Thomas 3 years ago
parent
commit
31ff6c3971
  1. 20
      apps/api/src/app/exchange-rate/exchange-rate.controller.ts
  2. 12
      apps/api/src/services/exchange-rate-data.service.ts
  3. 18
      apps/client/src/app/pages/portfolio/activities/create-or-update-activity-dialog/create-or-update-activity-dialog.component.ts
  4. 36
      apps/client/src/app/pages/portfolio/activities/create-or-update-activity-dialog/create-or-update-activity-dialog.html

20
apps/api/src/app/exchange-rate/exchange-rate.controller.ts

@ -1,6 +1,13 @@
import { IDataProviderHistoricalResponse } from '@ghostfolio/api/services/interfaces/interfaces';
import { Controller, Get, Param, UseGuards } from '@nestjs/common';
import {
Controller,
Get,
HttpException,
Param,
UseGuards
} from '@nestjs/common';
import { AuthGuard } from '@nestjs/passport';
import { StatusCodes, getReasonPhrase } from 'http-status-codes';
import { ExchangeRateService } from './exchange-rate.service';
@ -18,9 +25,18 @@ export class ExchangeRateController {
): Promise<IDataProviderHistoricalResponse> {
const date = new Date(dateString);
return this.exchangeRateService.getExchangeRate({
const { marketPrice } = await this.exchangeRateService.getExchangeRate({
date,
symbol
});
if (marketPrice) {
return { marketPrice };
}
throw new HttpException(
getReasonPhrase(StatusCodes.NOT_FOUND),
StatusCodes.NOT_FOUND
);
}
}

12
apps/api/src/services/exchange-rate-data.service.ts

@ -168,7 +168,7 @@ export class ExchangeRateDataService {
return this.toCurrency(aValue, aFromCurrency, aToCurrency);
}
let factor = 1;
let factor: number;
if (aFromCurrency !== aToCurrency) {
const dataSource = this.dataProviderService.getPrimaryDataSource();
@ -185,7 +185,6 @@ export class ExchangeRateDataService {
} else {
// TODO: Get from data provider service or calculate indirectly via base currency
// and market data
return this.toCurrency(aValue, aFromCurrency, aToCurrency);
}
}
@ -193,12 +192,15 @@ export class ExchangeRateDataService {
return factor * aValue;
}
// Fallback with error, if currencies are not available
Logger.error(
`No exchange rate has been found for ${aFromCurrency}${aToCurrency}`,
`No exchange rate has been found for ${aFromCurrency}${aToCurrency} at ${format(
aDate,
DATE_FORMAT
)}`,
'ExchangeRateDataService'
);
return aValue;
return undefined;
}
private async prepareCurrencies(): Promise<string[]> {

18
apps/client/src/app/pages/portfolio/activities/create-or-update-activity-dialog/create-or-update-activity-dialog.component.ts

@ -15,6 +15,7 @@ import {
MAT_LEGACY_DIALOG_DATA as MAT_DIALOG_DATA,
MatLegacyDialogRef as MatDialogRef
} from '@angular/material/legacy-dialog';
import { getDateFormatString } from '@ghostfolio/common/helper';
import { CreateOrderDto } from '@ghostfolio/api/app/order/create-order.dto';
import { UpdateOrderDto } from '@ghostfolio/api/app/order/update-order.dto';
import { LookupItem } from '@ghostfolio/api/app/symbol/interfaces/lookup-item.interface';
@ -56,6 +57,7 @@ export class CreateOrUpdateActivityDialog implements OnDestroy {
});
public currencies: string[] = [];
public currentMarketPrice = null;
public defaultDateFormat: string;
public filteredLookupItems: LookupItem[];
public filteredLookupItemsObservable: Observable<LookupItem[]>;
public filteredTagsObservable: Observable<Tag[]>;
@ -85,6 +87,7 @@ export class CreateOrUpdateActivityDialog implements OnDestroy {
const { currencies, platforms, tags } = this.dataService.fetchInfo();
this.currencies = currencies;
this.defaultDateFormat = getDateFormatString(this.locale);
this.platforms = platforms;
this.tags = tags.map(({ id, name }) => {
return {
@ -148,6 +151,9 @@ export class CreateOrUpdateActivityDialog implements OnDestroy {
let exchangeRateOfFee = 1;
let exchangeRateOfUnitPrice = 1;
this.activityForm.controls['feeInCustomCurrency'].setErrors(null);
this.activityForm.controls['unitPriceInCustomCurrency'].setErrors(null);
const currency = this.activityForm.controls['currency'].value;
const currencyOfFee = this.activityForm.controls['currencyOfFee'].value;
const currencyOfUnitPrice =
@ -166,7 +172,11 @@ export class CreateOrUpdateActivityDialog implements OnDestroy {
);
exchangeRateOfFee = marketPrice;
} catch {}
} catch {
this.activityForm.controls['feeInCustomCurrency'].setErrors({
invalid: true
});
}
}
const feeInCustomCurrency =
@ -194,7 +204,11 @@ export class CreateOrUpdateActivityDialog implements OnDestroy {
);
exchangeRateOfUnitPrice = marketPrice;
} catch {}
} catch {
this.activityForm.controls['unitPriceInCustomCurrency'].setErrors({
invalid: true
});
}
}
const unitPriceInCustomCurrency =

36
apps/client/src/app/pages/portfolio/activities/create-or-update-activity-dialog/create-or-update-activity-dialog.html

@ -7,7 +7,7 @@
<h1 *ngIf="data.activity.id" i18n mat-dialog-title>Update activity</h1>
<h1 *ngIf="!data.activity.id" i18n mat-dialog-title>Add activity</h1>
<div class="flex-grow-1 pt-3" mat-dialog-content>
<div>
<div class="mb-3">
<mat-form-field appearance="outline" class="w-100">
<mat-label i18n>Type</mat-label>
<mat-select formControlName="type">
@ -18,7 +18,7 @@
</mat-select>
</mat-form-field>
</div>
<div>
<div class="mb-3">
<mat-form-field appearance="outline" class="w-100">
<mat-label i18n>Account</mat-label>
<mat-select formControlName="accountId">
@ -33,6 +33,7 @@
</mat-form-field>
</div>
<div
class="mb-3"
[ngClass]="{ 'd-none': !activityForm.controls['searchSymbol'].hasValidator(Validators.required) }"
>
<mat-form-field appearance="outline" class="w-100">
@ -69,6 +70,7 @@
</mat-form-field>
</div>
<div
class="mb-3"
[ngClass]="{ 'd-none': !activityForm.controls['name'].hasValidator(Validators.required) }"
>
<mat-form-field appearance="outline" class="w-100">
@ -92,7 +94,7 @@
<input formControlName="dataSource" matInput />
</mat-form-field>
</div>
<div>
<div class="mb-3">
<mat-form-field appearance="outline" class="w-100">
<mat-label i18n>Date</mat-label>
<input formControlName="date" matInput [matDatepicker]="date" />
@ -106,13 +108,13 @@
<mat-datepicker #date disabled="false"></mat-datepicker>
</mat-form-field>
</div>
<div>
<div class="mb-3">
<mat-form-field appearance="outline" class="w-100">
<mat-label i18n>Quantity</mat-label>
<input formControlName="quantity" matInput type="number" />
</mat-form-field>
</div>
<div class="align-items-start d-flex">
<div class="align-items-start d-flex mb-3">
<mat-form-field appearance="outline" class="w-100">
<mat-label
><ng-container [ngSwitch]="activityForm.controls['type']?.value">
@ -139,6 +141,14 @@
</mat-option>
</mat-select>
</div>
<mat-error
*ngIf="activityForm.controls['unitPriceInCustomCurrency'].hasError('invalid')"
><ng-container i18n
>Oops! Could not get the historical exchange rate from</ng-container
>
{{ activityForm.controls['date']?.value | date: defaultDateFormat
}}</mat-error
>
</mat-form-field>
<button
*ngIf="currentMarketPrice && (data.activity.type === 'BUY' || data.activity.type === 'SELL')"
@ -168,7 +178,7 @@
>
</mat-form-field>
</div>
<div>
<div class="mb-3">
<mat-form-field appearance="outline" class="w-100">
<mat-label i18n>Fee</mat-label>
<input formControlName="feeInCustomCurrency" matInput type="number" />
@ -183,6 +193,14 @@
</mat-option>
</mat-select>
</div>
<mat-error
*ngIf="activityForm.controls['feeInCustomCurrency'].hasError('invalid')"
><ng-container i18n
>Oops! Could not get the historical exchange rate from</ng-container
>
{{ activityForm.controls['date']?.value | date: defaultDateFormat
}}</mat-error
>
</mat-form-field>
</div>
<div class="d-none">
@ -194,7 +212,7 @@
>
</mat-form-field>
</div>
<div>
<div class="mb-3">
<mat-form-field appearance="outline" class="w-100">
<mat-label i18n>Note</mat-label>
<textarea
@ -207,6 +225,7 @@
</mat-form-field>
</div>
<div
class="mb-3"
[ngClass]="{ 'd-none': activityForm.controls['type']?.value !== 'ITEM' }"
>
<mat-form-field appearance="outline" class="w-100">
@ -222,6 +241,7 @@
</mat-form-field>
</div>
<div
class="mb-3"
[ngClass]="{ 'd-none': activityForm.controls['type']?.value !== 'ITEM' }"
>
<mat-form-field appearance="outline" class="w-100">
@ -236,7 +256,7 @@
</mat-select>
</mat-form-field>
</div>
<div [ngClass]="{ 'd-none': tags?.length <= 0 }">
<div class="mb-3" [ngClass]="{ 'd-none': tags?.length <= 0 }">
<mat-form-field appearance="outline" class="w-100">
<mat-label i18n>Tags</mat-label>
<mat-chip-grid #tagsChipList>

Loading…
Cancel
Save