Объединяет наше (площади из реальной геометрии teap) + полноту девелоперской Excel-модели (каскад затрат + БДР с налогами). Расширяет financial.py из статического GDV−COST в полный статический расчёт. БЕЗ DCF (PR-3), БЕЗ новых вводных от пользователя — только нормативы + существующий TEAP. Каскад (financial.py): - Выручка: жильё (residential×price) + ПАРКИНГ (spaces×1.9М) - Затраты: СМР(резид+паркинг) + ПИР 2.5% + сети/ТУ 6000₽/м² + услуги заказчика 1.5% + резерв 3% + маркетинг/риэлтор 7% выручки + земля - БДР: валовая маржа → НДС (упрощённо) → прибыль до налога → налог 25% (ФЗ 176-ФЗ) → ЧИСТАЯ прибыль → ROI + чистая маржа% - Нормативы документированы (источник: Excel-эталон «Авангардная 13» + RU-рынок) FinancialModel (concept.py): +16 additive-полей (revenue breakdown, cost cascade, БДР/налоги, roi, margin_pct, irr_is_proxy). Старые 4 поля сохранены (backward-compat). IRR честно помечен irr_is_proxy=True (annualized net ROI, не DCF) — настоящий IRR/NPV/PBP в PR-3 (помесячный cashflow). НДС — документированное упрощение, не точный НК РФ. Офисы пропущены (нет площади в TEAP). PDF + concept UI: полный БДР-каскад + чистая прибыль/ROI + честные сноски. api-types.ts регенерён авторитетно (openapi-typescript + prettier, как CI). mypy strict clean (generative.*), +тесты (каскад/паркинг/налоги/backward-compat/ edge cost=0), 36 passed. Refs #1881
82 lines
3.2 KiB
Python
82 lines
3.2 KiB
Python
from typing import Any, Literal
|
||
|
||
from pydantic import BaseModel, Field
|
||
|
||
|
||
class ConceptInput(BaseModel):
|
||
"""Stage 1a — input contract. Frozen interface for frontend codegen."""
|
||
|
||
parcel_geojson: dict[str, Any] = Field(
|
||
..., description="GeoJSON Polygon of the parcel (WGS84 / EPSG:4326)"
|
||
)
|
||
housing_class: Literal["econom", "comfort", "business"] = "comfort"
|
||
target_floors: int = Field(9, ge=1, le=30)
|
||
development_type: Literal["spot", "mid_rise", "high_rise"] = "mid_rise"
|
||
land_cost_rub: float | None = Field(
|
||
None, ge=0, description="Optional land cost for financial model"
|
||
)
|
||
|
||
|
||
class TEAP(BaseModel):
|
||
"""Технико-экономические показатели."""
|
||
|
||
built_area_sqm: float
|
||
total_floor_area_sqm: float
|
||
residential_area_sqm: float
|
||
apartments_count: int
|
||
density: float
|
||
parking_spaces: int
|
||
|
||
|
||
class FinancialModel(BaseModel):
|
||
"""Static developer P&L for a concept variant (PR-1, эпик #1881).
|
||
|
||
Full static cost cascade + VAT + profit tax → net profit + ROI. NOT a DCF —
|
||
timing/discounting lands in PR-3. VAT is a documented simplification (see
|
||
``compute_financial`` docstring), not exact НК РФ mechanics.
|
||
|
||
The legacy summary fields (``revenue_rub`` / ``cost_rub`` /
|
||
``gross_margin_rub`` / ``irr``) are kept for backward-compat; the new fields
|
||
expose the full cascade and the net P&L.
|
||
"""
|
||
|
||
# ── Legacy summary (backward-compat) ───────────────────────────────────────
|
||
revenue_rub: float
|
||
cost_rub: float
|
||
gross_margin_rub: float
|
||
irr: float
|
||
|
||
# ── Revenue breakdown ──────────────────────────────────────────────────────
|
||
revenue_residential_rub: float
|
||
revenue_parking_rub: float
|
||
|
||
# ── Cost cascade ───────────────────────────────────────────────────────────
|
||
construction_rub: float
|
||
pir_rub: float
|
||
networks_rub: float
|
||
developer_services_rub: float
|
||
contingency_rub: float
|
||
marketing_rub: float
|
||
land_rub: float
|
||
|
||
# ── БДР / taxes → net profit ───────────────────────────────────────────────
|
||
vat_rub: float
|
||
profit_before_tax_rub: float
|
||
profit_tax_rub: float
|
||
net_profit_rub: float
|
||
|
||
# ── Metrics ────────────────────────────────────────────────────────────────
|
||
roi: float # net profit / total cost
|
||
margin_pct: float # net profit / revenue
|
||
irr_is_proxy: bool = True # frontend caveat: not a real DCF IRR (см. PR-3)
|
||
|
||
|
||
class ConceptVariant(BaseModel):
|
||
strategy: Literal["max_area", "max_insolation", "balanced"]
|
||
buildings_geojson: dict[str, Any]
|
||
teap: TEAP
|
||
financial: FinancialModel
|
||
|
||
|
||
class ConceptOutput(BaseModel):
|
||
variants: list[ConceptVariant]
|