Browse Source

Feature/extend accounts endpoint by dividend and interest (#5335)

* Extend accounts endpoint by dividend and interest

* Update changelog
pull/5345/head^2
Attila Cseh 4 days ago
committed by GitHub
parent
commit
711d716de0
No known key found for this signature in database GPG Key ID: B5690EEEBB952194
  1. 6
      CHANGELOG.md
  2. 5
      apps/api/src/app/account/account.service.ts
  3. 99
      apps/api/src/app/portfolio/portfolio.service.ts
  4. 3
      apps/api/src/interceptors/redact-values-in-response/redact-values-in-response.interceptor.ts
  5. 2
      libs/common/src/lib/interfaces/responses/accounts-response.interface.ts
  6. 2
      libs/common/src/lib/types/account-with-value.type.ts

6
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/), 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). 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 ## 2.190.0 - 2025-08-09
### Changed ### Changed

5
apps/api/src/app/account/account.service.ts

@ -12,7 +12,8 @@ import {
AccountBalance, AccountBalance,
Order, Order,
Platform, Platform,
Prisma Prisma,
SymbolProfile
} from '@prisma/client'; } from '@prisma/client';
import { Big } from 'big.js'; import { Big } from 'big.js';
import { format } from 'date-fns'; import { format } from 'date-fns';
@ -62,7 +63,7 @@ export class AccountService {
orderBy?: Prisma.AccountOrderByWithRelationInput; orderBy?: Prisma.AccountOrderByWithRelationInput;
}): Promise< }): Promise<
(Account & { (Account & {
activities?: Order[]; activities?: (Order & { SymbolProfile?: SymbolProfile })[];
balances?: AccountBalance[]; balances?: AccountBalance[];
platform?: Platform; platform?: Platform;
})[] })[]

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

@ -161,7 +161,7 @@ export class PortfolioService {
this.accountService.accounts({ this.accountService.accounts({
where, where,
include: { include: {
activities: true, activities: { include: { SymbolProfile: true } },
platform: true platform: true
}, },
orderBy: { name: 'asc' } orderBy: { name: 'asc' }
@ -176,39 +176,74 @@ export class PortfolioService {
const userCurrency = this.request.user.settings.settings.baseCurrency; const userCurrency = this.request.user.settings.settings.baseCurrency;
return accounts.map((account) => { return Promise.all(
let transactionCount = 0; accounts.map(async (account) => {
let dividendInBaseCurrency = 0;
let interestInBaseCurrency = 0;
let transactionCount = 0;
for (const { isDraft } of account.activities) { for (const {
if (!isDraft) { currency,
transactionCount += 1; 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 = const valueInBaseCurrency =
details.accounts[account.id]?.valueInBaseCurrency ?? 0; details.accounts[account.id]?.valueInBaseCurrency ?? 0;
const result = { const result = {
...account, ...account,
transactionCount, dividendInBaseCurrency,
valueInBaseCurrency, interestInBaseCurrency,
allocationInPercentage: null, // TODO transactionCount,
balanceInBaseCurrency: this.exchangeRateDataService.toCurrency(
account.balance,
account.currency,
userCurrency
),
value: this.exchangeRateDataService.toCurrency(
valueInBaseCurrency, valueInBaseCurrency,
userCurrency, allocationInPercentage: null, // TODO
account.currency 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({ public async getAccountsWithAggregations({
@ -242,6 +277,8 @@ export class PortfolioService {
} }
let totalBalanceInBaseCurrency = new Big(0); let totalBalanceInBaseCurrency = new Big(0);
let totalDividendInBaseCurrency = new Big(0);
let totalInterestInBaseCurrency = new Big(0);
let totalValueInBaseCurrency = new Big(0); let totalValueInBaseCurrency = new Big(0);
let transactionCount = 0; let transactionCount = 0;
@ -249,6 +286,12 @@ export class PortfolioService {
totalBalanceInBaseCurrency = totalBalanceInBaseCurrency.plus( totalBalanceInBaseCurrency = totalBalanceInBaseCurrency.plus(
account.balanceInBaseCurrency account.balanceInBaseCurrency
); );
totalDividendInBaseCurrency = totalDividendInBaseCurrency.plus(
account.dividendInBaseCurrency
);
totalInterestInBaseCurrency = totalInterestInBaseCurrency.plus(
account.interestInBaseCurrency
);
totalValueInBaseCurrency = totalValueInBaseCurrency.plus( totalValueInBaseCurrency = totalValueInBaseCurrency.plus(
account.valueInBaseCurrency account.valueInBaseCurrency
); );
@ -259,6 +302,8 @@ export class PortfolioService {
accounts, accounts,
transactionCount, transactionCount,
totalBalanceInBaseCurrency: totalBalanceInBaseCurrency.toNumber(), totalBalanceInBaseCurrency: totalBalanceInBaseCurrency.toNumber(),
totalDividendInBaseCurrency: totalDividendInBaseCurrency.toNumber(),
totalInterestInBaseCurrency: totalInterestInBaseCurrency.toNumber(),
totalValueInBaseCurrency: totalValueInBaseCurrency.toNumber() totalValueInBaseCurrency: totalValueInBaseCurrency.toNumber()
}; };
} }

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

@ -50,12 +50,15 @@ export class RedactValuesInResponseInterceptor<T>
'feeInBaseCurrency', 'feeInBaseCurrency',
'grossPerformance', 'grossPerformance',
'grossPerformanceWithCurrencyEffect', 'grossPerformanceWithCurrencyEffect',
'interestInBaseCurrency',
'investment', 'investment',
'netPerformance', 'netPerformance',
'netPerformanceWithCurrencyEffect', 'netPerformanceWithCurrencyEffect',
'quantity', 'quantity',
'symbolMapping', 'symbolMapping',
'totalBalanceInBaseCurrency', 'totalBalanceInBaseCurrency',
'totalDividendInBaseCurrency',
'totalInterestInBaseCurrency',
'totalValueInBaseCurrency', 'totalValueInBaseCurrency',
'unitPrice', 'unitPrice',
'value', 'value',

2
libs/common/src/lib/interfaces/responses/accounts-response.interface.ts

@ -3,6 +3,8 @@ import { AccountWithValue } from '@ghostfolio/common/types';
export interface AccountsResponse { export interface AccountsResponse {
accounts: AccountWithValue[]; accounts: AccountWithValue[];
totalBalanceInBaseCurrency: number; totalBalanceInBaseCurrency: number;
totalDividendInBaseCurrency: number;
totalInterestInBaseCurrency: number;
totalValueInBaseCurrency: number; totalValueInBaseCurrency: number;
transactionCount: number; transactionCount: number;
} }

2
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 & { export type AccountWithValue = AccountModel & {
allocationInPercentage: number; allocationInPercentage: number;
balanceInBaseCurrency: number; balanceInBaseCurrency: number;
dividendInBaseCurrency: number;
interestInBaseCurrency: number;
platform?: Platform; platform?: Platform;
transactionCount: number; transactionCount: number;
value: number; value: number;

Loading…
Cancel
Save