From f88ee5e5a08f8fbceb11c04ebd1969fbac0cc97e Mon Sep 17 00:00:00 2001 From: Thomas Kaul <4159106+dtslvr@users.noreply.github.com> Date: Wed, 8 Mar 2023 20:23:03 +0100 Subject: [PATCH] Feature/improve validation for manual currency (#1769) * Improve validation * Update changelog --- CHANGELOG.md | 1 + .../exchange-rate/exchange-rate.controller.ts | 20 ++++- .../exchange-rate/exchange-rate.service.ts | 7 +- .../services/exchange-rate-data.service.ts | 12 +-- ...ate-or-update-activity-dialog.component.ts | 18 ++++- .../create-or-update-activity-dialog.html | 36 +++++++-- apps/client/src/locales/messages.de.xlf | 76 +++++++++++------- apps/client/src/locales/messages.es.xlf | 78 ++++++++++++------- apps/client/src/locales/messages.fr.xlf | 78 ++++++++++++------- apps/client/src/locales/messages.it.xlf | 78 ++++++++++++------- apps/client/src/locales/messages.nl.xlf | 78 ++++++++++++------- apps/client/src/locales/messages.pt.xlf | 78 ++++++++++++------- apps/client/src/locales/messages.xlf | 75 +++++++++++------- 13 files changed, 426 insertions(+), 209 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c633de40..814b080f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Changed +- Improved the validation of the manual currency for the activity fee and unit price - Harmonized the axis style of charts - Made setting `NODE_ENV: production` optional (to avoid `ENOENT: no such file or directory` errors on startup) - Removed the environment variable `ENABLE_FEATURE_CUSTOM_SYMBOLS` diff --git a/apps/api/src/app/exchange-rate/exchange-rate.controller.ts b/apps/api/src/app/exchange-rate/exchange-rate.controller.ts index 75a6f57b..ca9b67ce 100644 --- a/apps/api/src/app/exchange-rate/exchange-rate.controller.ts +++ b/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 { const date = new Date(dateString); - return this.exchangeRateService.getExchangeRate({ + const exchangeRate = await this.exchangeRateService.getExchangeRate({ date, symbol }); + + if (exchangeRate) { + return { marketPrice: exchangeRate }; + } + + throw new HttpException( + getReasonPhrase(StatusCodes.NOT_FOUND), + StatusCodes.NOT_FOUND + ); } } diff --git a/apps/api/src/app/exchange-rate/exchange-rate.service.ts b/apps/api/src/app/exchange-rate/exchange-rate.service.ts index be7f3d55..d340bb5b 100644 --- a/apps/api/src/app/exchange-rate/exchange-rate.service.ts +++ b/apps/api/src/app/exchange-rate/exchange-rate.service.ts @@ -1,5 +1,4 @@ import { ExchangeRateDataService } from '@ghostfolio/api/services/exchange-rate-data.service'; -import { IDataProviderHistoricalResponse } from '@ghostfolio/api/services/interfaces/interfaces'; import { Injectable } from '@nestjs/common'; @Injectable() @@ -14,16 +13,14 @@ export class ExchangeRateService { }: { date: Date; symbol: string; - }): Promise { + }): Promise { const [currency1, currency2] = symbol.split('-'); - const marketPrice = await this.exchangeRateDataService.toCurrencyAtDate( + return this.exchangeRateDataService.toCurrencyAtDate( 1, currency1, currency2, date ); - - return { marketPrice }; } } diff --git a/apps/api/src/services/exchange-rate-data.service.ts b/apps/api/src/services/exchange-rate-data.service.ts index cbaa38af..080772eb 100644 --- a/apps/api/src/services/exchange-rate-data.service.ts +++ b/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 { 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 0ae65188..6bbf5cd6 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 @@ -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; public filteredTagsObservable: Observable; @@ -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 = 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 f59dc4fc..2291cf81 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 @@ -7,7 +7,7 @@

Update activity

Add activity

-
+
Type @@ -18,7 +18,7 @@
-
+
Account @@ -33,6 +33,7 @@
@@ -69,6 +70,7 @@
@@ -92,7 +94,7 @@
-
+
Date @@ -106,13 +108,13 @@
-
+
Quantity
-
+
@@ -139,6 +141,14 @@
+ Oops! Could not get the historical exchange rate from + {{ activityForm.controls['date']?.value | date: defaultDateFormat + }}
-
+
Fee @@ -183,6 +193,14 @@
+ Oops! Could not get the historical exchange rate from + {{ activityForm.controls['date']?.value | date: defaultDateFormat + }}
@@ -194,7 +212,7 @@ >
-
+
Note