Programação Por Equipe DevBras

Capa do artigo: XML de compras públicas rejeitado: a versão do leiaute é parte do arquivo

XML de compras públicas rejeitado: a versão do leiaute é parte do arquivo

Guarde o XSD de cada versão do leiaute, escolha a versão pela data de vigência, valide antes de enviar e registre versão, hash e protocolo de cada envio.

O gerador de XML funcionou por dois anos. Numa segunda-feira, todos os envios de aviso de licitação voltaram com erro de esquema, e o sistema mostrou só "falha no envio". O arquivo continuava idêntico ao que passava na semana anterior. O que mudou foi o leiaute: o órgão publicou uma versão nova do manual, com um elemento que deixou de ser opcional. Integração com sistema de compras públicas não é "um XML". É um XML de uma versão específica, válido a partir de uma data.

A resposta curta: guarde no repositório o XSD de cada versão do leiaute, faça o código escolher a versão pela data de vigência, valide o arquivo contra o esquema antes de enviar e registre em cada envio a versão usada, o hash do arquivo, o protocolo e a mensagem de retorno. Quando o órgão rejeitar, mostre o motivo, não "falhou".

Por que o arquivo que passava agora é rejeitado

O leiaute de um sistema de governo é uma especificação com data. Quando muda, o manual novo pode tornar obrigatório um campo antes opcional, mudar o tamanho de outro, trocar um código de tabela ou alterar o namespace. O portal passa a validar contra o esquema novo e o seu gerador, que implementou o esquema antigo de memória, continua produzindo o formato anterior.

Há um agravante: quem testa localmente costuma testar contra o arquivo de exemplo que veio com o manual antigo. O teste passa, porque compara o gerador com ele mesmo. Sem o XSD oficial da versão vigente no pipeline, ninguém descobre a mudança antes do órgão.

Este guia trata do lado técnico: guardar, escolher e validar a versão. Para o hábito de registrar a data do manual que você implementou, veja manual de integração do governo tem data.

Como organizar as versões do leiaute no projeto

  1. Baixe os esquemas oficiais de cada versão. Pegue os arquivos XSD que acompanham o manual, do site oficial, e guarde em pastas por versão, por exemplo schemas/v2.3/ e schemas/v3.0/. Anote a URL e a data do download num arquivo de texto ao lado.
  2. Registre a data de vigência de cada versão. O manual ou o comunicado do órgão diz a partir de quando a versão vale. Essa data vai para o código, não para a memória da equipe.
  3. Escolha a versão pela data do envio. Na transição, as duas versões podem coexistir por um período, e o gerador tem de saber qual usar em cada dia.
  4. Valide antes de enviar. Todo arquivo passa pelo XSD da versão escolhida. Se não passa, o envio nem sai.
  5. Teste no ambiente de testes do órgão, com dado fictício. Se o manual oferece homologação, use. Nunca descubra campo obrigatório enviando uma compra real.
  6. Registre cada envio. Versão, hash, horário, protocolo e resposta completa do órgão.

Os números de versão e as datas do exemplo abaixo são fictícios. Os seus vêm do manual oficial vigente.

Como validar o XML contra o XSD da versão certa

Em PHP, a extensão DOM valida contra XSD com schemaValidate, e a libxml devolve a lista de erros com linha e mensagem. É exatamente o que você quer mostrar a quem vai corrigir:

<?php
declare(strict_types=1);

const LEIAUTES = [
    '2.3' => ['xsd' => __DIR__ . '/schemas/v2.3/aviso.xsd', 'desde' => '2025-01-01'],
    '3.0' => ['xsd' => __DIR__ . '/schemas/v3.0/aviso.xsd', 'desde' => '2026-07-01'],
];

function leiauteVigente(DateTimeImmutable $data): string
{
    $escolhida = null;
    foreach (LEIAUTES as $versao => $info) {
        if ($data >= new DateTimeImmutable($info['desde'])) {
            $escolhida = (string) $versao;
        }
    }
    if ($escolhida === null) {
        throw new RuntimeException('Nenhum leiaute vigente em ' . $data->format('Y-m-d'));
    }
    return $escolhida;
}

function errosDeEsquema(string $xml, string $versao): array
{
    libxml_use_internal_errors(true);
    libxml_clear_errors();

    $doc = new DOMDocument();
    $valido = $doc->loadXML($xml) && $doc->schemaValidate(LEIAUTES[$versao]['xsd']);

    $erros = array_map(
        fn(LibXMLError $e): string => 'linha ' . $e->line . ': ' . trim($e->message),
        libxml_get_errors()
    );
    libxml_clear_errors();

    return $valido ? [] : $erros;
}

O array LEIAUTES está em ordem crescente de data, então a última versão cuja data já chegou é a vigente. A conversão (string) tem um motivo: o PHP guarda chaves como '2.3' como texto, mas converte chaves que são inteiros, como '4', para número. Sem a conversão, uma futura versão '4' faria a função devolver int e, com strict_types, quebrar no tipo de retorno.

Para conferir um arquivo na mão, sem rodar o sistema, o xmllint faz o mesmo papel:

xmllint --noout --schema schemas/v3.0/aviso.xsd envio-2026-10-12.xml

Se o esquema inclui outros arquivos por xs:include ou xs:import, mantenha a estrutura de pastas igual à do pacote oficial, porque os caminhos são relativos. E gere o XML com uma biblioteca, não concatenando texto; o guia de gerar XML em PHP sem concatenar mostra como.

O registro de cada envio

Quando o órgão reclamar de um arquivo de três meses atrás, você precisa saber exatamente o que saiu. Uma tabela simples resolve:

CREATE TABLE envios_compras (
  id bigint PRIMARY KEY,
  processo varchar(40) NOT NULL,
  leiaute_versao varchar(10) NOT NULL,
  arquivo_sha256 char(64) NOT NULL,
  enviado_em timestamp NOT NULL,
  ambiente varchar(12) NOT NULL,
  protocolo varchar(60),
  resposta_status varchar(20) NOT NULL,
  resposta_texto text
);

O hash, calculado com hash('sha256', $xml), prova qual conteúdo saiu, e o arquivo em si fica guardado num armazenamento com retenção definida. A coluna ambiente evita a confusão clássica entre o envio de teste e o real. A resposta_texto guarda a mensagem do órgão inteira, para a tela mostrar "elemento X obrigatório na linha 42" em vez de "falhou".

Por fim, lembre que o sistema não decide sozinho. O envio é feito em nome do órgão e há um responsável por ele. A tela deve deixar claro quem enviou, quando e com qual resultado, para essa pessoa poder agir.

Erros comuns

  • Implementar o leiaute de memória ou de um exemplo antigo. O teste compara o gerador com ele mesmo e passa até o dia em que o órgão rejeita tudo.
  • Trocar a versão no código na data da mudança, à mão. Se alguém esquecer ou adiantar, os envios do período saem no formato errado.
  • Apagar o XSD antigo ao adotar o novo. Reenvios e retificações de processos antigos podem exigir a versão anterior.
  • Mostrar "falha no envio" sem o motivo. Quem pode corrigir o dado não sabe o que corrigir e abre chamado para a TI.
  • Testar com dado real em produção. Um envio de teste vira publicação oficial e precisa de retificação formal.
  • Não registrar o hash do arquivo. Numa contestação, você não consegue provar o que enviou.

Checklist

  • Os XSDs oficiais de cada versão estão no repositório, com URL e data do download anotadas.
  • A data de vigência de cada versão está no código.
  • O gerador escolhe a versão pela data do envio.
  • Todo arquivo é validado contra o XSD antes de sair.
  • Cada envio registra versão, hash, ambiente, protocolo e resposta completa.
  • A tela de envio mostra a mensagem de rejeição do órgão.
  • Mudanças de leiaute são testadas no ambiente de testes com dado fictício.

Abra o código do seu gerador agora e responda sem consultar ninguém: qual versão do manual ele implementa, e em que data essa versão deixa de valer?

Avalie este artigo

Referência: https://www.gov.br/compras/pt-br/acesso-a-informacao/manuais/manual-siasg/manual-siasg-sidec/manual_sidec