144 lines
3.8 KiB
TypeScript
144 lines
3.8 KiB
TypeScript
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,
|
||
};
|