Remodel/packages/web/lib/api.ts
Mannu dba1e6990f
Some checks are pending
CI / Engine — lint / typecheck / test (push) Waiting to run
CI / API — lint / typecheck / test (push) Waiting to run
CI / Web — typecheck / lint / build (push) Waiting to run
feat: add profile data columns to generation sheet for detailed breakdown
- Add hourly_solar_profile and hourly_wind_profile to store per-hour output values
- Add solar_dc_mwp, solar_ac_mw, wind_mw to PipelineResult for capacity display
- Add columns: Solar 8760, DC MW, Solar MW / Wind 8760, MW, Wind MW
- Updated UI to show detailed breakdown at each level (Year/Month/Day/Hour)

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-16 13:31:20 +05:30

369 lines
9.2 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

const API_BASE = process.env.NEXT_PUBLIC_API_URL ?? "http://localhost:8000";
// ---------------------------------------------------------------------------
// Types
// ---------------------------------------------------------------------------
export interface Scenario {
id: string;
name: string;
status: string;
kpis_json: string | null;
created_at: string;
runtime_s?: number | null;
}
export interface ScenarioDetail extends Scenario {
inputs_json: string | null;
statements_json: string | null;
debt_schedule_json: string | null;
error_message: string | null;
}
export interface KpiSummary {
solved_tariff_inr_per_kwh?: number | null;
equity_irr?: number | null;
project_irr?: number | null;
min_dscr?: number | null;
avg_dscr?: number | null;
total_capex_cr?: number | null;
idc_cr?: number | null;
debt_cr?: number | null;
solar_y1_cuf?: number | null;
wind_y1_plf?: number | null;
lcoe_inr_per_kwh?: number | null;
payback_years?: number | null;
rtc_cuf_achieved?: number | null;
total_shortfall_mwh?: number | null;
total_curtailed_mwh?: number | null;
total_mcp_revenue_cr?: number | null;
}
export interface PnLRow {
year: number;
revenue_cr: number;
ppa_revenue_cr: number;
mcp_revenue_cr: number;
ppa_tariff_inr_per_kwh: number;
ppa_units_mwh: number;
mcp_units_mwh: number;
opex_total_cr: number;
om_cr: number;
insurance_cr: number;
land_lease_cr: number;
am_fee_cr: number;
misc_opex_cr: number;
ebitda_cr: number;
depreciation_book_cr: number;
ebit_cr: number;
interest_cr: number;
pbt_cr: number;
tax_cr: number;
pat_cr: number;
}
export interface CfsRow {
year: number;
pat_cr: number;
depreciation_cr: number;
delta_working_capital_cr: number;
cfo_cr: number;
capex_cr: number;
cfi_cr: number;
debt_drawdown_cr: number;
debt_repayment_cr: number;
equity_injection_cr: number;
cff_cr: number;
net_cash_flow_cr: number;
opening_cash_cr: number;
closing_cash_cr: number;
}
export interface BsRow {
year: number;
gross_block_cr: number;
accumulated_depr_cr: number;
net_block_cr: number;
cash_cr: number;
receivables_cr: number;
total_assets_cr: number;
equity_cr: number;
reserves_cr: number;
long_term_debt_cr: number;
payables_cr: number;
total_liabilities_cr: number;
}
export interface DebtYearRow {
year: number;
opening_balance_cr: number;
interest_cr: number;
principal_cr: number;
total_debt_service_cr: number;
closing_balance_cr: number;
dscr: number;
}
export interface GenerationRow {
year: number;
solar_mwh: number;
wind_mwh: number;
gross_mwh: number;
aux_loss_mwh: number;
tx_loss_mwh: number;
dsm_loss_mwh: number;
net_billable_mwh: number;
solar_cuf_pct: number | null;
wind_plf_pct: number | null;
revenue_cr: number;
}
export interface IdcMonthRow {
month: number;
equity_draw_cr: number;
debt_draw_cr: number;
idc_accrual_cr: number;
cum_equity_cr: number;
cum_debt_cr: number;
cum_idc_cr: number;
cum_tpc_cr: number;
}
export interface IdcPhasing {
construction_months: number;
base_capex_cr: number;
idc_cr: number;
total_capex_cr: number;
debt_cr: number;
equity_cr: number;
monthly: IdcMonthRow[];
}
export interface Statements {
pnl: PnLRow[];
cfs: CfsRow[];
bs: BsRow[];
generation?: GenerationRow[];
idc_phasing?: IdcPhasing;
// Hourly generation: 25 years × 8760 hours = 219,000 values per technology
solar_hourly?: number[];
wind_hourly?: number[];
// Hourly timeseries
hourly_timestamps?: string[];
hourly_fy?: string[];
hourly_proj_year?: number[];
hourly_total_re?: number[];
hourly_client_end?: number[];
hourly_load?: number[];
// Profile data for detailed view
hourly_solar_profile?: number[];
hourly_wind_profile?: number[];
}
export type CostBasis =
| "PER_WP_DC"
| "PER_MWP_DC"
| "PER_MW_AC"
| "PER_MW_WIND"
| "PER_MWH_BESS"
| "PER_ACRE"
| "PCT_OF_HARDCOST"
| "ABS_INR_CR";
export type DeprClass =
| "Plant"
| "BESS"
| "Building"
| "Land_NoDepr"
| "LandLease_Amortized"
| "Intangible"
| "Capitalized_NoDepr"
| "Expensed";
export type CostAttribution = "SolarOnly" | "WindOnly" | "BESSOnly" | "Common";
export interface CostItem {
id: string;
name: string;
category: "HardCost" | "SoftCost" | "EPCOverhead" | "EPCMargin" | "FinancingCost" | "Contingency";
basis: CostBasis;
value: number;
depr_class: DeprClass;
tax_pct?: number; // GST/tax rate, default 5% for modules
attribution: CostAttribution;
phasing_id?: string;
escalation_pct?: number;
}
export interface ScenarioInputPayload {
project?: {
name?: string;
state?: string | null;
capacity_solar_mwp?: number;
capacity_wind_mw?: number;
capacity_bess_mwh?: number;
capacity_bess_mw?: number;
land_acres?: number;
cod_year?: number;
cod_date?: string | null;
solar_cod_date?: string | null;
wind_cod_date?: string | null;
bess_cod_date?: string | null;
};
solar?: {
location_id: string;
capacity_dc_mwp: number;
capacity_ac_mw: number;
dc_ac_ratio?: number;
availability_fraction?: number;
dc_loss_fraction?: number;
soiling_fraction?: number;
degradation_y1?: number;
degradation_annual?: number;
stabilization_days?: number;
stabilization_energy_loss_frac?: number;
stabilization_dsm_addon_pct?: number;
} | null;
wind?: {
location_id: string;
capacity_mw: number;
hub_height_m?: number;
availability_fraction?: number;
wake_loss_fraction?: number;
stabilization_days?: number;
stabilization_energy_loss_frac?: number;
stabilization_dsm_addon_pct?: number;
} | null;
bess?: {
capacity_mwh: number;
power_mw: number;
rte?: number;
dod?: number;
} | null;
rtc?: {
rtc_mw?: number;
mcp_enabled?: boolean;
initial_soc_frac?: number;
} | null;
commercial?: {
tariff_inr_per_kwh?: number;
aux_consumption_pct?: number;
transmission_loss_pct?: number;
dsm_loss_pct?: number;
bad_debt_pct?: number;
receivable_days?: number;
payable_days?: number;
};
opex?: {
om_solar_cr_per_mw?: number;
om_wind_cr_per_mw?: number;
om_bess_cr_per_mwh?: number;
insurance_pct_of_capex?: number;
land_lease_cr?: number;
om_escalation_pct?: number;
om_solar_escalation_pct?: number;
om_solar_escalation_after_year?: number;
om_wind_escalation_pct?: number;
om_wind_escalation_after_year?: number;
om_bess_pct_of_capex?: number | null;
am_fee_pct_of_revenue?: number;
misc_cr?: number;
};
capex?: {
cost_items?: CostItem[];
debt_fraction?: number;
interest_rate_annual?: number;
construction_months?: number;
upfront_fee_pct?: number;
};
debt?: {
interest_rate_annual?: number;
tenor_years?: number;
moratorium_years?: number;
de_ratio?: number;
min_dscr?: number;
avg_dscr?: number;
schedule_shape?: string;
};
tax?: {
rate?: number;
wdv_plant_rate?: number;
wdv_bess_rate?: number;
wdv_building_rate?: number;
wdv_intangible_rate?: number;
};
solver?: {
mode: "solve_tariff" | "fixed_tariff";
target_equity_irr?: number;
fixed_tariff?: number | null;
};
}
export interface ProgressEvent {
stage: string;
pct: number;
}
// ---------------------------------------------------------------------------
// API helpers
// ---------------------------------------------------------------------------
async function apiFetch<T>(url: string, options?: RequestInit): Promise<T> {
const res = await fetch(`${API_BASE}${url}`, options);
if (!res.ok) throw new Error(`API error ${res.status}: ${await res.text()}`);
return res.json() as Promise<T>;
}
// ---------------------------------------------------------------------------
// Scenario functions
// ---------------------------------------------------------------------------
export async function createScenario(
name: string,
inputs?: ScenarioInputPayload,
): Promise<Scenario> {
return apiFetch<Scenario>("/api/scenarios", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ name, inputs }),
});
}
export async function getScenario(id: string): Promise<ScenarioDetail> {
return apiFetch<ScenarioDetail>(`/api/scenarios/${id}`);
}
export async function listScenarios(): Promise<Scenario[]> {
return apiFetch<Scenario[]>("/api/scenarios");
}
export async function getKpis(id: string): Promise<KpiSummary> {
return apiFetch<KpiSummary>(`/api/scenarios/${id}/kpis`);
}
export async function getStatements(id: string): Promise<Statements> {
return apiFetch<Statements>(`/api/scenarios/${id}/statements`);
}
export async function archiveScenario(id: string): Promise<void> {
await apiFetch(`/api/scenarios/${id}`, { method: "DELETE" });
}
export async function updateScenarioInputs(
id: string,
inputs: ScenarioInputPayload,
): Promise<Scenario> {
return apiFetch<Scenario>(`/api/scenarios/${id}/inputs`, {
method: "PATCH",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ inputs }),
});
}
export function scenarioEventsUrl(id: string): string {
return `${API_BASE}/api/scenarios/${id}/events`;
}
export function scenarioExcelUrl(id: string): string {
return `${API_BASE}/api/scenarios/${id}/export/excel`;
}