diff --git a/src/tbank/upcoming-events.dto.ts b/src/tbank/upcoming-events.dto.ts index 5360561..c1ec593 100644 --- a/src/tbank/upcoming-events.dto.ts +++ b/src/tbank/upcoming-events.dto.ts @@ -23,6 +23,12 @@ export class UpcomingEventDto { @ApiProperty() displayName: string; + @ApiProperty({ type: String, nullable: true }) + isin: string | null; + + @ApiProperty({ type: String, nullable: true }) + logoName: string | null; + @ApiProperty({ type: MoneyDto, nullable: true }) projectedAmount: MoneyDto | null; } diff --git a/src/tbank/upcoming-events.service.spec.ts b/src/tbank/upcoming-events.service.spec.ts index bb7c37f..773b384 100644 --- a/src/tbank/upcoming-events.service.spec.ts +++ b/src/tbank/upcoming-events.service.spec.ts @@ -62,6 +62,11 @@ describe('UpcomingEventsService', () => { ) .toISOString() .slice(0, 10); + tbank.getInstrumentByFigi.mockResolvedValue({ + name: 'Бумага', + isin: ' RU000A0JX0J2 ', + brand: { logoName: ' instrument-logo.png ' }, + }); tbank.getPortfolio.mockResolvedValue({ positions: [ { @@ -99,17 +104,52 @@ describe('UpcomingEventsService', () => { type: 'coupon', projectedAmount: { currency: 'RUB', value: '7.5' }, dateKind: 'event', + isin: 'RU000A0JX0J2', + logoName: 'instrument-logo.png', }, { type: 'dividend', projectedAmount: { currency: 'RUB', value: '2.5' }, dateKind: 'registry', + isin: 'RU000A0JX0J2', + logoName: 'instrument-logo.png', + }, + { + type: 'offer', + projectedAmount: null, + isin: 'RU000A0JX0J2', + logoName: 'instrument-logo.png', }, - { type: 'offer', projectedAmount: null }, ], }); }); + it.each([undefined, ' '])( + 'keeps events without optional metadata when it is %s', + async (metadata) => { + const today = new Date().toISOString().slice(0, 10); + tbank.getPortfolio.mockResolvedValue({ + positions: [ + { + instrumentUid: 'bond-uid', + ticker: 'BOND', + instrumentType: 'bond', + quantity: { units: 1 }, + }, + ], + }); + tbank.getInstrumentByUid.mockResolvedValue({ + name: 'Bond', + isin: metadata, + brand: { logoName: metadata }, + }); + moex.bondEvents.mockResolvedValue([{ type: 'offer', date: today }]); + await expect(service.get('user', 'account')).resolves.toMatchObject({ + events: [{ type: 'offer', isin: null, logoName: null }], + }); + }, + ); + it('does not read upstream data for inaccessible accounts', async () => { access.withAccount.mockRejectedValueOnce(new NotFoundException()); await expect(service.get('user', 'account')).rejects.toEqual( @@ -145,7 +185,12 @@ describe('UpcomingEventsService', () => { await expect(service.get('user', 'account')).resolves.toMatchObject({ events: [ - { ticker: 'SBER', projectedAmount: { currency: 'RUB', value: '1' } }, + { + ticker: 'SBER', + projectedAmount: { currency: 'RUB', value: '1' }, + isin: null, + logoName: null, + }, ], }); expect(tbank.getInstrumentByFigi).toHaveBeenCalledWith('token', 'S'); diff --git a/src/tbank/upcoming-events.service.ts b/src/tbank/upcoming-events.service.ts index 229a112..ab27441 100644 --- a/src/tbank/upcoming-events.service.ts +++ b/src/tbank/upcoming-events.service.ts @@ -108,6 +108,8 @@ export class UpcomingEventsService { instrumentUid: position.instrumentUid?.trim() || null, ticker: resolvedTicker, displayName: instrument?.name?.trim() || resolvedTicker, + isin: instrument?.isin?.trim() || null, + logoName: instrument?.brand?.logoName?.trim() || null, }; const quantity = decimal(position.quantity ?? {}); if (position.instrumentType?.toLowerCase() === 'share') { @@ -181,7 +183,7 @@ export class UpcomingEventsService { private dividend( metadata: Pick< UpcomingEventDto, - 'figi' | 'instrumentUid' | 'ticker' | 'displayName' + 'figi' | 'instrumentUid' | 'ticker' | 'displayName' | 'isin' | 'logoName' >, quantity: ReturnType, event: TbankDividend, @@ -206,7 +208,7 @@ export class UpcomingEventsService { private bond( metadata: Pick< UpcomingEventDto, - 'figi' | 'instrumentUid' | 'ticker' | 'displayName' + 'figi' | 'instrumentUid' | 'ticker' | 'displayName' | 'isin' | 'logoName' >, quantity: ReturnType, event: MoexBondEvent,