fix(tradein/pdf): МЕРА branding + fix 'Год постройки None' + drop duplicate deals-range on cover

- Дефолт-бренд generic: name «Trade-In Estimator» → «МЕРА» (brand.py). Masthead/
  footer/подписи секций читают brand.name → отчёт теперь брендирован МЕРА;
  white-label бренды (реальное name в brands) не тронуты.
- Cover: year_built=None утекал литеральным «None» в «Год постройки» → «—».
- Cover: убран дубль-бар «Диапазон цен по фактическим сделкам» (уже есть на
  стр. 03) — часть ужимания cover до одной A4-страницы. Тесты обновлены.
This commit is contained in:
bot-backend 2026-07-13 20:37:48 +03:00
parent b5477362f0
commit 9a73da4ce0
3 changed files with 10 additions and 22 deletions

View file

@ -28,7 +28,7 @@ class Brand:
_DEFAULT = Brand(
slug="generic",
name="Trade-In Estimator",
name="МЕРА",
logo_url=None,
primary_color="#1d4ed8",
accent_color="#f59e0b",

View file

@ -1086,16 +1086,14 @@ def _build_cover(estimate: AggregatedEstimate, input_snapshot: dict, brand) -> s
house_label = house_labels.get(house_type, "") if house_type else ""
repair_label = repair_labels.get(repair_state, "Не указано") if repair_state else "Не указано"
balcony_label = "Есть" if has_balcony else "Нет" if has_balcony is False else ""
# #pdf-none: year_built может быть None (адрес без house_metadata) — раньше в
# таблицу утекало литеральное «None»; показываем «—» как в остальных пустых полях.
year_label = _mono(str(year_built)) if year_built else ""
# Срок экспозиции показываем ТОЛЬКО при реальных days_on_market в analogs;
# если данных нет — days не рисуем (не выдумываем «4-118 дней», #pdf-honesty).
days_range = _days_on_market_range(estimate.analogs)
# Deals range — если deals есть, считаем; иначе fallback к listings range
deals_low, deals_high = _deals_range(
estimate.actual_deals, fallback=(estimate.range_low_rub, estimate.range_high_rub)
)
listings_bar = _price_range_chart_svg(
estimate.range_low_rub,
estimate.range_high_rub,
@ -1121,12 +1119,6 @@ def _build_cover(estimate: AggregatedEstimate, input_snapshot: dict, brand) -> s
"Росреестр, ДомКлик и продажи агентств недвижимости"
)
deals_bar = _price_range_chart_svg(
deals_low,
deals_high,
sub_label="Диапазон цен по фактическим сделкам",
)
disclaimer_html = ""
if brand.pdf_disclaimer:
disclaimer_html = (
@ -1160,7 +1152,7 @@ def _build_cover(estimate: AggregatedEstimate, input_snapshot: dict, brand) -> s
<td class="bold dotted-row">до {_mono(expires.strftime("%d.%m.%Y"))}</td></tr>
<tr><td class="dotted-row">Адрес</td><td class="bold dotted-row">{address}</td></tr>
<tr><td class="dotted-row">Год постройки</td>
<td class="bold dotted-row">{_mono(str(year_built))}</td></tr>
<td class="bold dotted-row">{year_label}</td></tr>
<tr><td class="dotted-row">Тип дома</td><td class="bold dotted-row">{house_label}</td></tr>
<tr><td class="dotted-row">Этаж / этажность</td>
<td class="bold dotted-row">{_mono(f"{floor} / {total_floors}")}</td></tr>
@ -1187,13 +1179,6 @@ def _build_cover(estimate: AggregatedEstimate, input_snapshot: dict, brand) -> s
{listings_bar}
</div>
<div style="break-inside:avoid;page-break-inside:avoid;">
<p style="margin:6pt 0 2pt 0;font-size:{_FS_MD};font-weight:700;">
Диапазон цен по фактическим сделкам
</p>
{deals_bar}
</div>
<!-- Что важно при оценке -->
<h3 style="margin-top:8pt;font-size:{_FS_MD};font-weight:700;">
Что важно при оценке стоимости квартиры:

View file

@ -233,7 +233,9 @@ def test_build_cover_with_sold_contains_dual_block() -> None:
assert "Ожидаемая цена продажи" in html
# existing sections untouched
assert "Диапазон цен в объявлениях" in html
assert "Диапазон цен по фактическим сделкам" in html
# #pdf-empty-pages: дубль-бар «по фактическим сделкам» убран с cover
# (остался на стр. 03) — cover ужат до одной A4-страницы.
assert "Диапазон цен по фактическим сделкам" not in html
def test_build_cover_without_sold_renders_only_asking() -> None:
@ -243,7 +245,8 @@ def test_build_cover_without_sold_renders_only_asking() -> None:
assert "Ожидаемая цена продажи" not in html
# existing layout intact
assert "Диапазон цен в объявлениях" in html
assert "Диапазон цен по фактическим сделкам" in html
# #pdf-empty-pages: дубль-бар «по фактическим сделкам» убран с cover (см. выше).
assert "Диапазон цен по фактическим сделкам" not in html
# ── _days_on_market_range — no fabricated (4, 118) corridor (#pdf-honesty) ─────