Browse Source

Merge branch 'main' into task/extend-user-detail-dialog-by-actions-menu

pull/6256/head
Thomas Kaul 1 week ago
committed by GitHub
parent
commit
f58bf7ea8b
No known key found for this signature in database GPG Key ID: B5690EEEBB952194
  1. 16
      CHANGELOG.md
  2. 6
      apps/api/src/app/account/account.controller.ts
  3. 7
      apps/api/src/app/admin/admin.service.ts
  4. 3
      apps/api/src/app/portfolio/calculator/portfolio-calculator.ts
  5. 1
      apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-baln-buy-and-buy.spec.ts
  6. 1
      apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-baln-buy-and-sell-in-two-activities.spec.ts
  7. 1
      apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-baln-buy-and-sell.spec.ts
  8. 1
      apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-baln-buy.spec.ts
  9. 1
      apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-btceur.spec.ts
  10. 1
      apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-btcusd-buy-and-sell-partially.spec.ts
  11. 1
      apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-btcusd.spec.ts
  12. 1
      apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-cash.spec.ts
  13. 1
      apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-googl-buy.spec.ts
  14. 1
      apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-msft-buy-with-dividend.spec.ts
  15. 1
      apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-novn-buy-and-sell-partially.spec.ts
  16. 1
      apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-novn-buy-and-sell.spec.ts
  17. 1
      apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-valuable.spec.ts
  18. 4
      apps/api/src/app/portfolio/interfaces/transaction-point-symbol.interface.ts
  19. 2
      apps/api/src/app/portfolio/portfolio.controller.ts
  20. 22
      apps/api/src/app/portfolio/portfolio.service.ts
  21. 91
      apps/api/src/helper/object.helper.spec.ts
  22. 69
      apps/api/src/helper/object.helper.ts
  23. 42
      apps/api/src/interceptors/redact-values-in-response/redact-values-in-response.interceptor.ts
  24. 19
      apps/api/src/interceptors/transform-data-source-in-response/transform-data-source-in-response.interceptor.ts
  25. 17
      apps/api/src/services/data-provider/data-enhancer/yahoo-finance/yahoo-finance.service.ts
  26. 39
      apps/client/src/app/components/admin-market-data/admin-market-data.component.ts
  27. 8
      apps/client/src/app/components/admin-market-data/create-asset-profile-dialog/create-asset-profile-dialog.component.ts
  28. 6
      apps/client/src/app/components/admin-overview/admin-overview.component.ts
  29. 6
      apps/client/src/app/components/admin-overview/admin-overview.html
  30. 2
      apps/client/src/app/components/holding-detail-dialog/holding-detail-dialog.html
  31. 10
      apps/client/src/app/pages/accounts/accounts-page.component.ts
  32. 2
      apps/client/src/app/pages/accounts/accounts-page.html
  33. 180
      apps/client/src/locales/messages.es.xlf
  34. 52
      libs/common/src/lib/config.ts
  35. 14
      libs/common/src/lib/helper.ts
  36. 4
      libs/common/src/lib/interfaces/admin-data.interface.ts
  37. 3
      libs/common/src/lib/models/timeline-position.ts
  38. 4
      libs/ui/src/lib/accounts-table/accounts-table.component.html
  39. 14
      libs/ui/src/lib/accounts-table/accounts-table.component.stories.ts
  40. 8
      libs/ui/src/lib/accounts-table/accounts-table.component.ts
  41. 52
      package-lock.json
  42. 10
      package.json

16
CHANGELOG.md

@ -9,13 +9,29 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Added ### Added
- Added the ability to fetch top holdings for ETF and mutual fund assets from _Yahoo Finance_
- Added support for the impersonation mode in the endpoint `GET api/v1/account/:id/balances`
- Added an action menu to the user detail dialog in the users section of the admin control panel - Added an action menu to the user detail dialog in the users section of the admin control panel
### Changed ### Changed
- Optimized the value redaction interceptor for the impersonation mode by introducing `fast-redact`
- Refactored `showTransactions` in favor of `showActivitiesCount` in the accounts table component
- Refactored `transactionCount` in favor of `activitiesCount` in the accounts table component
- Deprecated `transactionCount` in favor of `activitiesCount` in the endpoint `GET api/v1/admin`
- Removed the deprecated `firstBuyDate` in the portfolio calculator
- Upgraded `yahoo-finance2` from version `3.11.2` to `3.13.0`
## 2.234.0 - 2026-01-30
### Changed
- Improved the usability of the create asset profile dialog in the market data section of the admin control panel
- Improved the language localization for Chinese (`zh`) - Improved the language localization for Chinese (`zh`)
- Improved the language localization for German (`de`) - Improved the language localization for German (`de`)
- Improved the language localization for Spanish (`es`)
- Upgraded `angular` from version `21.0.6` to `21.1.1` - Upgraded `angular` from version `21.0.6` to `21.1.1`
- Upgraded `lodash` from version `4.17.21` to `4.17.23`
- Upgraded `Nx` from version `22.3.3` to `22.4.1` - Upgraded `Nx` from version `22.3.3` to `22.4.1`
- Upgraded `prettier` from version `3.8.0` to `3.8.1` - Upgraded `prettier` from version `3.8.0` to `3.8.1`

6
apps/api/src/app/account/account.controller.ts

@ -132,12 +132,16 @@ export class AccountController {
@UseGuards(AuthGuard('jwt'), HasPermissionGuard) @UseGuards(AuthGuard('jwt'), HasPermissionGuard)
@UseInterceptors(RedactValuesInResponseInterceptor) @UseInterceptors(RedactValuesInResponseInterceptor)
public async getAccountBalancesById( public async getAccountBalancesById(
@Headers(HEADER_KEY_IMPERSONATION.toLowerCase()) impersonationId: string,
@Param('id') id: string @Param('id') id: string
): Promise<AccountBalancesResponse> { ): Promise<AccountBalancesResponse> {
const impersonationUserId =
await this.impersonationService.validateImpersonationId(impersonationId);
return this.accountBalanceService.getAccountBalances({ return this.accountBalanceService.getAccountBalances({
filters: [{ id, type: 'ACCOUNT' }], filters: [{ id, type: 'ACCOUNT' }],
userCurrency: this.request.user.settings.settings.baseCurrency, userCurrency: this.request.user.settings.settings.baseCurrency,
userId: this.request.user.id userId: impersonationUserId || this.request.user.id
}); });
} }

7
apps/api/src/app/admin/admin.service.ts

@ -138,11 +138,11 @@ export class AdminService {
public async get(): Promise<AdminData> { public async get(): Promise<AdminData> {
const dataSources = Object.values(DataSource); const dataSources = Object.values(DataSource);
const [enabledDataSources, settings, transactionCount, userCount] = const [activitiesCount, enabledDataSources, settings, userCount] =
await Promise.all([ await Promise.all([
this.prismaService.order.count(),
this.dataProviderService.getDataSources(), this.dataProviderService.getDataSources(),
this.propertyService.get(), this.propertyService.get(),
this.prismaService.order.count(),
this.countUsersWithAnalytics() this.countUsersWithAnalytics()
]); ]);
@ -182,10 +182,11 @@ export class AdminService {
).filter(Boolean); ).filter(Boolean);
return { return {
activitiesCount,
dataProviders, dataProviders,
settings, settings,
transactionCount,
userCount, userCount,
transactionCount: activitiesCount,
version: environment.version version: environment.version
}; };
} }

3
apps/api/src/app/portfolio/calculator/portfolio-calculator.ts

@ -416,7 +416,6 @@ export abstract class PortfolioCalculator {
dividendInBaseCurrency: totalDividendInBaseCurrency, dividendInBaseCurrency: totalDividendInBaseCurrency,
fee: item.fee, fee: item.fee,
feeInBaseCurrency: item.feeInBaseCurrency, feeInBaseCurrency: item.feeInBaseCurrency,
firstBuyDate: item.firstBuyDate,
grossPerformance: !hasErrors ? (grossPerformance ?? null) : null, grossPerformance: !hasErrors ? (grossPerformance ?? null) : null,
grossPerformancePercentage: !hasErrors grossPerformancePercentage: !hasErrors
? (grossPerformancePercentage ?? null) ? (grossPerformancePercentage ?? null)
@ -1004,7 +1003,6 @@ export abstract class PortfolioCalculator {
fee: oldAccumulatedSymbol.fee.plus(fee), fee: oldAccumulatedSymbol.fee.plus(fee),
feeInBaseCurrency: feeInBaseCurrency:
oldAccumulatedSymbol.feeInBaseCurrency.plus(feeInBaseCurrency), oldAccumulatedSymbol.feeInBaseCurrency.plus(feeInBaseCurrency),
firstBuyDate: oldAccumulatedSymbol.firstBuyDate,
includeInHoldings: oldAccumulatedSymbol.includeInHoldings, includeInHoldings: oldAccumulatedSymbol.includeInHoldings,
quantity: newQuantity, quantity: newQuantity,
tags: oldAccumulatedSymbol.tags.concat(tags), tags: oldAccumulatedSymbol.tags.concat(tags),
@ -1024,7 +1022,6 @@ export abstract class PortfolioCalculator {
averagePrice: unitPrice, averagePrice: unitPrice,
dateOfFirstActivity: date, dateOfFirstActivity: date,
dividend: new Big(0), dividend: new Big(0),
firstBuyDate: date,
includeInHoldings: INVESTMENT_ACTIVITY_TYPES.includes(type), includeInHoldings: INVESTMENT_ACTIVITY_TYPES.includes(type),
investment: unitPrice.mul(quantity).mul(factor), investment: unitPrice.mul(quantity).mul(factor),
quantity: quantity.mul(factor), quantity: quantity.mul(factor),

1
apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-baln-buy-and-buy.spec.ts

@ -153,7 +153,6 @@ describe('PortfolioCalculator', () => {
dividendInBaseCurrency: new Big('0'), dividendInBaseCurrency: new Big('0'),
fee: new Big('3.2'), fee: new Big('3.2'),
feeInBaseCurrency: new Big('3.2'), feeInBaseCurrency: new Big('3.2'),
firstBuyDate: '2021-11-22',
grossPerformance: new Big('36.6'), grossPerformance: new Big('36.6'),
grossPerformancePercentage: new Big('0.07706261539956593567'), grossPerformancePercentage: new Big('0.07706261539956593567'),
grossPerformancePercentageWithCurrencyEffect: new Big( grossPerformancePercentageWithCurrencyEffect: new Big(

1
apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-baln-buy-and-sell-in-two-activities.spec.ts

@ -169,7 +169,6 @@ describe('PortfolioCalculator', () => {
dividendInBaseCurrency: new Big('0'), dividendInBaseCurrency: new Big('0'),
fee: new Big('3.2'), fee: new Big('3.2'),
feeInBaseCurrency: new Big('3.2'), feeInBaseCurrency: new Big('3.2'),
firstBuyDate: '2021-11-22',
grossPerformance: new Big('-12.6'), grossPerformance: new Big('-12.6'),
grossPerformancePercentage: new Big('-0.04408677396780965649'), grossPerformancePercentage: new Big('-0.04408677396780965649'),
grossPerformancePercentageWithCurrencyEffect: new Big( grossPerformancePercentageWithCurrencyEffect: new Big(

1
apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-baln-buy-and-sell.spec.ts

@ -153,7 +153,6 @@ describe('PortfolioCalculator', () => {
dividendInBaseCurrency: new Big('0'), dividendInBaseCurrency: new Big('0'),
fee: new Big('3.2'), fee: new Big('3.2'),
feeInBaseCurrency: new Big('3.2'), feeInBaseCurrency: new Big('3.2'),
firstBuyDate: '2021-11-22',
grossPerformance: new Big('-12.6'), grossPerformance: new Big('-12.6'),
grossPerformancePercentage: new Big('-0.0440867739678096571'), grossPerformancePercentage: new Big('-0.0440867739678096571'),
grossPerformancePercentageWithCurrencyEffect: new Big( grossPerformancePercentageWithCurrencyEffect: new Big(

1
apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-baln-buy.spec.ts

@ -143,7 +143,6 @@ describe('PortfolioCalculator', () => {
dividendInBaseCurrency: new Big('0'), dividendInBaseCurrency: new Big('0'),
fee: new Big('1.55'), fee: new Big('1.55'),
feeInBaseCurrency: new Big('1.55'), feeInBaseCurrency: new Big('1.55'),
firstBuyDate: '2021-11-30',
grossPerformance: new Big('24.6'), grossPerformance: new Big('24.6'),
grossPerformancePercentage: new Big('0.09004392386530014641'), grossPerformancePercentage: new Big('0.09004392386530014641'),
grossPerformancePercentageWithCurrencyEffect: new Big( grossPerformancePercentageWithCurrencyEffect: new Big(

1
apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-btceur.spec.ts

@ -204,7 +204,6 @@ describe('PortfolioCalculator', () => {
dividendInBaseCurrency: new Big('0'), dividendInBaseCurrency: new Big('0'),
fee: new Big('4.46'), fee: new Big('4.46'),
feeInBaseCurrency: new Big('4.46'), feeInBaseCurrency: new Big('4.46'),
firstBuyDate: '2021-12-12',
grossPerformance: new Big('-1458.72'), grossPerformance: new Big('-1458.72'),
grossPerformancePercentage: new Big('-0.03273724696701543726'), grossPerformancePercentage: new Big('-0.03273724696701543726'),
grossPerformancePercentageWithCurrencyEffect: new Big( grossPerformancePercentageWithCurrencyEffect: new Big(

1
apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-btcusd-buy-and-sell-partially.spec.ts

@ -167,7 +167,6 @@ describe('PortfolioCalculator', () => {
dividendInBaseCurrency: new Big('0'), dividendInBaseCurrency: new Big('0'),
fee: new Big('0'), fee: new Big('0'),
feeInBaseCurrency: new Big('0'), feeInBaseCurrency: new Big('0'),
firstBuyDate: '2015-01-01',
grossPerformance: new Big('27172.74').mul(0.97373), grossPerformance: new Big('27172.74').mul(0.97373),
grossPerformancePercentage: new Big('0.4241983590271396608571'), grossPerformancePercentage: new Big('0.4241983590271396608571'),
grossPerformancePercentageWithCurrencyEffect: new Big( grossPerformancePercentageWithCurrencyEffect: new Big(

1
apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-btcusd.spec.ts

@ -204,7 +204,6 @@ describe('PortfolioCalculator', () => {
dividendInBaseCurrency: new Big('0'), dividendInBaseCurrency: new Big('0'),
fee: new Big('4.46'), fee: new Big('4.46'),
feeInBaseCurrency: new Big('4.46'), feeInBaseCurrency: new Big('4.46'),
firstBuyDate: '2021-12-12',
grossPerformance: new Big('-1458.72'), grossPerformance: new Big('-1458.72'),
grossPerformancePercentage: new Big('-0.03273724696701543726'), grossPerformancePercentage: new Big('-0.03273724696701543726'),
grossPerformancePercentageWithCurrencyEffect: new Big( grossPerformancePercentageWithCurrencyEffect: new Big(

1
apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-cash.spec.ts

@ -239,7 +239,6 @@ describe('PortfolioCalculator', () => {
dividendInBaseCurrency: new Big(0), dividendInBaseCurrency: new Big(0),
fee: new Big(0), fee: new Big(0),
feeInBaseCurrency: new Big(0), feeInBaseCurrency: new Big(0),
firstBuyDate: '2023-12-31',
grossPerformance: new Big(0), grossPerformance: new Big(0),
grossPerformancePercentage: new Big(0), grossPerformancePercentage: new Big(0),
grossPerformancePercentageWithCurrencyEffect: new Big( grossPerformancePercentageWithCurrencyEffect: new Big(

1
apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-googl-buy.spec.ts

@ -149,7 +149,6 @@ describe('PortfolioCalculator', () => {
dividendInBaseCurrency: new Big('0'), dividendInBaseCurrency: new Big('0'),
fee: new Big('1'), fee: new Big('1'),
feeInBaseCurrency: new Big('0.9238'), feeInBaseCurrency: new Big('0.9238'),
firstBuyDate: '2023-01-03',
grossPerformance: new Big('27.33').mul(0.8854), grossPerformance: new Big('27.33').mul(0.8854),
grossPerformancePercentage: new Big('0.3066651705565529623'), grossPerformancePercentage: new Big('0.3066651705565529623'),
grossPerformancePercentageWithCurrencyEffect: new Big( grossPerformancePercentageWithCurrencyEffect: new Big(

1
apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-msft-buy-with-dividend.spec.ts

@ -139,7 +139,6 @@ describe('PortfolioCalculator', () => {
dividend: new Big('0.62'), dividend: new Big('0.62'),
dividendInBaseCurrency: new Big('0.62'), dividendInBaseCurrency: new Big('0.62'),
fee: new Big('19'), fee: new Big('19'),
firstBuyDate: '2021-09-16',
grossPerformance: new Big('33.25'), grossPerformance: new Big('33.25'),
grossPerformancePercentage: new Big('0.11136043941322258691'), grossPerformancePercentage: new Big('0.11136043941322258691'),
grossPerformancePercentageWithCurrencyEffect: new Big( grossPerformancePercentageWithCurrencyEffect: new Big(

1
apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-novn-buy-and-sell-partially.spec.ts

@ -149,7 +149,6 @@ describe('PortfolioCalculator', () => {
dividendInBaseCurrency: new Big('0'), dividendInBaseCurrency: new Big('0'),
fee: new Big('4.25'), fee: new Big('4.25'),
feeInBaseCurrency: new Big('4.25'), feeInBaseCurrency: new Big('4.25'),
firstBuyDate: '2022-03-07',
grossPerformance: new Big('21.93'), grossPerformance: new Big('21.93'),
grossPerformancePercentage: new Big('0.15113417083448194384'), grossPerformancePercentage: new Big('0.15113417083448194384'),
grossPerformancePercentageWithCurrencyEffect: new Big( grossPerformancePercentageWithCurrencyEffect: new Big(

1
apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-novn-buy-and-sell.spec.ts

@ -202,7 +202,6 @@ describe('PortfolioCalculator', () => {
dividendInBaseCurrency: new Big('0'), dividendInBaseCurrency: new Big('0'),
fee: new Big('0'), fee: new Big('0'),
feeInBaseCurrency: new Big('0'), feeInBaseCurrency: new Big('0'),
firstBuyDate: '2022-03-07',
grossPerformance: new Big('19.86'), grossPerformance: new Big('19.86'),
grossPerformancePercentage: new Big('0.13100263852242744063'), grossPerformancePercentage: new Big('0.13100263852242744063'),
grossPerformancePercentageWithCurrencyEffect: new Big( grossPerformancePercentageWithCurrencyEffect: new Big(

1
apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-valuable.spec.ts

@ -125,7 +125,6 @@ describe('PortfolioCalculator', () => {
dividendInBaseCurrency: new Big('0'), dividendInBaseCurrency: new Big('0'),
fee: new Big('0'), fee: new Big('0'),
feeInBaseCurrency: new Big('0'), feeInBaseCurrency: new Big('0'),
firstBuyDate: '2022-01-01',
grossPerformance: new Big('0'), grossPerformance: new Big('0'),
grossPerformancePercentage: new Big('0'), grossPerformancePercentage: new Big('0'),
grossPerformancePercentageWithCurrencyEffect: new Big('0'), grossPerformancePercentageWithCurrencyEffect: new Big('0'),

4
apps/api/src/app/portfolio/interfaces/transaction-point-symbol.interface.ts

@ -11,10 +11,6 @@ export interface TransactionPointSymbol {
dividend: Big; dividend: Big;
fee: Big; fee: Big;
feeInBaseCurrency: Big; feeInBaseCurrency: Big;
/** @deprecated use dateOfFirstActivity instead */
firstBuyDate: string;
includeInHoldings: boolean; includeInHoldings: boolean;
investment: Big; investment: Big;
quantity: Big; quantity: Big;

2
apps/api/src/app/portfolio/portfolio.controller.ts

@ -195,11 +195,13 @@ export class PortfolioController {
'excludedAccountsAndActivities', 'excludedAccountsAndActivities',
'fees', 'fees',
'filteredValueInBaseCurrency', 'filteredValueInBaseCurrency',
'fireWealth',
'grossPerformance', 'grossPerformance',
'grossPerformanceWithCurrencyEffect', 'grossPerformanceWithCurrencyEffect',
'interestInBaseCurrency', 'interestInBaseCurrency',
'items', 'items',
'liabilities', 'liabilities',
'liabilitiesInBaseCurrency',
'netPerformance', 'netPerformance',
'netPerformanceWithCurrencyEffect', 'netPerformanceWithCurrencyEffect',
'totalBuy', 'totalBuy',

22
apps/api/src/app/portfolio/portfolio.service.ts

@ -576,8 +576,8 @@ export class PortfolioService {
for (const { for (const {
activitiesCount, activitiesCount,
currency, currency,
dateOfFirstActivity,
dividend, dividend,
firstBuyDate,
grossPerformance, grossPerformance,
grossPerformanceWithCurrencyEffect, grossPerformanceWithCurrencyEffect,
grossPerformancePercentage, grossPerformancePercentage,
@ -633,7 +633,7 @@ export class PortfolioService {
assetSubClass: assetProfile.assetSubClass, assetSubClass: assetProfile.assetSubClass,
countries: assetProfile.countries, countries: assetProfile.countries,
dataSource: assetProfile.dataSource, dataSource: assetProfile.dataSource,
dateOfFirstActivity: parseDate(firstBuyDate), dateOfFirstActivity: parseDate(dateOfFirstActivity),
dividend: dividend?.toNumber() ?? 0, dividend: dividend?.toNumber() ?? 0,
grossPerformance: grossPerformance?.toNumber() ?? 0, grossPerformance: grossPerformance?.toNumber() ?? 0,
grossPerformancePercent: grossPerformancePercentage?.toNumber() ?? 0, grossPerformancePercent: grossPerformancePercentage?.toNumber() ?? 0,
@ -801,9 +801,9 @@ export class PortfolioService {
activitiesCount, activitiesCount,
averagePrice, averagePrice,
currency, currency,
dateOfFirstActivity,
dividendInBaseCurrency, dividendInBaseCurrency,
feeInBaseCurrency, feeInBaseCurrency,
firstBuyDate,
grossPerformance, grossPerformance,
grossPerformancePercentage, grossPerformancePercentage,
grossPerformancePercentageWithCurrencyEffect, grossPerformancePercentageWithCurrencyEffect,
@ -828,7 +828,10 @@ export class PortfolioService {
}); });
const dividendYieldPercent = getAnnualizedPerformancePercent({ const dividendYieldPercent = getAnnualizedPerformancePercent({
daysInMarket: differenceInDays(new Date(), parseDate(firstBuyDate)), daysInMarket: differenceInDays(
new Date(),
parseDate(dateOfFirstActivity)
),
netPerformancePercentage: timeWeightedInvestment.eq(0) netPerformancePercentage: timeWeightedInvestment.eq(0)
? new Big(0) ? new Big(0)
: dividendInBaseCurrency.div(timeWeightedInvestment) : dividendInBaseCurrency.div(timeWeightedInvestment)
@ -836,7 +839,10 @@ export class PortfolioService {
const dividendYieldPercentWithCurrencyEffect = const dividendYieldPercentWithCurrencyEffect =
getAnnualizedPerformancePercent({ getAnnualizedPerformancePercent({
daysInMarket: differenceInDays(new Date(), parseDate(firstBuyDate)), daysInMarket: differenceInDays(
new Date(),
parseDate(dateOfFirstActivity)
),
netPerformancePercentage: timeWeightedInvestmentWithCurrencyEffect.eq(0) netPerformancePercentage: timeWeightedInvestmentWithCurrencyEffect.eq(0)
? new Big(0) ? new Big(0)
: dividendInBaseCurrency.div(timeWeightedInvestmentWithCurrencyEffect) : dividendInBaseCurrency.div(timeWeightedInvestmentWithCurrencyEffect)
@ -845,7 +851,7 @@ export class PortfolioService {
const historicalData = await this.dataProviderService.getHistorical( const historicalData = await this.dataProviderService.getHistorical(
[{ dataSource, symbol }], [{ dataSource, symbol }],
'day', 'day',
parseISO(firstBuyDate), parseISO(dateOfFirstActivity),
new Date() new Date()
); );
@ -910,7 +916,7 @@ export class PortfolioService {
// Add historical entry for buy date, if no historical data available // Add historical entry for buy date, if no historical data available
historicalDataArray.push({ historicalDataArray.push({
averagePrice: activitiesOfHolding[0].unitPriceInAssetProfileCurrency, averagePrice: activitiesOfHolding[0].unitPriceInAssetProfileCurrency,
date: firstBuyDate, date: dateOfFirstActivity,
marketPrice: activitiesOfHolding[0].unitPriceInAssetProfileCurrency, marketPrice: activitiesOfHolding[0].unitPriceInAssetProfileCurrency,
quantity: activitiesOfHolding[0].quantity quantity: activitiesOfHolding[0].quantity
}); });
@ -924,6 +930,7 @@ export class PortfolioService {
return { return {
activitiesCount, activitiesCount,
dateOfFirstActivity,
marketPrice, marketPrice,
marketPriceMax, marketPriceMax,
marketPriceMin, marketPriceMin,
@ -931,7 +938,6 @@ export class PortfolioService {
tags, tags,
averagePrice: averagePrice.toNumber(), averagePrice: averagePrice.toNumber(),
dataProviderInfo: portfolioCalculator.getDataProviderInfos()?.[0], dataProviderInfo: portfolioCalculator.getDataProviderInfos()?.[0],
dateOfFirstActivity: firstBuyDate,
dividendInBaseCurrency: dividendInBaseCurrency.toNumber(), dividendInBaseCurrency: dividendInBaseCurrency.toNumber(),
dividendYieldPercent: dividendYieldPercent.toNumber(), dividendYieldPercent: dividendYieldPercent.toNumber(),
dividendYieldPercentWithCurrencyEffect: dividendYieldPercentWithCurrencyEffect:

91
apps/api/src/helper/object.helper.spec.ts

@ -1,4 +1,6 @@
import { query, redactAttributes } from './object.helper'; import { DEFAULT_REDACTED_PATHS } from '@ghostfolio/common/config';
import { query, redactPaths } from './object.helper';
describe('query', () => { describe('query', () => {
it('should get market price from stock API response', () => { it('should get market price from stock API response', () => {
@ -22,46 +24,38 @@ describe('query', () => {
describe('redactAttributes', () => { describe('redactAttributes', () => {
it('should redact provided attributes', () => { it('should redact provided attributes', () => {
expect(redactAttributes({ object: {}, options: [] })).toStrictEqual({}); expect(redactPaths({ object: {}, paths: [] })).toStrictEqual({});
expect( expect(redactPaths({ object: { value: 1000 }, paths: [] })).toStrictEqual({
redactAttributes({ object: { value: 1000 }, options: [] }) value: 1000
).toStrictEqual({ value: 1000 }); });
expect( expect(
redactAttributes({ redactPaths({
object: { value: 1000 }, object: { value: 1000 },
options: [{ attribute: 'value', valueMap: { '*': null } }] paths: ['value']
}) })
).toStrictEqual({ value: null }); ).toStrictEqual({ value: null });
expect( expect(
redactAttributes({ redactPaths({
object: { value: 'abc' }, object: { value: 'abc' },
options: [{ attribute: 'value', valueMap: { abc: 'xyz' } }] paths: ['value'],
valueMap: { abc: 'xyz' }
}) })
).toStrictEqual({ value: 'xyz' }); ).toStrictEqual({ value: 'xyz' });
expect( expect(
redactAttributes({ redactPaths({
object: { data: [{ value: 'a' }, { value: 'b' }] }, object: { data: [{ value: 'a' }, { value: 'b' }] },
options: [{ attribute: 'value', valueMap: { a: 1, b: 2 } }] paths: ['data[*].value'],
valueMap: { a: 1, b: 2 }
}) })
).toStrictEqual({ data: [{ value: 1 }, { value: 2 }] }); ).toStrictEqual({ data: [{ value: 1 }, { value: 2 }] });
expect(
redactAttributes({
object: { value1: 'a', value2: 'b' },
options: [
{ attribute: 'value1', valueMap: { a: 'x' } },
{ attribute: 'value2', valueMap: { '*': 'y' } }
]
})
).toStrictEqual({ value1: 'x', value2: 'y' });
console.time('redactAttributes execution time'); console.time('redactAttributes execution time');
expect( expect(
redactAttributes({ redactPaths({
object: { object: {
accounts: { accounts: {
'2e937c05-657c-4de9-8fb3-0813a2245f26': { '2e937c05-657c-4de9-8fb3-0813a2245f26': {
@ -1564,34 +1558,7 @@ describe('redactAttributes', () => {
currentNetWorth: null currentNetWorth: null
} }
}, },
options: [ paths: DEFAULT_REDACTED_PATHS
'balance',
'balanceInBaseCurrency',
'comment',
'convertedBalance',
'dividendInBaseCurrency',
'fee',
'feeInBaseCurrency',
'grossPerformance',
'grossPerformanceWithCurrencyEffect',
'investment',
'netPerformance',
'netPerformanceWithCurrencyEffect',
'quantity',
'symbolMapping',
'totalBalanceInBaseCurrency',
'totalValueInBaseCurrency',
'unitPrice',
'value',
'valueInBaseCurrency'
].map((attribute) => {
return {
attribute,
valueMap: {
'*': null
}
};
})
}) })
).toStrictEqual({ ).toStrictEqual({
accounts: { accounts: {
@ -1681,7 +1648,7 @@ describe('redactAttributes', () => {
], ],
dataSource: 'EOD_HISTORICAL_DATA', dataSource: 'EOD_HISTORICAL_DATA',
dateOfFirstActivity: '2021-11-30T23:00:00.000Z', dateOfFirstActivity: '2021-11-30T23:00:00.000Z',
dividend: 0, dividend: null,
grossPerformance: null, grossPerformance: null,
grossPerformancePercent: 0.3183066634822068, grossPerformancePercent: 0.3183066634822068,
grossPerformancePercentWithCurrencyEffect: 0.3183066634822068, grossPerformancePercentWithCurrencyEffect: 0.3183066634822068,
@ -1728,7 +1695,7 @@ describe('redactAttributes', () => {
], ],
dataSource: 'YAHOO', dataSource: 'YAHOO',
dateOfFirstActivity: '2021-04-22T22:00:00.000Z', dateOfFirstActivity: '2021-04-22T22:00:00.000Z',
dividend: 192, dividend: null,
grossPerformance: null, grossPerformance: null,
grossPerformancePercent: 0.3719230057375532, grossPerformancePercent: 0.3719230057375532,
grossPerformancePercentWithCurrencyEffect: 0.2650716044872953, grossPerformancePercentWithCurrencyEffect: 0.2650716044872953,
@ -1780,7 +1747,7 @@ describe('redactAttributes', () => {
], ],
dataSource: 'YAHOO', dataSource: 'YAHOO',
dateOfFirstActivity: '2018-09-30T22:00:00.000Z', dateOfFirstActivity: '2018-09-30T22:00:00.000Z',
dividend: 0, dividend: null,
grossPerformance: null, grossPerformance: null,
grossPerformancePercent: 0.8594552890963852, grossPerformancePercent: 0.8594552890963852,
grossPerformancePercentWithCurrencyEffect: 0.8594552890963852, grossPerformancePercentWithCurrencyEffect: 0.8594552890963852,
@ -1831,7 +1798,7 @@ describe('redactAttributes', () => {
countries: [], countries: [],
dataSource: 'COINGECKO', dataSource: 'COINGECKO',
dateOfFirstActivity: '2017-08-15T22:00:00.000Z', dateOfFirstActivity: '2017-08-15T22:00:00.000Z',
dividend: 0, dividend: null,
grossPerformance: null, grossPerformance: null,
grossPerformancePercent: 17.4925166352, grossPerformancePercent: 17.4925166352,
grossPerformancePercentWithCurrencyEffect: 17.4925166352, grossPerformancePercentWithCurrencyEffect: 17.4925166352,
@ -1882,7 +1849,7 @@ describe('redactAttributes', () => {
countries: [], countries: [],
dataSource: 'MANUAL', dataSource: 'MANUAL',
dateOfFirstActivity: '2021-01-31T23:00:00.000Z', dateOfFirstActivity: '2021-01-31T23:00:00.000Z',
dividend: 11.45, dividend: null,
grossPerformance: null, grossPerformance: null,
grossPerformancePercent: 0, grossPerformancePercent: 0,
grossPerformancePercentWithCurrencyEffect: -0.06153834320225245, grossPerformancePercentWithCurrencyEffect: -0.06153834320225245,
@ -1986,7 +1953,7 @@ describe('redactAttributes', () => {
], ],
dataSource: 'MANUAL', dataSource: 'MANUAL',
dateOfFirstActivity: '2021-03-31T22:00:00.000Z', dateOfFirstActivity: '2021-03-31T22:00:00.000Z',
dividend: 0, dividend: null,
grossPerformance: null, grossPerformance: null,
grossPerformancePercent: 0.27579517683678895, grossPerformancePercent: 0.27579517683678895,
grossPerformancePercentWithCurrencyEffect: 0.458553421589667, grossPerformancePercentWithCurrencyEffect: 0.458553421589667,
@ -2038,7 +2005,7 @@ describe('redactAttributes', () => {
], ],
dataSource: 'YAHOO', dataSource: 'YAHOO',
dateOfFirstActivity: '2023-01-02T23:00:00.000Z', dateOfFirstActivity: '2023-01-02T23:00:00.000Z',
dividend: 0, dividend: null,
grossPerformance: null, grossPerformance: null,
grossPerformancePercent: 0.7865431171216295, grossPerformancePercent: 0.7865431171216295,
grossPerformancePercentWithCurrencyEffect: 0.7865431171216295, grossPerformancePercentWithCurrencyEffect: 0.7865431171216295,
@ -2090,7 +2057,7 @@ describe('redactAttributes', () => {
], ],
dataSource: 'YAHOO', dataSource: 'YAHOO',
dateOfFirstActivity: '2017-01-02T23:00:00.000Z', dateOfFirstActivity: '2017-01-02T23:00:00.000Z',
dividend: 0, dividend: null,
grossPerformance: null, grossPerformance: null,
grossPerformancePercent: 17.184314638161936, grossPerformancePercent: 17.184314638161936,
grossPerformancePercentWithCurrencyEffect: 17.184314638161936, grossPerformancePercentWithCurrencyEffect: 17.184314638161936,
@ -2172,7 +2139,7 @@ describe('redactAttributes', () => {
], ],
dataSource: 'YAHOO', dataSource: 'YAHOO',
dateOfFirstActivity: '2019-02-28T23:00:00.000Z', dateOfFirstActivity: '2019-02-28T23:00:00.000Z',
dividend: 0, dividend: null,
grossPerformance: null, grossPerformance: null,
grossPerformancePercent: 0.8832083851170418, grossPerformancePercent: 0.8832083851170418,
grossPerformancePercentWithCurrencyEffect: 0.8832083851170418, grossPerformancePercentWithCurrencyEffect: 0.8832083851170418,
@ -2567,7 +2534,7 @@ describe('redactAttributes', () => {
], ],
dataSource: 'YAHOO', dataSource: 'YAHOO',
dateOfFirstActivity: '2018-02-28T23:00:00.000Z', dateOfFirstActivity: '2018-02-28T23:00:00.000Z',
dividend: 0, dividend: null,
grossPerformance: null, grossPerformance: null,
grossPerformancePercent: 0.3683200415015591, grossPerformancePercent: 0.3683200415015591,
grossPerformancePercentWithCurrencyEffect: 0.5806366182968891, grossPerformancePercentWithCurrencyEffect: 0.5806366182968891,
@ -2846,7 +2813,7 @@ describe('redactAttributes', () => {
], ],
dataSource: 'YAHOO', dataSource: 'YAHOO',
dateOfFirstActivity: '2021-08-18T22:00:00.000Z', dateOfFirstActivity: '2021-08-18T22:00:00.000Z',
dividend: 0, dividend: null,
grossPerformance: null, grossPerformance: null,
grossPerformancePercent: 0.3474381850624522, grossPerformancePercent: 0.3474381850624522,
grossPerformancePercentWithCurrencyEffect: 0.28744846894552306, grossPerformancePercentWithCurrencyEffect: 0.28744846894552306,
@ -2964,7 +2931,7 @@ describe('redactAttributes', () => {
assetClass: 'LIQUIDITY', assetClass: 'LIQUIDITY',
assetSubClass: 'CASH', assetSubClass: 'CASH',
countries: [], countries: [],
dividend: 0, dividend: null,
grossPerformance: null, grossPerformance: null,
grossPerformancePercent: 0, grossPerformancePercent: 0,
grossPerformancePercentWithCurrencyEffect: 0, grossPerformancePercentWithCurrencyEffect: 0,

69
apps/api/src/helper/object.helper.ts

@ -1,6 +1,6 @@
import { Big } from 'big.js'; import fastRedact from 'fast-redact';
import jsonpath from 'jsonpath'; import jsonpath from 'jsonpath';
import { cloneDeep, isArray, isObject } from 'lodash'; import { cloneDeep, isObject } from 'lodash';
export function hasNotDefinedValuesInObject(aObject: Object): boolean { export function hasNotDefinedValuesInObject(aObject: Object): boolean {
for (const key in aObject) { for (const key in aObject) {
@ -42,60 +42,29 @@ export function query({
return jsonpath.query(object, pathExpression); return jsonpath.query(object, pathExpression);
} }
export function redactAttributes({ export function redactPaths({
isFirstRun = true,
object, object,
options paths,
valueMap
}: { }: {
isFirstRun?: boolean;
object: any; object: any;
options: { attribute: string; valueMap: { [key: string]: any } }[]; paths: fastRedact.RedactOptions['paths'];
valueMap?: { [key: string]: any };
}): any { }): any {
if (!object || !options?.length) { const redact = fastRedact({
return object; paths,
} censor: (value) => {
if (valueMap) {
// Create deep clone if (valueMap[value]) {
const redactedObject = isFirstRun return valueMap[value];
? JSON.parse(JSON.stringify(object)) } else {
: object; return value;
for (const option of options) {
if (redactedObject.hasOwnProperty(option.attribute)) {
if (option.valueMap['*'] || option.valueMap['*'] === null) {
redactedObject[option.attribute] = option.valueMap['*'];
} else if (option.valueMap[redactedObject[option.attribute]]) {
redactedObject[option.attribute] =
option.valueMap[redactedObject[option.attribute]];
}
} else {
// If the attribute is not present on the current object,
// check if it exists on any nested objects
for (const property in redactedObject) {
if (isArray(redactedObject[property])) {
redactedObject[property] = redactedObject[property].map(
(currentObject) => {
return redactAttributes({
options,
isFirstRun: false,
object: currentObject
});
}
);
} else if (
isObject(redactedObject[property]) &&
!(redactedObject[property] instanceof Big)
) {
// Recursively call the function on the nested object
redactedObject[property] = redactAttributes({
options,
isFirstRun: false,
object: redactedObject[property]
});
} }
} else {
return null;
} }
} }
} });
return redactedObject; return JSON.parse(redact(object));
} }

42
apps/api/src/interceptors/redact-values-in-response/redact-values-in-response.interceptor.ts

@ -1,5 +1,8 @@
import { redactAttributes } from '@ghostfolio/api/helper/object.helper'; import { redactPaths } from '@ghostfolio/api/helper/object.helper';
import { HEADER_KEY_IMPERSONATION } from '@ghostfolio/common/config'; import {
DEFAULT_REDACTED_PATHS,
HEADER_KEY_IMPERSONATION
} from '@ghostfolio/common/config';
import { import {
hasReadRestrictedAccessPermission, hasReadRestrictedAccessPermission,
isRestrictedView isRestrictedView
@ -39,40 +42,9 @@ export class RedactValuesInResponseInterceptor<T> implements NestInterceptor<
}) || }) ||
isRestrictedView(user) isRestrictedView(user)
) { ) {
data = redactAttributes({ data = redactPaths({
object: data, object: data,
options: [ paths: DEFAULT_REDACTED_PATHS
'balance',
'balanceInBaseCurrency',
'comment',
'convertedBalance',
'dividendInBaseCurrency',
'fee',
'feeInBaseCurrency',
'grossPerformance',
'grossPerformanceWithCurrencyEffect',
'interestInBaseCurrency',
'investment',
'netPerformance',
'netPerformanceWithCurrencyEffect',
'quantity',
'symbolMapping',
'totalBalanceInBaseCurrency',
'totalDividendInBaseCurrency',
'totalInterestInBaseCurrency',
'totalValueInBaseCurrency',
'unitPrice',
'unitPriceInAssetProfileCurrency',
'value',
'valueInBaseCurrency'
].map((attribute) => {
return {
attribute,
valueMap: {
'*': null
}
};
})
}); });
} }

19
apps/api/src/interceptors/transform-data-source-in-response/transform-data-source-in-response.interceptor.ts

@ -1,4 +1,4 @@
import { redactAttributes } from '@ghostfolio/api/helper/object.helper'; import { redactPaths } from '@ghostfolio/api/helper/object.helper';
import { ConfigurationService } from '@ghostfolio/api/services/configuration/configuration.service'; import { ConfigurationService } from '@ghostfolio/api/services/configuration/configuration.service';
import { encodeDataSource } from '@ghostfolio/common/helper'; import { encodeDataSource } from '@ghostfolio/common/helper';
@ -58,13 +58,18 @@ export class TransformDataSourceInResponseInterceptor<
} }
} }
data = redactAttributes({ data = redactPaths({
valueMap,
object: data, object: data,
options: [ paths: [
{ 'activities[*].SymbolProfile.dataSource',
valueMap, 'benchmarks[*].dataSource',
attribute: 'dataSource' 'fearAndGreedIndex.CRYPTOCURRENCIES.dataSource',
} 'fearAndGreedIndex.STOCKS.dataSource',
'holdings[*].dataSource',
'items[*].dataSource',
'SymbolProfile.dataSource',
'watchlist[*].dataSource'
] ]
}); });
} }

17
apps/api/src/services/data-provider/data-enhancer/yahoo-finance/yahoo-finance.service.ts

@ -135,10 +135,10 @@ export class YahooFinanceDataEnhancerService implements DataEnhancerInterface {
shortName, shortName,
symbol symbol
}: { }: {
longName: Price['longName']; longName?: Price['longName'];
quoteType: Price['quoteType']; quoteType?: Price['quoteType'];
shortName: Price['shortName']; shortName?: Price['shortName'];
symbol: Price['symbol']; symbol?: Price['symbol'];
}) { }) {
let name = longName; let name = longName;
@ -217,6 +217,15 @@ export class YahooFinanceDataEnhancerService implements DataEnhancerInterface {
}); });
} }
} }
response.holdings = assetProfile.topHoldings.holdings.map(
({ holdingName, holdingPercent }) => {
return {
name: this.formatName({ longName: holdingName }),
weight: holdingPercent
};
}
);
} else if ( } else if (
assetSubClass === 'STOCK' && assetSubClass === 'STOCK' &&
assetProfile.summaryProfile?.country assetProfile.summaryProfile?.country

39
apps/client/src/app/components/admin-market-data/admin-market-data.component.ts

@ -63,7 +63,7 @@ import {
import { DeviceDetectorService } from 'ngx-device-detector'; import { DeviceDetectorService } from 'ngx-device-detector';
import { NgxSkeletonLoaderModule } from 'ngx-skeleton-loader'; import { NgxSkeletonLoaderModule } from 'ngx-skeleton-loader';
import { Subject } from 'rxjs'; import { Subject } from 'rxjs';
import { distinctUntilChanged, switchMap, takeUntil } from 'rxjs/operators'; import { distinctUntilChanged, takeUntil } from 'rxjs/operators';
import { AdminMarketDataService } from './admin-market-data.service'; import { AdminMarketDataService } from './admin-market-data.service';
import { GfAssetProfileDialogComponent } from './asset-profile-dialog/asset-profile-dialog.component'; import { GfAssetProfileDialogComponent } from './asset-profile-dialog/asset-profile-dialog.component';
@ -482,32 +482,27 @@ export class GfAdminMarketDataComponent
dialogRef dialogRef
.afterClosed() .afterClosed()
.pipe(takeUntil(this.unsubscribeSubject)) .pipe(takeUntil(this.unsubscribeSubject))
.subscribe(({ dataSource, symbol } = {}) => { .subscribe((result) => {
if (dataSource && symbol) { if (!result) {
this.router.navigate(['.'], { relativeTo: this.route });
return;
}
const { addAssetProfile, dataSource, symbol } = result;
if (addAssetProfile && dataSource && symbol) {
this.adminService this.adminService
.addAssetProfile({ dataSource, symbol }) .addAssetProfile({ dataSource, symbol })
.pipe( .pipe(takeUntil(this.unsubscribeSubject))
switchMap(() => { .subscribe(() => {
this.isLoading = true; this.loadData();
this.changeDetectorRef.markForCheck();
return this.adminService.fetchAdminMarketData({
filters: this.activeFilters,
take: this.pageSize
});
}),
takeUntil(this.unsubscribeSubject)
)
.subscribe(({ marketData }) => {
this.dataSource = new MatTableDataSource(marketData);
this.dataSource.sort = this.sort;
this.isLoading = false;
this.changeDetectorRef.markForCheck();
}); });
} else {
this.loadData();
} }
this.router.navigate(['.'], { relativeTo: this.route }); this.onOpenAssetProfileDialog({ dataSource, symbol });
}); });
}); });
} }

8
apps/client/src/app/components/admin-market-data/create-asset-profile-dialog/create-asset-profile-dialog.component.ts

@ -101,6 +101,7 @@ export class GfCreateAssetProfileDialogComponent implements OnDestroy, OnInit {
public onSubmit() { public onSubmit() {
if (this.mode === 'auto') { if (this.mode === 'auto') {
this.dialogRef.close({ this.dialogRef.close({
addAssetProfile: true,
dataSource: dataSource:
this.createAssetProfileForm.get('searchSymbol').value.dataSource, this.createAssetProfileForm.get('searchSymbol').value.dataSource,
symbol: this.createAssetProfileForm.get('searchSymbol').value.symbol symbol: this.createAssetProfileForm.get('searchSymbol').value.symbol
@ -127,10 +128,15 @@ export class GfCreateAssetProfileDialogComponent implements OnDestroy, OnInit {
takeUntil(this.unsubscribeSubject) takeUntil(this.unsubscribeSubject)
) )
.subscribe(() => { .subscribe(() => {
this.dialogRef.close(); this.dialogRef.close({
addAssetProfile: false,
dataSource: this.dataSourceForExchangeRates,
symbol: `${DEFAULT_CURRENCY}${currency}`
});
}); });
} else if (this.mode === 'manual') { } else if (this.mode === 'manual') {
this.dialogRef.close({ this.dialogRef.close({
addAssetProfile: true,
dataSource: 'MANUAL', dataSource: 'MANUAL',
symbol: `${this.ghostfolioPrefix}${this.createAssetProfileForm.get('addSymbol').value}` symbol: `${this.ghostfolioPrefix}${this.createAssetProfileForm.get('addSymbol').value}`
}); });

6
apps/client/src/app/components/admin-overview/admin-overview.component.ts

@ -73,6 +73,7 @@ import { takeUntil } from 'rxjs/operators';
templateUrl: './admin-overview.html' templateUrl: './admin-overview.html'
}) })
export class GfAdminOverviewComponent implements OnDestroy, OnInit { export class GfAdminOverviewComponent implements OnDestroy, OnInit {
public activitiesCount: number;
public couponDuration: StringValue = '14 days'; public couponDuration: StringValue = '14 days';
public coupons: Coupon[]; public coupons: Coupon[];
public hasPermissionForSubscription: boolean; public hasPermissionForSubscription: boolean;
@ -83,7 +84,6 @@ export class GfAdminOverviewComponent implements OnDestroy, OnInit {
public isDataGatheringEnabled: boolean; public isDataGatheringEnabled: boolean;
public permissions = permissions; public permissions = permissions;
public systemMessage: SystemMessage; public systemMessage: SystemMessage;
public transactionCount: number;
public userCount: number; public userCount: number;
public user: User; public user: User;
public version: string; public version: string;
@ -289,12 +289,12 @@ export class GfAdminOverviewComponent implements OnDestroy, OnInit {
this.adminService this.adminService
.fetchAdminData() .fetchAdminData()
.pipe(takeUntil(this.unsubscribeSubject)) .pipe(takeUntil(this.unsubscribeSubject))
.subscribe(({ settings, transactionCount, userCount, version }) => { .subscribe(({ activitiesCount, settings, userCount, version }) => {
this.activitiesCount = activitiesCount;
this.coupons = (settings[PROPERTY_COUPONS] as Coupon[]) ?? []; this.coupons = (settings[PROPERTY_COUPONS] as Coupon[]) ?? [];
this.isDataGatheringEnabled = this.isDataGatheringEnabled =
settings[PROPERTY_IS_DATA_GATHERING_ENABLED] === false ? false : true; settings[PROPERTY_IS_DATA_GATHERING_ENABLED] === false ? false : true;
this.systemMessage = settings[PROPERTY_SYSTEM_MESSAGE] as SystemMessage; this.systemMessage = settings[PROPERTY_SYSTEM_MESSAGE] as SystemMessage;
this.transactionCount = transactionCount;
this.userCount = userCount; this.userCount = userCount;
this.version = version; this.version = version;

6
apps/client/src/app/components/admin-overview/admin-overview.html

@ -20,11 +20,11 @@
<div class="w-50"> <div class="w-50">
<gf-value <gf-value
[locale]="user?.settings?.locale" [locale]="user?.settings?.locale"
[value]="transactionCount" [value]="activitiesCount"
/> />
@if (transactionCount && userCount) { @if (activitiesCount && userCount) {
<div> <div>
{{ transactionCount / userCount | number: '1.2-2' }} {{ activitiesCount / userCount | number: '1.2-2' }}
<span i18n>per User</span> <span i18n>per User</span>
</div> </div>
} }

2
apps/client/src/app/components/holding-detail-dialog/holding-detail-dialog.html

@ -380,10 +380,10 @@
[deviceType]="data.deviceType" [deviceType]="data.deviceType"
[hasPermissionToOpenDetails]="false" [hasPermissionToOpenDetails]="false"
[locale]="user?.settings?.locale" [locale]="user?.settings?.locale"
[showActivitiesCount]="false"
[showAllocationInPercentage]="user?.settings?.isExperimentalFeatures" [showAllocationInPercentage]="user?.settings?.isExperimentalFeatures"
[showBalance]="false" [showBalance]="false"
[showFooter]="false" [showFooter]="false"
[showTransactions]="false"
[showValue]="false" [showValue]="false"
[showValueInBaseCurrency]="false" [showValueInBaseCurrency]="false"
/> />

10
apps/client/src/app/pages/accounts/accounts-page.component.ts

@ -38,6 +38,7 @@ import { GfTransferBalanceDialogComponent } from './transfer-balance/transfer-ba
}) })
export class GfAccountsPageComponent implements OnDestroy, OnInit { export class GfAccountsPageComponent implements OnDestroy, OnInit {
public accounts: AccountModel[]; public accounts: AccountModel[];
public activitiesCount = 0;
public deviceType: string; public deviceType: string;
public hasImpersonationId: boolean; public hasImpersonationId: boolean;
public hasPermissionToCreateAccount: boolean; public hasPermissionToCreateAccount: boolean;
@ -45,7 +46,6 @@ export class GfAccountsPageComponent implements OnDestroy, OnInit {
public routeQueryParams: Subscription; public routeQueryParams: Subscription;
public totalBalanceInBaseCurrency = 0; public totalBalanceInBaseCurrency = 0;
public totalValueInBaseCurrency = 0; public totalValueInBaseCurrency = 0;
public transactionCount = 0;
public user: User; public user: User;
private unsubscribeSubject = new Subject<void>(); private unsubscribeSubject = new Subject<void>();
@ -128,14 +128,14 @@ export class GfAccountsPageComponent implements OnDestroy, OnInit {
.subscribe( .subscribe(
({ ({
accounts, accounts,
activitiesCount,
totalBalanceInBaseCurrency, totalBalanceInBaseCurrency,
totalValueInBaseCurrency, totalValueInBaseCurrency
transactionCount
}) => { }) => {
this.accounts = accounts; this.accounts = accounts;
this.activitiesCount = activitiesCount;
this.totalBalanceInBaseCurrency = totalBalanceInBaseCurrency; this.totalBalanceInBaseCurrency = totalBalanceInBaseCurrency;
this.totalValueInBaseCurrency = totalValueInBaseCurrency; this.totalValueInBaseCurrency = totalValueInBaseCurrency;
this.transactionCount = transactionCount;
if (this.accounts?.length <= 0) { if (this.accounts?.length <= 0) {
this.router.navigate([], { queryParams: { createDialog: true } }); this.router.navigate([], { queryParams: { createDialog: true } });
@ -358,8 +358,8 @@ export class GfAccountsPageComponent implements OnDestroy, OnInit {
private reset() { private reset() {
this.accounts = undefined; this.accounts = undefined;
this.activitiesCount = 0;
this.totalBalanceInBaseCurrency = 0; this.totalBalanceInBaseCurrency = 0;
this.totalValueInBaseCurrency = 0; this.totalValueInBaseCurrency = 0;
this.transactionCount = 0;
} }
} }

2
apps/client/src/app/pages/accounts/accounts-page.html

@ -4,6 +4,7 @@
<h1 class="d-none d-sm-block h3 mb-4 text-center" i18n>Accounts</h1> <h1 class="d-none d-sm-block h3 mb-4 text-center" i18n>Accounts</h1>
<gf-accounts-table <gf-accounts-table
[accounts]="accounts" [accounts]="accounts"
[activitiesCount]="activitiesCount"
[baseCurrency]="user?.settings?.baseCurrency" [baseCurrency]="user?.settings?.baseCurrency"
[deviceType]="deviceType" [deviceType]="deviceType"
[locale]="user?.settings?.locale" [locale]="user?.settings?.locale"
@ -14,7 +15,6 @@
" "
[totalBalanceInBaseCurrency]="totalBalanceInBaseCurrency" [totalBalanceInBaseCurrency]="totalBalanceInBaseCurrency"
[totalValueInBaseCurrency]="totalValueInBaseCurrency" [totalValueInBaseCurrency]="totalValueInBaseCurrency"
[transactionCount]="transactionCount"
(accountDeleted)="onDeleteAccount($event)" (accountDeleted)="onDeleteAccount($event)"
(accountToUpdate)="onUpdateAccount($event)" (accountToUpdate)="onUpdateAccount($event)"
(transferBalance)="onTransferBalance()" (transferBalance)="onTransferBalance()"

180
apps/client/src/locales/messages.es.xlf

@ -40,7 +40,7 @@
</trans-unit> </trans-unit>
<trans-unit id="9153520284278555926" datatype="html"> <trans-unit id="9153520284278555926" datatype="html">
<source>please</source> <source>please</source>
<target state="new">please</target> <target state="translated">por favor</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/pages/pricing/pricing-page.html</context> <context context-type="sourcefile">apps/client/src/app/pages/pricing/pricing-page.html</context>
<context context-type="linenumber">333</context> <context context-type="linenumber">333</context>
@ -84,7 +84,7 @@
</trans-unit> </trans-unit>
<trans-unit id="1351814922314683865" datatype="html"> <trans-unit id="1351814922314683865" datatype="html">
<source>with</source> <source>with</source>
<target state="new">with</target> <target state="translated">con</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/components/subscription-interstitial-dialog/subscription-interstitial-dialog.html</context> <context context-type="sourcefile">apps/client/src/app/components/subscription-interstitial-dialog/subscription-interstitial-dialog.html</context>
<context context-type="linenumber">87</context> <context context-type="linenumber">87</context>
@ -368,7 +368,7 @@
</trans-unit> </trans-unit>
<trans-unit id="5611965261696422586" datatype="html"> <trans-unit id="5611965261696422586" datatype="html">
<source>and is driven by the efforts of its <x id="START_LINK" ctype="x-a" equiv-text="&lt;a href=&quot;https://github.com/ghostfolio/ghostfolio/graphs/contributors&quot; title=&quot;Contributors to Ghostfolio&quot; &gt;"/>contributors<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a &gt;"/></source> <source>and is driven by the efforts of its <x id="START_LINK" ctype="x-a" equiv-text="&lt;a href=&quot;https://github.com/ghostfolio/ghostfolio/graphs/contributors&quot; title=&quot;Contributors to Ghostfolio&quot; &gt;"/>contributors<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a &gt;"/></source>
<target state="new">and is driven by the efforts of its <x id="START_LINK" ctype="x-a" equiv-text="&lt;a href=&quot;https://github.com/ghostfolio/ghostfolio/graphs/contributors&quot; title=&quot;Contributors to Ghostfolio&quot; &gt;"/>contributors<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a &gt;"/></target> <target state="translated">y es impulsado por los esfuerzos de sus <x id="START_LINK" ctype="x-a" equiv-text="&lt;a href=&quot;https://github.com/ghostfolio/ghostfolio/graphs/contributors&quot; title=&quot;Contributors to Ghostfolio&quot; &gt;"/>contribuidores<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a &gt;"/></target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/pages/about/overview/about-overview-page.html</context> <context context-type="sourcefile">apps/client/src/app/pages/about/overview/about-overview-page.html</context>
<context context-type="linenumber">49</context> <context context-type="linenumber">49</context>
@ -652,7 +652,7 @@
</trans-unit> </trans-unit>
<trans-unit id="2395205455607568422" datatype="html"> <trans-unit id="2395205455607568422" datatype="html">
<source>No auto-renewal on membership.</source> <source>No auto-renewal on membership.</source>
<target state="new">No auto-renewal on membership.</target> <target state="translated">No se renueva automáticamente la membresía.</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/components/user-account-membership/user-account-membership.html</context> <context context-type="sourcefile">apps/client/src/app/components/user-account-membership/user-account-membership.html</context>
<context context-type="linenumber">74</context> <context context-type="linenumber">74</context>
@ -1096,7 +1096,7 @@
</trans-unit> </trans-unit>
<trans-unit id="366169681580494481" datatype="html"> <trans-unit id="366169681580494481" datatype="html">
<source>Performance with currency effect</source> <source>Performance with currency effect</source>
<target state="new">Performance with currency effect</target> <target state="translated">Rendimiento con el efecto del tipo de cambio de divisa</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/pages/portfolio/analysis/analysis-page.html</context> <context context-type="sourcefile">apps/client/src/app/pages/portfolio/analysis/analysis-page.html</context>
<context context-type="linenumber">135</context> <context context-type="linenumber">135</context>
@ -1912,7 +1912,7 @@
</trans-unit> </trans-unit>
<trans-unit id="6004588582437169024" datatype="html"> <trans-unit id="6004588582437169024" datatype="html">
<source>Current week</source> <source>Current week</source>
<target state="new">Current week</target> <target state="translated">Semana actual</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts</context> <context context-type="sourcefile">apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts</context>
<context context-type="linenumber">191</context> <context context-type="linenumber">191</context>
@ -2076,7 +2076,7 @@
</trans-unit> </trans-unit>
<trans-unit id="7500665368930738879" datatype="html"> <trans-unit id="7500665368930738879" datatype="html">
<source>or start a discussion at</source> <source>or start a discussion at</source>
<target state="new">or start a discussion at</target> <target state="translated">o iniciar una discusión en</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/pages/about/overview/about-overview-page.html</context> <context context-type="sourcefile">apps/client/src/app/pages/about/overview/about-overview-page.html</context>
<context context-type="linenumber">94</context> <context context-type="linenumber">94</context>
@ -2148,7 +2148,7 @@
</trans-unit> </trans-unit>
<trans-unit id="2003818202621229370" datatype="html"> <trans-unit id="2003818202621229370" datatype="html">
<source>Sustainable retirement income</source> <source>Sustainable retirement income</source>
<target state="new">Sustainable retirement income</target> <target state="translated">Ingreso sostenible de retiro</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/pages/portfolio/fire/fire-page.html</context> <context context-type="sourcefile">apps/client/src/app/pages/portfolio/fire/fire-page.html</context>
<context context-type="linenumber">41</context> <context context-type="linenumber">41</context>
@ -2320,7 +2320,7 @@
</trans-unit> </trans-unit>
<trans-unit id="1531212547408073567" datatype="html"> <trans-unit id="1531212547408073567" datatype="html">
<source>contact us</source> <source>contact us</source>
<target state="new">contact us</target> <target state="translated">contactarnos</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/pages/pricing/pricing-page.html</context> <context context-type="sourcefile">apps/client/src/app/pages/pricing/pricing-page.html</context>
<context context-type="linenumber">336</context> <context context-type="linenumber">336</context>
@ -2420,7 +2420,7 @@
</trans-unit> </trans-unit>
<trans-unit id="7934616470747135563" datatype="html"> <trans-unit id="7934616470747135563" datatype="html">
<source>Latest activities</source> <source>Latest activities</source>
<target state="new">Latest activities</target> <target state="translated">Últimas actividades</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/pages/public/public-page.html</context> <context context-type="sourcefile">apps/client/src/app/pages/public/public-page.html</context>
<context context-type="linenumber">211</context> <context context-type="linenumber">211</context>
@ -2536,7 +2536,7 @@
</trans-unit> </trans-unit>
<trans-unit id="5211792611718918888" datatype="html"> <trans-unit id="5211792611718918888" datatype="html">
<source>annual interest rate</source> <source>annual interest rate</source>
<target state="new">annual interest rate</target> <target state="translated">tasa de interés anual</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/pages/portfolio/fire/fire-page.html</context> <context context-type="sourcefile">apps/client/src/app/pages/portfolio/fire/fire-page.html</context>
<context context-type="linenumber">185</context> <context context-type="linenumber">185</context>
@ -2656,7 +2656,7 @@
</trans-unit> </trans-unit>
<trans-unit id="7341990227686441824" datatype="html"> <trans-unit id="7341990227686441824" datatype="html">
<source>Could not validate form</source> <source>Could not validate form</source>
<target state="new">Could not validate form</target> <target state="translated">No se pudo validar el formulario</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts</context> <context context-type="sourcefile">apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts</context>
<context context-type="linenumber">554</context> <context context-type="linenumber">554</context>
@ -2892,7 +2892,7 @@
</trans-unit> </trans-unit>
<trans-unit id="8966698274727122602" datatype="html"> <trans-unit id="8966698274727122602" datatype="html">
<source>Authentication</source> <source>Authentication</source>
<target state="new">Authentication</target> <target state="translated">Autenticación</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/components/user-detail-dialog/user-detail-dialog.html</context> <context context-type="sourcefile">apps/client/src/app/components/user-detail-dialog/user-detail-dialog.html</context>
<context context-type="linenumber">35</context> <context context-type="linenumber">35</context>
@ -3044,7 +3044,7 @@
</trans-unit> </trans-unit>
<trans-unit id="3227075298129844075" datatype="html"> <trans-unit id="3227075298129844075" datatype="html">
<source>If you retire today, you would be able to withdraw</source> <source>If you retire today, you would be able to withdraw</source>
<target state="new">If you retire today, you would be able to withdraw</target> <target state="translated">Si te retirases hoy, podrías sacar</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/pages/portfolio/fire/fire-page.html</context> <context context-type="sourcefile">apps/client/src/app/pages/portfolio/fire/fire-page.html</context>
<context context-type="linenumber">68</context> <context context-type="linenumber">68</context>
@ -3112,7 +3112,7 @@
</trans-unit> </trans-unit>
<trans-unit id="7763941937414903315" datatype="html"> <trans-unit id="7763941937414903315" datatype="html">
<source>Looking for a student discount?</source> <source>Looking for a student discount?</source>
<target state="new">Looking for a student discount?</target> <target state="translated">¿Buscando un descuento para estudiantes?</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/pages/pricing/pricing-page.html</context> <context context-type="sourcefile">apps/client/src/app/pages/pricing/pricing-page.html</context>
<context context-type="linenumber">342</context> <context context-type="linenumber">342</context>
@ -3348,7 +3348,7 @@
</trans-unit> </trans-unit>
<trans-unit id="936060984157466006" datatype="html"> <trans-unit id="936060984157466006" datatype="html">
<source>Everything in <x id="START_TAG_STRONG" ctype="x-strong" equiv-text="&lt;strong&gt;"/>Basic<x id="CLOSE_TAG_STRONG" ctype="x-strong" equiv-text="&lt;/strong&gt;"/>, plus</source> <source>Everything in <x id="START_TAG_STRONG" ctype="x-strong" equiv-text="&lt;strong&gt;"/>Basic<x id="CLOSE_TAG_STRONG" ctype="x-strong" equiv-text="&lt;/strong&gt;"/>, plus</source>
<target state="new">Everything in <x id="START_TAG_STRONG" ctype="x-strong" equiv-text="&lt;strong&gt;"/>Basic<x id="CLOSE_TAG_STRONG" ctype="x-strong" equiv-text="&lt;/strong&gt;"/>, plus</target> <target state="translated">Todo en <x id="START_TAG_STRONG" ctype="x-strong" equiv-text="&lt;strong&gt;"/>Básico<x id="CLOSE_TAG_STRONG" ctype="x-strong" equiv-text="&lt;/strong&gt;"/>, s</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/pages/pricing/pricing-page.html</context> <context context-type="sourcefile">apps/client/src/app/pages/pricing/pricing-page.html</context>
<context context-type="linenumber">199</context> <context context-type="linenumber">199</context>
@ -3608,7 +3608,7 @@
</trans-unit> </trans-unit>
<trans-unit id="7498591289549626867" datatype="html"> <trans-unit id="7498591289549626867" datatype="html">
<source>Could not save asset profile</source> <source>Could not save asset profile</source>
<target state="new">Could not save asset profile</target> <target state="translated">No se pudo guardar el perfil del activo</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts</context> <context context-type="sourcefile">apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts</context>
<context context-type="linenumber">588</context> <context context-type="linenumber">588</context>
@ -3812,7 +3812,7 @@
</trans-unit> </trans-unit>
<trans-unit id="7702646444963497962" datatype="html"> <trans-unit id="7702646444963497962" datatype="html">
<source>By</source> <source>By</source>
<target state="new">By</target> <target state="translated">Por</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/pages/portfolio/fire/fire-page.html</context> <context context-type="sourcefile">apps/client/src/app/pages/portfolio/fire/fire-page.html</context>
<context context-type="linenumber">139</context> <context context-type="linenumber">139</context>
@ -3828,7 +3828,7 @@
</trans-unit> </trans-unit>
<trans-unit id="4340477809050781416" datatype="html"> <trans-unit id="4340477809050781416" datatype="html">
<source>Current year</source> <source>Current year</source>
<target state="new">Current year</target> <target state="translated">Año actual</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts</context> <context context-type="sourcefile">apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts</context>
<context context-type="linenumber">199</context> <context context-type="linenumber">199</context>
@ -3864,7 +3864,7 @@
</trans-unit> </trans-unit>
<trans-unit id="8319378030525016917" datatype="html"> <trans-unit id="8319378030525016917" datatype="html">
<source>Asset profile has been saved</source> <source>Asset profile has been saved</source>
<target state="new">Asset profile has been saved</target> <target state="translated">El perfil del activo ha sido guardado</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts</context> <context context-type="sourcefile">apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts</context>
<context context-type="linenumber">578</context> <context context-type="linenumber">578</context>
@ -4056,7 +4056,7 @@
</trans-unit> </trans-unit>
<trans-unit id="1468015720862673946" datatype="html"> <trans-unit id="1468015720862673946" datatype="html">
<source>View Details</source> <source>View Details</source>
<target state="new">View Details</target> <target state="translated">Ver detalles</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/components/admin-users/admin-users.html</context> <context context-type="sourcefile">apps/client/src/app/components/admin-users/admin-users.html</context>
<context context-type="linenumber">225</context> <context context-type="linenumber">225</context>
@ -4192,7 +4192,7 @@
</trans-unit> </trans-unit>
<trans-unit id="3556628518893194463" datatype="html"> <trans-unit id="3556628518893194463" datatype="html">
<source>per week</source> <source>per week</source>
<target state="new">per week</target> <target state="translated">por semana</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/components/subscription-interstitial-dialog/subscription-interstitial-dialog.html</context> <context context-type="sourcefile">apps/client/src/app/components/subscription-interstitial-dialog/subscription-interstitial-dialog.html</context>
<context context-type="linenumber">130</context> <context context-type="linenumber">130</context>
@ -4216,7 +4216,7 @@
</trans-unit> </trans-unit>
<trans-unit id="577204259483334667" datatype="html"> <trans-unit id="577204259483334667" datatype="html">
<source>and we share aggregated <x id="START_LINK" ctype="x-a" equiv-text="&lt;a title=&quot;Open Startup&quot; [routerLink]=&quot;routerLinkOpenStartup&quot; &gt;"/>key metrics<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a &gt;"/> of the platform’s performance</source> <source>and we share aggregated <x id="START_LINK" ctype="x-a" equiv-text="&lt;a title=&quot;Open Startup&quot; [routerLink]=&quot;routerLinkOpenStartup&quot; &gt;"/>key metrics<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a &gt;"/> of the platform’s performance</source>
<target state="new">and we share aggregated <x id="START_LINK" ctype="x-a" equiv-text="&lt;a title=&quot;Open Startup&quot; [routerLink]=&quot;routerLinkOpenStartup&quot; &gt;"/>key metrics<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a &gt;"/> of the platform’s performance</target> <target state="translated">y compartimos agregados <x id="START_LINK" ctype="x-a" equiv-text="&lt;a title=&quot;Open Startup&quot; [routerLink]=&quot;routerLinkOpenStartup&quot; &gt;"/>métricas clave<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a &gt;"/> del rendimiento de la plataforma</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/pages/about/overview/about-overview-page.html</context> <context context-type="sourcefile">apps/client/src/app/pages/about/overview/about-overview-page.html</context>
<context context-type="linenumber">32</context> <context context-type="linenumber">32</context>
@ -4260,7 +4260,7 @@
</trans-unit> </trans-unit>
<trans-unit id="5271053765919315173" datatype="html"> <trans-unit id="5271053765919315173" datatype="html">
<source>Website of Thomas Kaul</source> <source>Website of Thomas Kaul</source>
<target state="new">Website of Thomas Kaul</target> <target state="translated">Sitio web de Thomas Kaul</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/pages/about/overview/about-overview-page.html</context> <context context-type="sourcefile">apps/client/src/app/pages/about/overview/about-overview-page.html</context>
<context context-type="linenumber">44</context> <context context-type="linenumber">44</context>
@ -4440,7 +4440,7 @@
</trans-unit> </trans-unit>
<trans-unit id="2145636458848553570" datatype="html"> <trans-unit id="2145636458848553570" datatype="html">
<source>Sign in with OpenID Connect</source> <source>Sign in with OpenID Connect</source>
<target state="new">Sign in with OpenID Connect</target> <target state="translated">Iniciar sesión con OpenID Connect</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/components/login-with-access-token-dialog/login-with-access-token-dialog.html</context> <context context-type="sourcefile">apps/client/src/app/components/login-with-access-token-dialog/login-with-access-token-dialog.html</context>
<context context-type="linenumber">55</context> <context context-type="linenumber">55</context>
@ -4532,7 +4532,7 @@
</trans-unit> </trans-unit>
<trans-unit id="5289957034780335504" datatype="html"> <trans-unit id="5289957034780335504" datatype="html">
<source>The source code is fully available as <x id="START_LINK" ctype="x-a" equiv-text="&lt;a href=&quot;https://github.com/ghostfolio/ghostfolio&quot; title=&quot;Find Ghostfolio on GitHub&quot; &gt;"/>open source software<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a &gt;"/> (OSS) under the <x id="START_LINK_1" equiv-text="&lt;a href=&quot;https://www.gnu.org/licenses/agpl-3.0.html&quot; title=&quot;GNU Affero General Public License&quot; &gt;"/>AGPL-3.0 license<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a &gt;"/></source> <source>The source code is fully available as <x id="START_LINK" ctype="x-a" equiv-text="&lt;a href=&quot;https://github.com/ghostfolio/ghostfolio&quot; title=&quot;Find Ghostfolio on GitHub&quot; &gt;"/>open source software<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a &gt;"/> (OSS) under the <x id="START_LINK_1" equiv-text="&lt;a href=&quot;https://www.gnu.org/licenses/agpl-3.0.html&quot; title=&quot;GNU Affero General Public License&quot; &gt;"/>AGPL-3.0 license<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a &gt;"/></source>
<target state="new">The source code is fully available as <x id="START_LINK" ctype="x-a" equiv-text="&lt;a href=&quot;https://github.com/ghostfolio/ghostfolio&quot; title=&quot;Find Ghostfolio on GitHub&quot; &gt;"/>open source software<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a &gt;"/> (OSS) under the <x id="START_LINK_1" equiv-text="&lt;a href=&quot;https://www.gnu.org/licenses/agpl-3.0.html&quot; title=&quot;GNU Affero General Public License&quot; &gt;"/>AGPL-3.0 license<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a &gt;"/></target> <target state="translated">El código fuente está disponible completamente en <x id="START_LINK" ctype="x-a" equiv-text="&lt;a href=&quot;https://github.com/ghostfolio/ghostfolio&quot; title=&quot;Find Ghostfolio on GitHub&quot; &gt;"/>software de código abierto<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a &gt;"/> (OSS) bajo la <x id="START_LINK_1" equiv-text="&lt;a href=&quot;https://www.gnu.org/licenses/agpl-3.0.html&quot; title=&quot;GNU Affero General Public License&quot; &gt;"/>licencia AGPL-3.0<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a &gt;"/></target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/pages/about/overview/about-overview-page.html</context> <context context-type="sourcefile">apps/client/src/app/pages/about/overview/about-overview-page.html</context>
<context context-type="linenumber">16</context> <context context-type="linenumber">16</context>
@ -4604,7 +4604,7 @@
</trans-unit> </trans-unit>
<trans-unit id="3004519800638083911" datatype="html"> <trans-unit id="3004519800638083911" datatype="html">
<source>this is projected to increase to</source> <source>this is projected to increase to</source>
<target state="new">this is projected to increase to</target> <target state="translated">esto se proyecta a aumentar a</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/pages/portfolio/fire/fire-page.html</context> <context context-type="sourcefile">apps/client/src/app/pages/portfolio/fire/fire-page.html</context>
<context context-type="linenumber">147</context> <context context-type="linenumber">147</context>
@ -4656,7 +4656,7 @@
</trans-unit> </trans-unit>
<trans-unit id="3627006945295714424" datatype="html"> <trans-unit id="3627006945295714424" datatype="html">
<source>Job ID</source> <source>Job ID</source>
<target state="new">Job ID</target> <target state="translated">ID de trabajo</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/components/admin-jobs/admin-jobs.html</context> <context context-type="sourcefile">apps/client/src/app/components/admin-jobs/admin-jobs.html</context>
<context context-type="linenumber">34</context> <context context-type="linenumber">34</context>
@ -4740,7 +4740,7 @@
</trans-unit> </trans-unit>
<trans-unit id="8553460997100418147" datatype="html"> <trans-unit id="8553460997100418147" datatype="html">
<source>for</source> <source>for</source>
<target state="new">for</target> <target state="translated">para</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/components/subscription-interstitial-dialog/subscription-interstitial-dialog.html</context> <context context-type="sourcefile">apps/client/src/app/components/subscription-interstitial-dialog/subscription-interstitial-dialog.html</context>
<context context-type="linenumber">128</context> <context context-type="linenumber">128</context>
@ -4764,7 +4764,7 @@
</trans-unit> </trans-unit>
<trans-unit id="5134951682994822188" datatype="html"> <trans-unit id="5134951682994822188" datatype="html">
<source>Could not parse scraper configuration</source> <source>Could not parse scraper configuration</source>
<target state="new">Could not parse scraper configuration</target> <target state="translated">No se pudo analizar la configuración del scraper</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts</context> <context context-type="sourcefile">apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts</context>
<context context-type="linenumber">509</context> <context context-type="linenumber">509</context>
@ -4808,7 +4808,7 @@
</trans-unit> </trans-unit>
<trans-unit id="9187635907883145155" datatype="html"> <trans-unit id="9187635907883145155" datatype="html">
<source>Edit access</source> <source>Edit access</source>
<target state="new">Edit access</target> <target state="translated">Editar acceso</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/components/user-account-access/create-or-update-access-dialog/create-or-update-access-dialog.html</context> <context context-type="sourcefile">apps/client/src/app/components/user-account-access/create-or-update-access-dialog/create-or-update-access-dialog.html</context>
<context context-type="linenumber">11</context> <context context-type="linenumber">11</context>
@ -4880,7 +4880,7 @@
</trans-unit> </trans-unit>
<trans-unit id="8984201769958269296" datatype="html"> <trans-unit id="8984201769958269296" datatype="html">
<source>Get access to 80’000+ tickers from over 50 exchanges</source> <source>Get access to 80’000+ tickers from over 50 exchanges</source>
<target state="new">Get access to 80’000+ tickers from over 50 exchanges</target> <target state="translated">Obtén acceso a más de 80,000 tickers de más de 50 exchanges</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/components/subscription-interstitial-dialog/subscription-interstitial-dialog.html</context> <context context-type="sourcefile">apps/client/src/app/components/subscription-interstitial-dialog/subscription-interstitial-dialog.html</context>
<context context-type="linenumber">84</context> <context context-type="linenumber">84</context>
@ -5064,7 +5064,7 @@
</trans-unit> </trans-unit>
<trans-unit id="8014012170270529279" datatype="html"> <trans-unit id="8014012170270529279" datatype="html">
<source>less than</source> <source>less than</source>
<target state="new">less than</target> <target state="translated">menos que</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/components/subscription-interstitial-dialog/subscription-interstitial-dialog.html</context> <context context-type="sourcefile">apps/client/src/app/components/subscription-interstitial-dialog/subscription-interstitial-dialog.html</context>
<context context-type="linenumber">129</context> <context context-type="linenumber">129</context>
@ -5354,7 +5354,7 @@
</trans-unit> </trans-unit>
<trans-unit id="4257439615478050183" datatype="html"> <trans-unit id="4257439615478050183" datatype="html">
<source>Ghostfolio Status</source> <source>Ghostfolio Status</source>
<target state="new">Ghostfolio Status</target> <target state="translated">Estado de Ghostfolio</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/pages/about/overview/about-overview-page.html</context> <context context-type="sourcefile">apps/client/src/app/pages/about/overview/about-overview-page.html</context>
<context context-type="linenumber">62</context> <context context-type="linenumber">62</context>
@ -5362,7 +5362,7 @@
</trans-unit> </trans-unit>
<trans-unit id="4275978599610634089" datatype="html"> <trans-unit id="4275978599610634089" datatype="html">
<source>with your university e-mail address</source> <source>with your university e-mail address</source>
<target state="new">with your university e-mail address</target> <target state="translated">con tu dirección de correo electrónico de la universidad</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/pages/pricing/pricing-page.html</context> <context context-type="sourcefile">apps/client/src/app/pages/pricing/pricing-page.html</context>
<context context-type="linenumber">348</context> <context context-type="linenumber">348</context>
@ -5382,7 +5382,7 @@
</trans-unit> </trans-unit>
<trans-unit id="70768492340592330" datatype="html"> <trans-unit id="70768492340592330" datatype="html">
<source>and a safe withdrawal rate (SWR) of</source> <source>and a safe withdrawal rate (SWR) of</source>
<target state="new">and a safe withdrawal rate (SWR) of</target> <target state="translated">y una tasa de retiro segura (SWR) de</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/pages/portfolio/fire/fire-page.html</context> <context context-type="sourcefile">apps/client/src/app/pages/portfolio/fire/fire-page.html</context>
<context context-type="linenumber">108</context> <context context-type="linenumber">108</context>
@ -5546,7 +5546,7 @@
</trans-unit> </trans-unit>
<trans-unit id="5276907121760788823" datatype="html"> <trans-unit id="5276907121760788823" datatype="html">
<source>Request it</source> <source>Request it</source>
<target state="new">Request it</target> <target state="translated">Solicitar</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/pages/pricing/pricing-page.html</context> <context context-type="sourcefile">apps/client/src/app/pages/pricing/pricing-page.html</context>
<context context-type="linenumber">344</context> <context context-type="linenumber">344</context>
@ -5602,7 +5602,7 @@
</trans-unit> </trans-unit>
<trans-unit id="page.fire.projected.1" datatype="html"> <trans-unit id="page.fire.projected.1" datatype="html">
<source>,</source> <source>,</source>
<target state="new">,</target> <target state="translated">,</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/pages/portfolio/fire/fire-page.html</context> <context context-type="sourcefile">apps/client/src/app/pages/portfolio/fire/fire-page.html</context>
<context context-type="linenumber">145</context> <context context-type="linenumber">145</context>
@ -5618,7 +5618,7 @@
</trans-unit> </trans-unit>
<trans-unit id="4905798562247431262" datatype="html"> <trans-unit id="4905798562247431262" datatype="html">
<source>per month</source> <source>per month</source>
<target state="new">per month</target> <target state="translated">por mes</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/pages/portfolio/fire/fire-page.html</context> <context context-type="sourcefile">apps/client/src/app/pages/portfolio/fire/fire-page.html</context>
<context context-type="linenumber">94</context> <context context-type="linenumber">94</context>
@ -5866,7 +5866,7 @@
</trans-unit> </trans-unit>
<trans-unit id="858192247408211331" datatype="html"> <trans-unit id="858192247408211331" datatype="html">
<source>here</source> <source>here</source>
<target state="new">here</target> <target state="translated">aquí</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/pages/pricing/pricing-page.html</context> <context context-type="sourcefile">apps/client/src/app/pages/pricing/pricing-page.html</context>
<context context-type="linenumber">347</context> <context context-type="linenumber">347</context>
@ -5874,7 +5874,7 @@
</trans-unit> </trans-unit>
<trans-unit id="1600023202562292052" datatype="html"> <trans-unit id="1600023202562292052" datatype="html">
<source>Close Holding</source> <source>Close Holding</source>
<target state="new">Close Holding</target> <target state="translated">Cerrar posición</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/components/holding-detail-dialog/holding-detail-dialog.html</context> <context context-type="sourcefile">apps/client/src/app/components/holding-detail-dialog/holding-detail-dialog.html</context>
<context context-type="linenumber">442</context> <context context-type="linenumber">442</context>
@ -6175,7 +6175,7 @@
</trans-unit> </trans-unit>
<trans-unit id="7309206099560156141" datatype="html"> <trans-unit id="7309206099560156141" datatype="html">
<source>{VAR_PLURAL, plural, =1 {activity} other {activities}}</source> <source>{VAR_PLURAL, plural, =1 {activity} other {activities}}</source>
<target state="new">{VAR_PLURAL, plural, =1 {activity} other {activities}}</target> <target state="translated">{VAR_PLURAL, plural, =1 {actividad} other {actividades}}</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/components/portfolio-summary/portfolio-summary.component.html</context> <context context-type="sourcefile">apps/client/src/app/components/portfolio-summary/portfolio-summary.component.html</context>
<context context-type="linenumber">14</context> <context context-type="linenumber">14</context>
@ -6255,7 +6255,7 @@
</trans-unit> </trans-unit>
<trans-unit id="5707368132268957392" datatype="html"> <trans-unit id="5707368132268957392" datatype="html">
<source>Include in</source> <source>Include in</source>
<target state="new">Include in</target> <target state="translated">Incluir en</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html</context> <context context-type="sourcefile">apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html</context>
<context context-type="linenumber">374</context> <context context-type="linenumber">374</context>
@ -6539,7 +6539,7 @@
</trans-unit> </trans-unit>
<trans-unit id="3477953895055172777" datatype="html"> <trans-unit id="3477953895055172777" datatype="html">
<source>View Holding</source> <source>View Holding</source>
<target state="new">View Holding</target> <target state="translated">Ver fondos</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">libs/ui/src/lib/activities-table/activities-table.component.html</context> <context context-type="sourcefile">libs/ui/src/lib/activities-table/activities-table.component.html</context>
<context context-type="linenumber">450</context> <context context-type="linenumber">450</context>
@ -6683,7 +6683,7 @@
</trans-unit> </trans-unit>
<trans-unit id="4762855117875399861" datatype="html"> <trans-unit id="4762855117875399861" datatype="html">
<source>Oops! Could not update access.</source> <source>Oops! Could not update access.</source>
<target state="new">Oops! Could not update access.</target> <target state="translated">Oops! No se pudo actualizar el acceso.</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/components/user-account-access/create-or-update-access-dialog/create-or-update-access-dialog.component.ts</context> <context context-type="sourcefile">apps/client/src/app/components/user-account-access/create-or-update-access-dialog/create-or-update-access-dialog.component.ts</context>
<context context-type="linenumber">178</context> <context context-type="linenumber">178</context>
@ -6691,7 +6691,7 @@
</trans-unit> </trans-unit>
<trans-unit id="1355312194390410495" datatype="html"> <trans-unit id="1355312194390410495" datatype="html">
<source>, based on your total assets of</source> <source>, based on your total assets of</source>
<target state="new">, based on your total assets of</target> <target state="translated">, basado en tus activos totales de</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/pages/portfolio/fire/fire-page.html</context> <context context-type="sourcefile">apps/client/src/app/pages/portfolio/fire/fire-page.html</context>
<context context-type="linenumber">96</context> <context context-type="linenumber">96</context>
@ -6763,7 +6763,7 @@
</trans-unit> </trans-unit>
<trans-unit id="7819314041543176992" datatype="html"> <trans-unit id="7819314041543176992" datatype="html">
<source>Close</source> <source>Close</source>
<target state="translated">Cerca</target> <target state="translated">Cerrar</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html</context> <context context-type="sourcefile">apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html</context>
<context context-type="linenumber">594</context> <context context-type="linenumber">594</context>
@ -6807,7 +6807,7 @@
</trans-unit> </trans-unit>
<trans-unit id="4145496584631696119" datatype="html"> <trans-unit id="4145496584631696119" datatype="html">
<source>Role</source> <source>Role</source>
<target state="new">Role</target> <target state="translated">Rol</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/components/user-detail-dialog/user-detail-dialog.html</context> <context context-type="sourcefile">apps/client/src/app/components/user-detail-dialog/user-detail-dialog.html</context>
<context context-type="linenumber">14</context> <context context-type="linenumber">14</context>
@ -6839,7 +6839,7 @@
</trans-unit> </trans-unit>
<trans-unit id="8375528527939577247" datatype="html"> <trans-unit id="8375528527939577247" datatype="html">
<source><x id="START_BLOCK_IF" equiv-text="@if ( SymbolProfile?.currency &amp;&amp; data.baseCurrency !== SymbolProfile?.currency ) {"/> Change with currency effect <x id="CLOSE_BLOCK_IF" equiv-text="}"/><x id="START_BLOCK_ELSE" equiv-text="@else {"/> Change <x id="CLOSE_BLOCK_ELSE" equiv-text="}"/></source> <source><x id="START_BLOCK_IF" equiv-text="@if ( SymbolProfile?.currency &amp;&amp; data.baseCurrency !== SymbolProfile?.currency ) {"/> Change with currency effect <x id="CLOSE_BLOCK_IF" equiv-text="}"/><x id="START_BLOCK_ELSE" equiv-text="@else {"/> Change <x id="CLOSE_BLOCK_ELSE" equiv-text="}"/></source>
<target state="new"><x id="START_BLOCK_IF" equiv-text="@if ( SymbolProfile?.currency &amp;&amp; data.baseCurrency !== SymbolProfile?.currency ) {"/> Change with currency effect <x id="CLOSE_BLOCK_IF" equiv-text="}"/><x id="START_BLOCK_ELSE" equiv-text="@else {"/> Change <x id="CLOSE_BLOCK_ELSE" equiv-text="}"/></target> <target state="translated"><x id="START_BLOCK_IF" equiv-text="@if ( SymbolProfile?.currency &amp;&amp; data.baseCurrency !== SymbolProfile?.currency ) {"/> Cambiar con efecto de cambio dedivisa <x id="CLOSE_BLOCK_IF" equiv-text="}"/><x id="START_BLOCK_ELSE" equiv-text="@else {"/> Cambiar <x id="CLOSE_BLOCK_ELSE" equiv-text="}"/></target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/components/holding-detail-dialog/holding-detail-dialog.html</context> <context context-type="sourcefile">apps/client/src/app/components/holding-detail-dialog/holding-detail-dialog.html</context>
<context context-type="linenumber">63</context> <context context-type="linenumber">63</context>
@ -6847,7 +6847,7 @@
</trans-unit> </trans-unit>
<trans-unit id="6602358241522477056" datatype="html"> <trans-unit id="6602358241522477056" datatype="html">
<source>If you plan to open an account at</source> <source>If you plan to open an account at</source>
<target state="new">If you plan to open an account at</target> <target state="translated">Si planeas abrir una cuenta en</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/pages/pricing/pricing-page.html</context> <context context-type="sourcefile">apps/client/src/app/pages/pricing/pricing-page.html</context>
<context context-type="linenumber">312</context> <context context-type="linenumber">312</context>
@ -6855,7 +6855,7 @@
</trans-unit> </trans-unit>
<trans-unit id="6608617124920241143" datatype="html"> <trans-unit id="6608617124920241143" datatype="html">
<source><x id="START_BLOCK_IF" equiv-text="@if ( SymbolProfile?.currency &amp;&amp; data.baseCurrency !== SymbolProfile?.currency ) {"/> Performance with currency effect <x id="CLOSE_BLOCK_IF" equiv-text="}"/><x id="START_BLOCK_ELSE" equiv-text="@else {"/> Performance <x id="CLOSE_BLOCK_ELSE" equiv-text="}"/></source> <source><x id="START_BLOCK_IF" equiv-text="@if ( SymbolProfile?.currency &amp;&amp; data.baseCurrency !== SymbolProfile?.currency ) {"/> Performance with currency effect <x id="CLOSE_BLOCK_IF" equiv-text="}"/><x id="START_BLOCK_ELSE" equiv-text="@else {"/> Performance <x id="CLOSE_BLOCK_ELSE" equiv-text="}"/></source>
<target state="new"><x id="START_BLOCK_IF" equiv-text="@if ( SymbolProfile?.currency &amp;&amp; data.baseCurrency !== SymbolProfile?.currency ) {"/> Performance with currency effect <x id="CLOSE_BLOCK_IF" equiv-text="}"/><x id="START_BLOCK_ELSE" equiv-text="@else {"/> Performance <x id="CLOSE_BLOCK_ELSE" equiv-text="}"/></target> <target state="translated"><x id="START_BLOCK_IF" equiv-text="@if ( SymbolProfile?.currency &amp;&amp; data.baseCurrency !== SymbolProfile?.currency ) {"/> Rendimiento con cambio de divisa <x id="CLOSE_BLOCK_IF" equiv-text="}"/><x id="START_BLOCK_ELSE" equiv-text="@else {"/> Rendimiento <x id="CLOSE_BLOCK_ELSE" equiv-text="}"/></target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/components/holding-detail-dialog/holding-detail-dialog.html</context> <context context-type="sourcefile">apps/client/src/app/components/holding-detail-dialog/holding-detail-dialog.html</context>
<context context-type="linenumber">83</context> <context context-type="linenumber">83</context>
@ -6879,7 +6879,7 @@
</trans-unit> </trans-unit>
<trans-unit id="6664504469290651320" datatype="html"> <trans-unit id="6664504469290651320" datatype="html">
<source>send an e-mail to</source> <source>send an e-mail to</source>
<target state="new">send an e-mail to</target> <target state="translated">enviar un correo electrónico a</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/pages/about/overview/about-overview-page.html</context> <context context-type="sourcefile">apps/client/src/app/pages/about/overview/about-overview-page.html</context>
<context context-type="linenumber">87</context> <context context-type="linenumber">87</context>
@ -6951,7 +6951,7 @@
</trans-unit> </trans-unit>
<trans-unit id="2878377610946588870" datatype="html"> <trans-unit id="2878377610946588870" datatype="html">
<source>, assuming a</source> <source>, assuming a</source>
<target state="new">, assuming a</target> <target state="translated">, asumiendo un</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/pages/portfolio/fire/fire-page.html</context> <context context-type="sourcefile">apps/client/src/app/pages/portfolio/fire/fire-page.html</context>
<context context-type="linenumber">174</context> <context context-type="linenumber">174</context>
@ -6959,7 +6959,7 @@
</trans-unit> </trans-unit>
<trans-unit id="7522916136412124285" datatype="html"> <trans-unit id="7522916136412124285" datatype="html">
<source>to use our referral link and get a Ghostfolio Premium membership for one year</source> <source>to use our referral link and get a Ghostfolio Premium membership for one year</source>
<target state="new">to use our referral link and get a Ghostfolio Premium membership for one year</target> <target state="translated">para usar nuestro enlace de referido y obtener una membresía Ghostfolio Premium por un año</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/pages/pricing/pricing-page.html</context> <context context-type="sourcefile">apps/client/src/app/pages/pricing/pricing-page.html</context>
<context context-type="linenumber">340</context> <context context-type="linenumber">340</context>
@ -7039,7 +7039,7 @@
</trans-unit> </trans-unit>
<trans-unit id="3528767106831563012" datatype="html"> <trans-unit id="3528767106831563012" datatype="html">
<source>Ghostfolio is a lightweight wealth management application for individuals to keep track of stocks, ETFs or cryptocurrencies and make solid, data-driven investment decisions.</source> <source>Ghostfolio is a lightweight wealth management application for individuals to keep track of stocks, ETFs or cryptocurrencies and make solid, data-driven investment decisions.</source>
<target state="new">Ghostfolio is a lightweight wealth management application for individuals to keep track of stocks, ETFs or cryptocurrencies and make solid, data-driven investment decisions.</target> <target state="translated">Ghostfolio es una aplicación de gestión de patrimonio para aquellos individuos que desean realizar un seguimiento de acciones, ETFs o criptomonedas y tomar decisiones de inversión sólidas y basadas en datos.</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/pages/about/overview/about-overview-page.html</context> <context context-type="sourcefile">apps/client/src/app/pages/about/overview/about-overview-page.html</context>
<context context-type="linenumber">10</context> <context context-type="linenumber">10</context>
@ -7353,7 +7353,7 @@
</trans-unit> </trans-unit>
<trans-unit id="1789421195684815451" datatype="html"> <trans-unit id="1789421195684815451" datatype="html">
<source>Check the system status at</source> <source>Check the system status at</source>
<target state="new">Check the system status at</target> <target state="translated">Verificar el estado del sistema en</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/pages/about/overview/about-overview-page.html</context> <context context-type="sourcefile">apps/client/src/app/pages/about/overview/about-overview-page.html</context>
<context context-type="linenumber">57</context> <context context-type="linenumber">57</context>
@ -7369,7 +7369,7 @@
</trans-unit> </trans-unit>
<trans-unit id="7825231215382064101" datatype="html"> <trans-unit id="7825231215382064101" datatype="html">
<source>Change with currency effect</source> <source>Change with currency effect</source>
<target state="new">Change with currency effect</target> <target state="translated">Cambiar con el efecto del tipo de cambio de divisa</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/pages/portfolio/analysis/analysis-page.html</context> <context context-type="sourcefile">apps/client/src/app/pages/portfolio/analysis/analysis-page.html</context>
<context context-type="linenumber">116</context> <context context-type="linenumber">116</context>
@ -7509,7 +7509,7 @@
</trans-unit> </trans-unit>
<trans-unit id="1325095699053123251" datatype="html"> <trans-unit id="1325095699053123251" datatype="html">
<source>The project has been initiated by</source> <source>The project has been initiated by</source>
<target state="new">The project has been initiated by</target> <target state="translated">El proyecto ha sido iniciado por</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/pages/about/overview/about-overview-page.html</context> <context context-type="sourcefile">apps/client/src/app/pages/about/overview/about-overview-page.html</context>
<context context-type="linenumber">40</context> <context context-type="linenumber">40</context>
@ -7533,7 +7533,7 @@
</trans-unit> </trans-unit>
<trans-unit id="5004550577313573215" datatype="html"> <trans-unit id="5004550577313573215" datatype="html">
<source>Total amount</source> <source>Total amount</source>
<target state="new">Total amount</target> <target state="translated">Cantidad total</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/pages/portfolio/analysis/analysis-page.html</context> <context context-type="sourcefile">apps/client/src/app/pages/portfolio/analysis/analysis-page.html</context>
<context context-type="linenumber">95</context> <context context-type="linenumber">95</context>
@ -7625,7 +7625,7 @@
</trans-unit> </trans-unit>
<trans-unit id="6752851341939241310" datatype="html"> <trans-unit id="6752851341939241310" datatype="html">
<source>Find account, holding or page...</source> <source>Find account, holding or page...</source>
<target state="new">Find account, holding or page...</target> <target state="translated">Buscar cuenta, posición o página...</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">libs/ui/src/lib/assistant/assistant.component.ts</context> <context context-type="sourcefile">libs/ui/src/lib/assistant/assistant.component.ts</context>
<context context-type="linenumber">151</context> <context context-type="linenumber">151</context>
@ -8049,7 +8049,7 @@
</trans-unit> </trans-unit>
<trans-unit id="7383756232563820625" datatype="html"> <trans-unit id="7383756232563820625" datatype="html">
<source>Current month</source> <source>Current month</source>
<target state="new">Current month</target> <target state="translated">Mes actual</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts</context> <context context-type="sourcefile">apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts</context>
<context context-type="linenumber">195</context> <context context-type="linenumber">195</context>
@ -8234,7 +8234,7 @@
</trans-unit> </trans-unit>
<trans-unit id="5199695670214400859" datatype="html"> <trans-unit id="5199695670214400859" datatype="html">
<source>If you encounter a bug, would like to suggest an improvement or a new <x id="START_LINK" ctype="x-a" equiv-text="&lt;a [routerLink]=&quot;routerLinkFeatures&quot;&gt;"/>feature<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a &gt;"/>, please join the Ghostfolio <x id="START_LINK_1" equiv-text="&lt;a href=&quot;https://join.slack.com/t/ghostfolio/shared_invite/zt-vsaan64h-F_I0fEo5M0P88lP9ibCxFg&quot; title=&quot;Join the Ghostfolio Slack community&quot; &gt;"/>Slack<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a &gt;"/> community, post to <x id="START_LINK_2" equiv-text="&lt;a href=&quot;https://x.com/ghostfolio_&quot; title=&quot;Post to Ghostfolio on X (formerly Twitter)&quot; &gt;"/>@ghostfolio_<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a &gt;"/></source> <source>If you encounter a bug, would like to suggest an improvement or a new <x id="START_LINK" ctype="x-a" equiv-text="&lt;a [routerLink]=&quot;routerLinkFeatures&quot;&gt;"/>feature<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a &gt;"/>, please join the Ghostfolio <x id="START_LINK_1" equiv-text="&lt;a href=&quot;https://join.slack.com/t/ghostfolio/shared_invite/zt-vsaan64h-F_I0fEo5M0P88lP9ibCxFg&quot; title=&quot;Join the Ghostfolio Slack community&quot; &gt;"/>Slack<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a &gt;"/> community, post to <x id="START_LINK_2" equiv-text="&lt;a href=&quot;https://x.com/ghostfolio_&quot; title=&quot;Post to Ghostfolio on X (formerly Twitter)&quot; &gt;"/>@ghostfolio_<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a &gt;"/></source>
<target state="new">If you encounter a bug, would like to suggest an improvement or a new <x id="START_LINK" ctype="x-a" equiv-text="&lt;a [routerLink]=&quot;routerLinkFeatures&quot;&gt;"/>feature<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a &gt;"/>, please join the Ghostfolio <x id="START_LINK_1" equiv-text="&lt;a href=&quot;https://join.slack.com/t/ghostfolio/shared_invite/zt-vsaan64h-F_I0fEo5M0P88lP9ibCxFg&quot; title=&quot;Join the Ghostfolio Slack community&quot; &gt;"/>Slack<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a &gt;"/> community, post to <x id="START_LINK_2" equiv-text="&lt;a href=&quot;https://x.com/ghostfolio_&quot; title=&quot;Post to Ghostfolio on X (formerly Twitter)&quot; &gt;"/>@ghostfolio_<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a &gt;"/></target> <target state="translated">Si encuentras un error, deseas sugerir una mejora o una nueva <x id="START_LINK" ctype="x-a" equiv-text="&lt;a [routerLink]=&quot;routerLinkFeatures&quot;&gt;"/>característica<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a &gt;"/>, por favor únete a la comunidad Ghostfolio <x id="START_LINK_1" equiv-text="&lt;a href=&quot;https://join.slack.com/t/ghostfolio/shared_invite/zt-vsaan64h-F_I0fEo5M0P88lP9ibCxFg&quot; title=&quot;Join the Ghostfolio Slack community&quot; &gt;"/>Slack<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a &gt;"/>, publica en <x id="START_LINK_2" equiv-text="&lt;a href=&quot;https://x.com/ghostfolio_&quot; title=&quot;Post to Ghostfolio on X (formerly Twitter)&quot; &gt;"/>@ghostfolio_<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a &gt;"/></target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/pages/about/overview/about-overview-page.html</context> <context context-type="sourcefile">apps/client/src/app/pages/about/overview/about-overview-page.html</context>
<context context-type="linenumber">69</context> <context context-type="linenumber">69</context>
@ -8266,7 +8266,7 @@
</trans-unit> </trans-unit>
<trans-unit id="187187500641108332" datatype="html"> <trans-unit id="187187500641108332" datatype="html">
<source><x id="INTERPOLATION" equiv-text="{{ `Expires ${formatDistanceToNow( element.subscription.expiresAt )} (${ (element.subscription.expiresAt | date: defaultDateFormat) })` }}"/></source> <source><x id="INTERPOLATION" equiv-text="{{ `Expires ${formatDistanceToNow( element.subscription.expiresAt )} (${ (element.subscription.expiresAt | date: defaultDateFormat) })` }}"/></source>
<target state="new"><x id="INTERPOLATION" equiv-text="{{ `Expires ${formatDistanceToNow( element.subscription.expiresAt )} (${ (element.subscription.expiresAt | date: defaultDateFormat) })` }}"/></target> <target state="translated"><x id="INTERPOLATION" equiv-text="{{ `Expira ${formatDistanceToNow( element.subscription.expiresAt )} (${ (element.subscription.expiresAt | date: defaultDateFormat) })` }}"/></target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/components/admin-users/admin-users.html</context> <context context-type="sourcefile">apps/client/src/app/components/admin-users/admin-users.html</context>
<context context-type="linenumber">39</context> <context context-type="linenumber">39</context>
@ -8334,7 +8334,7 @@
</trans-unit> </trans-unit>
<trans-unit id="rule.economicMarketClusterRisk.category" datatype="html"> <trans-unit id="rule.economicMarketClusterRisk.category" datatype="html">
<source>Economic Market Cluster Risks</source> <source>Economic Market Cluster Risks</source>
<target state="new">Economic Market Cluster Risks</target> <target state="translated">Riesgos del clúster de mercados económicos</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/pages/i18n/i18n-page.html</context> <context context-type="sourcefile">apps/client/src/app/pages/i18n/i18n-page.html</context>
<context context-type="linenumber">106</context> <context context-type="linenumber">106</context>
@ -8342,7 +8342,7 @@
</trans-unit> </trans-unit>
<trans-unit id="rule.emergencyFund.category" datatype="html"> <trans-unit id="rule.emergencyFund.category" datatype="html">
<source>Emergency Fund</source> <source>Emergency Fund</source>
<target state="new">Emergency Fund</target> <target state="translated">Fondo de emergencia</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/pages/i18n/i18n-page.html</context> <context context-type="sourcefile">apps/client/src/app/pages/i18n/i18n-page.html</context>
<context context-type="linenumber">144</context> <context context-type="linenumber">144</context>
@ -8350,7 +8350,7 @@
</trans-unit> </trans-unit>
<trans-unit id="rule.fees.category" datatype="html"> <trans-unit id="rule.fees.category" datatype="html">
<source>Fees</source> <source>Fees</source>
<target state="new">Fees</target> <target state="translated">Comisiones</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/pages/i18n/i18n-page.html</context> <context context-type="sourcefile">apps/client/src/app/pages/i18n/i18n-page.html</context>
<context context-type="linenumber">161</context> <context context-type="linenumber">161</context>
@ -8358,7 +8358,7 @@
</trans-unit> </trans-unit>
<trans-unit id="rule.liquidity.category" datatype="html"> <trans-unit id="rule.liquidity.category" datatype="html">
<source>Liquidity</source> <source>Liquidity</source>
<target state="new">Liquidity</target> <target state="translated">Liquidez</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/pages/i18n/i18n-page.html</context> <context context-type="sourcefile">apps/client/src/app/pages/i18n/i18n-page.html</context>
<context context-type="linenumber">70</context> <context context-type="linenumber">70</context>
@ -8366,7 +8366,7 @@
</trans-unit> </trans-unit>
<trans-unit id="rule.liquidityBuyingPower" datatype="html"> <trans-unit id="rule.liquidityBuyingPower" datatype="html">
<source>Buying Power</source> <source>Buying Power</source>
<target state="new">Buying Power</target> <target state="translated">Poder de compra</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/pages/i18n/i18n-page.html</context> <context context-type="sourcefile">apps/client/src/app/pages/i18n/i18n-page.html</context>
<context context-type="linenumber">71</context> <context context-type="linenumber">71</context>
@ -8374,7 +8374,7 @@
</trans-unit> </trans-unit>
<trans-unit id="rule.liquidityBuyingPower.false.min" datatype="html"> <trans-unit id="rule.liquidityBuyingPower.false.min" datatype="html">
<source>Your buying power is below ${thresholdMin} ${baseCurrency}</source> <source>Your buying power is below ${thresholdMin} ${baseCurrency}</source>
<target state="new">Your buying power is below ${thresholdMin} ${baseCurrency}</target> <target state="translated">Tu poder de compra es inferior a ${thresholdMin} ${baseCurrency}</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/pages/i18n/i18n-page.html</context> <context context-type="sourcefile">apps/client/src/app/pages/i18n/i18n-page.html</context>
<context context-type="linenumber">73</context> <context context-type="linenumber">73</context>
@ -8382,7 +8382,7 @@
</trans-unit> </trans-unit>
<trans-unit id="rule.liquidityBuyingPower.false.zero" datatype="html"> <trans-unit id="rule.liquidityBuyingPower.false.zero" datatype="html">
<source>Your buying power is 0 ${baseCurrency}</source> <source>Your buying power is 0 ${baseCurrency}</source>
<target state="new">Your buying power is 0 ${baseCurrency}</target> <target state="translated">Tu poder de compra es 0 ${baseCurrency}</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/pages/i18n/i18n-page.html</context> <context context-type="sourcefile">apps/client/src/app/pages/i18n/i18n-page.html</context>
<context context-type="linenumber">77</context> <context context-type="linenumber">77</context>
@ -8390,7 +8390,7 @@
</trans-unit> </trans-unit>
<trans-unit id="rule.liquidityBuyingPower.true" datatype="html"> <trans-unit id="rule.liquidityBuyingPower.true" datatype="html">
<source>Your buying power exceeds ${thresholdMin} ${baseCurrency}</source> <source>Your buying power exceeds ${thresholdMin} ${baseCurrency}</source>
<target state="new">Your buying power exceeds ${thresholdMin} ${baseCurrency}</target> <target state="translated">Tu poder de compra excede ${thresholdMin} ${baseCurrency}</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/pages/i18n/i18n-page.html</context> <context context-type="sourcefile">apps/client/src/app/pages/i18n/i18n-page.html</context>
<context context-type="linenumber">80</context> <context context-type="linenumber">80</context>
@ -8422,7 +8422,7 @@
</trans-unit> </trans-unit>
<trans-unit id="rule.economicMarketClusterRiskDevelopedMarkets.false.max" datatype="html"> <trans-unit id="rule.economicMarketClusterRiskDevelopedMarkets.false.max" datatype="html">
<source>The developed markets contribution of your current investment (${developedMarketsValueRatio}%) exceeds ${thresholdMax}%</source> <source>The developed markets contribution of your current investment (${developedMarketsValueRatio}%) exceeds ${thresholdMax}%</source>
<target state="new">The developed markets contribution of your current investment (${developedMarketsValueRatio}%) exceeds ${thresholdMax}%</target> <target state="translated">La contribución a los mercados desarrollados de tu inversión actual (${developedMarketsValueRatio}%) supera el ${thresholdMax}%</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/pages/i18n/i18n-page.html</context> <context context-type="sourcefile">apps/client/src/app/pages/i18n/i18n-page.html</context>
<context context-type="linenumber">112</context> <context context-type="linenumber">112</context>
@ -8430,7 +8430,7 @@
</trans-unit> </trans-unit>
<trans-unit id="rule.economicMarketClusterRiskDevelopedMarkets.false.min" datatype="html"> <trans-unit id="rule.economicMarketClusterRiskDevelopedMarkets.false.min" datatype="html">
<source>The developed markets contribution of your current investment (${developedMarketsValueRatio}%) is below ${thresholdMin}%</source> <source>The developed markets contribution of your current investment (${developedMarketsValueRatio}%) is below ${thresholdMin}%</source>
<target state="new">The developed markets contribution of your current investment (${developedMarketsValueRatio}%) is below ${thresholdMin}%</target> <target state="translated">La contribución a los mercados desarrollados de tu inversión actual (${developedMarketsValueRatio}%) es inferior al ${thresholdMin}%</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/pages/i18n/i18n-page.html</context> <context context-type="sourcefile">apps/client/src/app/pages/i18n/i18n-page.html</context>
<context context-type="linenumber">117</context> <context context-type="linenumber">117</context>
@ -8438,7 +8438,7 @@
</trans-unit> </trans-unit>
<trans-unit id="rule.economicMarketClusterRiskDevelopedMarkets.true" datatype="html"> <trans-unit id="rule.economicMarketClusterRiskDevelopedMarkets.true" datatype="html">
<source>The developed markets contribution of your current investment (${developedMarketsValueRatio}%) is within the range of ${thresholdMin}% and ${thresholdMax}%</source> <source>The developed markets contribution of your current investment (${developedMarketsValueRatio}%) is within the range of ${thresholdMin}% and ${thresholdMax}%</source>
<target state="new">The developed markets contribution of your current investment (${developedMarketsValueRatio}%) is within the range of ${thresholdMin}% and ${thresholdMax}%</target> <target state="translated">La contribución a los mercados desarrollados de tu inversión actual (${developedMarketsValueRatio}%) está dentro del rango de ${thresholdMin}% y ${thresholdMax}%</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/pages/i18n/i18n-page.html</context> <context context-type="sourcefile">apps/client/src/app/pages/i18n/i18n-page.html</context>
<context context-type="linenumber">122</context> <context context-type="linenumber">122</context>
@ -8454,7 +8454,7 @@
</trans-unit> </trans-unit>
<trans-unit id="rule.economicMarketClusterRiskEmergingMarkets.false.max" datatype="html"> <trans-unit id="rule.economicMarketClusterRiskEmergingMarkets.false.max" datatype="html">
<source>The emerging markets contribution of your current investment (${emergingMarketsValueRatio}%) exceeds ${thresholdMax}%</source> <source>The emerging markets contribution of your current investment (${emergingMarketsValueRatio}%) exceeds ${thresholdMax}%</source>
<target state="new">The emerging markets contribution of your current investment (${emergingMarketsValueRatio}%) exceeds ${thresholdMax}%</target> <target state="translated">La contribución a los mercados emergentes de tu inversión actual (${emergingMarketsValueRatio}%) supera el ${thresholdMax}%</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/pages/i18n/i18n-page.html</context> <context context-type="sourcefile">apps/client/src/app/pages/i18n/i18n-page.html</context>
<context context-type="linenumber">130</context> <context context-type="linenumber">130</context>
@ -8462,7 +8462,7 @@
</trans-unit> </trans-unit>
<trans-unit id="rule.economicMarketClusterRiskEmergingMarkets.false.min" datatype="html"> <trans-unit id="rule.economicMarketClusterRiskEmergingMarkets.false.min" datatype="html">
<source>The emerging markets contribution of your current investment (${emergingMarketsValueRatio}%) is below ${thresholdMin}%</source> <source>The emerging markets contribution of your current investment (${emergingMarketsValueRatio}%) is below ${thresholdMin}%</source>
<target state="new">The emerging markets contribution of your current investment (${emergingMarketsValueRatio}%) is below ${thresholdMin}%</target> <target state="translated">La contribución a los mercados emergentes de tu inversión actual (${emergingMarketsValueRatio}%) es inferior al ${thresholdMin}%</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/pages/i18n/i18n-page.html</context> <context context-type="sourcefile">apps/client/src/app/pages/i18n/i18n-page.html</context>
<context context-type="linenumber">135</context> <context context-type="linenumber">135</context>
@ -8470,7 +8470,7 @@
</trans-unit> </trans-unit>
<trans-unit id="rule.economicMarketClusterRiskEmergingMarkets.true" datatype="html"> <trans-unit id="rule.economicMarketClusterRiskEmergingMarkets.true" datatype="html">
<source>The emerging markets contribution of your current investment (${emergingMarketsValueRatio}%) is within the range of ${thresholdMin}% and ${thresholdMax}%</source> <source>The emerging markets contribution of your current investment (${emergingMarketsValueRatio}%) is within the range of ${thresholdMin}% and ${thresholdMax}%</source>
<target state="new">The emerging markets contribution of your current investment (${emergingMarketsValueRatio}%) is within the range of ${thresholdMin}% and ${thresholdMax}%</target> <target state="translated">La contribución a los mercados emergentes de tu inversión actual (${emergingMarketsValueRatio}%) está dentro del rango de ${thresholdMin}% y ${thresholdMax}%</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/pages/i18n/i18n-page.html</context> <context context-type="sourcefile">apps/client/src/app/pages/i18n/i18n-page.html</context>
<context context-type="linenumber">140</context> <context context-type="linenumber">140</context>
@ -8494,7 +8494,7 @@
</trans-unit> </trans-unit>
<trans-unit id="rule.regionalMarketClusterRiskAsiaPacific" datatype="html"> <trans-unit id="rule.regionalMarketClusterRiskAsiaPacific" datatype="html">
<source>Asia-Pacific</source> <source>Asia-Pacific</source>
<target state="new">Asia-Pacific</target> <target state="translated">Asia-Pacífico</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/pages/i18n/i18n-page.html</context> <context context-type="sourcefile">apps/client/src/app/pages/i18n/i18n-page.html</context>
<context context-type="linenumber">165</context> <context context-type="linenumber">165</context>
@ -8502,7 +8502,7 @@
</trans-unit> </trans-unit>
<trans-unit id="rule.regionalMarketClusterRiskAsiaPacific.false.max" datatype="html"> <trans-unit id="rule.regionalMarketClusterRiskAsiaPacific.false.max" datatype="html">
<source>The Asia-Pacific market contribution of your current investment (${valueRatio}%) exceeds ${thresholdMax}%</source> <source>The Asia-Pacific market contribution of your current investment (${valueRatio}%) exceeds ${thresholdMax}%</source>
<target state="new">The Asia-Pacific market contribution of your current investment (${valueRatio}%) exceeds ${thresholdMax}%</target> <target state="translated">La contribución al mercado de Asia-Pacífico de tu inversión actual (${valueRatio}%) supera el ${thresholdMax}%</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/pages/i18n/i18n-page.html</context> <context context-type="sourcefile">apps/client/src/app/pages/i18n/i18n-page.html</context>
<context context-type="linenumber">167</context> <context context-type="linenumber">167</context>
@ -8510,7 +8510,7 @@
</trans-unit> </trans-unit>
<trans-unit id="rule.regionalMarketClusterRiskAsiaPacific.false.min" datatype="html"> <trans-unit id="rule.regionalMarketClusterRiskAsiaPacific.false.min" datatype="html">
<source>The Asia-Pacific market contribution of your current investment (${valueRatio}%) is below ${thresholdMin}%</source> <source>The Asia-Pacific market contribution of your current investment (${valueRatio}%) is below ${thresholdMin}%</source>
<target state="new">The Asia-Pacific market contribution of your current investment (${valueRatio}%) is below ${thresholdMin}%</target> <target state="translated">La contribución al mercado de Asia-Pacífico de tu inversión actual (${valueRatio}%) es inferior al ${thresholdMin}%</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/pages/i18n/i18n-page.html</context> <context context-type="sourcefile">apps/client/src/app/pages/i18n/i18n-page.html</context>
<context context-type="linenumber">171</context> <context context-type="linenumber">171</context>
@ -8518,7 +8518,7 @@
</trans-unit> </trans-unit>
<trans-unit id="rule.regionalMarketClusterRiskAsiaPacific.true" datatype="html"> <trans-unit id="rule.regionalMarketClusterRiskAsiaPacific.true" datatype="html">
<source>The Asia-Pacific market contribution of your current investment (${valueRatio}%) is within the range of ${thresholdMin}% and ${thresholdMax}%</source> <source>The Asia-Pacific market contribution of your current investment (${valueRatio}%) is within the range of ${thresholdMin}% and ${thresholdMax}%</source>
<target state="new">The Asia-Pacific market contribution of your current investment (${valueRatio}%) is within the range of ${thresholdMin}% and ${thresholdMax}%</target> <target state="translated">La contribución al mercado de Asia-Pacífico de tu inversión actual (${valueRatio}%) está dentro del rango de ${thresholdMin}% y ${thresholdMax}%</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/pages/i18n/i18n-page.html</context> <context context-type="sourcefile">apps/client/src/app/pages/i18n/i18n-page.html</context>
<context context-type="linenumber">175</context> <context context-type="linenumber">175</context>
@ -8526,7 +8526,7 @@
</trans-unit> </trans-unit>
<trans-unit id="rule.regionalMarketClusterRiskEmergingMarkets" datatype="html"> <trans-unit id="rule.regionalMarketClusterRiskEmergingMarkets" datatype="html">
<source>Emerging Markets</source> <source>Emerging Markets</source>
<target state="new">Emerging Markets</target> <target state="translated">Mercados emergentes</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/pages/i18n/i18n-page.html</context> <context context-type="sourcefile">apps/client/src/app/pages/i18n/i18n-page.html</context>
<context context-type="linenumber">180</context> <context context-type="linenumber">180</context>
@ -8534,7 +8534,7 @@
</trans-unit> </trans-unit>
<trans-unit id="rule.regionalMarketClusterRiskEmergingMarkets.false.max" datatype="html"> <trans-unit id="rule.regionalMarketClusterRiskEmergingMarkets.false.max" datatype="html">
<source>The Emerging Markets contribution of your current investment (${valueRatio}%) exceeds ${thresholdMax}%</source> <source>The Emerging Markets contribution of your current investment (${valueRatio}%) exceeds ${thresholdMax}%</source>
<target state="new">The Emerging Markets contribution of your current investment (${valueRatio}%) exceeds ${thresholdMax}%</target> <target state="translated">La contribución a los mercados emergentes de tu inversión actual (${valueRatio}%) supera el ${thresholdMax}%</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/pages/i18n/i18n-page.html</context> <context context-type="sourcefile">apps/client/src/app/pages/i18n/i18n-page.html</context>
<context context-type="linenumber">183</context> <context context-type="linenumber">183</context>
@ -8542,7 +8542,7 @@
</trans-unit> </trans-unit>
<trans-unit id="rule.regionalMarketClusterRiskEmergingMarkets.false.min" datatype="html"> <trans-unit id="rule.regionalMarketClusterRiskEmergingMarkets.false.min" datatype="html">
<source>The Emerging Markets contribution of your current investment (${valueRatio}%) is below ${thresholdMin}%</source> <source>The Emerging Markets contribution of your current investment (${valueRatio}%) is below ${thresholdMin}%</source>
<target state="new">The Emerging Markets contribution of your current investment (${valueRatio}%) is below ${thresholdMin}%</target> <target state="translated">La contribución a los mercados emergentes de tu inversión actual (${valueRatio}%) es inferior al ${thresholdMin}%</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/pages/i18n/i18n-page.html</context> <context context-type="sourcefile">apps/client/src/app/pages/i18n/i18n-page.html</context>
<context context-type="linenumber">187</context> <context context-type="linenumber">187</context>
@ -8550,7 +8550,7 @@
</trans-unit> </trans-unit>
<trans-unit id="rule.regionalMarketClusterRiskEmergingMarkets.true" datatype="html"> <trans-unit id="rule.regionalMarketClusterRiskEmergingMarkets.true" datatype="html">
<source>The Emerging Markets contribution of your current investment (${valueRatio}%) is within the range of ${thresholdMin}% and ${thresholdMax}%</source> <source>The Emerging Markets contribution of your current investment (${valueRatio}%) is within the range of ${thresholdMin}% and ${thresholdMax}%</source>
<target state="new">The Emerging Markets contribution of your current investment (${valueRatio}%) is within the range of ${thresholdMin}% and ${thresholdMax}%</target> <target state="translated">La contribución a los mercados emergentes de tu inversión actual (${valueRatio}%) está dentro del rango de ${thresholdMin}% y ${thresholdMax}%</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/pages/i18n/i18n-page.html</context> <context context-type="sourcefile">apps/client/src/app/pages/i18n/i18n-page.html</context>
<context context-type="linenumber">191</context> <context context-type="linenumber">191</context>
@ -8558,7 +8558,7 @@
</trans-unit> </trans-unit>
<trans-unit id="rule.regionalMarketClusterRiskEurope" datatype="html"> <trans-unit id="rule.regionalMarketClusterRiskEurope" datatype="html">
<source>Europe</source> <source>Europe</source>
<target state="new">Europe</target> <target state="translated">Europa</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/pages/i18n/i18n-page.html</context> <context context-type="sourcefile">apps/client/src/app/pages/i18n/i18n-page.html</context>
<context context-type="linenumber">195</context> <context context-type="linenumber">195</context>
@ -8566,7 +8566,7 @@
</trans-unit> </trans-unit>
<trans-unit id="rule.regionalMarketClusterRiskEurope.false.max" datatype="html"> <trans-unit id="rule.regionalMarketClusterRiskEurope.false.max" datatype="html">
<source>The Europe market contribution of your current investment (${valueRatio}%) exceeds ${thresholdMax}%</source> <source>The Europe market contribution of your current investment (${valueRatio}%) exceeds ${thresholdMax}%</source>
<target state="new">The Europe market contribution of your current investment (${valueRatio}%) exceeds ${thresholdMax}%</target> <target state="translated">La contribución al mercado europeo de tu inversión actual (${valueRatio}%) supera el ${thresholdMax}%</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/pages/i18n/i18n-page.html</context> <context context-type="sourcefile">apps/client/src/app/pages/i18n/i18n-page.html</context>
<context context-type="linenumber">197</context> <context context-type="linenumber">197</context>
@ -8574,7 +8574,7 @@
</trans-unit> </trans-unit>
<trans-unit id="rule.regionalMarketClusterRiskEurope.false.min" datatype="html"> <trans-unit id="rule.regionalMarketClusterRiskEurope.false.min" datatype="html">
<source>The Europe market contribution of your current investment (${valueRatio}%) is below ${thresholdMin}%</source> <source>The Europe market contribution of your current investment (${valueRatio}%) is below ${thresholdMin}%</source>
<target state="new">The Europe market contribution of your current investment (${valueRatio}%) is below ${thresholdMin}%</target> <target state="translated">La contribución al mercado europeo de tu inversión actual (${valueRatio}%) es inferior al ${thresholdMin}%</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/pages/i18n/i18n-page.html</context> <context context-type="sourcefile">apps/client/src/app/pages/i18n/i18n-page.html</context>
<context context-type="linenumber">201</context> <context context-type="linenumber">201</context>
@ -8582,7 +8582,7 @@
</trans-unit> </trans-unit>
<trans-unit id="rule.regionalMarketClusterRiskEurope.true" datatype="html"> <trans-unit id="rule.regionalMarketClusterRiskEurope.true" datatype="html">
<source>The Europe market contribution of your current investment (${valueRatio}%) is within the range of ${thresholdMin}% and ${thresholdMax}%</source> <source>The Europe market contribution of your current investment (${valueRatio}%) is within the range of ${thresholdMin}% and ${thresholdMax}%</source>
<target state="new">The Europe market contribution of your current investment (${valueRatio}%) is within the range of ${thresholdMin}% and ${thresholdMax}%</target> <target state="translated">La contribución al mercado europeo de tu inversión actual (${valueRatio}%) está dentro del rango de ${thresholdMin}% y ${thresholdMax}%</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/pages/i18n/i18n-page.html</context> <context context-type="sourcefile">apps/client/src/app/pages/i18n/i18n-page.html</context>
<context context-type="linenumber">205</context> <context context-type="linenumber">205</context>
@ -8694,7 +8694,7 @@
</trans-unit> </trans-unit>
<trans-unit id="339860602695747533" datatype="html"> <trans-unit id="339860602695747533" datatype="html">
<source>Registration Date</source> <source>Registration Date</source>
<target state="new">Registration Date</target> <target state="translated">Fecha de registro</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/components/user-detail-dialog/user-detail-dialog.html</context> <context context-type="sourcefile">apps/client/src/app/components/user-detail-dialog/user-detail-dialog.html</context>
<context context-type="linenumber">26</context> <context context-type="linenumber">26</context>

52
libs/common/src/lib/config.ts

@ -78,6 +78,58 @@ export const DEFAULT_PROCESSOR_GATHER_HISTORICAL_MARKET_DATA_CONCURRENCY = 1;
export const DEFAULT_PROCESSOR_PORTFOLIO_SNAPSHOT_COMPUTATION_CONCURRENCY = 1; export const DEFAULT_PROCESSOR_PORTFOLIO_SNAPSHOT_COMPUTATION_CONCURRENCY = 1;
export const DEFAULT_PROCESSOR_PORTFOLIO_SNAPSHOT_COMPUTATION_TIMEOUT = 30000; export const DEFAULT_PROCESSOR_PORTFOLIO_SNAPSHOT_COMPUTATION_TIMEOUT = 30000;
export const DEFAULT_REDACTED_PATHS = [
'accounts[*].balance',
'accounts[*].valueInBaseCurrency',
'activities[*].account.balance',
'activities[*].account.comment',
'activities[*].comment',
'activities[*].fee',
'activities[*].feeInAssetProfileCurrency',
'activities[*].feeInBaseCurrency',
'activities[*].quantity',
'activities[*].SymbolProfile.symbolMapping',
'activities[*].SymbolProfile.watchedByCount',
'activities[*].value',
'activities[*].valueInBaseCurrency',
'balance',
'balanceInBaseCurrency',
'balances[*].account.balance',
'balances[*].account.comment',
'balances[*].value',
'balances[*].valueInBaseCurrency',
'comment',
'dividendInBaseCurrency',
'feeInBaseCurrency',
'grossPerformance',
'grossPerformanceWithCurrencyEffect',
'historicalData[*].quantity',
'holdings[*].dividend',
'holdings[*].grossPerformance',
'holdings[*].grossPerformanceWithCurrencyEffect',
'holdings[*].holdings[*].valueInBaseCurrency',
'holdings[*].investment',
'holdings[*].netPerformance',
'holdings[*].netPerformanceWithCurrencyEffect',
'holdings[*].quantity',
'holdings[*].valueInBaseCurrency',
'interestInBaseCurrency',
'investmentInBaseCurrencyWithCurrencyEffect',
'netPerformance',
'netPerformanceWithCurrencyEffect',
'platforms[*].balance',
'platforms[*].valueInBaseCurrency',
'quantity',
'SymbolProfile.symbolMapping',
'SymbolProfile.watchedByCount',
'totalBalanceInBaseCurrency',
'totalDividendInBaseCurrency',
'totalInterestInBaseCurrency',
'totalValueInBaseCurrency',
'value',
'valueInBaseCurrency'
];
// USX is handled separately // USX is handled separately
export const DERIVED_CURRENCIES = [ export const DERIVED_CURRENCIES = [
{ {

14
libs/common/src/lib/helper.ts

@ -223,8 +223,8 @@ export function getDateFormatString(aLocale?: string) {
); );
return formatObject return formatObject
.map((object) => { .map(({ type, value }) => {
switch (object.type) { switch (type) {
case 'day': case 'day':
return 'dd'; return 'dd';
case 'month': case 'month':
@ -232,7 +232,7 @@ export function getDateFormatString(aLocale?: string) {
case 'year': case 'year':
return 'yyyy'; return 'yyyy';
default: default:
return object.value; return value;
} }
}) })
.join(''); .join('');
@ -271,8 +271,8 @@ export function getLowercase(object: object, path: string) {
export function getNumberFormatDecimal(aLocale?: string) { export function getNumberFormatDecimal(aLocale?: string) {
const formatObject = new Intl.NumberFormat(aLocale).formatToParts(9999.99); const formatObject = new Intl.NumberFormat(aLocale).formatToParts(9999.99);
return formatObject.find((object) => { return formatObject.find(({ type }) => {
return object.type === 'decimal'; return type === 'decimal';
})?.value; })?.value;
} }
@ -281,8 +281,8 @@ export function getNumberFormatGroup(aLocale = getLocale()) {
useGrouping: true useGrouping: true
}).formatToParts(9999.99); }).formatToParts(9999.99);
return formatObject.find((object) => { return formatObject.find(({ type }) => {
return object.type === 'group'; return type === 'group';
})?.value; })?.value;
} }

4
libs/common/src/lib/interfaces/admin-data.interface.ts

@ -1,12 +1,16 @@
import { DataProviderInfo } from './data-provider-info.interface'; import { DataProviderInfo } from './data-provider-info.interface';
export interface AdminData { export interface AdminData {
activitiesCount: number;
dataProviders: (DataProviderInfo & { dataProviders: (DataProviderInfo & {
assetProfileCount: number; assetProfileCount: number;
useForExchangeRates: boolean; useForExchangeRates: boolean;
})[]; })[];
settings: { [key: string]: boolean | object | string | string[] }; settings: { [key: string]: boolean | object | string | string[] };
/** @deprecated use activitiesCount instead */
transactionCount: number; transactionCount: number;
userCount: number; userCount: number;
version: string; version: string;
} }

3
libs/common/src/lib/models/timeline-position.ts

@ -35,9 +35,6 @@ export class TimelinePosition {
@Type(() => Big) @Type(() => Big)
feeInBaseCurrency: Big; feeInBaseCurrency: Big;
/** @deprecated use dateOfFirstActivity instead */
firstBuyDate: string;
@Transform(transformToBig, { toClassOnly: true }) @Transform(transformToBig, { toClassOnly: true })
@Type(() => Big) @Type(() => Big)
grossPerformance: Big; grossPerformance: Big;

4
libs/ui/src/lib/accounts-table/accounts-table.component.html

@ -115,7 +115,7 @@
></td> ></td>
</ng-container> </ng-container>
<ng-container matColumnDef="transactions"> <ng-container matColumnDef="activitiesCount">
<th <th
*matHeaderCellDef *matHeaderCellDef
class="justify-content-end px-1" class="justify-content-end px-1"
@ -129,7 +129,7 @@
{{ element.transactionCount }} {{ element.transactionCount }}
</td> </td>
<td *matFooterCellDef class="px-1 text-right" mat-footer-cell> <td *matFooterCellDef class="px-1 text-right" mat-footer-cell>
{{ transactionCount }} {{ activitiesCount }}
</td> </td>
</ng-container> </ng-container>

14
libs/ui/src/lib/accounts-table/accounts-table.component.stories.ts

@ -115,10 +115,10 @@ export const Loading: Story = {
hasPermissionToOpenDetails: false, hasPermissionToOpenDetails: false,
locale: 'en-US', locale: 'en-US',
showActions: false, showActions: false,
showActivitiesCount: true,
showAllocationInPercentage: false, showAllocationInPercentage: false,
showBalance: true, showBalance: true,
showFooter: true, showFooter: true,
showTransactions: true,
showValue: true, showValue: true,
showValueInBaseCurrency: true showValueInBaseCurrency: true
} }
@ -127,39 +127,39 @@ export const Loading: Story = {
export const Default: Story = { export const Default: Story = {
args: { args: {
accounts, accounts,
activitiesCount: 12,
baseCurrency: 'USD', baseCurrency: 'USD',
deviceType: 'desktop', deviceType: 'desktop',
hasPermissionToOpenDetails: false, hasPermissionToOpenDetails: false,
locale: 'en-US', locale: 'en-US',
showActions: false, showActions: false,
showActivitiesCount: true,
showAllocationInPercentage: false, showAllocationInPercentage: false,
showBalance: true, showBalance: true,
showFooter: true, showFooter: true,
showTransactions: true,
showValue: true, showValue: true,
showValueInBaseCurrency: true, showValueInBaseCurrency: true,
totalBalanceInBaseCurrency: 12428.2, totalBalanceInBaseCurrency: 12428.2,
totalValueInBaseCurrency: 107971.70321466809, totalValueInBaseCurrency: 107971.70321466809
transactionCount: 12
} }
}; };
export const WithoutFooter: Story = { export const WithoutFooter: Story = {
args: { args: {
accounts, accounts,
activitiesCount: 12,
baseCurrency: 'USD', baseCurrency: 'USD',
deviceType: 'desktop', deviceType: 'desktop',
hasPermissionToOpenDetails: false, hasPermissionToOpenDetails: false,
locale: 'en-US', locale: 'en-US',
showActions: false, showActions: false,
showActivitiesCount: true,
showAllocationInPercentage: false, showAllocationInPercentage: false,
showBalance: true, showBalance: true,
showFooter: false, showFooter: false,
showTransactions: true,
showValue: true, showValue: true,
showValueInBaseCurrency: true, showValueInBaseCurrency: true,
totalBalanceInBaseCurrency: 12428.2, totalBalanceInBaseCurrency: 12428.2,
totalValueInBaseCurrency: 107971.70321466809, totalValueInBaseCurrency: 107971.70321466809
transactionCount: 12
} }
}; };

8
libs/ui/src/lib/accounts-table/accounts-table.component.ts

@ -55,20 +55,20 @@ import { Subject, Subscription } from 'rxjs';
}) })
export class GfAccountsTableComponent implements OnChanges, OnDestroy { export class GfAccountsTableComponent implements OnChanges, OnDestroy {
@Input() accounts: Account[]; @Input() accounts: Account[];
@Input() activitiesCount: number;
@Input() baseCurrency: string; @Input() baseCurrency: string;
@Input() deviceType: string; @Input() deviceType: string;
@Input() hasPermissionToOpenDetails = true; @Input() hasPermissionToOpenDetails = true;
@Input() locale = getLocale(); @Input() locale = getLocale();
@Input() showActions: boolean; @Input() showActions: boolean;
@Input() showActivitiesCount = true;
@Input() showAllocationInPercentage: boolean; @Input() showAllocationInPercentage: boolean;
@Input() showBalance = true; @Input() showBalance = true;
@Input() showFooter = true; @Input() showFooter = true;
@Input() showTransactions = true;
@Input() showValue = true; @Input() showValue = true;
@Input() showValueInBaseCurrency = true; @Input() showValueInBaseCurrency = true;
@Input() totalBalanceInBaseCurrency: number; @Input() totalBalanceInBaseCurrency: number;
@Input() totalValueInBaseCurrency: number; @Input() totalValueInBaseCurrency: number;
@Input() transactionCount: number;
@Output() accountDeleted = new EventEmitter<string>(); @Output() accountDeleted = new EventEmitter<string>();
@Output() accountToUpdate = new EventEmitter<Account>(); @Output() accountToUpdate = new EventEmitter<Account>();
@ -101,8 +101,8 @@ export class GfAccountsTableComponent implements OnChanges, OnDestroy {
public ngOnChanges() { public ngOnChanges() {
this.displayedColumns = ['status', 'account', 'platform']; this.displayedColumns = ['status', 'account', 'platform'];
if (this.showTransactions) { if (this.showActivitiesCount) {
this.displayedColumns.push('transactions'); this.displayedColumns.push('activitiesCount');
} }
if (this.showBalance) { if (this.showBalance) {

52
package-lock.json

@ -1,12 +1,12 @@
{ {
"name": "ghostfolio", "name": "ghostfolio",
"version": "2.233.0", "version": "2.234.0",
"lockfileVersion": 3, "lockfileVersion": 3,
"requires": true, "requires": true,
"packages": { "packages": {
"": { "": {
"name": "ghostfolio", "name": "ghostfolio",
"version": "2.233.0", "version": "2.234.0",
"hasInstallScript": true, "hasInstallScript": true,
"license": "AGPL-3.0", "license": "AGPL-3.0",
"dependencies": { "dependencies": {
@ -62,13 +62,14 @@
"dotenv": "17.2.3", "dotenv": "17.2.3",
"dotenv-expand": "12.0.3", "dotenv-expand": "12.0.3",
"envalid": "8.1.1", "envalid": "8.1.1",
"fast-redact": "3.5.0",
"fuse.js": "7.1.0", "fuse.js": "7.1.0",
"google-spreadsheet": "3.2.0", "google-spreadsheet": "3.2.0",
"helmet": "7.0.0", "helmet": "7.0.0",
"http-status-codes": "2.3.0", "http-status-codes": "2.3.0",
"ionicons": "8.0.13", "ionicons": "8.0.13",
"jsonpath": "1.1.1", "jsonpath": "1.1.1",
"lodash": "4.17.21", "lodash": "4.17.23",
"marked": "17.0.1", "marked": "17.0.1",
"ms": "3.0.0-canary.1", "ms": "3.0.0-canary.1",
"ng-extract-i18n-merge": "3.2.1", "ng-extract-i18n-merge": "3.2.1",
@ -88,7 +89,7 @@
"svgmap": "2.14.0", "svgmap": "2.14.0",
"tablemark": "4.1.0", "tablemark": "4.1.0",
"twitter-api-v2": "1.27.0", "twitter-api-v2": "1.27.0",
"yahoo-finance2": "3.11.2", "yahoo-finance2": "3.13.0",
"zone.js": "0.16.0" "zone.js": "0.16.0"
}, },
"devDependencies": { "devDependencies": {
@ -122,10 +123,11 @@
"@storybook/angular": "10.1.10", "@storybook/angular": "10.1.10",
"@trivago/prettier-plugin-sort-imports": "5.2.2", "@trivago/prettier-plugin-sort-imports": "5.2.2",
"@types/big.js": "6.2.2", "@types/big.js": "6.2.2",
"@types/fast-redact": "3.0.4",
"@types/google-spreadsheet": "3.1.5", "@types/google-spreadsheet": "3.1.5",
"@types/jest": "30.0.0", "@types/jest": "30.0.0",
"@types/jsonpath": "0.2.4", "@types/jsonpath": "0.2.4",
"@types/lodash": "4.17.21", "@types/lodash": "4.17.23",
"@types/node": "22.15.17", "@types/node": "22.15.17",
"@types/papaparse": "5.3.7", "@types/papaparse": "5.3.7",
"@types/passport-google-oauth20": "2.0.16", "@types/passport-google-oauth20": "2.0.16",
@ -7614,6 +7616,12 @@
"url": "https://dotenvx.com" "url": "https://dotenvx.com"
} }
}, },
"node_modules/@nestjs/config/node_modules/lodash": {
"version": "4.17.21",
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
"integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==",
"license": "MIT"
},
"node_modules/@nestjs/core": { "node_modules/@nestjs/core": {
"version": "11.1.8", "version": "11.1.8",
"resolved": "https://registry.npmjs.org/@nestjs/core/-/core-11.1.8.tgz", "resolved": "https://registry.npmjs.org/@nestjs/core/-/core-11.1.8.tgz",
@ -12930,6 +12938,13 @@
"@types/send": "*" "@types/send": "*"
} }
}, },
"node_modules/@types/fast-redact": {
"version": "3.0.4",
"resolved": "https://registry.npmjs.org/@types/fast-redact/-/fast-redact-3.0.4.tgz",
"integrity": "sha512-tgGJaXucrCH4Yx2l/AI6e/JQksZhKGIQsVwBMTh+nxUhQDv5tXScTs5DHTw+qSKDXnHL2dTAh1e2rd5pcFQyNQ==",
"dev": true,
"license": "MIT"
},
"node_modules/@types/geojson": { "node_modules/@types/geojson": {
"version": "7946.0.16", "version": "7946.0.16",
"resolved": "https://registry.npmjs.org/@types/geojson/-/geojson-7946.0.16.tgz", "resolved": "https://registry.npmjs.org/@types/geojson/-/geojson-7946.0.16.tgz",
@ -13076,9 +13091,9 @@
} }
}, },
"node_modules/@types/lodash": { "node_modules/@types/lodash": {
"version": "4.17.21", "version": "4.17.23",
"resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.17.21.tgz", "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.17.23.tgz",
"integrity": "sha512-FOvQ0YPD5NOfPgMzJihoT+Za5pdkDJWcbpuj1DjaKZIr/gxodQjY/uWEFlTNqW2ugXHUiL8lRQgw63dzKHZdeQ==", "integrity": "sha512-RDvF6wTulMPjrNdCoYRC8gNR880JNGT8uB+REUpC2Ns4pRqQJhGz90wh7rgdXDPpCczF3VGktDuFGVnz8zP7HA==",
"dev": true, "dev": true,
"license": "MIT" "license": "MIT"
}, },
@ -19934,6 +19949,15 @@
"integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==",
"license": "MIT" "license": "MIT"
}, },
"node_modules/fast-redact": {
"version": "3.5.0",
"resolved": "https://registry.npmjs.org/fast-redact/-/fast-redact-3.5.0.tgz",
"integrity": "sha512-dwsoQlS7h9hMeYUq1W++23NDcBLV4KqONnITDV9DjfS3q1SgDGVrBdvvTLUotWtPSD7asWDV9/CmsZPy8Hf70A==",
"license": "MIT",
"engines": {
"node": ">=6"
}
},
"node_modules/fast-safe-stringify": { "node_modules/fast-safe-stringify": {
"version": "2.1.1", "version": "2.1.1",
"resolved": "https://registry.npmjs.org/fast-safe-stringify/-/fast-safe-stringify-2.1.1.tgz", "resolved": "https://registry.npmjs.org/fast-safe-stringify/-/fast-safe-stringify-2.1.1.tgz",
@ -25070,9 +25094,9 @@
} }
}, },
"node_modules/lodash": { "node_modules/lodash": {
"version": "4.17.21", "version": "4.17.23",
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.23.tgz",
"integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", "integrity": "sha512-LgVTMpQtIopCi79SJeDiP0TfWi5CNEc/L/aRdTh3yIvmZXTnheWpKjSZhnvMl8iXbC1tFg9gdHHDMLoV7CnG+w==",
"license": "MIT" "license": "MIT"
}, },
"node_modules/lodash-es": { "node_modules/lodash-es": {
@ -35355,9 +35379,9 @@
} }
}, },
"node_modules/yahoo-finance2": { "node_modules/yahoo-finance2": {
"version": "3.11.2", "version": "3.13.0",
"resolved": "https://registry.npmjs.org/yahoo-finance2/-/yahoo-finance2-3.11.2.tgz", "resolved": "https://registry.npmjs.org/yahoo-finance2/-/yahoo-finance2-3.13.0.tgz",
"integrity": "sha512-SIvMXjrOktBRD8m+qXAGCK+vR1vwBKuMgCnvmbxv29+t6LTDu0vAUxNYfbigsMRTmBzS4F9TQwbYF90g3Om4HA==", "integrity": "sha512-czBj2q/MD68YEsB7aXNnGhJvWxYZn01O5r/i7VYiQV2m2sWwhca6tKgjwf/LT7zHHEVxhKNiGLB46glLnmq9Ag==",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@deno/shim-deno": "~0.18.0", "@deno/shim-deno": "~0.18.0",

10
package.json

@ -1,6 +1,6 @@
{ {
"name": "ghostfolio", "name": "ghostfolio",
"version": "2.233.0", "version": "2.234.0",
"homepage": "https://ghostfol.io", "homepage": "https://ghostfol.io",
"license": "AGPL-3.0", "license": "AGPL-3.0",
"repository": "https://github.com/ghostfolio/ghostfolio", "repository": "https://github.com/ghostfolio/ghostfolio",
@ -106,13 +106,14 @@
"dotenv": "17.2.3", "dotenv": "17.2.3",
"dotenv-expand": "12.0.3", "dotenv-expand": "12.0.3",
"envalid": "8.1.1", "envalid": "8.1.1",
"fast-redact": "3.5.0",
"fuse.js": "7.1.0", "fuse.js": "7.1.0",
"google-spreadsheet": "3.2.0", "google-spreadsheet": "3.2.0",
"helmet": "7.0.0", "helmet": "7.0.0",
"http-status-codes": "2.3.0", "http-status-codes": "2.3.0",
"ionicons": "8.0.13", "ionicons": "8.0.13",
"jsonpath": "1.1.1", "jsonpath": "1.1.1",
"lodash": "4.17.21", "lodash": "4.17.23",
"marked": "17.0.1", "marked": "17.0.1",
"ms": "3.0.0-canary.1", "ms": "3.0.0-canary.1",
"ng-extract-i18n-merge": "3.2.1", "ng-extract-i18n-merge": "3.2.1",
@ -132,7 +133,7 @@
"svgmap": "2.14.0", "svgmap": "2.14.0",
"tablemark": "4.1.0", "tablemark": "4.1.0",
"twitter-api-v2": "1.27.0", "twitter-api-v2": "1.27.0",
"yahoo-finance2": "3.11.2", "yahoo-finance2": "3.13.0",
"zone.js": "0.16.0" "zone.js": "0.16.0"
}, },
"devDependencies": { "devDependencies": {
@ -166,10 +167,11 @@
"@storybook/angular": "10.1.10", "@storybook/angular": "10.1.10",
"@trivago/prettier-plugin-sort-imports": "5.2.2", "@trivago/prettier-plugin-sort-imports": "5.2.2",
"@types/big.js": "6.2.2", "@types/big.js": "6.2.2",
"@types/fast-redact": "3.0.4",
"@types/google-spreadsheet": "3.1.5", "@types/google-spreadsheet": "3.1.5",
"@types/jest": "30.0.0", "@types/jest": "30.0.0",
"@types/jsonpath": "0.2.4", "@types/jsonpath": "0.2.4",
"@types/lodash": "4.17.21", "@types/lodash": "4.17.23",
"@types/node": "22.15.17", "@types/node": "22.15.17",
"@types/papaparse": "5.3.7", "@types/papaparse": "5.3.7",
"@types/passport-google-oauth20": "2.0.16", "@types/passport-google-oauth20": "2.0.16",

Loading…
Cancel
Save