--liquibase formatted sql -- PANELS · core · fn_work_program_replace: jsonb_object_length no existe en PostgreSQL --changeset panel:core-032a-fn-work-program-replace-period-count splitStatements:false CREATE OR REPLACE FUNCTION core.fn_work_program_replace(payload jsonb) RETURNS jsonb LANGUAGE plpgsql SECURITY INVOKER SET search_path = core AS $$ DECLARE v_project_id bigint := NULLIF(payload->>'project_id', '')::bigint; v_source text := COALESCE(nullif(btrim(payload->>'source'), ''), 'import'); v_status text := COALESCE(nullif(btrim(payload->>'status'), ''), 'active'); v_start date; v_end date; v_program_id bigint; v_period jsonb; v_period_id bigint; v_period_map jsonb := '{}'::jsonb; v_period_count integer := 0; v_slot jsonb; v_partida jsonb; v_chapter jsonb; v_ch jsonb; v_slot_count integer := 0; v_partida_count integer := 0; v_err jsonb; BEGIN IF v_project_id IS NULL THEN RETURN core.rpc_err('VALIDATION', 'fn_work_program_replace: project_id es obligatorio', jsonb_build_object('fn', 'fn_work_program_replace')); END IF; v_err := core._wp_assert_project_ready(v_project_id); IF v_err IS NOT NULL THEN RETURN v_err; END IF; SELECT start_date, end_date INTO v_start, v_end FROM projects WHERE id = v_project_id; DELETE FROM work_programs WHERE project_id = v_project_id; INSERT INTO work_programs (project_id, start_date, end_date, duration_days, source, status) VALUES (v_project_id, v_start, v_end, (v_end - v_start + 1), v_source, v_status) RETURNING id INTO v_program_id; FOR v_period IN SELECT * FROM jsonb_array_elements(core._wp_generate_periods(v_start, v_end)) LOOP INSERT INTO work_program_periods (program_id, period_index, label, start_date, end_date) VALUES ( v_program_id, (v_period->>'period_index')::integer, v_period->>'label', (v_period->>'start_date')::date, (v_period->>'end_date')::date ) RETURNING id INTO v_period_id; v_period_map := v_period_map || jsonb_build_object(v_period->>'period_index', v_period_id); v_period_count := v_period_count + 1; END LOOP; FOR v_slot IN SELECT * FROM jsonb_array_elements(COALESCE(payload->'concept_slots', '[]'::jsonb)) LOOP v_period_id := (v_period_map->>(v_slot->>'period_index'))::bigint; IF v_period_id IS NULL THEN CONTINUE; END IF; IF NOT EXISTS ( SELECT 1 FROM budget_items bi WHERE bi.id = (v_slot->>'budget_item_id')::bigint AND bi.project_id = v_project_id ) THEN CONTINUE; END IF; IF core._wp_slot_weight((v_slot->>'start_pct')::numeric, (v_slot->>'end_pct')::numeric) <= 0 THEN CONTINUE; END IF; INSERT INTO work_program_concept_slots (program_id, budget_item_id, period_id, start_pct, end_pct) VALUES ( v_program_id, (v_slot->>'budget_item_id')::bigint, v_period_id, COALESCE((v_slot->>'start_pct')::numeric, 0), COALESCE((v_slot->>'end_pct')::numeric, 0) ); v_slot_count := v_slot_count + 1; END LOOP; FOR v_ch IN SELECT * FROM jsonb_array_elements(COALESCE(payload->'chapters_to_create', '[]'::jsonb)) LOOP INSERT INTO budget_chapters (project_id, parent_id, code, name, wbs, sort_order) VALUES ( v_project_id, NULLIF(v_ch->>'parent_id', '')::bigint, upper(COALESCE(nullif(btrim(v_ch->>'code'), ''), '')), COALESCE(nullif(btrim(v_ch->>'name'), ''), 'Partida'), COALESCE(nullif(btrim(v_ch->>'wbs'), ''), ''), COALESCE((v_ch->>'sort_order')::integer, 0) ); END LOOP; FOR v_partida IN SELECT * FROM jsonb_array_elements(COALESCE(payload->'partida_amounts', '[]'::jsonb)) LOOP v_period_id := (v_period_map->>(v_partida->>'period_index'))::bigint; IF v_period_id IS NULL THEN CONTINUE; END IF; IF NOT EXISTS ( SELECT 1 FROM budget_chapters bc WHERE bc.id = (v_partida->>'budget_chapter_id')::bigint AND bc.project_id = v_project_id ) THEN CONTINUE; END IF; IF COALESCE((v_partida->>'amount')::numeric, 0) = 0 THEN CONTINUE; END IF; INSERT INTO work_program_partida_amounts (program_id, budget_chapter_id, period_id, amount) VALUES ( v_program_id, (v_partida->>'budget_chapter_id')::bigint, v_period_id, round(COALESCE((v_partida->>'amount')::numeric, 0), 2) ); v_partida_count := v_partida_count + 1; END LOOP; RETURN core.rpc_ok( jsonb_build_object( 'program_id', v_program_id, 'concept_slots', v_slot_count, 'partida_amounts', v_partida_count, 'period_count', v_period_count ), format('Programa de obra del proyecto %s reemplazado (%s slots, %s erogaciones)', v_project_id, v_slot_count, v_partida_count), jsonb_build_object('fn', 'fn_work_program_replace', 'project_id', v_project_id) ); EXCEPTION WHEN OTHERS THEN RETURN core.rpc_from_exception('fn_work_program_replace', SQLSTATE, SQLERRM); END; $$;