241 lines
31 KiB
Markdown
241 lines
31 KiB
Markdown
# brokerage-account-income-analytics Specification
|
||
|
||
## Purpose
|
||
Позволяет User анализировать фактически полученные купоны и дивиденды одного Brokerage Account в RUB, проверять состав результата и отдельно видеть известный ближайший прогноз.
|
||
|
||
## Requirements
|
||
|
||
### Requirement: Realized income analytics is account-scoped and period-bound
|
||
|
||
Система MUST предоставлять аутентифицированному User аналитику фактически полученного дохода выбранного открытого поддерживаемого Brokerage Account либо существующего режима «Все счета» (`all`) из счетов, доступных через текущий T-Bank connection User. В режиме `all` система MUST проверять доступ каждого входящего счёта и подтверждать полноту каждого исходного периода по всем входящим счетам; отсутствующий вклад счёта MUST NOT заменяться нулём. Применённый период MUST задаваться включительными календарными датами `from` и `to` в Europe/Moscow, MUST NOT начинаться после своего окончания, MUST NOT содержать будущую дату. Система MUST NOT ограничивать максимальную длительность периода. При отсутствии применённого периода система MUST использовать интервал от первого дня текущего месяца до сегодняшнего дня включительно.
|
||
|
||
Чтение MUST использовать сохранённые операции текущего подключения и локальное подтверждение доступа без синхронного запроса T-Bank. Ответ MUST иметь envelope `{ data, meta }` по `tbank-data-sync`. Полнота MUST определяться для выбранного периода и каждого необходимого периода сравнения отдельно. Запрос MUST приоритетно загружать их пропуски до фоновой догрузки остальной истории; известные правила календарных дат MUST сохраняться.
|
||
|
||
#### Scenario: Аналитика за период по умолчанию
|
||
|
||
- **WHEN** аутентифицированный User запрашивает аналитику доступного Brokerage Account без периода
|
||
- **THEN** система рассчитывает результат от первого дня текущего месяца по Europe/Moscow до сегодняшнего дня включительно
|
||
|
||
#### Scenario: Аналитика за допустимый пользовательский период
|
||
|
||
- **WHEN** User применяет включительный период любой длительности, в том числе более 366 дней, без будущих дат
|
||
- **THEN** система рассчитывает аналитику доступного Brokerage Account за все календарные даты этого периода по Europe/Moscow
|
||
|
||
#### Scenario: Отклонение недопустимого периода
|
||
|
||
- **WHEN** User запрашивает период с некорректной или будущей датой либо с `from` после `to`
|
||
- **THEN** система отклоняет запрос и не возвращает результат для другого или усечённого периода
|
||
|
||
#### Scenario: Отклонение недоступного счёта
|
||
|
||
- **WHEN** User запрашивает аналитику Brokerage Account, который не является открытым поддерживаемым счётом, доступным через его текущий T-Bank connection
|
||
- **THEN** система возвращает существующий ответ о недоступности счёта и не раскрывает его доход или операции
|
||
|
||
#### Scenario: Общий доход требует полноты всех счетов
|
||
- **WHEN** User запрашивает режим `all` и выбранный период одного доступного счёта ещё не загружен полностью
|
||
- **THEN** система не возвращает окончательные общие суммы, сохраняет раздельное состояние покрытия счетов и запрашивает недостающую разрешённую историю
|
||
|
||
#### Scenario: Открыт ещё не загруженный период
|
||
- **WHEN** запрошенный допустимый период ещё не имеет сохранённых операций или подтверждённого полного пустого покрытия
|
||
- **THEN** система отвечает `200` со статусом первичной загрузки или частичного покрытия, запрашивает нужные интервалы и не выдаёт нулевой окончательный доход
|
||
|
||
### Requirement: Realized income uses exact executed coupon and dividend payments
|
||
|
||
Система MUST рассчитывать фактически полученный доход только по исполненным Ledger Entries с точным upstream-типом coupon или dividend. Система MUST возвращать общий итог, купонный subtotal и дивидендный subtotal как точные суммы broker-reported payments в RUB. Знак каждой выплаты MUST сохраняться, поэтому отрицательная корректировка MUST уменьшать соответствующий итог и оставаться доступной в составе результата. Dividend transfer, погашение principal, offer, trade, transfer, fee, tax, overnight income, иной тип операции, нереализованный результат и изменение рыночной цены MUST NOT входить в фактически полученный доход. Выплаты в валютах, отличных от RUB, MUST отфильтровываться при получении и MUST NOT входить в доход, subtotal или итог.
|
||
|
||
#### Scenario: Купоны и дивиденды образуют раздельные subtotals
|
||
|
||
- **WHEN** выбранный период содержит исполненные купонные и дивидендные выплаты в RUB
|
||
- **THEN** система возвращает их точные раздельные subtotals и общий итог, равный их сумме
|
||
|
||
#### Scenario: Отрицательная корректировка сохраняет знак
|
||
|
||
- **WHEN** исполненная coupon или dividend Ledger Entry содержит отрицательную broker-reported payment
|
||
- **THEN** система включает её с исходным знаком, уменьшает соответствующие subtotal и total и сохраняет запись доступной для проверки
|
||
|
||
#### Scenario: Налоги и иные операции не входят в доход
|
||
|
||
- **WHEN** выбранный период наряду с coupon и dividend содержит tax, fee, trade, transfer, maturity или иной тип Ledger Entry
|
||
- **THEN** система не включает эти записи в coupon subtotal, dividend subtotal или total
|
||
|
||
### Requirement: User explicitly applies an analytics period represented in the URL
|
||
|
||
Страница выбранного Brokerage Account «Пассивный доход» MUST позволять User редактировать черновик `from` и `to`, не изменяя видимую аналитику до явного применения. Верхний блок фильтров MUST предоставлять два подписанных календарных поля начала и окончания периода с тем же взаимодействием выбора даты, что в истории событий портфеля, и отображением дат в формате `ДД.ММ.ГГГГ`. Поля и действия применения и сброса MUST быть доступны с клавиатуры. Явное применение MUST заменить применённый период, загрузить соответствующий результат и представить применённые даты в URL страницы. Недопустимый черновик MUST показывать ошибку соответствующего поля и MUST NOT заменять последний применённый период. Сброс MUST немедленно вернуть применённый период к текущему месяцу по сегодняшний день и синхронизировать URL. Перезагрузка или открытие URL с допустимым периодом MUST восстановить тот же применённый период. Страница MUST NOT дублировать период отдельной подписью под блоком фильтров. Действие перехода на страницу MUST быть доступно из выбранного Brokerage Account, а не из глобальной навигации.
|
||
|
||
#### Scenario: Черновик периода не применён
|
||
|
||
- **WHEN** User изменяет черновик `from` или `to`, но не применяет его
|
||
- **THEN** видимые итоги и карточки выплат остаются основанными на последнем применённом периоде
|
||
|
||
#### Scenario: Выбор дат в верхнем блоке
|
||
|
||
- **WHEN** User выбирает начало и окончание периода через календарные поля
|
||
- **THEN** выбранные даты отображаются в подписанных полях в формате `ДД.ММ.ГГГГ`, а изменение остаётся черновиком до явного применения
|
||
|
||
#### Scenario: Управление фильтрами с клавиатуры
|
||
|
||
- **WHEN** User управляет верхним блоком фильтров с клавиатуры
|
||
- **THEN** он может последовательно перейти к обоим полям дат, изменить период и выполнить применение или сброс без мыши
|
||
|
||
#### Scenario: Применение периода
|
||
|
||
- **WHEN** User явно применяет допустимый черновик периода
|
||
- **THEN** страница загружает аналитику этого периода и отражает применённые `from` и `to` в URL
|
||
|
||
#### Scenario: Недопустимый черновик периода
|
||
|
||
- **WHEN** User пытается применить черновик с отсутствующей, некорректной или будущей датой либо с `from` после `to`
|
||
- **THEN** страница показывает ошибку соответствующего поля и сохраняет последний применённый период, его URL и видимую аналитику
|
||
|
||
#### Scenario: Восстановление применённого периода из URL
|
||
|
||
- **WHEN** User открывает URL страницы с допустимыми `from` и `to`
|
||
- **THEN** страница показывает аналитику за указанный применённый период
|
||
|
||
#### Scenario: Сброс периода
|
||
|
||
- **WHEN** User сбрасывает пользовательский период
|
||
- **THEN** страница немедленно применяет период от первого дня текущего месяца по Europe/Moscow до сегодняшнего дня и синхронизирует URL
|
||
|
||
### Requirement: Realized analytics never presents an incomplete result as complete
|
||
|
||
Система MUST формировать итог выбранного периода, его trend buckets,
|
||
instrument breakdown и drill-down только после успешного получения всех
|
||
необходимых cursor pages исполненных операций этого периода. Каждое comparison
|
||
MUST дополнительно требовать полного результата соответствующего периода
|
||
сравнения. Ошибка любой необходимой страницы MUST NOT возвращать частичные
|
||
totals, comparisons, buckets или
|
||
breakdown как полный результат. При временной недоступности клиент MUST
|
||
сохранять предыдущий полный результат той же выборки либо показывать
|
||
нейтральную недоступность соответствующего расчёта без технических сообщений
|
||
и ручного повтора, пока автоматические повторы не исчерпаны; после их исчерпания
|
||
применяются предупреждение и «Повторить» по `frontend-data-resilience`.
|
||
Восстановление SHALL выполняться автоматически по `frontend-data-resilience`.
|
||
|
||
Доступные независимые части MUST оставаться видимыми; неполное сравнение MUST
|
||
обозначаться как недоступное без нулевой базы и окончательной разницы. Успешный
|
||
полный период без подходящих выплат MUST возвращать нулевой результат и
|
||
отображаться с объяснением пустого состояния. Неизвестный результат MUST NOT
|
||
подменяться таким нулевым результатом.
|
||
|
||
#### Scenario: Все страницы операций успешно получены
|
||
- **WHEN** для расчёта требуется несколько cursor pages и каждая страница успешно получена
|
||
- **THEN** система формирует результат по всем подходящим Ledger Entries со всех страниц
|
||
|
||
#### Scenario: Одна из страниц операций недоступна
|
||
- **WHEN** получение любой необходимой cursor page завершается временной ошибкой
|
||
- **THEN** страница сохраняет предыдущий полный результат той же выборки либо показывает нейтральную недоступность расчёта без ручного повтора и не выдаёт частичный результат за полный
|
||
|
||
#### Scenario: В периоде нет купонов и дивидендов
|
||
- **WHEN** все необходимые операции успешно получены, но среди них нет подходящих coupon или dividend Ledger Entries
|
||
- **THEN** страница показывает нулевой результат и объясняет, что за выбранный период купоны и дивиденды не найдены
|
||
|
||
#### Scenario: Основной период готов, сравнение пока нет
|
||
- **WHEN** выбранный период полностью рассчитан, а необходимый период сравнения ещё не получен
|
||
- **THEN** основной доход остаётся доступным, сравнение обозначается как недоступное без нулевой базы, частичной разницы и сообщения о синхронизации
|
||
|
||
### Requirement: Account overview has an independent current-month income summary
|
||
|
||
Обзор выбранного Brokerage Account MUST показывать компактную
|
||
summary «Получено купонами и дивидендами» за период от первого дня текущего
|
||
месяца по Europe/Moscow до сегодняшнего дня включительно и действие перехода
|
||
на страницу «Пассивный доход». Summary MUST всегда использовать этот current-month
|
||
период и MUST NOT наследовать последний пользовательский период страницы аналитики.
|
||
Пустой или неуспешный расчёт MUST оставаться локальным состоянием блока и
|
||
MUST NOT заменять успешно отображённый Portfolio или другие блоки обзора.
|
||
Временная ошибка обновления MUST сохранять доступную сводку той же выборки без
|
||
предупреждения и ручного повтора, пока автоматические повторы не исчерпаны. При отсутствии результата SHALL показываться
|
||
нейтральная недоступность после первичного ожидания с автоматическим восстановлением.
|
||
|
||
#### Scenario: Обзор показывает доход текущего месяца
|
||
- **WHEN** обзор Brokerage Account загружен и current-month analytics успешно рассчитана
|
||
- **THEN** он показывает итог в RUB с начала текущего месяца по сегодняшний день и переход к полной аналитике
|
||
|
||
#### Scenario: Пользовательский период не влияет на обзор
|
||
- **WHEN** User ранее применил на странице аналитики период, отличный от текущего месяца
|
||
- **THEN** summary обзора по-прежнему рассчитывается от первого дня текущего месяца по сегодняшний день
|
||
|
||
#### Scenario: Current-month summary недоступен
|
||
- **WHEN** Portfolio обзора успешно загружен, но расчёт current-month summary завершается временной ошибкой
|
||
- **THEN** Portfolio и остальные блоки остаются доступными, а summary сохраняет предыдущий результат той же выборки либо показывает нейтральную недоступность без ручного повтора
|
||
|
||
### Requirement: Income payments remain traceable to exact Ledger Entries
|
||
|
||
В разделе «Выплаты» страница MUST показывать единый список карточек точных исполненных Ledger Entries применённого периода в исходном серверном порядке entries. Каждая выплата MUST иметь собственную сразу видимую карточку с описанием, датой исполнения, доступной identity Instrument и signed Money в RUB. Купоны и дивиденды MUST использовать то же представление выплаты, что и на экранах журнала, прогноза и графиков инструментов, при этом карточка MUST обозначать эту сумму именно как фактически исполненную, а не прогнозную. В режиме одного Brokerage Account страница MUST NOT добавлять внешние группировочные карточки или заголовки по Instrument, subtotals инструмента или количество выплат инструмента. В режиме «Все счета» записи раздела «Выплаты» MUST группироваться по тем же правилам ключа, состава, порядка и итога, что и записи журнала по Requirements «All-accounts ledger groups same-day entries of one instrument», «All-accounts ledger group shows each entry and an exact total» и «All-accounts ledger grouping preserves order and limits» capability `brokerage-account-ledger`. Каждая запись при этом MUST оставаться отдельной сразу видимой карточкой внутри группы, а итог группы MUST NOT показываться, пока полнота периода не подтверждена; receipt count MUST NOT добавляться. Список MUST сохранять отрицательные корректировки и повторяющиеся operation IDs как отдельные записи и MUST NOT включать записи вне аналитического итога. Backend MUST обеспечивать согласованность entries, instrumentKey и receipt count при формировании ответа из одного набора записей. Frontend SHALL доверять типизированному contract без второго runtime consistency validator и MUST NOT пересчитывать аналитические денежные итоги. Единственное исключение — итог визуальной группы режима «Все счета»: это точная сумма показанных записей группы в RUB, и он MUST NOT подменять аналитические итоги.
|
||
|
||
#### Scenario: Каждая выплата показана отдельной карточкой
|
||
|
||
- **WHEN** в режиме одного Brokerage Account выбранный период содержит несколько включённых Ledger Entries одного или разных Instrument
|
||
- **THEN** раздел «Выплаты» сразу показывает отдельную карточку каждой записи в исходном порядке без внешних группировочных блоков, раскрытия, итогов инструмента и receipt count на UI
|
||
|
||
#### Scenario: Список воспроизводит точный состав дохода
|
||
|
||
- **WHEN** User просматривает загруженный раздел «Выплаты»
|
||
- **THEN** он видит все и только включённые coupon и dividend Ledger Entries применённого периода с датой, описанием, доступной identity и исходной суммой, обозначенной как исполненная выплата
|
||
|
||
#### Scenario: Отрицательная корректировка и повторяющиеся IDs сохраняются
|
||
|
||
- **WHEN** доход содержит отрицательную корректировку либо несколько включённых entries с одинаковым operation ID
|
||
- **THEN** каждая запись остаётся отдельной карточкой, а корректировка отображается с исходным знаком
|
||
|
||
#### Scenario: Выплаты одной бумаги на двух счетах в режиме «Все счета»
|
||
|
||
- **WHEN** в режиме «Все счета» за один день получены купон по облигации на брокерском счёте +200 RUB и купон по той же облигации на ИИС +100 RUB
|
||
- **THEN** раздел «Выплаты» показывает их одной группой с итогом +300 RUB, внутри которой видны отдельные карточки каждого счёта, а аналитические итоги страницы не меняются
|
||
|
||
### Requirement: Analytics summary remains readable
|
||
|
||
Страница MUST обозначать фактический результат как «Получено купонами и дивидендами» и показывать total, coupon subtotal и dividend subtotal выбранного периода в RUB. Она MUST объяснять, что tax operations не включены, не называя фактический результат gross, net, суммой до налогов, суммой после налогов или налоговым расчётом. Значения MUST оставаться доступными как читаемый текст, не только как графика, цвет или положение. Страница MUST NOT показывать блок «Динамика», его график или текстовый список временных buckets независимо от длительности выбранного периода. Страница MUST NOT показывать блоки «Предыдущий период» и «Годом ранее» или сообщения о недоступности сравнений.
|
||
|
||
#### Scenario: Итоги дохода доступны как текст
|
||
|
||
- **WHEN** выбранный период содержит исполненные купонные или дивидендные выплаты
|
||
- **THEN** страница показывает читаемые total, coupon subtotal и dividend subtotal в RUB с обозначением фактически полученного дохода и объяснением исключения tax operations
|
||
|
||
#### Scenario: Динамика отсутствует для периода любой длительности
|
||
|
||
- **WHEN** User открывает аналитику или применяет допустимый период любой длительности
|
||
- **THEN** страница показывает итоги и выплаты без динамики, сравнений периодов и состояний их доступности
|
||
|
||
### Requirement: Income page excludes upcoming forecasts
|
||
|
||
Страница «Пассивный доход» MUST NOT показывать блок «Ближайший прогноз», его суммы, список будущих выплат, состояния загрузки, пустоты или ошибки. Forecast values MUST NOT входить в realized total, trend buckets или instrument breakdown. Доступность данных Future Cash Events MUST NOT влиять на отображение успешно загруженной realized analytics. Существующая самостоятельная страница «Будущие события» и её доступность из выбранного Brokerage Account MUST сохраняться.
|
||
|
||
#### Scenario: Известные будущие выплаты не появляются на странице дохода
|
||
|
||
- **WHEN** для выбранного Brokerage Account известны будущие coupon или dividend события с суммой или без неё либо maturity или offer события
|
||
- **THEN** страница дохода не показывает блок «Ближайший прогноз» и не включает эти будущие события в фактически полученный доход
|
||
|
||
#### Scenario: Изменение исторического периода не возвращает прогноз
|
||
|
||
- **WHEN** User применяет допустимый исторический период или сбрасывает фильтры
|
||
- **THEN** страница показывает соответствующую realized analytics без блока «Ближайший прогноз»
|
||
|
||
#### Scenario: Данные будущих событий недоступны
|
||
|
||
- **WHEN** realized analytics успешно загружена, а данные Future Cash Events недоступны
|
||
- **THEN** итоги и карточки выплат остаются доступными, а страница дохода не показывает состояние ошибки или загрузки прогноза
|
||
|
||
#### Scenario: Самостоятельная страница будущих событий сохраняется
|
||
|
||
- **WHEN** User переходит из выбранного Brokerage Account на существующую страницу «Будущие события»
|
||
- **THEN** эта страница остаётся доступной с прежним поведением независимо от отсутствия прогноза на странице дохода
|
||
|
||
### Requirement: Доход обновляется без технических сообщений
|
||
|
||
Страница дохода MUST сохранять применённый период, черновик фильтров и
|
||
доступный результат при фоновом обновлении. Временная ошибка SHALL
|
||
восстанавливаться автоматически без кнопок обновления и повтора, времени
|
||
получения данных и уведомлений об их загрузке. Изменение периода MUST NOT
|
||
подписывать прежний результат новыми датами. Ошибки пользовательского периода
|
||
MUST оставаться у полей и MUST NOT запускать автоматический повтор ошибочного
|
||
ввода. Подтверждённая потеря доступа MUST скрывать личные результаты.
|
||
|
||
#### Scenario: Доход обновляется при редактировании периода
|
||
- **WHEN** пользователь редактирует черновик дат, а фоновое обновление применённого периода завершилось
|
||
- **THEN** черновик и фокус сохраняются, результат относится к прежнему применённому периоду и техническое уведомление не появляется
|
||
|
||
#### Scenario: Новый период пока недоступен
|
||
- **WHEN** пользователь применил другой допустимый период, но его расчёт ещё не получен
|
||
- **THEN** страница показывает ожидание или недоступность нового результата, не выдаёт прежние суммы за доход нового периода и восстанавливается автоматически
|