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 ead22a51..6e46660d 100644
--- a/tradein-mvp/backend/app/services/exporters/trade_in_pdf.py
+++ b/tradein-mvp/backend/app/services/exporters/trade_in_pdf.py
@@ -1600,7 +1600,7 @@ def _build_deals_page(estimate: AggregatedEstimate, input_snapshot: dict, brand)
# ── Page 4: Offer (Trade-In vs Самопродажа) ──────────────────────────────────
-def _build_offer_page(estimate: AggregatedEstimate, brand) -> str: # type: ignore[no-untyped-def]
+def _build_offer_page(estimate: AggregatedEstimate, input_snapshot: dict, brand) -> str: # type: ignore[no-untyped-def,type-arg]
median = estimate.median_price_rub
# Расчёт расходов: часть — на основе медианы (торг «от цены в объявлении»),
@@ -1627,8 +1627,24 @@ def _build_offer_page(estimate: AggregatedEstimate, brand) -> str: # type: igno
rieltor_low = int(sold_price * rieltor_pct_low / 100)
rieltor_high = int(sold_price * rieltor_pct_high / 100)
- # 3 месяца аренды 1-комнатной квартиры, ЕКБ 2026: ~28-45 тыс. руб./мес.
- rent_low, rent_high = 84_000, 135_000
+ # 3 месяца аренды квартиры, ЕКБ 2026 (базовая ставка — студия/1-комн., ~28-45
+ # тыс. руб./мес.), масштабируется по числу комнат объекта (#pdf-honesty: раньше
+ # был плоский 1-комнатный тариф для ЛЮБОГО объекта, включая 3-4-комнатные).
+ # Ориентировочные месячные ставки аренды по комнатности, ЕКБ 2026:
+ # студия/1к ~28-45k, 2к ~40-60k, 3к ~55-80k, 4к+ ~75-110k.
+ # Множители — грубое отношение верхних границ этих диапазонов к базовой (1к).
+ rooms = int(input_snapshot.get("rooms", 0) or 0)
+ if rooms <= 1:
+ rent_multiplier = 1.0
+ elif rooms == 2:
+ rent_multiplier = 1.35
+ elif rooms == 3:
+ rent_multiplier = 1.8
+ else:
+ rent_multiplier = 2.4
+ rent_base_low, rent_base_high = 84_000, 135_000
+ rent_low = round(rent_base_low * rent_multiplier / 1000) * 1000
+ rent_high = round(rent_base_high * rent_multiplier / 1000) * 1000
# Юридическое сопровождение сделки — диапазон (не фикс), варьируется по объёму работ.
juridical_low, juridical_high = 15_000, 50_000
# Реклама/продвижение за 3 месяца — низкая уверенность в оценке (нет единого
@@ -1709,7 +1725,8 @@ def _build_offer_page(estimate: AggregatedEstimate, brand) -> str: # type: igno
|
Аренда после сделки
- однокомнатной квартиры на 3 месяца
+
+ {f"{rooms}-комн. квартиры на 3 месяца" if rooms > 0 else "квартиры на 3 месяца"}
|
бесплатно |
@@ -2190,7 +2207,7 @@ def generate_trade_in_pdf(
_build_cover(estimate, input_snapshot, brand)
+ _build_listings_page(estimate, input_snapshot, brand)
+ _build_deals_page(estimate, input_snapshot, brand)
- + _build_offer_page(estimate, brand)
+ + _build_offer_page(estimate, input_snapshot, brand)
)
html_str = (
diff --git a/tradein-mvp/backend/tests/services/test_trade_in_pdf_dual_price.py b/tradein-mvp/backend/tests/services/test_trade_in_pdf_dual_price.py
index 2f405445..4d5cf26e 100644
--- a/tradein-mvp/backend/tests/services/test_trade_in_pdf_dual_price.py
+++ b/tradein-mvp/backend/tests/services/test_trade_in_pdf_dual_price.py
@@ -349,3 +349,44 @@ def test_deals_banner_neutral_when_no_discount() -> None:
_assert_well_formed(html)
assert "10–18%" not in html
assert "продаются дешевле, чем заявлено" in html
+
+
+# ── _build_offer_page — rent scales with room count, not a flat 1-room figure ──
+# (#pdf-honesty LOW audit R2 #5: «Общие финансовые потери» hardcoded a 1-room
+# 3-month rent for EVERY object, including 3-4-room ones.)
+
+
+def test_offer_page_rent_scales_up_for_larger_objects() -> None:
+ est = _estimate()
+ snapshot_1room = {**_SNAPSHOT, "rooms": 1}
+ snapshot_3room = {**_SNAPSHOT, "rooms": 3}
+
+ html_1room = mod._build_offer_page(est, snapshot_1room, _BRAND)
+ html_3room = mod._build_offer_page(est, snapshot_3room, _BRAND)
+ _assert_well_formed(html_1room)
+ _assert_well_formed(html_3room)
+
+ # Base (1-room) rent range unchanged: 84 000 – 135 000.
+ assert mod._fmt_rub(84_000) in html_1room
+ assert mod._fmt_rub(135_000) in html_1room
+ # 3-room rent scaled up (×1.8 heuristic): 151 000 – 243 000.
+ assert mod._fmt_rub(151_000) in html_3room
+ assert mod._fmt_rub(243_000) in html_3room
+ # The flat 1-room figure must NOT leak into the 3-room object's report.
+ assert mod._fmt_rub(135_000) not in html_3room
+
+
+def test_offer_page_rent_subtitle_reflects_room_count() -> None:
+ est = _estimate()
+
+ html_1room = mod._build_offer_page(est, {**_SNAPSHOT, "rooms": 1}, _BRAND)
+ assert "1-комн. квартиры на 3 месяца" in html_1room
+ assert "однокомнатной квартиры на 3 месяца" not in html_1room
+
+ html_3room = mod._build_offer_page(est, {**_SNAPSHOT, "rooms": 3}, _BRAND)
+ assert "3-комн. квартиры на 3 месяца" in html_3room
+
+ # Unknown room count (0/missing) → neutral subtitle, no fabricated room number.
+ html_unknown = mod._build_offer_page(est, {**_SNAPSHOT, "rooms": 0}, _BRAND)
+ assert "квартиры на 3 месяца" in html_unknown
+ assert "-комн. квартиры на 3 месяца" not in html_unknown