95 lines
3.4 KiB
TypeScript
95 lines
3.4 KiB
TypeScript
import { formatDecimal } from '#src/common/decimal';
|
|
import { MILLISECONDS_PER_SECOND } from '#src/common/time';
|
|
import { decimal } from '#src/integrations/tbank/decimal';
|
|
import { TbankOperationType } from '#src/integrations/tbank/tbank-protocol';
|
|
import { TbankMoney, TbankOperationItem } from '#src/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<string, OperationCategory> = {
|
|
[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),
|
|
);
|
|
}
|