From 1fee67f60cf1342c8deb32dc066611af379b7e7c Mon Sep 17 00:00:00 2001 From: Davi Aliaga Date: Thu, 17 Sep 2026 19:44:06 -0300 Subject: [PATCH 1/4] =?UTF-8?q?feat/index:=20adiciona=20unique=20index=20p?= =?UTF-8?q?ara=20convite=20de=20usu=C3=A1rio?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- sql/002_indices.sql | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/sql/002_indices.sql b/sql/002_indices.sql index df7d7e1..4b9d15e 100644 --- a/sql/002_indices.sql +++ b/sql/002_indices.sql @@ -290,4 +290,7 @@ CREATE UNIQUE INDEX IF NOT EXISTS idx_telefone_empresa_principal ON public.telefone_empresa (id_empresa) WHERE principal = TRUE; CREATE UNIQUE INDEX IF NOT EXISTS idx_email_empresa_principal -ON public.email_empresa (id_empresa) WHERE principal = TRUE; \ No newline at end of file +ON public.email_empresa (id_empresa) WHERE principal = TRUE; + +CREATE UNIQUE INDEX IF NOT EXISTS idx_convite_usuario_pendente +ON public.convite_usuario (id_usuario) WHERE status = 'PENDENTE'; \ No newline at end of file From 8cb3f2423249ce5d08d9a3893a7519623f138380 Mon Sep 17 00:00:00 2001 From: Davi Aliaga Date: Thu, 17 Sep 2026 19:44:22 -0300 Subject: [PATCH 2/4] =?UTF-8?q?feat/triggers:=20adiciona=20fun=C3=A7=C3=B5?= =?UTF-8?q?es=20e=20triggers=20para=20gerenciamento=20de=20convites?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- sql/003_functions_triggers_procedures.sql | 81 +++++++++++++++++++++++ 1 file changed, 81 insertions(+) diff --git a/sql/003_functions_triggers_procedures.sql b/sql/003_functions_triggers_procedures.sql index a17317a..e5af99b 100644 --- a/sql/003_functions_triggers_procedures.sql +++ b/sql/003_functions_triggers_procedures.sql @@ -512,6 +512,69 @@ BEGIN END; $$ LANGUAGE plpgsql; +-- 18 (impedir reutilização de convite usado / revogado / expirado) +CREATE OR REPLACE FUNCTION public.fn_bloquear_reuso_convite() +RETURNS TRIGGER AS $$ +BEGIN + IF OLD.status IN ('USADO', 'REVOGADO', 'EXPIRADO') AND NEW.status <> OLD.status THEN + RAISE EXCEPTION 'Convite já foi finalizado e não pode ter seu status alterado.'; + END IF; + + RETURN NEW; +END; +$$ LANGUAGE plpgsql; + +-- 19 (expirar convite automaticamente ao tentar usar) +CREATE OR REPLACE FUNCTION public.fn_validar_expiracao_convite() +RETURNS TRIGGER AS $$ +BEGIN + IF NEW.status = 'USADO' AND OLD.expira_em <= NOW() THEN + RAISE EXCEPTION 'Convite expirado não pode ser mais usado'; + END IF; + + RETURN NEW; +END; +$$ LANGUAGE plpgsql; + +-- 20 (garantir que se o convite expire, seja possível criar outro apenas relacionado ao usuário pendente) +CREATE OR REPLACE TRIGGER public.fn_preparar_criacao_convite() +RETURNS TRIGGER AS $$ +DECLARE + v_status_usuario VARCHAR(40); + v_firebase_uid VARCHAR(128); + +BEGIN + SELECT status, firebase_uid + INTO v_status_usuario, v_firebase_uid + FROM usuario_sistema + WHERE id = NEW.id_usuario; + + IF v_status_usuario <> 'PENDENTE' THEN + RAISE EXCEPTION 'Convite só pode ser criado para usuário pendente'; + END IF; + + IF v_firebase_uid IS NOT NULL THEN + RAISE EXCEPTION 'Convite não pode ser criado para usuário já ativado'; + END IF; + + UPDATE convite_usuario SET status = 'EXPIRADO' + WHERE id_usuario = NEW.id_usuario + AND status = 'PENDENTE' + AND expira_em <= NOW(); + + IF EXISTS ( + SELECT 1 FROM convite_usuario + SET status = 'EXPIRADO' + WHERE id_usuario = NEW.id_usuario + AND status = 'PENDENTE' + AND expirado_em > NOW(0) + ) THEN + RAISE EXCEPTION 'Já existe convite pendente válido para esse usuário' + END IF; + + RETURN NEW; +END; +$$ LANGUAGE plpgsql; -- ###################### -- TRIGGERS @@ -676,6 +739,24 @@ CREATE OR REPLACE TRIGGER tg_validar_exclusao_telefone_colaborador BEFORE DELETE ON public.telefone_colaborador FOR EACH ROW EXECUTE FUNCTION public.fn_validar_exclusao_colaborador(); +-- 19 (trigger que conecta a função fn_bloquear_reuso_convite() à convite_usuario) +CREATE OR REPLACE TRIGGER tg_bloquear_reuso_convite +BEFORE UPDATE ON public.convite_usuario +FOR EACH ROW +EXECUTE FUNCTION public.fn_bloquear_reuso_convite(); + +-- 20 (trigger que conecta a função fn_validar_expiracao_convite() à convite_usuario) +CREATE OR REPLACE TRIGGER tg_validar_expiracao_convite +BEFORE UPDATE ON public.convite_usuario +FOR EACH ROW +EXECUTE FUNCTION public.fn_validar_expiracao_convite(); + +-- 21 (trigger que conecta a função fn_preparar_criacao_convite à convite_usuario) +CREATE OR REPLACE TRIGGER tg_preparar_criacao_convite +BEFORE INSERT ON public.convite_usuario +FOR EACH ROW +EXECUTE FUNCTION public.fn_preparar_criacao_convite(); + -- ###################### -- PROCEDURES -- ###################### From 3b189403ed540322daa0c30ebd15d98b9499fc02 Mon Sep 17 00:00:00 2001 From: Davi Aliaga Date: Thu, 17 Sep 2026 19:51:29 -0300 Subject: [PATCH 3/4] hotfix/trigger: renomear trigger para function --- sql/003_functions_triggers_procedures.sql | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sql/003_functions_triggers_procedures.sql b/sql/003_functions_triggers_procedures.sql index e5af99b..25827ee 100644 --- a/sql/003_functions_triggers_procedures.sql +++ b/sql/003_functions_triggers_procedures.sql @@ -537,7 +537,7 @@ END; $$ LANGUAGE plpgsql; -- 20 (garantir que se o convite expire, seja possível criar outro apenas relacionado ao usuário pendente) -CREATE OR REPLACE TRIGGER public.fn_preparar_criacao_convite() +CREATE OR REPLACE FUNCTION public.fn_preparar_criacao_convite() RETURNS TRIGGER AS $$ DECLARE v_status_usuario VARCHAR(40); From ac5ff090663a1048a5b437019f610397a360abc5 Mon Sep 17 00:00:00 2001 From: Davi Aliaga Date: Thu, 17 Sep 2026 19:56:59 -0300 Subject: [PATCH 4/4] fix/trigger: atualiza SQL para especificar schema do convite_usuario --- sql/003_functions_triggers_procedures.sql | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/sql/003_functions_triggers_procedures.sql b/sql/003_functions_triggers_procedures.sql index 25827ee..758d2dd 100644 --- a/sql/003_functions_triggers_procedures.sql +++ b/sql/003_functions_triggers_procedures.sql @@ -557,19 +557,19 @@ BEGIN RAISE EXCEPTION 'Convite não pode ser criado para usuário já ativado'; END IF; - UPDATE convite_usuario SET status = 'EXPIRADO' - WHERE id_usuario = NEW.id_usuario - AND status = 'PENDENTE' + UPDATE pdca.convite_usuario SET status = 'EXPIRADO' + WHERE id_usuario = NEW.id_usuario + AND status = 'PENDENTE' AND expira_em <= NOW(); IF EXISTS ( - SELECT 1 FROM convite_usuario - SET status = 'EXPIRADO' + SELECT 1 + FROM convite_usuario WHERE id_usuario = NEW.id_usuario AND status = 'PENDENTE' - AND expirado_em > NOW(0) + AND expira_em > NOW() ) THEN - RAISE EXCEPTION 'Já existe convite pendente válido para esse usuário' + RAISE EXCEPTION 'Já existe convite pendente válido para este usuário'; END IF; RETURN NEW;