--liquibase formatted sql -- PANELS · core · fn_work_program_get: jsonb_agg no puede anidar SUM (42803) --changeset panel:core-036-fn-work-program-get-curve splitStatements:false CREATE OR REPLACE FUNCTION core.fn_work_program_get(payload jsonb) RETURNS jsonb LANGUAGE plpgsql SECURITY INVOKER SET search_path = core AS $$ DECLARE v_project_id bigint := NULLIF(payload->>'project_id', '')::bigint; v_program jsonb; v_periods jsonb; v_budget_total numeric(14,2); v_scheduled jsonb; v_cumulative numeric := 0; v_row jsonb; v_arr jsonb := '[]'::jsonb; v_pct_period numeric; v_pct_cum numeric; BEGIN IF v_project_id IS NULL THEN RETURN core.rpc_err('VALIDATION', 'fn_work_program_get: project_id es obligatorio', jsonb_build_object('fn', 'fn_work_program_get')); END IF; SELECT to_jsonb(wp) INTO v_program FROM work_programs wp WHERE wp.project_id = v_project_id; IF v_program IS NULL THEN SELECT COALESCE(SUM(amount), 0) INTO v_budget_total FROM budget_items WHERE project_id = v_project_id; RETURN core.rpc_ok( jsonb_build_object( 'program', NULL, 'periods', '[]'::jsonb, 'budget_subtotal', v_budget_total, 'curve', '[]'::jsonb ), format('Proyecto %s sin programa de obra', v_project_id), jsonb_build_object('fn', 'fn_work_program_get', 'project_id', v_project_id) ); END IF; SELECT COALESCE(jsonb_agg(to_jsonb(p) ORDER BY p.period_index), '[]'::jsonb) INTO v_periods FROM work_program_periods p WHERE p.program_id = (v_program->>'id')::bigint; SELECT COALESCE(SUM(amount), 0) INTO v_budget_total FROM budget_items WHERE project_id = v_project_id; -- SUM va en la subquery; jsonb_agg afuera. Postgres no permite agregados anidados -- (sqlstate 42803) y esto petaba el GET justo después de importar por partida. SELECT COALESCE(jsonb_agg(to_jsonb(x) ORDER BY x.period_index), '[]'::jsonb) INTO v_scheduled FROM ( SELECT pr.period_index, pr.label, round(COALESCE(SUM(pa.amount), 0), 2) AS amount FROM work_program_periods pr LEFT JOIN work_program_partida_amounts pa ON pa.period_id = pr.id WHERE pr.program_id = (v_program->>'id')::bigint GROUP BY pr.period_index, pr.label ) x; FOR v_row IN SELECT value FROM jsonb_array_elements(v_scheduled) LOOP v_pct_period := CASE WHEN v_budget_total > 0 THEN round((v_row.value->>'amount')::numeric / v_budget_total, 6) ELSE 0 END; v_cumulative := v_cumulative + (v_row.value->>'amount')::numeric; v_pct_cum := CASE WHEN v_budget_total > 0 THEN round(v_cumulative / v_budget_total, 6) ELSE 0 END; v_arr := v_arr || jsonb_build_array(jsonb_build_object( 'period_index', v_row.value->'period_index', 'label', v_row.value->'label', 'amount', v_row.value->'amount', 'pct_period', v_pct_period, 'pct_cumulative', v_pct_cum )); END LOOP; RETURN core.rpc_ok( jsonb_build_object( 'program', v_program, 'periods', v_periods, 'budget_subtotal', v_budget_total, 'curve', v_arr ), format('Programa de obra del proyecto %s', v_project_id), jsonb_build_object('fn', 'fn_work_program_get', 'project_id', v_project_id) ); EXCEPTION WHEN OTHERS THEN RETURN core.rpc_from_exception('fn_work_program_get', SQLSTATE, SQLERRM); END; $$;