From 7b1d6498533f1ac046c3a115b2b09b5bfd91f817 Mon Sep 17 00:00:00 2001 From: Sergey Krylov Date: Sun, 21 Jun 2026 21:22:56 +0300 Subject: [PATCH] docs: add broker events and payouts feature docs --- docs/epics/BrokerPortfolio.md | 1 + .../broker-events-and-payouts/plan.md | 209 ++++++++++++++++++ .../broker-events-and-payouts/spec.md | 191 ++++++++++++++++ .../broker-events-and-payouts/tasks.md | 78 +++++++ docs/inbox.md | 8 + docs/roadmap.md | 3 + 6 files changed, 490 insertions(+) create mode 100644 docs/features/broker-events-and-payouts/plan.md create mode 100644 docs/features/broker-events-and-payouts/spec.md create mode 100644 docs/features/broker-events-and-payouts/tasks.md diff --git a/docs/epics/BrokerPortfolio.md b/docs/epics/BrokerPortfolio.md index ba3fff0..f74bcd8 100644 --- a/docs/epics/BrokerPortfolio.md +++ b/docs/epics/BrokerPortfolio.md @@ -28,6 +28,7 @@ - [Отображение брокерского портфеля](../features/broker-portfolio-display/spec.md) - [x] [Разделы брокерского счёта](../features/broker-account-sections/spec.md) — реализовано - [Информативный обзор брокерских счетов](../features/broker-accounts-overview/spec.md) +- [Календарь событий и прогноз будущих выплат брокерского счёта](../features/broker-events-and-payouts/spec.md) - [Улучшение UI операций](../features/broker-operations-ui-improvements/spec.md) - [Пагинация и загрузка позиций](../features/broker-positions-pagination-and-loading/spec.md) - [Исправление deadline и очереди T-Bank](../features/tbank-deadline-queue-fix/spec.md) diff --git a/docs/features/broker-events-and-payouts/plan.md b/docs/features/broker-events-and-payouts/plan.md new file mode 100644 index 0000000..9a1b22d --- /dev/null +++ b/docs/features/broker-events-and-payouts/plan.md @@ -0,0 +1,209 @@ +# Broker Events And Payouts Implementation Plan + +> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development +> (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use +> checkbox (`- [ ]`) syntax for tracking. + +**Goal:** Добавить в брокерский счёт T-Bank раздел предстоящих событий и ориентировочный прогноз +будущих выплат по выбранному периоду. + +**Architecture:** Backend добавляет отдельный read-only endpoint событий поверх уже существующих +данных T-Bank и MOEX. Слой агрегации строит best-effort список событий по текущим позициям счёта и +summary по будущим денежным потокам. Frontend расширяет shell брокерского счёта новой вкладкой +`События`, отдельной страницей и компактным overview-виджетом ближайших событий. + +**Tech Stack:** NestJS 10, Prisma, CacheService, MOEX client, T-Bank module, React 18, React Router +6, TanStack Query 5, TypeScript, Vitest, Testing Library. + +--- + +## Связанные SDD-артефакты + +- Epic: `docs/epics/BrokerPortfolio.md` +- Spec: `docs/features/broker-events-and-payouts/spec.md` +- Tasks: `docs/features/broker-events-and-payouts/tasks.md` + +## Gate перед реализацией + +До начала кода пользователь должен отдельно подтвердить, что: + +- текущая spec остаётся источником истины; +- `plan.md` и `tasks.md` согласованы; +- первая версия действительно ограничивается T-Bank-сценарием и оценкой по текущим позициям; +- follow-up по entitlement, налогам и полной купонной сетке остаются вне реализации. + +## Архитектурные решения + +### Backend endpoint + +Новый endpoint: + +```text +GET /api/v1/broker/accounts/:accountId/events?from=YYYY-MM-DD&to=YYYY-MM-DD +``` + +Endpoint возвращает: + +- `items` — плоский список событий по текущим позициям счёта; +- `summary` — агрегаты по денежным событиям за период; +- `asOf` — момент построения read model. + +### Backend read model + +Новый сервис `BrokerEventsService` живёт внутри `TBankModule` и использует: + +- `BrokerAccountsService` для проверки счёта; +- `BrokerPortfolioService` или внутренний shared-path для получения текущих позиций; +- `BrokerInstrumentsService` для сопоставления T-Bank instrument metadata; +- `MoexClientService` для дивидендов и bond enrichment; +- `CacheService` для кэширования результата по `accountId + from + to`. + +Сервис не записывает события в Prisma и не вводит отдельные таблицы в первой версии. + +### Формирование событий + +По акциям: + +- для каждой позиции, сопоставимой с MOEX share instrument, читаются dividend events; +- в выборку попадают только события, чья `registryCloseDate` лежит в диапазоне; +- `estimatedAmount` рассчитывается как текущий `quantity * payoutPerUnit`, если payout известен. + +По облигациям: + +- используется уже доступный enrichment-слой MOEX; +- `nextCouponDate` даёт событие `coupon`; +- `matDate` даёт событие `maturity`; +- `offerDate` даёт событие `offer`; +- `couponValue` и `faceValue` используются для оценки `estimatedAmount`, когда они доступны. + +### Семантика summary + +`summary` агрегирует только денежные события: + +- дивиденды; +- купоны; +- погашения. + +Оферты остаются в общем списке событий, но не обязаны входить в сумму денежных потоков первой +версии. + +### Частичная деградация + +Сервис работает по принципу best-effort: + +- ошибка по одной бумаге не роняет весь ответ; +- unsupported или несопоставимые позиции пропускаются; +- если сумма события не может быть рассчитана, событие всё равно возвращается без суммы. + +## API и типы + +### Query contract + +Новый query DTO: + +```ts +type BrokerEventsQuery = { + from: string; + to: string; +}; +``` + +Обе даты обязательны в первой версии, чтобы не вводить неочевидные дефолты по периоду. + +### Response contract + +```ts +type BrokerPortfolioEvent = { + id: string; + type: 'dividend' | 'coupon' | 'maturity' | 'offer'; + category: 'cashflow' | 'corporate'; + eventDate: string; + paymentDate: string | null; + ticker: string | null; + name: string | null; + instrumentUid: string | null; + instrumentType: 'share' | 'bond' | 'other'; + quantitySnapshot: number | null; + payoutPerUnit: number | null; + estimatedAmount: number | null; + currency: string | null; + estimateMode: 'current_position'; +}; + +type BrokerEventsSummary = { + eventCount: number; + nearestEventDate: string | null; + totalEstimatedCashflow: number; + dividendsTotal: number; + couponsTotal: number; + principalRepaymentTotal: number; +}; +``` + +## Карта файлов + +### Backend + +- Create `apps/backend/src/modules/tbank/dto/broker-events-query.dto.ts` +- Create `apps/backend/src/modules/tbank/dto/broker-events-response.dto.ts` +- Modify `apps/backend/src/modules/tbank/dto/broker-envelope.dto.ts` +- Modify `apps/backend/src/modules/tbank/types/broker.types.ts` +- Create `apps/backend/src/modules/tbank/services/broker-events.service.ts` +- Create `apps/backend/src/modules/tbank/services/broker-events.service.spec.ts` +- Modify `apps/backend/src/modules/tbank/tbank.controller.ts` +- Modify `apps/backend/src/modules/tbank/tbank.controller.spec.ts` +- Modify `apps/backend/src/modules/tbank/tbank.module.ts` + +### Frontend + +- Modify `apps/frontend/src/shared/api/responses.ts` +- Modify `apps/frontend/src/shared/api/index.ts` +- Modify `apps/frontend/src/shared/api/types.ts` through codegen if Swagger changes are published +- Create `apps/frontend/src/entities/broker-event/api/brokerEventApi.ts` +- Create `apps/frontend/src/entities/broker-event/model/useBrokerEvents.ts` +- Create `apps/frontend/src/entities/broker-event/index.ts` +- Create `apps/frontend/src/pages/broker-events/ui/BrokerEventsPage.tsx` +- Create `apps/frontend/src/pages/broker-events/index.ts` +- Create `apps/frontend/src/widgets/broker-events-overview/ui/BrokerEventsOverview.tsx` +- Create `apps/frontend/src/widgets/broker-events-overview/index.ts` +- Modify `apps/frontend/src/widgets/broker-account-layout/ui/BrokerAccountLayout.tsx` +- Modify `apps/frontend/src/pages/broker-account/ui/BrokerAccountOverviewPage.tsx` +- Modify `apps/frontend/src/app/routing/AppRoutes.tsx` + +### Documentation + +- Modify `apps/docs/docs/backend/tbank-invest.md` +- Modify `apps/docs/docs/backend/modules.md` if the public module description changes materially + +## Rollout shape + +### Overview + +Overview получает компактный блок ближайших событий: + +- не более 5 событий; +- summary по ближайшим выплатам не обязателен, если он перегружает карточку; +- ссылка ведёт в полный раздел `События`. + +### Dedicated page + +Страница `События` включает: + +- выбор диапазона дат; +- summary cards; +- список событий; +- loading, empty и error states. + +## Verification strategy + +- Backend unit tests для event mapping, summary и partial failures. +- Backend controller tests для query validation и response shape. +- Frontend hook tests для query lifecycle. +- Frontend page/component tests для фильтра периода, summary и overview-виджета. +- Final verification: + - `npm test -w apps/backend` + - `npm test -w apps/frontend` + - `npm run build -w apps/backend` + - `npm run build -w apps/frontend` + - `npm run build -w apps/docs` если публичные docs менялись + - `npm run codegen -w apps/frontend` если Swagger-контракт обновлён diff --git a/docs/features/broker-events-and-payouts/spec.md b/docs/features/broker-events-and-payouts/spec.md new file mode 100644 index 0000000..8fa7256 --- /dev/null +++ b/docs/features/broker-events-and-payouts/spec.md @@ -0,0 +1,191 @@ +# Календарь событий и прогноз будущих выплат брокерского счёта + +Дата: 2026-06-21 +Статус: согласовано к планированию +Эпик: [Портфель брокера](../../epics/BrokerPortfolio.md) + +## Цель + +Дать пользователю брокерского счёта T-Bank отдельный раздел, где можно увидеть будущие события по +бумагам счёта и ориентировочный денежный поток по выбранному диапазону дат. + +## Пользовательский результат + +Пользователь может: + +- быстро увидеть ближайшие события по текущему счёту прямо на overview; +- открыть отдельную вкладку `События` внутри счёта; +- выбрать период, например с `2026-06-22` по `2026-07-29`; +- получить список дивидендов, купонов, погашений и оферт, попадающих в этот период; +- увидеть ориентировочную сумму будущих выплат по выбранному диапазону; +- понимать, какие значения являются оценкой по текущим позициям, а не подтверждённым правом на + выплату. + +## Область изменений + +Фича относится только к брокерским счетам T-Bank и включает: + +- новый блок ближайших событий на overview счёта; +- новую вкладку `События` в навигации брокерского счёта; +- фильтр диапазона дат; +- список событий по текущим позициям счёта; +- агрегированный summary по будущим выплатам за выбранный период. + +## Требования + +### 1. Область первой версии + +- Первая версия работает только внутри маршрутов `/broker/:accountId/*`. +- Ручные портфели `PortfolioModule` не входят в область фичи. +- Backend остаётся единственным клиентом T-Bank и MOEX. +- Фича является read-only и не изменяет состав счёта или права пользователя. + +### 2. Навигация и точки входа + +- На overview брокерского счёта показывается компактный блок ближайших событий. +- Блок overview содержит переход во вкладку `События`. +- У счёта появляется отдельный раздел `События` рядом с существующими разделами `Обзор`, `Акции`, + `Облигации` и `Операции`. +- Переход между разделами не меняет выбранный счёт. + +### 3. Типы событий первой версии + +Первая версия поддерживает события: + +- `dividend` — дивиденд по акции; +- `coupon` — купон по облигации; +- `maturity` — погашение облигации; +- `offer` — оферта по облигации. + +По смыслу события делятся на: + +- `cashflow` — событие с ожидаемым денежным эффектом; +- `corporate` — важное событие без обязательной денежной оценки в первой версии. + +Правила классификации: + +- `dividend` относится к `cashflow`; +- `coupon` относится к `cashflow`; +- `maturity` относится к `cashflow`; +- `offer` относится к `corporate`. + +### 4. Диапазон дат + +- Пользователь задаёт диапазон `from` / `to`. +- Диапазон фильтруется по дате события, а не по дате поступления денег. +- Границы диапазона включительные. +- Если `eventDate` не попадает в диапазон, событие не показывается. +- В первой версии отдельный режим фильтрации по `paymentDate` отсутствует. + +### 5. Источники данных + +- Для состава счёта используются текущие позиции T-Bank. +- Для акций используются дивидендные данные MOEX. +- Для облигаций используются уже доступные поля MOEX enrichment, включая `nextCouponDate`, + `couponValue`, `matDate`, `offerDate` и `faceValue`. +- Фича не требует отдельного постоянного хранилища событий в первой версии. + +### 6. Правила прогноза выплат + +- Все денежные суммы первой версии являются оценкой по текущим позициям счёта на момент запроса. +- Фича не восстанавливает историческое владение бумагой на дату отсечки. +- Если состав счёта изменится, прогноз по тем же датам может измениться. +- UI обязан явно показывать, что сумма является estimate или прогнозом. + +#### Дивиденды + +- Событие строится по `registryCloseDate`. +- Если размер выплаты доступен, ориентировочная сумма считается как количество бумаг в текущем + счёте, умноженное на выплату на единицу. +- Если размер выплаты отсутствует, событие всё равно может быть показано без итоговой суммы. + +#### Купоны + +- Событие строится по `nextCouponDate`. +- Если размер купона доступен, ориентировочная сумма считается как количество облигаций в текущем + счёте, умноженное на `couponValue`. +- Если размер купона отсутствует, событие показывается без итоговой суммы. + +#### Погашение + +- Событие строится по `matDate`. +- Если известен `faceValue`, ориентировочная сумма считается как количество облигаций в текущем + счёте, умноженное на номинал. +- Если номинал отсутствует, событие показывается без итоговой суммы. + +#### Оферта + +- Событие строится по `offerDate`. +- В первой версии оферта считается информационным событием. +- Для оферты не требуется обязательная денежная оценка. + +### 7. Overview счёта + +- Overview показывает ближайшие 3-5 событий выбранного счёта. +- Для каждого события overview показывает дату, инструмент, тип события и сумму при наличии. +- Если ближайших событий нет, overview показывает спокойное пустое состояние. +- Ошибка загрузки блока событий не должна ломать остальной overview счёта. + +### 8. Вкладка `События` + +- Вкладка содержит фильтр периода, summary и список событий. +- Список событий показывает: + - дату события; + - тип события; + - инструмент; + - тип инструмента; + - количество бумаг, использованное для расчёта; + - выплату на единицу при наличии; + - итоговую ориентировочную сумму при наличии; + - валюту при наличии. +- Для денежных оценок UI показывает признак `estimate`. + +### 9. Summary по периоду + +Summary по выбранному периоду показывает: + +- количество событий; +- ближайшую дату события; +- общий ориентировочный денежный поток; +- сумму дивидендов; +- сумму купонов; +- сумму погашений. + +Оферты не обязаны входить в денежный итог первой версии. + +### 10. Ошибки, пустые состояния и частичная деградация + +- Ошибка по одному инструменту не должна ломать весь календарь счёта. +- Если по событию недоступна сумма, событие может быть показано без суммы. +- Если в выбранном диапазоне нет событий, пользователь видит пустое состояние, а не ошибку. +- Ошибка вкладки `События` не должна скрывать общую навигацию счёта. + +## Ограничения + +- Первая версия не покрывает ручные портфели. +- Первая версия не учитывает налоги, комиссии и валютную конвертацию. +- Первая версия не различает в UI `eventDate` и `paymentDate` как отдельные режимы фильтрации. +- Если источник облигаций даёт только ближайший купон, дальний график будущих купонов не + гарантируется. +- Фича не заменяет историческую аналитику операций и не является налоговым расчётом. + +## Acceptance Criteria + +- На overview брокерского счёта отображается блок ближайших событий. +- В навигации брокерского счёта есть вкладка `События`. +- Пользователь может задать диапазон дат. +- Вкладка показывает только события, дата которых попадает в выбранный диапазон. +- Пользователь видит дивиденды, купоны, погашения и оферты, если они доступны по текущим позициям. +- Summary показывает агрегированный прогноз будущих выплат по диапазону. +- Все денежные суммы явно обозначены как оценочные. +- Пустой диапазон отображается как отдельное пустое состояние. +- Ошибка по одному инструменту не ломает весь ответ. + +## Вне области фичи + +- точный entitlement-расчёт по историческому владению и датам отсечки; +- поддержка ручных портфелей; +- единый календарь по нескольким доменам портфелей; +- налоги, мультивалютный net cashflow и комиссии; +- iCal/export, уведомления и напоминания; +- отдельный аналитический раздел вне брокерского счёта. diff --git a/docs/features/broker-events-and-payouts/tasks.md b/docs/features/broker-events-and-payouts/tasks.md new file mode 100644 index 0000000..014f6b7 --- /dev/null +++ b/docs/features/broker-events-and-payouts/tasks.md @@ -0,0 +1,78 @@ +# Календарь событий и прогноз будущих выплат брокерского счёта — задачи + +Статус: запланировано + +Связанные документы: + +- [Epic](../../epics/BrokerPortfolio.md) +- [Spec](spec.md) +- [Plan](plan.md) + +## 1. Подготовка и gate + +- [ ] Получить отдельное подтверждение `spec.md`, `plan.md` и `tasks.md` перед началом кода. +- [ ] Подтвердить, что первая версия ограничена T-Bank-сценарием и расчётом по текущим позициям. +- [ ] Зафиксировать, что follow-up по historical entitlement, налогам и полной купонной сетке не + входят в первую реализацию. + +## 2. Backend contract + +- [ ] Добавить новый broker endpoint событий с обязательными параметрами `from` и `to`. +- [ ] Описать Swagger DTO для query, response item, summary и envelope. +- [ ] Синхронизировать backend internal types для списка событий и summary. + +## 3. Backend aggregation + +- [ ] Добавить `BrokerEventsService` в `TBankModule`. +- [ ] Собрать текущие позиции счёта через существующий backend path без frontend-композиции. +- [ ] Построить dividend events по MOEX dividend data. +- [ ] Построить coupon, maturity и offer events по MOEX bond enrichment. +- [ ] Рассчитывать `estimatedAmount` только там, где для этого достаточно данных. +- [ ] Реализовать best-effort деградацию по отдельным инструментам. +- [ ] Добавить кэширование результата по `accountId + from + to`. + +## 4. Backend tests + +- [ ] Покрыть дивиденды, купоны, погашения и оферты unit-тестами. +- [ ] Проверить включительные границы диапазона дат. +- [ ] Проверить partial-failure сценарий, где один инструмент не ломает весь ответ. +- [ ] Проверить controller response shape и валидацию query. + +## 5. Frontend data layer + +- [ ] Добавить entity/hook для чтения broker events. +- [ ] Добавить handwritten response types для событий и summary. +- [ ] Обновить generated frontend API types, если меняется опубликованный Swagger-контракт. + +## 6. Интерфейс overview + +- [ ] Добавить overview-виджет ближайших событий для выбранного счёта. +- [ ] Показать не более 5 ближайших событий. +- [ ] Добавить ссылку на полную вкладку `События`. +- [ ] Реализовать loading, empty и local error state без поломки overview. + +## 7. Вкладка `События` + +- [ ] Добавить новый раздел `События` в `BrokerAccountLayout`. +- [ ] Добавить маршрут `/broker/:accountId/events`. +- [ ] Реализовать выбор диапазона дат. +- [ ] Реализовать summary по выбранному периоду. +- [ ] Реализовать список событий с признаком `estimate`. +- [ ] Разделить денежные и неденежные события на уровне представления. + +## 8. Frontend tests + +- [ ] Покрыть hook событий тестами query lifecycle. +- [ ] Покрыть overview-виджет сценариями loading, empty, error и success. +- [ ] Покрыть страницу `События` сменой диапазона, отображением summary и пустым состоянием. +- [ ] Проверить наличие новой вкладки в навигации счёта. + +## 9. Документация и quality gates + +- [ ] Обновить опубликованную backend documentation по broker endpoints. +- [ ] Прогнать backend tests. +- [ ] Прогнать frontend tests. +- [ ] Прогнать backend build. +- [ ] Прогнать frontend build. +- [ ] Прогнать docs build, если менялась опубликованная документация. +- [ ] Отметить roadmap и связанные SDD-статусы только после полной проверки реализации. diff --git a/docs/inbox.md b/docs/inbox.md index bebddb9..0c77c03 100644 --- a/docs/inbox.md +++ b/docs/inbox.md @@ -281,6 +281,14 @@ cash flow, бюджеты, аналитика, прогнозы и автома - Исследовать учёт количества бумаг на дату фиксации, валют, налогов, комиссий, оферт и изменений состава портфеля. - Календарь событий должен стать источником событий, а аналитика выплат — их денежным представлением. +- Для broker-first версии закрепить follow-up на точный расчёт entitlement по истории операций, а не + только по текущим позициям счёта. +- Исследовать раздельную семантику `eventDate` и `paymentDate`, чтобы будущий фильтр периода мог + переключаться между датой события и датой поступления денег. +- Проверить, можно ли получить полный график купонов, амортизаций и частичных погашений, а не + только ближайшие доступные bond events. +- Отдельно продумать мультивалютность, налоги и net cashflow, чтобы будущие выплаты не выглядели + как гарантированная сумма к зачислению. ## Портфельная аналитика diff --git a/docs/roadmap.md b/docs/roadmap.md index 591892a..55f663b 100644 --- a/docs/roadmap.md +++ b/docs/roadmap.md @@ -12,6 +12,9 @@ Roadmap отражает порядок продуктовой работы, н - [x] [Разделы брокерского счёта](features/broker-account-sections/spec.md) — реализовано. - [x] [Информативный обзор брокерских счетов](features/broker-accounts-overview/spec.md) — реализовано. +- [ ] [Календарь событий и прогноз будущих выплат брокерского счёта](features/broker-events-and-payouts/spec.md) + — отдельная вкладка событий и прогноз будущих выплат по выбранному диапазону дат для T-Bank + счёта. ## Следующие этапы для активной фичи -- 2.47.2