From eb0f0827902375e738d6b9f14516bfde7397686e Mon Sep 17 00:00:00 2001 From: bot-backend Date: Fri, 14 Aug 2026 15:25:00 +0300 Subject: [PATCH] =?UTF-8?q?fix(tradein/pdf):=20135-=D0=A4=D0=97=20=D0=B4?= =?UTF-8?q?=D0=B8=D1=81=D0=BA=D0=BB=D0=B5=D0=B9=D0=BC=D0=B5=D1=80=20=D0=B2?= =?UTF-8?q?=20=D1=84=D1=83=D1=82=D0=B5=D1=80=D0=B5=20=D0=BA=D0=B0=D0=B6?= =?UTF-8?q?=D0=B4=D0=BE=D0=B9=20=D1=81=D1=82=D1=80=D0=B0=D0=BD=D0=B8=D1=86?= =?UTF-8?q?=D1=8B=20(=D0=91=D0=BB=D0=BE=D0=BA=204.2)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Running @bottom-center margin-box печатал только мета/wordmark — юр-требование (индикативный расчёт, не отчёт об оценке по 135-ФЗ) отсутствовало на всех 4 страницах. Бюджет высоты подвала = margin-bottom (19mm≈53.9pt) уже был заполнен почти впритык (~51pt) после 42a50cf8 (ровно 4 страницы без пустых). Сжат существующий HUD-хром внутри _page_footer (margin-top 6→4pt, padding-top 8→6pt, line-height мета/wordmark 1.35→1.15, разделитель margin 6pt 0→3pt 0, экономия ~13pt) + добавлен текст дисклеймера отдельным блоком (5pt/line-height 1.15, ~3 строки ≈17pt). Экономии внутри подвала не хватило без деградации до нечитаемого — минимально поднят @page margin-bottom 19mm→21mm (+2mm). Реальный WeasyPrint-рендер (native Pango/cairo) недоступен на Windows-деве — пагинация (риск отката к 5-й пустой странице из-за margin-bottom на всех 4 страницах) не подтверждена локально, арифметика в docstring _page_footer. --- .../app/services/exporters/trade_in_pdf.py | 54 ++++++++++++++++--- 1 file changed, 48 insertions(+), 6 deletions(-) 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 8e02aae4..ef896560 100644 --- a/tradein-mvp/backend/app/services/exporters/trade_in_pdf.py +++ b/tradein-mvp/backend/app/services/exporters/trade_in_pdf.py @@ -238,6 +238,11 @@ _FS_MD = "10.5pt" # базовый текст (body), значения в та _FS_LG = "13pt" # заголовки страниц (h2, PT Serif) _FS_XL = "16pt" # главный заголовок cover (h1, PT Serif) _FS_XXL = "22pt" # крупные ценовые цифры (dual-price блок) +# Намеренное исключение из 6-шаговой шкалы: running-footer — @page margin-box с +# фиксированной высотой (19mm ≈ 53.9pt), делить с mono-мета-строкой/wordmark +# практически нечем (см. _page_footer). 135-ФЗ дисклеймер (Блок 4.2) должен +# влезать в ~380-450 симв. на каждой странице без пятой пустой страницы. +_FS_XXS = "5pt" # ТОЛЬКО 135-ФЗ футер-дисклеймер (_page_footer) — не переиспользовать # ── Embedded fonts (PT Sans / PT Serif, ParaType, SIL OFL 1.1) ────────────── @@ -528,6 +533,20 @@ def _page_header(brand, report_num: str, report_date: dt.date) -> str: # type: ) +# Блок 4.2 юр-требований: должен печататься в подвале КАЖДОЙ страницы отчёта +# (не только cover). Текст утверждён владельцем продукта дословно — не менять +# формулировку без явного запроса. Заведён как модульная константа (не inline +# в _page_footer), чтобы не расползалась по нескольким билдерам страниц. +_PDF_135FZ_FOOTER_NOTICE = ( + "Документ содержит индикативный (ориентировочный) расчёт стоимости объекта, " + "сформированный автоматически сервисом «МЕРА». Не является отчётом об оценке " + "по Федеральному закону № 135-ФЗ и не имеет установленной этим законом " + "юридической силы. Не предназначен для использования при ипотечном " + "кредитовании, в судебных разбирательствах, нотариальных действиях и иных " + "случаях, где законом предусмотрено обязательное проведение независимой оценки." +) + + def _page_footer( brand, # type: ignore[no-untyped-def] report_num: str, @@ -542,7 +561,10 @@ def _page_footer( строка 1 — mono meta (№ отчёта / дата / срок действия); тонкая градиентная линия-разделитель; строка 2 — точка акцента + wordmark (brand.name — НЕ - хардкод «МЕРА», white-label остаётся рабочим). Номер версии продукта здесь + хардкод «МЕРА», white-label остаётся рабочим); строка 3 — 135-ФЗ дисклеймер + (Блок 4.2, _PDF_135FZ_FOOTER_NOTICE) — печатается на КАЖДОЙ странице, т.к. + footer рендерится один раз как running @page margin-box (см. вызов в + generate_trade_in_pdf), а не per-page. Номер версии продукта здесь НЕ дублируется — единственное место вывода версии в PDF — running-header (_page_header → product_version_line()); раньше рядом с wordmark висел decorative "vN.N.N" (_REPORT_ENGINE_VERSION), не связанный с реальной @@ -552,6 +574,17 @@ def _page_footer( на офер-странице), которого нет в веб-референсе (там нет пагинации). Не удалён — вынесен приглушённой строкой НАД HUD-баром, чтобы не терять полезную для печатного многостраничного отчёта информацию. + + #footer-height-budget (2026-08-14, Блок 4.2): @bottom-center margin-box + высотой = page margin-bottom (см. _build_css). Добавление 135-ФЗ текста + (~440 симв.) потребовало И сжать существующий HUD-хром (margin-top + 6→4pt, padding-top 8→6pt, line-height мета/wordmark строк 1.35→1.15, + градиент-разделитель margin 6pt 0→3pt 0 — экономия ~13pt), И минимально + поднять @page margin-bottom (19mm→21mm, +2mm/+5.67pt) — сжатия одного + подвала было недостаточно без деградации до нечитаемого. Риск: margin-bottom + режет тело КАЖДОЙ из 4 страниц → потенциальный откат к 5-й почти пустой + странице (регрессия, чинившаяся в 42a50cf8) — реальным рендером + (WeasyPrint/Pango, недоступен на Windows-деве) не подтверждено, см. PR. """ note_html = "" if page_note: @@ -583,18 +616,18 @@ def _page_footer( # тела страницы) и был источником сложности; заменён на простую тонкую # градиентную линию-разделитель между строками meta/wordmark. return f""" -
+
{note_html} -
+ color:{_MUTED_2};line-height:1.15;">
ОТЧЁТ {_html.escape(report_num)} ДАТА {report_date.strftime("%d.%m.%Y")} {valid_until_html}
-
@@ -606,6 +639,11 @@ def _page_footer( {_html.escape(brand.name).upper()}
+
+ {_html.escape(_PDF_135FZ_FOOTER_NOTICE)} +
""" @@ -1962,7 +2000,11 @@ def _build_css(brand=None) -> str: # type: ignore[no-untyped-def] }} @page {{ size: A4; - margin: 25mm 18mm 19mm 18mm; + /* bottom 19mm→21mm (#footer-height-budget, Блок 4.2): +2mm — минимум, + которого не хватило внутри @bottom-center margin-box (высота margin-box + = margin-bottom) даже после сжатия HUD-хрома _page_footer под 135-ФЗ + дисклеймер на каждой странице. См. арифметику в _page_footer(). */ + margin: 25mm 18mm 21mm 18mm; @top-center {{ content: element(runningHeader); vertical-align: bottom; }} @bottom-center {{ content: element(runningFooter); vertical-align: top; }} }}