Uma auditoria útil de um conector Odoo não começa pelos testes. Começa pelo log e por uma operação real de ponta a ponta: uma encomenda que entra, um envio que sai, um reembolso que volta. O conector Mirakl passava a sua suite em verde no Odoo 19, 18 e 17 e escondia 18 falhas confirmadas; outro conector de marketplace dava 236 de 236 em builds reais do Odoo.sh e escondia 20 falhas de contrato e 12 de versão. Esse artigo conta a falha; este, o método: dez pontos por ordem, o sintoma que denuncia cada um e a prova que cabe numa tarde.
Ideia-chave: o conector que herda já lhe diz o que se passa, na tabela de log e nos crons desligados.
O que recolher antes de se sentar
Quatro dados que não dependem de quem fez o conector. A versão real do Odoo: SELECT latest_version FROM ir_module_module WHERE name = 'base';. A versão da API do terceiro que o código assume, quase sempre visível no URL base. A data da última alteração dos mocks: git log -1 --format=%cd -- tests/. E quem recebe os erros: um e-mail, uma atividade, um ficheiro ou ninguém.
Os dez pontos, por ordem
1. O log escreve mesmo?
Sintoma: a tabela de log tem zero linhas após meses de crons. Prova: SELECT last_value FROM pg_sequences WHERE sequencename = '<tabla_log>_id_seq';. Um NULL aí significa que essa sequência nunca foi lida: ninguém apagou as linhas, simplesmente não se escreveu nenhuma. Isto apareceu na produção do conector Mirakl. O auxiliar que devolve o modelo de log devolvia um recordset vazio quando tinha êxito, e no Odoo um recordset vazio é falso, pelo que if not log_model: return False acertava sempre e cada «isto registamos» era um no-op. A correção é if log_model is False.
2. A data de corte da sincronização
Sintoma: uma encomenda criada de manhã e enviada à tarde fica no Odoo com o estado da manhã. Prova: três encomendas que mudaram de estado ontem no marketplace, comparadas com write_date e o estado no Odoo. No Mirakl o conector pedia start_date, que filtra por data de criação, em vez de start_update_date, que filtra por data de alteração.
3. Idempotência: encomenda duplicada, reembolso duplicado
Sintoma: dois sale.order com a mesma referência externa, ou um reembolso que aparece duas vezes no marketplace. Prova: SELECT client_order_ref, count(*) FROM sale_order GROUP BY 1 HAVING count(*) > 1;, e criar um duplicado a partir de odoo shell para ver se a base de dados o rejeita de verdade: o facto de a restrição estar escrita no modelo não significa que exista no Postgres. No Odoo 19 um _sql_constraints herdado já não cria nada e o ORM apenas deixa um aviso no log, pelo que perde a garantia sem se aperceber; ao contrário, é ruidoso, porque models.Constraint não existe no 17 nem no 18 e aí o módulo nem sequer importa. E o cliente HTTP: no Mirakl voltava a tentar perante 5xx e timeout para qualquer método, também para o PUT do reembolso.
4. Erros HTTP e novas tentativas
Sintoma: a conta amanhece «não autorizada» e toda a sincronização parada. Prova: provoque um 401, um 429 e, se for possível, um 403 de quota. No segundo caso, o 403 de quota não vinha no formato de erro documentado e lia-se como falha de permissões: um limite de velocidade parava tudo. O que deve ver-se: Retry-After respeitado e com um limite máximo, os 5xx e os timeouts repetidos apenas em leituras, o 429 também em escritas porque o servidor o rejeitou sem processar, e a conta bloqueada apenas por uma falha de autenticação real.
5. Mocks versus contrato real
Sintoma: um campo que chega sempre vazio, um filtro que «não filtra». Prova: coloque o OpenAPI atual do fornecedor no repositório e compare os parâmetros de cada wrapper, as chaves de cada corpo de escrita e os campos que cada parser lê. No segundo caso surgiram 16 discrepâncias assim em 88 operações, e mais quatro apenas a chamar: um cabeçalho de paginação relativo impedia importar uma única encomenda a partir da página um.
6. Permissões e SSRF via redirecionamento
Sintoma: nenhum, e por isso está na lista. Prova: grep -rn "allow_redirects\|\.sudo()\|has_group" models/. Cada chamada de saída deve levar allow_redirects=False e rejeitar um 3xx, porque requests segue redirecionamentos por defeito e um host comprometido pode enviá-lo para um IP interno. E cada action_* que trabalha com sudo() deve verificar o grupo no servidor: o groups= do botão é apenas interface. Em julho de 2026 aplicámo-lo a cerca de dezasseis conectores próprios.
7. Crons: numbercall e intervalos
Sintoma: «o cron deixou de correr na terça-feira», sem erro nem rasto. Prova no Odoo 17: SELECT cron_name, active, numbercall, nextcall FROM ir_cron WHERE cron_name ILIKE '%conector%';. No 17 numbercall vale 1 por defeito, pelo que um cron declarado sem ele corre uma vez e desativa-se sozinho, como está no ir_cron.py da série 17.0. O Odoo 18 eliminou o campo e o 19 não o tem, pelo que quem testou no 18 ou no 19 nunca o viu. E veja o intervalo: uma sincronização completa de dez em dez minutos acaba num 429.
8. A versão do Odoo que o código assume
Sintoma: um formulário sem chatter, um separador «payload» em branco, uma encomenda cancelada no marketplace que continua confirmada no Odoo. Prova: grep -rn "<chatter/>\|widget=\"json\"\|read_group\|<tree\|<list" views/ models/. No Mirakl surgiram os quatro ao mesmo tempo: <chatter/> existe desde o 18, widget="json" só no 19, read_group está depreciado no 19, e sale.order.action_cancel() devolve um assistente no 17 e no 18 para uma encomenda confirmada, pelo que o cancelamento nunca acontecia. É a lista de um módulo que compila no Odoo 19 e não se instala.
9. Migração dos dados históricos
Sintoma: corrige o cálculo e as encomendas antigas continuam erradas, porque o conector não volta a tocar numa encomenda existente. Prova: conte os registos anteriores à correção que incumprem a regra nova e decida entre um script de migração ou uma correção única e documentada. No Mirakl foram 18 encomendas com o IVA duplicado no total; corrigiram-se as 15 ativas, apenas se o total coincidia com o do marketplace. E uma armadilha: os dados noupdate de base, incluindo os grupos, não são reaplicados na atualização, pelo que uma alteração de pertença deixa o grupo vazio em produção e cheio na sua base limpa.
10. Documentação do terceiro versus comportamento real
Sintoma: o código faz o que diz o PDF e o servidor faz outra coisa. Prova: uma chamada real por família de endpoint e uma revisão de quais endpoints estão depreciados hoje. No Mirakl o modelo de importação por ficheiro continuava em vigor, mas a mensagens usava um endpoint depreciado. No segundo caso, um endpoint aceitava um único valor onde a documentação dizia lista, e esse referencial estava vazio desde o início. Vai em último porque é o mais caro.
A tarde, resumida
| Ponto | Sintoma | Prova |
|---|---|---|
| Log | Zero linhas após meses de crons | count(*) e pg_sequences.last_value |
| Data de corte | Estados congelados | Encomendas de ontem contra write_date |
| Idempotência | Duplicados, reembolso duplicado | GROUP BY HAVING; a base de dados rejeita o duplicado? |
| Erros HTTP | «Não autorizada» após um 429/403 | Provocar 401, 429, 403 |
| Mocks | Campo sempre vazio | OpenAPI atual contra wrappers e parsers |
| SSRF e permissões | Nenhum | grep allow_redirects, sudo(), has_group |
| Crons | Desliga-se sem erro (17) | SELECT active, numbercall FROM ir_cron |
| Versão do Odoo | Chatter ou payload em branco | grep chatter, widget json, read_group, tree/list |
| Histórico | O antigo continua errado após a correção | Contar registos anteriores que incumprem a regra |
| Documentação | O servidor não faz o que diz o PDF | Uma chamada real por família de endpoint |
O que sai da auditoria: um relatório com severidade, não uma lista de desejos
O relatório ordena por dano: primeiro dinheiro (reembolso duplicado, encomenda duplicada, IVA duas vezes), depois dados (estado congelado, log que não escreve, cron desligado), depois ergonomia. Cada achado leva a sua reprodução e o que não se reproduz é riscado: na auditoria do Mirakl entraram 121 candidatos, 33 foram refutados numa passagem cética e apenas 18 sobreviveram.
A segunda saída é o gate: a suite que existia, em verde antes e depois, não prova a correção. A prova é a execução nas três séries sobre builds reais, 163 de 163 no 19, 18 e 17 para o Mirakl, mais uma verificação em direto. É o que separa um conector nativo bem feito de uma camada intermédia que sobrevive à custa de não se comprometer.
Quando se corrige e quando se reescreve
Corrige-se quando o modelo de dados está certo (uma tabela por objeto do terceiro, um log, uma referência externa única) e as falhas vivem na camada HTTP, nas janelas e nas versões: cabe numa versão, como no Mirakl. Reescreve-se quando falha o fundo: uma API que o fornecedor retirou, sem log nem referência externa, escritas não idempotentes por desenho, ou um conector preso a uma série do Odoo quando precisa de três. É a decisão que preparamos para consultoras e partners tecnológicos que herdam integrações, e também quando o conector é o do seu próprio software.
Perguntas frequentes
Porque não começar pela suite de testes?
Porque uma suite em verde diz que o código coincide com o seu mock, não com o fornecedor nem com a produção. A suite vai no fim, como gate da correção.
É preciso acesso à produção?
Para os pontos 1, 2, 3, 7 e 9 sim, ou a uma cópia: a sequência do log, os estados congelados, os duplicados e os crons desligados só se veem em dados reais.
O que entregam no final?
Um relatório com cada achado provado e ordenado por dinheiro, dados e ergonomia; a recomendação de corrigir ou reescrever; e, se se corrigir, a correção com o seu gate nas séries do Odoo do cliente. O detalhe está em serviços.
Ligações úteis dentro da FlexigoTech
O que fazemos sobre isto
Herdou um conector que falha ou em que ninguém confia?
Conte-nos o que integra, em que versão do Odoo corre e que sintoma vê. Dizemos-lhe que pontos da lista veríamos primeiro e o que é preciso para o fazer. Escreva para comercial@flexigobe.com ou ligue para o +34 616 809 504.

