Se rendre au contenu

Odoo 20 hors ligne : quand deux saisies se croisent, laquelle gagne ?

On a rejoué notre test de conflits sur Odoo 20.0. Ce qu'Odoo fait bien, où il écrase sans prévenir, comment nos applications traitent le même cas, et cinq tests à faire sur la vôtre.
23 octobre 2026 par
Odoo 20 hors ligne : quand deux saisies se croisent, laquelle gagne ?

Odoo 20 sait enregistrer hors connexion. Au retour du réseau, la dernière synchronisation l'emporte, même quand elle porte la saisie la plus ancienne. Nos applications mobiles pour Odoo relisent Odoo avant d'écrire : quand deux saisies se croisent, rien n'est écrasé et l'utilisateur choisit.

Le test tient en trois lignes. L'utilisateur A passe hors ligne et enregistre « AAA ». L'utilisateur B, en ligne, enregistre « BBB » quelques secondes plus tard. A se reconnecte. Valeur finale : « AAA ». La saisie de B a disparu, et personne n'a reçu d'alerte.

On avait mesuré ce résultat sur Odoo 19.4 en juillet, dans notre audit du mode hors connexion. Le 23 octobre, on a rejoué le même protocole sur Odoo 20.0 : Odoo 20 hors ligne ne change pas ce point.

Ce que fait Odoo 20 hors ligne, et ce qu'il fait bien

Le test montre aussi ce qui marche.

  • Deux utilisateurs modifient des champs différents : les deux modifications survivent, parce qu'Odoo 20 n'envoie que les champs modifiés.
  • La fiche est supprimée pendant le hors ligne : la synchronisation échoue avec une MissingError. Un badge rouge « Sync issues » apparaît en haut de l'écran, la modification reste dans la file et l'utilisateur peut l'abandonner.
  • Le même champ des deux côtés : la dernière synchronisation gagne, sans avertissement. Le chatter montre les deux changements dans l'ordre, mais il date la saisie hors ligne à l'heure de la synchronisation. Dans notre test, le technicien a saisi « AAA » huit secondes avant que le bureau saisisse « BBB ». Le chatter affiche « BBB → AAA » deux secondes après la saisie du bureau : à la lecture, c'est le technicien qui semble avoir corrigé le bureau.

Dans les sources de la 20.0 Community, les modifications sont mises en file dans IndexedDB puis rejouées une par une comme des appels web_save ordinaires (offline_plugin.js). Aucun contrôle de version du type write_date n'apparaît sur ce chemin. C'est exactement ce qu'on avait relevé dans la 19.4 : web_save exécute un write() nu.

Pour un utilisateur de bureau qui perd le Wi-Fi dix minutes, c'est acceptable : la probabilité qu'un collègue touche la même fiche dans ce laps de temps est faible. Pour un technicien qui passe sa journée en cave ou en zone blanche, elle ne l'est plus.

Le même test sur nos applications

Nos applications synchronisent une base SQLite locale avec Odoo. On leur a fait passer le scénario AAA/BBB, puis ses deux variantes. Le tableau met les résultats à côté de ceux d'Odoo 20.

ScénarioOdoo 20 hors ligneApplications ASKA
A et B modifient deux champs différents Les deux gardées Les deux gardées
A et B modifient le même champ La dernière synchro gagne, sans alerte Conflit signalé, rien n'est écrasé
La fiche est supprimée dans Odoo Erreur en file, à abandonner Conflit signalé, saisie gardée

Sur les champs différents, Odoo 20 et nos applications font la même chose : seuls les champs modifiés partent. La différence se joue sur le même champ. Un technicien reste hors ligne des heures entières, la probabilité qu'un collègue touche la même fiche n'est plus faible, et on ne laisse pas la dernière synchronisation décider à sa place.

Comment on détecte un conflit avant d'écrire

La technique porte un nom : le verrou optimiste. On ne bloque personne pendant la saisie. On vérifie, au moment d'écrire, que personne n'a touché à la donnée entre-temps. Dans notre framework, ça se joue en trois temps (figure 1).

1. Noter ce qui change, et ce qu'il y avait avant. Un déclencheur SQLite enregistre chaque champ modifié sur le téléphone, avec sa valeur d'avant et la write_date que la fiche portait dans Odoo au dernier téléchargement.

2. Relire Odoo juste avant d'envoyer. Une seule requête search_read par lot relit la write_date et les champs concernés.

3. Comparer, puis écrire ou s'arrêter.

  • write_date inchangée : la modification part.
  • Odoo a bougé, mais sur d'autres champs : les champs de A partent, ceux de B restent.
  • Le même champ a changé des deux côtés, avec des valeurs différentes : c'est un conflit. Rien n'est écrit pour cette fiche, la saisie reste sur le téléphone, et le prochain téléchargement ne l'écrase pas.
  • La fiche n'existe plus dans Odoo : conflit de suppression, la saisie est gardée.
Étape 1 · sur le téléphone
Noter
Chaque champ modifié, sa valeur d'avant et la write_date d'Odoo au dernier téléchargement.
Étape 2 · avant l'envoi
Relire Odoo
Un search_read par lot : write_date et champs concernés.
Étape 3 · fiche par fiche
Comparer
Puis écrire, ou s'arrêter et garder la saisie.
Odoo inchangé
La modification part.
Autres champs
Les champs de A partent, ceux de B restent.
Même champ
Conflit : rien n'est écrit, la saisie reste sur le téléphone.
Fiche supprimée
Conflit de suppression, la saisie est gardée.
Figure 1. Le contrôle avant envoi : trois étapes, quatre issues. Deux issues écrivent dans Odoo, deux s'arrêtent et demandent un choix.

Le conflit est stocké dans la base locale. Il survit à un redémarrage de l'application, ce qui compte sur un terminal qu'on éteint le soir.

Les modèles Odoo sans write_date n'ont pas ce contrôle. Pour eux, seuls les champs modifiés partent, ce qui règle déjà le cas des champs différents.

Le piège des faux conflits

Au troisième faux conflit de la journée, un utilisateur appuie sur le premier bouton venu sans lire. Le framework écarte donc les cas où Odoo reformule une valeur à l'enregistrement, ce qui ferait croire qu'elle a changé (figure 2).

Champ HTML
L'app envoie
Merci
Odoo relit
<p>Merci</p>
Comparaison brute : faux conflit
Framework ASKA : comparé après html_normalize
Nombre à 2 décimales
L'app envoie
2/3
Odoo relit
0,67
Comparaison brute : faux conflit
Framework ASKA : comparé après l'arrondi du champ
Figure 2. Deux valeurs qu'Odoo reformule à l'enregistrement, et qui feraient croire à un changement.

Pour le HTML, le framework compare les valeurs sous la forme qu'Odoo stocke, relevée dans ses sources. Pour les nombres, il lit les décimales de chaque champ par fields_get (une fois par modèle) et compare après l'arrondi d'Odoo (float_round).

Il reste des faux conflits possibles, par exemple sur un montant arrondi selon la devise ou une valeur qu'Odoo recalcule à l'enregistrement. On les a documentés. La règle qu'on s'est donnée : dans le doute, un conflit en trop, jamais un écrasement.

Ce que voit l'utilisateur

Sans écran pour le résoudre, un conflit bloque la fiche sur le téléphone, et l'utilisateur ne le sait pas. Chaque application a donc reçu un écran dédié (figure 3), qui commence par une phrase :

« Quelqu'un a modifié les mêmes champs dans Odoo pendant que vous travailliez hors ligne. Rien n'a été écrasé : choisissez la version à garder. »

Pour chaque fiche, l'écran montre les deux valeurs côte à côte et propose deux boutons : Garder ma version ou Garder Odoo. Pour une fiche supprimée dans Odoo : Le recréer ou Le supprimer ici.

Conflits de synchronisation
Quelqu'un a modifié les mêmes champs dans Odoo pendant que vous travailliez hors ligne. Rien n'a été écrasé : choisissez la version à garder.
INTERVENTION
INT-0412 · Avenue du Lac
N° de compteur
Sur le téléphone
AAA
Dans Odoo
BBB
Garder ma version
Garder Odoo
Figure 3. L'écran de conflits, reconstitué à partir de nos applications. Les deux valeurs sont côte à côte, le choix tient en un geste.

Dans une application de logistique, le choix a une conséquence de plus. Un préparateur peut avoir validé un transfert sur le téléphone, alors qu'un mouvement de ce transfert est en conflit sur la quantité. L'écran le dit avant le choix : avec « Garder Odoo », l'opération sera validée avec la quantité d'Odoo ; avec « Garder ma version », avec celle du téléphone.

Cet écran est dans nos cinq applications publiées sur l'App Store et Google Play : Field Service, Logistics, Events, Sales Force et Suite.

Avertir, fusionner ou laisser gagner la dernière synchro ?

C'est la question de fond de tout mode hors ligne, et la réponse dépend du champ (figure 4).

StratégieQuand l'utiliserExemple
Avertir et laisser l'humain choisirSaisie manuelle qui a un sens métierAdresse, quantité comptée, remarque d'intervention
FusionnerValeur cumulative ou listeÉtiquettes ajoutées, quantités en delta, pièces jointes
Laisser gagner la dernière synchroDonnée technique sans valeur pour l'utilisateurHorodatage de consultation, préférence d'affichage
Quelqu'un a saisi cette valeur à la main, et elle a un sens métier ?
Oui
Avertir
L'humain choisit. Adresse, quantité comptée, remarque.
Non
C'est une liste ou un cumul ?
Oui
Fusionner
Étiquettes, deltas, pièces jointes.
Non
Dernière synchro
Horodatage, préférence d'affichage.
Figure 4. Deux questions suffisent pour choisir la stratégie d'un champ. Sur une application de terrain, la plupart des champs s'arrêtent à la première.

Pour une application de terrain branchée sur Odoo, la majorité des champs tombe dans la première ligne. C'est pour ça qu'on a choisi d'avertir par défaut, et de fusionner là où c'est possible. Exemple concret : nos applications ajoutent ou retirent une étiquette ((4, id) et (3, id) dans l'ORM d'Odoo) au lieu de remplacer toute la liste.

Cinq questions à poser à votre application hors ligne

Que votre application vienne de chez nous, d'un autre éditeur ou d'Odoo lui-même, ces cinq questions se vérifient en une demi-heure avec deux appareils.

  1. Envoie-t-elle seulement les champs modifiés ? Test : A change un champ hors ligne, B en change un autre en ligne. Les deux doivent survivre.
  2. Relit-elle Odoo avant d'écrire ? Test : le scénario AAA/BBB. Vous devez voir un avertissement, pas « AAA » en silence.
  3. Que devient une saisie sur une fiche supprimée ? Elle ne doit ni disparaître en silence, ni bloquer la file pour toujours.
  4. Crie-t-elle au loup ? Modifiez un champ HTML ou un nombre à décimales sans conflit réel. Aucun avertissement ne doit apparaître.
  5. Le conflit survit-il à un redémarrage ? Éteignez l'appareil avant de résoudre. Le conflit doit être toujours là au rallumage.

FAQ

Odoo 20 fonctionne-t-il hors ligne ?

Oui, pour enregistrer des modifications sur des fiches déjà ouvertes. Les modifications sont mises en file puis rejouées au retour du réseau. En revanche, aucun contrôle de conflit n'est fait au rejeu : la dernière synchronisation gagne.

Que se passe-t-il si deux personnes modifient la même donnée dans Odoo, l'une hors ligne ?

Avec le mode hors ligne d'Odoo 20, la valeur envoyée en dernier remplace l'autre, même si elle a été saisie avant. Une application qui pratique le verrou optimiste relit la write_date avant d'écrire et signale le conflit au lieu d'écraser.

Comment éviter les faux conflits de synchronisation avec Odoo ?

En comparant les valeurs telles qu'Odoo les stocke : HTML normalisé, nombres arrondis selon les décimales du champ. Sans cette étape, une simple remarque ou une quantité à décimales déclenche des alertes à tort.

Conclusion

Une saisie écrasée sans alerte ne laisse aucune trace : personne chez vous ne sait combien vos techniciens en ont perdu ce mois-ci.

Si vos équipes saisissent dans Odoo 20 hors ligne, ou dans une application mobile branchée sur Odoo, qu'elle vienne de chez nous ou non, réservez 30 minutes avec nous. On rejoue le scénario AAA/BBB sur votre outil, avec deux de vos appareils. À la fin, vous savez si une saisie de terrain peut disparaître chez vous sans que personne ne soit prévenu, et vous recevez le résultat par écrit. Le diagnostic est offert.

Réserver mon diagnostic de 30 minutes


À lire aussi

Peppol dans Odoo 20 : le guide pour une PME du Hainaut
Obligatoire depuis le 1er janvier 2026 entre entreprises belges. Ce que la loi demande, comment Odoo 20 envoie et reçoit par Peppol, et les erreurs de configuration à corriger avant le premier envoi.