import { formatMoney, formatSnapshotDay } from "@/shared/lib"; import type { BrokerageOperation } from "../api/useBrokerageOperations"; enum OperationType { Buy = "OPERATION_TYPE_BUY", BuyCard = "OPERATION_TYPE_BUY_CARD", BuyMargin = "OPERATION_TYPE_BUY_MARGIN", Coupon = "OPERATION_TYPE_COUPON", DivExtension = "OPERATION_TYPE_DIV_EXT", Dividend = "OPERATION_TYPE_DIVIDEND", DividendTransfer = "OPERATION_TYPE_DIVIDEND_TRANSFER", InputSecurities = "OPERATION_TYPE_INPUT_SECURITIES", OutputSecurities = "OPERATION_TYPE_OUTPUT_SECURITIES", Sell = "OPERATION_TYPE_SELL", SellCard = "OPERATION_TYPE_SELL_CARD", SellMargin = "OPERATION_TYPE_SELL_MARGIN", } const operationTransferPrefix = "OPERATION_TYPE_TRANS_"; type PaymentPresentation = { amount: string; label: string; tone: "negative" | "neutral" | "positive"; }; function brokerageOperationGroupKey( operation: BrokerageOperation, ): null | string { const identity = operation.instrumentUid || operation.figi; if (!identity) return null; return JSON.stringify([ formatSnapshotDay(operation.occurredAt), identity, operation.type, ]); } function entryLabelKey(operation: BrokerageOperation): string { if (operation.category === "income") return isOperationType(operation.type, OperationType.Coupon) ? "ledger.entryLabel.income.coupon" : "ledger.entryLabel.income.dividend"; return `ledger.category.${operation.category}`; } function isCouponPayment(operation: BrokerageOperation): boolean { return isOperationType(operation.type, OperationType.Coupon); } function isIncomePayment(operation: BrokerageOperation): boolean { return [OperationType.Coupon, OperationType.Dividend].some(type => isOperationType(operation.type, type), ); } function isOperationType(value: unknown, expected: OperationType): boolean { return value === expected; } function isZeroDecimal(value: string): boolean { return /^-?0$|^-?0\.0+$/.test(value); } function operationLabelKey(type: string): string { if ( [OperationType.Buy, OperationType.BuyCard, OperationType.BuyMargin].some( candidate => isOperationType(type, candidate), ) ) return "ledger.operationType.buy"; if ( [OperationType.Sell, OperationType.SellCard, OperationType.SellMargin].some( candidate => isOperationType(type, candidate), ) ) return "ledger.operationType.sell"; if ( [ OperationType.DivExtension, OperationType.Dividend, OperationType.DividendTransfer, ].some(candidate => isOperationType(type, candidate)) ) return "ledger.operationType.dividend"; if (type.includes("TAX")) return "ledger.operationType.tax"; if (type.includes("FEE")) return "ledger.operationType.fee"; if (isOperationType(type, OperationType.InputSecurities)) return "ledger.operationType.input"; if (isOperationType(type, OperationType.OutputSecurities)) return "ledger.operationType.output"; return type.startsWith(operationTransferPrefix) ? "ledger.operationType.transfer" : "ledger.operationType.other"; } function paymentPresentation( operation: BrokerageOperation, ): PaymentPresentation { if (!operation.payment) return { amount: "—", label: "ledger.noMoney", tone: "neutral", }; if (isZeroDecimal(operation.payment.value)) return { amount: formatMoney(operation.payment), label: "ledger.noMoney", tone: "neutral", }; if (operation.payment.value.startsWith("-")) return { amount: `−${formatMoney({ ...operation.payment, value: operation.payment.value.slice(1), })}`, label: entryLabelKey(operation), tone: "negative", }; return { amount: `+${formatMoney(operation.payment)}`, label: entryLabelKey(operation), tone: "positive", }; } export { brokerageOperationGroupKey, entryLabelKey, isCouponPayment, isIncomePayment, isZeroDecimal, operationLabelKey, paymentPresentation, };