Skip to content

feat(indústria): tornar apontamento parcial idempotente e rastreável - #416

Merged
devmnsoft merged 1 commit into
mainfrom
codex/concluir-fluxo-industrial-e-corrigir-formularios
Sep 15, 2026
Merged

devmnsoft merged 1 commit into
mainfrom
codex/concluir-fluxo-industrial-e-corrigir-formularios

Conversation

@devmnsoft

Copy link
Copy Markdown
Owner

Motivation

  • Garantir confirmação de apontamentos concorrentes de chão de fábrica sem duplicação nem excesso sobre a quantidade planejada.
  • Evitar dupla contabilização entre apontamento (registro de produção) e entrada física do produto acabado, e possibilitar rastreabilidade entre apontamento, consumo, entrada e inspeção.
  • Fornecer migração forward-only idempotente que preserve histórico e permita auditoria e validação por probes do manifesto.

Description

  • API: IndustriaController.ApontarAsync foi reforçada para exigir Idempotency-Key, calcular um SHA-256 do payload, checar/reusar a entrada idempotente ou rejeitar chave reutilizada com conteúdo diferente, bloquear a OP com FOR UPDATE, validar estado e limitar produção acumulada dentro do planejado, e executar inserção do apontamento + atualização da OP + auditoria em uma única transação.
  • Migration e schema: adicionada migration database/postgres/migrations/20260915120000_industria_fluxo_operacional.sql que introduz colunas de versionamento e controle (version, quantidade_aprovada, quantidade_rejeitada), campos de idempotência e rastreabilidade em apontamento/consumo/produção/inspeção, índices únicos de idempotência e constraints de integridade de quantidades.
  • Consolidação e testes estáticos: manifesto e todos os script_completo* foram atualizados com a nova migration, e os testes existentes foram ampliados para verificar contratos estáticos relativos à idempotência, for update, prevenção de dupla contabilização e presença da migration.

Testing

  • Executados checks estáticos e de integridade: python3 -m json.tool database/postgres/migrations/manifest.json passed and migration checksum verified against the manifest, ./scripts/check-api-route-conflicts.sh found no route conflicts, bash -n scripts/*.sh scripts/ci/*.sh succeeded, and git diff --check and conflict-marker scan returned clean results.
  • Migration and script synchronization succeeded by programmatic verification that the new migration was appended to manifest.json and to consolidated scripts (database/postgres/script_completo.sql, script_completop.sql, etc.), and index/partial-index checks ran producing non-blocking warnings (repository-wide historic warnings) but no blocking failures.
  • Blocked runtime gates: dotnet restore / dotnet build / unit/integration tests and Razor/Swagger validation were not executed because the environment lacks the .NET SDK, psql/PostgreSQL and pwsh (these checks are marked blocked and must be run in CI or a local runtime with PostgreSQL 16 and .NET 10).

Codex Task

@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Sep 15, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-09-15T22:24:02.212394Z e912021 PR opened
🔒 Security Review ✅ Completed 2026-09-15T22:26:23.566480Z e912021 PR opened
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@devmnsoft
devmnsoft merged commit 0ab62eb into main Sep 15, 2026
4 of 11 checks passed

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: e912021a66

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

if (string.IsNullOrWhiteSpace(key) || key.Length > 160) return BadRequest(ApiResponse<object>.Fail("Idempotency-Key válida é obrigatória.", cid));
var payloadHash = Convert.ToHexString(SHA256.HashData(Encoding.UTF8.GetBytes(JsonSerializer.Serialize(r)))).ToLowerInvariant();
var tenantId = RequireTenant(); using var c = _context.CreateConnection(); c.Open(); using var tx = c.BeginTransaction(System.Data.IsolationLevel.ReadCommitted);
var repetido = await c.QuerySingleOrDefaultAsync<(long Id, string PayloadHash)?>("select id, payload_hash as PayloadHash from sigov.industria_apontamento where tenant_id=@TenantId and idempotency_key=@Key for update", new { TenantId = tenantId, Key = key }, tx);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Claim the idempotency key atomically

When two requests with the same tenant and key arrive before either insert commits, both queries can observe no row because FOR UPDATE cannot lock a nonexistent record. After they serialize on the production-order lock, the second request reaches the insert and violates ux_industria_apontamento_idempotencia, which the generic catch converts to HTTP 500 instead of an idempotent replay or payload conflict. Claim the key atomically, such as with an upsert or a key-scoped advisory lock, before deciding whether this is a replay.

Useful? React with 👍 / 👎.

if (r.QuantidadeBoas < 0 || r.QuantidadeRefugo < 0 || (r.Tipo == "PRODUCAO" && r.QuantidadeBoas + r.QuantidadeRefugo <= 0)) return BadRequest(ApiResponse<object>.Fail("A produção informada deve ser positiva.", cid));
var key = Request.Headers["Idempotency-Key"].ToString().Trim();
if (string.IsNullOrWhiteSpace(key) || key.Length > 160) return BadRequest(ApiResponse<object>.Fail("Idempotency-Key válida é obrigatória.", cid));
var payloadHash = Convert.ToHexString(SHA256.HashData(Encoding.UTF8.GetBytes(JsonSerializer.Serialize(r)))).ToLowerInvariant();

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Include the target order in replay equivalence

The payload hash serializes only the body and omits the route's production-order id. If a key previously used for order A is sent to order B with the same body, the tenant-wide lookup treats it as an identical replay and returns A's appointment ID with HTTP 200 while making no change to B. Include the order ID in the hash or fetch and compare the stored ordem_id, returning a conflict when the key targets another order.

Useful? React with 👍 / 👎.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant