diff --git a/tradein-mvp/backend/app/services/exporters/trade_in_pdf.py b/tradein-mvp/backend/app/services/exporters/trade_in_pdf.py index 08cb4ac3..425dda5f 100644 --- a/tradein-mvp/backend/app/services/exporters/trade_in_pdf.py +++ b/tradein-mvp/backend/app/services/exporters/trade_in_pdf.py @@ -566,7 +566,7 @@ def _page_footer( # тела страницы) и был источником сложности; заменён на простую тонкую # градиентную линию-разделитель между строками meta/wordmark. return f""" -
' "Запрос — по чему выставлены сопоставимые квартиры в объявлениях. " "Ожидаемая цена продажи — реалистичная цена сделки по данным ДКП " "Росреестра, обычно ниже запроса." @@ -869,7 +869,7 @@ def _dual_price_block(estimate: AggregatedEstimate, brand) -> str: # type: igno return ( f'
| № отчета | {_mono(report_num)} | ||||
| Дата отчета | @@ -1172,7 +1168,7 @@ def _build_cover(estimate: AggregatedEstimate, input_snapshot: dict, brand) -> s {_dual_price_block(estimate, brand)}
| Цены в объявлениях — ожидания собственников | {advice_discount_text} | @@ -1193,19 +1189,20 @@ def _build_cover(estimate: AggregatedEstimate, input_snapshot: dict, brand) -> sИнвестиции в 400 – 600 тыс. руб. повышают цену объекта всего на 150 – 250 тыс. руб — покупатель оценивает общее состояние квартиры |
| Неочевидные расходы при самостоятельной продаже | -При самостоятельной продаже суммарные расходы могут достигать - до 15% стоимости квартиры (торг, риелтор, нотариус, справки) | -
+
Этот отчёт онлайн: {settings.public_url}?id={estimate.estimate_id}
{disclaimer_html} - {footer_html} """ @@ -1294,23 +1291,11 @@ def _build_listings_page(estimate: AggregatedEstimate, input_snapshot: dict, bra top5 = sorted(estimate.analogs, key=lambda x: x.distance_m or 9999)[:5] examples_rows = _examples_rows(top5) - report_num = _report_number(estimate) - today = dt.date.today() - footer_html = _page_footer( - brand, - report_num, - today, - f"{_html.escape(brand.name)} · Анализ рынка вторичной недвижимости · стр. 2", - valid_until=_expires_date(estimate), - ) - heading_html = _section_heading("02", "РЫНОК КВАРТИР – АНАЛОГОВ ПО ОБЪЯВЛЕНИЯМ") return f"""+ Расчёт носит ориентировочный характер и не является офертой. +
""" @@ -1879,8 +1838,12 @@ def _build_css(brand=None) -> str: # type: ignore[no-untyped-def] }} @page {{ size: A4; - margin: 20mm 18mm 20mm 18mm; + margin: 25mm 18mm 19mm 18mm; + @top-center {{ content: element(runningHeader); vertical-align: bottom; }} + @bottom-center {{ content: element(runningFooter); vertical-align: top; }} }} +.running-header {{ position: running(runningHeader); }} +.running-footer {{ position: running(runningFooter); }} * {{ box-sizing: border-box; }} body {{ font-family: 'PT Sans', 'DejaVu Sans', sans-serif; @@ -2188,8 +2151,25 @@ def generate_trade_in_pdf( if estimate.insufficient_data: body_html = _build_insufficient_data_page(estimate, input_snapshot, brand) else: + # Running header/footer — единый HUD-хром печатается WeasyPrint'ом в page + # margin-boxes (@top-center/@bottom-center, см. _build_css) на КАЖДОЙ странице + # документа, а не только там, где явно вставлен в поток секции-билдера. + # Рендерится один раз здесь (не внутри каждого _build_*_page) — иначе WeasyPrint + # печатает независимый running-элемент с последнего "видимого" перед разрывом + # страницы, что дало бы 4 разных чанка вместо одного персистентного хрома. + _chrome_report_num = _report_number(estimate) + _chrome_today = dt.date.today() + _chrome_expires = _expires_date(estimate) + chrome_html = ( + f'