From 7fcbc2e755326a895fc49005e2765c591abbea9c Mon Sep 17 00:00:00 2001 From: Thomas Kaul <4159106+dtslvr@users.noreply.github.com> Date: Sun, 26 Jul 2026 21:34:42 +0200 Subject: [PATCH] Ignore future-dated account balances in the portfolio calculation --- .../account-balance.service.ts | 7 +++++- apps/api/src/app/account/account.service.ts | 25 ++++++++++++++++--- .../src/app/activities/activities.service.ts | 5 ++++ .../roai/portfolio-calculator-cash.spec.ts | 8 ++++++ 4 files changed, 40 insertions(+), 5 deletions(-) diff --git a/apps/api/src/app/account-balance/account-balance.service.ts b/apps/api/src/app/account-balance/account-balance.service.ts index 84932f4295..51d0c263b5 100644 --- a/apps/api/src/app/account-balance/account-balance.service.ts +++ b/apps/api/src/app/account-balance/account-balance.service.ts @@ -15,7 +15,7 @@ import { Injectable } from '@nestjs/common'; import { EventEmitter2 } from '@nestjs/event-emitter'; import { AccountBalance, Prisma } from '@prisma/client'; import { Big } from 'big.js'; -import { format, parseISO } from 'date-fns'; +import { endOfToday, format, isAfter, parseISO } from 'date-fns'; import { groupBy } from 'lodash'; @Injectable() @@ -116,6 +116,11 @@ export class AccountBalanceService { const lastBalancesByAccount: { [accountId: string]: Big } = {}; for (const { accountId, date, valueInBaseCurrency } of balances) { + if (isAfter(date, endOfToday())) { + // Skip account balances in the future + continue; + } + const formattedDate = format(date, DATE_FORMAT); lastBalancesByAccount[accountId] = new Big(valueInBaseCurrency); diff --git a/apps/api/src/app/account/account.service.ts b/apps/api/src/app/account/account.service.ts index 2098062340..7aafe7d332 100644 --- a/apps/api/src/app/account/account.service.ts +++ b/apps/api/src/app/account/account.service.ts @@ -19,7 +19,7 @@ import { Tag } from '@prisma/client'; import { Big } from 'big.js'; -import { format } from 'date-fns'; +import { endOfToday, format, isAfter } from 'date-fns'; import { groupBy } from 'lodash'; import { CashDetails } from './interfaces/cash-details.interface'; @@ -41,7 +41,11 @@ export class AccountService { include: { balances: { orderBy: { date: 'desc' }, - take: 1 + take: 1, + where: { + // Ignore account balances in the future + date: { lte: endOfToday() } + } } }, where: { id_userId } @@ -95,7 +99,15 @@ export class AccountService { include.balances = { orderBy: { date: 'desc' }, - ...(isBalancesIncluded ? {} : { take: 1 }) + ...(isBalancesIncluded + ? {} + : { + take: 1, + where: { + // Ignore account balances in the future + date: { lte: endOfToday() } + } + }) }; if (isTagsIncluded) { @@ -118,7 +130,12 @@ export class AccountService { return accounts.map((account) => { const result = { ...account, - balance: account.balances[0]?.value ?? 0, + balance: + // The balances are ordered by date descending, hence the first account + // balance which is not in the future reflects the current balance + account.balances.find(({ date }) => { + return !isAfter(date, endOfToday()); + })?.value ?? 0, tags: isTagsIncluded ? (account.tags as unknown as { tag: Tag }[]).map(({ tag }) => { return tag; diff --git a/apps/api/src/app/activities/activities.service.ts b/apps/api/src/app/activities/activities.service.ts index 0a390d4ec3..0e3d3f0c3b 100644 --- a/apps/api/src/app/activities/activities.service.ts +++ b/apps/api/src/app/activities/activities.service.ts @@ -468,6 +468,11 @@ export class ActivitiesService { let currentBalanceInBaseCurrency = 0; for (const balanceItem of balances) { + if (isAfter(balanceItem.date, endOfToday())) { + // Skip account balances in the future + continue; + } + const syntheticActivityTemplate: Activity = { userId, accountId: account.id, diff --git a/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-cash.spec.ts b/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-cash.spec.ts index 9bef6ad394..1fc705b467 100644 --- a/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-cash.spec.ts +++ b/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-cash.spec.ts @@ -166,6 +166,14 @@ describe('PortfolioCalculator', () => { id: randomUUID(), value: 2000, valueInBaseCurrency: 1800 + }, + { + // Ignored future account balance + accountId, + date: parseDate('2050-12-31'), + id: randomUUID(), + value: 0, + valueInBaseCurrency: 0 } ] });