TailorFitWMS — Warehouse Management System

A custom-built warehouse system for Cookinglife: from order intake and picking through to stock management, shipping labels and NetSuite ERP sync — nearly two years of continuous production use and maintenance.

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

Role & scope

Cookinglife (a cookware webshop with physical stores alongside it) runs its entire warehouse process on a system I built and have been developing ever since: order intake, picking (single, batch, trolley and multi-level), stock and bin-location management, packing, shipping labels and synchronisation with NetSuite as the ERP. Alongside ongoing maintenance, the existing PHP platform is being migrated to Laravel module by module, without downtime.

  • Order intake through to shipping: one system for the whole fulfilment process, connected to NetSuite as the source of truth for orders and stock.
  • Multiple picking methods (single, batch, trolley, multi-level) and barcode scanning on the floor via OnScan.js.
  • Multi-carrier shipping labels via Sendcloud, with a dedicated sweep job that fetches labels as soon as NetSuite creates them after the "packed" status.
  • Integrations with Shopify and Magento, plus address and phone-number normalisation through Alumio.
  • Zero-downtime migration to Laravel: module by module (orders, picking, stock, shipping, NetSuite, admin) via nginx routing, with instant per-module rollback.
View live platform

Impact

  • One system for the complete fulfilment process instead of separate tools for picking, stock and shipping.
  • Fixed a structural NetSuite error on kit orders (products made up of several components) by allocating stock automatically across multiple bins instead of requiring everything to sit in a single bin.
  • Removed a race condition between creating and fetching shipping labels with a recurring sync job, so labels no longer went missing every so often.
  • A backup shipping path for when the WMS goes down outside office hours, with reconciliation so orders don't reappear twice in the pick queue.
  • Nearly two years in continuous production (848 commits since August 2024) — not a greenfield project but a system that keeps growing with the business.

Architecture

Three layers: a shared Core, the TailorFitWMS module itself, and per-customer Overwrites — so company-specific tweaks never end up in the core. Functionality is built as plugin modules that can be switched on or off per company, with event-driven extension points. Data lives in MySQL (transactional records), MongoDB (documents) and Redis (caching and sessions); background work (label sync, NetSuite tasks) runs through Laravel Horizon, at 100 to 500 jobs per hour. The ongoing migration from the existing PHP platform to Laravel happens module by module via nginx location blocks, so each part can be rolled out and rolled back separately without touching the rest.

Model choice

No AI/LLM at the core of this platform — fulfilment logic (stock, picking, shipping, ERP sync) calls for predictability and traceability, not the flexibility of a language model. The main "model choice" here is architectural, not AI: moving off the existing custom PHP framework to Laravel for a larger tooling ecosystem and better maintainability, but doing it gradually and module by module rather than as a big-bang rewrite, to limit the risk to a system that runs a warehouse 24/7.

Cost & latency

  • The production environment runs on 5 Docker services, each allocated 8 CPU / 8GB — sizing that fits a system continuously processing order and NetSuite traffic, not one built for peak load alone.
  • Shipping labels are deliberately asynchronous: NetSuite only creates the label after the "packed" status, so the system doesn't have to wait — a sweep job collects labels every 3 minutes instead of polling per order.
  • The NetSuite integration (OAuth1 TBA, SOAP + RESTlet + REST) is the slower link in the chain; local warehouse actions such as scanning and picking keep running independently of it.

Lessons learned

Kit orders (products made up of several components) kept failing on NetSuite's VALID_LINE_ITEM_REQD error, because the components' stock was spread across multiple bins while NetSuite expected one bin per line. Solved by rewriting the RESTlet integration so stock allocates across multiple bins automatically. Shipping labels turned out to go missing occasionally due to a race condition between the moment NetSuite creates a label and the moment the system fetches it; a recurring sweep job every 3 minutes fixed that structurally instead of as a one-off patch. And for the evenings and weekends when the WMS itself goes down, there is an external emergency path so shipping can continue — with reconciliation logic that stops those same orders reappearing twice in the pick queue once the system comes back online.