From 7ffdd9f7bf3de8257149f4ade8651c4b3127d515 Mon Sep 17 00:00:00 2001 From: Alberto Martinez <349769123@users.noreply.cursor.com> Date: Tue, 8 Sep 2026 21:46:48 +0000 Subject: [PATCH] fix: recargar programa de obra (v_row no es una tabla) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Problema Tras el #21 el GET ya no choca por agregados anidados, pero al recargar aparece: `fn_work_program_get (sqlstate=42P01): missing FROM-clause entry for table "v_row"` `v_row` es `jsonb`. Escribir `v_row.value` lo toma Postgres como `tabla.columna`, no como campo del JSON. El #21 destapó este bug: el 42803 cortaba antes de llegar al loop. ## Cambio Changeset **037** (no se edita el 036, ya corrido). La curva se arma en SQL (subquery + `SUM() OVER`) sin el `FOR` ni `v_row.value`. ## Cómo probar 1. Mergear este PR y aplicar Liquibase (037). 2. Abrir Programa de obra en la obra que ya importaste. Debe cargar sin toast rojo. 3. Recargar la página: igual. 4. Si hace falta, volver a Importar el Excel por partida y confirmar que se ve el programa.
Open in Web Open in Cursor 
--- db/core/changelog-master.xml | 1 + .../037-fn-work-program-get-curve-vrow.sql | 85 +++++++++++++++++++ 2 files changed, 86 insertions(+) create mode 100644 db/core/changesets/037-fn-work-program-get-curve-vrow.sql diff --git a/db/core/changelog-master.xml b/db/core/changelog-master.xml index d11dbb0..83712ea 100644 --- a/db/core/changelog-master.xml +++ b/db/core/changelog-master.xml @@ -46,5 +46,6 @@ + diff --git a/db/core/changesets/037-fn-work-program-get-curve-vrow.sql b/db/core/changesets/037-fn-work-program-get-curve-vrow.sql new file mode 100644 index 0000000..228101a --- /dev/null +++ b/db/core/changesets/037-fn-work-program-get-curve-vrow.sql @@ -0,0 +1,85 @@ +--liquibase formatted sql +-- PANELS · core · fn_work_program_get: v_row es jsonb, no un record (.value = 42P01) + +--changeset panel:core-037-fn-work-program-get-curve-vrow 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_curve jsonb; +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; + + -- Sin FOR + v_row.value (eso lo toma Postgres como tabla.columna → 42P01). + -- SUM de montos en subquery; jsonb_agg y el acumulado van en capas distintas + -- para no anidar agregados (42803). + SELECT COALESCE(jsonb_agg(to_jsonb(y) ORDER BY y.period_index), '[]'::jsonb) + INTO v_curve + FROM ( + SELECT + x.period_index, + x.label, + x.amount, + CASE WHEN v_budget_total > 0 THEN round(x.amount / v_budget_total, 6) ELSE 0 END AS pct_period, + CASE WHEN v_budget_total > 0 + THEN round(SUM(x.amount) OVER (ORDER BY x.period_index) / v_budget_total, 6) + ELSE 0 + END AS pct_cumulative + 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 + ) y; + + RETURN core.rpc_ok( + jsonb_build_object( + 'program', v_program, + 'periods', v_periods, + 'budget_subtotal', v_budget_total, + 'curve', v_curve + ), + 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; +$$;