Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
-- Fluxo industrial: concorrência, idempotência e cadeia de custódia do apontamento.
-- Forward-only e idempotente; preserva os registros industriais publicados.
alter table if exists sigov.industria_ordem_producao
add column if not exists version bigint not null default 1,
add column if not exists quantidade_aprovada numeric(14,4) not null default 0,
add column if not exists quantidade_rejeitada numeric(14,4) not null default 0;

alter table if exists sigov.industria_apontamento
add column if not exists idempotency_key varchar(160),
add column if not exists payload_hash varchar(64),
add column if not exists unidade varchar(20),
add column if not exists almoxarifado_id bigint,
add column if not exists lote_produzido varchar(80),
add column if not exists confirmado_at timestamptz;

alter table if exists sigov.industria_consumo_material
add column if not exists apontamento_id bigint references sigov.industria_apontamento(id),
add column if not exists lote varchar(80),
add column if not exists idempotency_key varchar(160);

alter table if exists sigov.industria_producao_acabada
add column if not exists apontamento_id bigint references sigov.industria_apontamento(id),
add column if not exists bloqueado_qualidade boolean not null default false,
add column if not exists idempotency_key varchar(160);

alter table if exists sigov.industria_inspecao_qualidade
add column if not exists apontamento_id bigint references sigov.industria_apontamento(id),
add column if not exists quantidade_inspecionada numeric(14,4),
add column if not exists quantidade_aprovada numeric(14,4),
add column if not exists quantidade_rejeitada numeric(14,4);

create unique index if not exists ux_industria_apontamento_idempotencia
on sigov.industria_apontamento(tenant_id, idempotency_key)
where idempotency_key is not null;
create unique index if not exists ux_industria_consumo_idempotencia
on sigov.industria_consumo_material(tenant_id, idempotency_key)
where idempotency_key is not null;
create unique index if not exists ux_industria_producao_idempotencia
on sigov.industria_producao_acabada(tenant_id, idempotency_key)
where idempotency_key is not null;
create index if not exists ix_industria_consumo_apontamento
on sigov.industria_consumo_material(tenant_id, apontamento_id);
create index if not exists ix_industria_producao_apontamento
on sigov.industria_producao_acabada(tenant_id, apontamento_id);
create index if not exists ix_industria_inspecao_apontamento
on sigov.industria_inspecao_qualidade(tenant_id, apontamento_id);

do $$ begin
if not exists (select 1 from pg_constraint where conname='ck_industria_ordem_quantidades_qualidade') then
alter table sigov.industria_ordem_producao add constraint ck_industria_ordem_quantidades_qualidade
check (quantidade_aprovada >= 0 and quantidade_rejeitada >= 0
and quantidade_aprovada + quantidade_rejeitada <= quantidade_produzida);
end if;
if not exists (select 1 from pg_constraint where conname='ck_industria_inspecao_quantidades') then
alter table sigov.industria_inspecao_qualidade add constraint ck_industria_inspecao_quantidades
check (quantidade_inspecionada is null or
(quantidade_inspecionada > 0 and coalesce(quantidade_aprovada,0) >= 0
and coalesce(quantidade_rejeitada,0) >= 0
and coalesce(quantidade_aprovada,0) + coalesce(quantidade_rejeitada,0) <= quantidade_inspecionada));
end if;
end $$;
13 changes: 13 additions & 0 deletions database/postgres/migrations/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -2384,6 +2384,19 @@
"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"
}
]
},
{
"version": "20260915120000",
"description": "Fluxo industrial transacional, idempotente e rastreável",
"category": "schema",
"file": "20260915120000_industria_fluxo_operacional.sql",
"checksum": "dfc5b79bb8ea359e14bf0f3eb3733481ec1784db0ceddf5c08ba6cb2f5069713",
"applyAutomatically": true,
"includeInBaseline": true,
"dependencies": [
"20260914130000"
],
"postConditionSql": "select exists(select 1 from information_schema.columns where table_schema='sigov' and table_name='industria_apontamento' and column_name='idempotency_key') and exists(select 1 from pg_indexes where schemaname='sigov' and indexname='ux_industria_apontamento_idempotencia')"
}
],
"compatibilityAfterAll": [
Expand Down
64 changes: 64 additions & 0 deletions database/postgres/script_completo.sql
Original file line number Diff line number Diff line change
Expand Up @@ -31103,3 +31103,67 @@ create unique index if not exists ux_bootstrap_grupo_nome_tenant
-- EXCLUDED_FROM_BASELINE: 20260902000000_rc50_98_ged_workflow_branding_logo.sql [schema]
-- EXCLUDED_FROM_BASELINE: 20260902010000_corr_compras_checksum_schema.sql [schema]
-- EXCLUDED_FROM_BASELINE: 20260903130000_corr_licitapro_postconditions_schema.sql [corrective]


-- MIGRATION: 20260915120000_industria_fluxo_operacional.sql
-- Fluxo industrial: concorrência, idempotência e cadeia de custódia do apontamento.
-- Forward-only e idempotente; preserva os registros industriais publicados.
alter table if exists sigov.industria_ordem_producao
add column if not exists version bigint not null default 1,
add column if not exists quantidade_aprovada numeric(14,4) not null default 0,
add column if not exists quantidade_rejeitada numeric(14,4) not null default 0;

alter table if exists sigov.industria_apontamento
add column if not exists idempotency_key varchar(160),
add column if not exists payload_hash varchar(64),
add column if not exists unidade varchar(20),
add column if not exists almoxarifado_id bigint,
add column if not exists lote_produzido varchar(80),
add column if not exists confirmado_at timestamptz;

alter table if exists sigov.industria_consumo_material
add column if not exists apontamento_id bigint references sigov.industria_apontamento(id),
add column if not exists lote varchar(80),
add column if not exists idempotency_key varchar(160);

alter table if exists sigov.industria_producao_acabada
add column if not exists apontamento_id bigint references sigov.industria_apontamento(id),
add column if not exists bloqueado_qualidade boolean not null default false,
add column if not exists idempotency_key varchar(160);

alter table if exists sigov.industria_inspecao_qualidade
add column if not exists apontamento_id bigint references sigov.industria_apontamento(id),
add column if not exists quantidade_inspecionada numeric(14,4),
add column if not exists quantidade_aprovada numeric(14,4),
add column if not exists quantidade_rejeitada numeric(14,4);

create unique index if not exists ux_industria_apontamento_idempotencia
on sigov.industria_apontamento(tenant_id, idempotency_key)
where idempotency_key is not null;
create unique index if not exists ux_industria_consumo_idempotencia
on sigov.industria_consumo_material(tenant_id, idempotency_key)
where idempotency_key is not null;
create unique index if not exists ux_industria_producao_idempotencia
on sigov.industria_producao_acabada(tenant_id, idempotency_key)
where idempotency_key is not null;
create index if not exists ix_industria_consumo_apontamento
on sigov.industria_consumo_material(tenant_id, apontamento_id);
create index if not exists ix_industria_producao_apontamento
on sigov.industria_producao_acabada(tenant_id, apontamento_id);
create index if not exists ix_industria_inspecao_apontamento
on sigov.industria_inspecao_qualidade(tenant_id, apontamento_id);

do $$ begin
if not exists (select 1 from pg_constraint where conname='ck_industria_ordem_quantidades_qualidade') then
alter table sigov.industria_ordem_producao add constraint ck_industria_ordem_quantidades_qualidade
check (quantidade_aprovada >= 0 and quantidade_rejeitada >= 0
and quantidade_aprovada + quantidade_rejeitada <= quantidade_produzida);
end if;
if not exists (select 1 from pg_constraint where conname='ck_industria_inspecao_quantidades') then
alter table sigov.industria_inspecao_qualidade add constraint ck_industria_inspecao_quantidades
check (quantidade_inspecionada is null or
(quantidade_inspecionada > 0 and coalesce(quantidade_aprovada,0) >= 0
and coalesce(quantidade_rejeitada,0) >= 0
and coalesce(quantidade_aprovada,0) + coalesce(quantidade_rejeitada,0) <= quantidade_inspecionada));
end if;
end $$;
64 changes: 64 additions & 0 deletions database/postgres/script_completo_dev.sql
Original file line number Diff line number Diff line change
Expand Up @@ -31281,3 +31281,67 @@ begin
end loop;
end loop;
end $$;


-- MIGRATION: 20260915120000_industria_fluxo_operacional.sql
-- Fluxo industrial: concorrência, idempotência e cadeia de custódia do apontamento.
-- Forward-only e idempotente; preserva os registros industriais publicados.
alter table if exists sigov.industria_ordem_producao
add column if not exists version bigint not null default 1,
add column if not exists quantidade_aprovada numeric(14,4) not null default 0,
add column if not exists quantidade_rejeitada numeric(14,4) not null default 0;

alter table if exists sigov.industria_apontamento
add column if not exists idempotency_key varchar(160),
add column if not exists payload_hash varchar(64),
add column if not exists unidade varchar(20),
add column if not exists almoxarifado_id bigint,
add column if not exists lote_produzido varchar(80),
add column if not exists confirmado_at timestamptz;

alter table if exists sigov.industria_consumo_material
add column if not exists apontamento_id bigint references sigov.industria_apontamento(id),
add column if not exists lote varchar(80),
add column if not exists idempotency_key varchar(160);

alter table if exists sigov.industria_producao_acabada
add column if not exists apontamento_id bigint references sigov.industria_apontamento(id),
add column if not exists bloqueado_qualidade boolean not null default false,
add column if not exists idempotency_key varchar(160);

alter table if exists sigov.industria_inspecao_qualidade
add column if not exists apontamento_id bigint references sigov.industria_apontamento(id),
add column if not exists quantidade_inspecionada numeric(14,4),
add column if not exists quantidade_aprovada numeric(14,4),
add column if not exists quantidade_rejeitada numeric(14,4);

create unique index if not exists ux_industria_apontamento_idempotencia
on sigov.industria_apontamento(tenant_id, idempotency_key)
where idempotency_key is not null;
create unique index if not exists ux_industria_consumo_idempotencia
on sigov.industria_consumo_material(tenant_id, idempotency_key)
where idempotency_key is not null;
create unique index if not exists ux_industria_producao_idempotencia
on sigov.industria_producao_acabada(tenant_id, idempotency_key)
where idempotency_key is not null;
create index if not exists ix_industria_consumo_apontamento
on sigov.industria_consumo_material(tenant_id, apontamento_id);
create index if not exists ix_industria_producao_apontamento
on sigov.industria_producao_acabada(tenant_id, apontamento_id);
create index if not exists ix_industria_inspecao_apontamento
on sigov.industria_inspecao_qualidade(tenant_id, apontamento_id);

do $$ begin
if not exists (select 1 from pg_constraint where conname='ck_industria_ordem_quantidades_qualidade') then
alter table sigov.industria_ordem_producao add constraint ck_industria_ordem_quantidades_qualidade
check (quantidade_aprovada >= 0 and quantidade_rejeitada >= 0
and quantidade_aprovada + quantidade_rejeitada <= quantidade_produzida);
end if;
if not exists (select 1 from pg_constraint where conname='ck_industria_inspecao_quantidades') then
alter table sigov.industria_inspecao_qualidade add constraint ck_industria_inspecao_quantidades
check (quantidade_inspecionada is null or
(quantidade_inspecionada > 0 and coalesce(quantidade_aprovada,0) >= 0
and coalesce(quantidade_rejeitada,0) >= 0
and coalesce(quantidade_aprovada,0) + coalesce(quantidade_rejeitada,0) <= quantidade_inspecionada));
end if;
end $$;
64 changes: 64 additions & 0 deletions database/script_completo.sql
Original file line number Diff line number Diff line change
Expand Up @@ -31103,3 +31103,67 @@ create unique index if not exists ux_bootstrap_grupo_nome_tenant
-- EXCLUDED_FROM_BASELINE: 20260902000000_rc50_98_ged_workflow_branding_logo.sql [schema]
-- EXCLUDED_FROM_BASELINE: 20260902010000_corr_compras_checksum_schema.sql [schema]
-- EXCLUDED_FROM_BASELINE: 20260903130000_corr_licitapro_postconditions_schema.sql [corrective]


-- MIGRATION: 20260915120000_industria_fluxo_operacional.sql
-- Fluxo industrial: concorrência, idempotência e cadeia de custódia do apontamento.
-- Forward-only e idempotente; preserva os registros industriais publicados.
alter table if exists sigov.industria_ordem_producao
add column if not exists version bigint not null default 1,
add column if not exists quantidade_aprovada numeric(14,4) not null default 0,
add column if not exists quantidade_rejeitada numeric(14,4) not null default 0;

alter table if exists sigov.industria_apontamento
add column if not exists idempotency_key varchar(160),
add column if not exists payload_hash varchar(64),
add column if not exists unidade varchar(20),
add column if not exists almoxarifado_id bigint,
add column if not exists lote_produzido varchar(80),
add column if not exists confirmado_at timestamptz;

alter table if exists sigov.industria_consumo_material
add column if not exists apontamento_id bigint references sigov.industria_apontamento(id),
add column if not exists lote varchar(80),
add column if not exists idempotency_key varchar(160);

alter table if exists sigov.industria_producao_acabada
add column if not exists apontamento_id bigint references sigov.industria_apontamento(id),
add column if not exists bloqueado_qualidade boolean not null default false,
add column if not exists idempotency_key varchar(160);

alter table if exists sigov.industria_inspecao_qualidade
add column if not exists apontamento_id bigint references sigov.industria_apontamento(id),
add column if not exists quantidade_inspecionada numeric(14,4),
add column if not exists quantidade_aprovada numeric(14,4),
add column if not exists quantidade_rejeitada numeric(14,4);

create unique index if not exists ux_industria_apontamento_idempotencia
on sigov.industria_apontamento(tenant_id, idempotency_key)
where idempotency_key is not null;
create unique index if not exists ux_industria_consumo_idempotencia
on sigov.industria_consumo_material(tenant_id, idempotency_key)
where idempotency_key is not null;
create unique index if not exists ux_industria_producao_idempotencia
on sigov.industria_producao_acabada(tenant_id, idempotency_key)
where idempotency_key is not null;
create index if not exists ix_industria_consumo_apontamento
on sigov.industria_consumo_material(tenant_id, apontamento_id);
create index if not exists ix_industria_producao_apontamento
on sigov.industria_producao_acabada(tenant_id, apontamento_id);
create index if not exists ix_industria_inspecao_apontamento
on sigov.industria_inspecao_qualidade(tenant_id, apontamento_id);

do $$ begin
if not exists (select 1 from pg_constraint where conname='ck_industria_ordem_quantidades_qualidade') then
alter table sigov.industria_ordem_producao add constraint ck_industria_ordem_quantidades_qualidade
check (quantidade_aprovada >= 0 and quantidade_rejeitada >= 0
and quantidade_aprovada + quantidade_rejeitada <= quantidade_produzida);
end if;
if not exists (select 1 from pg_constraint where conname='ck_industria_inspecao_quantidades') then
alter table sigov.industria_inspecao_qualidade add constraint ck_industria_inspecao_quantidades
check (quantidade_inspecionada is null or
(quantidade_inspecionada > 0 and coalesce(quantidade_aprovada,0) >= 0
and coalesce(quantidade_rejeitada,0) >= 0
and coalesce(quantidade_aprovada,0) + coalesce(quantidade_rejeitada,0) <= quantidade_inspecionada));
end if;
end $$;
32 changes: 32 additions & 0 deletions docs/execucao/rc51_02_industria_fluxo_operacional.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
# RC51.02 — fluxo industrial operacional

Corte: 2026-09-15. Estado: **implementado sem validação runtime**; não homologado porque o checkout não dispõe de .NET, PostgreSQL, PowerShell nem navegador.

## Retomada e dependências

Branch `work`, HEAD inicial `0846c3df2d93198fbd9c8321f405d1d27bb844e2`, sem remoto ou upstream e árvore inicialmente limpa. O contrato normativo é .NET SDK 10.0.100/C# 14, MVC/Razor, API REST, PostgreSQL 16+ e Dapper.

| Dependência | Classificação | Evidência e limite |
|---|---|---|
| Ordens e estados | parcial | OP, histórico e permissões persistidos; transições antigas ainda precisam de prova concorrente no PostgreSQL |
| Ficha técnica/versionamento | parcial | a OP referencia a ficha e seus itens; falta fotografia imutável formal da composição liberada |
| Reserva/consumo | parcial | saldo comercial canônico usa lock/transação; reserva ainda não é individualizada por OP |
| Apontamento | implementada sem validação | confirmação agora exige idempotência, bloqueia a OP e limita o acumulado |
| Lotes/estoque | parcial | lote acabado e vínculos persistidos; saldo canônico não discrimina lote/almoxarifado |
| Qualidade/não conformidade | parcial | inspeção canônica preservada e quantidades distintas adicionadas; liberação do disponível ainda pendente |
| Custeio | parcial | custo cadastral ausente falha; cálculo histórico existente não foi promovido |
| SaaS/autorização | implementada sem validação | módulo e avaliador canônicos permanecem nas rotas; suspensão requer prova runtime |
| Auditoria | implementada sem validação | evento e correlation id permanecem na mesma transação do apontamento |
| Interface | parcial | template responsivo existente; jornada de edição dedicada e ensaio visual permanecem bloqueados |

## Regras e transições preservadas

- `PLANEJADA → LIBERADA → EM_PRODUCAO ↔ PAUSADA → CONCLUIDA`; cancelamento é terminal. Liberação requer perfil com `industria.ordens.liberar` e ficha quando o produto assim exige.
- Apontamento confirmado exige `industria.apontamentos.criar`, OP liberada/em produção/pausada, quantidade positiva para `PRODUCAO`, chave idempotente e acumulado dentro do planejado. A mesma chave e conteúdo retorna o registro; conteúdo diferente resulta em conflito. O lock da OP serializa confirmações concorrentes.
- `quantidade_planejada`, `quantidade_produzida`, `quantidade_aprovada`, `quantidade_rejeitada` e o saldo pendente de inspeção são conceitos separados. Entrada física não soma novamente a produção apontada.
- Consumo continua explícito: nenhuma fórmula proporcional foi inventada. Material reservado e consumido permanecem conceitos distintos.
- Conclusão produtiva não cria pagamento nem equivale a encerramento financeiro. Inspeção obrigatória pendente bloqueia conclusão.

## Riscos e próximo item

A nova cadeia de custódia permite vincular consumo, entrada e inspeção ao apontamento, mas as rotas legadas ainda não exigem esses vínculos. O próximo item exato é mover **consumo + entrada acabada + criação da inspeção** para uma única operação transacional confirmada por apontamento, individualizando reserva e lote no estoque canônico, seguida pelos cenários PostgreSQL 16 de dois tenants, retry, concorrência e rollback intermediário. GED permanece por último.
Loading
Loading