Feature/extend asset profile for currency (#3495)
* Extend asset profile for currency * Update changelog
This commit is contained in:
parent
70e633b997
commit
ff121243e4
@ -10,6 +10,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||||||
### Added
|
### Added
|
||||||
|
|
||||||
- Added a dialog for the benchmarks in the markets overview
|
- Added a dialog for the benchmarks in the markets overview
|
||||||
|
- Extended the asset profile details dialog of the admin control for currencies
|
||||||
- Extended the content of the _Self-Hosting_ section by the mobile app question on the Frequently Asked Questions (FAQ) page
|
- Extended the content of the _Self-Hosting_ section by the mobile app question on the Frequently Asked Questions (FAQ) page
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
import { OrderModule } from '@ghostfolio/api/app/order/order.module';
|
||||||
import { SubscriptionModule } from '@ghostfolio/api/app/subscription/subscription.module';
|
import { SubscriptionModule } from '@ghostfolio/api/app/subscription/subscription.module';
|
||||||
import { TransformDataSourceInRequestModule } from '@ghostfolio/api/interceptors/transform-data-source-in-request/transform-data-source-in-request.module';
|
import { TransformDataSourceInRequestModule } from '@ghostfolio/api/interceptors/transform-data-source-in-request/transform-data-source-in-request.module';
|
||||||
import { ApiModule } from '@ghostfolio/api/services/api/api.module';
|
import { ApiModule } from '@ghostfolio/api/services/api/api.module';
|
||||||
@ -24,6 +25,7 @@ import { QueueModule } from './queue/queue.module';
|
|||||||
DataProviderModule,
|
DataProviderModule,
|
||||||
ExchangeRateDataModule,
|
ExchangeRateDataModule,
|
||||||
MarketDataModule,
|
MarketDataModule,
|
||||||
|
OrderModule,
|
||||||
PrismaModule,
|
PrismaModule,
|
||||||
PropertyModule,
|
PropertyModule,
|
||||||
QueueModule,
|
QueueModule,
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
import { OrderService } from '@ghostfolio/api/app/order/order.service';
|
||||||
import { SubscriptionService } from '@ghostfolio/api/app/subscription/subscription.service';
|
import { SubscriptionService } from '@ghostfolio/api/app/subscription/subscription.service';
|
||||||
import { environment } from '@ghostfolio/api/environments/environment';
|
import { environment } from '@ghostfolio/api/environments/environment';
|
||||||
import { ConfigurationService } from '@ghostfolio/api/services/configuration/configuration.service';
|
import { ConfigurationService } from '@ghostfolio/api/services/configuration/configuration.service';
|
||||||
@ -13,11 +14,13 @@ import {
|
|||||||
PROPERTY_IS_READ_ONLY_MODE,
|
PROPERTY_IS_READ_ONLY_MODE,
|
||||||
PROPERTY_IS_USER_SIGNUP_ENABLED
|
PROPERTY_IS_USER_SIGNUP_ENABLED
|
||||||
} from '@ghostfolio/common/config';
|
} from '@ghostfolio/common/config';
|
||||||
|
import { isCurrency, getCurrencyFromSymbol } from '@ghostfolio/common/helper';
|
||||||
import {
|
import {
|
||||||
AdminData,
|
AdminData,
|
||||||
AdminMarketData,
|
AdminMarketData,
|
||||||
AdminMarketDataDetails,
|
AdminMarketDataDetails,
|
||||||
AdminMarketDataItem,
|
AdminMarketDataItem,
|
||||||
|
EnhancedSymbolProfile,
|
||||||
Filter,
|
Filter,
|
||||||
UniqueAsset
|
UniqueAsset
|
||||||
} from '@ghostfolio/common/interfaces';
|
} from '@ghostfolio/common/interfaces';
|
||||||
@ -42,6 +45,7 @@ export class AdminService {
|
|||||||
private readonly dataProviderService: DataProviderService,
|
private readonly dataProviderService: DataProviderService,
|
||||||
private readonly exchangeRateDataService: ExchangeRateDataService,
|
private readonly exchangeRateDataService: ExchangeRateDataService,
|
||||||
private readonly marketDataService: MarketDataService,
|
private readonly marketDataService: MarketDataService,
|
||||||
|
private readonly orderService: OrderService,
|
||||||
private readonly prismaService: PrismaService,
|
private readonly prismaService: PrismaService,
|
||||||
private readonly propertyService: PropertyService,
|
private readonly propertyService: PropertyService,
|
||||||
private readonly subscriptionService: SubscriptionService,
|
private readonly subscriptionService: SubscriptionService,
|
||||||
@ -295,6 +299,16 @@ export class AdminService {
|
|||||||
dataSource,
|
dataSource,
|
||||||
symbol
|
symbol
|
||||||
}: UniqueAsset): Promise<AdminMarketDataDetails> {
|
}: UniqueAsset): Promise<AdminMarketDataDetails> {
|
||||||
|
let activitiesCount: EnhancedSymbolProfile['activitiesCount'] = 0;
|
||||||
|
let currency: EnhancedSymbolProfile['currency'] = '-';
|
||||||
|
let dateOfFirstActivity: EnhancedSymbolProfile['dateOfFirstActivity'];
|
||||||
|
|
||||||
|
if (isCurrency(getCurrencyFromSymbol(symbol))) {
|
||||||
|
currency = getCurrencyFromSymbol(symbol);
|
||||||
|
({ activitiesCount, dateOfFirstActivity } =
|
||||||
|
await this.orderService.getStatisticsByCurrency(currency));
|
||||||
|
}
|
||||||
|
|
||||||
const [[assetProfile], marketData] = await Promise.all([
|
const [[assetProfile], marketData] = await Promise.all([
|
||||||
this.symbolProfileService.getSymbolProfiles([
|
this.symbolProfileService.getSymbolProfiles([
|
||||||
{
|
{
|
||||||
@ -322,8 +336,11 @@ export class AdminService {
|
|||||||
return {
|
return {
|
||||||
marketData,
|
marketData,
|
||||||
assetProfile: assetProfile ?? {
|
assetProfile: assetProfile ?? {
|
||||||
symbol,
|
activitiesCount,
|
||||||
currency: '-'
|
currency,
|
||||||
|
dataSource,
|
||||||
|
dateOfFirstActivity,
|
||||||
|
symbol
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@ -413,19 +430,15 @@ export class AdminService {
|
|||||||
this.exchangeRateDataService
|
this.exchangeRateDataService
|
||||||
.getCurrencyPairs()
|
.getCurrencyPairs()
|
||||||
.map(async ({ dataSource, symbol }) => {
|
.map(async ({ dataSource, symbol }) => {
|
||||||
const currency = symbol.replace(DEFAULT_CURRENCY, '');
|
let activitiesCount: EnhancedSymbolProfile['activitiesCount'] = 0;
|
||||||
|
let currency: EnhancedSymbolProfile['currency'] = '-';
|
||||||
|
let dateOfFirstActivity: EnhancedSymbolProfile['dateOfFirstActivity'];
|
||||||
|
|
||||||
const { _count, _min } = await this.prismaService.order.aggregate({
|
if (isCurrency(getCurrencyFromSymbol(symbol))) {
|
||||||
_count: true,
|
currency = getCurrencyFromSymbol(symbol);
|
||||||
_min: {
|
({ activitiesCount, dateOfFirstActivity } =
|
||||||
date: true
|
await this.orderService.getStatisticsByCurrency(currency));
|
||||||
},
|
}
|
||||||
where: {
|
|
||||||
SymbolProfile: {
|
|
||||||
currency
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
const marketDataItemCount =
|
const marketDataItemCount =
|
||||||
marketDataItems.find((marketDataItem) => {
|
marketDataItems.find((marketDataItem) => {
|
||||||
@ -436,15 +449,15 @@ export class AdminService {
|
|||||||
})?._count ?? 0;
|
})?._count ?? 0;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
activitiesCount,
|
||||||
currency,
|
currency,
|
||||||
dataSource,
|
dataSource,
|
||||||
marketDataItemCount,
|
marketDataItemCount,
|
||||||
symbol,
|
symbol,
|
||||||
activitiesCount: _count as number,
|
|
||||||
assetClass: AssetClass.LIQUIDITY,
|
assetClass: AssetClass.LIQUIDITY,
|
||||||
assetSubClass: AssetSubClass.CASH,
|
assetSubClass: AssetSubClass.CASH,
|
||||||
countriesCount: 0,
|
countriesCount: 0,
|
||||||
date: _min.date,
|
date: dateOfFirstActivity,
|
||||||
id: undefined,
|
id: undefined,
|
||||||
name: symbol,
|
name: symbol,
|
||||||
sectorsCount: 0
|
sectorsCount: 0
|
||||||
|
@ -10,7 +10,11 @@ import {
|
|||||||
GATHER_ASSET_PROFILE_PROCESS_OPTIONS
|
GATHER_ASSET_PROFILE_PROCESS_OPTIONS
|
||||||
} from '@ghostfolio/common/config';
|
} from '@ghostfolio/common/config';
|
||||||
import { getAssetProfileIdentifier } from '@ghostfolio/common/helper';
|
import { getAssetProfileIdentifier } from '@ghostfolio/common/helper';
|
||||||
import { Filter, UniqueAsset } from '@ghostfolio/common/interfaces';
|
import {
|
||||||
|
EnhancedSymbolProfile,
|
||||||
|
Filter,
|
||||||
|
UniqueAsset
|
||||||
|
} from '@ghostfolio/common/interfaces';
|
||||||
import { OrderWithAccount } from '@ghostfolio/common/types';
|
import { OrderWithAccount } from '@ghostfolio/common/types';
|
||||||
|
|
||||||
import { Injectable } from '@nestjs/common';
|
import { Injectable } from '@nestjs/common';
|
||||||
@ -429,6 +433,26 @@ export class OrderService {
|
|||||||
return { activities, count };
|
return { activities, count };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async getStatisticsByCurrency(
|
||||||
|
currency: EnhancedSymbolProfile['currency']
|
||||||
|
): Promise<{
|
||||||
|
activitiesCount: EnhancedSymbolProfile['activitiesCount'];
|
||||||
|
dateOfFirstActivity: EnhancedSymbolProfile['dateOfFirstActivity'];
|
||||||
|
}> {
|
||||||
|
const { _count, _min } = await this.prismaService.order.aggregate({
|
||||||
|
_count: true,
|
||||||
|
_min: {
|
||||||
|
date: true
|
||||||
|
},
|
||||||
|
where: { SymbolProfile: { currency } }
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
activitiesCount: _count as number,
|
||||||
|
dateOfFirstActivity: _min.date
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
public async order(
|
public async order(
|
||||||
orderWhereUniqueInput: Prisma.OrderWhereUniqueInput
|
orderWhereUniqueInput: Prisma.OrderWhereUniqueInput
|
||||||
): Promise<Order | null> {
|
): Promise<Order | null> {
|
||||||
|
@ -146,7 +146,7 @@
|
|||||||
i18n
|
i18n
|
||||||
size="medium"
|
size="medium"
|
||||||
[locale]="data.locale"
|
[locale]="data.locale"
|
||||||
[value]="assetProfile?.activitiesCount ?? 0"
|
[value]="assetProfile?.activitiesCount"
|
||||||
>Activities</gf-value
|
>Activities</gf-value
|
||||||
>
|
>
|
||||||
</div>
|
</div>
|
||||||
|
@ -13,7 +13,12 @@ import {
|
|||||||
} from 'date-fns';
|
} from 'date-fns';
|
||||||
import { de, es, fr, it, nl, pl, pt, tr, zhCN } from 'date-fns/locale';
|
import { de, es, fr, it, nl, pl, pt, tr, zhCN } from 'date-fns/locale';
|
||||||
|
|
||||||
import { ghostfolioScraperApiSymbolPrefix, locale } from './config';
|
import {
|
||||||
|
DEFAULT_CURRENCY,
|
||||||
|
DERIVED_CURRENCIES,
|
||||||
|
ghostfolioScraperApiSymbolPrefix,
|
||||||
|
locale
|
||||||
|
} from './config';
|
||||||
import { Benchmark, UniqueAsset } from './interfaces';
|
import { Benchmark, UniqueAsset } from './interfaces';
|
||||||
import { BenchmarkTrend, ColorScheme } from './types';
|
import { BenchmarkTrend, ColorScheme } from './types';
|
||||||
|
|
||||||
@ -161,6 +166,10 @@ export function getCssVariable(aCssVariable: string) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function getCurrencyFromSymbol(aSymbol = '') {
|
||||||
|
return aSymbol.replace(DEFAULT_CURRENCY, '');
|
||||||
|
}
|
||||||
|
|
||||||
export function getDateFnsLocale(aLanguageCode: string) {
|
export function getDateFnsLocale(aLanguageCode: string) {
|
||||||
if (aLanguageCode === 'de') {
|
if (aLanguageCode === 'de') {
|
||||||
return de;
|
return de;
|
||||||
@ -322,8 +331,18 @@ export function interpolate(template: string, context: any) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export function isCurrency(aSymbol = '') {
|
export function isCurrency(aCurrency = '') {
|
||||||
return currencies[aSymbol];
|
return currencies[aCurrency] || isDerivedCurrency(aCurrency);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function isDerivedCurrency(aCurrency: string) {
|
||||||
|
if (aCurrency === 'USX') {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return DERIVED_CURRENCIES.find(({ currency }) => {
|
||||||
|
return currency === aCurrency;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export function parseDate(date: string): Date | null {
|
export function parseDate(date: string): Date | null {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user