TailorFitWMS — Warehouse Management System

Een op maat gebouwd magazijnsysteem voor Cookinglife: van order-intake en picking tot voorraadbeheer, verzendlabels en NetSuite ERP-synchronisatie — bijna twee jaar doorlopend in productie en onderhoud.

Warehouse management
PHP → Laravel-migratie
NetSuite ERP
Multi-carrier shipping

Rol & scope

Cookinglife (webshop in kookartikelen, met fysieke winkels erbij) draait het volledige magazijnproces op een systeem dat ik gebouwd heb en sindsdien doorontwikkel: order-intake, picking (single, batch, trolley en multi-level), voorraad- en bin-locatiebeheer, packing, verzendlabels en synchronisatie met NetSuite als ERP. Naast doorlopend onderhoud loopt er een migratie van het bestaande PHP-platform naar Laravel, module voor module, zonder downtime.

  • Order-intake tot en met verzending: één systeem voor het hele fulfilment-proces, gekoppeld aan NetSuite als bron van waarheid voor orders en voorraad.
  • Meerdere pickmethodes (single, batch, trolley, multi-level) en barcodescanning op de vloer via OnScan.js.
  • Multi-carrier verzendlabels via Sendcloud, met een eigen sweep-job die labels ophaalt zodra NetSuite ze na de "packed"-status heeft aangemaakt.
  • Koppelingen met Shopify, Magento en adres/telefoonnummer-normalisatie via Alumio.
  • Zero-downtime migratie naar Laravel: module per module (orders, picking, voorraad, shipping, NetSuite, admin) omgezet via nginx-routing, met instant rollback per module.
Bekijk live platform

Impact

  • Eén systeem voor het complete fulfilment-proces in plaats van losse tools voor picking, voorraad en verzending.
  • Een structurele NetSuite-fout bij kit-orders (producten die uit meerdere onderdelen bestaan) opgelost door voorraad automatisch over meerdere bins te laten alloceren in plaats van te eisen dat alles in één bin ligt.
  • Race condition tussen het aanmaken en ophalen van verzendlabels weggenomen met een terugkerende sync-job, zodat labels niet meer af en toe zoekraakten.
  • Een backup-verzendpad voor wanneer het WMS buiten kantooruren uitvalt, met reconciliatie zodat orders niet dubbel in de pick-queue verschijnen.
  • Bijna twee jaar doorlopend in productie (848 commits sinds augustus 2024) — geen greenfield-project maar een systeem dat blijft meegroeien met het bedrijf.

Architectuur

Drie lagen: een gedeelde Core, de TailorFitWMS-module zelf, en per-klant Overwrites — zodat bedrijfsspecifieke aanpassingen niet in de kern terechtkomen. Functionaliteit is opgebouwd als plugin-modules die per bedrijf aan- of uitgezet kunnen worden, met event-driven uitbreidingspunten. Data staat in MySQL (transactionele gegevens), MongoDB (documenten) en Redis (caching en sessies); achtergrondwerk (label-sync, NetSuite-taken) loopt via Laravel Horizon, met 100 tot 500 jobs per uur. De lopende migratie van het bestaande PHP-platform naar Laravel gebeurt module per module via nginx location-blocks, zodat elk onderdeel apart uitgerold en teruggedraaid kan worden zonder de rest te raken.

Modelkeuze

Geen AI/LLM in de kern van dit platform — fulfilment-logica (voorraad, picking, verzending, ERP-synchronisatie) vraagt om voorspelbaarheid en herleidbaarheid, niet om de flexibiliteit van een taalmodel. De belangrijkste "modelkeuze" hier is architectuur, niet AI: overstappen van het bestaande custom PHP-framework naar Laravel voor een groter ecosysteem aan tooling en onderhoudbaarheid, maar dat geleidelijk en module per module in plaats van een big-bang rewrite, om het risico op een systeem dat 24/7 een magazijn aanstuurt te beperken.

Kosten & latency

  • Productieomgeving draait op 5 Docker-services, elk met 8 CPU / 8GB toegewezen — dimensionering die past bij een systeem dat continu order- en NetSuite-verkeer verwerkt, niet bij piekbelasting alleen.
  • Verzendlabels zijn bewust asynchroon: NetSuite maakt het label pas aan na de "packed"-status, dus het systeem hoeft niet te wachten — een sweep-job haalt labels elke 3 minuten op in plaats van te pollen per order.
  • NetSuite-integratie (OAuth1 TBA, SOAP + RESTlet + REST) is de trager onderdeel van de keten; lokale magazijnacties zoals scannen en picken blijven daar los van draaien.

Lessons learned

Kit-orders (producten die uit meerdere onderdelen bestaan) liepen vast op NetSuite's VALID_LINE_ITEM_REQD-fout, omdat de voorraad van de onderdelen over meerdere bins verspreid stond terwijl NetSuite één bin per regel verwachtte. Opgelost door de RESTlet-integratie zo te herschrijven dat voorraad automatisch over meerdere bins alloceert. Verzendlabels bleken af en toe zoek te raken door een race condition tussen het moment dat NetSuite een label aanmaakt en het moment dat het systeem het ophaalt; een terugkerende sweep-job elke 3 minuten loste dat structureel op in plaats van een eenmalige patch. En voor de avonden en weekenden dat het WMS zelf uitvalt, is er een extern noodpad om toch te kunnen verzenden — met reconciliatie-logica die voorkomt dat diezelfde orders dubbel in de pick-queue verschijnen zodra het systeem weer online is.