gendesign/backend/app/schemas
Light1YT b6b9aa9929
All checks were successful
CI / changes (pull_request) Successful in 7s
CI / frontend-tests (pull_request) Successful in 1m10s
CI / openapi-codegen-check (pull_request) Successful in 2m3s
CI / backend-tests (pull_request) Successful in 10m10s
feat(financial): DCF-график продаж по реальному темпу поглощения (rank 1)
Финмодель брала фиксированное окно продаж 30 мес независимо от рынка
(schedule_is_default всегда true). Теперь окно считается из ЛОКАЛЬНОГО темпа
поглощения, который уже вычисляется в том же /analyze, но финмодель его
игнорировала.

Корректность абсорбции (ключевое): velocity.monthly_velocity_sqm — это СУММА
поглощения ВСЕГО конкурентного набора в радиусе (м²/мес), НЕ темп одного
проекта. Поэтому per-project absorption = monthly_velocity / max(n_with_sales,1)
(темп одного типичного локального продавца) — иначе модель считала бы, что новый
проект забирает весь рыночный темп (дико оптимистично). Поле
project_absorption_sqm_per_month добавлено в VelocityResult (objective-путь);
rosreestr-fallback и вырожденные пути → None (поквартальный count без
по-проектной декомпозиции не может задавать график).

financial.py: окно = clamp(ceil(residential/velocity), MIN=6, MAX=120) при
конечной velocity>0; иначе дефолт 30. Эскроу-инвариант сохранён:
sales_end=max(sales_start+base, constr_end). Инвариант Σ cashflow == net_profit
держится (перенос выручки во времени не меняет сумму). schedule_is_default
флипается в false когда график рыночный; новое поле sales_duration_months
(реализованное окно) для UI/PDF.

Wiring: parcels.py → synthesize_parcel_financial(velocity_sqm_per_month) →
compute_financial(market_velocity_sqm_per_month). Generative §1c путь пока
передаёт None (out of scope, follow-up).

Тесты: +13 (None→дефолт+инвариант; рыночная velocity; клампы MIN/MAX; эскроу;
non-finite→fallback; rosreestr→None; инвариант по размерам окна; регресс PR-3 —
ровно одна смена знака на коротком окне). Полный бэкенд: 3414 passed, 0 failed.
ruff+mypy(strict financial.py) чисто. api-types перегенерены.

Code-review: 2× approve, 0 majors (adversarial correctness-lens подтвердил
семантику абсорбции, инвариант, не-proxy IRR, клампы, rosreestr-None).

Refs #1881

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-24 02:56:48 +05:00
..
__init__.py init 2026-04-25 13:45:19 +03:00
chat.py fix(week-review): backend-аудит v2 — 82 фиксов (#1660) 2026-06-17 17:13:38 +00:00
complex_buildings.py feat(api,analytics): bulk geo backfill + complex_buildings query 2026-05-11 15:56:17 +03:00
concept.py feat(financial): DCF-график продаж по реальному темпу поглощения (rank 1) 2026-06-24 02:56:48 +05:00
custom_poi.py feat(#254): user_custom_pois — backend schema + CRUD + scoring integration 2026-05-17 09:34:33 +03:00
insight.py feat(insights): manual-entry Insight entity CRUD + §19 audit (#948 part A) 2026-06-08 12:41:39 +05:00
job_settings.py feat(jobs): centralized job_settings API + DB-driven beat schedule 2026-05-11 15:38:13 +03:00
location.py feat(location): district-level Location entity + indices (#948 part B) 2026-06-08 13:28:19 +05:00
market.py feat(analytics): velocity-anomaly alerts (#17) + ARN ДДУ price indicator (#99) 2026-06-13 22:04:49 +05:00
nspd_bulk.py feat(site-finder): per-building помещения/машино-места + parking_ratio (on-demand MVP, #96) 2026-06-14 18:48:16 +05:00
own_project.py feat(site-finder): own-portfolio data source for §25.3 cannibalization (#1169 PR1) 2026-06-08 16:21:53 +05:00
parcel.py feat(site-finder): additive objective_lots price fallback for competitors (#307 OBJ-3) (#1332) 2026-06-14 16:10:02 +00:00
recommend.py fix(schemas): expose per-bucket elasticity + velocity_source в RecommendBucket (#1204) 2026-06-13 05:14:05 +00:00
trade_in.py fix(week-review): автофиксы код-ревью — 169 issue (label «week ревью 1») 2026-06-15 20:21:11 +05:00