import type { BrokerOperation, BrokerOperationCategory, BrokerOperationsPage, } from '../types/broker.types'; import type { TBankOperationItem, TBankOperationsByCursorResponse, } from '../types/tbank-proto.types'; import { mapInteger, mapMoneyValue, mapTimestampToIso } from './money.mapper'; const TRADE_TYPES = new Set([ 'OPERATION_TYPE_BUY', 'OPERATION_TYPE_BUY_CARD', 'OPERATION_TYPE_SELL', 'OPERATION_TYPE_SELL_CARD', 'OPERATION_TYPE_BUY_MARGIN', 'OPERATION_TYPE_SELL_MARGIN', 'OPERATION_TYPE_DELIVERY_BUY', 'OPERATION_TYPE_DELIVERY_SELL', ]); const INCOME_TYPES = new Set([ 'OPERATION_TYPE_DIVIDEND', 'OPERATION_TYPE_COUPON', 'OPERATION_TYPE_BOND_REPAYMENT', 'OPERATION_TYPE_BOND_REPAYMENT_FULL', 'OPERATION_TYPE_OVERNIGHT', 'OPERATION_TYPE_OVER_INCOME', 'OPERATION_TYPE_ACCRUING_VARMARGIN', 'OPERATION_TYPE_TAX_REPO_REFUND', 'OPERATION_TYPE_TAX_REPO_REFUND_PROGRESSIVE', 'OPERATION_TYPE_DIV_EXT', 'OPERATION_TYPE_DFA_REDEMPTION', ]); const TAX_TYPES = new Set([ 'OPERATION_TYPE_TAX', 'OPERATION_TYPE_BOND_TAX', 'OPERATION_TYPE_DIVIDEND_TAX', 'OPERATION_TYPE_TAX_CORRECTION', 'OPERATION_TYPE_BENEFIT_TAX', 'OPERATION_TYPE_TAX_PROGRESSIVE', 'OPERATION_TYPE_BOND_TAX_PROGRESSIVE', 'OPERATION_TYPE_DIVIDEND_TAX_PROGRESSIVE', 'OPERATION_TYPE_BENEFIT_TAX_PROGRESSIVE', 'OPERATION_TYPE_TAX_CORRECTION_PROGRESSIVE', 'OPERATION_TYPE_TAX_REPO', 'OPERATION_TYPE_TAX_REPO_PROGRESSIVE', 'OPERATION_TYPE_TAX_REPO_HOLD', 'OPERATION_TYPE_TAX_REPO_HOLD_PROGRESSIVE', 'OPERATION_TYPE_TAX_CORRECTION_COUPON', ]); const FEE_TYPES = new Set([ 'OPERATION_TYPE_SERVICE_FEE', 'OPERATION_TYPE_MARGIN_FEE', 'OPERATION_TYPE_BROKER_FEE', 'OPERATION_TYPE_SUCCESS_FEE', 'OPERATION_TYPE_TRACK_MFEE', 'OPERATION_TYPE_TRACK_PFEE', 'OPERATION_TYPE_CASH_FEE', 'OPERATION_TYPE_OUT_FEE', 'OPERATION_TYPE_OUT_STAMP_DUTY', 'OPERATION_TYPE_OUTPUT_PENALTY', 'OPERATION_TYPE_ADVICE_FEE', 'OPERATION_TYPE_OVER_COM', 'OPERATION_TYPE_OTHER_FEE', 'OPERATION_TYPE_FUNDING', ]); const TRANSFER_TYPES = new Set([ 'OPERATION_TYPE_INPUT', 'OPERATION_TYPE_OUTPUT', 'OPERATION_TYPE_INPUT_SECURITIES', 'OPERATION_TYPE_OUTPUT_SECURITIES', 'OPERATION_TYPE_OUTPUT_SWIFT', 'OPERATION_TYPE_INPUT_SWIFT', 'OPERATION_TYPE_OUTPUT_ACQUIRING', 'OPERATION_TYPE_INPUT_ACQUIRING', 'OPERATION_TYPE_TRANS_IIS_BS', 'OPERATION_TYPE_TRANS_BS_BS', 'OPERATION_TYPE_OUT_MULTI', 'OPERATION_TYPE_INP_MULTI', 'OPERATION_TYPE_OVER_PLACEMENT', ]); export function categorizeOperationType(type: string | null | undefined): BrokerOperationCategory { if (!type) return 'other'; if (TRADE_TYPES.has(type)) return 'trade'; if (INCOME_TYPES.has(type)) return 'income'; if (TAX_TYPES.has(type)) return 'tax'; if (FEE_TYPES.has(type)) return 'fee'; if (TRANSFER_TYPES.has(type)) return 'transfer'; return 'other'; } export function mapOperation(item: TBankOperationItem, accountId: string): BrokerOperation { const type = item.type || 'OPERATION_TYPE_UNSPECIFIED'; return { cursor: item.cursor ?? null, accountId: item.brokerAccountId || accountId, id: item.id ?? null, parentOperationId: item.parentOperationId ?? null, date: mapTimestampToIso(item.date), type, category: categorizeOperationType(type), description: item.description || item.name || null, name: item.name ?? null, state: item.state ?? null, instrumentUid: item.instrumentUid ?? null, figi: item.figi ?? null, ticker: item.ticker ?? null, classCode: item.classCode ?? null, instrumentType: item.instrumentType ?? null, payment: mapMoneyValue(item.payment), price: mapMoneyValue(item.price), commission: mapMoneyValue(item.commission), yield: mapMoneyValue(item.yield), accruedInt: mapMoneyValue(item.accruedInt), quantity: mapInteger(item.quantity), quantityDone: mapInteger(item.quantityDone), }; } export function mapOperationsPage( accountId: string, response: TBankOperationsByCursorResponse, ): BrokerOperationsPage { return { accountId, items: (response.items ?? []).map((item) => mapOperation(item, accountId)), nextCursor: response.nextCursor || null, hasNext: response.hasNext ?? false, asOf: new Date().toISOString(), }; }