panels-origin/scripts/verify-project-code-next.sql
Cursor Agent 5147027716
fix: evitar código PRY duplicado al crear proyecto (RLS vs UNIQUE global)
fn_next_project_code contaba solo proyectos visibles del tenant; projects.code
es UNIQUE en toda la base. El siguiente PRY-0001 chocaba con otra obra/seed.
Secuencia global con SECURITY DEFINER y lock al crear.

Co-authored-by: alberto.martinez <alberto.martinez@mrdev.mx>
2026-09-16 20:55:11 +00:00

44 lines
2.2 KiB
PL/PgSQL

-- Simula RLS: tenant B no ve PRY-0001 de tenant A pero el código debe ser global.
DROP SCHEMA IF EXISTS core CASCADE;
CREATE SCHEMA core;
SET search_path = core;
CREATE FUNCTION 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 'empty'; END IF; END; $$;
CREATE FUNCTION rpc_ok(p_data jsonb, p_message text, p_ctx jsonb DEFAULT '{}'::jsonb) RETURNS jsonb
LANGUAGE plpgsql IMMUTABLE AS $$
BEGIN PERFORM rpc_assert_message(p_message);
RETURN jsonb_build_object('ok', true, 'data', p_data); END; $$;
CREATE FUNCTION rpc_created(p_data jsonb, p_message text, p_ctx jsonb DEFAULT '{}'::jsonb) RETURNS jsonb
LANGUAGE plpgsql IMMUTABLE AS $$
BEGIN PERFORM rpc_assert_message(p_message);
RETURN jsonb_build_object('ok', true, 'data', p_data); END; $$;
CREATE FUNCTION 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 rpc_assert_message(p_message);
RETURN jsonb_build_object('ok', false, 'message', p_message); END; $$;
CREATE FUNCTION rpc_from_exception(p_fn text, p_sqlstate text, p_message text, p_detail text DEFAULT NULL) RETURNS jsonb
LANGUAGE plpgsql IMMUTABLE AS $$
BEGIN RETURN rpc_err('INTERNAL', format('%s %s', p_fn, p_message), '{}'::jsonb); END; $$;
CREATE TABLE companies (id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY, name text);
CREATE TABLE projects (
id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
code text NOT NULL UNIQUE,
name text NOT NULL,
address text DEFAULT '',
theme_id text NOT NULL DEFAULT 'arctec-dos-logos-fold',
status text DEFAULT 'activo',
company_id bigint,
tenant_id int
);
ALTER TABLE projects ENABLE ROW LEVEL SECURITY;
CREATE POLICY tenant_isolation ON projects
USING (tenant_id = NULLIF(current_setting('app.tenant_id', true), '')::integer)
WITH CHECK (tenant_id = NULLIF(current_setting('app.tenant_id', true), '')::integer);
INSERT INTO companies (name) VALUES ('Co A'), ('Co B');
-- PRY-0001 ya existe (otro tenant / seed)
INSERT INTO projects (code, name, theme_id, company_id, tenant_id)
VALUES ('PRY-0001', 'Obra seed', 'arctec-dos-logos-fold', 1, 1);