fix(generative/financial): VAT only on parking, residential ДДУ exempt (ст. 149 НК) #1898
4 changed files with 92 additions and 24 deletions
|
|
@ -132,7 +132,7 @@ def _financial_table(variants: Sequence[ConceptVariant]) -> str:
|
||||||
("Земля, млн руб", [_fmt_money(v.financial.land_rub) for v in variants]),
|
("Земля, млн руб", [_fmt_money(v.financial.land_rub) for v in variants]),
|
||||||
("Итого затраты, млн руб", [_fmt_money(v.financial.cost_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.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],
|
[_fmt_money(v.financial.profit_before_tax_rub) for v in variants],
|
||||||
|
|
@ -180,8 +180,9 @@ def _build_html(variants: Sequence[ConceptVariant]) -> str:
|
||||||
"График фаз и темп продаж — типовые допущения, НЕ график конкретного проекта; "
|
"График фаз и темп продаж — типовые допущения, НЕ график конкретного проекта; "
|
||||||
"точность метрик зависит от реального графика. Где IRR помечен «оценочный» — поток "
|
"точность метрик зависит от реального графика. Где IRR помечен «оценочный» — поток "
|
||||||
"вырожденный (нет смены знака), показан аннуализированный ROI вместо DCF-IRR. "
|
"вырожденный (нет смены знака), показан аннуализированный ROI вместо DCF-IRR. "
|
||||||
"НДС — упрощённая оценка (встроенный НДС в положительной марже, не полная механика "
|
"НДС — реализация жилья и услуги застройщика по ДДУ освобождены (ст. 149 НК РФ), "
|
||||||
"входного/выходного НДС НК РФ). Налог на прибыль — 25% (с 2025). Цены и себестоимость "
|
"входной НДС по СМР встроен в себестоимость; НДС начисляется только на нежилую часть "
|
||||||
|
"(машиноместа). Налог на прибыль — 25% (с 2025). Цены и себестоимость "
|
||||||
"— рыночные ориентиры. Коммерческие/офисные площади не учитываются (нет в ТЭП).</p>"
|
"— рыночные ориентиры. Коммерческие/офисные площади не учитываются (нет в ТЭП).</p>"
|
||||||
"</body></html>"
|
"</body></html>"
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -52,19 +52,24 @@ Cascade (in :func:`compute_financial`):
|
||||||
* **БДР / taxes** = gross margin − VAT − profit tax → net profit.
|
* **БДР / taxes** = gross margin − VAT − profit tax → net profit.
|
||||||
* **Metrics** = net ROI on cost, net margin on revenue, and DCF NPV/IRR/payback.
|
* **Metrics** = net ROI on cost, net margin on revenue, and DCF NPV/IRR/payback.
|
||||||
|
|
||||||
VAT — DOCUMENTED SIMPLIFICATION
|
НДС — ОСВОБОЖДЕНИЕ ЖИЛЬЯ, НДС ТОЛЬКО НА ПАРКИНГ (ст. 149 НК РФ)
|
||||||
-------------------------------
|
------------------------------------------------------------
|
||||||
Exact НК РФ VAT mechanics (output VAT on flats vs. input-VAT deductions on
|
Реализация жилых помещений и услуги застройщика по ДДУ ОСВОБОЖДЕНЫ от НДС
|
||||||
construction/materials, эскроу specifics, residential-sale exemptions) are out of
|
(пп.22 и пп.23.1 п.3 ст. 149 НК РФ) → выходной НДС на жильё = 0. Входной НДС по
|
||||||
PR-1 scope. We use a conservative, honest approximation on the project's
|
СМР при этом НЕвозмещаем (п.2 ст.170 НК РФ) и уже «сидит» в gross-ставках
|
||||||
*value-added* (gross margin):
|
себестоимости (рыночные ориентиры ``_CONSTRUCTION_COST_PER_SQM`` даны С НДС) →
|
||||||
|
чистая нагрузка по НДС на жилую часть ≈ 0.
|
||||||
|
|
||||||
vat = max(0, gross_margin) × VAT_RATE / (1 + VAT_RATE)
|
Машиноместа — НЕЖИЛЫЕ помещения: их реализация ОБЛАГАЕТСЯ НДС 20%, входной НДС
|
||||||
|
вычитается. Поэтому НДС начисляется ТОЛЬКО на нежилой value-added паркинга
|
||||||
|
(встроенный НДС в добавленной стоимости машиномест):
|
||||||
|
|
||||||
i.e. the embedded VAT inside the positive margin. This deliberately *does not*
|
vat = max(0, revenue_parking − construction_parking) × VAT_RATE / (1 + VAT_RATE)
|
||||||
model the full input/output deduction chain — it is a stand-in that keeps the net
|
|
||||||
P&L plausible and conservative. A loss-making project pays no VAT here. Replace
|
Если паркинг тоже окажется под ДДУ-льготой — оценка консервативна (чуть занижает
|
||||||
with proper output/input modelling when calibrating (PR-2/PR-3).
|
прибыль завышенным НДС), что соответствует honest/conservative-политике модуля.
|
||||||
|
Полную механику входного/выходного НДС с эскроу-спецификой не моделируем —
|
||||||
|
жильё освобождено, входной НДС встроен в gross-СМР.
|
||||||
|
|
||||||
IRR — REAL DCF, PROXY FALLBACK (PR-3)
|
IRR — REAL DCF, PROXY FALLBACK (PR-3)
|
||||||
-------------------------------------
|
-------------------------------------
|
||||||
|
|
@ -521,8 +526,9 @@ def compute_financial(
|
||||||
Полный девелоперский каскад: выручка (жильё + паркинг) → каскад затрат (СМР +
|
Полный девелоперский каскад: выручка (жильё + паркинг) → каскад затрат (СМР +
|
||||||
ПИР + сети + услуги заказчика + резерв + маркетинг + земля) → БДР с НДС и
|
ПИР + сети + услуги заказчика + резерв + маркетинг + земля) → БДР с НДС и
|
||||||
налогом на прибыль → чистая прибыль + ROI. Поверх статики — помесячный DCF
|
налогом на прибыль → чистая прибыль + ROI. Поверх статики — помесячный DCF
|
||||||
(NPV/IRR/PBP) по дефолт-графику фаз (см. docstring модуля). НДС — упрощение;
|
(NPV/IRR/PBP) по дефолт-графику фаз (см. docstring модуля). НДС — жильё
|
||||||
офисов нет в TEAP — пропущены.
|
освобождено (ст. 149 НК РФ), начисляется только на паркинг; офисов нет в
|
||||||
|
TEAP — пропущены.
|
||||||
|
|
||||||
Чистая функция: НЕ ходит в БД. Калибровку цены продажи жилья делает API-слой
|
Чистая функция: НЕ ходит в БД. Калибровку цены продажи жилья делает API-слой
|
||||||
(lookup рынка) и прокидывает сюда через ``market_price_per_sqm``.
|
(lookup рынка) и прокидывает сюда через ``market_price_per_sqm``.
|
||||||
|
|
@ -583,9 +589,14 @@ def compute_financial(
|
||||||
|
|
||||||
# ── БДР / налоги → чистая прибыль ─────────────────────────────────────────
|
# ── БДР / налоги → чистая прибыль ─────────────────────────────────────────
|
||||||
gross_margin = revenue - cost # валовая (как было)
|
gross_margin = revenue - cost # валовая (как было)
|
||||||
# НДС: УПРОЩЕНО — встроенный НДС в положительной марже (value-added),
|
# НДС: жилые помещения и услуги застройщика по ДДУ освобождены (пп.22 / пп.23.1
|
||||||
# не полная механика входного/выходного НДС НК РФ (см. docstring модуля).
|
# п.3 ст. 149 НК РФ) → output-НДС на жильё = 0, а невозмещаемый входной НДС по СМР
|
||||||
vat = max(0.0, gross_margin) * _VAT_RATE / (1.0 + _VAT_RATE)
|
# уже сидит в gross-ставках себестоимости. НДС начисляется ТОЛЬКО на нежилую часть
|
||||||
|
# (машиноместа — нежилые, их реализация облагается): встроенный НДС в value-added
|
||||||
|
# паркинга. Если паркинг тоже окажется по ДДУ-льготе — оценка консервативна (чуть
|
||||||
|
# занижает прибыль), что соответствует «honest/conservative» политике модуля.
|
||||||
|
parking_value_added = revenue_parking - construction_parking
|
||||||
|
vat = max(0.0, parking_value_added) * _VAT_RATE / (1.0 + _VAT_RATE)
|
||||||
profit_before_tax = gross_margin - vat
|
profit_before_tax = gross_margin - vat
|
||||||
profit_tax = max(0.0, profit_before_tax) * _PROFIT_TAX_RATE
|
profit_tax = max(0.0, profit_before_tax) * _PROFIT_TAX_RATE
|
||||||
net_profit = profit_before_tax - profit_tax
|
net_profit = profit_before_tax - profit_tax
|
||||||
|
|
|
||||||
|
|
@ -157,6 +157,62 @@ def test_invariant_holds_for_all_development_types() -> None:
|
||||||
assert abs(sum(cf) - model.net_profit_rub) < 1.0, dev_type
|
assert abs(sum(cf) - model.net_profit_rub) < 1.0, dev_type
|
||||||
|
|
||||||
|
|
||||||
|
# ── НДС: освобождение жилья (ст.149 НК), НДС только на паркинг ───────────────────
|
||||||
|
|
||||||
|
|
||||||
|
def test_vat_zero_for_residential_only_project() -> None:
|
||||||
|
# Реализация жилья + услуги застройщика по ДДУ освобождены от НДС (пп.22/пп.23.1
|
||||||
|
# п.3 ст.149 НК РФ). Проект БЕЗ паркинга → НДС == 0 (раньше было margin×0.20/1.20).
|
||||||
|
# Profitable comfort-проект всё равно остаётся прибыльным.
|
||||||
|
t = _teap(residential=10_000.0, gfa=11_000.0, parking=0)
|
||||||
|
model = financial.compute_financial(
|
||||||
|
teap=t, housing_class="comfort", land_cost_rub=200_000_000.0, development_type="mid_rise"
|
||||||
|
)
|
||||||
|
# Освобождение явно: нет паркинга → нет облагаемого value-added → НДС ровно 0.
|
||||||
|
assert model.vat_rub == 0.0
|
||||||
|
assert model.revenue_parking_rub == 0.0
|
||||||
|
# Налогооблагаемая база до налога теперь == валовой марже (НДС не съедает её).
|
||||||
|
assert model.profit_before_tax_rub == pytest.approx(model.gross_margin_rub, abs=0.01)
|
||||||
|
# Проект прибыльный (освобождение НДС не делает его убыточным).
|
||||||
|
assert model.net_profit_rub > 0
|
||||||
|
|
||||||
|
|
||||||
|
def test_vat_only_on_parking_value_added() -> None:
|
||||||
|
# С паркингом → НДС > 0 и считается ТОЛЬКО на нежилой value-added машиномест:
|
||||||
|
# vat == max(0, revenue_parking − construction_parking) × 0.20/1.20.
|
||||||
|
# Жилая часть (выручка/маржа жилья) в базу НДС НЕ входит (ст.149 НК РФ).
|
||||||
|
parking = 50
|
||||||
|
cls = "comfort"
|
||||||
|
t = _teap(residential=10_000.0, gfa=11_000.0, parking=parking)
|
||||||
|
model = financial.compute_financial(
|
||||||
|
teap=t, housing_class=cls, land_cost_rub=200_000_000.0, development_type="mid_rise"
|
||||||
|
)
|
||||||
|
# Ожидаемое из констант класса: revenue_parking − construction_parking паркинга.
|
||||||
|
revenue_parking = parking * financial._PARKING_PRICE_PER_SPOT[cls]
|
||||||
|
construction_parking = parking * financial._PARKING_COST_PER_SPOT[cls]
|
||||||
|
parking_value_added = revenue_parking - construction_parking
|
||||||
|
expected_vat = max(0.0, parking_value_added) * financial._VAT_RATE / (1.0 + financial._VAT_RATE)
|
||||||
|
assert model.vat_rub > 0.0
|
||||||
|
assert model.vat_rub == pytest.approx(round(expected_vat, 2), abs=0.01)
|
||||||
|
# База НДС — именно нежилой value-added, а НЕ полная валовая маржа проекта.
|
||||||
|
full_margin_vat = (
|
||||||
|
max(0.0, model.gross_margin_rub) * financial._VAT_RATE / (1.0 + financial._VAT_RATE)
|
||||||
|
)
|
||||||
|
assert model.vat_rub < full_margin_vat # жильё освобождено → НДС много меньше margin×rate
|
||||||
|
|
||||||
|
|
||||||
|
def test_vat_invariant_holds_with_parking_vat() -> None:
|
||||||
|
# Инвариант Σ cashflow == net_profit держится с новым НДС (vat прокидывается в
|
||||||
|
# построитель cashflow без изменения формы — меняется только база НДС).
|
||||||
|
t = _teap(residential=10_000.0, gfa=11_000.0, parking=50)
|
||||||
|
model = financial.compute_financial(
|
||||||
|
teap=t, housing_class="comfort", land_cost_rub=200_000_000.0, development_type="mid_rise"
|
||||||
|
)
|
||||||
|
assert model.vat_rub > 0.0
|
||||||
|
cf = _rebuild_cashflow(model, construction_months=36, residential_area_sqm=10_000.0)
|
||||||
|
assert abs(sum(cf) - model.net_profit_rub) < 1.0
|
||||||
|
|
||||||
|
|
||||||
# ── Настоящий IRR vs proxy fallback ─────────────────────────────────────────────
|
# ── Настоящий IRR vs proxy fallback ─────────────────────────────────────────────
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -298,10 +298,10 @@ function VariantPanel({ parcel, variant }: PanelProps) {
|
||||||
{financial.irr_is_proxy
|
{financial.irr_is_proxy
|
||||||
? "IRR помечен как оценочный: денежный поток вырожденный (нет смены знака), показан аннуализированный ROI вместо DCF-IRR. "
|
? "IRR помечен как оценочный: денежный поток вырожденный (нет смены знака), показан аннуализированный ROI вместо DCF-IRR. "
|
||||||
: ""}
|
: ""}
|
||||||
НДС — упрощённая оценка (встроенный НДС в положительной марже, не
|
НДС: жильё и услуги застройщика по ДДУ освобождены (ст. 149 НК
|
||||||
полная механика входного/выходного НДС НК РФ); привязан к
|
РФ); НДС начисляется только на паркинг (нежилые машиноместа).
|
||||||
признанию выручки. Налог на прибыль — 25% (с 2025 года). Цена
|
Входной НДС по строительству уже учтён в себестоимости. Налог на
|
||||||
продажи жилья —{" "}
|
прибыль — 25% (с 2025 года). Цена продажи жилья —{" "}
|
||||||
{financial.price_is_calibrated
|
{financial.price_is_calibrated
|
||||||
? `калибрована по рынку (${priceSourceCaption(financial)})`
|
? `калибрована по рынку (${priceSourceCaption(financial)})`
|
||||||
: "норматив класса (нет рыночных данных по участку)"}
|
: "норматив класса (нет рыночных данных по участку)"}
|
||||||
|
|
@ -414,7 +414,7 @@ function FinancialCascadeTable({ financial }: { financial: FinancialModel }) {
|
||||||
value={formatMoneyCompact(financial.gross_margin_rub)}
|
value={formatMoneyCompact(financial.gross_margin_rub)}
|
||||||
/>
|
/>
|
||||||
<CascadeRow
|
<CascadeRow
|
||||||
label="НДС (упрощённо)"
|
label="НДС (паркинг)"
|
||||||
value={formatMoneyCompact(financial.vat_rub)}
|
value={formatMoneyCompact(financial.vat_rub)}
|
||||||
/>
|
/>
|
||||||
<CascadeRow
|
<CascadeRow
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue