# Улучшение отображения брокерского портфеля Дата: 2026-06-17 Статус: согласовано к планированию ## Контекст Страница брокерского счета находится в `apps/frontend/src/pages/broker/BrokerAccountDetailPage.tsx`. Сейчас она показывает все позиции одной таблицей, выводит колонку `Доходность`, не показывает отдельную колонку текущей цены, не делает тикеры кликабельными и загружает операции одним запросом с `limit: 100`. Данные для страницы уже есть в текущем frontend-контракте: - `BrokerPosition.instrumentType`, `ticker`, `name`, `quantity`, `currentPrice`, `currentValue`; - `BrokerOperation.type`, `category`, `description`, `ticker`, `instrumentType`, `payment`, `price`; - `BrokerOperationsPage.nextCursor` и `hasNext` для cursor-пагинации. Бэкенд и публичный API для этой задачи менять не нужно. ## Цель Сделать страницу брокерского портфеля легче для чтения: разделить классы инструментов, убрать лишнюю доходность из таблицы позиций, добавить текущую цену, сделать переходы к карточкам инструментов и явно показать финансовый смысл операций. ## Область изменений В рамках задачи меняется только frontend брокерской страницы: - `apps/frontend/src/pages/broker/BrokerAccountDetailPage.tsx`; - новые helper-функции и компоненты внутри `apps/frontend/src/pages/broker/`; - тесты в `apps/frontend/src/pages/broker/` и существующий `BrokerPages.test.tsx`. `apps/docs`, backend DTO, OpenAPI и codegen не меняются. ## Позиции Позиции брокерского портфеля показываются отдельными секциями: - `Акции` для `instrumentType === 'share'`; - `Облигации` для `instrumentType === 'bond'`; - `Другие инструменты`, если в портфеле есть позиции с другим или неизвестным типом. Пустые секции не отображаются. Таблица каждой секции использует компактные колонки: | Колонка | Источник | Поведение | | ---------- | -------------- | ---------------------------------------------------------- | | Тикер | `ticker` | Если известен маршрут инструмента, тикер является ссылкой. | | Название | `name` | Если имени нет, показывается `-`. | | Количество | `quantity` | Если количества нет, показывается `-`. | | Цена | `currentPrice` | Деньги форматируются как `ru-RU` currency. | | Стоимость | `currentValue` | Деньги форматируются как `ru-RU` currency. | Колонка `Доходность` удаляется из таблиц позиций брокерского портфеля. Доходность остается доступной в summary над таблицами, где она не смешивается с построчным составом портфеля. ### Ссылки на инструменты Маршрутизация повторяет ручной портфель: - акция: `/stocks/:ticker`; - облигация: `/bonds/:ticker`. Если `ticker` отсутствует или тип инструмента не поддержан, значение показывается текстом без ссылки. Для неизвестного `instrumentType` допускается fallback по `classCode`, если он однозначно указывает на акцию или облигацию. ## Операции Операции показываются по 10 штук на странице. Страница использует существующий cursor API: - первый запрос: `{ limit: 10 }`; - переход вперед: `{ limit: 10, cursor: nextCursor }`; - переход назад: восстановление предыдущего cursor из локального stack в UI. UI показывает номер текущей страницы, кнопку `Назад` и кнопку `Вперед`. `Назад` выключена на первой странице. `Вперед` выключена, когда `hasNext === false` или нет `nextCursor`. Таблица операций сохраняет основные колонки: | Колонка | Источник | Поведение | | ---------- | --------------------------------- | ------------------------------------------------------------- | | Дата | `date` | `toLocaleString('ru-RU')`, при отсутствии `-`. | | Тип | `type`, `description`, `category` | Русскоязычное название и бейдж эффекта. | | Инструмент | `ticker`, `description` | Если известен маршрут инструмента, значение является ссылкой. | | Сумма | `payment` | Форматируется как деньги и окрашивается по эффекту операции. | ### Русские названия типов Для известных T-Bank enum-значений frontend показывает русские названия. Минимальный набор: | T-Bank type | Название | | ------------------------------------ | -------------------------- | | `OPERATION_TYPE_BUY` | Покупка | | `OPERATION_TYPE_BUY_CARD` | Покупка | | `OPERATION_TYPE_SELL` | Продажа | | `OPERATION_TYPE_SELL_CARD` | Продажа | | `OPERATION_TYPE_COUPON` | Выплата купона | | `OPERATION_TYPE_DIVIDEND` | Дивиденды | | `OPERATION_TYPE_BOND_REPAYMENT` | Погашение облигации | | `OPERATION_TYPE_BOND_REPAYMENT_FULL` | Полное погашение облигации | | `OPERATION_TYPE_TAX` | Налог | | `OPERATION_TYPE_BOND_TAX` | Налог по облигациям | | `OPERATION_TYPE_DIVIDEND_TAX` | Налог на дивиденды | | `OPERATION_TYPE_BROKER_FEE` | Комиссия брокера | | `OPERATION_TYPE_SERVICE_FEE` | Комиссия за обслуживание | | `OPERATION_TYPE_INPUT` | Пополнение | | `OPERATION_TYPE_OUTPUT` | Вывод средств | | `OPERATION_TYPE_INPUT_SECURITIES` | Зачисление бумаг | | `OPERATION_TYPE_OUTPUT_SECURITIES` | Списание бумаг | Если тип неизвестен, но есть `description`, показывается `description`. Если нет и описания, показывается очищенный enum без префикса `OPERATION_TYPE_`. ### Эффект операции Выбранный дизайн: бейдж эффекта в колонке `Тип` плюс цвет суммы. Эффект определяется не только знаком суммы, потому что сделки не являются доходом сами по себе: - `Пополнение` и доходы вроде купонов или дивидендов получают эффект `Пополняет`; - налоги, комиссии и вывод средств получают эффект `Списывает`; - покупки, продажи, ввод/вывод бумаг и погашение тела облигации получают эффект `Перекладка`; - неизвестные операции получают эффект `Неясно`. Цвета используются как вспомогательный признак, а текст бейджа остается основным признаком для доступности: - `Пополняет`: зеленый акцент; - `Списывает`: красный акцент; - `Перекладка`: нейтральный или синий акцент; - `Неясно`: приглушенный серый акцент. ## Ошибки и пустые состояния - Если портфель не загрузился, остается текущее сообщение об ошибке портфеля. - Если операции загружаются, показывается `Загрузка операций...`. - Если операций нет, показывается пустое состояние `Операций за выбранный период нет`. - Если у позиции или операции нет тикера, вместо ссылки показывается текстовое значение или `-`. ## TDD-стратегия Сначала пишутся failing tests: 1. Helper-тесты для группировки позиций, маршрутов инструментов, русских названий операций и эффекта операций. 2. Component/page-тесты для раздельных таблиц акций и облигаций, отсутствия колонки `Доходность` и наличия `Цена`. 3. Component/page-тесты для кликабельных инструментов в позициях и операциях. 4. Component/page-тесты для cursor-пагинации операций по 10 элементов. Затем реализуются helper-функции, компоненты таблиц и интеграция в `BrokerAccountDetailPage`. ## Acceptance Criteria - Позиции акций и облигаций отображаются в отдельных таблицах. - Позиции с другим типом отображаются в `Другие инструменты`, если такие позиции есть. - В таблицах позиций нет колонки `Доходность`. - В таблицах позиций есть колонка `Цена`. - Тикеры позиций ведут на `/stocks/:ticker` или `/bonds/:ticker`. - Операции запрашиваются с `limit: 10`. - Пользователь может переходить вперед и назад по cursor-страницам операций. - В операциях колонка `Инструмент` ведет на страницу акции или облигации, если известен тип инструмента. - В операциях колонка `Тип` показывает русскоязычные названия. - В операциях визуально понятно, пополняет операция портфель, списывает средства, является перекладкой или не классифицирована.