diff --git a/src/app/api/portfolio/[portfolioId]/scenario/[scenarioId]/measures/route.ts b/src/app/api/portfolio/[portfolioId]/scenario/[scenarioId]/measures/route.ts index dccf47d9..21aaed37 100644 --- a/src/app/api/portfolio/[portfolioId]/scenario/[scenarioId]/measures/route.ts +++ b/src/app/api/portfolio/[portfolioId]/scenario/[scenarioId]/measures/route.ts @@ -20,62 +20,11 @@ export async function GET( const pid = BigInt(portfolioId); const sid = BigInt(scenarioId); - // TEMP: Remove batteries as underspecified - // const result = await db.execute(sql` - // WITH latest_plans AS ( - // SELECT DISTINCT ON (property_id) - // * - // FROM plan - // WHERE portfolio_id = ${pid} - // AND scenario_id = ${sid} - // ORDER BY property_id, created_at DESC - // ), - - // recommendation_flags AS ( - // SELECT - // r.id AS recommendation_id, - // r.measure_type AS measure_type, - // r.property_id AS property_id, - // r.estimated_cost AS estimated_cost, - // BOOL_OR(m.includes_battery) AS includes_battery - - // FROM latest_plans lp - // JOIN plan_recommendations pr - // ON pr.plan_id = lp.id - // JOIN recommendation r - // ON r.id = pr.recommendation_id - - // LEFT JOIN recommendation_materials rm - // ON rm.recommendation_id = r.id - // LEFT JOIN material m - // ON m.id = rm.material_id - // AND m.is_active = true - - // WHERE r.default = true - // AND r.already_installed = false - - // GROUP BY - // r.id, - // r.measure_type, - // r.property_id, - // r.estimated_cost - // ) - - // SELECT - // measure_type, - // COALESCE(includes_battery, false) AS includes_battery, - - // COUNT(DISTINCT property_id)::int AS homes_count, - // SUM(estimated_cost)::float AS total_cost, - // AVG(estimated_cost)::float AS average_cost - - // FROM recommendation_flags - // GROUP BY - // measure_type, - // includes_battery - // ORDER BY total_cost DESC; - // `); - + // Latest plan per property for this scenario, then its recommendations read + // through the DENORMALISED link: join by the indexed recommendation.property_id + // and scope to the plan via recommendation.plan_id. The plan_recommendations + // join table is retired (no rows for new-approach plans), so the old EXISTS + // against it returned zero measures. See the handover / ADR notes. const result = await db.execute(sql` SELECT r.measure_type, @@ -83,23 +32,19 @@ export async function GET( COUNT(DISTINCT r.property_id)::int AS homes_count, SUM(r.estimated_cost)::float AS total_cost, AVG(r.estimated_cost)::float AS average_cost - FROM recommendation r - WHERE r.default = true + FROM ( + SELECT DISTINCT ON (property_id) + id, property_id + FROM plan + WHERE portfolio_id = ${pid} + AND scenario_id = ${sid} + ORDER BY property_id, created_at DESC + ) lp + JOIN recommendation r + ON r.property_id = lp.property_id + AND r.plan_id = lp.id + AND r.default = true AND r.already_installed = false - AND EXISTS ( - SELECT 1 - FROM ( - SELECT DISTINCT ON (p.property_id) - p.id - FROM plan p - WHERE p.portfolio_id = ${pid} - AND p.scenario_id = ${sid} - ORDER BY p.property_id, p.created_at DESC - ) lp - JOIN plan_recommendations pr - ON pr.plan_id = lp.id - WHERE pr.recommendation_id = r.id - ) GROUP BY r.measure_type, r.type diff --git a/src/app/api/portfolio/[portfolioId]/scenario/default/measures/route.ts b/src/app/api/portfolio/[portfolioId]/scenario/default/measures/route.ts index 1e3d2bc1..3640c773 100644 --- a/src/app/api/portfolio/[portfolioId]/scenario/default/measures/route.ts +++ b/src/app/api/portfolio/[portfolioId]/scenario/default/measures/route.ts @@ -19,6 +19,11 @@ export async function GET( const pid = BigInt(portfolioId); + // Latest default plan per property, then its recommendations read through the + // DENORMALISED link: join by the indexed recommendation.property_id and scope + // to the plan via recommendation.plan_id. The plan_recommendations join table + // is retired (no rows for new-approach plans), so the old EXISTS against it + // returned zero measures. const result = await db.execute(sql` SELECT r.measure_type, @@ -26,23 +31,19 @@ export async function GET( COUNT(DISTINCT r.property_id)::int AS homes_count, SUM(r.estimated_cost)::float AS total_cost, AVG(r.estimated_cost)::float AS average_cost - FROM recommendation r - WHERE r.default = true + FROM ( + SELECT DISTINCT ON (property_id) + id, property_id + FROM plan + WHERE portfolio_id = ${pid} + AND is_default = true + ORDER BY property_id, created_at DESC + ) lp + JOIN recommendation r + ON r.property_id = lp.property_id + AND r.plan_id = lp.id + AND r.default = true AND r.already_installed = false - AND EXISTS ( - SELECT 1 - FROM ( - SELECT DISTINCT ON (p.property_id) - p.id - FROM plan p - WHERE p.portfolio_id = ${pid} - AND p.is_default = true - ORDER BY p.property_id, p.created_at DESC - ) lp - JOIN plan_recommendations pr - ON pr.plan_id = lp.id - WHERE pr.recommendation_id = r.id - ) GROUP BY r.measure_type, r.type