panels-origin/db/core/changesets/006-rpc-infra.sql
Cursor Agent 8717c00a83
Refactor payroll_http and excel to use core RPC functions
- Replace db.prepare() in payroll_http with callCoreFn for attendance,
  loans, loan receipts, and legacy payroll period routes
- Use respondRpc/respondApiError for standardized HTTP responses
- Add fn_loan_list/get, fn_loan_payment_get, and fn_payroll_period_* RPCs
  to 013-rpc-payroll.sql with grants
- Refactor excel storeDocument to fn_worker_document_store (S3 stays in TS)
- Refactor importExcel and upsertWorker to fn_workers_import_batch

Co-authored-by: alberto.martinez <alberto.martinez@mrdev.mx>
2026-09-03 23:57:56 +00:00

147 lines
4.1 KiB
PL/PgSQL

--liquibase formatted sql
-- PANELS · core · RPC infrastructure (envelope helpers)
--changeset panel:core-006a-rpc-generic-messages endDelimiter:; splitStatements:true
CREATE OR REPLACE FUNCTION core.rpc_generic_messages()
RETURNS text[]
LANGUAGE sql
IMMUTABLE
AS $$
SELECT ARRAY[
'OK', 'Error', 'ERROR', 'Operación exitosa', 'Operacion exitosa',
'Algo salió mal', 'Algo salio mal', 'Error interno', 'Error interno del servidor'
]::text[];
$$;
--changeset panel:core-006b-rpc-assert-message endDelimiter:; splitStatements:true
CREATE OR REPLACE FUNCTION core.rpc_assert_message(p_message text)
RETURNS void
LANGUAGE plpgsql
IMMUTABLE
AS $$
BEGIN
IF p_message IS NULL OR btrim(p_message) = '' THEN
RAISE EXCEPTION 'RPC message must not be empty';
END IF;
IF p_message = ANY (core.rpc_generic_messages()) THEN
RAISE EXCEPTION 'RPC message is too generic: %', p_message;
END IF;
END;
$$;
--changeset panel:core-006c-rpc-ok endDelimiter:; splitStatements:true
CREATE OR REPLACE FUNCTION core.rpc_ok(
p_data jsonb,
p_message text,
p_ctx jsonb DEFAULT '{}'::jsonb
)
RETURNS jsonb
LANGUAGE plpgsql
IMMUTABLE
AS $$
BEGIN
PERFORM core.rpc_assert_message(p_message);
RETURN jsonb_build_object(
'ok', true,
'code', 'OK',
'layer', 'db',
'message', p_message,
'context', COALESCE(p_ctx, '{}'::jsonb),
'data', p_data,
'errors', NULL
);
END;
$$;
--changeset panel:core-006d-rpc-created endDelimiter:; splitStatements:true
CREATE OR REPLACE FUNCTION core.rpc_created(
p_data jsonb,
p_message text,
p_ctx jsonb DEFAULT '{}'::jsonb
)
RETURNS jsonb
LANGUAGE plpgsql
IMMUTABLE
AS $$
BEGIN
PERFORM core.rpc_assert_message(p_message);
RETURN jsonb_build_object(
'ok', true,
'code', 'CREATED',
'layer', 'db',
'message', p_message,
'context', COALESCE(p_ctx, '{}'::jsonb),
'data', p_data,
'errors', NULL
);
END;
$$;
--changeset panel:core-006e-rpc-err endDelimiter:; splitStatements:true
CREATE OR REPLACE FUNCTION core.rpc_err(
p_code text,
p_message text,
p_ctx jsonb DEFAULT '{}'::jsonb,
p_errors jsonb DEFAULT NULL
)
RETURNS jsonb
LANGUAGE plpgsql
IMMUTABLE
AS $$
BEGIN
PERFORM core.rpc_assert_message(p_message);
RETURN jsonb_build_object(
'ok', false,
'code', p_code,
'layer', 'db',
'message', p_message,
'context', COALESCE(p_ctx, '{}'::jsonb),
'data', NULL,
'errors', p_errors
);
END;
$$;
--changeset panel:core-006f-rpc-map-exception endDelimiter:; splitStatements:true
CREATE OR REPLACE FUNCTION core.rpc_from_exception(
p_fn text,
p_sqlstate text,
p_message text,
p_detail text DEFAULT NULL
)
RETURNS jsonb
LANGUAGE plpgsql
IMMUTABLE
AS $$
DECLARE
v_code text := 'INTERNAL';
v_msg text;
v_ctx jsonb := jsonb_build_object('fn', p_fn, 'sqlstate', p_sqlstate);
BEGIN
IF p_sqlstate = '23505' THEN
v_code := 'CONFLICT';
v_msg := format('CONFLICT en %s: %s', p_fn, COALESCE(p_detail, p_message));
ELSIF p_sqlstate = '23503' THEN
v_code := 'VALIDATION';
v_msg := format('Referencia inválida en %s: %s', p_fn, COALESCE(p_detail, p_message));
ELSIF p_sqlstate = '23514' THEN
v_code := 'VALIDATION';
v_msg := format('Restricción de valor en %s: %s', p_fn, COALESCE(p_detail, p_message));
ELSIF p_sqlstate = '40P01' THEN
v_code := 'CONFLICT';
v_msg := format('Deadlock en %s: otra operación modificó los mismos registros, intente de nuevo', p_fn);
ELSE
v_code := 'INTERNAL';
v_msg := format('Error inesperado en %s (sqlstate=%s): %s', p_fn, p_sqlstate, p_message);
END IF;
RETURN core.rpc_err(v_code, v_msg, v_ctx);
END;
$$;
--changeset panel:core-006g-rpc-grants endDelimiter:; splitStatements:true
GRANT EXECUTE ON FUNCTION core.rpc_generic_messages() TO panels_core_app;
GRANT EXECUTE ON FUNCTION core.rpc_assert_message(text) TO panels_core_app;
GRANT EXECUTE ON FUNCTION core.rpc_ok(jsonb, text, jsonb) TO panels_core_app;
GRANT EXECUTE ON FUNCTION core.rpc_created(jsonb, text, jsonb) TO panels_core_app;
GRANT EXECUTE ON FUNCTION core.rpc_err(text, text, jsonb, jsonb) TO panels_core_app;
GRANT EXECUTE ON FUNCTION core.rpc_from_exception(text, text, text, text) TO panels_core_app;