From 678a671d23b3f3db1d2ad8989924ffb35a9687ee Mon Sep 17 00:00:00 2001 From: Sergey Krylov Date: Tue, 25 Aug 2026 09:27:42 +0300 Subject: [PATCH] feat(portfolio-view): sync delta spec and archive change MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Remove old combined requirement, add two focused requirements: 'Состояния и ошибки Portfolio на клиенте' and 'Представление карточек позиций на клиенте' - Archive extend-instrument-logo-to-bonds change --- .../.openspec.yaml | 6 ++ .../design.md | 58 ++++++++++++++ .../proposal.md | 36 +++++++++ .../specs/portfolio-view/spec.md | 78 +++++++++++++++++++ .../tasks.md | 20 +++++ openspec/specs/portfolio-view/spec.md | 47 ++++++----- 6 files changed, 224 insertions(+), 21 deletions(-) create mode 100644 openspec/changes/archive/2026-08-25-extend-instrument-logo-to-bonds/.openspec.yaml create mode 100644 openspec/changes/archive/2026-08-25-extend-instrument-logo-to-bonds/design.md create mode 100644 openspec/changes/archive/2026-08-25-extend-instrument-logo-to-bonds/proposal.md create mode 100644 openspec/changes/archive/2026-08-25-extend-instrument-logo-to-bonds/specs/portfolio-view/spec.md create mode 100644 openspec/changes/archive/2026-08-25-extend-instrument-logo-to-bonds/tasks.md diff --git a/openspec/changes/archive/2026-08-25-extend-instrument-logo-to-bonds/.openspec.yaml b/openspec/changes/archive/2026-08-25-extend-instrument-logo-to-bonds/.openspec.yaml new file mode 100644 index 0000000..d6ef81d --- /dev/null +++ b/openspec/changes/archive/2026-08-25-extend-instrument-logo-to-bonds/.openspec.yaml @@ -0,0 +1,6 @@ +schema: wealth-ledger-sdd +created: 2026-08-25 +affected_areas: + - frontend + - specs + - docs diff --git a/openspec/changes/archive/2026-08-25-extend-instrument-logo-to-bonds/design.md b/openspec/changes/archive/2026-08-25-extend-instrument-logo-to-bonds/design.md new file mode 100644 index 0000000..a8f45c7 --- /dev/null +++ b/openspec/changes/archive/2026-08-25-extend-instrument-logo-to-bonds/design.md @@ -0,0 +1,58 @@ +# extend-instrument-logo-to-bonds — Design + +## Context + +Данные уже готовы: `logoName` приходит в `PortfolioPositionDto` для позиций любого типа (passthrough из T-Bank metadata, без персистенции), фронтенд строит CDN-URL через `instrumentLogoUrl` и рендерит логотип компонентом `PositionLogo` (40px, фолбэк первой буквой через `onError`). Ограничение «только акции» существует в одном месте представления страницы счёта. + +Осложнение: локальный проп `stock` на `PositionSection`/`PositionCard` управляет сразу тремя аспектами — одноколоночной раскладкой grid, рендером логотипа и форматом абсолютного дохода (знаковое значение против текстовой подписи). Паритет облигаций означает, что все три аспекта применяются к секции `BOND` целиком; менять их по отдельности не нужно. + +Ограничения репозитория: FSD-границы и public API (dependency-cruiser, Knip), только typed client поверх generated OpenAPI, i18n через локали, gate `npm ci && npm run verify` под Node 22. + +## Goals / Non-Goals + +**Goals:** +- Секции `BOND` получают полное представление плиток `STOCK`: одна карточка в ряду, визуальный логотип с буква-фолбэком, знаковый доход без подписи. +- Минимальный диф в слое представления страницы счёта; переиспользование существующих `instrumentLogoUrl` и `PositionLogo` без изменений. +- Тесты, зеркалирующие существующие сценарии акций для облигаций. + +**Non-Goals:** +- Логотипы для общей секции иных типов (фондов, валют и т.д.) — гейт остаётся allowlist по типам секций. +- Изменения backend, API-контракта, generated types и OpenAPI snapshot. +- Экстракция `PositionLogo`/`instrumentLogoUrl` в entities ради переиспользования — потребителя по-прежнему один экран. +- Логотипы на странице upcoming events; понятие «эмитент» в модели. + +## Decisions + +**D1. Разъединить смысл флага, сохранив один проп.** Проп `stock` переименовывается в презентационный термин (например, `wide`), отражающий «широкая плитка во всю строку + логотип + знаковый доход». Секции `STOCK` и `BOND` передают его, общая секция иных типов — нет. +*Alternative:* два независимых пропа (`onePerRow`, `withLogo`). Отклонено: сегодня они всегда движутся вместе; разъединять, пока не появился тип, которому нужен только один аспект, — спекуляция. + +**D2. Логотип без изменений.** `PositionLogo` и `instrumentLogoUrl` реиспользуются как есть, включая `key={logoSrc ?? "no-logo"}` для сброса failed-state между карточками и CDN-размер 160 для 40px аватара. Новых настроек размера/радиуса не вводится. + +**D3. Формат доходности следует за флагом.** Знаковый формат без подписи применяется к `STOCK` и `BOND`, текстовая подпись остаётся только у секции иных типов — как прямое следствие D1, без отдельной ветки по типу. + +**D4. Тесты повторяют паттерны акций.** В тестах страницы счёта добавляются случаи: облигация с `logoName` рендерит изображение с брендовым URL; облигация без `logoName` — буква-фолбэк без img; знаковый доход облигации без текстовой подписи; одноколоночная секция облигаций. Существующие тесты секции иных типов служат регрессионной сеткой неизменённого пути. + +## Delivery Map + +| Area | Status | Architectural outcome or N/A reason | +| --- | --- | --- | +| frontend | `affected` | Представление страницы счёта: единый презентационный флаг для секций `STOCK` и `BOND`; логотип и знаковый доход на карточках облигаций; обновлённые unit/component тесты | +| backend | `N/A` | `logoName` уже возвращается для всех типов позиций; контракт Portfolio и зависимости не меняются | +| specs | `affected` | Утверждённые Delta Specs синхронизируются в главную спеку `portfolio-view` при archive после реализации | +| docs | `affected` | As-built: `docs/features/portfolio-view.md` отражает паритет облигаций; глоссарий не меняется (термин Instrument Logo уже типо-агностичен) | + +### Contract Gate Decision + +| Gate | Status | Reason | +| --- | --- | --- | +| runtime-openapi | `N/A` | API-контракт не меняется ни на одной стороне: backend вне scope, generated types не обновляются | + +## Risks / Trade-offs + +- **Редкие бренды у облигаций.** Брендовые данные T-Bank для облигаций встречаются реже, чем у акций, поэтому буква-фолбэк будет видна чаще. Mitigation: фолбэк — штатное, уже протестированное поведение; спецификация явно допускает оба исхода. +- **Связанность трёх аспектов представления.** Общий флаг означает: нельзя включить логотип, не сделав плитку широкой. Осознанный trade-off ради минимального дифа; точка разъединения обозначена в D1. +- **Визуальная регрессия других секций.** Путь секции иных типов не меняется (ветка по умолчанию того же флага); риск ограничен одним файлом представления и покрыт существующими тестами. + +## Migration Plan + +Поставка одним изменением frontend без флагов миграции: изменение чисто презентационное, состояние и данные не затрагиваются. Rollback — revert коммита frontend. Порядок: имплементация → `npm ci && npm run verify` → semantic verification против Proposal/Delta Specs/Design → as-built docs → workspace gate `verify-workspace-change.sh` → archive. diff --git a/openspec/changes/archive/2026-08-25-extend-instrument-logo-to-bonds/proposal.md b/openspec/changes/archive/2026-08-25-extend-instrument-logo-to-bonds/proposal.md new file mode 100644 index 0000000..d59c0ed --- /dev/null +++ b/openspec/changes/archive/2026-08-25-extend-instrument-logo-to-bonds/proposal.md @@ -0,0 +1,36 @@ +# extend-instrument-logo-to-bonds — Proposal + +## Why + +Облигации в портфеле отображаются иначе, чем акции: без логотипа бренда инструмента, компактной плиткой в адаптивном grid и с текстовой подписью доходности. При этом имя файла логотипа бренда уже является частью данных позиции для всех типов инструментов — ограничение «показывать только акциям» существует исключительно на уровне отображения. Асимметрия выглядит как незавершённость недавней фичи логотипов и усложняет восприятие портфеля: однотипные карточки читаются по-разному. + +## What Changes + +Клиент приводит карточки облигаций к полному визуальному паритету с карточками акций: + +- облигации отображаются по одной карточке в ряду при любой ширине экрана; +- карточка облигации показывает визуальный логотип бренда инструмента рядом с отображаемым идентификатором позиции; при отсутствии имени файла логотипа либо неудачной загрузке изображения вместо логотипа показывается первая буква отображаемого идентификатора; +- абсолютный доход в карточке облигации отображается значением с явным знаком и цветовой индикацией, без текстовой подписи доходности. + +Non-goals: + +- визуальные логотипы для секций иных типов инструментов (фонды, валюты и др.) не вводятся; +- состав данных и контракт API Portfolio не меняются: ни новых полей, ни новых эндпоинтов; +- раздельные секции «Акции» и «Облигации», upstream-порядок позиций и все финансовые значения сохраняются; +- доменный словарь не меняется: используется существующий термин Instrument Logo; понятие «эмитент» не вводится. + +## Capabilities + +### New Capabilities + +_(нет)_ + +### Modified Capabilities + +- `portfolio-view`: правила отображения карточек секции `BOND` меняются с «прежнего вида» (компактная плитка в адаптивном grid, абсолютный доход с текстовой подписью, без логотипа) на паритет с карточками `STOCK`: одна карточка в ряду, визуальный логотип бренда с фолбэком первой буквы идентификатора, доход со знаком без подписи. Сценарий «Секция облигаций сохраняет прежний вид» заменяется сценарием паритета облигаций с акциями; сценарии отображения логотипа, его фолбэка и подписи доходности обобщаются с `STOCK` на `STOCK` и `BOND`. Правила отображения общей секции иных типов сохраняются без изменений. + +## Impact + +- Пользователи: вид портфеля с облигациями меняется визуально — облигации отображаются в том же стиле, что и акции; данные, порядок и значения остаются прежними. +- Система: изменение затрагивает только клиентское отображение портфеля; серверная часть и контракты не меняются. +- Документация: as-built документация представления портфеля обновляется при реализации. diff --git a/openspec/changes/archive/2026-08-25-extend-instrument-logo-to-bonds/specs/portfolio-view/spec.md b/openspec/changes/archive/2026-08-25-extend-instrument-logo-to-bonds/specs/portfolio-view/spec.md new file mode 100644 index 0000000..b3ccb75 --- /dev/null +++ b/openspec/changes/archive/2026-08-25-extend-instrument-logo-to-bonds/specs/portfolio-view/spec.md @@ -0,0 +1,78 @@ +# extend-instrument-logo-to-bonds — Delta Specs + +## REMOVED Requirements + +### Requirement: Ошибки и состояния Portfolio на клиенте +**Reason**: Требование объединяет загрузку и состояния ошибок Portfolio с правилами визуального представления карточек позиций. Правка представления облигаций внутри общего требования невозможна без переформулировки части его сценариев, чьи имена зафиксированы за прежним поведением («Секция облигаций сохраняет прежний вид» и сценарии только про акции); переименование сценариев дельта-формат не поддерживает. + +**Migration**: Всё наблюдаемое поведение переносится в этой же дельте в ADDED-требования «Состояния и ошибки Portfolio на клиенте» и «Представление карточек позиций на клиенте». Единственное изменение поведения против текущей спеки — карточки облигаций приводятся к паритету с карточками акций согласно Proposal; остальные правила сохраняются дословно. + +## ADDED Requirements + +### 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-состояние с доступными для ошибки действиями; действие повтора разрешено только для временной и неизвестной ошибки. + +#### Scenario: Показ устаревшего Portfolio +- **WHEN** клиент получает успешный ответ с `isStale: true` +- **THEN** клиент показывает позиции и остатки, время последнего успешного снимка и предупреждение без действия ручного обновления + +#### Scenario: Недоступный счёт на клиенте +- **WHEN** клиент получает `404` для Portfolio +- **THEN** клиент не показывает Portfolio и предлагает вернуться к списку Brokerage Account без действия повтора + +### Requirement: Представление карточек позиций на клиенте +Клиент MUST отображать `STOCK` и `BOND` отдельными секциями. Внутри каждой секции MUST сохраняться upstream-порядок позиций. Позиции иных типов MUST оставаться доступными в отдельной общей секции. + +Карточки секций `STOCK` и `BOND` MUST отображаться по одной в ряду при любой ширине экрана. Карточка акции или облигации MUST показывать визуальный логотип бренда инструмента рядом с отображаемым идентификатором позиции; при отсутствии имени файла логотипа либо неудачной загрузке изображения вместо логотипа MUST отображаться первая буква отображаемого идентификатора позиции. Абсолютный доход в карточке акции или облигации MUST отображаться значением с явным знаком (`+` для положительного, `-` для отрицательного, без знака при нуле) с цветовой индикацией знака и без текстовой подписи доходности. + +Карточки общей секции иных типов MUST сохранять прежнее поведение: компактная плитка в адаптивном grid (одна колонка на узком экране, несколько колонок на широком), абсолютный доход с текстовой подписью доходности и цветовой индикацией знака, без визуального логотипа. Плитки всех секций MUST показывать отображаемый идентификатор позиции, количество, `currentValue` как главную метрику, `absoluteReturn`, `averagePurchasePrice`, долю позиции среди позиций той же валюты и `isin`. Отсутствующие метрики MUST быть скрыты, а не отображаться заглушкой. При форматировании денежных значений в русской локали клиент MUST отображать RUB как символ `₽`; другие валюты MUST сохранять понятное обозначение. + +#### Scenario: Отдельные акции и облигации +- **WHEN** Portfolio содержит позиции `STOCK` и `BOND` +- **THEN** клиент отображает их в отдельных секциях «Акции» и «Облигации» без изменения порядка в каждой секции + +#### Scenario: Портфель отображается на мобильном устройстве +- **WHEN** User открывает загруженный Portfolio на узком экране +- **THEN** клиент сохраняет читаемое одноколоночное отображение агрегатов, позиций и Cash Balance + +#### Scenario: Карточка позиции отображает доступные метрики +- **WHEN** позиция имеет значение текущей стоимости, абсолютного дохода, средней цены покупки и ISIN +- **THEN** клиент показывает идентификатор, количество, текущую стоимость, абсолютный доход, среднюю цену покупки, долю среди позиций той же валюты и ISIN + +#### Scenario: Отсутствующие метрики скрыты +- **WHEN** у позиции отсутствует значение текущей стоимости, средней цены покупки или дохода +- **THEN** клиент скрывает соответствующую метрику вместо отображения заглушки + +#### Scenario: Доход окрашен по знаку +- **WHEN** у позиции абсолютный доход положительный или отрицательный +- **THEN** клиент отображает его зелёным для положительного и красным для отрицательного, без цвета при его отсутствии + +#### Scenario: Доля позиции в валюте +- **WHEN** позиция имеет вычислимую текущую стоимость в валюте +- **THEN** клиент отображает её долю среди всех позиций той же валюты, не включая Cash Balance + +#### 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 с текстовой подписью доходности и без визуального логотипа diff --git a/openspec/changes/archive/2026-08-25-extend-instrument-logo-to-bonds/tasks.md b/openspec/changes/archive/2026-08-25-extend-instrument-logo-to-bonds/tasks.md new file mode 100644 index 0000000..43b4907 --- /dev/null +++ b/openspec/changes/archive/2026-08-25-extend-instrument-logo-to-bonds/tasks.md @@ -0,0 +1,20 @@ +# extend-instrument-logo-to-bonds — Tasks + +## 1. Frontend: паритет представления облигаций + +- [x] 1.1 Переименовать презентационный флаг `stock` → `wide` в `PositionSection`/`PositionCard` страницы счёта; секции `STOCK` и `BOND` передают `wide`, общая секция иных типов — без флага. Verification: `npm run typecheck && npm run lint` в frontend. +- [x] 1.2 Расширить тесты страницы счёта случаями облигаций: изображение логотипа с брендовым CDN URL при непустом `logoName`; буква-фолбэк без img при отсутствии имени либо ошибке загрузки; абсолютный доход с явным знаком без текстовой подписи; одноколоночная раскладка секции облигаций. Verification: `npm run test:run` в frontend. +- [x] 1.3 Прогнать существующие тесты карточек акций и секции иных типов как регрессию неизменённых путей (акции — поведение сохранено; иные типы — прежний вид без логотипа и с подписью доходности). Verification: `npm run test:run` в frontend. +- [x] 1.4 Canonical gate репозитория frontend после завершения правок. Verification: `npm ci && npm run verify` в frontend под Node 22. + +## 2. Specs: соответствие дельты реализации + +- [x] 2.1 Сверить реализацию с утверждёнными Delta Specs по сценариям «Логотип акции или облигации отображается», «Фолбэк без логотипа у акции или облигации», «Доходность акции или облигации со знаком без подписи», «Одноколоночные секции акций и облигаций», «Секция иных типов сохраняет прежний вид»; при обнаружении расхождения остановиться и согласовать правку дельты с Change Owner до продолжения. Verification: `openspec validate extend-instrument-logo-to-bonds --type change --strict --store wealth-ledger-specs`. + +## 3. Docs: as-built документация + +- [x] 3.1 Обновить as-built описание представления портфеля (`docs/features/portfolio-view.md`): карточки облигаций в едином стиле с акциями — логотип бренда, фолбэк первой буквы, знаковый доход без подписи, одноколоночная секция; подтвердить, что термин Instrument Logo в `docs/architecture/domain-glossary.md` не требует правок. Verification: `npm run verify` в docs. + +## 4. Workspace verification + +- [x] 4.1 Cross-area completion gate из корня workspace под Node 22. Verification: `bash specs/scripts/verify-workspace-change.sh extend-instrument-logo-to-bonds`. diff --git a/openspec/specs/portfolio-view/spec.md b/openspec/specs/portfolio-view/spec.md index 4005c2f..3a86a57 100644 --- a/openspec/specs/portfolio-view/spec.md +++ b/openspec/specs/portfolio-view/spec.md @@ -100,17 +100,11 @@ - **WHEN** T-Bank отклоняет token как неаутентифицированный или без прав при обновлении Portfolio - **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-состояние с доступными для ошибки действиями; действие повтора разрешено только для временной и неизвестной ошибки. -Клиент MUST отображать `STOCK` и `BOND` отдельными секциями. Внутри каждой секции MUST сохраняться upstream-порядок позиций. Позиции иных типов MUST оставаться доступными в отдельной общей секции. - -Карточки секции `STOCK` MUST отображаться по одной в ряду при любой ширине экрана. Карточка акции MUST показывать визуальный логотип бренда инструмента рядом с отображаемым идентификатором позиции; при отсутствии имени файла логотипа либо неудачной загрузке изображения вместо логотипа MUST отображаться первая буква отображаемого идентификатора позиции. Абсолютный доход в карточке акции MUST отображаться значением с явным знаком (`+` для положительного, `-` для отрицательного, без знака при нуле) с цветовой индикацией знака и без текстовой подписи доходности. - -Карточки секции `BOND` и общей секции иных типов MUST сохранять прежнее поведение: компактная плитка в адаптивном grid (одна колонка на узком экране, несколько колонок на широком), абсолютный доход с текстовой подписью доходности и цветовой индикацией знака, без визуального логотипа. Плитки всех секций MUST показывать отображаемый идентификатор позиции, количество, `currentValue` как главную метрику, `absoluteReturn`, `averagePurchasePrice`, долю позиции среди позиций той же валюты и `isin`. Отсутствующие метрики MUST быть скрыты, а не отображаться заглушкой. При форматировании денежных значений в русской локали клиент MUST отображать RUB как символ `₽`; другие валюты MUST сохранять понятное обозначение. - #### Scenario: Показ устаревшего Portfolio - **WHEN** клиент получает успешный ответ с `isStale: true` - **THEN** клиент показывает позиции и остатки, время последнего успешного снимка и предупреждение без действия ручного обновления @@ -119,14 +113,21 @@ - **WHEN** клиент получает `404` для Portfolio - **THEN** клиент не показывает Portfolio и предлагает вернуться к списку Brokerage Account без действия повтора -#### Scenario: Портфель отображается на мобильном устройстве -- **WHEN** User открывает загруженный Portfolio на узком экране -- **THEN** клиент сохраняет читаемое одноколоночное отображение агрегатов, позиций и Cash Balance +### Requirement: Представление карточек позиций на клиенте +Клиент MUST отображать `STOCK` и `BOND` отдельными секциями. Внутри каждой секции MUST сохраняться upstream-порядок позиций. Позиции иных типов MUST оставаться доступными в отдельной общей секции. + +Карточки секций `STOCK` и `BOND` MUST отображаться по одной в ряду при любой ширине экрана. Карточка акции или облигации MUST показывать визуальный логотип бренда инструмента рядом с отображаемым идентификатором позиции; при отсутствии имени файла логотипа либо неудачной загрузке изображения вместо логотипа MUST отображаться первая буква отображаемого идентификатора позиции. Абсолютный доход в карточке акции или облигации MUST отображаться значением с явным знаком (`+` для положительного, `-` для отрицательного, без знака при нуле) с цветовой индикацией знака и без текстовой подписи доходности. + +Карточки общей секции иных типов MUST сохранять прежнее поведение: компактная плитка в адаптивном grid (одна колонка на узком экране, несколько колонок на широком), абсолютный доход с текстовой подписью доходности и цветовой индикацией знака, без визуального логотипа. Плитки всех секций MUST показывать отображаемый идентификатор позиции, количество, `currentValue` как главную метрику, `absoluteReturn`, `averagePurchasePrice`, долю позиции среди позиций той же валюты и `isin`. Отсутствующие метрики MUST быть скрыты, а не отображаться заглушкой. При форматировании денежных значений в русской локали клиент MUST отображать RUB как символ `₽`; другие валюты MUST сохранять понятное обозначение. #### Scenario: Отдельные акции и облигации - **WHEN** Portfolio содержит позиции `STOCK` и `BOND` - **THEN** клиент отображает их в отдельных секциях «Акции» и «Облигации» без изменения порядка в каждой секции +#### Scenario: Портфель отображается на мобильном устройстве +- **WHEN** User открывает загруженный Portfolio на узком экране +- **THEN** клиент сохраняет читаемое одноколоночное отображение агрегатов, позиций и Cash Balance + #### Scenario: Карточка позиции отображает доступные метрики - **WHEN** позиция имеет значение текущей стоимости, абсолютного дохода, средней цены покупки и ISIN - **THEN** клиент показывает идентификатор, количество, текущую стоимость, абсолютный доход, среднюю цену покупки, долю среди позиций той же валюты и ISIN @@ -143,22 +144,26 @@ - **WHEN** позиция имеет вычислимую текущую стоимость в валюте - **THEN** клиент отображает её долю среди всех позиций той же валюты, не включая Cash Balance -#### Scenario: Одноколоночная секция акций -- **WHEN** User открывает загруженный Portfolio с позициями `STOCK` на широком экране -- **THEN** клиент отображает карточки акций по одной в ряду +#### Scenario: Одноколоночные секции акций и облигаций +- **WHEN** User открывает загруженный Portfolio с позициями `STOCK` или `BOND` на широком экране +- **THEN** клиент отображает карточки соответствующей секции по одной в ряду -#### Scenario: Логотип акции отображается -- **WHEN** позиция `STOCK` содержит непустое имя файла логотипа бренда и изображение успешно загружается +#### Scenario: Логотип акции или облигации отображается +- **WHEN** позиция `STOCK` или `BOND` содержит непустое имя файла логотипа бренда и изображение успешно загружается - **THEN** клиент показывает изображение логотипа рядом с отображаемым идентификатором позиции -#### Scenario: Фолбэк без логотипа -- **WHEN** у позиции `STOCK` имя файла логотипа отсутствует либо изображение не загружается +#### Scenario: Фолбэк без логотипа у акции или облигации +- **WHEN** у позиции `STOCK` или `BOND` имя файла логотипа отсутствует либо изображение не загружается - **THEN** вместо логотипа клиент показывает первую букву отображаемого идентификатора позиции -#### Scenario: Доходность акции со знаком без подписи -- **WHEN** карточка акции показывает абсолютный доход +#### Scenario: Доходность акции или облигации со знаком без подписи +- **WHEN** карточка акции или облигации показывает абсолютный доход - **THEN** клиент отображает значение с явным знаком `+` или `-` (без знака при нуле) и без текстовой подписи доходности -#### Scenario: Секция облигаций сохраняет прежний вид +#### Scenario: Секция облигаций в едином стиле с акциями - **WHEN** User открывает загруженный Portfolio с позициями `BOND` -- **THEN** клиент отображает облигации плитками в адаптивном grid с текстовой подписью доходности и без визуального логотипа +- **THEN** клиент отображает облигации по одной карточке в ряду с визуальным логотипом бренда либо фолбэком первой буквы и абсолютным доходом с явным знаком без текстовой подписи + +#### Scenario: Секция иных типов сохраняет прежний вид +- **WHEN** User открывает загруженный Portfolio с позициями иных типов (не `STOCK` и не `BOND`) +- **THEN** клиент отображает их компактными плитками в адаптивном grid с текстовой подписью доходности и без визуального логотипа