Un audit utile d'un connecteur Odoo ne commence pas par les tests. Il commence par le log et par une opération réelle de bout en bout : une commande qui entre, un envoi qui sort, un remboursement qui revient. Le connecteur Mirakl passait sa suite au vert sur Odoo 19, 18 et 17 et cachait 18 défauts confirmés ; un autre connecteur de marketplace affichait 236 sur 236 sur de vrais builds Odoo.sh et cachait 20 défauts de contrat et 12 de version. Cet article raconte l'échec ; celui-ci, la méthode : dix points dans l'ordre, le symptôme qui trahit chacun et le contrôle qui tient en un après-midi.
Idée clé : le connecteur dont vous héritez vous dit déjà ce qui ne va pas, dans la table de log et dans les crons désactivés.
Ce qu'il faut rassembler avant de vous asseoir
Quatre informations qui ne dépendent pas de celui qui a conçu le connecteur. La version réelle d'Odoo : SELECT latest_version FROM ir_module_module WHERE name = 'base';. La version de l'API du tiers que suppose le code, presque toujours visible dans l'URL de base. La date du dernier changement des mocks : git log -1 --format=%cd -- tests/. Et qui reçoit les erreurs : un e-mail, une activité, un fichier ou personne.
Les dix points, dans l'ordre
1. Le log écrit-il vraiment ?
Symptôme : la table de log compte zéro ligne après des mois de crons. Test : SELECT last_value FROM pg_sequences WHERE sequencename = '<tabla_log>_id_seq';. Un NULL ici signifie que cette séquence n'a jamais été lue : personne n'a supprimé les lignes, aucune n'a jamais été écrite. C'est ce qu'a révélé la production du connecteur Mirakl. L'assistant qui renvoie le modèle de log renvoyait un recordset vide en cas de succès, et dans Odoo un recordset vide est faux, donc if not log_model: return False réussissait toujours et chaque « ceci, nous le journalisons » était un no-op. La correction est if log_model is False.
2. La date de coupure de la synchronisation
Symptôme : une commande créée le matin et expédiée l'après-midi reste dans Odoo avec le statut du matin. Test : trois commandes qui ont changé de statut hier sur la marketplace, comparées avec write_date et le statut dans Odoo. Chez Mirakl, le connecteur demandait start_date, qui filtre par date de création, au lieu de start_update_date, qui filtre par date de modification.
3. Idempotence : double commande, double remboursement
Symptôme : deux sale.order avec la même référence externe, ou un remboursement qui apparaît deux fois sur la marketplace. Test : SELECT client_order_ref, count(*) FROM sale_order GROUP BY 1 HAVING count(*) > 1;, puis créer un doublon depuis odoo shell pour voir si la base le rejette vraiment : une contrainte écrite dans le modèle ne signifie pas qu'elle existe dans Postgres. Dans Odoo 19, un _sql_constraints hérité ne crée plus rien et l'ORM laisse seulement un avertissement dans le log : vous perdez la garantie sans le savoir ; dans l'autre sens, c'est bruyant, car models.Constraint n'existe ni en 17 ni en 18 et le module ne s'y importe même pas. Et le client HTTP : chez Mirakl, il relançait sur 5xx et timeout pour n'importe quelle méthode, y compris le PUT du remboursement.
4. Erreurs HTTP et reprises
Symptôme : le compte se réveille « non autorisé » et toute la synchronisation est à l'arrêt. Test : provoquez un 401, un 429 et, si possible, un 403 de quota. Dans le second cas, le 403 de quota n'arrivait pas au format d'erreur documenté et était lu comme un défaut de permissions : une limite de débit arrêtait tout. Ce qu'on doit voir : Retry-After respecté et plafonné, les 5xx et les timeouts relancés seulement sur les lectures, le 429 aussi sur les écritures parce que le serveur l'a rejeté sans le traiter, et le compte bloqué uniquement par un véritable échec d'authentification.
5. Mocks contre contrat réel
Symptôme : un champ qui arrive toujours vide, un filtre qui « ne filtre pas ». Test : mettez l'OpenAPI actuel du fournisseur dans le dépôt et comparez les paramètres de chaque wrapper, les clés de chaque corps d'écriture et les champs que lit chaque parser. Dans le second cas, 16 écarts de ce type sont sortis sur 88 opérations, et quatre de plus rien qu'en appelant : un en-tête de pagination relatif empêchait d'importer une seule commande à partir de la page un.
6. Permissions et SSRF via redirection
Symptôme : aucun, et c'est pourquoi il figure dans la liste. Test : grep -rn "allow_redirects\|\.sudo()\|has_group" models/. Chaque appel sortant doit porter allow_redirects=False et rejeter un 3xx, car requests suit les redirections par défaut et un hôte compromis peut vous envoyer vers une IP interne. Et chaque action_* qui travaille avec sudo() doit vérifier le groupe côté serveur : le groups= du bouton n'est que de l'interface. En juillet 2026, nous l'avons appliqué à une seizaine de connecteurs maison.
7. Crons : numbercall et intervalles
Symptôme : « le cron s'est arrêté mardi », sans erreur ni trace. Test sur Odoo 17 : SELECT cron_name, active, numbercall, nextcall FROM ir_cron WHERE cron_name ILIKE '%conector%';. En 17, numbercall vaut 1 par défaut, donc un cron déclaré sans lui s'exécute une fois et se désactive tout seul, comme l'indique le ir_cron.py de la série 17.0. Odoo 18 a supprimé le champ et 19 ne l'a pas, donc celui qui a testé en 18 ou 19 ne l'a jamais vu. Et regardez l'intervalle : une synchronisation complète toutes les dix minutes finit en 429.
8. La version d'Odoo que suppose le code
Symptôme : un formulaire sans chatter, un onglet « payload » vide, une commande annulée sur la marketplace qui reste confirmée dans Odoo. Test : grep -rn "<chatter/>\|widget=\"json\"\|read_group\|<tree\|<list" views/ models/. Chez Mirakl, les quatre sont apparus à la fois : <chatter/> existe depuis la 18, widget="json" seulement en 19, read_group est déprécié en 19, et sale.order.action_cancel() renvoie un assistant en 17 et 18 pour une commande confirmée, si bien que l'annulation n'avait jamais lieu. C'est la liste de un module qui compile sur Odoo 19 et ne s'installe pas.
9. Migration des données historiques
Symptôme : vous corrigez le calcul et les anciennes commandes restent fausses, car le connecteur ne retouche pas une commande existante. Test : comptez les enregistrements antérieurs à la correction qui enfreignent la nouvelle règle et choisissez entre un script de migration et une correction unique et documentée. Chez Mirakl, il s'agissait de 18 commandes avec la TVA en double dans le total ; les 15 actives ont été corrigées, uniquement si le total coïncidait avec celui de la marketplace. Et un piège : les données noupdate de base, groupes compris, ne sont pas réappliquées lors de la mise à jour, donc un changement d'appartenance laisse le groupe vide en production et plein dans votre base propre.
10. Documentation du tiers contre comportement réel
Symptôme : le code fait ce que dit le PDF et le serveur fait autre chose. Test : un appel réel par famille d'endpoint et une revue des endpoints dépréciés aujourd'hui. Chez Mirakl, le modèle d'import par fichier était toujours en vigueur, mais la messagerie utilisait un endpoint déprécié. Dans le second cas, un endpoint acceptait une seule valeur là où la documentation disait liste, et ce référentiel était vide depuis le début. Il vient en dernier parce que c'est le plus coûteux.
L'après-midi, en résumé
| Point | Symptôme | Test |
|---|---|---|
| Log | Zéro ligne après des mois de crons | count(*) et pg_sequences.last_value |
| Date de coupure | Statuts figés | Commandes d'hier contre write_date |
| Idempotence | Doublons, double remboursement | GROUP BY HAVING ; la base rejette-t-elle le doublon ? |
| Erreurs HTTP | « Non autorisé » après un 429/403 | Provoquer 401, 429, 403 |
| Mocks | Champ toujours vide | OpenAPI actuel contre wrappers et parsers |
| SSRF et permissions | Aucun | grep allow_redirects, sudo(), has_group |
| Crons | S'éteint sans erreur (17) | SELECT active, numbercall FROM ir_cron |
| Version d'Odoo | Chatter ou payload vide | grep chatter, widget json, read_group, tree/list |
| Historique | L'ancien reste faux après la correction | Compter les enregistrements antérieurs qui enfreignent la règle |
| Documentation | Le serveur ne fait pas ce que dit le PDF | Un appel réel par famille d'endpoint |
Ce qui sort de l'audit : un rapport avec gravité, pas une liste de souhaits
Le rapport classe par dommage : d'abord l'argent (double remboursement, commande en double, TVA deux fois), puis les données (statut figé, log qui n'écrit pas, cron éteint), puis l'ergonomie. Chaque constat porte sa reproduction et ce qui ne se reproduit pas est rayé : dans l'audit de Mirakl, 121 candidats sont entrés, 33 ont été réfutés lors d'une passe sceptique et seuls 18 ont survécu.
La seconde sortie est le gate : la suite existante, au vert avant et après, ne prouve pas la correction. La preuve, c'est l'exécution sur les trois séries sur de vrais builds, 163 sur 163 en 19, 18 et 17 pour Mirakl, plus une vérification en conditions réelles. C'est ce qui sépare un connecteur natif bien fait de une couche intermédiaire qui survit à force de ne pas s'engager.
Quand on corrige et quand on réécrit
On corrige quand le modèle de données est bon (une table par objet du tiers, un log, une référence externe unique) et que les défauts vivent dans la couche HTTP, les fenêtres et les versions : cela tient en une version, comme avec Mirakl. On réécrit quand le fond est défaillant : une API que le fournisseur a retirée, ni log ni référence externe, des écritures non idempotentes par conception, ou un connecteur lié à une série d'Odoo alors qu'il vous en faut trois. C'est la décision que nous préparons pour les cabinets de conseil et les partenaires technologiques qui héritent d'intégrations, et aussi quand le connecteur est celui de leur propre logiciel.
Questions fréquentes
Pourquoi ne pas commencer par la suite de tests ?
Parce qu'une suite au vert dit que le code coïncide avec son mock, pas avec le fournisseur ni avec la production. La suite vient à la fin, comme gate de la correction.
Faut-il un accès à la production ?
Pour les points 1, 2, 3, 7 et 9, oui, ou à une copie : la séquence du log, les statuts figés, les doublons et les crons éteints ne se voient que dans des données réelles.
Que livrez-vous à la fin ?
Un rapport avec chaque constat prouvé et classé par argent, données et ergonomie ; la recommandation de corriger ou de réécrire ; et, si l'on corrige, la correction avec son gate sur les séries d'Odoo du client. Le détail est dans services.
Liens utiles chez FlexigoTech
Ce que nous faisons à ce sujet
Vous avez hérité d'un connecteur qui échoue ou auquel personne ne fait confiance ?
Dites-nous ce qu'il intègre, sur quelle version d'Odoo il tourne et quel symptôme vous voyez. Nous vous dirons quels points de la liste nous regarderions en premier et ce qu'il faut pour le faire. Écrivez à comercial@flexigobe.com ou appelez le +34 616 809 504.

