Conferência de CT-e para uma grande operação logística
PythonPlaywright
>Contexto
Um escritório contábil confere todo mês os CT-e, os conhecimentos de transporte eletrônicos, dos clientes transportadores. Uma das fontes é o portal de uma grande operação logística. Hoje a conferência é à mão: baixar os XMLs, importar no sistema contábil e comparar os relatórios para ver se o total bate.
Esta prova de conceito automatiza a conferência. Ela compara o relatório do portal com os XMLs do mesmo período e responde, documento por documento, OK, DIVERGENTE ou NÃO CONFERIDO. Um conector somente leitura entra no portal, baixa o relatório e o lote de XMLs e já roda a conferência. Ele rodou no portal real.
Construí entre 30 de setembro e 20 de outubro de 2026, em 62 commits. Depois, a prova de conceito virou um piloto ponta a ponta, ainda em andamento: captura em três fontes, importação no sistema contábil e conferência dos três lados.
>Arquitetura (HLD)
Tudo roda no PC Windows do escritório, como agente local: Python 3.13 num ambiente próprio, com o Chromium do Playwright. Um script PowerShell instala tudo com versões fixas e confere o SHA-256 de cada download; rodar de novo não duplica nada.
A rodada começa pelo botão do painel ou pelo Agendador de Tarefas, que recupera a data perdida se o PC estava desligado. Primeiro o atualizador vê se há versão nova. Depois o conector entra no portal só para ler: faz login, gera o relatório, baixa o lote de XMLs e compara a quantidade baixada com o contador da tela. Os arquivos vão para uma pasta por rodada e cliente. Os leitores levam relatório e XMLs ao mesmo modelo, e o comparador devolve OK, DIVERGENTE ou NÃO CONFERIDO para cada documento. O resultado sai em resumo, CSV, JSON e planilha e segue por e-mail, via SMTP. No piloto, a mesma rodada também importa a pasta no sistema contábil, que roda em nuvem.
O estado fica no disco do PC: as pastas das rodadas, o histórico e logs sem segredos. As senhas ficam no Gerenciador de Credenciais do Windows, e o cadastro dos clientes só na máquina, nunca no repositório. Além do agendamento, nada é assíncrono: não há fila nem servidor no meio. As atualizações saem como um pacote zip e um manifesto assinado numa hospedagem estática. O robô recusa o pacote se a assinatura ou o SHA-256 não batem, troca só os arquivos do programa e volta à versão anterior se a nova não passar no teste rápido.
Passe o mouse ou foque uma peça: ela acende com as ligações.
13 peças13 ligações3 áreas
Componentes
Painel e agendamento :: Janela de um botão no PC do escritório e uma tarefa no Agendador do Windows que roda nas datas combinadas e recupera a data perdida.
Conector do portal :: Playwright, só leitura: login, relatório, lote de XMLs, retentativa com modal travado, divisão do mês em semanas e contagem pela tela.
Leitores plugáveis :: Levam Excel (binário e HTML), xlsx, CSV, PDF e XMLs em pasta ou zip para o mesmo modelo de documento.
Comparador :: Aplica as regras documento por documento e devolve OK, DIVERGENTE ou NÃO CONFERIDO, sempre com o motivo.
Saída e e-mail :: Resumo em português, CSV, JSON, planilha e código de saída 0, 1 ou 2; o e-mail sai por SMTP com a planilha anexa e sem XML.
Cofre de credenciais :: As senhas dos portais ficam no Gerenciador de Credenciais do Windows; mensagens de erro citam só o nome do segredo, nunca o valor.
Atualizador assinado :: Antes de cada rodada baixa o manifesto de uma hospedagem estática, confere assinatura e SHA-256, troca só o código e volta atrás se o teste rápido falha.
>Trade-offs
Três estados, com NÃO CONFERIDO para tudo o que não deu para verificar.
Alternativa descartada: Só OK ou DIVERGENTE.
Por quê: Para um contador, uma automação que diz OK sem ter conferido é pior que o processo manual. A primeira versão devolvia OK em pelo menos 4 situações erradas, entre elas captura vazia e CT-e com evento de cancelamento.
Conector somente leitura, que se recusa a clicar em botões que alteram o portal.
Alternativa descartada: Automação livre da tela, inclusive salvar o layout do relatório.
Por quê: É regra fixa do projeto: nos portais, só leitura e download.
Comparar a quantidade baixada com o contador da tela do portal e gerar o relatório de novo se não bater.
Alternativa descartada: Confiar no arquivo baixado.
Por quê: Numa execução o relatório veio com mais linhas do que CT-e válidos. Se a contagem segue diferente, o resultado é NÃO CONFERIDO.
CT-e de complemento numa categoria própria, com quantidade e soma.
Alternativa descartada: Misturar os complementos com as outras divergências.
Por quê: O filtro padrão do relatório não lista complementos. No mês inteiro, os CT-e a mais eram todos complementos; separados, a divergência fica explicada.
Rodar como agente local no computador do escritório.
Alternativa descartada: App na nuvem que recebe os arquivos por upload.
Por quê: A pasta de XMLs e o sistema contábil ficam na máquina do escritório, e um mês de XMLs pesa: 15 mil XMLs sintéticos mínimos já dão 5,9 MB zipados.
>Destaques
>Rodou no portal real, só leitura: OK em todos os CT-e de 01 a 04/09/2026
>Setembro de 2026 inteiro: OK em todos os CT-e, complementos incluídos
>15 mil XMLs cruzados com 15 mil linhas de relatório em 0,8 s, com dados sintéticos
>504 testes, com um portal falso local que imita os rótulos e o modal do real
>62 commits entre 30/09 e 20/10/2026
>Curiosidades
*O pdftotext 25 perde um caractere quando dois valores se encostam no PDF. O leitor recompõe o valor, e a soma do rodapé continua conferida, então uma recomposição errada não vira OK.
*Um teste injeta 65 linhas extras no relatório real de setembro, e o resultado não muda.
*Em 11 execuções reais houve 3 falhas intermitentes: 2 cliques travados pelo modal 'Processando, aguarde...' e 1 download que passou de 180 s.
*Regra do projeto: nenhum arquivo novo passa de 400 linhas.