Browse Source

Add quantity to accounts tab of holding detail dialog

pull/7595/head
Thomas Kaul 3 weeks ago
parent
commit
1ecd44a40e
  1. 62
      apps/api/src/app/portfolio/portfolio.service.spec.ts
  2. 47
      apps/api/src/app/portfolio/portfolio.service.ts

62
apps/api/src/app/portfolio/portfolio.service.spec.ts

@ -606,5 +606,67 @@ describe('PortfolioService', () => {
expect(accounts[account.id].quantity).toBeUndefined(); expect(accounts[account.id].quantity).toBeUndefined();
}); });
it('should only consider accounts of the current user if the activities are filtered by a single account', async () => {
const accountsSpy = jest.spyOn(accountService, 'accounts');
await getValueOfAccountsAndPlatforms({
activities: [],
filters: [{ id: account.id, type: 'ACCOUNT' }],
portfolioItemsNow: {},
userCurrency: 'USD',
userId: userDummyData.id
});
expect(accountsSpy).toHaveBeenCalledWith(
expect.objectContaining({
where: { userId: userDummyData.id, id: account.id }
})
);
});
it('should exclude the cash balance if the activities are filtered by a single holding', async () => {
const { accounts, platforms } = await getValueOfAccountsAndPlatforms({
activities: [
{
account,
accountId: account.id,
assetProfile: { symbol: 'AAPL' },
quantity: 1,
type: 'BUY'
}
],
filters: [{ id: 'AAPL', type: 'SYMBOL' }],
portfolioItemsNow: {
AAPL: { marketPriceInBaseCurrency: 10 }
},
userCurrency: 'USD',
userId: userDummyData.id
});
// 1 * 10 (activity), without the balance of 100
expect(accounts[account.id].valueInBaseCurrency).toBe(10);
expect(platforms[account.platformId].valueInBaseCurrency).toBe(10);
});
it('should not accumulate rounding errors of the balances of accounts sharing a platform', async () => {
const platformId = randomUUID();
jest.spyOn(accountService, 'getAccounts').mockResolvedValue([
{ ...account, platformId, balance: 0.1, id: randomUUID() },
{ ...account, platformId, balance: 0.2, id: randomUUID() }
] as unknown as AccountWithBalance[]);
const { platforms } = await getValueOfAccountsAndPlatforms({
activities: [],
filters: [],
portfolioItemsNow: {},
userCurrency: 'USD',
userId: userDummyData.id
});
// 0.1 (balance) + 0.2 (balance)
expect(platforms[platformId].valueInBaseCurrency).toBe(0.3);
});
}); });
}); });

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

@ -2214,7 +2214,7 @@ export class PortfolioService {
} else if (filters.length === 1 && filters[0].type === 'ACCOUNT') { } else if (filters.length === 1 && filters[0].type === 'ACCOUNT') {
currentAccounts = await this.accountService.accounts({ currentAccounts = await this.accountService.accounts({
include: { platform: true, tags: true }, include: { platform: true, tags: true },
where: { id: filters[0].id } where: { userId, id: filters[0].id }
}); });
} else { } else {
const accountIds = Array.from( const accountIds = Array.from(
@ -2242,39 +2242,43 @@ export class PortfolioService {
// Iterate over the accounts plus a null entry to group activities without // Iterate over the accounts plus a null entry to group activities without
// an account into the unknown bucket // an account into the unknown bucket
for (const account of [...currentAccounts, null]) { for (const account of [...currentAccounts, null]) {
const currentAccountId = account?.id || UNKNOWN_KEY;
const currentPlatformId = account?.platformId || UNKNOWN_KEY;
const ordersByAccount = activities.filter(({ accountId }) => { const ordersByAccount = activities.filter(({ accountId }) => {
return account ? accountId === account.id : !accountId; return account ? accountId === account.id : !accountId;
}); });
if (account) { if (account) {
accounts[account.id] = { // The cash balance is not part of a holding and would distort the value
// and thus the allocation per account and platform
const balanceInBaseCurrency = filterBySymbol
? 0
: this.exchangeRateDataService.toCurrency(
account.balance,
account.currency,
userCurrency
);
accounts[currentAccountId] = {
balance: account.balance, balance: account.balance,
currency: account.currency, currency: account.currency,
name: account.name, name: account.name,
valueInBaseCurrency: this.exchangeRateDataService.toCurrency( valueInBaseCurrency: balanceInBaseCurrency
account.balance,
account.currency,
userCurrency
)
}; };
if (platforms[account.platformId || UNKNOWN_KEY]?.valueInBaseCurrency) { if (platforms[currentPlatformId]) {
platforms[account.platformId || UNKNOWN_KEY].valueInBaseCurrency += platforms[currentPlatformId].valueInBaseCurrency = new Big(
this.exchangeRateDataService.toCurrency( platforms[currentPlatformId].valueInBaseCurrency
account.balance, )
account.currency, .plus(balanceInBaseCurrency)
userCurrency .toNumber();
);
} else { } else {
platforms[account.platformId || UNKNOWN_KEY] = { platforms[currentPlatformId] = {
balance: account.balance, balance: account.balance,
currency: account.currency, currency: account.currency,
name: account.platform?.name, name: account.platform?.name,
valueInBaseCurrency: this.exchangeRateDataService.toCurrency( valueInBaseCurrency: balanceInBaseCurrency
account.balance,
account.currency,
userCurrency
)
}; };
} }
} }
@ -2299,9 +2303,6 @@ export class PortfolioService {
); );
} }
const currentAccountId = account?.id || UNKNOWN_KEY;
const currentPlatformId = account?.platformId || UNKNOWN_KEY;
// The quantity is only meaningful if the activities are filtered by a // The quantity is only meaningful if the activities are filtered by a
// single holding // single holding
const quantityOfHolding = filterBySymbol const quantityOfHolding = filterBySymbol

Loading…
Cancel
Save