gendesign/backend/app/schemas/concept.py
Light1YT 65bab902e1
All checks were successful
CI / changes (pull_request) Successful in 6s
CI / frontend-tests (pull_request) Successful in 1m5s
CI / openapi-codegen-check (pull_request) Successful in 1m58s
CI / backend-tests (pull_request) Successful in 10m10s
feat(generative): full static developer financial cascade (epic #1881 PR-1)
Объединяет наше (площади из реальной геометрии 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
2026-06-23 16:45:58 +05:00

82 lines
3.2 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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]