refactor: enforce enum machine codes #18
Frontend verify / verify (pull_request) Canceled after 0s
Frontend security / security (pull_request) Canceled after 0s
Frontend security / security (push) Canceled after 0s
Frontend verify / verify (push) Canceled after 0s
Conventional commits / commits (pull_request) Canceled after 0s

Use owner enums for known codes; preserve wire values and unknown fallbacks.

Verified npm ci and verify: 379 tests, 5 lint checks, app and Storybook builds.
This commit is contained in:
2026-09-28 13:15:27 +03:00
parent bc8e4cc4dc
commit d20be86d73
26 changed files with 248 additions and 79 deletions
+8 -7
View File
@@ -1,6 +1,6 @@
import { useMutation, useQueryClient } from "@tanstack/react-query";
import { apiClient, apiErrorCode } from "@/shared/api";
import { apiClient, ApiErrorCode, apiErrorCode, isApiErrorCode } from "@/shared/api";
import type {
CreateBankCard,
@@ -34,14 +34,15 @@ function useBankMutations() {
});
const options = {
onError: (error: unknown) => {
const errorCode = apiErrorCode(error);
if (
[
"BANK_DEPENDENT_REFUNDS",
"BANK_INTENT_CONFLICT",
"BANK_REFUND_LIMIT",
"BANK_VERSION_CONFLICT",
"BANK_WRITE_CONFLICT",
].includes(apiErrorCode(error) ?? "")
ApiErrorCode.BankDependentRefunds,
ApiErrorCode.BankIntentConflict,
ApiErrorCode.BankRefundLimit,
ApiErrorCode.BankVersionConflict,
ApiErrorCode.BankWriteConflict,
].some(code => isApiErrorCode(errorCode, code))
)
return invalidate();
},
@@ -0,0 +1,23 @@
import { expect, it } from "vitest";
import { operationLabelKey } from "./display";
it.each([
["OPERATION_TYPE_BUY", "buy"],
["OPERATION_TYPE_BUY_CARD", "buy"],
["OPERATION_TYPE_BUY_MARGIN", "buy"],
["OPERATION_TYPE_SELL", "sell"],
["OPERATION_TYPE_SELL_CARD", "sell"],
["OPERATION_TYPE_SELL_MARGIN", "sell"],
["OPERATION_TYPE_DIV_EXT", "dividend"],
["OPERATION_TYPE_DIVIDEND", "dividend"],
["OPERATION_TYPE_DIVIDEND_TRANSFER", "dividend"],
["OPERATION_TYPE_INPUT_SECURITIES", "input"],
["OPERATION_TYPE_OUTPUT_SECURITIES", "output"],
["OPERATION_TYPE_FUTURE_TAX", "tax"],
["OPERATION_TYPE_FUTURE_FEE", "fee"],
["OPERATION_TYPE_TRANS_FUTURE", "transfer"],
["FUTURE_UNKNOWN_OPERATION", "other"],
])("preserves the wire operation label for %s", (type, label) => {
expect(operationLabelKey(type)).toBe(`ledger.operationType.${label}`);
});
+37 -16
View File
@@ -2,6 +2,23 @@ import { formatMoney } 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;
@@ -10,12 +27,16 @@ type PaymentPresentation = {
function entryLabelKey(operation: BrokerageOperation): string {
if (operation.category === "income")
return operation.type === "OPERATION_TYPE_COUPON"
return isOperationType(operation.type, OperationType.Coupon)
? "ledger.entryLabel.income.coupon"
: "ledger.entryLabel.income.dividend";
return `ledger.category.${operation.category}`;
}
function isOperationType(value: unknown, expected: OperationType): boolean {
return value === expected;
}
function isZeroDecimal(value: string): boolean {
return /^-?0$|^-?0\.0+$/.test(value);
}
@@ -23,35 +44,35 @@ function isZeroDecimal(value: string): boolean {
function operationLabelKey(type: string): string {
if (
[
"OPERATION_TYPE_BUY",
"OPERATION_TYPE_BUY_CARD",
"OPERATION_TYPE_BUY_MARGIN",
].includes(type)
OperationType.Buy,
OperationType.BuyCard,
OperationType.BuyMargin,
].some(candidate => isOperationType(type, candidate))
)
return "ledger.operationType.buy";
if (
[
"OPERATION_TYPE_SELL",
"OPERATION_TYPE_SELL_CARD",
"OPERATION_TYPE_SELL_MARGIN",
].includes(type)
OperationType.Sell,
OperationType.SellCard,
OperationType.SellMargin,
].some(candidate => isOperationType(type, candidate))
)
return "ledger.operationType.sell";
if (
[
"OPERATION_TYPE_DIV_EXT",
"OPERATION_TYPE_DIVIDEND",
"OPERATION_TYPE_DIVIDEND_TRANSFER",
].includes(type)
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 (type === "OPERATION_TYPE_INPUT_SECURITIES")
if (isOperationType(type, OperationType.InputSecurities))
return "ledger.operationType.input";
if (type === "OPERATION_TYPE_OUTPUT_SECURITIES")
if (isOperationType(type, OperationType.OutputSecurities))
return "ledger.operationType.output";
return type.startsWith("OPERATION_TYPE_TRANS_") ? "ledger.operationType.transfer" : "ledger.operationType.other";
return type.startsWith(operationTransferPrefix) ? "ledger.operationType.transfer" : "ledger.operationType.other";
}
function paymentPresentation(