# Engineering Design ## Context and Constraints Аналитика дохода использует выбранный период, отдельные previous/year диапазоны и публикует их через DTO и metadata; план синхронизации также запрашивает эти диапазоны. Frontend строит блоки сравнений из ответа и показывает исполненные операции через `LedgerEntryRow`. Полный журнал, недавние записи и личная история бумаги используют этот же ряд; прогноз использует `BrokerageEventCard`; календарные карточки акции и облигации реализованы внутри соответствующих страниц. Все уже используют `EntryCard` либо существующие Mantine primitives, но это не один предметный компонент выплаты. Граница реализации — утверждённые Delta Specs и [System Analysis](system-analysis/analysis.md), включая [целевой контракт](system-analysis/contracts/income-analytics-response.schema.json). Доступ, деньги, полнота периода, порядок записей и формы остальных API сохраняются. Клиент получает публичный OpenAPI artifact явно. ## Technical Goals / Non-Goals - Исключить сравнения из чтения, синхронизации, API и UI без изменения итогов выбранного периода. - Один компонент для купона/дивиденда в исполненной операции, прогнозе и графике бумаги; режим определяется источником, а не знаком или датой. - Не преобразовывать график в личный доход и не объединять чужие деньги, прогноз и сумму на бумагу. Не удалять `buckets` или `instruments` из API. ## Implementation Impact Map ### main N/A: нет изменений workspace, Compose, release orchestration или конфигурации. ### frontend Affected: страница дохода и её display model, компонент записи операций, компонент будущего события, календарные карточки акций/облигаций, локальная копия публичного OpenAPI и генерируемые типы. Владельцы экранов сохраняют свои списки, фильтры и локальные состояния. ### backend Affected: расчёт и планирование income периода, чтение операций для income-analytics, DTO ответа и план фонового получения выбранного интервала. Источник исполненных операций и агрегация selected остаются владельцами денег. ### qa N/A: самостоятельного репозитория QA нет; системные проверки описывает `qa-analysis.md` без введения несуществующей инфраструктуры. ## Decisions ### main Изменения не требуются: координация ссылками на Issue/Change, без workspace автоматизации ради одного API перехода. ### frontend Один предметный `PayoutCard` разместить на уровне `entities`, доступном страницам и компонентам других slices через public export. Он использует существующий `EntryCard` как визуальную основу; `EntryCard` не становится владельцем финансовой семантики. Презентационные данные передаются явным discriminated union по происхождению `executed | forecast | schedule`: исполненная запись содержит signed payment и точный timestamp; прогноз — nullable projected amount, dateKind и дату; график — nullable per-security сумму, отдельную nullable оценку и доступные даты/параметры выпуска. Ветка графика показывает дополнительные условия купона и дивиденда без размножения карточек. У отсутствующих значений текстовый fallback, ноль форматируется отдельно от неизвестного. Не вводить один универсальный произвольный набор optional props, допускающий ложную комбинацию «исполнено + оценка». Владельцы исходных данных адаптируют типизированный ответ непосредственно на границе использования либо через узкий mapper сущности для повторных callers: `LedgerEntryRow` передаёт точные купоны/дивиденды в `PayoutCard`, прочие операции оставляет на прежнем пути; `BrokerageEventCard` делает то же для coupon/dividend, сохраняя maturity/offer; страницы графиков заменяют только содержимое календарной карточки. Доход продолжает вызывать `LedgerEntryRow`, сохраняя `wrap`, HTML-список и occurrence key. Личная история инструмента сохраняет quantity/price/account details там, где они доступны. Ключи списков и порядок определяются caller, не самим компонентом. Удалить comparison-only display model, компонент блока, подписи и стили, относящиеся только к сравнениям. Не удалять предупреждение неполного selected или сохранённого snapshot. Обновить текущие fixtures и регрессии после передачи OpenAPI; генерация типов потребителя берёт только его локальную копию artifact. Альтернатива с одинаковым CSS для трёх существующих компонентов отвергнута: не обеспечивает фактическое переиспользование. Альтернатива с общим DTO backend отвергнута: источники и денежные смыслы различны, wire-контракт остальных API не меняется. ### backend Сократить план income-analytics до selected: сервис запрашивает один диапазон, не конструирует comparison totals/availability; план экранной синхронизации не включает предыдущий год и месяц. Сохранить независимый план current-month summary и серверный выбор точных типов купона/дивиденда. В DTO удалить `previous`, `yearComparison`, comparison DTO при отсутствии других callers; `dependencies` сохранить в исходной форме с единственной ролью `selected`. Текущий shared operations range reader использует индексированные имена resource keys для трёх периодов; после одного диапазона оставить корректную идентичность selected и убрать устаревший индексированный список. Сохраняем status/coverage/freshness и публикацию подтверждённого периода. Дневные/ недельные/месячные buckets остаются, так как их удаление не согласовано. Не вводить миграцию БД: убираются чтения и поля ответа, а не сохранённые операции или подтверждённое покрытие. Никакой пересборки прошлого дохода. ## Cross-cutting Implementation Существующая проверка пользователя/счёта и очистка личного query cache при смене подключения остаются на местах. Изменение количества планируемых диапазонов не добавляет work и не меняет поведение hourly attempt для других экранов. Существующие request ID, metadata попытки и статусы дают достаточную диагностику; новых событий или полей логирования не требуется. Пересчёта денег, валютной конвертации и нового кэша нет. ## Data / Storage Changes N/A: Prisma/SQLite schema и исторические записи не меняются; выбранное покрытие продолжает подтверждаться до публикации окончательного результата. ## Contract Implementation Backend обновляет DTO и исполняемый generated OpenAPI. Для обоих income-analytics маршрутов проверить отсутствие `previous`/`yearComparison` и наличие только `selected` в dependency role при сохранении nullable `data`, остальных полей и Problem Details. Сверить обе схемы 200 с [target JSON Schema](system-analysis/contracts/income-analytics-response.schema.json) семантически, а не только с TypeScript shape; существующую проверку parity runtime OpenAPI также сохранить. Передать созданный backend artifact frontend явно; frontend обновляет snapshot и generated types штатной локальной процедурой, без чтения соседнего checkout в сборке. Прежний клиент, требующий удалённых полей, несовместим. ## Developer Verification Strategy Backend: адресные проверки планирования только выбранного интервала, чтения income summary/analytics, точных signed сумм и недоступного selected; проверка HTTP контракта и OpenAPI parity. Frontend: наблюдаемое отсутствие блоков сравнений в loaded/loading/error/empty; сохранение period URL, порядка/повторов выплат и корректные режимы общего компонента на всех потребителях. После локальных узких проверок выполнить обязательный verify каждого изменённого implementation repo. Это developer-owned проверки; системная матрица отдельно в `qa-analysis.md`. ## UI/UX Package **Applicability and scope:** changed UI; пакет полностью inline, новых изображений и assets не требуется. Один approval Design относится к этой ревизии и приведённому ниже точному источнику переиспользования. **Package revision:** `1`, change `unify-income-payment-cards`. **Screens, states and interactions:** доход — фильтр, итоги, плоский список исполненных выплат, без сравнений; прогноз на странице событий/превью — пометка «Прогноз», неизвестная сумма отлична от нуля, дата реестра помечена; графики акции/облигации — «Событие по графику», разные подписи per-share/ per-bond и личной оценки. В истории операций карточка сохраняет описание, дату и доступную identity инструмента, signed Money и детали там, где были. Loaded, empty, loading, error и обновление старого snapshot сохраняют текущие локальные границы экранов. Оферта, погашение, сделка и налог не получают режим выплаты. Компонент сам не переключает состояние и не инициирует запрос. **Responsive behavior:** однотактный проход по карточкам без горизонтального overflow при узкой ширине и длинных значениях; labels, значения и статусы читаются текстом и клавиатурой, цвет остаётся дополнительным признаком. Сохраняются доступные имена региона и фокус элементов фильтра. **Reused source and differences:** immutable locator `wealth-ledger-specs:openspec/changes/archive/2026-09-09-simplify-income-page/design.md` (секция «Одна карточка соответствует одной выплате») — плоский income список, одна карточка на запись, signed Money и сохранение порядка. Дополнить только единым предметным presentation для прогнозов/графиков с явными подписями происхождения; не переносить прогноз внутрь income страницы. Визуальная основа реализации — существующий `EntryCard`; его геометрия остаётся общим стилем, а не заменой `PayoutCard`. Пакет целиком inline. ## Risks / Trade-offs - Разные даты и null-поля трёх источников провоцируют неверные labels при слишком широком mapper: явные варианты данных и локальные проверки экранов важнее экономии нескольких props. - Для календаря бумаги нет логотипа/тикера в каждой записи: владелец страницы передаёт известную identity, не создавая новый запрос. - Меньше загружаемых диапазонов снижает объём работы, но отсутствие сравнения может изменить порядок фонового покрытия старых периодов; цель сохранения другой независимой истории остаётся прежней. ## Migration / Rollout / Rollback Сначала обновить backend DTO/логику и получить опубликованный OpenAPI, затем передать artifact frontend и обновить UI/типизацию; согласованно поставить две стороны breaking ответа. Не выпускать старый frontend против нового backend как поддерживаемую комбинацию. При rollback вернуть согласованную предыдущую пару backend/frontend и прежний OpenAPI snapshot; сохранённые операции и покрытие не требуют data rollback. Синхронизация canonical specs и AS-IS docs выполняется по отдельным approved tasks после реализации. ## Solution Assessment N/A: не создаётся новая инфраструктура, интеграция, storage, кэш или система аутентификации. Существующие `EntryCard`, owner-slices и DTO сохраняются; сопоставление альтернатив общего UI дано в решении frontend.