feat(financial): surface office revenue in PDF + concept UI (offices presentation)
All checks were successful
CI / changes (pull_request) Successful in 7s
CI / frontend-tests (pull_request) Successful in 1m4s
CI / openapi-codegen-check (pull_request) Successful in 1m56s
CI / backend-tests (pull_request) Successful in 11m10s

Office tranche was computed but invisible in both export surfaces (жильё+паркинг
< GDV with an unexplained gap). Add «Выручка — нежилое (1-й этаж)» row to the PDF
financial table + concept cascade; relabel PDF VAT row «НДС (нежилое: паркинг +
коммерция)». Add office_area_sqm/revenue_office_rub to hand-maintained
concept-api types; make teap_synth.office_area_sqm optional in site-finder types
(dev mock on main lacks it). Fix ptica finance fixture for required
revenue_office_rub.

Refs #1881
This commit is contained in:
Light1YT 2026-06-25 16:27:17 +05:00
parent df1299175d
commit 6b4b081c44
5 changed files with 22 additions and 3 deletions

View file

@ -119,6 +119,10 @@ def _financial_table(variants: Sequence[ConceptVariant]) -> str:
"Выручка — паркинг, млн руб",
[_fmt_money(v.financial.revenue_parking_rub) for v in variants],
),
(
"Выручка — нежилое (1-й этаж), млн руб",
[_fmt_money(v.financial.revenue_office_rub) for v in variants],
),
("Выручка (GDV), млн руб", [_fmt_money(v.financial.revenue_rub) for v in variants]),
("СМР, млн руб", [_fmt_money(v.financial.construction_rub) for v in variants]),
("ПИР, млн руб", [_fmt_money(v.financial.pir_rub) for v in variants]),
@ -132,7 +136,10 @@ def _financial_table(variants: Sequence[ConceptVariant]) -> str:
("Земля, млн руб", [_fmt_money(v.financial.land_rub) for v in variants]),
("Итого затраты, млн руб", [_fmt_money(v.financial.cost_rub) for v in variants]),
("Валовая маржа, млн руб", [_fmt_money(v.financial.gross_margin_rub) for v in variants]),
("НДС (паркинг), млн руб", [_fmt_money(v.financial.vat_rub) for v in variants]),
(
"НДС (нежилое: паркинг + коммерция), млн руб",
[_fmt_money(v.financial.vat_rub) for v in variants],
),
(
"Прибыль до налога, млн руб",
[_fmt_money(v.financial.profit_before_tax_rub) for v in variants],

View file

@ -392,6 +392,10 @@ function FinancialCascadeTable({ financial }: { financial: FinancialModel }) {
label="Выручка — паркинг"
value={formatMoneyCompact(financial.revenue_parking_rub)}
/>
<CascadeRow
label="Выручка — нежилое (1-й этаж)"
value={formatMoneyCompact(financial.revenue_office_rub)}
/>
<CascadeRow
label="Выручка (GDV)"
value={formatMoneyCompact(financial.revenue_rub)}

View file

@ -116,6 +116,8 @@ function financialFixture(): ParcelFinancialEstimate {
margin_pct: 0.262,
irr: 0.214,
irr_is_proxy: false,
// Нежилое (коммерция/офисы 1-го этажа) — additive выручка к GDV (#1881).
revenue_office_rub: 180_000_000,
npv_rub: 640_000_000,
payback_months: 42,
discount_rate_used: 0.18,

View file

@ -43,6 +43,8 @@ export interface Teap {
built_area_sqm: number;
total_floor_area_sqm: number;
residential_area_sqm: number;
/** Нежилое (коммерция/офисы 1-го этажа), кв.м. Additive к жилой площади. */
office_area_sqm: number;
apartments_count: number;
density: number;
parking_spaces: number;
@ -57,6 +59,8 @@ export interface FinancialModel {
// Revenue breakdown
revenue_residential_rub: number;
revenue_parking_rub: number;
/** Нежилое (коммерция/офисы 1-го этажа) — additive выручка к GDV. */
revenue_office_rub: number;
// Cost cascade
construction_rub: number;
pir_rub: number;

View file

@ -529,8 +529,10 @@ export interface ParcelFinancialEstimate {
residential_area_sqm: number;
total_floor_area_sqm: number;
// Нежилое (коммерция/офисы 1-го этажа), кв.м. Вырезано из GFA до расчёта жилой
// (total_floor_area не меняется). 0 когда нежилое не моделировалось.
office_area_sqm: number;
// (total_floor_area не меняется). 0 когда нежилое не моделировалось. Optional:
// живой backend всегда отдаёт, но dev-мок parcel-analyze.json (на main) его не
// содержит — required форсировал бы правку мока, конфликтную между ветками.
office_area_sqm?: number;
parking_spaces: number;
apartments_count: number;
};