Rôle & périmètre
Cookinglife (boutique en ligne d'articles de cuisine, doublée de magasins physiques) fait tourner l'intégralité de son processus d'entrepôt sur un système que j'ai construit et que je fais évoluer depuis : réception des commandes, picking (unitaire, par lot, par chariot et multi-niveaux), gestion des stocks et des emplacements, emballage, étiquettes d'expédition et synchronisation avec NetSuite comme ERP. En parallèle de la maintenance continue, la plateforme PHP existante est migrée vers Laravel, module par module, sans interruption de service.
- De la réception des commandes à l'expédition : un seul système pour tout le processus de fulfilment, relié à NetSuite comme source de vérité pour les commandes et les stocks.
- Plusieurs méthodes de picking (unitaire, par lot, par chariot, multi-niveaux) et lecture de codes-barres sur le terrain via OnScan.js.
- Étiquettes d'expédition multi-transporteurs via Sendcloud, avec une tâche de balayage dédiée qui récupère les étiquettes dès que NetSuite les crée après le statut « packed ».
- Intégrations avec Shopify et Magento, plus la normalisation des adresses et des numéros de téléphone via Alumio.
- Migration vers Laravel sans interruption : module par module (commandes, picking, stocks, expédition, NetSuite, admin) via le routage nginx, avec rollback immédiat par module.
Impact
- Un seul système pour l'ensemble du processus de fulfilment au lieu d'outils séparés pour le picking, les stocks et l'expédition.
- Correction d'une erreur structurelle NetSuite sur les commandes de kits (produits composés de plusieurs éléments) en allouant automatiquement le stock sur plusieurs emplacements au lieu d'exiger que tout se trouve dans un seul.
- Suppression d'une race condition entre la création et la récupération des étiquettes d'expédition grâce à une tâche de synchronisation récurrente, si bien que les étiquettes ne se perdent plus de temps en temps.
- Un circuit d'expédition de secours pour les pannes du WMS en dehors des heures de bureau, avec réconciliation pour éviter que des commandes réapparaissent en double dans la file de picking.
- Près de deux ans en production continue (848 commits depuis août 2024) — pas un projet greenfield mais un système qui continue de grandir avec l'entreprise.
Architecture
Trois couches : un Core partagé, le module TailorFitWMS lui-même, et des Overwrites par client — pour que les adaptations propres à une entreprise n'atterrissent pas dans le noyau. Les fonctionnalités sont construites en modules plugin activables ou désactivables par entreprise, avec des points d'extension événementiels. Les données vivent dans MySQL (transactionnel), MongoDB (documents) et Redis (cache et sessions) ; le travail en arrière-plan (synchronisation des étiquettes, tâches NetSuite) passe par Laravel Horizon, à raison de 100 à 500 jobs par heure. La migration en cours de la plateforme PHP existante vers Laravel se fait module par module via des blocs location nginx, si bien que chaque partie peut être déployée et annulée séparément sans toucher au reste.
Choix du modèle
Pas d'IA/LLM au cœur de cette plateforme — la logique de fulfilment (stocks, picking, expédition, synchronisation ERP) exige de la prévisibilité et de la traçabilité, pas la souplesse d'un modèle de langage. Le principal « choix de modèle » est ici architectural et non lié à l'IA : quitter le framework PHP maison existant pour Laravel afin de bénéficier d'un écosystème d'outils plus large et d'une meilleure maintenabilité, mais progressivement et module par module plutôt qu'en réécriture big-bang, pour limiter le risque sur un système qui pilote un entrepôt 24 h/24.
Coûts & latence
- L'environnement de production tourne sur 5 services Docker, chacun avec 8 CPU / 8 Go alloués — un dimensionnement adapté à un système qui traite en continu du trafic de commandes et NetSuite, pas seulement des pics.
- Les étiquettes d'expédition sont volontairement asynchrones : NetSuite ne crée l'étiquette qu'après le statut « packed », le système n'a donc pas à attendre — une tâche de balayage récupère les étiquettes toutes les 3 minutes au lieu d'interroger commande par commande.
- L'intégration NetSuite (OAuth1 TBA, SOAP + RESTlet + REST) est le maillon le plus lent de la chaîne ; les actions locales en entrepôt comme le scan et le picking continuent de tourner indépendamment.
Enseignements
Les commandes de kits (produits composés de plusieurs éléments) échouaient sur l'erreur VALID_LINE_ITEM_REQD de NetSuite, parce que le stock des composants était réparti sur plusieurs emplacements alors que NetSuite attendait un emplacement par ligne. Résolu en réécrivant l'intégration RESTlet pour que le stock s'alloue automatiquement sur plusieurs emplacements. Les étiquettes d'expédition se perdaient parfois à cause d'une race condition entre le moment où NetSuite crée une étiquette et celui où le système la récupère ; une tâche de balayage toutes les 3 minutes a réglé cela structurellement plutôt qu'avec un correctif ponctuel. Et pour les soirées et week-ends où le WMS lui-même tombe, il existe un circuit d'urgence externe permettant de continuer à expédier — avec une logique de réconciliation qui empêche ces mêmes commandes de réapparaître en double dans la file de picking une fois le système revenu en ligne.