fix(tradein/pdf): sold-price consistency + актуальные допущения + свой дизайн отчёта #2327

Merged
lekss361 merged 1 commit from feat/tradein-pdf-redesign into main 2026-07-03 21:43:40 +00:00
Owner

Summary

PDF-отчёт Trade-In Estimator (tradein-mvp/backend/app/services/exporters/trade_in_pdf.py) был визуально устаревшим (1:1 копия референса конкурента «Брусника») и содержал реальный баг рассинхронизации данных.

  • Bug fix: offer-страница (стр. 4) считала выкупную цену как median − 10% (плоский хайркат), игнорируя estimate.expected_sold_price_rub — ту же реальную цифру по ДКП Росреестра, которую cover-страница (стр. 1) уже показывает как «Ожидаемая цена продажи». Расхождение между страницами одного PDF доходило до 10%+ на реальных данных. Теперь offer использует expected_sold_price_rub с graceful fallback на старую эвристику для старых оценок без sold-данных (зеркалит паттерн _discount_pct/_dual_price_block).
  • Актуализация допущений (research 2026, Циан/УПН/Домклик): риэлтор 2→2-5%, аренда 90-150к→84-135к/3мес, юр.сопровождение фикс 19 250→диапазон 15-50к, реклама 4-36к→6-45к, дисконт запрос→сделка «5-12%»→«10-18%» (актуально для Екатеринбурга 2026).
  • Свой дизайн вместо копии конкурента: палитра сконвертирована точной OKLab→sRGB матрицей из живых OKLCH-токенов веб-продукта (trade-in.css) — не выдумана заново; типографика DejaVu Serif (заголовки) / Sans (текст) / Sans Mono tabular-nums (все цифры) — гарантированно установлены в Docker-образе (fonts-dejavu-core), заодно чинит silent font-fallback (CSS раньше объявлял Helvetica/Arial, которых в контейнере физически нет). Унифицированы page-headers, price-range бары переписаны с faux-3D table-хака на чистый div, advantage-карточки на новом тёплом accent. Security guards (_safe_url, _safe_color, _make_safe_url_fetcher, allowlists) не тронуты.
  • Полировка: неразрывные пробелы в денежных figures (числа/₽ больше не переносятся посередине строки), нормализация тире, явные ширины колонок offer-таблицы.

Test plan

  • pytest tests/test_pdf_security.py tests/services/test_trade_in_pdf_dual_price.py — 42/42 passed
  • ruff check + ruff-format — чисто
  • Реальный рендер через Docker (prod-идентичный образ, weasyprint 69.0) + растеризация — визуально проверены все 4 страницы, pdffonts подтвердил embedded DejaVu Serif/Sans/Mono (не silent fallback), bug fix подтверждён численно на тестовых данных (cover и offer показывают одинаковую sold-цену)
  • code-reviewer pass — APPROVE, без блокеров
## Summary PDF-отчёт Trade-In Estimator (`tradein-mvp/backend/app/services/exporters/trade_in_pdf.py`) был визуально устаревшим (1:1 копия референса конкурента «Брусника») и содержал реальный баг рассинхронизации данных. - **Bug fix**: offer-страница (стр. 4) считала выкупную цену как `median − 10%` (плоский хайркат), игнорируя `estimate.expected_sold_price_rub` — ту же реальную цифру по ДКП Росреестра, которую cover-страница (стр. 1) уже показывает как «Ожидаемая цена продажи». Расхождение между страницами одного PDF доходило до 10%+ на реальных данных. Теперь offer использует `expected_sold_price_rub` с graceful fallback на старую эвристику для старых оценок без sold-данных (зеркалит паттерн `_discount_pct`/`_dual_price_block`). - **Актуализация допущений** (research 2026, Циан/УПН/Домклик): риэлтор 2→2-5%, аренда 90-150к→84-135к/3мес, юр.сопровождение фикс 19 250→диапазон 15-50к, реклама 4-36к→6-45к, дисконт запрос→сделка «5-12%»→«10-18%» (актуально для Екатеринбурга 2026). - **Свой дизайн** вместо копии конкурента: палитра сконвертирована точной OKLab→sRGB матрицей из живых OKLCH-токенов веб-продукта (`trade-in.css`) — не выдумана заново; типографика DejaVu Serif (заголовки) / Sans (текст) / Sans Mono tabular-nums (все цифры) — гарантированно установлены в Docker-образе (`fonts-dejavu-core`), заодно чинит silent font-fallback (CSS раньше объявлял Helvetica/Arial, которых в контейнере физически нет). Унифицированы page-headers, price-range бары переписаны с faux-3D table-хака на чистый div, advantage-карточки на новом тёплом accent. Security guards (`_safe_url`, `_safe_color`, `_make_safe_url_fetcher`, allowlists) не тронуты. - Полировка: неразрывные пробелы в денежных figures (числа/₽ больше не переносятся посередине строки), нормализация тире, явные ширины колонок offer-таблицы. ## Test plan - [x] `pytest tests/test_pdf_security.py tests/services/test_trade_in_pdf_dual_price.py` — 42/42 passed - [x] `ruff check` + `ruff-format` — чисто - [x] Реальный рендер через Docker (prod-идентичный образ, weasyprint 69.0) + растеризация — визуально проверены все 4 страницы, `pdffonts` подтвердил embedded DejaVu Serif/Sans/Mono (не silent fallback), bug fix подтверждён численно на тестовых данных (cover и offer показывают одинаковую sold-цену) - [x] code-reviewer pass — ✅ APPROVE, без блокеров
lekss361 added 1 commit 2026-07-03 21:38:34 +00:00
fix(tradein/pdf): offer-страница больше не расходится с cover + актуальные допущения + свой дизайн
All checks were successful
CI Trade-In / changes (pull_request) Successful in 8s
CI / changes (pull_request) Successful in 7s
CI Trade-In / frontend-checks (pull_request) Has been skipped
CI / backend-tests (pull_request) Has been skipped
CI / frontend-tests (pull_request) Has been skipped
CI / openapi-codegen-check (pull_request) Has been skipped
CI Trade-In / backend-tests (pull_request) Successful in 1m38s
13082653ec
Offer-страница PDF-отчёта считала выкупную цену как медиана-10% (плоский
хайркат), игнорируя estimate.expected_sold_price_rub — ту же реальную
цифру по ДКП Росреестра, которую cover-страница уже показывает как
«Ожидаемая цена продажи». Из-за этого страницы 1 и 4 одного отчёта могли
показывать разные цифры (расхождение 10%+ на реальных данных). Теперь
offer использует expected_sold_price_rub с fallback на старую эвристику
для старых оценок без sold-данных.

Заодно: актуализированы захардкоженные допущения о расходах при
самопродаже (риэлтор 2-5%, аренда 84-135к/3мес, юр.сопровождение
15-50к, реклама 6-45к, дисконт запрос-сделка 10-18% для ЕКБ 2026) по
данным Циан/УПН/Домклик; и собственная визуальная система вместо копии
референса конкурента (Брусника) — палитра сконвертирована точной
OKLab->sRGB матрицей из живых OKLCH-токенов веб-продукта (trade-in.css),
типографика DejaVu Serif/Sans/Mono (гарантированно установлены в образе,
чинит попутно silent font fallback на Helvetica/Arial, которых там нет).
lekss361 merged commit 7fd80adfa7 into main 2026-07-03 21:43:40 +00:00
lekss361 deleted branch feat/tradein-pdf-redesign 2026-07-03 21:43:41 +00:00
Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference: lekss361/gendesign#2327
No description provided.