166 lines
12 KiB
Markdown
166 lines
12 KiB
Markdown
# Улучшение отображения брокерского портфеля
|
||
|
||
Дата: 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-страницам операций.
|
||
- В операциях колонка `Инструмент` ведет на страницу акции или облигации, если известен тип инструмента.
|
||
- В операциях колонка `Тип` показывает русскоязычные названия.
|
||
- В операциях визуально понятно, пополняет операция портфель, списывает средства, является перекладкой или не классифицирована.
|