Renfort technique pour intégrateurs Odoo
Vos projets Odoo ne bloquent pas sur la compta.
Ils bloquent sur la boutique, sur la caisse, sur l'étiquette de colis qui ne sort pas de l'imprimante du dépôt. C'est la partie que personne n'aime chiffrer. C'est celle que je reprends.
Le constat
Une migration se vend sur l'ERP. Elle se juge sur ce que le client touche tous les jours.
Le plan comptable est carré, les stocks sont repris, la facturation tourne. Et pourtant le client râle.
Parce que sa vendeuse scanne un article sur deux. Parce qu'une page de rayon met trois secondes à s'afficher. Parce que l'étiquette de transport ne sort pas, et que personne ne sait dire pourquoi.
Ces sujets tombent entre deux chaises. Ils demandent de connaître Odoo en profondeur et d'aimer le web, CUPS, les DNS, les feuilles de style et les files d'impression. Dans beaucoup d'agences, il n'y a personne pour les prendre.
Je fais ça, et seulement ça. En renfort de votre équipe, sous votre marque, sans jamais toucher à votre périmètre ERP.
Ce que je reprends
La boutique et le site, dans Odoo
Thème, tunnel de commande, click and collect, pages de rayon, fiches produit. SEO technique, balisage schema.org, flux produits, redirections. Dans Odoo, pas dans un WordPress posé à côté qu'il faudra synchroniser toute sa vie.
Ce qui se passe dans le dépôt
Page de préparation des commandes, étiquettes de transport qui sortent toutes seules, impression depuis le serveur vers la box IoT, files CUPS et pilotes ZPL, caisse et codes-barres. Le genre de détail qui décide si l'outil est adopté ou contourné.
L'acquisition, et des chiffres relisables
Newsletters et campagnes dans Odoo, API Conversions de Meta écrite à la main, GA4, PostHog, Search Console, IndexNow. Des mesures qu'on peut recouper et contredire, pas un tableau de bord de démonstration.
Mettre en production sans parier
Scripts de déploiement, sauvegarde avant chaque écriture, surveillance externe, chien de garde sur la prod, procédure de redémarrage qui se vérifie elle-même et sait revenir en arrière. Une boutique et une caisse sur la même instance ne pardonnent pas.
La douleur numéro un des migrations
Une migration Odoo perd les URL indexées. Celle-ci n'en a pas perdu une.
Odoo sert ses fiches produit sous /shop/... par une constante écrite en dur,
SHOP_PATH, appelée à 27 endroits du code. L'ancien site les servait à la racine
du domaine. Réécrire les adresses, c'était accepter de repartir de zéro sur Google, avec un
client qui voit son trafic s'effondrer le lendemain de la bascule et une agence sans réponse
à lui donner.
La parade n'est pas un plan de redirections : c'est un convertisseur d'URL werkzeug qui ne reconnaît que les slugs de produits publiés. Werkzeug essaie ses règles dans l'ordre, donc tout ce qui n'est pas un produit connu retombe sur le routage normal d'Odoo. Les pages de contenu, le back-office et les routes natives gardent la priorité.
Les 808 adresses indexées ont été conservées à l'identique. Il est resté 79 redirections de rubriques, toutes à zéro clic, au lieu des 887 prévues au départ.
Le périmètre a été tranché sur des données, pas au ressenti : six mois de Search Console exportés, 47 199 lignes relues. Verdict, 609 fiches produit cumulaient 281 clics quand la page d'accueil en faisait 3 857.
L'actif à protéger n'était donc pas le référencement des fiches, c'étaient les signaux locaux de la page d'accueil. Savoir ce qu'on ne doit pas protéger fait gagner autant de temps que le reste.
class EvProductSlugConverter(BaseConverter):
"""Convertisseur d'URL qui ne matche que les slugs de produits publiés."""
regex = MOTIF_SLUG
def to_python(self, value):
# lang=None est indispensable : le convertisseur tourne PENDANT le routage,
# avant qu'Odoo n'ait normalisé la langue du navigateur. Un visiteur qui
# envoie « Accept-Language: fr-FR » laisse le contexte sur fr_FR, langue non
# installée ici, et le moindre accès à l'ORM lève alors « Invalid language
# code » : HTTP 422 sur les fiches produit ET sur toutes les pages de contenu.
env = request.env(context={'lang': None})
produit = env['product.template'].sudo().search(
[('ev_url_slug', '=', value), ('is_published', '=', True)], limit=1
)
if not produit:
raise ValidationError() # werkzeug passe à la règle suivante
return produit.with_env(request.env)
Les cinq lignes de commentaire valent le code. Ce lang=None vient d'un incident
réel : un convertisseur s'exécute pendant le routage, avant qu'Odoo n'ait normalisé la langue
du navigateur. Un visiteur avec Accept-Language: fr-FR, langue non installée sur
cette instance, faisait tomber les fiches produit et toutes les pages de contenu
en HTTP 422. Ce genre de détail ne s'apprend pas dans la documentation.
Le site, en vrai
Odoo peut faire une boutique qui ne ressemble pas à Odoo.
Ce n'est pas un thème acheté. Thème sur mesure, tunnel en deux étapes, click and collect avec calendrier de retrait, tri par disponibilité réelle, pastille de rupture, données structurées complètes. Tout vit dans Odoo : un seul stock, un seul catalogue, une seule base clients.
Le tunnel livré
Deux étapes, et rien qui ne serve la commande.
Le tunnel d'Odoo demande tout, tout de suite, et le compte avant la commande. Celui-ci demande l'e-mail, puis les coordonnées, puis fait payer. Voici les quatre écrans réels, dans l'ordre où la cliente les voit.
Le Click and Collect
Un calendrier qui ne se maintient nulle part.
- Les dates viennent des heures d'ouverture du magasin, déjà dans Odoo. Les jours proposés sont ceux où le magasin travaille, et les congés posés dans ce même calendrier bloquent les dates tout seuls. Rien ne se saisit deux fois, donc rien ne peut diverger.
- Jamais le jour même. La première date proposée est demain : une commande passée à 23 h ne peut pas être promise pour le matin.
- La date choisie s'écrit sur la commande, et le bon de livraison est régénéré pour la porter. La préparatrice la voit sur son écran sans aller la chercher, et le colis attend la bonne cliente le bon jour.
- Le retrait est en tête, et gratuit. C'est le seul mode de livraison qui fait revenir quelqu'un dans le magasin, et une cliente qui vient chercher un jeans en repart rarement avec le jeans seul.
- Les points de fidélité s'affichent avant le paiement : ce que la cliente a, ce qui lui manque pour cinq euros, et ce que la commande va lui rapporter. Le mécanisme existait déjà et personne ne le voyait : 2 170 clientes avaient des points et aucune page du tunnel ne le leur disait.
- Quatre moyens de paiement par un seul contrat, carte, Bancontact, Belfius et PayPal, avec la note Google affichée juste à côté. Celle qui s'affiche ici est celle que les demandes d'avis ont fait bouger.
Dans le même tunnel
L'adresse se vérifie chez bpost pendant qu'elle se tape.
Une adresse mal écrite se paie deux fois : l'étiquette part, le colis revient, et la cliente écrit au magasin. Le tunnel interroge donc bpost à la frappe et pose ses suggestions dans les champs d'Odoo, sans en ajouter un seul à l'écran.
Essayez Tapez « chaus » dans Rue, choisissez, puis tapez 8 dans Numéro. Les flèches parcourent la liste, Entrée choisit, Échap ferme. Le code postal et la ville interrogent le même niveau et se remplissent ensemble.
- Le widget officiel de bpost a été posé, puis retiré le même jour. Il ajoutait ses quatre champs au-dessus de ceux d'Odoo, avec ses libellés : la cliente voyait « code postal », « nom de rue », « numéro de maison », puis quinze centimètres plus bas « Code postal », « Rue », « Numéro ». Les mêmes informations, demandées deux fois.
- Il pesait 528 Ko d'Angular sur la page où la latence compte le plus de tout le site, et il lisait la clé d'API dans le HTML, donc elle était publique. On appelle maintenant le même service depuis le serveur : 16 Ko de JavaScript au total, et la clé ne quitte jamais la machine.
- Le contrat a été trouvé en lisant le widget, rien n'étant documenté : quatre niveaux qui s'enchaînent, localité, rue, numéro, boîte, chacun exigeant le contexte du précédent. Le paramètre s'appelle
locality, la réponse rendlocalityName, et l'en-tête estx-api-key: tout le reste répond 403 ou 400. - 220 ms de repos entre deux frappes, deux caractères minimum, six suggestions, quatre secondes de patience. En dessous de deux caractères tout le pays répond ; au-delà de quatre secondes, une suggestion arrive après que la cliente a fini de taper, donc elle ne sert plus à rien.
- Le tunnel ne dépend jamais du service. Aucun champ n'est verrouillé, aucune valeur refusée, et si bpost ne répond pas il ne se passe simplement rien. Une adresse étrangère, un lieu-dit, une rue trop neuve : tout se tape à la main. Une API qui tombe ne doit pas empêcher une vente.
- Et un interrupteur. Vider la clé dans les paramètres éteint les suggestions : le marqueur disparaît de la page, le script ne trouve rien et s'arrête. Le formulaire retrouve sa forme d'origine sans redémarrage ni mise à jour de module.
Le service de validation ne répond jamais « faux ». Il rend ce qu'il a su reconnaître, et corrige en silence : « Peron » devient « PERRON ». Le seul signal exploitable est le champ qui revient vide quand il n'a pas su placer la rue ou le numéro.
« 82 Chaussée de Mons » ne rend ni rue ni numéro. La même adresse écrite « Chaussée de Mons 82 » rend les deux. Une bonne part des adresses signalées ne sont donc pas fausses, seulement mal écrites, et se réparent sans rien demander à la cliente.
Le même service passe en revue les adresses déjà en base, celles reprises de l'ancien site comme celles saisies en magasin. On répare avant que le colis parte, pas après qu'il est revenu.
Le terrain, en vrai
Une page de préparation, écrite pour un téléphone tenu d'une main.
L'ancien site avait un outil de préparation maison, 5 292 lignes accumulées au fil des ans, qui mourait avec lui. Reconstruit en module Odoo : une route, une page autonome sans le gabarit de la boutique, aucun framework et aucune police distante. Elle s'ouvre vite sur le wifi du magasin, y compris quand il est mauvais.
Essayez Cochez une ligne : elle se barre. Cochez tout un bon et le bouton de validation apparaît, puis demande confirmation en nommant la cliente. La flèche en haut à droite remet les deux bons dans la file.
- 44 px minimum par cible tactile. En dessous, on rate la case et on coche la ligne d'à côté. Ici, ça veut dire préparer le mauvais article.
- La vignette fait 96 × 128, pas 72 × 96. C'est la photo qui fait reconnaître la pièce dans un rayon, le libellé ne sert qu'à confirmer. Trop petite, elle oblige à lire, et lire est lent.
- Le bouton photo est hors du label. Dedans, un clic sur l'image ouvrirait la galerie et cocherait la case du même geste.
- La dernière case ne valide pas. Cocher dit « j'ai la pièce en main » : fréquent, réversible. Valider expédie, crée une étiquette réelle et facture un envoi. Le bouton n'apparaît que quand tout est coché, et la confirmation nomme la cliente.
- Rechargement toutes les 30 secondes, par injection des seules cartes nouvelles. Jamais par rechargement de la page : la préparatrice a le pouce sur l'écran.
- Fenêtre de sept jours, « tout » par défaut. La base portait cinq bons prêts dont le plus vieux datait de cinq mois. Un bon oublié doit sauter aux yeux, pas se cacher derrière le bon onglet.
auth="user" ne protège rien ici : toute cliente ayant un compte est un res.users. Ce qui tient, c'est le refus des comptes partagés plus l'exigence du groupe Stock.
stock.move.line.picked et stock.move.picked sont deux champs distincts, le premier n'alimente pas le second, et button_validate ne regarde que le second.
Deux commandes préparées, zéro étiquette sortie. La recherche d'imprimante ramenait celle des étiquettes d'article, et le transporteur du bon valait in_store. Corrigé, et écrit.
L'après-vente, et la loi
La rétractation n'est pas un retour. Depuis juin, elle a son propre bouton.
Deux régimes coexistent sur une boutique belge. Ils ne se ressemblent pas, les confondre coûte cher, et un intégrateur les découvre rarement dans le cahier des charges : ils tombent à la fin, juste avant la mise en ligne.
Quatorze jours calendrier pour renoncer, sans motif, à compter du jour où la cliente reçoit le bien. Articles VI.47 à VI.53 du Code de droit économique, qui transposent la directive 2011/83/UE. Et si l'information sur ce droit n'est pas donnée dans les formes, l'article VI.48 porte le délai de quatorze jours à douze mois.
L'article 11 bis de cette même directive, inséré par la directive (UE) 2023/2673, impose une fonction de rétractation en ligne : un lien clairement identifié et disponible pendant tout le délai, un formulaire où la cliente confirme son identité et sa commande, un bouton de confirmation, et un accusé de réception sur support durable portant la date et l'heure.
| Retour commercial | Rétractation | |
|---|---|---|
| Motif | selon les conditions de la maison | aucun exigé |
| Frais de livraison aller | non remboursés | remboursés si la commande est rendue en entier |
| Forme du remboursement | avoir ou bon d'échange | le moyen de paiement d'origine |
| Délai | politique de la maison | 14 jours après la déclaration |
Cette colonne de droite n'est pas négociable, et deux textes du site la contredisaient. Les conditions générales déduisaient les frais de port dès que le total repassait sous le seuil de livraison offerte, y compris sur un retour total : sur une rétractation entière, tous les paiements reçus doivent être rendus, livraison standard comprise. Et les conditions promettaient un échange que la politique de retour refusait deux pages plus loin. Les deux ont été réalignées sur ce qui se pratique vraiment.
Ce qu'on a trouvé en ouvrant l'existant
Une page qui confirmait des demandes qu'elle n'enregistrait pas.
La page de rétractation reprise de l'ancien site affichait « veuillez remplir les informations ci-dessous », puis un volet dépliant qui annonçait « votre demande a été soumise avec succès ». Sans un seul champ de formulaire. Le formulaire n'avait pas migré, seul son habillage était passé. C'est pire qu'une page absente : la cliente lisait une confirmation pour une demande qui n'existait nulle part, et repartait rassurée.
- La date de déclaration est posée à la création et n'est jamais recalculée. C'est elle qui fait foi, c'est elle qui fait courir les quatorze jours, et aucun traitement ne doit pouvoir la déplacer.
- L'enregistrement et l'accusé horodaté sont automatiques. C'est le cœur de l'obligation légale : ça ne se confie pas à une tâche planifiée qui peut ne pas tourner.
- Le remboursement, lui, reste manuel. La loi autorise à attendre le colis ou la preuve de son envoi, et rembourser au clic ouvrirait la porte à l'abus. C'est une décision du commerçant, pas un automatisme.
- Un garde-fou quotidien pose un rappel à dix jours, pour que les quatorze ne soient jamais dépassés. Rien dans Odoo ne signale ce dépassement, et c'est pourtant lui qui coûte.
- La commande n'est trouvée que si la référence et l'e-mail concordent, et le message d'erreur est volontairement le même dans les deux cas d'échec. Sinon le formulaire devient un moyen de tester l'existence des commandes, dont les numéros se suivent.
- L'accusé ne s'efface pas après envoi. Odoo supprime par défaut certains messages une fois partis : un accusé de réception qui disparaît ne prouve plus rien, le jour où il faut prouver.
Détail qui a son importance : le lien du pied de page ne s'appelle plus « droit de rétractation » mais « renoncer au contrat ». Le texte européen veut une formule qui désigne la fonction, sans ambiguïté, pas l'intitulé d'une page d'information.
L'autre régime
Le retour commercial passe par un ticket, pas par une boîte mail.
Un formulaire public : sa commande retrouvée par référence et e-mail, les articles à rendre cochés un par un, un motif par article, et le choix entre remboursement et bon d'échange.
Trois brouillons créés d'un coup : un ticket dans l'équipe Retours, le retour de marchandise côté stock, et la note de crédit. Tout se traite depuis le ticket, remise en stock ou mise au rebut article par article, puis remboursement.
Aucune commande n'est exposée sans correspondance exacte de l'e-mail, le nombre d'essais par adresse IP est limité, et tout ce que la cliente écrit est échappé avant d'atterrir dans le ticket.
Un exemple de tuyauterie
Le flux produits, enrichi article par article.
1 557 articles servis en continu par un contrôleur Odoo. Odoo ne sait pas produire ça seul : la catégorie Google, le genre, la tranche d'âge et les images secondaires ont été déduits du catalogue et ajoutés. Voici un article, tel qu'il sort aujourd'hui.
<item>
<g:id>01-729053-604-blanc-L</g:id>
<g:item_group_id>T19092</g:item_group_id>
<title>Débardeur avec finition en maille filet - YAYA - blanc (L)</title>
<link>https://[domaine du client]/debardeur-avec-finition-en-maille-filet-yaya-blanc</link>
<g:image_link>.../product.product/37438/image_1920</g:image_link>
<g:additional_image_link>.../product.image/1116/image_1920</g:additional_image_link>
<g:additional_image_link>.../product.image/1117/image_1920</g:additional_image_link>
... cinq autres images secondaires
<g:availability>in_stock</g:availability>
<g:price>34.95 EUR</g:price>
<g:brand>YAYA</g:brand>
<g:size>L</g:size>
<g:google_product_category>212</g:google_product_category>
<g:gender>female</g:gender>
<g:age_group>adult</g:age_group>
</item> Les preuves
Huit choses trouvées sur un seul dossier.
Une boutique de prêt-à-porter, site public et caisse sur la même instance, migrée de la V14 à la V19. Tout ce qui suit a été mesuré avant et après, pas estimé. Aucun de ces points n'était dans le cahier des charges, et c'est exactement le problème.
13 469 références sur 23 691. La règle Lot barcodes interceptait les codes internes avant la règle Produit. Le défaut était antérieur à la migration et personne ne l'avait jamais nommé : comme une partie des articles passait, tout le monde croyait à un scanner capricieux.
Les visiteurs anonymes du site naviguaient avec les droits d'un administrateur système. Le symptôme remonté était du SEO : un sitemap de 5 759 URL pour 756 produits publiés. La cause était de la sécurité. Odoo ne filtre les produits dépubliés que pour les utilisateurs externes.
27 000 règles de prix mortes réévaluées à chaque affichage, et un champ calculé qui finissait par s'appeler lui-même. Deux causes indépendantes qui se masquaient l'une l'autre dans le profil.
wkhtmltopdf redemande ses feuilles de style à Odoo, en HTTP, sur le serveur vivant. Avec deux workers, un rendu un peu lourd les prend tous les deux et plus rien ne répond. Diagnostic, puis chien de garde qui tue les rendus qui s'éternisent.
L'e-mail « donnez-nous votre avis » de l'ancien site n'était ni dans l'outil d'envoi ni dans une extension du marché : un greffon maison, chargé sans activation, invisible dans l'administration. 281 demandes entre mai et août, 26 avis récoltés, note passée de 4,6 à 4,7. Avant de déclarer un automatisme perdu, il faut aller voir sur l'ancien serveur, il répond encore.
Un PNG envoyé dans une file ZPL. CUPS répondait fièrement completed, le pilote jetait le travail à la poubelle. Deux jours de colis étiquetés à la main avant que quelqu'un ose dire que ça ne marchait pas.
Le programme de fidélité repris de WooCommerce fonctionnait parfaitement. Aucune page du tunnel n'en soufflait mot. Le mécanisme existait déjà dans Odoo : il suffisait de l'afficher là où la cliente est identifiée et où une remise l'intéresse encore.
Après avoir compris pourquoi Odoo mettait une heure là où l'ancien outil mettait deux minutes : un seul fil de cron, et deux tâches qui ne peuvent pas se croiser. Et après avoir mesuré que l'accélérateur maison rendait l'envoi plus lent, puis l'avoir retiré.
Ce ne sont pas des anecdotes. Chacun de ces points a été écrit, daté et rangé dans une base de connaissances relisable, avec la cause, la parade et le piège à ne pas refaire. C'est ce qui reste chez vous quand je pars.
Quatre pièges que la documentation ne mentionne pas
Chacun a coûté une demi-journée. Une fois.
Ceux-là sont dans le code de l'éditeur, pas dans le mien. Ils ont un point commun : ils ne lèvent aucune erreur, ou ils la lèvent très loin de leur cause. C'est pour ça qu'ils coûtent cher la première fois, et rien du tout la deuxième.
literal_eval Les domaines des campagnes marketing sont lus avec literal_eval, pas safe_eval : ni datetime, ni context_today, ni relativedelta n'y passent. Tout calendrier doit vivre dans un champ stocké qu'un cron entretient.
marketing.campaign Une campagne aspire tout l'historique le jour où on la démarre. Sur les commandes, ça faisait 11 849 lignes reprises de l'ancien site, prêtes à recevoir un e-mail. Ce qui protège : un filtre sur l'origine web plus un plancher de date. Ne jamais élargir un domaine de campagne sans compter d'abord ce qui entrerait.
ir.qweb Odoo surveille les balises de script de ses pages de contenu et réécrit ce qu'il ne reconnaît pas. Un pixel publicitaire collé dans l'éditeur ne part jamais, et rien ne le signale. Le suivi des conversions a donc été écrit côté serveur, avec l'API dédiée.
cookie Un cookie de consentement illisible fait tomber tout le site en 500, pas seulement la barre qui le lit. Un visiteur avec un cookie corrompu ne voit plus rien, et son navigateur le lui renvoie à chaque tentative.
L'autre moitié de l'honnêteté
Trois erreurs qui sont les miennes, et ce qu'elles ont produit.
Vous ne cherchez pas quelqu'un qui ne tombe jamais, ça n'existe pas. Vous cherchez quelqu'un qui sait ce qui s'est passé, qui le dit avant qu'on le découvre, et qui en sort un garde-fou au lieu d'une promesse.
@api.depends('id') est interdit en Odoo 19 et l'exception tombe
à l'import du module : le registre entier refuse de charger, tout
répond 500, caisse comprise. C'était pendant la fermeture de caisse, et la gérante a vu
« Connexion perdue » à l'écran. Sa fermeture a abouti quand même, 36 commandes, écart de
0,00 €.
Trois erreurs de méthode enchaînées, et aucune n'est celle d'Odoo : un modèle déployé sans mettre à jour le module, un arrêt de service qui rend la main avant l'arrêt réel donc deux mises à jour mortes sans que rien ne le dise, et un journal vide pris pour une réussite parce que le contrôle lisait un fichier que la configuration ne remplissait pas.
Ce qui en est resté Une liste de redémarrage exécutable où chaque case se vérifie elle-même, et qui refuse de démarrer tant qu'une session de caisse est ouverte. Plus une règle : une réparation ponctuelle laissée en place devient un piège. Celle posée après cet incident a écrasé un déploiement le lendemain. Retirée.
Odoo laisse la ligature œ intacte dans ses slugs : aucune normalisation
Unicode ne la décompose, et \w la considère comme une lettre. Mon
convertisseur, lui, n'acceptait que l'ASCII. La route ne matche jamais, la fiche répond
404, et rien ne le signale : ni erreur, ni alerte, ni ligne de journal.
Deux pièces en vente étaient dans ce cas, dont une depuis trois mois. Les mots en cause : « cœur » et « nœud », du vocabulaire courant en prêt-à-porter.
Ce qui en est resté Un motif de slug écrit une fois, relu par les convertisseurs et par le code qui écrit. Il refuse désormais d'enregistrer en silence une adresse qu'il ne saurait pas relire : l'erreur se voit à l'écriture, pas trois mois plus tard.
website_meta_title est un Char(size=70) et Odoo tronque en
silence. Mon script de reprise a suivi la même limite, au caractère et non au mot.
Soixante-trois fiches se sont retrouvées dans Google avec un titre qui s'arrête au milieu
du nom de l'enseigne. Taux de clic mesuré sur ces pages : 0,4 %.
Ce qui en est resté La coupe au mot, et un inventaire des 4 226 adresses avec les titres et les métas réellement servis, pas ceux qu'on croit avoir posés. Un chiffre qu'on ne voit pas est un chiffre auquel on finit par croire.
Quand ça tombe
Deux pannes, un garde-fou, et ce qu'il en reste.
La boutique et la caisse ne répondaient plus. Le serveur, lui, allait très bien : charge à 0,01, 4,8 Go de mémoire libres, SSH instantané. Quand la machine est au repos et que le site est mort, c'est un interblocage, pas une surcharge.
Une commande payée pose un verrou, puis lance le rendu du bon de livraison.
wkhtmltopdf redemande ses feuilles de style à Odoo, en HTTP, sur le serveur
vivant. Avec deux workers, le premier attend le PDF, le second attend le verrou : plus
personne pour servir la requête qui débloquerait tout. Et limit_time_real
réglé à dix heures, donc rien ne serait venu dénouer ça avant le soir.
Le déblocage tient en un kill du rendu suspendu. Surtout pas un
redémarrage : la caisse du magasin tourne sur cette instance, et un redémarrage coupe un
encaissement en cours.
Chaque minute : un rendu PDF de plus de 120 secondes est tué, les verrous PostgreSQL tenus depuis plus de 60 secondes sont signalés, et la page de connexion est appelée en local avec une seconde tentative avant de crier. Un rendu légitime prend six secondes.
Il pousse ensuite son état vers un moniteur externe. S'il se tait, l'alerte tombe : un surveillant mort ne prévient de rien, et son silence ressemble beaucoup au calme.
Vérifié sur la production avec une vraie fausse panne : processus lancé à 13h33m59, tué tout seul à 13h36m04, après 121 secondes. Ce test a révélé un défaut invisible autrement, la fenêtre de silence était trop courte et aurait produit de fausses alertes. Son périmètre s'arrête là : il ne redémarre jamais rien.
La box qui pilote l'imprimante ticket ne redémarrait plus. SSH répondait, mais les ports web étaient tous fermés, en connection refused et non en timeout : la machine est vivante et refuse activement, donc rien n'écoute.
Carte micro-SD sortie, lue dans un lecteur dont on a d'abord prouvé qu'il fonctionne.
Elle annonce 127 139 840 octets, une taille qui ne correspond à aucune
carte existante, sans aucune table de partition, et son premier secteur contient
55aa répété, un motif de test électronique. Une table valide porte cette
signature sur ses deux derniers octets. Le contrôleur a perdu son
adressage.
Le redémarrage n'a rien cassé, il a révélé une panne installée depuis des mois : un Linux qui tourne garde l'essentiel en mémoire et ne relit presque jamais son disque. Remède, image officielle vérifiée par empreinte, carte haute endurance, et un clone de secours dans le tiroir. Une caisse tombe aussi pour des raisons qui n'ont rien de logiciel.
Ce qui reste dans le dépôt
Vingt-quatre modules, nommés par leur rôle.
Le préfixe porte le nom du client, il est masqué ici. Chacun est un module Odoo autonome, installé en production, versionné, avec ses commentaires en français et sa raison d'être écrite en tête de fichier.
Comment on travaille
Quatre règles, écrites avant de commencer.
Votre client ne me voit pas, ou il me voit sous votre nom. Au choix, et c'est le vôtre. Je ne démarche jamais un compte rencontré chez vous.
Un périmètre écrit, un prix, une date. Ce qui sort du périmètre est chiffré à part, avant d'être fait. Pas de régie qui dérive et qu'on découvre à la facture.
Du code commenté en français, des modules propres hors de vos arbres git, les scripts de déploiement et la procédure de retour arrière. Rien qui ne tourne que sur ma machine.
Je reste sur le web, le terrain et l'exploitation. La compta, les stocks, la production et la paie restent chez vous. Si un correctif doit passer par votre périmètre, je le documente et je vous le passe.
Les questions qui viennent toujours
Sept réponses, avant le premier appel.
Une migration vers Odoo fait-elle perdre le référencement ?
Pas si les adresses sont conservées. Odoo sert ses fiches produit sous /shop/... par une constante écrite en dur, appelée à 27 endroits du code. Un convertisseur d'URL werkzeug qui ne reconnaît que les slugs de produits publiés permet de garder les adresses de l'ancien site à l'identique, sans voler les URL des pages de contenu ni des routes natives. Sur le dossier de référence, les 808 adresses indexées ont été conservées et il n'est resté que 79 redirections de rubriques, toutes à zéro clic, au lieu des 887 prévues au départ.
Travaillez-vous en marque blanche pour une agence Odoo ?
Oui, c'est le mode par défaut. Le client final ne me voit pas, ou il me voit sous le nom de l'agence : c'est l'agence qui choisit. Je ne démarche jamais un compte rencontré chez vous.
Quel périmètre Odoo prenez-vous, et lequel ne prenez-vous pas ?
Je prends le site et la boutique dans Odoo, le tunnel de commande, le SEO technique et les flux produits, les outils de terrain (préparation de commandes, étiquettes de transport, impression vers box IoT, caisse et codes-barres) et l'exploitation (déploiement, surveillance, retour arrière). Je ne touche pas à la comptabilité, aux stocks, à la production ni à la paie : ce périmètre reste chez l'intégrateur.
Comment se facture une mission de renfort Odoo ?
Par lot, au forfait : un périmètre écrit, un prix et une date. Ce qui sort du périmètre est chiffré à part avant d'être réalisé. Pas de régie qui dérive et qu'on découvre à la facture.
Gérez-vous le droit de rétractation et les retours dans Odoo ?
Oui, et les deux régimes sont tenus séparés. La rétractation est une obligation légale : quatorze jours calendrier sans motif à compter de la réception du bien, articles VI.47 à VI.53 du Code de droit économique belge, et depuis le 19 juin 2026 une fonction de rétractation en ligne imposée par l'article 11 bis de la directive 2011/83/UE, inséré par la directive (UE) 2023/2673, avec accusé de réception horodaté sur support durable. Le retour commercial, lui, relève de la politique du commerçant et passe par un formulaire public qui crée le ticket, le retour de marchandise et la note de crédit en brouillon. Sur une rétractation totale, tous les paiements reçus sont remboursés, frais de livraison standard compris.
Que reste-t-il à l'agence à la fin de la mission ?
Des modules Odoo propres déposés dans un répertoire distinct de vos arbres git, du code commenté en français, les scripts de déploiement et la procédure de retour arrière, plus une base de connaissances qui documente chaque cause, chaque parade et chaque piège rencontré.
Sur quelle version d'Odoo intervenez-vous ?
Odoo 19, en production, boutique publique et point de vente sur la même instance. L'expérience de référence est une migration complète de V14 à V19, réalisée en six semaines.
Un dossier qui traîne depuis trois mois ?
Décrivez-le en cinq lignes. Je réponds sous 48 h : si c'est pour moi, ce que ça coûte et combien de temps ça prend. Si ce n'est pas pour moi, je le dis aussi, et je le dis vite.