refactor: extract upcoming event mapping
This commit is contained in:
@@ -0,0 +1,68 @@
|
|||||||
|
import {
|
||||||
|
decimalFromString,
|
||||||
|
formatDecimal,
|
||||||
|
multiply,
|
||||||
|
} from '../../../common/decimal';
|
||||||
|
import { ISO_DATE_LENGTH, ISO_DATE_START, MILLISECONDS_PER_SECOND } from '../../../common/time';
|
||||||
|
import type { MoexBondEvent } from '../../../integrations/moex/moex.api';
|
||||||
|
import { decimal } from '../../../integrations/tbank/decimal';
|
||||||
|
import type { TbankDividend } from '../../../integrations/tbank/tbank.api';
|
||||||
|
import { UpcomingEventDto } from './upcoming-events.dto';
|
||||||
|
|
||||||
|
export function dividendEvent(
|
||||||
|
metadata: Pick<
|
||||||
|
UpcomingEventDto,
|
||||||
|
'displayName' | 'figi' | 'instrumentUid' | 'isin' | 'logoName' | 'ticker'
|
||||||
|
>,
|
||||||
|
quantity: ReturnType<typeof decimal>,
|
||||||
|
event: TbankDividend,
|
||||||
|
): UpcomingEventDto {
|
||||||
|
return {
|
||||||
|
...metadata,
|
||||||
|
type: 'dividend',
|
||||||
|
date: new Date(Number(event.recordDate?.seconds) * MILLISECONDS_PER_SECOND)
|
||||||
|
.toISOString()
|
||||||
|
.slice(ISO_DATE_START, ISO_DATE_LENGTH),
|
||||||
|
dateKind: 'registry',
|
||||||
|
projectedAmount: event.dividendNet
|
||||||
|
? projectedMoney(
|
||||||
|
quantity,
|
||||||
|
formatDecimal(decimal(event.dividendNet)),
|
||||||
|
event.dividendNet.currency,
|
||||||
|
)
|
||||||
|
: null,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function bondEvent(
|
||||||
|
metadata: Pick<
|
||||||
|
UpcomingEventDto,
|
||||||
|
'displayName' | 'figi' | 'instrumentUid' | 'isin' | 'logoName' | 'ticker'
|
||||||
|
>,
|
||||||
|
quantity: ReturnType<typeof decimal>,
|
||||||
|
event: MoexBondEvent,
|
||||||
|
): UpcomingEventDto {
|
||||||
|
return {
|
||||||
|
...metadata,
|
||||||
|
type: event.type,
|
||||||
|
date: event.date,
|
||||||
|
dateKind: 'event',
|
||||||
|
projectedAmount:
|
||||||
|
'value' in event
|
||||||
|
? projectedMoney(quantity, event.value, event.currency)
|
||||||
|
: null,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function projectedMoney(
|
||||||
|
quantity: ReturnType<typeof decimal>,
|
||||||
|
value: string,
|
||||||
|
currency: string | undefined,
|
||||||
|
): UpcomingEventDto['projectedAmount'] {
|
||||||
|
const normalizedCurrency = currency?.trim().toUpperCase();
|
||||||
|
if (!normalizedCurrency) return null;
|
||||||
|
return {
|
||||||
|
currency: normalizedCurrency,
|
||||||
|
value: formatDecimal(multiply(quantity, decimalFromString(value))),
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
import { decimalFromString } from '../../../common/decimal';
|
import { decimalFromString } from '../../../common/decimal';
|
||||||
import { bondEvent, dividendEvent } from './upcoming-events.service';
|
import { bondEvent, dividendEvent } from './upcoming-event.mapper';
|
||||||
jest.mock('../../../prisma/prisma.service', () => ({
|
jest.mock('../../../prisma/prisma.service', () => ({
|
||||||
PrismaService: class {},
|
PrismaService: class {},
|
||||||
}));
|
}));
|
||||||
|
|||||||
@@ -1,22 +1,17 @@
|
|||||||
import { BadRequestException, Injectable } from '@nestjs/common';
|
import { BadRequestException, Injectable } from '@nestjs/common';
|
||||||
|
|
||||||
import type { MoexBondEvent } from '../../../integrations/moex/moex.api';
|
|
||||||
|
|
||||||
import {
|
import {
|
||||||
add,
|
add,
|
||||||
decimalFromString,
|
decimalFromString,
|
||||||
formatDecimal,
|
formatDecimal,
|
||||||
isZero,
|
isZero,
|
||||||
multiply,
|
|
||||||
} from '../../../common/decimal';
|
} from '../../../common/decimal';
|
||||||
import { MoneyDto } from '../../../common/money.dto';
|
import { MoneyDto } from '../../../common/money.dto';
|
||||||
import { ISO_DATE_LENGTH, ISO_DATE_START, MILLISECONDS_PER_SECOND } from '../../../common/time';
|
import { ISO_DATE_LENGTH, ISO_DATE_START } from '../../../common/time';
|
||||||
import { decimal } from '../../../integrations/tbank/decimal';
|
|
||||||
import {
|
import {
|
||||||
InstrumentMetadataService,
|
InstrumentMetadataService,
|
||||||
metadataKey,
|
metadataKey,
|
||||||
TbankAccessService,
|
TbankAccessService,
|
||||||
type TbankDividend,
|
|
||||||
} from '../../../integrations/tbank/tbank.api';
|
} from '../../../integrations/tbank/tbank.api';
|
||||||
import { CatalogEnrichmentService } from '../instruments/instruments.api';
|
import { CatalogEnrichmentService } from '../instruments/instruments.api';
|
||||||
import { investmentResourceMeta } from '../investment-resource.mapper';
|
import { investmentResourceMeta } from '../investment-resource.mapper';
|
||||||
@@ -30,6 +25,7 @@ import {
|
|||||||
UpcomingEventsDto,
|
UpcomingEventsDto,
|
||||||
UpcomingEventsQueryDto,
|
UpcomingEventsQueryDto,
|
||||||
} from './upcoming-events.dto';
|
} from './upcoming-events.dto';
|
||||||
|
import { bondEvent, dividendEvent } from './upcoming-event.mapper';
|
||||||
|
|
||||||
enum EventsErrorCode { InvalidPeriod = 'INVALID_EVENTS_PERIOD' }
|
enum EventsErrorCode { InvalidPeriod = 'INVALID_EVENTS_PERIOD' }
|
||||||
|
|
||||||
@@ -211,51 +207,6 @@ export class UpcomingEventsService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function dividendEvent(
|
|
||||||
metadata: Pick<
|
|
||||||
UpcomingEventDto,
|
|
||||||
'displayName' | 'figi' | 'instrumentUid' | 'isin' | 'logoName' | 'ticker'
|
|
||||||
>,
|
|
||||||
quantity: ReturnType<typeof decimal>,
|
|
||||||
event: TbankDividend,
|
|
||||||
): UpcomingEventDto {
|
|
||||||
return {
|
|
||||||
...metadata,
|
|
||||||
type: 'dividend',
|
|
||||||
date: new Date(Number(event.recordDate?.seconds) * MILLISECONDS_PER_SECOND)
|
|
||||||
.toISOString()
|
|
||||||
.slice(ISO_DATE_START, ISO_DATE_LENGTH),
|
|
||||||
dateKind: 'registry',
|
|
||||||
projectedAmount: event.dividendNet
|
|
||||||
? projectedMoney(
|
|
||||||
quantity,
|
|
||||||
formatDecimal(decimal(event.dividendNet)),
|
|
||||||
event.dividendNet.currency,
|
|
||||||
)
|
|
||||||
: null,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export function bondEvent(
|
|
||||||
metadata: Pick<
|
|
||||||
UpcomingEventDto,
|
|
||||||
'displayName' | 'figi' | 'instrumentUid' | 'isin' | 'logoName' | 'ticker'
|
|
||||||
>,
|
|
||||||
quantity: ReturnType<typeof decimal>,
|
|
||||||
event: MoexBondEvent,
|
|
||||||
): UpcomingEventDto {
|
|
||||||
return {
|
|
||||||
...metadata,
|
|
||||||
type: event.type,
|
|
||||||
date: event.date,
|
|
||||||
dateKind: 'event',
|
|
||||||
projectedAmount:
|
|
||||||
'value' in event
|
|
||||||
? projectedMoney(quantity, event.value, event.currency)
|
|
||||||
: null,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
function supported(position: Position): boolean {
|
function supported(position: Position): boolean {
|
||||||
return (
|
return (
|
||||||
!isZero(decimalFromString(position.quantity)) &&
|
!isZero(decimalFromString(position.quantity)) &&
|
||||||
@@ -274,19 +225,6 @@ function dateWindow(query: UpcomingEventsQueryDto) {
|
|||||||
return { from, to };
|
return { from, to };
|
||||||
}
|
}
|
||||||
|
|
||||||
function projectedMoney(
|
|
||||||
quantity: ReturnType<typeof decimal>,
|
|
||||||
value: string,
|
|
||||||
currency: string | undefined,
|
|
||||||
): MoneyDto | null {
|
|
||||||
const normalizedCurrency = currency?.trim().toUpperCase();
|
|
||||||
if (!normalizedCurrency) return null;
|
|
||||||
return {
|
|
||||||
currency: normalizedCurrency,
|
|
||||||
value: formatDecimal(multiply(quantity, decimalFromString(value))),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
function incomeTotals(events: UpcomingEventDto[]): MoneyDto[] {
|
function incomeTotals(events: UpcomingEventDto[]): MoneyDto[] {
|
||||||
const totals = new Map<string, ReturnType<typeof decimal>>();
|
const totals = new Map<string, ReturnType<typeof decimal>>();
|
||||||
for (const event of events) {
|
for (const event of events) {
|
||||||
|
|||||||
Reference in New Issue
Block a user