spec: archive compact portfolio totals
|
Before Width: | Height: | Size: 45 KiB After Width: | Height: | Size: 45 KiB |
|
Before Width: | Height: | Size: 18 KiB After Width: | Height: | Size: 18 KiB |
|
Before Width: | Height: | Size: 37 KiB After Width: | Height: | Size: 37 KiB |
|
Before Width: | Height: | Size: 50 KiB After Width: | Height: | Size: 50 KiB |
|
Before Width: | Height: | Size: 33 KiB After Width: | Height: | Size: 33 KiB |
|
Before Width: | Height: | Size: 35 KiB After Width: | Height: | Size: 35 KiB |
|
Before Width: | Height: | Size: 43 KiB After Width: | Height: | Size: 43 KiB |
|
Before Width: | Height: | Size: 47 KiB After Width: | Height: | Size: 47 KiB |
@@ -115,17 +115,107 @@ Portfolio Snapshot является результатом наблюдения
|
|||||||
- **THEN** система не возвращает fallback Snapshot и отвечает `400` Problem Details с code `INVALID_TBANK_TOKEN`
|
- **THEN** система не возвращает fallback Snapshot и отвечает `400` Problem Details с code `INVALID_TBANK_TOKEN`
|
||||||
|
|
||||||
### Requirement: Состояния и ошибки Portfolio на клиенте
|
### Requirement: Состояния и ошибки Portfolio на клиенте
|
||||||
Клиент SHALL загружать Portfolio при открытии защищённого маршрута `/accounts/:accountId`. Он MUST использовать успешный ответ в течение 30 секунд, без automatic retry, polling или refetch при возврате фокуса. После истечения срока клиент MUST скрыть предыдущий ответ и показать loading-состояние до нового результата.
|
|
||||||
|
|
||||||
Клиент MUST показать штатное empty-состояние только при пустых `positions` и `cashBalances`. При `isStale: true` клиент MUST показывать Snapshot, `lastSuccessfulAt` и нейтральное предупреждение о временной недоступности T-Bank. При `404` клиент MUST показать состояние недоступного счёта с действием возврата к списку счетов. При отсутствии Snapshot и `TBANK_NOT_CONNECTED`, `INVALID_TBANK_TOKEN`, `TBANK_UNAVAILABLE` или иной ошибке клиент MUST показать безопасное error-состояние с доступными для ошибки действиями; действие повтора разрешено только для временной и неизвестной ошибки.
|
Клиент SHALL загружать Portfolio при открытии защищённого маршрута
|
||||||
|
`/accounts/:accountId`. Он MUST использовать успешный ответ в течение
|
||||||
|
30 секунд, без automatic retry, polling или refetch при возврате фокуса.
|
||||||
|
При загрузке без ранее полученного успешного ответа для этого же Brokerage
|
||||||
|
Account клиент MUST показывать структурное skeleton-состояние ожидаемого
|
||||||
|
содержимого, включая сводку, без вымышленных сумм. Область загрузки MUST
|
||||||
|
иметь доступное обозначение занятости; skeleton MUST NOT быть интерактивным
|
||||||
|
или получать клавиатурный фокус.
|
||||||
|
|
||||||
|
Истечение срока свежести MUST NOT само по себе скрывать уже доступный
|
||||||
|
Client Cached Snapshot. Во время фонового обновления и после его ошибки
|
||||||
|
клиент MUST сохранять ранее полученные данные того же Portfolio, включая
|
||||||
|
валютные итоги и исходное `lastSuccessfulAt`. При обновлении клиент MUST
|
||||||
|
показывать компактную неблокирующую индикацию без замены данных skeleton.
|
||||||
|
При ошибке обновления он MUST показывать локализованное неблокирующее
|
||||||
|
предупреждение о последних полученных данных и предоставлять действие
|
||||||
|
повторной попытки. Сохранённые данные MUST NOT считаться новым успешным
|
||||||
|
снимком или подтверждением текущей доступности Brokerage Account.
|
||||||
|
|
||||||
|
Клиент MUST показать штатное empty-состояние только при пустых `positions`
|
||||||
|
и `cashBalances`, без нулевых валютных карточек. Для показываемого состава
|
||||||
|
Portfolio клиент MUST сохранять время успешного получения снимка
|
||||||
|
`lastSuccessfulAt`, форматируемое в UTC. При `isStale: true` клиент MUST
|
||||||
|
показывать Snapshot, его исходное время и нейтральное предупреждение
|
||||||
|
о временной недоступности T-Bank. Это предупреждение само по себе MUST NOT
|
||||||
|
добавлять действие ручного обновления. Server-side stale и ошибка фонового
|
||||||
|
обновления Client Cached Snapshot MUST оставаться раздельными состояниями:
|
||||||
|
ошибка клиента MUST NOT менять или подменять `isStale`, а оба предупреждения
|
||||||
|
и действие повтора клиентского обновления MUST оставаться доступными при
|
||||||
|
одновременном наличии этих состояний.
|
||||||
|
|
||||||
|
При отсутствии ранее успешно полученного Client Cached Snapshot этого же
|
||||||
|
Portfolio и ответе `404` клиент MUST показать состояние недоступного счёта
|
||||||
|
с действием возврата к списку счетов без действия повтора. При отсутствии
|
||||||
|
Snapshot и `TBANK_NOT_CONNECTED`, `INVALID_TBANK_TOKEN`, `TBANK_UNAVAILABLE`
|
||||||
|
или иной ошибке клиент MUST показать безопасное error-состояние с доступными
|
||||||
|
для ошибки действиями; действие повтора разрешено только для временной
|
||||||
|
и неизвестной ошибки.
|
||||||
|
|
||||||
#### Scenario: Показ устаревшего Portfolio
|
#### Scenario: Показ устаревшего Portfolio
|
||||||
|
|
||||||
- **WHEN** клиент получает успешный ответ с `isStale: true`
|
- **WHEN** клиент получает успешный ответ с `isStale: true`
|
||||||
- **THEN** клиент показывает позиции и остатки, время последнего успешного снимка и предупреждение без действия ручного обновления
|
- **THEN** клиент показывает позиции, остатки, валютные итоги, время
|
||||||
|
последнего успешного снимка и нейтральное предупреждение; само это
|
||||||
|
предупреждение не добавляет действие ручного обновления
|
||||||
|
|
||||||
#### Scenario: Недоступный счёт на клиенте
|
#### Scenario: Недоступный счёт на клиенте
|
||||||
- **WHEN** клиент получает `404` для Portfolio
|
|
||||||
- **THEN** клиент не показывает Portfolio и предлагает вернуться к списку Brokerage Account без действия повтора
|
- **WHEN** для текущего Portfolio отсутствует ранее успешно полученный
|
||||||
|
Client Cached Snapshot и клиент получает `404`
|
||||||
|
- **THEN** клиент не показывает Portfolio и предлагает вернуться к списку
|
||||||
|
Brokerage Account без действия повтора
|
||||||
|
|
||||||
|
#### Scenario: Первичная загрузка Portfolio
|
||||||
|
|
||||||
|
- **WHEN** клиент ожидает Portfolio этого Brokerage Account без ранее
|
||||||
|
полученного успешного ответа
|
||||||
|
- **THEN** он показывает структурный skeleton, включая место сводки,
|
||||||
|
с доступным обозначением загрузки, без вымышленных или нулевых сумм
|
||||||
|
и без фокусируемых placeholders
|
||||||
|
|
||||||
|
#### Scenario: Обновление после истечения срока свежести
|
||||||
|
|
||||||
|
- **WHEN** срок свежести показанного Portfolio истёк и началось фоновое
|
||||||
|
обновление данных того же Brokerage Account
|
||||||
|
- **THEN** клиент сохраняет прежние валютные итоги и время их успешного
|
||||||
|
получения, показывает неблокирующую индикацию обновления и не заменяет
|
||||||
|
доступный состав и сводку skeleton-состоянием
|
||||||
|
|
||||||
|
#### Scenario: Ошибка обновления после успешного ответа
|
||||||
|
|
||||||
|
- **WHEN** клиент уже показывает Client Cached Snapshot Portfolio,
|
||||||
|
а обновление этих же данных завершается ошибкой
|
||||||
|
- **THEN** клиент сохраняет данные, суммы и время прежнего снимка,
|
||||||
|
показывает неблокирующее предупреждение и действие повтора;
|
||||||
|
ошибка не делает сохранённый снимок свежим и не изменяет `isStale`
|
||||||
|
|
||||||
|
#### Scenario: Ошибка клиентского обновления при server-side stale
|
||||||
|
|
||||||
|
- **WHEN** клиент показывает Portfolio с `isStale: true`, а фоновое
|
||||||
|
обновление этого же Portfolio завершается ошибкой
|
||||||
|
- **THEN** прежние суммы и время остаются видимыми одновременно
|
||||||
|
с нейтральным server-side предупреждением, отдельным предупреждением
|
||||||
|
об ошибке клиентского обновления и его действием повтора
|
||||||
|
|
||||||
|
#### Scenario: Повторное обновление успешно
|
||||||
|
|
||||||
|
- **WHEN** после ошибки фонового обновления User запускает повторную
|
||||||
|
попытку и получает успешный Portfolio Snapshot
|
||||||
|
- **THEN** до ответа прежние суммы и время остаются видимыми, затем
|
||||||
|
клиент показывает значения и `lastSuccessfulAt` успешного ответа,
|
||||||
|
убирает предупреждение об ошибке клиентского обновления и показывает
|
||||||
|
server-side предупреждение только в соответствии с новым `isStale`
|
||||||
|
|
||||||
|
#### Scenario: Штатный пустой Portfolio на клиенте
|
||||||
|
|
||||||
|
- **WHEN** успешно полученный Portfolio содержит пустые `positions`
|
||||||
|
и `cashBalances`
|
||||||
|
- **THEN** клиент показывает существующее empty-состояние без валютных
|
||||||
|
карточек с вымышленными нулевыми итогами
|
||||||
|
|
||||||
### Requirement: Представление карточек позиций на клиенте
|
### Requirement: Представление карточек позиций на клиенте
|
||||||
Клиент MUST отображать `STOCK` и `BOND` отдельными секциями. Внутри каждой секции MUST сохраняться upstream-порядок позиций. Позиции иных типов MUST оставаться доступными в отдельной общей секции.
|
Клиент MUST отображать `STOCK` и `BOND` отдельными секциями. Внутри каждой секции MUST сохраняться upstream-порядок позиций. Позиции иных типов MUST оставаться доступными в отдельной общей секции.
|
||||||
@@ -193,3 +283,148 @@ Portfolio Snapshot является результатом наблюдения
|
|||||||
#### Scenario: Секция иных типов сохраняет прежний вид
|
#### Scenario: Секция иных типов сохраняет прежний вид
|
||||||
- **WHEN** User открывает загруженный Portfolio с позициями иных типов (не `STOCK` и не `BOND`)
|
- **WHEN** User открывает загруженный Portfolio с позициями иных типов (не `STOCK` и не `BOND`)
|
||||||
- **THEN** клиент отображает их компактными плитками в адаптивном grid с текстовой подписью доходности и без визуального логотипа
|
- **THEN** клиент отображает их компактными плитками в адаптивном grid с текстовой подписью доходности и без визуального логотипа
|
||||||
|
|
||||||
|
### Requirement: Компактная сводка валютных итогов Portfolio
|
||||||
|
|
||||||
|
На странице Brokerage Account клиент SHALL показывать каждый полученный
|
||||||
|
валютный итог Portfolio отдельной компактной карточкой. При достаточной
|
||||||
|
ширине карточки валютная метка и сумма MUST располагаться в одной строке;
|
||||||
|
сумма MUST оставаться основной читаемой величиной. Обычная сводка с одной
|
||||||
|
или тремя валютами MUST занимать меньше высоты, чем прежнее представление
|
||||||
|
с меткой над суммой при тех же значениях и ширине экрана.
|
||||||
|
|
||||||
|
На широком экране несколько карточек SHALL располагаться рядом при наличии
|
||||||
|
места; на узком экране MUST сохраняться одна карточка в ряду. Если полное
|
||||||
|
значение не помещается рядом с меткой, клиент MUST предоставлять ему полную
|
||||||
|
доступную ширину карточки и переносить содержимое с увеличением высоты.
|
||||||
|
Сводка MUST NOT обрезать суммы, заменять их многоточием или требовать
|
||||||
|
горизонтальной прокрутки для чтения карточек и значений.
|
||||||
|
|
||||||
|
#### Scenario: Обычная сводка на широком экране
|
||||||
|
|
||||||
|
- **WHEN** User открывает Portfolio с итогами `125000.50` RUB, `320.00` USD
|
||||||
|
и `9876.54321` EUR на широком экране с местом для трёх карточек
|
||||||
|
- **THEN** клиент показывает три отдельные карточки рядом, метку и полную
|
||||||
|
сумму в одной строке каждой карточки, с меньшей высотой сводки, чем при
|
||||||
|
прежнем размещении метки над суммой в тех же условиях
|
||||||
|
|
||||||
|
#### Scenario: Обычная сводка на узком экране
|
||||||
|
|
||||||
|
- **WHEN** User открывает тот же Portfolio с тремя валютными итогами
|
||||||
|
на узком экране
|
||||||
|
- **THEN** клиент показывает три карточки по одной в ряду, сохраняя полные
|
||||||
|
читаемые суммы и меньшую общую высоту сводки, чем при прежнем размещении
|
||||||
|
метки над суммой в тех же условиях
|
||||||
|
|
||||||
|
#### Scenario: Portfolio с одной валютой
|
||||||
|
|
||||||
|
- **WHEN** Portfolio содержит один валютный итог
|
||||||
|
- **THEN** клиент показывает одну компактную карточку без пустых карточек
|
||||||
|
для других валют; обычная сумма и метка помещаются в одну строку при
|
||||||
|
достаточной ширине на широком и узком экране
|
||||||
|
|
||||||
|
#### Scenario: Длинная сумма на узком экране
|
||||||
|
|
||||||
|
- **WHEN** User просматривает итог `123456789012.345678901` RUB при ширине
|
||||||
|
экрана 320 пикселей и значение не помещается рядом с валютной меткой
|
||||||
|
- **THEN** сумма получает полную доступную ширину карточки и при необходимости
|
||||||
|
переносится; все цифры и знак валюты читаемы без обрезания, многоточия
|
||||||
|
и горизонтальной прокрутки
|
||||||
|
|
||||||
|
### Requirement: Обозначение и доступная идентичность валюты в сводке
|
||||||
|
|
||||||
|
В валютных метках и суффиксах сумм сводки клиент MUST обозначать RUB знаком
|
||||||
|
`₽`, USD знаком `$`, EUR знаком `€` вместо видимых ISO-кодов этих валют.
|
||||||
|
Каждая сумма MUST оставаться однозначно связанной со своей исходной валютой,
|
||||||
|
включая чтение вспомогательными технологиями. Для RUB, USD и EUR MUST быть
|
||||||
|
доступны названия «Российский рубль», «Доллар США» и «Евро» соответственно.
|
||||||
|
Символ или цвет сами по себе MUST NOT быть единственным доступным способом
|
||||||
|
определения валюты.
|
||||||
|
|
||||||
|
Другие полученные валюты MUST оставаться в сводке с понятным обозначением
|
||||||
|
и доступной идентичностью. Если знак отсутствует или неоднозначен, клиент
|
||||||
|
MUST сохранять возможность различить исходную валюту по её названию или
|
||||||
|
ISO-коду, в том числе визуально; неизвестное полное название MUST NOT
|
||||||
|
подменяться вымышленным. Совпадающие знаки разных валют MUST NOT приводить
|
||||||
|
к объединению карточек или неразличимому представлению их валют.
|
||||||
|
|
||||||
|
#### Scenario: Рубль, доллар США и евро показаны знаками
|
||||||
|
|
||||||
|
- **WHEN** сводка содержит RUB, USD и EUR
|
||||||
|
- **THEN** метки показывают `₽`, `$` и `€`, а суммы заканчиваются теми же
|
||||||
|
знаками; видимые RUB, USD и EUR не подменяют выбранные знаки
|
||||||
|
|
||||||
|
#### Scenario: Валютная сумма читается вспомогательной технологией
|
||||||
|
|
||||||
|
- **WHEN** User читает валютный итог сводки вспомогательной технологией
|
||||||
|
- **THEN** ему доступны полное значение и однозначная валютная идентичность,
|
||||||
|
включая «Российский рубль», «Доллар США» или «Евро» для соответствующих валют
|
||||||
|
|
||||||
|
#### Scenario: Получена другая валюта
|
||||||
|
|
||||||
|
- **WHEN** сводка содержит валюту помимо RUB, USD и EUR
|
||||||
|
- **THEN** её отдельный итог остаётся доступным с понятным обозначением
|
||||||
|
и доступным названием либо исходным ISO-кодом при неизвестном названии
|
||||||
|
|
||||||
|
#### Scenario: У разных валют совпадает знак
|
||||||
|
|
||||||
|
- **WHEN** Portfolio содержит итоги двух исходных валют с одинаковым знаком
|
||||||
|
- **THEN** сводка показывает два отдельных итога, и User может различить
|
||||||
|
их валюты визуально и вспомогательной технологией без догадки по сумме
|
||||||
|
|
||||||
|
### Requirement: Полные точные значения и смысл валютной сводки
|
||||||
|
|
||||||
|
Сводка MUST показывать полученные валютные итоги без изменения их знака,
|
||||||
|
цифр и дробной точности. Для русской локали клиент SHALL сохранять группировку
|
||||||
|
разрядов пробелами и десятичную запятую, не округляя значение, не отбрасывая
|
||||||
|
дробные позиции и не сокращая сумму до тысяч, миллионов или экспоненциальной
|
||||||
|
записи. Требование MUST сохраняться для длинных, отрицательных значений
|
||||||
|
и отрицательных дробей с нулевой целой частью.
|
||||||
|
|
||||||
|
Каждая карточка MUST представлять только полученный итог позиций и Cash
|
||||||
|
Balance своей исходной валюты. Сводка MUST NOT конвертировать или складывать
|
||||||
|
разные валюты, показывать рублёвый итог как общую стоимость Portfolio,
|
||||||
|
повторно добавлять Cash Balance либо обещать полную оценку при наличии
|
||||||
|
позиций без вычислимой стоимости. Для отсутствующего валютного итога клиент
|
||||||
|
MUST NOT создавать нулевую карточку. Отсутствие итогов само по себе MUST NOT
|
||||||
|
обозначать пустой Portfolio, если позиции или Cash Balance присутствуют.
|
||||||
|
|
||||||
|
#### Scenario: Обычные суммы сохраняют дробные позиции
|
||||||
|
|
||||||
|
- **WHEN** в сводку поступают значения `125000.50` RUB, `320.00` USD
|
||||||
|
и `9876.54321` EUR
|
||||||
|
- **THEN** клиент показывает `125 000,50 ₽`, `320,00 $` и `9 876,54321 €`
|
||||||
|
с сохранением каждой исходной цифры и дробной позиции
|
||||||
|
|
||||||
|
#### Scenario: Длинные и отрицательные значения не теряют точность
|
||||||
|
|
||||||
|
- **WHEN** в сводку поступают `123456789012.345678901` RUB,
|
||||||
|
`-1234567.890123` USD и `987654321.000001` EUR
|
||||||
|
- **THEN** пользователь видит полные `123 456 789 012,345678901 ₽`,
|
||||||
|
`-1 234 567,890123 $` и `987 654 321,000001 €` без округления,
|
||||||
|
сокращений и потери отрицательного знака на широком и узком экране
|
||||||
|
|
||||||
|
#### Scenario: Отрицательная сумма меньше единицы
|
||||||
|
|
||||||
|
- **WHEN** полученный валютный итог равен `-0.5` USD
|
||||||
|
- **THEN** сводка показывает `-0,5 $`, сохраняя знак и дробную величину
|
||||||
|
|
||||||
|
#### Scenario: Несколько валют и денежные остатки
|
||||||
|
|
||||||
|
- **WHEN** полученные итоги RUB и USD уже включают позиции и Cash Balance
|
||||||
|
соответствующих валют
|
||||||
|
- **THEN** клиент показывает эти два итога без повторного добавления денег,
|
||||||
|
межвалютного итога или представления RUB как общей стоимости Portfolio
|
||||||
|
|
||||||
|
#### Scenario: Стоимость части позиций неизвестна
|
||||||
|
|
||||||
|
- **WHEN** Portfolio содержит позиции без вычислимой стоимости и доступные
|
||||||
|
валютные итоги, сформированные без этих позиций
|
||||||
|
- **THEN** сводка показывает только полученные итоги, не добавляет нули
|
||||||
|
за отсутствующие стоимости и не называет результат полной оценкой Portfolio
|
||||||
|
|
||||||
|
#### Scenario: Итоги отсутствуют при непустом Portfolio
|
||||||
|
|
||||||
|
- **WHEN** `totalsByCurrency` пуст, но `positions` или `cashBalances` непусты
|
||||||
|
- **THEN** клиент не создаёт валютные карточки и не заменяет доступный
|
||||||
|
состав Portfolio состоянием пустого портфеля
|
||||||
|
|||||||