fix(generative): устаревший VAT/office caveat в concept PDF (#2445-C1) (#2455)
This commit is contained in:
parent
c0c666cccc
commit
f3988add0a
2 changed files with 28 additions and 9 deletions
|
|
@ -66,9 +66,7 @@ def _strategy_label(strategy: str) -> str:
|
|||
|
||||
def _teap_table(variants: Sequence[ConceptVariant]) -> str:
|
||||
"""HTML-таблица ТЭП по всем вариантам (строки — показатели, колонки — стратегии)."""
|
||||
headers = "".join(
|
||||
f"<th>{html.escape(_strategy_label(v.strategy))}</th>" for v in variants
|
||||
)
|
||||
headers = "".join(f"<th>{html.escape(_strategy_label(v.strategy))}</th>" for v in variants)
|
||||
rows: list[tuple[str, list[str]]] = [
|
||||
("Пятно застройки, кв.м", [_fmt_int(v.teap.built_area_sqm) for v in variants]),
|
||||
("Общая площадь (GFA), кв.м", [_fmt_int(v.teap.total_floor_area_sqm) for v in variants]),
|
||||
|
|
@ -107,9 +105,7 @@ def _fmt_irr(financial: FinancialModel) -> str:
|
|||
|
||||
def _financial_table(variants: Sequence[ConceptVariant]) -> str:
|
||||
"""HTML-таблица финмодели (деньги в млн руб; полный каскад + БДР + DCF)."""
|
||||
headers = "".join(
|
||||
f"<th>{html.escape(_strategy_label(v.strategy))}</th>" for v in variants
|
||||
)
|
||||
headers = "".join(f"<th>{html.escape(_strategy_label(v.strategy))}</th>" for v in variants)
|
||||
rows: list[tuple[str, list[str]]] = [
|
||||
(
|
||||
"Выручка — жильё, млн руб",
|
||||
|
|
@ -188,9 +184,13 @@ def _build_html(variants: Sequence[ConceptVariant]) -> str:
|
|||
"точность метрик зависит от реального графика. Где IRR помечен «оценочный» — поток "
|
||||
"вырожденный (нет смены знака), показан аннуализированный ROI вместо DCF-IRR. "
|
||||
"НДС — реализация жилья и услуги застройщика по ДДУ освобождены (ст. 149 НК РФ), "
|
||||
"входной НДС по СМР встроен в себестоимость; НДС начисляется только на нежилую часть "
|
||||
"(машиноместа). Налог на прибыль — 25% (с 2025). Цены и себестоимость "
|
||||
"— рыночные ориентиры. Коммерческие/офисные площади не учитываются (нет в ТЭП).</p>"
|
||||
"входной НДС по СМР встроен в себестоимость; НДС начисляется на нежилые части — "
|
||||
"машиноместа и коммерцию/офисы 1-го этажа (встроенный НДС в добавленной стоимости "
|
||||
"каждой части). Налог на прибыль — 25% (с 2025). Цены и себестоимость — рыночные "
|
||||
"ориентиры. Коммерция/офисы 1-го этажа учтены по нормативной доле от общей площади "
|
||||
"и продаются с умеренной наценкой к цене жилья того же класса; себестоимость СМР "
|
||||
"нежилого — по той же ставке, что и жильё (отдельной строки затрат нет, повторного "
|
||||
"учёта в затратах нет).</p>"
|
||||
"</body></html>"
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -119,6 +119,25 @@ def test_pdf_html_build_graceful_on_empty() -> None:
|
|||
assert "нет вариантов" in html
|
||||
|
||||
|
||||
def test_pdf_caveat_does_not_contradict_office_revenue_row() -> None:
|
||||
"""Эпик #2445 C1: каскад включает нежилую выручку/НДС — caveat не должен это отрицать.
|
||||
|
||||
Payload — ``comfort`` (office_share=0.05 в teap.py), поэтому строка "Выручка —
|
||||
нежилое (1-й этаж)" в таблице ненулевая: caveat ниже НЕ может утверждать, что
|
||||
коммерция/офисы "не учитываются" или что НДС — "только машиноместа".
|
||||
"""
|
||||
variants = geometry.generate(_payload())
|
||||
html = pdf._build_html(variants)
|
||||
assert "Выручка — нежилое (1-й этаж), млн руб" in html
|
||||
assert "НДС (нежилое: паркинг + коммерция), млн руб" in html
|
||||
# Старые (стейл) формулировки, отрицающие модель нежилого, — не должны вернуться.
|
||||
assert "не учитываются" not in html
|
||||
assert "только на нежилую часть" not in html
|
||||
assert "только машиноместа" not in html
|
||||
# Caveat честно описывает нежилое как учтённое (не «нет в ТЭП»).
|
||||
assert "нет в ТЭП" not in html
|
||||
|
||||
|
||||
@pytest.mark.skipif(
|
||||
not _weasyprint_available(),
|
||||
reason="WeasyPrint native libs unavailable on this host",
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue