feat: expose logos and ISINs for upcoming events

This commit is contained in:
2026-09-09 17:57:54 +03:00
parent a83e26e5ef
commit 33f703d9a3
3 changed files with 57 additions and 4 deletions
+6
View File
@@ -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;
}
+47 -2
View File
@@ -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');
+4 -2
View File
@@ -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<typeof decimal>,
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<typeof decimal>,
event: MoexBondEvent,