Files
specs/openspec/changes/unify-income-payment-cards/design.md
T
codex-bot 6c858a8858
Specs security / security (push) Successful in 9s
Specs verify / verify (push) Successful in 12s
spec: unify income payment cards and drop period comparisons (WealthLedger/main#33) (#37)
spec: unify income payment cards and drop period comparisons (WealthLedger/main#33)
2026-10-13 18:16:27 +03:00

209 lines
16 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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.