import { formatDecimal } from '../../../../common/decimal'; import { MILLISECONDS_PER_SECOND } from '../../../../common/time'; import { decimal } from '../../../../integrations/tbank/decimal'; import { TbankOperationType } from '../../../../integrations/tbank/tbank-protocol'; import { TbankMoney, TbankOperationItem } from '../../../../integrations/tbank/tbank.api'; import { type OperationCategory } from '../../operation-category'; import { BrokerageOperationDto } from '../presentation/brokerage-operation.dto'; const MISSING_TIMESTAMP_COMPONENT = 0; const NANOSECONDS_PER_MILLISECOND = 1_000_000; const types: Record = { [TbankOperationType.Buy]: 'trade', [TbankOperationType.BuyCard]: 'trade', [TbankOperationType.Sell]: 'trade', [TbankOperationType.SellCard]: 'trade', [TbankOperationType.BuyMargin]: 'trade', [TbankOperationType.SellMargin]: 'trade', [TbankOperationType.Coupon]: 'income', [TbankOperationType.Dividend]: 'income', [TbankOperationType.DividendTransfer]: 'income', [TbankOperationType.DivExt]: 'income', [TbankOperationType.TransIisBs]: 'transfer', [TbankOperationType.TransBsBs]: 'transfer', [TbankOperationType.BondTax]: 'tax', [TbankOperationType.Tax]: 'tax', [TbankOperationType.DividendTax]: 'tax', [TbankOperationType.ServiceFee]: 'fee', [TbankOperationType.MarginFee]: 'fee', [TbankOperationType.BrokerFee]: 'fee', [TbankOperationType.SuccessFee]: 'fee', [TbankOperationType.Input]: 'transfer', [TbankOperationType.Output]: 'transfer', [TbankOperationType.InputSecurities]: 'transfer', [TbankOperationType.OutputSecurities]: 'transfer', }; export function operationCategory(type: string | undefined): OperationCategory { return types[type ?? ''] ?? 'other'; } export function mapBrokerageOperation( operation: TbankOperationItem, occurredAt = operationTimestamp(operation.date), ): BrokerageOperationDto { return { id: operation.id ?? operation.cursor ?? '', occurredAt: occurredAt.toISOString(), type: operation.type ?? TbankOperationType.Unspecified, category: operationCategory(operation.type), description: operation.description?.trim() || operation.name?.trim() || '', figi: text(operation.figi), instrumentUid: text(operation.instrumentUid), ticker: text(operation.ticker), instrumentType: text(operation.instrumentType), logoName: null, payment: money(operation.payment), quantity: (operationCategory(operation.type) === 'trade' ? operation.quantityDone : operation.quantity) === undefined ? null : String( operationCategory(operation.type) === 'trade' ? operation.quantityDone : operation.quantity, ), price: money(operation.price), accruedInterest: money(operation.accruedInt), commission: money(operation.commission), }; } function text(value: string | undefined): null | string { return value?.trim() || null; } function money(value: TbankMoney | undefined) { const currency = value?.currency?.trim().toUpperCase(); return currency && value ? { currency, value: formatDecimal(decimal(value)) } : null; } export function operationTimestamp( value: undefined | { nanos?: number; seconds?: number | string }, ) { return new Date( Number(value?.seconds ?? MISSING_TIMESTAMP_COMPONENT) * MILLISECONDS_PER_SECOND + Math.floor((value?.nanos ?? MISSING_TIMESTAMP_COMPONENT) / NANOSECONDS_PER_MILLISECOND), ); }