diff --git a/CHANGELOG.md b/CHANGELOG.md index 76609858a..c15972cc2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,12 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## Unreleased + +### Changed + +- Extended the accounts endpoint by dividend and interest + ## 2.190.0 - 2025-08-09 ### Changed diff --git a/apps/api/src/app/account/account.service.ts b/apps/api/src/app/account/account.service.ts index 1c8adbd16..398a89bb9 100644 --- a/apps/api/src/app/account/account.service.ts +++ b/apps/api/src/app/account/account.service.ts @@ -12,7 +12,8 @@ import { AccountBalance, Order, Platform, - Prisma + Prisma, + SymbolProfile } from '@prisma/client'; import { Big } from 'big.js'; import { format } from 'date-fns'; @@ -62,7 +63,7 @@ export class AccountService { orderBy?: Prisma.AccountOrderByWithRelationInput; }): Promise< (Account & { - activities?: Order[]; + activities?: (Order & { SymbolProfile?: SymbolProfile })[]; balances?: AccountBalance[]; platform?: Platform; })[] diff --git a/apps/api/src/app/portfolio/portfolio.service.ts b/apps/api/src/app/portfolio/portfolio.service.ts index 784661e20..c15d06c1e 100644 --- a/apps/api/src/app/portfolio/portfolio.service.ts +++ b/apps/api/src/app/portfolio/portfolio.service.ts @@ -161,7 +161,7 @@ export class PortfolioService { this.accountService.accounts({ where, include: { - activities: true, + activities: { include: { SymbolProfile: true } }, platform: true }, orderBy: { name: 'asc' } @@ -176,39 +176,74 @@ export class PortfolioService { const userCurrency = this.request.user.settings.settings.baseCurrency; - return accounts.map((account) => { - let transactionCount = 0; + return Promise.all( + accounts.map(async (account) => { + let dividendInBaseCurrency = 0; + let interestInBaseCurrency = 0; + let transactionCount = 0; - for (const { isDraft } of account.activities) { - if (!isDraft) { - transactionCount += 1; + for (const { + currency, + date, + isDraft, + quantity, + SymbolProfile, + type, + unitPrice + } of account.activities) { + switch (type) { + case ActivityType.DIVIDEND: + dividendInBaseCurrency += + await this.exchangeRateDataService.toCurrencyAtDate( + new Big(quantity).mul(unitPrice).toNumber(), + currency ?? SymbolProfile.currency, + userCurrency, + date + ); + break; + case ActivityType.INTEREST: + interestInBaseCurrency += + await this.exchangeRateDataService.toCurrencyAtDate( + unitPrice, + currency ?? SymbolProfile.currency, + userCurrency, + date + ); + break; + } + + if (!isDraft) { + transactionCount += 1; + } } - } - const valueInBaseCurrency = - details.accounts[account.id]?.valueInBaseCurrency ?? 0; + const valueInBaseCurrency = + details.accounts[account.id]?.valueInBaseCurrency ?? 0; - const result = { - ...account, - transactionCount, - valueInBaseCurrency, - allocationInPercentage: null, // TODO - balanceInBaseCurrency: this.exchangeRateDataService.toCurrency( - account.balance, - account.currency, - userCurrency - ), - value: this.exchangeRateDataService.toCurrency( + const result = { + ...account, + dividendInBaseCurrency, + interestInBaseCurrency, + transactionCount, valueInBaseCurrency, - userCurrency, - account.currency - ) - }; + allocationInPercentage: null, // TODO + balanceInBaseCurrency: this.exchangeRateDataService.toCurrency( + account.balance, + account.currency, + userCurrency + ), + value: this.exchangeRateDataService.toCurrency( + valueInBaseCurrency, + userCurrency, + account.currency + ) + }; - delete result.activities; + delete result.activities; - return result; - }); + return result; + }) + ); } public async getAccountsWithAggregations({ @@ -242,6 +277,8 @@ export class PortfolioService { } let totalBalanceInBaseCurrency = new Big(0); + let totalDividendInBaseCurrency = new Big(0); + let totalInterestInBaseCurrency = new Big(0); let totalValueInBaseCurrency = new Big(0); let transactionCount = 0; @@ -249,6 +286,12 @@ export class PortfolioService { totalBalanceInBaseCurrency = totalBalanceInBaseCurrency.plus( account.balanceInBaseCurrency ); + totalDividendInBaseCurrency = totalDividendInBaseCurrency.plus( + account.dividendInBaseCurrency + ); + totalInterestInBaseCurrency = totalInterestInBaseCurrency.plus( + account.interestInBaseCurrency + ); totalValueInBaseCurrency = totalValueInBaseCurrency.plus( account.valueInBaseCurrency ); @@ -259,6 +302,8 @@ export class PortfolioService { accounts, transactionCount, totalBalanceInBaseCurrency: totalBalanceInBaseCurrency.toNumber(), + totalDividendInBaseCurrency: totalDividendInBaseCurrency.toNumber(), + totalInterestInBaseCurrency: totalInterestInBaseCurrency.toNumber(), totalValueInBaseCurrency: totalValueInBaseCurrency.toNumber() }; } diff --git a/apps/api/src/interceptors/redact-values-in-response/redact-values-in-response.interceptor.ts b/apps/api/src/interceptors/redact-values-in-response/redact-values-in-response.interceptor.ts index 83b66b370..73b18960a 100644 --- a/apps/api/src/interceptors/redact-values-in-response/redact-values-in-response.interceptor.ts +++ b/apps/api/src/interceptors/redact-values-in-response/redact-values-in-response.interceptor.ts @@ -50,12 +50,15 @@ export class RedactValuesInResponseInterceptor 'feeInBaseCurrency', 'grossPerformance', 'grossPerformanceWithCurrencyEffect', + 'interestInBaseCurrency', 'investment', 'netPerformance', 'netPerformanceWithCurrencyEffect', 'quantity', 'symbolMapping', 'totalBalanceInBaseCurrency', + 'totalDividendInBaseCurrency', + 'totalInterestInBaseCurrency', 'totalValueInBaseCurrency', 'unitPrice', 'value', diff --git a/libs/common/src/lib/interfaces/responses/accounts-response.interface.ts b/libs/common/src/lib/interfaces/responses/accounts-response.interface.ts index 5e03ea34a..0a6af978f 100644 --- a/libs/common/src/lib/interfaces/responses/accounts-response.interface.ts +++ b/libs/common/src/lib/interfaces/responses/accounts-response.interface.ts @@ -3,6 +3,8 @@ import { AccountWithValue } from '@ghostfolio/common/types'; export interface AccountsResponse { accounts: AccountWithValue[]; totalBalanceInBaseCurrency: number; + totalDividendInBaseCurrency: number; + totalInterestInBaseCurrency: number; totalValueInBaseCurrency: number; transactionCount: number; } diff --git a/libs/common/src/lib/types/account-with-value.type.ts b/libs/common/src/lib/types/account-with-value.type.ts index d86a7ca1f..08af86454 100644 --- a/libs/common/src/lib/types/account-with-value.type.ts +++ b/libs/common/src/lib/types/account-with-value.type.ts @@ -3,6 +3,8 @@ import { Account as AccountModel, Platform } from '@prisma/client'; export type AccountWithValue = AccountModel & { allocationInPercentage: number; balanceInBaseCurrency: number; + dividendInBaseCurrency: number; + interestInBaseCurrency: number; platform?: Platform; transactionCount: number; value: number;