refactor!: support RUB only (WealthLedger/main#55) (#204)
Backend verify / commits (push) Skipped
Backend verify / security (push) Successful in 1m32s
Backend verify / verify (push) Failing after 9m25s

BREAKING CHANGE: per-currency API fields replaced with single RUB Money.
This commit was merged in pull request #204.
This commit is contained in:
2026-11-11 10:07:11 +03:00
parent 85d65f8997
commit 809936e62d
61 changed files with 617 additions and 735 deletions
@@ -145,19 +145,15 @@ describe('persistent portfolio projection', () => {
]),
).toEqual([
['Known', 'STOCK', '2'],
['B', 'OTHER', '-0.5'],
]);
expect(snapshot.positions[0].absoluteReturn).toEqual({
currency: 'RUB',
value: '2.000000002',
});
expect(snapshot.totalsByCurrency).toEqual([
{ currency: 'RUB', value: '7.000000002' },
{ currency: 'USD', value: '-2' },
]);
expect(snapshot.cashBalances).toEqual([{ currency: 'RUB', value: '1' }]);
expect(snapshot.totals).toEqual({ currency: 'RUB', value: '7.000000002' });
expect(snapshot.cash).toEqual({ currency: 'RUB', value: '1' });
});
it('keeps net-zero cash groups, missing valuations and incompatible currencies distinct', async () => {
it('filters non-RUB cash and positions out of the RUB-only snapshot', async () => {
repository.reserve.mockResolvedValueOnce({
launch: true,
work: { id: 'work', attemptId: 'attempt' },
@@ -180,17 +176,10 @@ describe('persistent portfolio projection', () => {
assert(handle);
await handle.done;
const snapshot = (repository.publish.mock.calls as [unknown, string, PortfolioDto][])[0][2];
expect(snapshot.cashBalances).toEqual([
{ currency: 'RUB', value: '0' },
{ currency: 'EUR', value: '1' },
]);
expect(snapshot.totalsByCurrency).toEqual([
{ currency: 'EUR', value: '1' },
{ currency: 'USD', value: '3' },
]);
expect(snapshot.cash).toEqual({ currency: 'RUB', value: '0' });
expect(snapshot.totals).toEqual({ currency: 'RUB', value: '0' });
expect(snapshot.positions[0].currentValue).toBeNull();
expect(snapshot.positions[1].absoluteReturn).toBeNull();
expect(snapshot.positions[1].averagePurchasePrice).toEqual({ currency: 'RUB', value: '2' });
expect(snapshot.positions).toHaveLength(1);
});
it('counts cash once when T-Bank also returns it as a CURRENCY position', async () => {
@@ -213,9 +202,9 @@ describe('persistent portfolio projection', () => {
assert(handle);
await handle.done;
const snapshot = (repository.publish.mock.calls as [unknown, string, PortfolioDto][])[0][2];
expect(snapshot.totalsByCurrency).toEqual([{ currency: 'RUB', value: '121.49' }]);
expect(snapshot.positions.map((position) => position.type)).toEqual(['FUND', 'CURRENCY']);
expect(snapshot.structure.currencies[0].totalValue).toBe('121.49');
expect(snapshot.totals).toEqual({ currency: 'RUB', value: '121.49' });
expect(snapshot.positions.map((position) => position.type)).toEqual(['FUND']);
expect(snapshot.structure.totalValue).toBe('121.49');
});
// Test intent: a corrupted Json payload must fail loudly instead of being spread into the API response.
@@ -235,9 +224,9 @@ describe('persistent portfolio projection', () => {
lastSuccessfulAt: membershipAt.toISOString(),
isStale: false,
positions: [],
cashBalances: [],
structure: { currencies: [], unvaluedCategories: [] },
totalsByCurrency: [{ currency: 'RUB', value: '1.000000001' }],
cash: { currency: 'RUB', value: '0' },
structure: { totalValue: '1.000000001', hasNegativeValues: false, categories: [], unvaluedCategories: [] },
totals: { currency: 'RUB', value: '1.000000001' },
};
const account = {
accountId: 'a',
@@ -261,8 +250,8 @@ describe('persistent portfolio projection', () => {
expect(partial.meta.availability).toBe('temporarily-unavailable');
expect(partial.meta.completeness).toBe('incomplete');
const saved = await service.saved('user', 'all');
expect(saved.accounts[0].snapshot?.totalsByCurrency).toEqual(
snapshot.totalsByCurrency,
expect(saved.accounts[0].snapshot?.totals).toEqual(
snapshot.totals,
);
expect(saved.accounts[1].snapshot).toBeNull();
repository.read.mockResolvedValue({
@@ -279,9 +268,9 @@ describe('persistent portfolio projection', () => {
work: [],
});
const complete = await service.read('user', 'all');
expect(complete.data?.totalsByCurrency).toEqual([
{ currency: 'RUB', value: '2.000000002' },
]);
expect(complete.data?.totals).toEqual({
currency: 'RUB', value: '2.000000002',
});
expect(complete.data?.lastSuccessfulAt).toBe(new Date(0).toISOString());
expect(complete.data?.isStale).toBe(true);
expect(tbank.getPortfolio).not.toHaveBeenCalled();