mirror of
https://origin.cursor.com/mrdevmx/panels.git
synced 2026-10-09 19:43:18 +00:00
<!-- CURSOR_AGENT_PR_BODY_BEGIN --> ## Problema Tras cargar el programa, la pantalla pide en paralelo get / concepts / partidas / **vs-cost**. El vs-cost fallaba: `fn_work_program_vs_cost (sqlstate=42703): column m.project_id does not exist` `warehouse_movements` no tiene `project_id`. El proyecto está en `warehouses.project_id`. ## Cambio (changeset 038) - Salidas de almacén por `JOIN warehouses w ON w.id = m.warehouse_id WHERE w.project_id = …` - Ejecutado por partida **sin duplicar** al cruzar varias semanas (el join anterior multiplicaba el monto) ## Prueba local (hecha antes de pedir merge) En Postgres 16 corrí las **4 RPCs** que usa la página, con presupuesto, 2 semanas, gasto $50 y salida de almacén $25: - `fn_work_program_get` — curva 400 / 600 - `fn_work_program_list_concepts` — items - `fn_work_program_list_partidas` — A001 - `fn_work_program_vs_cost` — ejecutado semanal **75**, por partida **75** (no 150) Script: `scripts/verify-work-program-load.sh` ## Cómo probar en tu ambiente 1. Mergear y aplicar Liquibase (038). 2. Recargar Programa de obra en la obra ya importada. Sin toast rojo. 3. La pestaña / bloque de programado vs costo debe abrir. <!-- CURSOR_AGENT_PR_BODY_END --> <div><a href="https://cursor.com/agents/bc-d8a5d34a-ead9-4912-8296-322cff799783?cursor_ref=pr_footer&cursor_cta=open_in_web"><picture><source media="(prefers-color-scheme: dark)" srcset="https://cursor.com/assets/images/open-in-web-dark.png"><source media="(prefers-color-scheme: light)" srcset="https://cursor.com/assets/images/open-in-web-light.png"><img alt="Open in Web" width="114" height="28" src="https://cursor.com/assets/images/open-in-web-dark.png"></picture></a> <a href="https://cursor.com/background-agent?bcId=bc-d8a5d34a-ead9-4912-8296-322cff799783&cursor_ref=pr_footer&cursor_cta=open_in_cursor"><picture><source media="(prefers-color-scheme: dark)" srcset="https://cursor.com/assets/images/open-in-cursor-dark.png"><source media="(prefers-color-scheme: light)" srcset="https://cursor.com/assets/images/open-in-cursor-light.png"><img alt="Open in Cursor" width="131" height="28" src="https://cursor.com/assets/images/open-in-cursor-dark.png"></picture></a> </div>
103 lines
3.7 KiB
PL/PgSQL
103 lines
3.7 KiB
PL/PgSQL
--liquibase formatted sql
|
|
-- PANELS · core · fn_work_program_vs_cost: warehouse_movements no tiene project_id
|
|
|
|
--changeset panel:core-038-fn-work-program-vs-cost splitStatements:false
|
|
CREATE OR REPLACE FUNCTION core.fn_work_program_vs_cost(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_id bigint;
|
|
v_periods jsonb;
|
|
v_by_period jsonb;
|
|
v_by_chapter jsonb;
|
|
v_budget_total numeric(14,2);
|
|
BEGIN
|
|
IF v_project_id IS NULL THEN
|
|
RETURN core.rpc_err('VALIDATION', 'fn_work_program_vs_cost: project_id es obligatorio',
|
|
jsonb_build_object('fn', 'fn_work_program_vs_cost'));
|
|
END IF;
|
|
|
|
SELECT id INTO v_program_id FROM work_programs WHERE project_id = v_project_id;
|
|
IF v_program_id IS NULL THEN
|
|
RETURN core.rpc_ok(jsonb_build_object('periods', '[]'::jsonb, 'by_period', '[]'::jsonb, 'by_chapter', '[]'::jsonb),
|
|
format('Proyecto %s sin programa', v_project_id),
|
|
jsonb_build_object('fn', 'fn_work_program_vs_cost', 'project_id', v_project_id));
|
|
END IF;
|
|
|
|
SELECT COALESCE(SUM(amount), 0) INTO v_budget_total FROM budget_items WHERE project_id = v_project_id;
|
|
|
|
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;
|
|
|
|
SELECT COALESCE(jsonb_agg(to_jsonb(x) ORDER BY x.period_index), '[]'::jsonb) INTO v_by_period
|
|
FROM (
|
|
SELECT
|
|
pr.period_index,
|
|
pr.label,
|
|
pr.start_date,
|
|
pr.end_date,
|
|
round(COALESCE(SUM(pa.amount), 0), 2) AS scheduled_amount,
|
|
round(COALESCE((
|
|
SELECT SUM(e.subtotal)
|
|
FROM expense_entries e
|
|
WHERE e.project_id = v_project_id
|
|
AND e.status = 'confirmed'
|
|
AND e.counts_toward_budget = true
|
|
AND e.expense_date BETWEEN pr.start_date AND pr.end_date
|
|
), 0) + COALESCE((
|
|
SELECT SUM(m.total_cost)
|
|
FROM warehouse_movements m
|
|
JOIN warehouses w ON w.id = m.warehouse_id
|
|
WHERE w.project_id = v_project_id
|
|
AND m.movement_type = 'exit'
|
|
AND m.movement_date BETWEEN pr.start_date AND pr.end_date
|
|
), 0), 2) AS executed_amount
|
|
FROM work_program_periods pr
|
|
LEFT JOIN work_program_partida_amounts pa ON pa.period_id = pr.id AND pa.program_id = v_program_id
|
|
WHERE pr.program_id = v_program_id
|
|
GROUP BY pr.id, pr.period_index, pr.label, pr.start_date, pr.end_date
|
|
) x;
|
|
|
|
SELECT COALESCE(jsonb_agg(to_jsonb(y) ORDER BY y.chapter_code), '[]'::jsonb) INTO v_by_chapter
|
|
FROM (
|
|
SELECT
|
|
bc.id AS chapter_id,
|
|
bc.code AS chapter_code,
|
|
bc.name AS chapter_name,
|
|
round(COALESCE((
|
|
SELECT SUM(pa.amount)
|
|
FROM work_program_partida_amounts pa
|
|
WHERE pa.budget_chapter_id = bc.id AND pa.program_id = v_program_id
|
|
), 0), 2) AS scheduled_amount,
|
|
round(COALESCE((
|
|
SELECT SUM(core._cost_executed_amount(bi.id))
|
|
FROM budget_items bi
|
|
WHERE bi.chapter_id = bc.id
|
|
), 0), 2) AS executed_amount,
|
|
round(COALESCE((
|
|
SELECT SUM(bi.amount)
|
|
FROM budget_items bi
|
|
WHERE bi.chapter_id = bc.id
|
|
), 0), 2) AS budget_amount
|
|
FROM budget_chapters bc
|
|
WHERE bc.project_id = v_project_id
|
|
) y;
|
|
|
|
RETURN core.rpc_ok(
|
|
jsonb_build_object(
|
|
'periods', v_periods,
|
|
'budget_subtotal', v_budget_total,
|
|
'by_period', v_by_period,
|
|
'by_chapter', v_by_chapter
|
|
),
|
|
format('Programado vs ejecutado del proyecto %s', v_project_id),
|
|
jsonb_build_object('fn', 'fn_work_program_vs_cost', 'project_id', v_project_id)
|
|
);
|
|
EXCEPTION WHEN OTHERS THEN
|
|
RETURN core.rpc_from_exception('fn_work_program_vs_cost', SQLSTATE, SQLERRM);
|
|
END;
|
|
$$;
|