--liquibase formatted sql -- PANELS · core · cierre de almacén al concluir proyecto --changeset panel:core-026a-fn-project-update-warehouse splitStatements:false CREATE OR REPLACE FUNCTION core.fn_project_update(payload jsonb) RETURNS jsonb LANGUAGE plpgsql SECURITY INVOKER SET search_path = core AS $$ DECLARE v_id bigint := (payload->>'id')::bigint; v_cur projects%ROWTYPE; v_name text; v_address text; v_theme_id text; v_status text; v_company_id integer; v_contract_amount numeric(14,2); v_start_date date; v_end_date date; v_resident_name text; v_siroc text; v_payroll_tax_pct numeric(5,2); v_close_env jsonb; BEGIN IF v_id IS NULL THEN RETURN core.rpc_err( 'VALIDATION', 'fn_project_update: id es obligatorio', jsonb_build_object('fn', 'fn_project_update', 'field', 'id') ); END IF; SELECT * INTO v_cur FROM projects WHERE id = v_id; IF NOT FOUND THEN RETURN core.rpc_err( 'NOT_FOUND', format('fn_project_update: proyecto id=%s no encontrado', v_id), jsonb_build_object('fn', 'fn_project_update', 'id', v_id) ); END IF; v_name := coalesce(nullif(btrim(payload->>'name'), ''), v_cur.name); v_address := CASE WHEN payload ? 'address' THEN coalesce(nullif(btrim(payload->>'address'), ''), '') ELSE v_cur.address END; v_theme_id := coalesce(nullif(btrim(payload->>'theme_id'), ''), v_cur.theme_id); v_status := CASE WHEN payload ? 'status' AND btrim(payload->>'status') IN ('activo', 'pausado', 'concluido', 'cancelado') THEN btrim(payload->>'status') ELSE v_cur.status END; v_company_id := coalesce((payload->>'company_id')::integer, v_cur.company_id); v_contract_amount := CASE WHEN payload ? 'contract_amount' THEN (payload->>'contract_amount')::numeric(14,2) ELSE v_cur.contract_amount END; v_start_date := CASE WHEN payload ? 'start_date' THEN nullif(btrim(payload->>'start_date'), '')::date ELSE v_cur.start_date END; v_end_date := CASE WHEN payload ? 'end_date' THEN nullif(btrim(payload->>'end_date'), '')::date ELSE v_cur.end_date END; v_resident_name := CASE WHEN payload ? 'resident_name' THEN coalesce(nullif(btrim(payload->>'resident_name'), ''), '') ELSE v_cur.resident_name END; v_siroc := CASE WHEN payload ? 'siroc' THEN upper(coalesce(nullif(btrim(payload->>'siroc'), ''), '')) ELSE v_cur.siroc END; v_payroll_tax_pct := coalesce((payload->>'payroll_tax_pct')::numeric(5,2), v_cur.payroll_tax_pct); IF v_company_id IS NULL THEN RETURN core.rpc_err( 'VALIDATION', 'fn_project_update: company_id es obligatorio', jsonb_build_object('fn', 'fn_project_update', 'field', 'company_id', 'id', v_id) ); END IF; IF NOT EXISTS (SELECT 1 FROM companies WHERE id = v_company_id) THEN RETURN core.rpc_err( 'VALIDATION', format('fn_project_update: empresa id=%s no encontrada', v_company_id), jsonb_build_object('fn', 'fn_project_update', 'company_id', v_company_id, 'id', v_id) ); END IF; IF v_start_date IS NOT NULL AND v_end_date IS NOT NULL AND v_end_date < v_start_date THEN RETURN core.rpc_err( 'VALIDATION', 'fn_project_update: la fecha de término no puede ser anterior al inicio', jsonb_build_object('fn', 'fn_project_update', 'field', 'end_date', 'id', v_id) ); END IF; IF v_status IN ('concluido', 'cancelado') AND v_cur.status NOT IN ('concluido', 'cancelado') THEN v_close_env := core.fn_warehouse_close_project(jsonb_build_object( 'project_id', v_id, 'force', COALESCE((payload->>'force_warehouse_close')::boolean, false), 'closed_by_name', coalesce(nullif(btrim(payload->>'closed_by_name'), ''), 'sistema'), 'close_notes', coalesce(nullif(btrim(payload->>'warehouse_close_notes'), ''), 'Cierre automático al concluir obra') )); IF NOT COALESCE((v_close_env->>'ok')::boolean, false) THEN RETURN v_close_env; END IF; END IF; UPDATE projects SET name = v_name, address = v_address, theme_id = v_theme_id, status = v_status, company_id = v_company_id, contract_amount = v_contract_amount, start_date = v_start_date, end_date = v_end_date, resident_name = v_resident_name, siroc = v_siroc, payroll_tax_pct = v_payroll_tax_pct WHERE id = v_id; RETURN core.rpc_ok( jsonb_build_object('id', v_id, 'code', v_cur.code, 'status', v_status), format('Proyecto %s actualizado', v_cur.code), jsonb_build_object('fn', 'fn_project_update', 'id', v_id, 'code', v_cur.code) ); EXCEPTION WHEN OTHERS THEN RETURN core.rpc_from_exception('fn_project_update', SQLSTATE, SQLERRM); END; $$;