Sync stock multi-canal Shopify ↔ Odoo ↔ marketplaces : l'architecture qui tient
Comment éviter les ventes en double quand Shopify, retail POS, Veepee et Mirakl tirent sur le même stock. Architecture event-driven, locks de réservation, gestion des conflits.
Multi-canal, c’est joli sur la slide. En production, c’est le sujet qui tue le plus de marques : quelqu’un a vendu sur Veepee un produit qu’on n’a plus en stock parce que Shopify l’a écoulé il y a 90 secondes.
Voici l’architecture que j’utilise quand je connecte Shopify + retail POS + Veepee + Mirakl + Amazon sur un même Odoo.
Le problème : la latence d’événements
Imagine un produit avec 1 unité en stock. À 14h00 :
- 14:00:01.000 → un client achète sur Shopify retail (POS)
- 14:00:01.500 → un autre client clique “Acheter” sur Veepee
- 14:00:02.300 → un troisième client clique “Acheter” sur Amazon
Si chaque canal “vérifie” le stock avant de finaliser, et que chaque canal “réserve” après commande, tu as 3 commandes confirmées pour 1 unité.
C’est de la race condition classique. Sans mécanisme de réservation atomique, tu vendras toujours en double.
L’architecture qui tient
Principe : Odoo = source unique du stock, jamais Shopify
C’est la règle non-négociable. Odoo gère tous les emplacements physiques. Shopify, Veepee, Amazon ne sont que des canaux de réservation/déstockage.
Stock physique réel
│
▼
┌─────────┐
│ Odoo │ ← source de vérité (par emplacement)
└────┬────┘
│ push stock disponible (par canal)
▼
┌────────────────────────────────────┐
│ Connecteurs (Shopify, Veepee…) │
│ appliquent les **règles de stock** │
│ par canal/locale/groupe client │
└────────────────────────────────────┘
│ ventes confirmées
▼
┌─────────┐
│ Odoo │ ← écriture de la vente, mise à jour du stock
└─────────┘Allocation par canal
Chaque produit Odoo a un stock total. Chaque canal voit une quote-part allocée :
- Shopify retail : 30 %
- Shopify e-commerce : 40 %
- Veepee : 15 %
- Mirakl : 10 %
- Buffer (réserve) : 5 %
Quand Shopify e-commerce vend, il décrémente sa quote-part Shopify e-commerce, pas le stock total. Si la quote-part atteint zéro, il dit “rupture” même si le stock total n’est pas encore vide. Tu évites les doubles ventes inter-canaux.
Réallocation automatique tous les jours à 4h du matin selon les ventes des 7 derniers jours (le canal qui vend mieux récupère plus).
L’implémentation technique
1. Odoo expose un endpoint stock par canal
Tu crées dans Odoo un module Studio + OWL custom qui :
- Lit le stock par emplacement (table
stock.quant) - Applique la matrice d’allocation (ratios par canal)
- Expose un endpoint
/api/stock/by-channel/<sku>qui retourne le stock dispo pour chaque canal
2. Les connecteurs PULL (et PUSH)
Shopify : webhook inventory_levels/update pour le stock entrant (réceptions Odoo) + GraphQL inventoryAdjustQuantity pour le stock sortant.
Veepee : pull toutes les 5 minutes via leur Marchand API + push via leur API d’allocation.
Mirakl : push de stock toutes les 15 minutes via Mirakl Connect.
Amazon : push via SP-API toutes les 15 minutes.
Tout passe par n8n self-hosted orchestré.
3. Les locks de réservation (le truc magique)
Le vrai secret, ce sont les locks atomiques sur Odoo :
# pseudo-code Odoo
def reserve_stock(sku, channel, qty):
with self.env.cr.savepoint():
self._cr.execute(
"SELECT qty_allocated FROM stock_channel_alloc "
"WHERE sku = %s AND channel = %s "
"FOR UPDATE", # ← lock atomique
[sku, channel]
)
current = self._cr.fetchone()[0]
if current < qty:
raise UserError("Insufficient stock")
self._cr.execute(
"UPDATE stock_channel_alloc "
"SET qty_allocated = qty_allocated - %s "
"WHERE sku = %s AND channel = %s",
[qty, sku, channel]
)
# commit auto à la fin du savepointLe FOR UPDATE PostgreSQL bloque la ligne pendant la transaction. Si deux requêtes simultanées arrivent, la 2e attend la 1ère. Pas de doubles réservations possibles.
C’est ce mécanisme qui te protège des race conditions, et Shopify ou Mirakl natifs ne le font pas.
Les pièges classiques
Piège 1 : se reposer uniquement sur Shopify “policy”
Shopify a des “inventory policies” (continue / deny). En multi-canal, ne JAMAIS mettre continue. Sinon Shopify accepte des commandes même quand stock = 0 selon Odoo.
Piège 2 : pousser Odoo → Shopify avec un cron de 30 minutes
À 30 min de latence, sur un produit qui vend bien, tu auras 5-15 commandes en double par jour. Cron à 30 minutes = pas acceptable. Webhook event-driven obligatoire : push stock Odoo → Shopify dès qu’il y a un mouvement.
Piège 3 : ne pas gérer les retours
Quand un client retourne, le stock revient. Ne JAMAIS le re-pousser brut sur Shopify le jour même : la règle business est “stock retour validé J+3 par contrôle qualité”. Sinon tu vends de la marchandise endommagée.
Piège 4 : oublier le buffer réserve
Si tu alloues 100 % aux canaux, à la première rupture tu n’as plus rien pour gérer le SAV (échange/retours), les commerciaux qui shippent à des VIP, les cadeaux salons. Toujours 5-10 % en buffer manuel.
Coûts indicatifs
| Composant | Cap-ex initial | Op-ex annuel |
|---|---|---|
| Module Odoo allocation channel + locks | 6-12 K€ | 1 K€ |
| Connector Shopify ↔ Odoo (event-driven) | 8-15 K€ | 2 K€ |
| Connector Veepee ↔ Odoo | 4-8 K€ | 1 K€ |
| Connector Mirakl ↔ Odoo | 4-8 K€ | 1 K€ |
| Orchestration n8n self-hosted | 3-5 K€ | 0.5 K€ |
| Monitoring + alertes (Datadog/Grafana) | 2 K€ | 1 K€ |
| Total | 27-50 K€ | 6-7 K€ |
À comparer aux 15-30 K€/an de pertes sur ventes en double, refunds forcés, frais customer service, perte de réputation marketplace (note Veepee/Mirakl). ROI net positif dès l’année 1.
Ce que je vois rater le plus souvent
- ❌ Connector Shopify → Odoo ASYNCHRONE sans queue → perte d’événements
- ❌ Pas de monitoring de l’écart stock Odoo / Shopify (objectif < 0.5 %)
- ❌ Réallocation des quotes-parts canal manuelle, jamais faite
- ❌ Pas de plan B en cas de panne Veepee API (queue les events 24-48h)
Mon framework décision
Tu regardes 3 chiffres :
- Combien de canaux ? — 1-2 canaux : architecture simple, native Shopify suffit. 3+ canaux : architecture event-driven obligatoire.
- Quelle marge produit ? — Si marge < 30 %, chaque vente double t’écorche. Si marge > 60 %, tu peux te permettre quelques ratés.
- Volume retail vs e-com ? — Retail = stock physique localisé, demande des locations Shopify Locations + Odoo emplacements. E-com pur = simpler.
Audit
Si tu as déjà une sync stock multi-canal qui gigle (commandes en double, stock incohérent, écarts inventaire) — l’audit gratuit que je fais te sort un diagnostic complet de tes flux et un plan d’action sous 72 h.
Bâtissons votre architecture ensemble.
Audit gratuit sous 24 h ouvrées. Devis chiffré sous 72 h. Aucun engagement derrière.
Réserver un audit