fixed filtering logic

This commit is contained in:
Khalim Conn-Kowlessar 2026-02-07 19:59:51 +00:00
parent 1864cce8d1
commit dce30edf00
3 changed files with 101 additions and 35 deletions

View file

@ -25,6 +25,14 @@ type UpgradedAggregates = {
total_funding: number | null;
};
type PortfolioAggregates = {
avg_sap: number | null;
avg_carbon: number | null;
avg_bills: number | null;
total_carbon: number | null;
total_bills: number | null;
};
type EpcRow = {
effective_sap: number | null;
};
@ -87,7 +95,7 @@ export async function GET(
: null;
/* ----------------------------------------------------------
QUERY 1 Scenario metrics (plans ONLY)
QUERY 1 Scenario metrics (PLANS ONLY)
---------------------------------------------------------- */
const scenarioMetricsResult = await db.execute(sql`
WITH latest_plans AS (
@ -124,19 +132,10 @@ export async function GET(
JOIN property p ON p.id = lp.property_id;
`);
const scenarioAgg = scenarioMetricsResult.rows[0] as
| ScenarioAggregates
| undefined;
if (!scenarioAgg) {
return NextResponse.json(
{ error: "No scenario metrics found" },
{ status: 404 },
);
}
const scenarioAgg = scenarioMetricsResult.rows[0] as ScenarioAggregates;
/* ----------------------------------------------------------
QUERY 1b Upgrade costs (still plan-only)
QUERY 1b Upgrade costs (PLANS ONLY)
---------------------------------------------------------- */
const upgradedResult = await db.execute(sql`
WITH latest_plans AS (
@ -170,14 +169,78 @@ export async function GET(
const upgraded = upgradedResult.rows[0] as UpgradedAggregates;
/* ----------------------------------------------------------
QUERY 2 EPC distribution (ALL properties)
This is the important new one
QUERY 2 Portfolio AFTER scenario (ALL properties)
---------------------------------------------------------- */
const portfolioMetricsResult = await db.execute(sql`
SELECT
AVG(effective_sap)::float AS avg_sap,
AVG(effective_carbon)::float AS avg_carbon,
AVG(effective_bills)::float AS avg_bills,
SUM(effective_carbon)::float AS total_carbon,
SUM(effective_bills)::float AS total_bills
FROM (
SELECT
/* ---------- SAP ---------- */
CASE
WHEN lp.id IS NOT NULL THEN lp.post_sap_points
ELSE p.current_sap_points
END AS effective_sap,
/* ---------- Carbon ---------- */
CASE
WHEN lp.id IS NOT NULL THEN lp.post_co2_emissions
ELSE e.co2_emissions
END AS effective_carbon,
/* ---------- Bills ---------- */
CASE
WHEN lp.id IS NOT NULL THEN lp.post_energy_bill
ELSE (
e.heating_cost_current +
e.hot_water_cost_current +
e.lighting_cost_current +
e.appliances_cost_current +
e.gas_standing_charge +
e.electricity_standing_charge -
COALESCE(e.installed_measures_total_energy_bill_adjustment, 0)
)
END AS effective_bills
FROM property p
LEFT JOIN property_details_epc e
ON e.property_id = p.id
LEFT JOIN LATERAL (
SELECT *
FROM plan
WHERE plan.property_id = p.id
AND plan.portfolio_id = ${pid}
AND plan.scenario_id = ${sid}
AND (
${hideNonCompliant} = false
OR (
${minSap}::float IS NOT NULL
AND plan.post_sap_points >= ${minSap}::float
)
)
ORDER BY created_at DESC
LIMIT 1
) lp ON true
WHERE p.portfolio_id = ${pid}
) q;
`);
const portfolioAgg = portfolioMetricsResult.rows[0] as PortfolioAggregates;
/* ----------------------------------------------------------
QUERY 3 EPC band distribution (ALL properties)
---------------------------------------------------------- */
const epcRows = await db.execute(sql`
SELECT
CASE
WHEN lp.id IS NOT NULL THEN lp.post_sap_points
ELSE COALESCE(p.current_sap_points, 0)
ELSE p.current_sap_points
END AS effective_sap
FROM property p
LEFT JOIN LATERAL (
@ -218,30 +281,35 @@ export async function GET(
/* ----------------------------------------------------------
RESPONSE
---------------------------------------------------------- */
const pc_cost = (upgraded.total_cost ?? 0) * 0.3;
const constructionCost = upgraded.total_cost ?? 0;
const nUpgraded = upgraded.n_units_upgraded ?? 0;
const pc_cost = constructionCost * 0.3;
return NextResponse.json({
/* -------- portfolio-after-scenario -------- */
avg_sap:
scenarioAgg.avg_sap !== null
? Number(scenarioAgg.avg_sap).toFixed(1)
portfolioAgg.avg_sap !== null
? Number(portfolioAgg.avg_sap).toFixed(1)
: null,
avg_carbon: scenarioAgg.avg_carbon,
avg_bills: scenarioAgg.avg_bills,
total_carbon: scenarioAgg.total_carbon,
total_bills: scenarioAgg.total_bills,
n_units: scenarioAgg.n_units,
scenario_epc_counts,
pc_cost,
avg_carbon: portfolioAgg.avg_carbon,
avg_bills: portfolioAgg.avg_bills,
total_carbon: portfolioAgg.total_carbon,
total_bills: portfolioAgg.total_bills,
n_units_upgraded: upgraded.n_units_upgraded,
construction_cost: upgraded.total_cost ?? 0,
/* -------- scenario-only -------- */
n_units: scenarioAgg.n_units,
n_units_upgraded: nUpgraded,
construction_cost: constructionCost,
contingency: upgraded.contingency ?? 0,
total_funding: upgraded.total_funding ?? 0,
net_cost: (upgraded.total_cost ?? 0) - (upgraded.total_funding ?? 0),
gross_per_unit:
upgraded.n_units_upgraded > 0
? ((upgraded.total_cost ?? 0) + pc_cost) / upgraded.n_units_upgraded
: 0,
net_cost: constructionCost - (upgraded.total_funding ?? 0),
total_sap_uplift: scenarioAgg.total_sap_uplift ?? 0,
gross_per_unit:
nUpgraded > 0 ? (constructionCost + pc_cost) / nUpgraded : 0,
/* -------- shared -------- */
scenario_epc_counts,
pc_cost,
});
}

View file

@ -17,8 +17,6 @@ export default async function ReportingPage(props: {
]);
const scenarios = await getScenarios(Number(portfolioId));
console.log("Baseline EPC counts", baseline.epcBands);
return (
<div className="max-w-8xl mx-auto px-6 pb-10 space-y-4 pt-4">
<div className="mb-6">

View file

@ -37,7 +37,7 @@ export async function middleware(req: NextRequest) {
export const config = {
matcher: [
// Protect only your apps authenticated areas
// Protect only apps authenticated areas
"/home/:path*",
"/portfolio/:path*",
"/search/:path*",