59 KiB
portfolio-view Specification
Purpose
Позволяет авторизованному User просматривать нормализованный текущий состав и агрегаты Portfolio одного доступного Brokerage Account без валютной конвертации.
Requirements
Requirement: Получение Portfolio выбранного Brokerage Account
Система SHALL предоставлять авторизованному User GET /api/v1/brokerage-accounts/:accountId/portfolio. Она MUST возвращать нормализованный полный Portfolio Snapshot только для открытого поддерживаемого Brokerage Account, доступного через текущий T-Bank connection User. Ответ MUST иметь envelope { data, meta }: data MUST содержать сохранённый полный Portfolio Snapshot с lastSuccessfulAt в RFC 3339 UTC без миллисекунд, boolean isStale, totalsByCurrency, positions и cashBalances; meta MUST содержать наблюдаемое состояние resource по tbank-data-sync, включая freshness, полноту, время последнего успешного Snapshot, последнюю часовую попытку и доступность. Обычный GET MUST читать сохранённый Snapshot и его наблюдаемое состояние без ожидания T-Bank и MUST NOT запускать обновление личного Portfolio.
Portfolio Snapshot является результатом наблюдения Portfolio на конкретный момент. Он MUST NOT означать, что WealthLedger хранит Portfolio как долговременный агрегат или подтверждает доступность Brokerage Account вне текущего T-Bank connection.
Scenario: Получение текущего Portfolio
- WHEN User запрашивает Portfolio доступного Brokerage Account, а полный Snapshot текущего подключения успешно сохранён
- THEN система отвечает
200с полным нормализованным Portfolio Snapshot вdata, егоlastSuccessfulAtиisStale, а также наблюдаемым состоянием вmeta
Scenario: Недоступный Brokerage Account
- WHEN
accountIdне принадлежит User, закрыт, неподдерживаем или больше не доступен через текущий T-Bank connection - THEN система отвечает
404без Portfolio Snapshot
Requirement: Нормализованный состав Portfolio
Система MUST возвращать только обычные Portfolio Position с ненулевым количеством и доступные Cash Balance с ненулевой суммой. Cash Balance MUST быть отдельным от Portfolio Position элементом и агрегироваться до одного значения на валюту. Portfolio Position с типом CURRENCY MUST сохранять исходное количество и идентификаторы для контракта, но отражает тот же кэш, что и Cash Balance, и MUST NOT повторно учитываться в валютных итогах и структуре. Клиент MUST показывать этот кэш один раз как Cash Balance в разделе «Деньги», без отдельной карточки CURRENCY. Порядок Portfolio Position и Cash Balance MUST соответствовать upstream-порядку; система MUST NOT сортировать или объединять Portfolio Position с одинаковым FIGI. Виртуальные позиции и заблокированные денежные остатки MUST NOT входить в Portfolio.
Каждая Portfolio Position MUST содержать нормализованный тип STOCK, BOND, FUND, CURRENCY, FUTURE, OPTION, STRUCTURED_NOTE, DIGITAL_FINANCIAL_ASSET либо OTHER, строковое quantity, nullable currentValue, nullable averagePurchasePrice, nullable absoluteReturn, nullable isin, nullable ticker и nullable logoName — имя файла логотипа бренда инструмента, nullable sector — сектор инструмента и nullable issuer — эмитент позиции. Имя файла логотипа MUST заполняться из metadata инструмента, когда upstream предоставляет бренд с непустым именем файла логотипа; во всех остальных случаях оно MUST быть null. Логотип является presentation metadata: он не влияет на количество, стоимость, агрегаты, успешность или свежесть Portfolio Snapshot. Поле sector позиции типа STOCK, BOND или FUND MUST содержать значение сектора из metadata инструмента в том виде, как его определяет T-Bank, без перекодирования и собственной классификации, с обрезанными пробелами в начале и конце; значение из одних пробелов считается отсутствующим. Во всех остальных случаях, включая остальные типы позиций, оно MUST быть null. Поле issuer позиции типа STOCK, BOND или FUND, включая фонд, MUST быть объектом с непустыми строками id — идентификатор бренда актива инструмента — и name — название этого бренда (не название компании), когда T-Bank предоставляет для инструмента бренд актива с идентификатором и названием, непустыми после обрезки пробелов в начале и конце; значение из одних пробелов считается отсутствующим. Во всех остальных случаях, включая остальные типы позиций, оно MUST быть null. Система MUST NOT определять эмитента по совпадению названия, тикера или ISIN. Сектор и эмитент не влияют на количество, стоимость, агрегаты, успешность или свежесть Portfolio Snapshot: их отсутствие либо недоступность MUST NOT делать Snapshot неуспешным или устаревшим и MUST NOT скрывать позицию или её финансовые значения, кроме отказа token по требованию «Свежесть и fallback полного Portfolio Snapshot». Система MUST искать metadata по instrument UID при его наличии, затем по FIGI. Отображаемый идентификатор MUST предпочитать name, затем ticker, а при недоступности metadata использовать FIGI, не скрывая финансовые значения.
Scenario: Позиция с неизвестным upstream-типом
- WHEN upstream возвращает обычную ненулевую позицию с нераспознанным типом инструмента
- THEN система возвращает её в исходном порядке с типом
OTHER
Scenario: Позиция валютного инструмента не является денежным остатком
- WHEN upstream возвращает ненулевую Portfolio Position валютного инструмента и ненулевой Cash Balance той же валюты
- THEN система возвращает Portfolio Position с типом
CURRENCYотдельно от Cash Balance и не объединяет их между собой
Scenario: Позиция без metadata инструмента
- WHEN lookup metadata по FIGI не дал ticker или name либо временно недоступен
- THEN система возвращает позицию с FIGI как fallback-идентификатором и всеми доступными финансовыми значениями
Scenario: Имя инструмента доступно
- WHEN metadata инструмента по FIGI содержит непустое name
- THEN система возвращает name как отображаемый идентификатор позиции вместо FIGI или ticker
Scenario: FIGI не разрешается, но доступен instrument UID
- WHEN FIGI позиции не даёт metadata, а её instrument UID возвращает непустое name
- THEN система возвращает name, полученное по instrument UID
Scenario: ISIN инструмента доступен
- WHEN metadata инструмента позиции содержит непустой
isin - THEN система возвращает его в поле
isinпозиции
Scenario: ISIN инструмента отсутствует
- WHEN у инструмента позиции нет ISIN либо metadata недоступна
- THEN система возвращает позицию с
isin: null, не скрывая финансовые значения
Scenario: Тикер инструмента доступен
- WHEN metadata инструмента позиции содержит непустой
ticker - THEN система возвращает его в поле
tickerпозиции
Scenario: Тикер инструмента отсутствует
- WHEN у инструмента позиции нет ticker либо metadata недоступна
- THEN система возвращает позицию с
ticker: null, не скрывая финансовые значения
Scenario: Имя файла логотипа бренда доступно
- WHEN metadata инструмента позиции содержит бренд с непустым именем файла логотипа
- THEN система возвращает его в поле
logoNameпозиции
Scenario: Имя файла логотипа бренда отсутствует
- WHEN у инструмента позиции нет бренда, имя файла логотипа пустое либо metadata недоступна
- THEN система возвращает позицию с
logoName: null, не скрывая финансовые значения
Scenario: Денежные остатки в одной валюте
- WHEN upstream возвращает несколько ненулевых доступных денежных остатков одной валюты
- THEN система возвращает один Cash Balance с их точной суммой
Scenario: Штатный пустой Portfolio
- WHEN у доступного Brokerage Account нет ненулевых обычных позиций и доступных денежных остатков
- THEN система отвечает
200с пустымиpositions,cashBalancesиtotalsByCurrency
Scenario: Сектор инструмента доступен
- WHEN metadata инструмента позиции содержит сектор
energy - THEN система возвращает в поле
sectorпозиции значениеenergyбез изменений
Scenario: Сектор инструмента отсутствует
- WHEN у инструмента позиции нет сектора, сектор пустой либо metadata недоступна
- THEN система возвращает позицию с
sector: null, не скрывая финансовые значения
Scenario: Эмитент позиции доступен
- WHEN T-Bank предоставляет для инструмента позиции бренд актива с идентификатором
brand-1и названием «Сбер» - THEN система возвращает в поле
issuerпозиции идентификаторbrand-1и название «Сбер»
Scenario: Название эмитента — название бренда
- WHEN T-Bank предоставляет для инструмента бренд актива с названием «Сбер» и компанией «ПАО Сбербанк»
- THEN система возвращает в
issuer.name«Сбер»
Scenario: Эмитент у фонда
- WHEN T-Bank предоставляет для инструмента позиции
FUNDбренд актива с идентификаторомbrand-2и названием «Фонд» - THEN система возвращает в
issuerпозиции идентификаторbrand-2и название «Фонд»
Scenario: Пробелы по краям обрезаются
- WHEN T-Bank возвращает сектор " energy " и название бренда " Сбер "
- THEN система возвращает
sectorenergyиissuer.name«Сбер»
Scenario: Значение из одних пробелов
- WHEN T-Bank возвращает сектор " " либо название бренда " "
- THEN система возвращает
sector: nullлибоissuer: nullсоответственно
Scenario: Остальные типы позиций
- WHEN T-Bank предоставляет сектор и бренд актива для инструмента позиции типа
FUTURE - THEN система возвращает у этой позиции
sector: nullиissuer: null
Scenario: Эмитент позиции отсутствует
- WHEN у инструмента позиции нет бренда актива, идентификатор либо название бренда пустые или данные бренда недоступны
- THEN система возвращает позицию с
issuer: null, не скрывая финансовые значения
Scenario: Эмитент не определяется по названию
- WHEN две позиции имеют одинаковое название, но разные идентификаторы бренда актива
- THEN система возвращает в
issuerпозиций разные идентификаторы бренда и не объединяет их
Scenario: Недоступность сектора и эмитента не нарушает Snapshot
- GIVEN сектор и бренд актива позиции ещё ни разу не получены
- WHEN получение сектора и бренда актива завершается ошибкой, а количества и цены позиции получены
- THEN система сохраняет Snapshot с
sector: nullиissuer: nullу затронутых позиций; ихcurrentValue,totalsByCurrency,lastSuccessfulAtиisStaleтакие же, как при доступных сектора и бренде
Requirement: Точные значения и валютные агрегаты
Все количества и финансовые значения в Portfolio Snapshot MUST передаваться десятичными строками без экспоненциальной записи и без незначащих завершающих нулей. Денежное значение MUST быть единым Money-значением с ISO-кодом валюты и десятичной строкой значения. Система MUST корректно нормализовать отрицательные целую и дробную части upstream-значений. Отсутствующее значение MUST передаваться как null, а не как ноль.
currentValue Portfolio Position MUST быть точным произведением количества на текущую цену при наличии валидных множителей и валюты цены. absoluteReturn MUST быть точной величиной (currentPrice - averagePurchasePrice) * quantity только когда обе цены имеют одинаковую известную валюту. Позиция без вычислимой текущей стоимости MUST оставаться в ответе, но MUST NOT входить в агрегаты. totalsByCurrency MUST содержать точную сумму currentValue позиций, кроме CURRENCY, и Cash Balance в каждой валюте без валютной конвертации. Структура Portfolio MUST учитывать CURRENCY только как CASH из Cash Balance, без второго вклада.
Scenario: Валютная позиция отражает денежный остаток
- WHEN Portfolio содержит
CURRENCYposition стоимостью 100 RUB и Cash Balance 100 RUB - THEN система сохраняет
CURRENCYposition с количеством вpositions, но возвращает итог 100 RUB и один вкладCASH100 RUB в структуре; клиент показывает 100 RUB в «Деньги» без отдельной валютной карточки
Scenario: Мультивалютный Portfolio
- WHEN Portfolio содержит финансовые значения в RUB и USD
- THEN система возвращает отдельные агрегаты RUB и USD без общего сконвертированного итога
Scenario: Позиция без вычислимой стоимости
- WHEN у ненулевой позиции отсутствует либо невалидна цена, количество или валюта цены
- THEN система возвращает позицию с
currentValue: nullи не включает её вtotalsByCurrency
Scenario: Отрицательная дробная часть upstream-значения
- WHEN upstream возвращает сумму с
units: 0и отрицательной дробной частьюnano: -500000000 - THEN система передаёт её как строку
"-0.5"
Requirement: Свежесть и fallback полного Portfolio Snapshot
Система MUST хранить последний полный успешно полученный Portfolio Snapshot с принадлежностью User, экземпляру подключения и Brokerage Account. Обычный GET MUST возвращать { data, meta }, где data является сохранённым Snapshot, а meta соответствует наблюдаемому состоянию resource по tbank-data-sync, читать его без ожидания T-Bank и MUST NOT запускать обновление личного Portfolio, даже если Snapshot отсутствует или старше 60 минут; обновлением владеет часовой обход. Portfolio source MUST считаться свежим в течение 60 минут после успешного получения. Ошибка обхода MUST сохранять прежний полный Snapshot и его время без лимита fallback. Если состав получен полностью, но отдельная позиция не имеет цены, система MUST сохранить новый состав с неполной оценкой; metadata MUST NOT определять успешность или свежесть финансового Snapshot, кроме отказа token при получении сектора или бренда актива позиции: он MUST обрабатываться так же, как отказ token при обходе Portfolio (исключение, источник — #48). Сохранённая доступная identity позиции MAY оставаться доступной при недоступности online-справочника.
Система MUST делать прежние личные datasets недоступными после успешной замены или удаления подключения. Поздняя работа прежнего подключения MUST NOT публиковаться в новом. Самостоятельные справочные сведения и результаты lookup MUST запрашиваться онлайн, а не сохраняться как долговременный справочник; ошибка lookup не подтверждает отсутствие инструмента. Исключение: сектор и бренд актива позиции MUST храниться как последнее известное значение и обновляться по требованию «Обновление сектора и эмитента» (#48).
Scenario: Свежий Snapshot
- WHEN User повторно запрашивает тот же Portfolio в течение 60 минут после успешного получения
- THEN система возвращает свежий Snapshot без нового обращения к T-Bank
Scenario: Медленный lookup инструмента
- WHEN сведения об инструменте ещё запрашиваются онлайн
- THEN система не задерживает локальный ответ Portfolio ради lookup и показывает сохранённую доступную identity
Scenario: Fallback после временной upstream-ошибки
- WHEN часовой обход Portfolio неуспешен из-за временной недоступности T-Bank, а в текущей границе подключения есть полный успешный Snapshot
- THEN система отвечает
200этим Snapshot сisStale: trueи исходнымlastSuccessfulAt
Scenario: Недействительный token не маскируется fallback-данными
- WHEN T-Bank отклоняет token как неаутентифицированный или без прав при часовом обходе Portfolio
- THEN система не возвращает fallback Snapshot и отвечает
400Problem Details с codeINVALID_TBANK_TOKEN
Scenario: Неполная оценка при новом составе
- WHEN успешно получен полный состав позиций, но у одной позиции нет вычислимой цены
- THEN новый состав становится текущим Portfolio Snapshot с неизвестной стоимостью позиции без подстановки нуля
Scenario: Сектор и бренд читаются из хранимого значения
- GIVEN сектор позиции получен 10 часов назад
- WHEN User запрашивает Portfolio
- THEN система возвращает хранимый
sectorбез обращения к T-Bank за сектором
Scenario: Отказ token при получении бренда
- WHEN T-Bank отклоняет token как неаутентифицированный или без прав при получении сектора или бренда актива позиции
- THEN система не возвращает fallback Snapshot и отвечает
400Problem Details с codeINVALID_TBANK_TOKEN, как при отказе token в обходе Portfolio
Requirement: Состояния и ошибки Portfolio на клиенте
Клиент SHALL загружать Portfolio при открытии защищённого маршрута
/accounts/:accountId и использовать успешный ответ в течение 30 секунд.
Временные сбои и возвращение к экрану с требующими обновления данными SHALL
обрабатываться автоматическим восстановлением по frontend-data-resilience.
Клиент MUST NOT предоставлять ручное обновление или повторную загрузку.
При первичной загрузке без успешного результата того же Brokerage Account
клиент MUST показывать структурный skeleton ожидаемого содержимого, включая
сводку, без вымышленных сумм. Область MUST иметь доступное обозначение занятости;
skeleton MUST NOT быть интерактивным или получать фокус. Неуспешное или
затянувшееся первичное получение SHALL переходить в локальную недоступность
по frontend-loading-presentation, а не в бесконечную загрузку.
Истечение срока свежести MUST NOT скрывать доступный Client Cached Snapshot.
При обновлении и временной ошибке клиент MUST сохранять данные того же
Portfolio, включая валютные итоги, без индикации фоновой работы собственного
чтения, технических предупреждений об этом чтении и уведомлений. Исходное
lastSuccessfulAt и isStale MUST сохранять внутренний смысл: их показ или
скрытие MUST NOT делать сохранённый снимок новым успешным результатом или
подтверждением текущего доступа. Server-side stale и ошибка клиентского чтения
MUST NOT подменять друг друга.
Состояние источника из meta — время последнего успешного результата, признак
устаревания и исход последней часовой попытки — не относится к собственному
чтению клиента: клиент MUST представлять его текстом либо доступно именованным
элементом по frontend-data-resilience, не превращая в действие пользователя.
Клиент MUST показывать штатное empty-состояние только при подтверждённом полном
результате с пустыми positions и cashBalances, без вымышленных нулевых карточек.
Неполный итог MUST NOT выдаваться за окончательный; доступные независимые данные
MUST сохраняться на экране. При подтверждённом 404 клиент MUST скрыть личный
Portfolio и предложить возврат к списку счетов. При TBANK_NOT_CONNECTED или
INVALID_TBANK_TOKEN он MUST скрыть личные данные и предложить восстановление
подключения. Эти состояния MUST NOT маскироваться сохранённым снимком или
вызывать техническую повторную загрузку.
Scenario: Показ устаревшего Portfolio
- WHEN клиент получает успешный ответ с
isStale: trueв действующей границе доступа - THEN клиент показывает позиции, остатки и доступные валютные итоги и обозначает устаревание источника текстом либо доступно именованным элементом, без ручного обновления
Scenario: Неуспешная последняя попытка сохраняет Portfolio
- WHEN клиент получает полный Portfolio и
metaс неуспехом последней часовой попытки - THEN он сохраняет Portfolio и исходное время видимыми и отдельно показывает текстовое либо доступно именованное предупреждение о неуспехе попытки, не предлагая ручной повтор
Scenario: Недоступный счёт на клиенте
- WHEN клиент получает подтверждённый
404текущего Portfolio - 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 прежние суммы остаются видимыми без технических предупреждений и ручных действий, сохраняя различие внутренних состояний
Scenario: Повторное обновление успешно
- WHEN после временной ошибки автоматическое восстановление получает успешный Portfolio Snapshot
- THEN до ответа прежние суммы остаются видимыми, затем клиент показывает новый результат без уведомления об обновлении и без переноса пользовательского фокуса
Scenario: Штатный пустой Portfolio на клиенте
- WHEN успешно полученный полный Portfolio содержит пустые
positionsиcashBalances - THEN клиент показывает существующее empty-состояние без валютных карточек с вымышленными нулевыми итогами
Scenario: Первичный Portfolio пока недоступен
- WHEN данных Portfolio нет, а первичное получение не удалось или превысило ограниченное время ожидания
- THEN клиент показывает нейтральную недоступность без кнопки повтора до исчерпания автоматических повторов и автоматически заменяет её содержимым после успешного восстановления
Scenario: Требуется восстановить подключение
- WHEN клиент получает
TBANK_NOT_CONNECTEDилиINVALID_TBANK_TOKENпри наличии сохранённого личного Portfolio - THEN личные данные скрываются, а клиент предлагает необходимое действие подключения без технической повторной загрузки
Requirement: Представление карточек позиций на клиенте
Клиент MUST отображать STOCK и BOND отдельными секциями. Внутри каждой секции MUST сохраняться upstream-порядок позиций. Позиции иных типов MUST оставаться доступными в отдельной общей секции.
Карточки секций STOCK и BOND MUST отображаться по одной в ряду при любой ширине экрана. Карточка акции или облигации MUST показывать визуальный логотип бренда инструмента рядом с отображаемым идентификатором позиции; при отсутствии имени файла логотипа либо неудачной загрузке изображения вместо логотипа MUST отображаться первая буква отображаемого идентификатора позиции. Абсолютный доход в карточке акции или облигации MUST отображаться значением с явным знаком (+ для положительного, - для отрицательного, без знака при нуле) с цветовой индикацией знака и без текстовой подписи доходности.
Карточка STOCK MUST показывать только ticker под отображаемым идентификатором позиции, если тикер доступен; при ticker: null она MUST сохранять текущее отображение isin как fallback. Карточки BOND и общей секции иных типов MUST сохранять отображение isin. Карточки общей секции иных типов MUST сохранять прежнее поведение: компактная плитка в адаптивном grid (одна колонка на узком экране, несколько колонок на широком), абсолютный доход с текстовой подписью доходности и цветовой индикацией знака, без визуального логотипа. Плитки всех секций MUST показывать отображаемый идентификатор позиции, количество, currentValue как главную метрику, absoluteReturn, averagePurchasePrice и долю позиции среди позиций той же валюты. Отсутствующие метрики MUST быть скрыты, а не отображаться заглушкой. При форматировании денежных значений в русской локали клиент MUST отображать RUB как символ ₽; другие валюты MUST сохранять понятное обозначение.
Scenario: Отдельные акции и облигации
- WHEN Portfolio содержит позиции
STOCKиBOND - THEN клиент отображает их в отдельных секциях «Акции» и «Облигации» без изменения порядка в каждой секции
Scenario: Портфель отображается на мобильном устройстве
- WHEN User открывает загруженный Portfolio на узком экране
- THEN клиент сохраняет читаемое одноколоночное отображение агрегатов, позиций и Cash Balance
Scenario: Карточка акции с доступным тикером
- WHEN позиция
STOCKимеет непустойtickerи ISIN - THEN клиент показывает под отображаемым идентификатором только тикер и не показывает ISIN
Scenario: Карточка акции без тикера
- WHEN позиция
STOCKимеетticker: nullи непустой ISIN - THEN клиент показывает ISIN под отображаемым идентификатором
Scenario: Карточка облигации сохраняет ISIN
- WHEN позиция
BONDимеет доступные ticker и ISIN - THEN клиент показывает ISIN и не заменяет его тикером
Scenario: Карточка позиции отображает доступные метрики
- WHEN позиция, не относящаяся к
STOCKилиCURRENCY, имеет значение текущей стоимости, абсолютного дохода, средней цены покупки и ISIN - THEN клиент показывает идентификатор, количество, текущую стоимость, абсолютный доход, среднюю цену покупки, долю позиции среди позиций той же валюты и ISIN
Scenario: Отсутствующие метрики скрыты
- WHEN у позиции отсутствует значение текущей стоимости, средней цены покупки или дохода
- THEN клиент скрывает соответствующую метрику вместо отображения заглушки
Scenario: Доход окрашен по знаку
- WHEN у позиции абсолютный доход положительный или отрицательный
- THEN клиент отображает его зелёным для положительного и красным для отрицательного, без цвета при его отсутствии
Scenario: Доля позиции в валюте
- WHEN позиция имеет вычислимую текущую стоимость в валюте
- THEN клиент отображает её долю среди всех отображаемых позиций той же валюты, не включая Cash Balance и зеркальные
CURRENCYpositions
Scenario: Одноколоночные секции акций и облигаций
- WHEN User открывает загруженный Portfolio с позициями
STOCKилиBONDна широком экране - THEN клиент отображает карточки соответствующей секции по одной в ряду
Scenario: Логотип акции или облигации отображается
- WHEN позиция
STOCKилиBONDсодержит непустое имя файла логотипа бренда и изображение успешно загружается - THEN клиент показывает изображение логотипа рядом с отображаемым идентификатором позиции
Scenario: Фолбэк без логотипа у акции или облигации
- WHEN у позиции
STOCKилиBONDимя файла логотипа отсутствует либо изображение не загружается - THEN вместо логотипа клиент показывает первую букву отображаемого идентификатора позиции
Scenario: Доходность акции или облигации со знаком без подписи
- WHEN карточка акции или облигации показывает абсолютный доход
- THEN клиент отображает значение с явным знаком
+или-(без знака при нуле) и без текстовой подписи доходности
Scenario: Секция облигаций в едином стиле с акциями
- WHEN User открывает загруженный Portfolio с позициями
BOND - THEN клиент отображает облигации по одной карточке в ряду с визуальным логотипом бренда либо фолбэком первой буквы и абсолютным доходом с явным знаком без текстовой подписи
Scenario: Секция иных типов сохраняет прежний вид
- WHEN User открывает загруженный Portfolio с позициями иных типов (не
STOCKи неBOND) - THEN клиент отображает их компактными плитками в адаптивном grid с текстовой подписью доходности и без визуального логотипа
Requirement: Компактная сводка валютных итогов Portfolio
На странице Brokerage Account клиент SHALL показывать каждый полученный валютный итог Portfolio отдельной компактной карточкой. При достаточной ширине карточки валютная метка и сумма MUST располагаться в одной строке; сумма MUST оставаться основной читаемой величиной. Обычная сводка с одной или тремя валютами MUST занимать меньше высоты, чем прежнее представление с меткой над суммой при тех же значениях и ширине экрана.
На широком экране несколько карточек SHALL располагаться рядом при наличии места; на узком экране MUST сохраняться одна карточка в ряду. Если полное значение не помещается рядом с меткой, клиент MUST предоставлять ему полную доступную ширину карточки и переносить содержимое с увеличением высоты. Сводка MUST NOT обрезать суммы, заменять их многоточием или требовать горизонтальной прокрутки для чтения карточек и значений.
Scenario: Обычная сводка на широком экране
- WHEN User открывает Portfolio с итогами
125000.50RUB,320.00USD и9876.54321EUR на широком экране с местом для трёх карточек - THEN клиент показывает три отдельные карточки рядом, метку и полную сумму в одной строке каждой карточки, с меньшей высотой сводки, чем при прежнем размещении метки над суммой в тех же условиях
Scenario: Обычная сводка на узком экране
- WHEN User открывает тот же Portfolio с тремя валютными итогами на узком экране
- THEN клиент показывает три карточки по одной в ряду, сохраняя полные читаемые суммы и меньшую общую высоту сводки, чем при прежнем размещении метки над суммой в тех же условиях
Scenario: Portfolio с одной валютой
- WHEN Portfolio содержит один валютный итог
- THEN клиент показывает одну компактную карточку без пустых карточек для других валют; обычная сумма и метка помещаются в одну строку при достаточной ширине на широком и узком экране
Scenario: Длинная сумма на узком экране
- WHEN User просматривает итог
123456789012.345678901RUB при ширине экрана 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.50RUB,320.00USD и9876.54321EUR - THEN клиент показывает
125 000,50 ₽,320,00 $и9 876,54321 €с сохранением каждой исходной цифры и дробной позиции
Scenario: Длинные и отрицательные значения не теряют точность
- WHEN в сводку поступают
123456789012.345678901RUB,-1234567.890123USD и987654321.000001EUR - THEN пользователь видит полные
123 456 789 012,345678901 ₽,-1 234 567,890123 $и987 654 321,000001 €без округления, сокращений и потери отрицательного знака на широком и узком экране
Scenario: Отрицательная сумма меньше единицы
- WHEN полученный валютный итог равен
-0.5USD - 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 состоянием пустого портфеля
Requirement: Обновление сектора и эмитента
Система SHALL обновлять сектор и бренд актива позиции при первом обходе по hourly-investment-sync, начавшемся не ранее чем через 24 часа после последнего успешного получения; в остальных обходах значение не меняется. Пока значение не получено, получение повторяется при каждом обходе.
Источник (ненормативно): #48, #48
Scenario: Обход раньше 24 часов
- GIVEN бренд позиции получен в понедельник в 09:00, а затем T-Bank сменил его название
- WHEN обход начинается во вторник в 08:59, то есть через 23 ч 59 мин
- THEN
issuer.nameостаётся прежним
Scenario: Обход через 24 часа
- GIVEN бренд позиции получен в понедельник в 09:00, а затем T-Bank сменил его название
- WHEN обход начинается во вторник в 09:00
- THEN
issuer.nameновое
Requirement: Результат получения сектора и эмитента
Результат получения SHALL определять поле позиции и отсчёт 24 часов по таблице; совпадает ровно одна строка.
| Исход получения | Поле | Отсчёт 24 часов |
|---|---|---|
| Получено значение | Новое значение | Начинается заново |
| Ответ «не найдено» или значения нет | null |
Начинается заново |
| Ответ о другом инструменте или активе | Прежнее значение или null |
Не меняется; получение неуспешно |
| Иная ошибка, кроме отказа token | Прежнее значение или null |
Не меняется; получение неуспешно |
Неуспешное получение SHALL NOT мешать обновлению остальных данных Portfolio; отказ token — по «Свежесть и fallback полного Portfolio Snapshot».
Источник (ненормативно): #48
Scenario: Ответ без значения заменяет известное значение
- GIVEN у позиции известен
sectorenergy, 24 часа прошли - WHEN T-Bank отвечает, что сектора у инструмента нет
- THEN
sector: null
Scenario: «Не найдено» перезапускает отсчёт
- GIVEN у позиции известен
sectorenergy, 24 часа прошли, и обход получает от T-Bank «не найдено» - WHEN следующий обход начинается через час, а T-Bank уже возвращает сектор
financial - THEN
sectorостаётсяnull; значениеfinancialпоявляется при первом обходе, начавшемся не ранее чем через 24 часа после ответа «не найдено»
Scenario: Ответ о другом инструменте
- GIVEN у позиции известен
sectorenergy, 24 часа прошли - WHEN T-Bank возвращает данные другого инструмента
- THEN
sectorостаётсяenergy, а следующий обход повторяет получение
Scenario: Недоступность T-Bank сохраняет известное значение
- GIVEN у позиции известен
sectorenergy, 24 часа прошли - WHEN получение сектора завершается недоступностью T-Bank, а цены и количества получены
- THEN позиция возвращается с
sectorenergy, остальные данные Portfolio обновлены,lastSuccessfulAtновый
Scenario: Значение ещё не известно
- GIVEN новая позиция, сектор которой ни разу не получен успешно
- WHEN получение сектора неуспешно при одном обходе и успешно при следующем
- THEN после первого обхода
sector: null, после следующего значение сектора