"""Generative Design — Stage 1c: simplified financial model. From the Stage 1c ``TEAP`` block we derive the ``FinancialModel`` contract: * ``revenue_rub`` = residential_area_sqm * sale price per sqm (by housing class). * ``cost_rub`` = total_floor_area_sqm * construction cost per sqm + land cost. * ``gross_margin_rub`` = revenue - cost. * ``irr`` = simplified proxy (margin-on-cost / project years), NO time discounting — this is a static stand-in until the Phase 1 cashflow model lands. Prices/costs are coarse RU-market proxies for an MVP (см. константы ниже); they are deliberately conservative round numbers, not a calibrated pricing engine. The IRR field is a *proxy*: a real internal rate of return needs a dated cashflow series, which is out of MVP scope — we return an annualised margin ratio so the field is populated with a plausible, monotonic number rather than zero. Детерминированно, без LLM / внешних API / БД. """ from __future__ import annotations import logging from typing import Literal from app.schemas.concept import TEAP, FinancialModel logger = logging.getLogger(__name__) HousingClass = Literal["econom", "comfort", "business"] # ── Цена продажи жилья, руб/кв.м (proxy рынка ЕКБ/региона, упрощённо) ────────── _SALE_PRICE_PER_SQM: dict[HousingClass, float] = { "econom": 110_000.0, "comfort": 145_000.0, "business": 210_000.0, } # ── Себестоимость СМР, руб/кв.м общей площади (выше класс -> дороже отделка/инж) ─ _CONSTRUCTION_COST_PER_SQM: dict[HousingClass, float] = { "econom": 72_000.0, "comfort": 88_000.0, "business": 120_000.0, } # Условный горизонт проекта (лет) для аннуализации margin-on-cost в IRR-proxy. _PROJECT_YEARS: float = 3.0 def compute_financial( *, teap: TEAP, housing_class: HousingClass, land_cost_rub: float | None, ) -> FinancialModel: """Свести ТЭП + класс + стоимость земли в :class:`FinancialModel`. Args: teap: Stage 1c ТЭП (берём residential_area_sqm и total_floor_area_sqm). housing_class: задаёт цену продажи и себестоимость СМР. land_cost_rub: стоимость участка (опционально); None -> 0 в затратах. """ sale_price = _SALE_PRICE_PER_SQM[housing_class] construction_cost = _CONSTRUCTION_COST_PER_SQM[housing_class] revenue = teap.residential_area_sqm * sale_price construction = teap.total_floor_area_sqm * construction_cost land = land_cost_rub if land_cost_rub is not None else 0.0 cost = construction + land gross_margin = revenue - cost # IRR-proxy: аннуализированная маржа-на-затраты. НЕ настоящий IRR (нет дисконта/ # дат денежных потоков — отложено в Phase 1). Защита от деления на ноль и # клампинг в разумный диапазон, чтобы поле было монотонным и читаемым. if cost > 0: margin_on_cost = gross_margin / cost irr = margin_on_cost / _PROJECT_YEARS else: irr = 0.0 irr = max(-1.0, min(1.0, irr)) model = FinancialModel( revenue_rub=round(revenue, 2), cost_rub=round(cost, 2), gross_margin_rub=round(gross_margin, 2), irr=round(irr, 4), ) logger.info( "financial: revenue=%.0f cost=%.0f margin=%.0f irr_proxy=%.3f", model.revenue_rub, model.cost_rub, model.gross_margin_rub, model.irr, ) return model __all__ = ["HousingClass", "compute_financial"]