Rol & alcance
Cookinglife (tienda online de artículos de cocina, con tiendas físicas además) gestiona todo su proceso de almacén sobre un sistema que construí y que sigo desarrollando desde entonces: recepción de pedidos, picking (individual, por lotes, con carro y multinivel), gestión de stock y ubicaciones, empaquetado, etiquetas de envío y sincronización con NetSuite como ERP. Junto al mantenimiento continuo, la plataforma PHP existente se está migrando a Laravel módulo a módulo, sin tiempo de inactividad.
- De la recepción del pedido al envío: un único sistema para todo el proceso de fulfilment, conectado a NetSuite como fuente de verdad para pedidos y stock.
- Varios métodos de picking (individual, por lotes, con carro, multinivel) y escaneo de códigos de barras en planta mediante OnScan.js.
- Etiquetas de envío multitransportista vía Sendcloud, con un trabajo de barrido propio que recoge las etiquetas en cuanto NetSuite las crea tras el estado «packed».
- Integraciones con Shopify y Magento, además de normalización de direcciones y números de teléfono mediante Alumio.
- Migración a Laravel sin caídas: módulo a módulo (pedidos, picking, stock, envíos, NetSuite, admin) mediante enrutado en nginx, con rollback inmediato por módulo.
Impacto
- Un único sistema para todo el proceso de fulfilment en lugar de herramientas sueltas para picking, stock y envíos.
- Se resolvió un error estructural de NetSuite en pedidos de kits (productos compuestos por varias piezas) asignando el stock automáticamente entre varias ubicaciones en lugar de exigir que todo estuviera en una sola.
- Se eliminó una condición de carrera entre la creación y la recogida de etiquetas de envío con un trabajo de sincronización recurrente, de modo que las etiquetas ya no se perdían de vez en cuando.
- Una ruta de envío de respaldo para cuando el WMS cae fuera del horario de oficina, con reconciliación para que los pedidos no reaparezcan duplicados en la cola de picking.
- Casi dos años en producción continua (848 commits desde agosto de 2024) — no un proyecto greenfield, sino un sistema que sigue creciendo con la empresa.
Arquitectura
Tres capas: un Core compartido, el propio módulo TailorFitWMS y Overwrites por cliente — para que los ajustes específicos de una empresa no acaben en el núcleo. La funcionalidad está construida como módulos plugin que se activan o desactivan por empresa, con puntos de extensión basados en eventos. Los datos viven en MySQL (datos transaccionales), MongoDB (documentos) y Redis (caché y sesiones); el trabajo en segundo plano (sincronización de etiquetas, tareas de NetSuite) corre sobre Laravel Horizon, con 100 a 500 jobs por hora. La migración en curso de la plataforma PHP existente a Laravel se hace módulo a módulo mediante bloques location de nginx, de forma que cada parte se despliega y se revierte por separado sin tocar el resto.
Elección de modelo
Sin IA/LLM en el núcleo de esta plataforma: la lógica de fulfilment (stock, picking, envíos, sincronización con el ERP) exige previsibilidad y trazabilidad, no la flexibilidad de un modelo de lenguaje. La «elección de modelo» principal aquí es arquitectónica, no de IA: pasar del framework PHP propio existente a Laravel por un ecosistema de herramientas mayor y mejor mantenibilidad, pero de forma gradual y módulo a módulo en lugar de una reescritura big-bang, para limitar el riesgo en un sistema que dirige un almacén 24/7.
Costes & latencia
- El entorno de producción corre sobre 5 servicios Docker, cada uno con 8 CPU / 8 GB asignados — un dimensionado acorde a un sistema que procesa tráfico de pedidos y NetSuite de forma continua, no solo picos.
- Las etiquetas de envío son deliberadamente asíncronas: NetSuite solo crea la etiqueta tras el estado «packed», así que el sistema no tiene que esperar — un trabajo de barrido recoge etiquetas cada 3 minutos en lugar de consultar pedido a pedido.
- La integración con NetSuite (OAuth1 TBA, SOAP + RESTlet + REST) es el eslabón más lento de la cadena; las acciones locales de almacén como escanear y hacer picking siguen funcionando al margen.
Lecciones aprendidas
Los pedidos de kits (productos compuestos por varias piezas) fallaban una y otra vez con el error VALID_LINE_ITEM_REQD de NetSuite, porque el stock de los componentes estaba repartido entre varias ubicaciones mientras NetSuite esperaba una ubicación por línea. Se resolvió reescribiendo la integración RESTlet para que el stock se asigne automáticamente entre varias ubicaciones. Las etiquetas de envío se perdían de vez en cuando por una condición de carrera entre el momento en que NetSuite crea una etiqueta y el momento en que el sistema la recoge; un trabajo de barrido recurrente cada 3 minutos lo resolvió de forma estructural en lugar de con un parche puntual. Y para las tardes y fines de semana en que el propio WMS cae, existe una vía de emergencia externa para poder seguir enviando — con lógica de reconciliación que evita que esos mismos pedidos reaparezcan duplicados en la cola de picking cuando el sistema vuelve.