## Контекст Portfolio сейчас получает текущее состояние счёта через T-Bank OperationsService и хранит только краткоживущие in-memory Portfolio Snapshots. В WealthLedger нет endpoint операций, типов операций, маршрута или локальной базы операций. Vendored T-Bank proto уже объявляет `GetOperationsByCursor`. Мотивация приведена в proposal.md, а поведение - в delta-spec `brokerage-account-ledger`. ## Цели и нецели **Цели:** - Добавить защищённый read path с cursor-пагинацией для исполненных операций T-Bank. - Сохранить соответствие «одна upstream-операция - одна запись» и точные денежные значения. - Использовать существующие паттерны T-Bank auth, проверки счёта, типизированного OpenAPI и локальных UI-состояний. **Не входит в объём:** - Постоянное хранение, синхронизация или экспорт операций. - Расчёт последовательных денежных остатков, итогов, налоговой отчётности или исторической оценки портфеля. - Конвертация валют, рыночные изменения цены или настраиваемое пользователем количество последних событий. ## Решения ### Прямое чтение из T-Bank с кратким in-memory cache Сервис журнала будет запрашивать `GetOperationsByCursor` по требованию и кратко кэшировать точный результат запроса с ключом из User, счёта и нормализованного query. Это соответствует времени жизни текущего Portfolio Snapshot и исключает Prisma schema, backfill и жизненный цикл синхронизации. Рассмотренная альтернатива: сохранять и синхронизировать страницы операций, как в `references/moex-vibe`. Отклонена, поскольку запрошенной возможности нужна текущая история только для чтения, а не автономная локальная история или производный расчёт баланса. ### Нормализованная страница операций на границе Brokerage Account `GET /api/v1/brokerage-accounts/:accountId/operations` будет принимать необязательные включительные границы периода, подмножество категорий и cursor/limit. Ответ содержит id счёта, упорядоченные записи, `nextCursor`, `hasNext` и timestamp наблюдения. Он будет использовать существующую семантику принадлежности счёта и T-Bank token, чтобы недоступное connection, невалидный token и недоступный upstream оставались согласованными с Portfolio. Backend сопоставляет T-Bank enum-типы шести стабильным категориям: `trade`, `income`, `tax`, `fee`, `transfer`, `other`. Он сохраняет исходный тип для точного label и будущего уточнения категорий. Запрашивается только `OPERATION_STATE_EXECUTED`. Рассмотренная альтернатива: передавать raw protobuf-объекты операций. Отклонена, поскольку имена protobuf, кодирование денег и соглашения об optional-полях попадут в публичный API. ### Одна upstream-операция - одна запись журнала Mapper сохраняет payment, commission, price, accrued interest и количества отдельными optional-полями одной записи. Комиссия сделки не разделяется на вымышленную запись комиссии. Записи без payment не получают искусственное нулевое значение. Это предотвращает двойной учёт, когда T-Bank передаёт и поле комиссии операции, и отдельные fee-операции. Decimal strings и ISO-коды валют следуют существующему контракту Portfolio; frontend только форматирует их и не выполняет финансовую арифметику. ### Независимая устойчивость обзора и полной истории Обзор счёта выполнит небольшой запрос журнала с `limit=5` вместе с запросом Portfolio. Его loading, empty и error views остаются внутри блока последних событий. Полная страница по `/accounts/:accountId/operations` владеет состояниями периода, категорий и cursor; изменение фильтров сбрасывает cursor. Она компактно отображает строки без цены, количества и раскрытия деталей. Рассмотренная альтернатива: tab внутри страницы Portfolio. Отклонена, поскольку controls периода и pagination сделают основной обзор громоздким и свяжут несвязанные error states. ### Generated OpenAPI-типы на границе frontend Swagger-контракт backend синхронизируется до frontend-работ. Frontend entity выводит типы request/response из generated `paths` и использует общий аутентифицированный API client. UI-labels, category controls и сообщения являются i18n-строками. ## Риски и компромиссы - [T-Bank не передаёт остаток после операции] -> Показывать только фактические изменения; отложить дизайн последовательного баланса до потребности в persisted history и reconciliation. - [Расширение upstream enum] -> Сопоставлять неизвестные типы с `other`, сохраняя raw type и upstream description. - [Повторная навигация создаёт upstream-запросы] -> Краткий cache точного query и frontend query cache сокращают дублирование, не делая данные постоянными. - [Ошибка последних событий может скрыть Portfolio] -> Оставить состояние локальным для блока. - [Период дат может содержать много операций] -> Использовать T-Bank cursor-пагинацию и ограниченные размеры страниц. ## План миграции 1. Развернуть backend endpoint и OpenAPI-контракт; миграция данных не требуется. 2. Синхронизировать generated frontend API files и развернуть блок последних событий и маршрут истории. 3. Для отката удалить frontend-навигацию и deployment endpoint; постоянные пользовательские данные не требуют очистки.