"""25-year Profit & Loss statement. Revenue - OpEx (O&M, insurance, land lease, AM fee, misc) = EBITDA - Depreciation (book SLM) = EBIT - Interest expense (from debt schedule) = PBT - Current tax (115BAA) = PAT """ from __future__ import annotations from remodel_engine.schemas.financial import OpexConfig, PnLRow def compute_ppa_units( gen_mwh_by_year: list[float], aux_pct: float, tx_loss_pct: float, dsm_loss_pct: float, ) -> list[float]: """Net PPA-billable units for each year in MWh.""" return [ round(mwh * (1 - aux_pct) * (1 - tx_loss_pct) * (1 - dsm_loss_pct), 2) for mwh in gen_mwh_by_year ] def compute_revenue( ac_gen_mwh_by_year: list[float], tariff_inr_per_kwh: float, aux_pct: float, tx_loss_pct: float, dsm_loss_pct: float, bad_debt_pct: float = 0.0, ) -> list[float]: """Net revenue for each year in INR Crore. net_injection = generation * (1 - aux) * (1 - tx_loss) * (1 - dsm_loss) revenue = net_injection * tariff / 1000 (kWh from MWh: already in kWh via MWh*1000) """ rev = [] for mwh in ac_gen_mwh_by_year: net_kwh = mwh * 1000.0 * (1 - aux_pct) * (1 - tx_loss_pct) * (1 - dsm_loss_pct) gross_rev = net_kwh * tariff_inr_per_kwh / 1e7 # INR → Cr (1 Cr = 1e7 INR) rev.append(round(gross_rev * (1 - bad_debt_pct), 4)) return rev def compute_opex( revenue_by_year: list[float], solar_mw: float, wind_mw: float, bess_mwh: float, base_capex_cr: float, config: OpexConfig, ) -> list[float]: """Total opex for each year in INR Crore (excluding depreciation and interest).""" opex: list[float] = [] for y, rev in enumerate(revenue_by_year): esc = (1.0 + config.om_escalation_pct) ** y om = ( config.om_solar_cr_per_mw * solar_mw + config.om_wind_cr_per_mw * wind_mw + config.om_bess_cr_per_mwh * bess_mwh ) * esc insurance = config.insurance_pct_of_capex * base_capex_cr land = config.land_lease_cr * esc am_fee = config.am_fee_pct_of_revenue * rev misc = config.misc_cr * esc opex.append(round(om + insurance + land + am_fee + misc, 4)) return opex def build_pnl( revenue_by_year: list[float], gen_mwh_by_year: list[float], tariff_inr_per_kwh: float, mcp_revenue_by_year: list[float], mcp_units_by_year: list[float], opex_by_year: list[float], book_depr_by_year: list[float], interest_by_year: list[float], current_tax_by_year: list[float], deferred_tax_by_year: list[float], solar_mw: float, wind_mw: float, bess_mwh: float, base_capex_cr: float, config: OpexConfig, ) -> list[PnLRow]: """Build 25-year P&L row list.""" n = len(revenue_by_year) rows: list[PnLRow] = [] for y in range(n): ppa_rev = revenue_by_year[y] ppa_units = gen_mwh_by_year[y] if y < len(gen_mwh_by_year) else 0.0 mcp_rev = mcp_revenue_by_year[y] if y < len(mcp_revenue_by_year) else 0.0 mcp_units = mcp_units_by_year[y] if y < len(mcp_units_by_year) else 0.0 total_rev = ppa_rev + mcp_rev esc = (1.0 + config.om_escalation_pct) ** y om = ( config.om_solar_cr_per_mw * solar_mw + config.om_wind_cr_per_mw * wind_mw + config.om_bess_cr_per_mwh * bess_mwh ) * esc ins = config.insurance_pct_of_capex * base_capex_cr land = config.land_lease_cr * esc am = config.am_fee_pct_of_revenue * total_rev misc = config.misc_cr * esc opex_total = om + ins + land + am + misc ebitda = total_rev - opex_total depr = book_depr_by_year[y] ebit = ebitda - depr interest = interest_by_year[y] pbt = ebit - interest ct = current_tax_by_year[y] dt = deferred_tax_by_year[y] pat = pbt - ct rows.append(PnLRow( year=y + 1, revenue_cr=round(total_rev, 4), ppa_revenue_cr=round(ppa_rev, 4), mcp_revenue_cr=round(mcp_rev, 4), ppa_tariff_inr_per_kwh=tariff_inr_per_kwh, ppa_units_mwh=round(ppa_units, 2), mcp_units_mwh=round(mcp_units, 2), opex_total_cr=round(opex_total, 4), om_cr=round(om, 4), insurance_cr=round(ins, 4), land_lease_cr=round(land, 4), am_fee_cr=round(am, 4), misc_opex_cr=round(misc, 4), ebitda_cr=round(ebitda, 4), depreciation_book_cr=round(depr, 4), ebit_cr=round(ebit, 4), interest_cr=round(interest, 4), pbt_cr=round(pbt, 4), tax_cr=round(ct, 4), pat_cr=round(pat, 4), deferred_tax_cr=round(dt, 4), )) return rows