chore: bring develop into release sync branch (WealthLedger/main#30) #44

Merged
codex-bot merged 10 commits from develop into chore/30-sync-release 2026-10-16 07:41:05 +03:00
37 changed files with 191 additions and 45 deletions
Showing only changes of commit 4b2806ead5 - Show all commits
@@ -1,6 +1,8 @@
# Delta for Brokerage Account Upcoming Events
## Requirement: User can filter upcoming events by a forecast date range
## ADDED Requirements
### Requirement: User can filter upcoming events by a forecast date range
The events page MUST retain its URL-backed date-range filter. When the URL has
no `from` or `to` parameters, the applied range MUST include the current date
@@ -53,30 +53,6 @@
- **WHEN** выбранный период содержит подходящие выплаты в нескольких исходных валютах
- **THEN** система возвращает отдельные итоги по каждой валюте без сконвертированного grand total
### Requirement: Period comparisons preserve calendar and currency meaning
Система MUST сопоставлять выбранный период с непосредственно предшествующим периодом. Для полного календарного месяца предыдущим периодом MUST быть предыдущий полный календарный месяц; для любого другого периода предыдущий период MUST иметь ту же включительную продолжительность и непосредственно предшествовать выбранному. Когда соответствующие календарные даты годом ранее доступны, система MUST также показывать year-over-year comparison; когда они недоступны, система MUST обозначать сравнение как недоступное, а не подменять его нулём. Каждое сравнение MUST показывать абсолютные coupon, dividend и total значения и их денежные разницы отдельно по исходным валютам и MUST NOT характеризовать их как инвестиционную эффективность.
#### Scenario: Сравнение полного календарного месяца
- **WHEN** User применяет период от первого до последнего дня календарного месяца
- **THEN** система сравнивает его с полным непосредственно предшествующим календарным месяцем
#### Scenario: Сравнение произвольного периода
- **WHEN** User применяет период, не являющийся полным календарным месяцем
- **THEN** система сравнивает его с непосредственно предшествующим включительным периодом той же продолжительности
#### Scenario: Доступно сравнение год к году
- **WHEN** для соответствующих календарных дат годом ранее доступна полная история подходящих операций
- **THEN** система показывает отдельные source-currency значения и денежные разницы относительно этого периода
#### Scenario: Годовая история недоступна
- **WHEN** полная история для соответствующих календарных дат годом ранее недоступна
- **THEN** система обозначает year-over-year comparison как недоступное и не показывает отсутствующие данные как нулевой доход
### 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, а не из глобальной навигации.
@@ -84,7 +60,7 @@
#### Scenario: Черновик периода не применён
- **WHEN** User изменяет черновик `from` или `to`, но не применяет его
- **THEN** видимые итоги, сравнения и карточки выплат остаются основанными на последнем применённом периоде
- **THEN** видимые итоги и карточки выплат остаются основанными на последнем применённом периоде
#### Scenario: Выбор дат в верхнем блоке
@@ -118,12 +94,12 @@
### Requirement: Realized analytics never presents an incomplete result as complete
Система MUST формировать выбранный результат, его comparisons, trend buckets, instrument breakdown и drill-down только после успешного получения всех необходимых cursor pages исполненных операций. Ошибка любой необходимой страницы MUST завершать расчёт восстанавливаемой ошибкой и MUST NOT возвращать частичные totals, comparisons, buckets или breakdown как полный результат. Успешный период без подходящих выплат MUST возвращать нулевой результат и отображаться с явным объяснением пустого состояния.
Система MUST формировать выбранный результат, его trend buckets, instrument breakdown и drill-down только после успешного получения всех необходимых cursor pages исполненных операций выбранного периода. Ошибка любой необходимой страницы MUST завершать расчёт восстанавливаемой ошибкой и MUST NOT возвращать частичные totals, buckets или breakdown как полный результат. Успешный период без подходящих выплат MUST возвращать нулевой результат и отображаться с явным объяснением пустого состояния. Система MUST NOT загружать другие периоды ради сравнения полученного дохода с выбранным периодом.
#### Scenario: Все страницы операций успешно получены
- **WHEN** для расчёта требуется несколько cursor pages и каждая страница успешно получена
- **THEN** система формирует результат по всем подходящим Ledger Entries со всех страниц
- **THEN** система формирует результат по всем подходящим Ledger Entries со всех страниц выбранного периода без расчёта сравнений с другими периодами
#### Scenario: Одна из страниц операций недоступна
@@ -156,7 +132,7 @@
### Requirement: Income payments remain traceable to exact Ledger Entries
В разделе «Выплаты» страница MUST показывать единый список карточек точных исполненных Ledger Entries применённого периода в исходном серверном порядке entries. Каждая выплата MUST иметь собственную сразу видимую карточку с описанием, датой, доступной identity Instrument и signed Money в исходной валюте. Страница MUST NOT добавлять внешние группировочные карточки или заголовки по Instrument либо валюте, subtotals инструмента или количество выплат инструмента. Список MUST сохранять отрицательные корректировки и повторяющиеся operation IDs как отдельные записи и MUST NOT включать записи вне аналитического итога. Backend MUST обеспечивать согласованность entries, instrumentKey, валюты и receipt count при формировании ответа из одного набора записей. Frontend SHALL доверять типизированному contract без второго runtime consistency validator и MUST NOT пересчитывать денежные итоги.
В разделе «Выплаты» страница MUST показывать единый список карточек точных исполненных Ledger Entries применённого периода в исходном серверном порядке entries. Каждая выплата MUST иметь собственную сразу видимую карточку с описанием, датой исполнения, доступной identity Instrument и signed Money в исходной валюте. Купоны и дивиденды MUST использовать то же представление выплаты, что и на экранах журнала, прогноза и графиков инструментов, при этом карточка MUST обозначать эту сумму именно как фактически исполненную, а не прогнозную. Страница MUST NOT добавлять внешние группировочные карточки или заголовки по Instrument либо валюте, subtotals инструмента или количество выплат инструмента. Список MUST сохранять отрицательные корректировки и повторяющиеся operation IDs как отдельные записи и MUST NOT включать записи вне аналитического итога. Backend MUST обеспечивать согласованность entries, instrumentKey, валюты и receipt count при формировании ответа из одного набора записей. Frontend SHALL доверять типизированному contract без второго runtime consistency validator и MUST NOT пересчитывать денежные итоги.
#### Scenario: Каждая выплата показана отдельной карточкой
@@ -166,7 +142,7 @@
#### Scenario: Список воспроизводит точный состав дохода
- **WHEN** User просматривает загруженный раздел «Выплаты»
- **THEN** он видит все и только включённые coupon и dividend Ledger Entries применённого периода с датой, описанием, доступной identity и исходной суммой
- **THEN** он видит все и только включённые coupon и dividend Ledger Entries применённого периода с датой, описанием, доступной identity и исходной суммой, обозначенной как исполненная выплата
#### Scenario: Отрицательная корректировка и повторяющиеся IDs сохраняются
@@ -180,7 +156,7 @@
### Requirement: Analytics summary remains readable per currency
Страница MUST обозначать фактический результат как «Получено купонами и дивидендами» и для каждой исходной валюты показывать total, coupon subtotal и dividend subtotal выбранного периода. Она MUST объяснять, что tax operations не включены, не называя фактический результат gross, net, суммой до налогов, суммой после налогов или налоговым расчётом. Значения MUST оставаться доступными как читаемый текст, не только как графика, цвет или положение. Страница MUST NOT показывать блок «Динамика», его график, переключатель валюты графика или текстовый список временных buckets независимо от длительности выбранного периода. Удаление блока MUST NOT изменять итоги или сравнения периодов.
Страница MUST обозначать фактический результат как «Получено купонами и дивидендами» и для каждой исходной валюты показывать total, coupon subtotal и dividend subtotal выбранного периода. Она MUST объяснять, что tax operations не включены, не называя фактический результат gross, net, суммой до налогов, суммой после налогов или налоговым расчётом. Значения MUST оставаться доступными как читаемый текст, не только как графика, цвет или положение. Страница MUST NOT показывать блок «Динамика», его график, переключатель валюты графика или текстовый список временных buckets независимо от длительности выбранного периода. Страница MUST NOT показывать блоки «Предыдущий период» и «Годом ранее» или сообщения о недоступности сравнений.
#### Scenario: Итоги дохода доступны как текст
@@ -195,11 +171,11 @@
#### Scenario: Динамика отсутствует для периода любой длительности
- **WHEN** User открывает аналитику или применяет допустимый период любой длительности
- **THEN** страница показывает итоги и существующие сравнения, но не показывает блок «Динамика», график, его переключатель валюты или текстовый список временных buckets
- **THEN** страница показывает итоги и выплаты без динамики, сравнений периодов и состояний их доступности
### Requirement: Income page excludes upcoming forecasts
Страница «Пассивный доход» MUST NOT показывать блок «Ближайший прогноз», его суммы, список будущих выплат, состояния загрузки, пустоты или ошибки. Forecast values MUST NOT входить в realized total, comparisons, trend buckets или instrument breakdown. Доступность данных Future Cash Events MUST NOT влиять на отображение успешно загруженной realized analytics. Существующая самостоятельная страница «Будущие события» и её доступность из выбранного Brokerage Account MUST сохраняться.
Страница «Пассивный доход» MUST NOT показывать блок «Ближайший прогноз», его суммы, список будущих выплат, состояния загрузки, пустоты или ошибки. Forecast values MUST NOT входить в realized total, trend buckets или instrument breakdown. Доступность данных Future Cash Events MUST NOT влиять на отображение успешно загруженной realized analytics. Существующая самостоятельная страница «Будущие события» и её доступность из выбранного Brokerage Account MUST сохраняться.
#### Scenario: Известные будущие выплаты не появляются на странице дохода
@@ -214,7 +190,7 @@
#### Scenario: Данные будущих событий недоступны
- **WHEN** realized analytics успешно загружена, а данные Future Cash Events недоступны
- **THEN** итоги, сравнения и карточки выплат остаются доступными, а страница дохода не показывает состояние ошибки или загрузки прогноза
- **THEN** итоги и карточки выплат остаются доступными, а страница дохода не показывает состояние ошибки или загрузки прогноза
#### Scenario: Самостоятельная страница будущих событий сохраняется
@@ -48,49 +48,60 @@
- **THEN** запись журнала передаёт её вместе с доступными идентификаторами инструмента, не меняя остальные сведения операции
### Requirement: Account overview includes recent ledger events
Обзор счёта MUST показывать пять последних исполненных записей журнала этого счёта и действие перехода к его полному журналу. Каждая показанная запись MUST иметь ту же визуальную идентичность инструмента и семантику денежного движения, что и запись полного журнала. Неуспешный или пустой запрос последних записей MUST оставаться локальным состоянием блока последних событий и MUST NOT заменять успешно отображённый обзор Portfolio.
Обзор счёта MUST показывать пять последних исполненных записей журнала этого счёта и действие перехода к его полному журналу. Каждая показанная запись MUST иметь ту же визуальную идентичность инструмента и семантику денежного движения, что и запись полного журнала. Исполненные купоны и дивиденды MUST иметь то же представление выплаты, что и на других экранах с купонами и дивидендами; остальные операции сохраняют представление записи журнала. Неуспешный или пустой запрос последних записей MUST оставаться локальным состоянием блока последних событий и MUST NOT заменять успешно отображённый обзор Portfolio.
#### Scenario: Отображение последних событий
- **WHEN** загружается обзор счёта и его журнал содержит исполненные операции
- **THEN** обзор показывает не более пяти последних записей и ссылку на полный журнал
- **THEN** обзор показывает не более пяти последних записей и ссылку на полный журнал, а купоны и дивиденды узнаваемы по тому же представлению выплаты
#### Scenario: Последние записи недоступны
- **WHEN** обзор счёта успешно загружается, но запрос последних записей завершается ошибкой
- **THEN** обзор Portfolio остаётся доступным, а блок последних событий отображает собственное состояние восстановления
#### Scenario: Последняя запись не содержит visual metadata инструмента
- **WHEN** одна из последних записей не имеет доступной presentation metadata инструмента
- **THEN** обзор показывает для неё понятный текстовый fallback и сохраняет дату, тип, инструмент и денежное изменение записи доступными для чтения
### Requirement: Full ledger presents compact rows
Страница полного журнала MUST показывать для каждой записи дату, тип операции, инструмент и денежное изменение в исходной валюте в компактной структурированной строке. При доступной presentation metadata инструмента строка MUST показывать его визуальную идентичность; при её отсутствии MUST использовать понятный текстовый fallback. Для каждой записи денежное изменение в исходной валюте MUST сопровождаться подписью конкретного типа события и знаком: для купонной операции — «Купон», для дивидендной — «Дивиденд», для остальных записей — подпись их категории («Сделка», «Налог», «Комиссия», «Перевод», «Прочее»). Строка MUST NOT повторять подпись типа события во вторичной метадате записи. Знак «+» / «−» и семантический цвет MUST оставаться носителями направления и MUST NOT заменяться только текстом. Страница MUST обозначать записи без движения денег нейтрально и MUST NOT показывать цену или количество.
Страница полного журнала MUST показывать для каждой записи дату, тип операции, инструмент и денежное изменение в исходной валюте в компактной структурированной строке. При доступной presentation metadata инструмента строка MUST показывать его визуальную идентичность; при её отсутствии MUST использовать понятный текстовый fallback. Для купонных и дивидендных операций на странице полного журнала и в личной истории инструмента MUST использоваться то же представление выплаты, что в доходе, прогнозе и графиках инструментов, с обозначением исполненного движения. Для каждой записи денежное изменение в исходной валюте MUST сопровождаться подписью конкретного типа события и знаком: для купонной операции — «Купон», для дивидендной — «Дивиденд», для остальных записей — подпись их категории («Сделка», «Налог», «Комиссия», «Перевод», «Прочее»). Строка MUST NOT повторять подпись типа события во вторичной метадате записи. Знак «+» / «−» и семантический цвет MUST оставаться носителями направления и MUST NOT заменяться только текстом. Страница MUST обозначать записи без движения денег нейтрально и MUST NOT показывать цену или количество.
#### Scenario: Отображение записи только с инструментом
- **WHEN** полный журнал отображает запись без денежной выплаты
- **THEN** страница определяет её как запись без движения денег
#### Scenario: Отображение поступления
- **WHEN** полный журнал отображает запись с положительной денежной выплатой
- **THEN** строка явно показывает поступление знаком и подписью конкретного типа события («Купон», «Дивиденд» или категории) и его денежное изменение в исходной валюте, не полагаясь только на цвет
#### Scenario: Отображение списания
- **WHEN** полный журнал отображает запись с отрицательной денежной выплатой
- **THEN** строка явно показывает списание знаком и подписью конкретного типа события («Купон», «Дивиденд» или категории) и его денежное изменение в исходной валюте, не полагаясь только на цвет
#### Scenario: Отображение купона
- **WHEN** полный журнал отображает запись с upstream-типом купона
- **THEN** строка показывает подпись «Купон»
- **WHEN** полный журнал или личная история инструмента отображает исполненную операцию с upstream-типом купона
- **THEN** запись показывает подпись «Купон» и исполненную сумму в том же представлении выплаты, что на других экранах
#### Scenario: Отображение дивиденда
- **WHEN** полный журнал отображает запись с upstream-типом дивиденда
- **THEN** строка показывает подпись «Дивиденд»
- **WHEN** полный журнал или личная история инструмента отображает исполненную операцию с upstream-типом дивиденда
- **THEN** запись показывает подпись «Дивиденд» и исполненную сумму в том же представлении выплаты, что на других экранах
#### Scenario: Подпись типа не дублируется
- **WHEN** полный журнал отображает запись с типом события и вторичной метадатой
- **THEN** подпись типа события присутствует в строке один раз и не повторяется во вторичной метадате записи
#### Scenario: Отсутствует visual metadata инструмента
- **WHEN** полный журнал отображает запись без доступной presentation metadata инструмента
- **THEN** строка показывает понятный текстовый fallback без подмены или сокрытия доступных данных инструмента
@@ -84,43 +84,97 @@ Instrument в Future Cash Event MUST иметь тот же доменный с
- **THEN** система трактует ответ как отсутствие данных по инструменту и не завершает обновление ошибкой
### Requirement: Account page presents an upcoming events list
Страница Brokerage Account MUST предоставлять read-only действие перехода на отдельную страницу «Будущие события». Эта страница MUST показывать события сохранённого полного прогноза компактным списком без фильтров, пагинации, суммарного итога или действия ручной синхронизации, с датой, типом, инструментом и прогнозной суммой в исходной валюте, если она известна. Страница MUST показывать `calculatedAt`, исходное окно прогноза и наблюдаемые freshness, полноту, время последнего успешного прогноза, последнюю часовую попытку и доступность из `meta` понятным текстом или иконкой с доступным именем, а не только цветом. Она MUST объяснять, что прогноз рассчитан по количеству бумаг на `calculatedAt`, а показанные события дополнительно ограничены запрошенным включительным UTC-окном; прогноз указан до налогов и может быть неполным из-за доступности данных источника. Открытие страницы и повтор HTTP-чтения MUST NOT запускать личную синхронизацию.
Страница Brokerage Account MUST предоставлять read-only действие перехода на отдельную страницу «Будущие события». Эта страница MUST показывать события сохранённого полного прогноза компактным списком без фильтров, пагинации, суммарного итога или действия ручной синхронизации, с датой, типом, инструментом и прогнозной суммой в исходной валюте, если она известна. Купоны и дивиденды MUST использовать то же представление выплаты, что и исполненные купоны, дивиденды и события графика инструмента, но MUST быть явно обозначены как прогноз, а не полученные деньги. Дата дивиденда MUST быть обозначена как дата реестра. Страница MUST показывать `calculatedAt`, исходное окно прогноза и наблюдаемые freshness, полноту, время последнего успешного прогноза, последнюю часовую попытку и доступность из `meta` понятным текстом или иконкой с доступным именем, а не только цветом. Она MUST объяснять, что прогноз рассчитан по количеству бумаг на `calculatedAt`, а показанные события дополнительно ограничены запрошенным включительным UTC-окном; прогноз указан до налогов и может быть неполным из-за доступности данных источника. Открытие страницы и повтор HTTP-чтения MUST NOT запускать личную синхронизацию.
#### Scenario: Просмотр ближайших событий
- **WHEN** User открывает «Будущие события» из обзора Brokerage Account
- **THEN** он видит отсортированный read-only список событий сохранённого 30-календарного окна и `calculatedAt`, где offer обозначен без суммы
- **THEN** он видит отсортированный read-only список событий сохранённого 30-календарного окна и `calculatedAt`, где купоны и дивиденды представлены как прогнозные выплаты, а offer обозначен без суммы
#### Scenario: Прогнозный дивиденд с неизвестной суммой
- **WHEN** прогноз содержит дивиденд с известной датой реестра, но без суммы
- **THEN** его карточка обозначает дату как дату реестра и сумму как неизвестную, не показывает нулевую или полученную выплату
#### Scenario: Пустое состояние страницы
- **WHEN** запрос будущих событий успешно возвращает пустой список
- **THEN** страница сообщает, что в сохранённом окне прогноза событий не найдено, показывает `calculatedAt` и объясняет ограничения покрытия источника
#### Scenario: Первая загрузка либо ошибка без сохранённого прогноза
- **WHEN** запрос будущих событий возвращает `data: null`, а `meta` обозначает выполнение первой загрузки либо её неуспех
- **THEN** страница показывает доступное состояние загрузки либо ошибки без частичного списка и предоставляет при ошибке действие повторить только HTTP-чтение
#### Scenario: Ошибка страницы
- **WHEN** запрос будущих событий завершается ошибкой
- **THEN** страница не показывает частичный список и предоставляет действие повторить только HTTP-чтение
#### Scenario: Ошибка обновления при сохранённом прогнозе на странице
- **WHEN** запрос будущих событий возвращает последний полный прогноз вместе с ошибкой его обновления
- **THEN** страница показывает этот прогноз с его исходными `calculatedAt` и окном, отдельно сообщает об ошибке обновления и предоставляет действие повторить только HTTP-чтение
### Requirement: Account overview includes upcoming events
Обзор Brokerage Account MUST показывать не более пяти ближайших Future Cash Event сохранённого полного прогноза в применённом запрошенном окне, его `calculatedAt`, исходное окно, наблюдаемую freshness, полноту, последнюю часовую попытку и доступность из `meta`, а также действие перехода на read-only полную страницу «Будущие события». Статус MUST быть понятен текстом или иконкой с доступным именем, а не только цветом. Неуспешное обновление при сохранённом прогнозе MUST оставаться локальным состоянием блока, сохранять показанные события и их исходное окно и MUST NOT заменять успешно отображённый Portfolio. Если полного прогноза нет, неуспешное обновление MUST оставаться локальным состоянием блока без частичного списка. Обзор MUST NOT предоставлять действие ручной синхронизации; его повтор MUST выполнять только HTTP-чтение.
Обзор Brokerage Account MUST показывать не более пяти ближайших Future Cash Event сохранённого полного прогноза в применённом запрошенном окне, его `calculatedAt`, исходное окно, наблюдаемую freshness, полноту, последнюю часовую попытку и доступность из `meta`, а также действие перехода на read-only полную страницу «Будущие события». Купоны и дивиденды MUST быть представлены той же карточкой выплаты, что на полной странице событий и остальных экранах, с явным обозначением прогнозного статуса; погашение и оферта сохраняют своё значение. Статус MUST быть понятен текстом или иконкой с доступным именем, а не только цветом. Неуспешное обновление при сохранённом прогнозе MUST оставаться локальным состоянием блока, сохранять показанные события и их исходное окно и MUST NOT заменять успешно отображённый Portfolio. Если полного прогноза нет, неуспешное обновление MUST оставаться локальным состоянием блока без частичного списка. Обзор MUST NOT предоставлять действие ручной синхронизации; его повтор MUST выполнять только HTTP-чтение.
#### Scenario: Отображение ближайших событий
- **WHEN** обзор счёта успешно загружен и сохранённый прогноз содержит будущие события
- **THEN** обзор показывает первые пять событий в порядке даты, `calculatedAt` и действие перехода к полной странице
- **THEN** обзор показывает первые пять событий в порядке даты, `calculatedAt` и действие перехода к полной странице, используя единое представление для прогнозных купонов и дивидендов
#### Scenario: Ближайшие события недоступны без сохранённого прогноза
- **WHEN** обзор счёта успешно загружен, а события возвращают `data: null` и `meta` обозначает выполнение первой загрузки либо её неуспех
- **THEN** Portfolio остаётся доступным, а блок будущих событий отображает собственное доступное состояние загрузки либо ошибки без частичного списка
#### Scenario: Ближайшие события недоступны
- **WHEN** обзор счёта успешно загружен, но запрос будущих событий завершается ошибкой
- **THEN** Portfolio остаётся доступным, а блок будущих событий отображает собственную ошибку
#### Scenario: Неуспешное обновление не скрывает сохранённый прогноз
- **WHEN** обзор счёта получает сохранённый полный прогноз вместе с ошибкой его обновления
- **THEN** Portfolio и события сохранённого окна остаются доступными, а блок отдельно обозначает ошибку и предлагает повторить только HTTP-чтение
### Requirement: User can filter upcoming events by a forecast date range
The events page MUST retain its URL-backed date-range filter. When the URL has
no `from` or `to` parameters, the applied range MUST include the current date
and the following six calendar dates. The page MUST allow selecting today and
future dates in its date picker and MUST prevent selecting dates before today.
The date-picker restriction MUST NOT invalidate or rewrite a valid past date
range supplied directly in the URL. A malformed date, impossible calendar date,
or reversed range MUST be normalized to the default range, removing invalid
`from` and `to` parameters from the URL. Applying and resetting the filter MUST
continue to use the existing URL-backed period behavior.
#### Scenario: Default range starts today and spans seven calendar dates
- **WHEN** the user opens the events page without `from` or `to` parameters
- **THEN** the applied range starts on the current calendar date
- **AND** the inclusive end date is six calendar dates after the start date
#### Scenario: Future dates can be selected and past dates cannot
- **WHEN** the user opens the date picker
- **THEN** today and later dates are selectable
- **AND** dates before today are not selectable
#### Scenario: Valid past URL range is preserved
- **WHEN** the user opens the events page with valid `from` and `to` dates in
the past and `from` is not after `to`
- **THEN** the page applies that URL range without clamping or replacing it
#### Scenario: Invalid URL range falls back to the default
- **WHEN** the URL contains a malformed date, an impossible calendar date, or
a range whose `from` date is after its `to` date
- **THEN** the page applies the default seven-calendar-date range
- **AND** invalid `from` and `to` parameters are removed from the URL
@@ -0,0 +1,26 @@
# instrument-income-schedules Specification
## Purpose
Позволяет отличать опубликованные календарные сведения о купонах и дивидендах
инструмента от фактически полученных личных выплат и прогноза по позиции.
## Requirements
### Requirement: Instrument payout schedules do not imply executed income
На страницах акций и облигаций известные будущие, прошедшие и недатированные события графика дивидендов или купонов MUST использовать то же представление выплаты, что и купоны/дивиденды других экранов, но MUST обозначать их как события графика, не подтверждающие фактическое зачисление. Доступные дата реестра, дата выплаты, сумма на одну бумагу и отдельная личная оценка MUST сохранять свои подписи и смысл; неизвестная сумма или дата MUST NOT подменяться нулём или вымышленной датой. Исполненная личная операция MUST оставаться отличимой от события графика, даже когда их даты совпадают.
#### Scenario: Будущий купон с известными условиями
- **WHEN** User просматривает будущий купон облигации с известными датой выплаты, суммой на облигацию и оценкой для позиции
- **THEN** единое представление показывает купон как событие графика, сохраняет обе разные суммы и дату и не называет оценку полученными деньгами
#### Scenario: Прошедший дивиденд без подтверждённой операции
- **WHEN** User просматривает в истории акции событие дивидендного графика с прошедшей датой реестра
- **THEN** представление показывает его как событие графика с суммой на акцию, а не как исполненную выплату пользователю
#### Scenario: Неизвестные условия графика
- **WHEN** в купонном или дивидендном графике отсутствует дата выплаты либо денежное значение
- **THEN** представление сохраняет известные сведения и явно различает неизвестное значение и ноль без вымышленной даты зачисления
+77
View File
@@ -0,0 +1,77 @@
# stock-screener Specification
## Purpose
Определяет поиск доступных к покупке акций по характеристикам всего доступного каталога, просмотр совпавших выпусков и навигацию по результату поиска.
## Requirements
### Requirement: Скринер ищет среди доступных к покупке акций
Система SHALL по нажатию «Найти» искать по всему доступному на момент запроса каталогу акций, не ограничивая исходный набор первыми совпадениями текстового поиска. В результаты MUST включаться только акции, для которых в полученных сведениях подтверждены и доступность покупки, и возможность торговли через API. Скринер MUST показывать время получения списка и MUST NOT представлять эти признаки как гарантию, что конкретный пользователь сможет исполнить заявку прямо сейчас.
#### Scenario: Поиск без названия или тикера
- **WHEN** пользователь не ввёл текст и нажал «Найти»
- **THEN** система ищет по доступному каталогу и показывает выпуски, удовлетворяющие условиям и обоим признакам доступности
#### Scenario: Акция недоступна для покупки
- **WHEN** у акции отсутствует подтверждение доступности покупки или торговли через API
- **THEN** система не включает её в результаты скринера независимо от остальных совпавших условий
### Requirement: Условия скринера применяются совместно
Система SHALL позволять независимо задавать необязательные условия по сектору, типу акции, валюте расчётов, площадке, стране риска и максимальному количеству бумаг в лоте, а также необязательный текстовый запрос по названию или тикеру. После нажатия «Найти» система SHALL возвращать только акции, удовлетворяющие всем заданным условиям. Отсутствующее или неизвестное значение выбранного условия MUST NOT считаться совпадением.
#### Scenario: Несколько условий без текста
- **WHEN** пользователь выбрал сектор и валюту, оставил текст пустым и нажал «Найти»
- **THEN** в результате остаются только доступные к покупке акции выбранного сектора и валюты
#### Scenario: Текст совместно с характеристиками
- **WHEN** пользователь ввёл название или тикер, выбрал тип акции и нажал «Найти»
- **THEN** система отбирает совпадения по тексту и типу среди доступного каталога до разделения результата на страницы
#### Scenario: Неизвестный сектор
- **WHEN** пользователь выбрал сектор, а у акции сектор не указан
- **THEN** акция не считается совпадением
#### Scenario: Граница максимального лота
- **WHEN** задано максимальное количество бумаг в лоте
- **THEN** акции с известным размером лота не больше указанного включаются при соблюдении остальных условий, а акции с неизвестным или большим лотом исключаются
### Requirement: Результат представляет отдельные выпуски
Система SHALL показывать не более одной карточки для каждого выпуска с одинаковым ISIN; акции с разными ISIN SHALL оставаться отдельными результатами. При отсутствии ISIN отдельным результатом SHALL быть инструмент с собственным идентификатором. Представитель выпуска MUST выбираться среди инструментов, удовлетворяющих заданным условиям, а карточка MUST вести к выбранному инструменту.
#### Scenario: Несколько подходящих режимов одного выпуска
- **WHEN** несколько инструментов с одним ISIN удовлетворяют всем условиям
- **THEN** система показывает одну карточку выпуска со ссылкой на один из этих инструментов
#### Scenario: Один режим не соответствует условиям
- **WHEN** два инструмента имеют один ISIN, но только один удовлетворяет всем условиям
- **THEN** карточка выпуска ведёт к соответствующему условиям инструменту
#### Scenario: Разные выпуски одного эмитента
- **WHEN** обыкновенная и привилегированная акции имеют разные ISIN и обе удовлетворяют условиям
- **THEN** система показывает их как отдельные результаты
### Requirement: Страницы относятся к одному результату поиска
Система SHALL упорядочивать совпадения по тикеру, затем по идентификатору инструмента, и показывать не более 30 выпусков на странице. Переход между страницами одного поиска MUST сохранять состав и порядок полученного результата без пропусков и повторов. Изменение полей без нажатия «Найти» MUST NOT заменять текущий результат; повторное нажатие «Найти», в том числе без изменения условий, SHALL запускать новый поиск и возвращать к первой странице.
#### Scenario: Переход к следующей странице
- **WHEN** пользователь открывает следующую страницу полученного результата
- **THEN** он видит следующие до 30 выпусков в том же порядке и с тем же временем получения списка, без повторения выпусков предыдущей страницы
#### Scenario: Повторный поиск
- **WHEN** пользователь изменил условия или оставил их прежними и нажал «Найти» повторно
- **THEN** система получает новый результат с новым временем получения списка и показывает его первую страницу
#### Scenario: Поля изменены, но поиск не запущен
- **WHEN** пользователь изменяет условия и ещё не нажал «Найти»
- **THEN** ранее полученные страницы не подменяются результатом для новых условий
### Requirement: Отсутствие совпадений отличается от недоступности поиска
Система MUST показывать пустой результат только после успешного поиска без совпадений. Если получить сведения для поиска не удалось, система SHALL сообщать о недоступности поиска и MUST NOT выдавать отсутствие акций за подтверждённый результат.
#### Scenario: Успешный поиск без совпадений
- **WHEN** поиск завершился успешно, но ни одна акция не удовлетворяет условиям
- **THEN** система показывает, что подходящие акции не найдены
#### Scenario: Источник каталога недоступен
- **WHEN** поиск не может получить сведения о каталоге
- **THEN** система сообщает об ошибке получения результата, не утверждая, что подходящих акций нет