Files
specs/openspec/changes/persist-tbank-data/specs/brokerage-accounts/spec.md
T
ksv741 2f5814abdd
Conventional commits / commits (pull_request) Successful in 12s
Specs security / security (pull_request) Successful in 7s
Specs verify / verify (pull_request) Successful in 11s
Specs security / security (push) Successful in 7s
Specs verify / verify (push) Successful in 9s
spec: complete persist-tbank-data implementation
Record the approved proposal, delta specifications, design and 27 verified
implementation tasks. Keep the change active; archiving and main-spec synchronization
are deferred.
2026-09-26 21:29:24 +03:00

23 KiB
Raw Blame History

MODIFIED Requirements

Requirement: Список активных брокерских счетов

Система SHALL предоставлять авторизованному User GET /api/v1/brokerage-accounts, возвращающий envelope { data, meta }, где data содержит сохранённый массив открытых поддерживаемых инвестиционных счетов T-Bank, доступных через текущий T-Bank connection User, в порядке, полученном от T-Bank. Каждый элемент MUST содержать стабильный id, непустое name, нормализованный type (BROKERAGE, INDIVIDUAL_INVESTMENT, INVEST_BOX или MONEY_MARKET_FUND) и nullable openedOn в формате YYYY-MM-DD. Система MUST включать только счета T-Bank типов «брокерский счёт», «ИИС», «Инвесткопилка» и «фонд денежного рынка»; банковские и неизвестные типы MUST NOT возвращаться. Система MUST удалить внешние пробелы из имени и при пустом результате использовать непустое название соответствующего типа.

Каждый элемент MUST также содержать nullable portfolioSummary. Доступная сводка MUST содержать totalsByCurrency: точные Money-итоги Portfolio по исходным ISO-валютам, включающие стоимость позиций и Cash Balance в той же валюте. Она MUST NOT содержать FX-конвертированный или межвалютный итог и MUST NOT выдавать Cash Balance за самостоятельный второй итог. Если успешная сводка Portfolio конкретного доступного счёта ещё не сохранена, portfolioSummary MUST быть null, не исключая этот счёт из списка и не делая список неуспешным. Пустой доступный Portfolio MUST передаваться доступной сводкой с пустым totalsByCurrency, а не null.

Список Brokerage Account и его сводки постоянно сохраняются и являются результатом последнего успешного наблюдения текущего брокерского подключения и MUST NOT создавать ожидание, что WealthLedger хранит долговременное право владения счётом или Portfolio независимо от T-Bank connection.

Обычный запрос MUST читать сохранённый список и доступные сводки без ожидания T-Bank. Срок свежести списка MUST составлять пять минут; сводки MUST следовать минутной свежести Portfolio. Отсутствующий список MUST возвращаться как 200 с data: null, meta.availability: missing и состоянием запрошенной загрузки. Только успешно полученный полный пустой список MUST возвращаться как data: [] с meta.availability: complete. Время, свежесть и состояние загрузки MUST передаваться по контракту tbank-data-sync. Отсутствие либо ошибка загрузки сводки MUST отличаться от отсутствия самого счёта; сохранённая успешная сводка MUST оставаться доступной после временной ошибки её обновления.

Scenario: Поддерживаемые счета возвращаются в upstream-порядке

  • WHEN User с действующим T-Bank connection запрашивает список, а последняя успешная синхронизация сохранила открытые брокерский счёт, ИИС и неподдержанный дебетовый счёт
  • THEN система отвечает 200 с массивом брокерского счёта и ИИС в data в исходном порядке T-Bank без дебетового счёта

Scenario: Пустой список поддерживаемых счетов

  • WHEN User с действующим T-Bank connection имеет сохранённый успешно подтверждённый пустой список открытых поддерживаемых инвестиционных счетов
  • THEN система отвечает 200 с data: [] и meta.availability: complete

Scenario: Пустое название получает fallback

  • WHEN T-Bank возвращает поддерживаемый открытый счёт с пустым либо состоящим из пробелов именем
  • THEN система отвечает этим счётом с непустым названием его нормализованного типа

Scenario: Дата открытия отсутствует

  • WHEN T-Bank не возвращает дату открытия поддерживаемого открытого счёта
  • THEN система отвечает этим счётом с openedOn: null

Scenario: Мультивалютная сводка счёта

  • WHEN доступный Portfolio счёта содержит итоги в RUB и USD, включая Cash Balance в USD
  • THEN ответ содержит одну доступную portfolioSummary с отдельными Money-итогами RUB и USD без FX-конвертации, общего итога или отдельного итога Cash Balance

Scenario: Сводка одного счёта временно недоступна

  • WHEN список доступных счетов получен, но Portfolio одного из них ещё не сохранён и его загрузка временно недоступна
  • THEN система отвечает 200 всеми счетами в исходном порядке, передаёт для этого счёта portfolioSummary: null и не заменяет успешные сводки остальных счетов ошибкой списка

Scenario: Доступный Portfolio пуст

  • WHEN доступный счёт не имеет ненулевых позиций и доступных денежных остатков
  • THEN система передаёт доступную portfolioSummary с пустым totalsByCurrency

Scenario: Список ещё не загружен

  • WHEN User впервые запрашивает список текущего подключения и успешного сохранённого результата ещё нет
  • THEN система возвращает 200 с data: null, состоянием загрузки в meta, запрашивает фоновую синхронизацию и не выдаёт отсутствие данных за отсутствие счетов

Scenario: Ошибка обновления сохранённой сводки

  • WHEN обновление Portfolio одного счёта временно неуспешно и его успешная сводка уже сохранена
  • THEN список сохраняет эту сводку с прежним временем и видимым состоянием ошибки обновления вместо null

Requirement: Состояния недоступности списка счетов

Система MUST возвращать последний полностью сохранённый список текущего подключения без синхронной проверки T-Bank. Неполный ответ обновления MUST NOT заменять полный список. При отсутствии T-Bank connection система MUST отвечать 409 Problem Details с code TBANK_NOT_CONNECTED. Если для текущего подключения уже подтверждено отклонение token как неаутентифицированного или без прав, система MUST отвечать 400 Problem Details с code INVALID_TBANK_TOKEN и сохранять connection. При временной либо иной upstream-ошибке фонового обновления система MUST сохранять прежний список и время его успешной загрузки, при наблюдении завершённой попытки возвращая 200 с meta.sync.state: failed и безопасным meta.sync.errorCode. Обычный запрос экрана либо ручное обновление MAY запросить новую попытку согласно tbank-data-sync; наблюдение статуса MUST NOT запускать её. При отсутствии сохранённого списка data MUST оставаться null. Ошибка обновления MUST NOT менять время успешной загрузки и сама по себе MUST NOT делать ещё свежие данные устаревшими; свежесть MUST продолжать определяться сроком соответствующего источника. Подтверждённая недействительность текущего token MUST запрещать выдачу личных данных, включая сохранённый список; локальная сессия и текущая граница подключения MUST проверяться до чтения.

Scenario: Подключение отсутствует

  • WHEN User без сохранённого T-Bank connection запрашивает список
  • THEN система не обращается к T-Bank и отвечает 409 Problem Details с code TBANK_NOT_CONNECTED

Scenario: Сохранённый token больше не действителен

  • WHEN фоновая проверка подтвердила, что T-Bank отклоняет token текущего подключения как неаутентифицированный или без прав, и User снова запрашивает список
  • THEN система отвечает 400 Problem Details с code INVALID_TBANK_TOKEN и не удаляет connection

Scenario: T-Bank временно недоступен

  • WHEN фоновое обновление списка завершается временной либо иной upstream-ошибкой
  • THEN последующее наблюдение результата без нового запуска отвечает 200 с последним сохранённым списком либо data: null, meta.sync.state: failed и meta.sync.errorCode: TBANK_UNAVAILABLE, не подтверждая новую успешную загрузку

Requirement: Экран списка брокерских счетов

Клиент SHALL загружать список при открытии защищённого маршрута /accounts и предоставлять действие «Обновить», без автоматического refetch при возврате фокуса. Клиент MUST использовать успешный frontend-кэш списка в течение 60 секунд. После истечения этого срока клиент MUST запрашивать локальный результат и состояние синхронизации; при уже имеющемся успешном списке он MUST сохранять последний успешный Client Cached Snapshot видимым во время фонового обновления и после его ошибки, ясно отделяя это состояние от свежего результата. Клиент MUST читать список из data, показывать свежесть и состояние синхронизации из meta и MUST NOT считать успешный HTTP-ответ с data: null пустым списком. Ручное обновление MUST обходить проверку давности списка и показанных сводок, не обновляя всю историю счетов. При успешном непустом ответе клиент MUST отобразить адаптивный одноколоночный список широких интерактивных карточек счетов с локализованными типом и, если доступна, датой открытия без времени; каждая карточка MUST ясно показывать тип, имя, финансовую сводку, вторичные метаданные и заметное действие открытия счёта, вести на /accounts/:accountId по всей области и сохранять доступное клавиатурное состояние. После успешной замены или удаления T-Bank connection клиент MUST инвалидировать список счетов.

Для доступной portfolioSummary карточка MUST показывать не более двух Money-итогов в исходных валютах и обозначать наличие остальных валют, если они есть; она MUST NOT показывать или подразумевать FX-конвертацию, межвалютный агрегированный итог либо Cash Balance как отдельный второй итог. При portfolioSummary: null карточка MUST показывать локальное безопасное состояние временной недоступности сводки, сохраняя метаданные счёта и возможность открыть его. При доступной пустой сводке карточка MUST не выдавать отсутствие итогов за временную ошибку.

Scenario: Пользователь открывает счёт из списка

  • WHEN клиент успешно загрузил непустой список и User выбирает карточку счёта
  • THEN клиент переходит на /accounts/:accountId с id выбранного счёта

Scenario: Список пуст

  • WHEN клиент получает data: [] с meta.availability: complete
  • THEN клиент показывает штатное сообщение об отсутствии инвестиционных счетов T-Bank

Scenario: Список берётся из свежего frontend-кэша

  • WHEN User повторно открывает список в течение 60 секунд после успешной загрузки
  • THEN клиент показывает сохранённые счета без нового API-запроса

Scenario: Устаревший список загружается повторно

  • WHEN User открывает список после истечения 60 секунд frontend-кэша и уже существует последний успешный список
  • THEN клиент оставляет этот список видимым во время нового запроса и ясно обозначает фоновое обновление

Scenario: Ошибка фонового обновления не скрывает snapshot

  • WHEN обновление устаревшего списка завершается временной ошибкой при наличии последнего успешного Client Cached Snapshot
  • THEN клиент сохраняет snapshot видимым, безопасно сообщает о неуспешном обновлении и предоставляет применимое действие повторной загрузки

Scenario: Карточка ограничивает валютные итоги

  • WHEN доступная сводка счёта содержит итоги в трёх или более валютах
  • THEN карточка показывает не более двух исходных валютных итогов и обозначает наличие остальных без общего сконвертированного значения

Scenario: Недоступность сводки локальна карточке

  • WHEN клиент получает список, в котором portfolioSummary одного счёта равна null
  • THEN он показывает временную недоступность только на этой карточке, не скрывает остальные карточки и сохраняет переход на детали недоступной сводки счёта

Scenario: Пустая сводка не является ошибкой

  • WHEN клиент получает доступную portfolioSummary с пустым totalsByCurrency
  • THEN он показывает нейтральное состояние отсутствия финансовых итогов, а не состояние временной недоступности

Scenario: Начальная загрузка не становится пустым списком

  • WHEN клиент получает data: null со статусом запрошенной синхронизации
  • THEN он показывает состояние первичной загрузки или её ошибки, а не сообщение об отсутствии инвестиционных счетов

Scenario: Ручное обновление списка

  • WHEN User нажимает «Обновить» на экране списка счетов
  • THEN клиент запрашивает обновление списка и показанных сводок независимо от давности, сохраняя доступные данные и отображая прогресс

Requirement: Клиентские состояния ошибки списка счетов

Клиент MUST NOT автоматически повторять неуспешный запрос списка. При TBANK_NOT_CONNECTED клиент SHALL показать объяснение отсутствия подключения и действие «Открыть настройки T-Bank». При INVALID_TBANK_TOKEN клиент SHALL показать объяснение недействительного token и то же действие. При TBANK_UNAVAILABLE клиент SHALL показать безопасное объяснение временной недоступности, действие повторной загрузки и действие открытия настроек T-Bank. При иной ошибке клиент MUST показать общий безопасный error-state только с действием повторной загрузки. При TBANK_NOT_CONNECTED или INVALID_TBANK_TOKEN клиент MUST скрыть личные данные даже при наличии Client Cached Snapshot. Для временной и неизвестной ошибки без snapshot клиент MUST показать ошибку без списка; при наличии snapshot он MUST сохранить его видимым с безопасным предупреждением. meta.sync.state: failed MUST отображаться как ошибка фоновой синхронизации, отдельно от ошибки HTTP-запроса; успешный HTTP-ответ сам по себе MUST NOT скрывать такую ошибку.

Scenario: Подключение требуется

  • WHEN клиент получает TBANK_NOT_CONNECTED
  • THEN клиент не показывает список и предлагает открыть настройки T-Bank

Scenario: Известная ошибка не повторяется автоматически

  • WHEN запрос списка завершается известной ошибкой либо сервер сообщает завершённую неуспешную синхронизацию
  • THEN клиент не выполняет дополнительные запросы, пока User не выберет действие повторной загрузки

Scenario: Token требует внимания

  • WHEN клиент получает INVALID_TBANK_TOKEN
  • THEN клиент не показывает список и предлагает открыть настройки T-Bank

Scenario: Временная ошибка T-Bank

  • WHEN клиент получает TBANK_UNAVAILABLE и сохранённого списка нет
  • THEN клиент не показывает список и предлагает повторить загрузку либо открыть настройки T-Bank

Scenario: Неизвестная ошибка загрузки

  • WHEN клиент получает ошибку без известного кода списка счетов и сохранённого списка нет
  • THEN клиент не показывает список и предлагает только повторить загрузку

Scenario: Известная ошибка при сохранённом snapshot

  • WHEN фоновое обновление списка с последним успешным Client Cached Snapshot получает TBANK_UNAVAILABLE через HTTP-ошибку либо meta.sync.errorCode
  • THEN клиент сохраняет snapshot видимым, сообщает о временной недоступности и предлагает повторить загрузку либо открыть настройки T-Bank