Repository navigation
Corrige trigger de compatibilidade e preserva suspensão SaaS #406
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,63 @@ | ||
| -- Correção forward-only da compatibilidade entre a contratação canônica e a projeção legada. | ||
| -- tenant_modulo_contratado é a autoridade; nenhum estado legado concede acesso ao contrato. | ||
| create or replace function sigov.fn_tenant_modulo_compatibilizar() returns trigger | ||
| language plpgsql | ||
| security invoker | ||
| set search_path = pg_catalog, sigov | ||
| as $$ | ||
| declare | ||
| v_modulo_id bigint; | ||
| v_habilitado boolean; | ||
| begin | ||
| select ms.id | ||
| into v_modulo_id | ||
| from sigov.modulo_saas ms | ||
| where ms.codigo = new.modulo_codigo | ||
| and ms.ativo | ||
| and not ms.is_deleted | ||
| limit 1; | ||
|
|
||
| if v_modulo_id is null then | ||
| return new; | ||
| end if; | ||
|
|
||
| -- A projeção só fica habilitada quando contrato, vigência e registro permitem. | ||
| -- SUSPENSO, INADIMPLENTE, CANCELADO e EXPIRADO continuam contratualmente | ||
| -- distinguíveis, mas nunca são convertidos em acesso habilitado. | ||
| v_habilitado := new.ativo | ||
| and new.status in ('TRIAL', 'EM_IMPLANTACAO', 'CONTRATADO', 'HABILITADO', 'ATIVO', 'BETA') | ||
| and (new.vigencia_inicio is null or new.vigencia_inicio <= current_date) | ||
| and (new.vigencia_fim is null or new.vigencia_fim >= current_date) | ||
| and (new.cancelamento_agendado_para is null or new.cancelamento_agendado_para > current_date); | ||
|
|
||
| insert into sigov.tenant_modulo ( | ||
| tenant_id, modulo_saas_id, habilitado, contratado, inicio_at, fim_at, | ||
| configuracoes, ativo, created_by, updated_by, correlation_id) | ||
| values ( | ||
| new.tenant_id, v_modulo_id, v_habilitado, | ||
| new.ativo and new.status not in ('DISPONIVEL', 'CANCELADO', 'EXPIRADO'), | ||
| coalesce(new.vigencia_inicio, current_date)::timestamptz, | ||
| new.vigencia_fim::timestamptz, new.parametros_json, new.ativo, | ||
| new.created_by, new.updated_by, new.correlation_id) | ||
| on conflict (tenant_id, modulo_saas_id) do update set | ||
| habilitado = excluded.habilitado, | ||
| contratado = excluded.contratado, | ||
| inicio_at = excluded.inicio_at, | ||
| fim_at = excluded.fim_at, | ||
| configuracoes = excluded.configuracoes, | ||
| ativo = excluded.ativo, | ||
| updated_at = now(), | ||
| updated_by = excluded.updated_by, | ||
| correlation_id = excluded.correlation_id; | ||
|
|
||
| return new; | ||
| end | ||
| $$; | ||
|
|
||
| -- Recriar corrige de forma determinística os cenários ausente, desabilitado, | ||
| -- associado a função divergente ou instalado com eventos/momento incorretos. | ||
| drop trigger if exists trg_tenant_modulo_compatibilizar on sigov.tenant_modulo_contratado; | ||
| create trigger trg_tenant_modulo_compatibilizar | ||
| after insert or update on sigov.tenant_modulo_contratado | ||
| for each row | ||
| execute function sigov.fn_tenant_modulo_compatibilizar(); | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -2313,8 +2313,24 @@ | |
| "sql": "select case when not exists(select 1 from pg_trigger t join pg_proc p on p.oid=t.tgfoid join pg_namespace n on n.oid=p.pronamespace where t.tgrelid=to_regclass('sigov.tenant_modulo_contratado') and t.tgname='trg_tenant_modulo_contrato_auditar' and not t.tgisinternal and t.tgenabled<>'D' and n.nspname='sigov' and p.proname='fn_tenant_modulo_contrato_auditar' and pg_get_functiondef(p.oid) like '%coalesce(new.usuario_responsavel_id,new.updated_by,new.created_by)%') then 'objeto=trg_tenant_modulo_contrato_auditar esperado=habilitado com função corrigida obtido=ausente, desabilitado ou divergente' end" | ||
| }, | ||
| { | ||
| "name": "trigger SaaS de compatibilidade corrigido", | ||
| "sql": "select case when not exists(select 1 from pg_trigger t join pg_proc p on p.oid=t.tgfoid join pg_namespace n on n.oid=p.pronamespace where t.tgrelid=to_regclass('sigov.tenant_modulo_contratado') and t.tgname='trg_tenant_modulo_compatibilizar' and not t.tgisinternal and t.tgenabled<>'D' and n.nspname='sigov' and p.proname='fn_tenant_modulo_compatibilizar' and pg_get_functiondef(p.oid) like '%case when new.ativo and new.status%') then 'objeto=trg_tenant_modulo_compatibilizar esperado=habilitado com suspensão preservada obtido=ausente, desabilitado ou divergente' end" | ||
| "name": "trigger SaaS existe na tabela canônica", | ||
| "sql": "select case when not exists(select 1 from pg_trigger t where t.tgrelid=to_regclass('sigov.tenant_modulo_contratado') and t.tgname='trg_tenant_modulo_compatibilizar' and not t.tgisinternal) then 'objeto=trg_tenant_modulo_compatibilizar esperado=existente em sigov.tenant_modulo_contratado obtido=ausente ou em outra tabela' end" | ||
| }, | ||
| { | ||
| "name": "trigger SaaS habilitado para execução normal", | ||
| "sql": "select case when not exists(select 1 from pg_trigger t where t.tgrelid=to_regclass('sigov.tenant_modulo_contratado') and t.tgname='trg_tenant_modulo_compatibilizar' and not t.tgisinternal and t.tgenabled in ('O','A')) then 'objeto=trg_tenant_modulo_compatibilizar esperado=habilitado para execução normal obtido=desabilitado ou restrito a réplica' end" | ||
| }, | ||
| { | ||
| "name": "trigger SaaS eventos momento e granularidade", | ||
| "sql": "select case when not exists(select 1 from pg_trigger t where t.tgrelid=to_regclass('sigov.tenant_modulo_contratado') and t.tgname='trg_tenant_modulo_compatibilizar' and not t.tgisinternal and (t.tgtype & 1)=1 and (t.tgtype & 2)=0 and (t.tgtype & 4)=4 and (t.tgtype & 16)=16 and (t.tgtype & 8)=0 and (t.tgtype & 32)=0 and t.tgqual is null) then 'objeto=trg_tenant_modulo_compatibilizar esperado=AFTER INSERT OR UPDATE FOR EACH ROW sem WHEN obtido=eventos, momento, granularidade ou condição divergente' end" | ||
| }, | ||
| { | ||
| "name": "trigger SaaS função canônica", | ||
| "sql": "select case when not exists(select 1 from pg_trigger t join pg_proc p on p.oid=t.tgfoid join pg_namespace n on n.oid=p.pronamespace where t.tgrelid=to_regclass('sigov.tenant_modulo_contratado') and t.tgname='trg_tenant_modulo_compatibilizar' and not t.tgisinternal and n.nspname='sigov' and p.proname='fn_tenant_modulo_compatibilizar' and p.pronargs=0 and p.prorettype='trigger'::regtype) then 'objeto=trg_tenant_modulo_compatibilizar esperado=sigov.fn_tenant_modulo_compatibilizar() returns trigger obtido=função, schema ou assinatura divergente' end" | ||
| }, | ||
| { | ||
| "name": "função SaaS contrato estrutural de projeção", | ||
| "sql": "select case when not exists(select 1 from pg_proc p join pg_namespace n on n.oid=p.pronamespace where n.nspname='sigov' and p.proname='fn_tenant_modulo_compatibilizar' and p.pronargs=0 and p.prorettype='trigger'::regtype and p.prosecdef=false and array_to_string(p.proconfig,',') like '%search_path=pg_catalog, sigov%') then 'objeto=sigov.fn_tenant_modulo_compatibilizar esperado=security invoker com search_path fixo obtido=privilégio ou resolução de schema divergente' end" | ||
|
Comment on lines
+2332
to
+2333
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
On a fresh database, AGENTS.md reference: AGENTS.md:L11-L11 Useful? React with 👍 / 👎. |
||
| } | ||
| ] | ||
| }, | ||
|
|
@@ -2333,6 +2349,41 @@ | |
| "20260901090000" | ||
| ], | ||
| "postConditionSql": "select exists(select 1 from information_schema.columns where table_schema='sigov' and table_name='manutencao_ordem_servico' and column_name='origem_tipo') and exists(select 1 from pg_index where indexrelid=to_regclass('sigov.ux_manutencao_os_origem') and indisvalid)" | ||
| }, | ||
| { | ||
| "version": "20260914130000", | ||
| "description": "Correção da projeção SaaS com suspensão e vigência preservadas", | ||
| "category": "correction", | ||
| "file": "20260914130000_corr_saas_compatibilidade_suspensao.sql", | ||
| "checksum": "1b8b48920a5654b89b36aa167d294d8942e7f4c2f1c4774150419c9419f71a13", | ||
| "applyAutomatically": true, | ||
| "includeInBaseline": true, | ||
| "dependencies": [ | ||
| "20260910120000" | ||
| ], | ||
| "postConditionSql": "select exists(select 1 from pg_trigger t join pg_proc p on p.oid=t.tgfoid join pg_namespace n on n.oid=p.pronamespace where t.tgrelid=to_regclass('sigov.tenant_modulo_contratado') and t.tgname='trg_tenant_modulo_compatibilizar' and not t.tgisinternal and t.tgenabled in ('O','A') and n.nspname='sigov' and p.proname='fn_tenant_modulo_compatibilizar')", | ||
| "postConditionProbes": [ | ||
| { | ||
| "name": "trigger SaaS existe na tabela canônica", | ||
| "sql": "select case when not exists(select 1 from pg_trigger t where t.tgrelid=to_regclass('sigov.tenant_modulo_contratado') and t.tgname='trg_tenant_modulo_compatibilizar' and not t.tgisinternal) then 'objeto=trg_tenant_modulo_compatibilizar esperado=existente em sigov.tenant_modulo_contratado obtido=ausente ou em outra tabela' end" | ||
| }, | ||
| { | ||
| "name": "trigger SaaS habilitado para execução normal", | ||
| "sql": "select case when not exists(select 1 from pg_trigger t where t.tgrelid=to_regclass('sigov.tenant_modulo_contratado') and t.tgname='trg_tenant_modulo_compatibilizar' and not t.tgisinternal and t.tgenabled in ('O','A')) then 'objeto=trg_tenant_modulo_compatibilizar esperado=habilitado para execução normal obtido=desabilitado ou restrito a réplica' end" | ||
| }, | ||
| { | ||
| "name": "trigger SaaS eventos momento e granularidade", | ||
| "sql": "select case when not exists(select 1 from pg_trigger t where t.tgrelid=to_regclass('sigov.tenant_modulo_contratado') and t.tgname='trg_tenant_modulo_compatibilizar' and not t.tgisinternal and (t.tgtype & 1)=1 and (t.tgtype & 2)=0 and (t.tgtype & 4)=4 and (t.tgtype & 16)=16 and (t.tgtype & 8)=0 and (t.tgtype & 32)=0 and t.tgqual is null) then 'objeto=trg_tenant_modulo_compatibilizar esperado=AFTER INSERT OR UPDATE FOR EACH ROW sem WHEN obtido=eventos, momento, granularidade ou condição divergente' end" | ||
| }, | ||
| { | ||
| "name": "trigger SaaS função canônica", | ||
| "sql": "select case when not exists(select 1 from pg_trigger t join pg_proc p on p.oid=t.tgfoid join pg_namespace n on n.oid=p.pronamespace where t.tgrelid=to_regclass('sigov.tenant_modulo_contratado') and t.tgname='trg_tenant_modulo_compatibilizar' and not t.tgisinternal and n.nspname='sigov' and p.proname='fn_tenant_modulo_compatibilizar' and p.pronargs=0 and p.prorettype='trigger'::regtype) then 'objeto=trg_tenant_modulo_compatibilizar esperado=sigov.fn_tenant_modulo_compatibilizar() returns trigger obtido=função, schema ou assinatura divergente' end" | ||
| }, | ||
| { | ||
| "name": "função SaaS contrato estrutural de projeção", | ||
| "sql": "select case when not exists(select 1 from pg_proc p join pg_namespace n on n.oid=p.pronamespace where n.nspname='sigov' and p.proname='fn_tenant_modulo_compatibilizar' and p.pronargs=0 and p.prorettype='trigger'::regtype and p.prosecdef=false and array_to_string(p.proconfig,',') like '%search_path=pg_catalog, sigov%') then 'objeto=sigov.fn_tenant_modulo_compatibilizar esperado=security invoker com search_path fixo obtido=privilégio ou resolução de schema divergente' end" | ||
| } | ||
| ] | ||
| } | ||
| ], | ||
| "compatibilityAfterAll": [ | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For the installations this migration explicitly repairs because the old trigger was missing, disabled, or divergent, replacing the trigger does not process existing canonical rows. A contract already marked
SUSPENSO/INADIMPLENTE, or with an elapsed scheduled cancellation, can therefore retaintenant_modulo.habilitado=true; legacy consumers such asTenantContextSwitchRepositoryandAgroModuleRepositorycontinue granting access until an unrelated update fires the new trigger. Reproject the existingtenant_modulo_contratadorows as part of this migration.AGENTS.md reference: AGENTS.md:L14-L14
Useful? React with 👍 / 👎.