Plan maestro de desarrollo

Aplicación web logística SaaS multi-tenant

Documento consolidado para desarrollar una aplicación web especializada en manejo logístico, inventario, almacenes, cotizaciones, pedidos, fabricación, picking, despacho, usuarios, roles, permisos y PWA, usando Node.js + MySQL en servidor compartido.

Node.js + Express EJS + Phoenix MySQL compartido Multi-tenant Suscripción PWA Dark theme Responsive

1. Enfoque general del proyecto

La aplicación debe nacer como un SaaS logístico multi-tenant para empresas que manejan inventario, almacenes, pedidos, fabricación, cotización y despacho.

El punto diferencial será que el sistema no tratará todos los productos igual. Cada producto podrá tener un perfil de control logístico.

  • Producto simple
  • Producto serializado
  • Producto con lote
  • Producto con vencimiento
  • Producto con variantes
  • Pack
  • Producto compuesto por fórmula
  • Producto fabricado bajo proceso
  • Producto con control mixto

Ejemplos

Producto Control requerido
LaptopNúmero de serie
MedicamentoLote + vencimiento
PernosLote / medidas / fabricación
CamisetaTalla + color
Kit de instalaciónPack
Anclaje químicoFórmula / fabricación
Caja de productosPack / unidad / caja

2. Estructura recomendada del proyecto

Estructura preparada para crecer, pero manteniendo simplicidad para un solo programador. Incluye módulos de usuarios, roles, permisos e invitaciones para los suscriptores.

logistica-saas/
│
├── app.js
├── server.js
├── package.json
├── .env
├── .env.example
├── README.md
│
├── docs/
│   ├── 00-PROJECT-VISION.md
│   ├── 01-ARCHITECTURE.md
│   ├── 02-MULTI-TENANCY.md
│   ├── 03-DATABASE-STANDARDS.md
│   ├── 04-MODULES-SCOPE.md
│   ├── 05-PRODUCT-CONTROL-MODEL.md
│   ├── 06-INVENTORY-WORKFLOW.md
│   ├── 07-ANCHORS-MANUFACTURING.md
│   ├── 08-PHOENIX-UI-GUIDE.md
│   ├── 09-PWA-GUIDE.md
│   ├── 10-CODEX-INSTRUCTIONS.md
│   ├── 11-ROADMAP.md
│   └── 12-USERS-ROLES-PERMISSIONS.md
│
├── src/
│   ├── config/
│   │   ├── app.config.js
│   │   ├── db.config.js
│   │   ├── auth.config.js
│   │   ├── upload.config.js
│   │   └── subscription.config.js
│   │
│   ├── database/
│   │   ├── connection.js
│   │   ├── migrations/
│   │   │   ├── 001_create_tenants.sql
│   │   │   ├── 002_create_users.sql
│   │   │   ├── 003_create_subscriptions.sql
│   │   │   ├── 004_create_products.sql
│   │   │   ├── 005_create_warehouses.sql
│   │   │   ├── 006_create_inventory.sql
│   │   │   ├── 007_create_quotes_orders.sql
│   │   │   ├── 008_create_manufacturing.sql
│   │   │   ├── 009_create_dispatch.sql
│   │   │   ├── 010_create_audit_logs.sql
│   │   │   └── 011_create_users_roles_permissions.sql
│   │   │
│   │   └── seeds/
│   │       ├── seed_plans.sql
│   │       ├── seed_roles.sql
│   │       ├── seed_permissions.sql
│   │       ├── seed_countries.sql
│   │       ├── seed_currencies.sql
│   │       └── seed_tax_rates.sql
│   │
│   ├── core/
│   │   ├── errors/
│   │   │   ├── AppError.js
│   │   │   └── errorHandler.js
│   │   │
│   │   ├── helpers/
│   │   │   ├── asyncHandler.js
│   │   │   ├── date.helper.js
│   │   │   ├── money.helper.js
│   │   │   ├── pagination.helper.js
│   │   │   └── tenant.helper.js
│   │   │
│   │   ├── middlewares/
│   │   │   ├── auth.middleware.js
│   │   │   ├── tenant.middleware.js
│   │   │   ├── subscription.middleware.js
│   │   │   ├── permission.middleware.js
│   │   │   ├── validate.middleware.js
│   │   │   ├── csrf.middleware.js
│   │   │   ├── rateLimit.middleware.js
│   │   │   └── audit.middleware.js
│   │   │
│   │   ├── services/
│   │   │   ├── audit.service.js
│   │   │   ├── file.service.js
│   │   │   ├── mail.service.js
│   │   │   ├── notification.service.js
│   │   │   └── sequence.service.js
│   │   │
│   │   └── validators/
│   │       └── common.validator.js
│   │
│   ├── modules/
│   │   ├── auth/
│   │   │   ├── auth.routes.js
│   │   │   ├── auth.controller.js
│   │   │   ├── auth.service.js
│   │   │   ├── auth.model.js
│   │   │   └── auth.validator.js
│   │   │
│   │   ├── tenants/
│   │   │   ├── tenants.routes.js
│   │   │   ├── tenants.controller.js
│   │   │   ├── tenants.service.js
│   │   │   ├── tenants.model.js
│   │   │   └── tenants.validator.js
│   │   │
│   │   ├── subscriptions/
│   │   │   ├── subscriptions.routes.js
│   │   │   ├── subscriptions.controller.js
│   │   │   ├── subscriptions.service.js
│   │   │   └── subscriptions.model.js
│   │   │
│   │   ├── users/
│   │   │   ├── users.routes.js
│   │   │   ├── users.controller.js
│   │   │   ├── users.service.js
│   │   │   ├── users.model.js
│   │   │   └── users.validator.js
│   │   │
│   │   ├── roles/
│   │   │   ├── roles.routes.js
│   │   │   ├── roles.controller.js
│   │   │   ├── roles.service.js
│   │   │   ├── roles.model.js
│   │   │   ├── permissions.service.js
│   │   │   └── roles.validator.js
│   │   │
│   │   ├── settings/
│   │   │   ├── settings.routes.js
│   │   │   ├── settings.controller.js
│   │   │   ├── settings.service.js
│   │   │   └── settings.model.js
│   │   │
│   │   ├── customers/
│   │   │   ├── customers.routes.js
│   │   │   ├── customers.controller.js
│   │   │   ├── customers.service.js
│   │   │   └── customers.model.js
│   │   │
│   │   ├── suppliers/
│   │   │   ├── suppliers.routes.js
│   │   │   ├── suppliers.controller.js
│   │   │   ├── suppliers.service.js
│   │   │   └── suppliers.model.js
│   │   │
│   │   ├── products/
│   │   │   ├── products.routes.js
│   │   │   ├── products.controller.js
│   │   │   ├── products.service.js
│   │   │   ├── products.model.js
│   │   │   ├── productControls.service.js
│   │   │   ├── productVariants.service.js
│   │   │   ├── productPacks.service.js
│   │   │   └── products.validator.js
│   │   │
│   │   ├── warehouses/
│   │   │   ├── warehouses.routes.js
│   │   │   ├── warehouses.controller.js
│   │   │   ├── warehouses.service.js
│   │   │   ├── warehouses.model.js
│   │   │   └── locations.service.js
│   │   │
│   │   ├── inventory/
│   │   │   ├── inventory.routes.js
│   │   │   ├── inventory.controller.js
│   │   │   ├── inventory.service.js
│   │   │   ├── inventory.model.js
│   │   │   ├── stock.service.js
│   │   │   ├── kardex.service.js
│   │   │   ├── lot.service.js
│   │   │   ├── serial.service.js
│   │   │   └── expiration.service.js
│   │   │
│   │   ├── quotes/
│   │   │   ├── quotes.routes.js
│   │   │   ├── quotes.controller.js
│   │   │   ├── quotes.service.js
│   │   │   ├── quotes.model.js
│   │   │   └── quotePricing.service.js
│   │   │
│   │   ├── orders/
│   │   │   ├── orders.routes.js
│   │   │   ├── orders.controller.js
│   │   │   ├── orders.service.js
│   │   │   └── orders.model.js
│   │   │
│   │   ├── purchases/
│   │   │   ├── purchases.routes.js
│   │   │   ├── purchases.controller.js
│   │   │   ├── purchases.service.js
│   │   │   └── purchases.model.js
│   │   │
│   │   ├── manufacturing/
│   │   │   ├── manufacturing.routes.js
│   │   │   ├── manufacturing.controller.js
│   │   │   ├── manufacturing.service.js
│   │   │   ├── manufacturing.model.js
│   │   │   ├── anchors.service.js
│   │   │   ├── formulas.service.js
│   │   │   └── operations.service.js
│   │   │
│   │   ├── picking/
│   │   │   ├── picking.routes.js
│   │   │   ├── picking.controller.js
│   │   │   ├── picking.service.js
│   │   │   └── picking.model.js
│   │   │
│   │   ├── dispatch/
│   │   │   ├── dispatch.routes.js
│   │   │   ├── dispatch.controller.js
│   │   │   ├── dispatch.service.js
│   │   │   └── dispatch.model.js
│   │   │
│   │   ├── currencies/
│   │   │   ├── currencies.routes.js
│   │   │   ├── currencies.controller.js
│   │   │   ├── currencies.service.js
│   │   │   └── currencies.model.js
│   │   │
│   │   ├── taxes/
│   │   │   ├── taxes.routes.js
│   │   │   ├── taxes.controller.js
│   │   │   ├── taxes.service.js
│   │   │   └── taxes.model.js
│   │   │
│   │   ├── reports/
│   │   │   ├── reports.routes.js
│   │   │   ├── reports.controller.js
│   │   │   ├── reports.service.js
│   │   │   └── reports.model.js
│   │   │
│   │   └── pwa/
│   │       ├── pwa.routes.js
│   │       ├── pwa.controller.js
│   │       └── push.service.js
│   │
│   ├── routes/
│   │   ├── web.routes.js
│   │   ├── api.routes.js
│   │   └── index.js
│   │
│   └── views/
│       ├── layouts/
│       │   ├── main.ejs
│       │   ├── auth.ejs
│       │   └── blank.ejs
│       │
│       ├── partials/
│       │   ├── head.ejs
│       │   ├── navbar.ejs
│       │   ├── sidebar.ejs
│       │   ├── footer.ejs
│       │   ├── flash.ejs
│       │   └── breadcrumbs.ejs
│       │
│       ├── auth/
│       │   ├── login.ejs
│       │   ├── register.ejs
│       │   └── forgot-password.ejs
│       │
│       ├── dashboard/
│       │   └── index.ejs
│       │
│       ├── users/
│       │   ├── index.ejs
│       │   ├── create.ejs
│       │   ├── edit.ejs
│       │   ├── show.ejs
│       │   └── invite.ejs
│       │
│       ├── roles/
│       │   ├── index.ejs
│       │   ├── create.ejs
│       │   ├── edit.ejs
│       │   └── permissions.ejs
│       │
│       ├── products/
│       │   ├── index.ejs
│       │   ├── create.ejs
│       │   ├── edit.ejs
│       │   └── show.ejs
│       │
│       ├── warehouses/
│       ├── inventory/
│       ├── quotes/
│       ├── orders/
│       ├── purchases/
│       ├── manufacturing/
│       ├── picking/
│       ├── dispatch/
│       ├── settings/
│       └── errors/
│           ├── 403.ejs
│           ├── 404.ejs
│           └── 500.ejs
│
├── public/
│   ├── phoenix/
│   │   ├── css/
│   │   ├── js/
│   │   ├── img/
│   │   └── vendors/
│   │
│   ├── app/
│   │   ├── css/
│   │   │   └── custom.css
│   │   ├── js/
│   │   │   ├── main.js
│   │   │   ├── pwa.js
│   │   │   └── forms.js
│   │   └── img/
│   │
│   ├── manifest.json
│   ├── service-worker.js
│   └── offline.html
│
├── storage/
│   ├── uploads/
│   ├── exports/
│   ├── temp/
│   └── logs/
│
├── scripts/
│   ├── migrate.js
│   ├── seed.js
│   ├── create-admin.js
│   ├── create-tenant.js
│   └── backup-db.js
│
└── tests/
    ├── auth.test.js
    ├── tenant.test.js
    ├── users.test.js
    ├── roles.test.js
    ├── products.test.js
    └── inventory.test.js

3. Criterio de arquitectura

Para un solo programador, evita crear demasiadas capas al inicio. La estructura por módulo debe ser consistente.

module/\n├── module.routes.js\n├── module.controller.js\n├── module.service.js\n├── module.model.js\n└── module.validator.js
ArchivoResponsabilidad
routes.jsDefine URLs y middlewares.
controller.jsRecibe request, responde vistas o JSON.
service.jsContiene lógica de negocio.
model.jsEjecuta consultas SQL parametrizadas.
validator.jsValida datos de entrada.

products.controller.js

No debe hacer SQL directo.

products.service.js

Resuelve reglas de negocio: serie, lote, vencimiento, pack, fórmula y fabricación.

products.model.js

Solo debe consultar MySQL usando SQL parametrizado.

4. Principios multi-tenant

Como usarás base de datos compartida, todas las tablas operativas deben tener tenant_id.

  • products
  • warehouses
  • inventory_stock
  • inventory_movements
  • customers
  • quotes
  • orders
  • purchases
  • manufacturing_orders
  • dispatches
  • users
  • roles
Regla crítica: el tenant_id nunca debe venir del frontend.

Flujo correcto

Login correcto
Usuario autenticado
Se obtiene tenant_id desde users.tenant_id
Se valida suscripción activa
Se guarda tenant_id en sesión/JWT
Middleware lo agrega a req.tenant
Todas las queries usan req.tenant.id
req.tenant = {\n  id: user.tenant_id,\n  plan_id: user.plan_id,\n  modules: ['inventory', 'quotes', 'dispatch']\n};

5. Usuarios, roles y permisos para suscriptores

Cada suscriptor debe poder ingresar a su cuenta y administrar sus propios usuarios internos, roles y permisos. Esta funcionalidad debe estar completamente aislada por tenant.

Principio: un usuario, rol o permiso asignado pertenece al tenant autenticado. Ninguna pantalla debe permitir administrar usuarios de otro suscriptor.

Módulos involucrados

users

Gestión de usuarios internos del suscriptor: crear, invitar, editar, bloquear, reactivar, asignar rol y consultar accesos.

roles

Gestión de perfiles y permisos por tenant. Permite definir qué puede hacer cada usuario en la aplicación.

Roles base sugeridos

RolUso recomendado
ownerPropietario de la cuenta. Control total del tenant, suscripción, usuarios, roles y configuración.
adminAdministrador interno. Gestiona operación, usuarios y configuración autorizada.
supervisorSupervisa inventario, pedidos, fabricación, picking y despacho.
salesCrea clientes, cotizaciones y pedidos.
warehouse_managerAdministra almacenes, ubicaciones, stock y ajustes autorizados.
inventory_operatorRegistra ingresos, salidas, transferencias y movimientos permitidos.
picking_operatorPrepara pedidos y confirma picking.
dispatch_operatorGestiona rutas, despachos, evidencias y entregas.
manufacturing_operatorGestiona órdenes de fabricación y avance de operaciones.
accounting_viewerConsulta reportes comerciales y documentos sin modificar operación.
readonlySolo lectura.

Permisos iniciales

Formato recomendado: module.action.

  • products.view
  • products.create
  • products.update
  • products.delete
  • inventory.view
  • inventory.adjust
  • inventory.transfer
  • quotes.create
  • quotes.approve
  • orders.confirm
  • purchases.create
  • manufacturing.start
  • manufacturing.finish
  • picking.prepare
  • dispatch.close
  • reports.view
  • users.manage
  • roles.manage
  • settings.manage

Restricción por plan

Un usuario no puede recibir permisos sobre módulos que el plan del tenant no tenga habilitados. Si el plan no incluye fabricación, ningún rol debe poder usar permisos manufacturing.*.

Flujo para invitar usuario

Owner/Admin ingresa nombre, email y rol.
Sistema valida que el email no exista dentro del mismo tenant.
Sistema crea invitación con token temporal.
Usuario recibe enlace.
Usuario define password.
Sistema activa usuario.
Sistema registra auditoría.

Flujo de login con roles

Usuario ingresa email y password.
Se valida credencial.
Se valida usuario activo.
Se obtiene tenant_id desde users.
Se valida tenant activo.
Se valida suscripción activa.
Se cargan roles y permisos.
Se genera sesión/JWT.
Se registra acceso.

Tablas sugeridas

CREATE TABLE users (
  id BIGINT AUTO_INCREMENT PRIMARY KEY,
  tenant_id BIGINT NOT NULL,
  name VARCHAR(160) NOT NULL,
  email VARCHAR(190) NOT NULL,
  password_hash VARCHAR(255) NOT NULL,
  status ENUM('invited','active','blocked','disabled') DEFAULT 'active',
  last_login_at DATETIME NULL,
  created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
  updated_at DATETIME NULL,
  deleted_at DATETIME NULL,
  UNIQUE KEY uq_users_tenant_email (tenant_id, email),
  INDEX idx_users_tenant (tenant_id)
);

CREATE TABLE roles (
  id BIGINT AUTO_INCREMENT PRIMARY KEY,
  tenant_id BIGINT NOT NULL,
  code VARCHAR(80) NOT NULL,
  name VARCHAR(120) NOT NULL,
  description VARCHAR(255) NULL,
  is_system TINYINT(1) DEFAULT 0,
  is_active TINYINT(1) DEFAULT 1,
  created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
  UNIQUE KEY uq_roles_tenant_code (tenant_id, code),
  INDEX idx_roles_tenant (tenant_id)
);

CREATE TABLE permissions (
  id BIGINT AUTO_INCREMENT PRIMARY KEY,
  module_code VARCHAR(80) NOT NULL,
  action_code VARCHAR(80) NOT NULL,
  permission_key VARCHAR(160) NOT NULL,
  description VARCHAR(255) NULL,
  UNIQUE KEY uq_permissions_key (permission_key)
);

CREATE TABLE role_permissions (
  id BIGINT AUTO_INCREMENT PRIMARY KEY,
  tenant_id BIGINT NOT NULL,
  role_id BIGINT NOT NULL,
  permission_id BIGINT NOT NULL,
  created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
  UNIQUE KEY uq_role_permission (tenant_id, role_id, permission_id),
  INDEX idx_role_permissions_tenant (tenant_id)
);

CREATE TABLE user_roles (
  id BIGINT AUTO_INCREMENT PRIMARY KEY,
  tenant_id BIGINT NOT NULL,
  user_id BIGINT NOT NULL,
  role_id BIGINT NOT NULL,
  created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
  UNIQUE KEY uq_user_role (tenant_id, user_id, role_id),
  INDEX idx_user_roles_tenant (tenant_id)
);

CREATE TABLE user_invitations (
  id BIGINT AUTO_INCREMENT PRIMARY KEY,
  tenant_id BIGINT NOT NULL,
  email VARCHAR(190) NOT NULL,
  token_hash VARCHAR(255) NOT NULL,
  role_id BIGINT NULL,
  status ENUM('pending','accepted','expired','cancelled') DEFAULT 'pending',
  expires_at DATETIME NOT NULL,
  created_by BIGINT NOT NULL,
  accepted_at DATETIME NULL,
  created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
  INDEX idx_user_invitations_tenant (tenant_id),
  INDEX idx_user_invitations_email (tenant_id, email)
);

Auditoría obligatoria

  • Crear usuario.
  • Invitar usuario.
  • Cambiar rol.
  • Bloquear usuario.
  • Reactivar usuario.
  • Eliminar usuario.
  • Cambiar password.
  • Inicio de sesión.
  • Cierre de sesión.
  • Login fallido.

6. Sin Redis al inicio: alternativa simple

Como empezarás en servidor compartido sin Redis, usa MySQL para funciones operativas básicas.

  • Sesiones
  • Revocación de tokens
  • Rate limit básico
  • Cola simple de tareas
  • Auditoría
  • Jobs pendientes

Tablas recomendadas

user_sessions\nrevoked_tokens\nrate_limit_logs\napp_jobs\naudit_logs

Evolución futura

Función inicial con MySQLEvolución futura con Redis
user_sessionsRedis sessions
revoked_tokensRedis blacklist
rate_limit_logsRedis rate limit
app_jobsBullMQ
locks por DBRedis locks
cache simple por tablaRedis cache

7. Modelo base de productos

El producto debe tener un campo principal: control_type.

simple\nserial\nlot\nexpiration\nlot_expiration\nvariant\npack\nformula\nmanufactured\nmixed

Tablas principales

products\nproduct_variants\nproduct_pack_items\nproduct_formula_items\nproduct_control_rules\nproduct_units\nproduct_categories\nproduct_brands

Ejemplo de tabla products

CREATE TABLE products (
  id BIGINT AUTO_INCREMENT PRIMARY KEY,
  tenant_id BIGINT NOT NULL,
  sku VARCHAR(80) NOT NULL,
  name VARCHAR(180) NOT NULL,
  description TEXT NULL,

  control_type ENUM(
    'simple',
    'serial',
    'lot',
    'expiration',
    'lot_expiration',
    'variant',
    'pack',
    'formula',
    'manufactured',
    'mixed'
  ) NOT NULL DEFAULT 'simple',

  base_unit_id BIGINT NULL,
  category_id BIGINT NULL,
  brand_id BIGINT NULL,

  has_variants TINYINT(1) DEFAULT 0,
  has_serial_control TINYINT(1) DEFAULT 0,
  has_lot_control TINYINT(1) DEFAULT 0,
  has_expiration_control TINYINT(1) DEFAULT 0,
  is_pack TINYINT(1) DEFAULT 0,
  is_formula TINYINT(1) DEFAULT 0,
  is_manufactured TINYINT(1) DEFAULT 0,

  cost_price DECIMAL(15,4) DEFAULT 0,
  sale_price DECIMAL(15,4) DEFAULT 0,
  currency_id BIGINT NULL,
  tax_profile_id BIGINT NULL,

  is_active TINYINT(1) DEFAULT 1,

  created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
  updated_at DATETIME NULL,

  UNIQUE KEY uq_products_tenant_sku (tenant_id, sku),
  INDEX idx_products_tenant (tenant_id),
  INDEX idx_products_control_type (tenant_id, control_type)
);

8. Inventario multi-almacén y ubicaciones

Debes separar almacén, zona, rack, columna, nivel, posición, stock y movimiento kardex.

Tablas sugeridas

warehouses\nwarehouse_zones\nwarehouse_locations\ninventory_stock\ninventory_movements\ninventory_lots\ninventory_serials\ninventory_expirations\nstock_reservations

Ubicación logística

Almacén Lima\n└── Zona A\n    └── Rack R01\n        └── Columna C03\n            └── Nivel N02\n                └── Posición P05

Tabla sugerida

CREATE TABLE warehouse_locations (
  id BIGINT AUTO_INCREMENT PRIMARY KEY,
  tenant_id BIGINT NOT NULL,
  warehouse_id BIGINT NOT NULL,

  zone_code VARCHAR(50) NULL,
  rack_code VARCHAR(50) NULL,
  column_code VARCHAR(50) NULL,
  level_code VARCHAR(50) NULL,
  position_code VARCHAR(50) NULL,

  full_code VARCHAR(150) NOT NULL,

  max_weight DECIMAL(15,4) NULL,
  max_volume DECIMAL(15,4) NULL,

  is_active TINYINT(1) DEFAULT 1,

  created_at DATETIME DEFAULT CURRENT_TIMESTAMP,

  UNIQUE KEY uq_location_code (tenant_id, warehouse_id, full_code),
  INDEX idx_location_tenant (tenant_id)
);

9. Flujo operativo principal

Cliente
Cotización
Pedido
Validación de stock
Compra / abastecimiento si falta stock
Fabricación si aplica
Reserva de inventario
Picking
Packing
Ruta de despacho
Entrega
Evidencia / cierre

Estados sugeridos

Cotización

draft\nsent\naccepted\nrejected\nexpired\nconverted_to_order

Pedido

draft\nconfirmed\nwaiting_stock\nin_purchase\nin_manufacturing\nready_for_picking\npicking\npacked\ndispatched\ndelivered\ncancelled

Fabricación

planned\nin_cutting\nin_beveling\nin_stamping\nin_bending\nin_packaging\nfinished\ncancelled

Despacho

pending\nassigned_route\nin_transit\ndelivered\nfailed\nreturned

10. Módulo especializado para pernería y anclajes

Este módulo será clave para diferenciarte.

Debe permitir cotizar y controlar fabricación de anclajes considerando:

  • Tipo de anclaje
  • Material
  • Diámetro
  • Longitud
  • Rosca
  • Acabado
  • Cantidad
  • Merma
  • Corte
  • Biselado
  • Estampado
  • Doblez
  • Galvanizado o acabado
  • Empaque
  • Despacho

Tablas sugeridas

anchor_quote_items\nmanufacturing_orders\nmanufacturing_order_items\nmanufacturing_operations\nmanufacturing_operation_logs\nproduct_formulas\nproduct_formula_items

Operaciones base

cutting\nbeveling\nthreading\nstamping\nbending\nwelding\nsurface_treatment\npackaging\nquality_control\ndispatch_preparation

Cálculo de cotización de anclaje

Costo de material\n+ Costo de merma\n+ Costo por operación\n+ Costo de empaque\n+ Costo de despacho\n+ Margen\n+ Impuesto si aplica\n= Precio final

11. Multi-moneda e impuestos

Debes tener configuración por tenant.

Tablas sugeridas

countries\ncurrencies\nexchange_rates\ntax_profiles\ntenant_tax_settings\ntenant_currency_settings\nprice_lists\nprice_list_items

Configuración importante por cliente

Moneda principal\nMoneda secundaria\nPaís\nTasa de impuesto\nLista de precios incluye impuesto: sí/no\nRedondeo de precios\nCantidad de decimales
PaísConfiguración ejemplo
PerúMoneda base: PEN. Impuesto: IGV 18%. Precios incluyen impuesto: Sí o No.
MéxicoMoneda base: MXN. Impuesto: IVA 16%. Precios incluyen impuesto: Sí o No.

12. PWA

Archivos necesarios

public/manifest.json\npublic/service-worker.js\npublic/offline.html\npublic/app/js/pwa.js

Funciones iniciales

  • Instalable en escritorio y móvil.
  • Página offline básica.
  • Cache de assets.
  • Cache de vistas principales.
  • Preparado para push notifications.
  • Íconos por tamaño.
No recomiendo hacer inventario offline completo en la primera etapa. Es más complejo por sincronización y riesgo de inconsistencia.

13. Archivos .md para guiar a CODEX/COMEX

Estos archivos deben crearse dentro de /docs. Incluyen todo el alcance técnico, reglas de desarrollo y el nuevo módulo de usuarios, roles y permisos.

00-PROJECT-VISION.mdver contenido
# Visión del Proyecto

## Nombre provisional
Logística SaaS Multi-Tenant

## Objetivo
Desarrollar una aplicación web SaaS especializada en gestión logística, inventario, almacenes, cotizaciones, pedidos, fabricación y despacho para empresas de habla hispana.

## Stack obligatorio
- Node.js
- Express
- MySQL
- EJS con template Phoenix
- PWA
- Servidor compartido al inicio
- Sin Redis en la primera etapa

## Principio principal
El sistema debe diferenciarse por manejar productos con distintos perfiles de control logístico:
- Simple
- Por número de serie
- Por lote
- Por vencimiento
- Por lote y vencimiento
- Con variantes
- Pack
- Compuesto por fórmula
- Fabricado
- Mixto

## Enfoque de desarrollo
El proyecto será desarrollado por un solo programador, por lo tanto:
- La estructura debe ser simple.
- Los módulos deben ser claros.
- Evitar sobreingeniería.
- Evitar microservicios al inicio.
- Mantener separación entre rutas, controladores, servicios, modelos y vistas.

## Mercado objetivo
Empresas importadoras, distribuidoras, mayoristas técnicos, almacenes, empresas de pernería, ferretería técnica, alimentos, medicinas, hardware, repuestos y productos con trazabilidad logística.

## Diferenciador
El sistema no solo registra stock. Debe entender cómo se controla cada producto y adaptar el flujo operativo según el tipo de producto.
01-ARCHITECTURE.mdver contenido
# Arquitectura General

## Patrón principal
Aplicación monolítica modular con Node.js + Express.

## Renderizado
Usar EJS para render server-side e integrar el template Phoenix.

## Base de datos
Usar MySQL con base de datos compartida para todos los tenants.

## Multi-tenancy
Todas las tablas operativas deben incluir tenant_id.
El tenant_id nunca debe venir del frontend.
El tenant_id se obtiene desde el usuario autenticado.

## Capas por módulo
Cada módulo debe seguir esta estructura:
- routes.js
- controller.js
- service.js
- model.js
- validator.js

## Responsabilidades

### Routes
Define rutas, middlewares y permisos.

### Controller
Recibe la solicitud HTTP y devuelve vista o JSON.

### Service
Contiene reglas de negocio.

### Model
Ejecuta consultas SQL parametrizadas.

### Validator
Valida entradas del usuario.

## Reglas
- No hacer SQL en controllers.
- No leer tenant_id desde formularios.
- No duplicar lógica de negocio en vistas.
- No colocar lógica sensible en el frontend.
- Todas las consultas operativas deben filtrar por tenant_id.
- Todas las operaciones críticas deben registrar auditoría.

## Preparación para crecimiento
En la primera etapa se usará MySQL para sesiones, revocación, jobs y rate limit básico.
En una etapa futura se podrá incorporar Redis para:
- Cache
- Sesiones
- Rate limit
- Colas
- Locks
02-MULTI-TENANCY.mdver contenido
# Multi-Tenancy y Seguridad

## Modelo elegido
Base de datos compartida con tenant_id en todas las tablas operativas.

## Regla crítica
El tenant_id nunca debe provenir del frontend, formularios, query params ni body.

## Resolución del tenant
El tenant se resuelve después del login usando la relación entre usuario y empresa tenant.

Flujo:
1. Usuario ingresa credenciales.
2. Se valida usuario.
3. Se obtiene tenant_id desde la tabla users.
4. Se valida que el tenant esté activo.
5. Se valida que la suscripción esté activa.
6. Se cargan rol y permisos.
7. Se genera sesión/JWT.
8. tenant_id queda disponible en req.tenant.

## Middleware obligatorio
Toda ruta protegida debe usar:
- auth.middleware.js
- tenant.middleware.js
- subscription.middleware.js
- permission.middleware.js cuando aplique

## Consultas SQL
Toda consulta sobre datos del cliente debe incluir:
WHERE tenant_id = ?

## Prohibido
- Aceptar tenant_id desde el frontend.
- Construir SQL con concatenación de strings.
- Mostrar datos de otro tenant.
- Ejecutar acciones sin validar permisos.
- Saltar auditoría en operaciones críticas.

## Auditoría
Registrar:
- tenant_id
- user_id
- acción
- módulo
- recurso afectado
- IP
- user agent
- fecha y hora
03-DATABASE-STANDARDS.mdver contenido
# Estándares de Base de Datos

## Motor
MySQL.

## Convenciones
- Tablas en plural: products, warehouses, orders.
- Campos en snake_case.
- Llaves primarias: id BIGINT AUTO_INCREMENT.
- Todas las tablas operativas deben tener tenant_id.
- Usar created_at, updated_at y deleted_at cuando aplique.
- Preferir soft delete para registros importantes.

## Campos base por tabla operativa
- id
- tenant_id
- created_at
- updated_at
- deleted_at
- is_active

## Índices obligatorios
Toda tabla con tenant_id debe tener índice por tenant_id.

Ejemplo:
INDEX idx_products_tenant (tenant_id)

Cuando aplique, usar índices compuestos:
UNIQUE KEY uq_products_tenant_sku (tenant_id, sku)

## Seguridad SQL
- Todas las consultas deben ser parametrizadas.
- No concatenar valores del usuario.
- Validar datos antes de llegar al modelo.
- Paginar listados grandes.

## Migraciones
Cada cambio estructural debe agregarse como archivo SQL dentro de:
src/database/migrations

Formato:
001_create_tenants.sql
002_create_users.sql
003_create_products.sql

## Seeds
Los datos iniciales deben ir en:
src/database/seeds
04-MODULES-SCOPE.mdver contenido
# Alcance de Módulos

## Módulos base

### Auth
Login, logout, sesiones, recuperación de contraseña y validación de usuario.

### Tenants
Registro de empresas cliente, datos fiscales, país, moneda y configuración general.

### Subscriptions
Planes, módulos habilitados, estado de suscripción y restricciones.

### Users
Usuarios por tenant, invitaciones, activación, bloqueo, cambio de rol y administración de acceso interno del suscriptor.

### Roles
Roles y permisos por tenant. Debe permitir que cada suscriptor cree perfiles como administrador, supervisor, vendedor, almacenero, operador de picking, despacho y consulta.

### Settings
Configuración de moneda, impuesto, idioma, listas de precios y preferencias.

### Customers
Clientes comerciales del tenant.

### Suppliers
Proveedores.

### Products
Catálogo de productos con perfil de control logístico.

### Warehouses
Almacenes y ubicaciones.

### Inventory
Stock, kardex, lotes, series, vencimientos y reservas.

### Quotes
Cotizaciones a clientes.

### Orders
Pedidos confirmados.

### Purchases
Órdenes de compra para abastecimiento.

### Manufacturing
Fabricación de productos y anclajes.

### Picking
Preparación de pedidos.

### Dispatch
Rutas, despachos, evidencias y cierre de entrega.

### Currencies
Monedas y tipos de cambio.

### Taxes
Configuración de impuestos a las ventas.

### Reports
Reportes operativos.

### PWA
Manifest, service worker, offline básico y preparación para push notifications.

## Regla de simplicidad
No crear módulos que no serán usados en la primera versión.
Cada módulo debe tener una finalidad concreta.
05-PRODUCT-CONTROL-MODEL.mdver contenido
# Modelo de Control de Productos

## Objetivo
Permitir que cada producto defina cómo debe ser controlado en inventario, ventas, compras, fabricación y despacho.

## Tipos de control
- simple
- serial
- lot
- expiration
- lot_expiration
- variant
- pack
- formula
- manufactured
- mixed

## Producto simple
Solo controla cantidad.

Ejemplo:
- Tornillo estándar
- Caja genérica
- Consumible simple

## Producto serializado
Cada unidad tiene número de serie único.

Ejemplo:
- Laptop
- Electrodoméstico
- Equipo electrónico

## Producto por lote
Control por lote de fabricación.

Ejemplo:
- Pernos
- Insumos industriales
- Productos químicos

## Producto con vencimiento
Control por fecha de vencimiento.

Ejemplo:
- Medicinas
- Alimentos
- Productos perecibles

## Producto con lote y vencimiento
Control combinado.

Ejemplo:
- Medicamentos
- Alimentos industriales

## Producto con variantes
Producto base con opciones.

Ejemplo:
- Color
- Talla
- Modelo
- Medida
- Acabado

## Pack
Producto comercial formado por varios productos.

Ejemplo:
- Kit de instalación
- Combo promocional

## Producto por fórmula
Producto compuesto que consume insumos según una receta o fórmula.

Ejemplo:
- Anclaje químico
- Mezcla
- Producto fabricado con componentes

## Producto fabricado
Producto que requiere proceso productivo.

Ejemplo:
- Anclaje cortado, biselado, doblado y empacado.

## Reglas
El sistema debe validar el control requerido antes de permitir movimientos de inventario.
Si el producto es serializado, no puede ingresar stock sin serie.
Si el producto usa lote, no puede ingresar stock sin lote.
Si el producto vence, no puede ingresar stock sin vencimiento.
Si el producto es pack, debe validar componentes.
Si el producto es fórmula, debe validar insumos.
06-INVENTORY-WORKFLOW.mdver contenido
# Flujo de Inventario

## Objetivo
Controlar stock multi-almacén, ubicaciones, kardex, lotes, series, vencimientos y reservas.

## Flujo de ingreso
1. Se registra compra, fabricación o ajuste.
2. Se identifica producto.
3. Se valida tipo de control.
4. Se registra lote, serie o vencimiento si corresponde.
5. Se asigna almacén y ubicación.
6. Se actualiza stock.
7. Se registra kardex.
8. Se registra auditoría.

## Flujo de salida
1. Se confirma pedido.
2. Se valida disponibilidad.
3. Se reserva stock.
4. Se genera picking.
5. Se confirma retiro físico.
6. Se descuenta stock.
7. Se registra kardex.
8. Se prepara despacho.

## Kardex
Todo movimiento debe registrar:
- tenant_id
- product_id
- warehouse_id
- location_id
- movement_type
- quantity
- unit_cost
- reference_type
- reference_id
- user_id
- created_at

## Tipos de movimiento
- purchase_in
- manufacturing_in
- sale_out
- transfer_in
- transfer_out
- adjustment_in
- adjustment_out
- return_in
- damaged_out

## Regla
Nunca modificar stock sin registrar kardex.
07-ANCHORS-MANUFACTURING.mdver contenido
# Módulo de Fabricación de Anclajes

## Objetivo
Permitir cotizar y controlar fabricación de anclajes y productos técnicos similares.

## Procesos considerados
- Corte
- Biselado
- Roscado
- Estampado
- Doblez
- Soldadura
- Tratamiento superficial
- Control de calidad
- Empaque
- Preparación para despacho

## Datos para cotizar
- Tipo de producto
- Material
- Diámetro
- Longitud
- Cantidad
- Unidad de medida
- Merma esperada
- Costo de material
- Costo por operación
- Costo de empaque
- Costo de despacho
- Margen
- Impuesto

## Fórmula general
Precio final =
Costo material
+ Costo merma
+ Costo operaciones
+ Costo empaque
+ Costo despacho
+ Margen
+ Impuesto si aplica

## Estados de fabricación
- planned
- in_cutting
- in_beveling
- in_threading
- in_stamping
- in_bending
- in_packaging
- quality_control
- finished
- cancelled

## Regla
Una fabricación debe poder consumir inventario de insumos y generar ingreso de producto terminado.
08-PHOENIX-UI-GUIDE.mdver contenido
# Guía de Interfaz con Template Phoenix

## Objetivo
Integrar el template Phoenix con EJS de forma ordenada y mantenible.

## Ubicación de assets
Los archivos originales del template deben colocarse en:
public/phoenix

Los estilos propios de la aplicación deben colocarse en:
public/app/css/custom.css

Los scripts propios deben colocarse en:
public/app/js

## Layouts
Usar layouts EJS:
- main.ejs para panel principal
- auth.ejs para login y registro
- blank.ejs para páginas especiales

## Partials
Usar partials para:
- head
- navbar
- sidebar
- footer
- breadcrumbs
- mensajes flash

## Reglas
- No modificar directamente archivos originales del template salvo necesidad.
- Personalizar desde custom.css.
- No duplicar sidebar en cada vista.
- No colocar lógica compleja en EJS.
- Las vistas solo deben mostrar datos preparados por el controller.

## Menú principal inicial
- Dashboard
- Clientes
- Proveedores
- Productos
- Almacenes
- Inventario
- Cotizaciones
- Pedidos
- Compras
- Fabricación
- Picking
- Despachos
- Reportes
- Configuración
- Usuarios
- Roles y permisos
09-PWA-GUIDE.mdver contenido
# Guía PWA

## Objetivo
Permitir que la aplicación sea instalable y tenga funcionamiento offline básico.

## Archivos requeridos
- public/manifest.json
- public/service-worker.js
- public/offline.html
- public/app/js/pwa.js

## Funciones iniciales
- Instalación en escritorio y móvil.
- Cache de assets principales.
- Página offline.
- Preparación para futuras notificaciones push.

## No incluir en primera etapa
- Inventario offline completo.
- Sincronización compleja.
- Conflictos de stock offline.

## Motivo
El inventario requiere consistencia fuerte. Permitir movimientos offline puede generar errores de stock si no existe una estrategia robusta de sincronización.

## Primera versión recomendada
- Login online.
- Operación online.
- Dashboard y páginas base cacheadas.
- Página offline informativa.
10-CODEX-INSTRUCTIONS.mdver contenido
# Instrucciones para CODEX/COMEX

## Rol esperado
Actuar como asistente de desarrollo para una aplicación SaaS logística multi-tenant construida con Node.js, Express, MySQL, EJS y template Phoenix.

## Reglas obligatorias
- No usar PHP.
- No usar frameworks frontend complejos.
- No usar Redis en la primera etapa.
- No crear microservicios.
- No omitir tenant_id en tablas operativas.
- No aceptar tenant_id desde frontend.
- No hacer SQL sin parámetros.
- No colocar lógica de negocio en vistas EJS.
- No crear archivos innecesarios.
- No permitir que un usuario administre usuarios de otro tenant.
- No permitir que un rol tenga permisos sobre módulos no incluidos en el plan activo del tenant.

## Estructura por módulo
Cada módulo debe seguir:
module.routes.js
module.controller.js
module.service.js
module.model.js
module.validator.js

## Comentario obligatorio al inicio de cada archivo
Cada archivo generado debe iniciar con un comentario que explique:
- Nombre del archivo.
- Módulo al que pertenece.
- Responsabilidad.
- Si maneja datos multi-tenant.

Ejemplo:

/**
 * Archivo: products.service.js
 * Módulo: Products
 * Responsabilidad: Contiene la lógica de negocio para productos.
 * Multi-tenant: Sí. Toda operación debe recibir tenant_id desde req.tenant.
 */

## Estilo de código
- Código claro.
- Funciones pequeñas.
- Comentarios útiles.
- Manejo de errores con asyncHandler.
- Validación antes de llegar al modelo.
- Consultas SQL parametrizadas.
- Paginación en listados.

## Prioridad de desarrollo
1. Base Express + EJS + Phoenix.
2. Conexión MySQL.
3. Auth.
4. Multi-tenancy.
5. Suscripciones.
6. Usuarios y roles.
7. Productos.
8. Almacenes.
9. Inventario.
10. Cotizaciones.
11. Pedidos.
12. Fabricación de anclajes.
13. Picking.
14. Despacho.
15. Reportes.
16. PWA.

## Criterio principal
Construir primero una versión funcional simple antes de agregar automatizaciones avanzadas.
11-ROADMAP.mdver contenido
# Roadmap de Desarrollo

## Etapa 1: Base técnica
- Crear proyecto Node.js.
- Configurar Express.
- Configurar EJS.
- Integrar template Phoenix.
- Configurar MySQL.
- Crear estructura modular.
- Crear layout principal.
- Crear dashboard inicial.

## Etapa 2: Seguridad y multi-tenancy
- Login.
- Logout.
- Sesiones.
- Middleware de autenticación.
- Middleware de tenant.
- Middleware de suscripción.
- Roles y permisos.
- Usuarios por tenant.
- Invitaciones de usuarios.
- Auditoría.

## Etapa 3: Configuración del tenant
- Datos de empresa.
- País.
- Moneda.
- Impuesto.
- Preferencia de precios con o sin impuesto.
- Idioma.
- Plan activo.

## Etapa 4: Catálogo de productos
- Categorías.
- Marcas.
- Unidades.
- Productos.
- Tipos de control.
- Variantes.
- Packs.
- Fórmulas.

## Etapa 5: Almacenes e inventario
- Almacenes.
- Ubicaciones.
- Stock.
- Kardex.
- Lotes.
- Series.
- Vencimientos.
- Reservas.

## Etapa 6: Clientes, proveedores y compras
- Clientes.
- Contactos.
- Proveedores.
- Órdenes de compra.
- Ingreso de mercadería.

## Etapa 7: Cotizaciones y pedidos
- Cotización.
- Conversión a pedido.
- Validación de stock.
- Reserva de inventario.
- Estado del pedido.

## Etapa 8: Fabricación de anclajes
- Cotización técnica.
- Fórmulas.
- Operaciones.
- Orden de fabricación.
- Consumo de insumos.
- Producto terminado.

## Etapa 9: Picking y despacho
- Lista de picking.
- Confirmación de preparación.
- Packing.
- Rutas.
- Despacho.
- Evidencia de entrega.

## Etapa 10: Reportes y PWA
- Reportes de stock.
- Reportes de rotación.
- Reportes de pedidos.
- Reportes de despacho.
- Manifest.
- Service worker.
- Offline básico.
12-USERS-ROLES-PERMISSIONS.mdver contenido
# Usuarios, Roles y Permisos por Suscriptor

## Objetivo
Permitir que cada suscriptor administre sus propios usuarios internos, roles y permisos, sin afectar ni visualizar usuarios de otros tenants.

## Principio multi-tenant
Cada usuario pertenece a un tenant.
Cada rol pertenece a un tenant.
Cada permiso asignado debe validarse dentro del tenant actual.
El tenant_id nunca debe venir del formulario.

## Tablas principales
- users
- roles
- permissions
- role_permissions
- user_roles
- user_invitations
- user_sessions
- password_resets
- audit_logs

## Roles base sugeridos
- owner
- admin
- supervisor
- sales
- warehouse_manager
- inventory_operator
- picking_operator
- dispatch_operator
- manufacturing_operator
- accounting_viewer
- readonly

## Criterio de roles

### owner
Usuario propietario de la cuenta del suscriptor.
Puede administrar empresa, suscripción, usuarios, roles, permisos, configuración y todos los módulos habilitados por el plan.

### admin
Administrador interno del tenant.
Puede administrar operaciones, usuarios y configuración, pero no necesariamente cambiar datos críticos de facturación o suscripción.

### supervisor
Supervisa operaciones, pedidos, inventario, fabricación, picking y despacho.

### sales
Crea clientes, cotizaciones y pedidos.

### warehouse_manager
Administra almacenes, ubicaciones, stock y ajustes autorizados.

### inventory_operator
Registra movimientos, ingresos, salidas y transferencias según permisos.

### picking_operator
Visualiza pedidos asignados, prepara picking y confirma preparación.

### dispatch_operator
Gestiona rutas, despachos, evidencias y entregas.

### manufacturing_operator
Gestiona órdenes de fabricación y avance de operaciones.

### accounting_viewer
Consulta reportes comerciales y documentos, sin modificar operación.

### readonly
Solo lectura.

## Matriz de permisos inicial
Los permisos deben tener formato:
module.action

Ejemplos:
- products.view
- products.create
- products.update
- products.delete
- inventory.view
- inventory.adjust
- inventory.transfer
- quotes.create
- quotes.approve
- orders.confirm
- purchases.create
- manufacturing.start
- manufacturing.finish
- picking.prepare
- dispatch.close
- reports.view
- users.manage
- roles.manage
- settings.manage

## Restricción por plan
Un usuario no puede recibir permisos sobre módulos que el plan del tenant no tenga habilitados.

Ejemplo:
Si el plan no incluye manufacturing, ningún rol del tenant debe poder usar:
- manufacturing.view
- manufacturing.create
- manufacturing.start
- manufacturing.finish

## Flujo para invitar usuario
1. Owner/Admin ingresa nombre, email y rol.
2. Sistema valida que el email no exista dentro del mismo tenant.
3. Sistema crea invitación con token temporal.
4. Usuario recibe enlace.
5. Usuario define password.
6. Sistema activa usuario.
7. Sistema registra auditoría.

## Flujo de login
1. Usuario ingresa email y password.
2. Se valida credencial.
3. Se valida usuario activo.
4. Se obtiene tenant_id desde users.
5. Se valida tenant activo.
6. Se valida suscripción activa.
7. Se cargan roles y permisos.
8. Se genera sesión/JWT.
9. Se registra acceso.

## Middleware requerido
- auth.middleware.js valida sesión/JWT.
- tenant.middleware.js resuelve tenant desde usuario autenticado.
- subscription.middleware.js valida plan activo y módulos habilitados.
- permission.middleware.js valida permisos específicos por ruta.
- audit.middleware.js registra acciones sensibles.

## Regla crítica
La gestión de usuarios debe estar completamente aislada por tenant.
Un owner de un tenant no puede ver, crear, editar, bloquear ni eliminar usuarios de otro tenant.

## Auditoría obligatoria
Registrar cuando:
- Se crea usuario.
- Se invita usuario.
- Se cambia rol.
- Se bloquea usuario.
- Se reactiva usuario.
- Se elimina usuario.
- Se cambia password.
- Se inicia sesión.
- Se cierra sesión.
- Se falla login.

14. Plan de implementación recomendado

Fase 1 — Base mínima funcional

Objetivo: tener la aplicación corriendo con Phoenix, login y dashboard.

app.js\nserver.js\nconnection.js\nlayouts EJS\npartials EJS\nlogin\ndashboard\nmiddleware de errores\nmiddleware auth básico

No empieces por inventario. Primero necesitas una base sólida.

Fase 2 — Multi-tenant real

tenants\nusers\nroles\npermissions\nrole_permissions\nuser_roles\nuser_invitations\nsubscriptions\nplans\nplan_modules\naudit_logs
  • Un usuario pertenece a un tenant.
  • Un tenant tiene una suscripción.
  • Una suscripción activa habilita módulos.
  • Cada request protegida debe tener req.tenant.
  • Roles y permisos deben validarse por tenant.

Fase 3 — Configuración comercial del tenant

countries\ncurrencies\nexchange_rates\ntax_profiles\ntenant_settings\nprice_lists

El cliente debe poder definir país, moneda principal, impuesto, precios con o sin impuesto, idioma, decimales y redondeo.

Fase 4 — Productos

Este será el núcleo diferencial.

1. Producto simple\n2. Producto con variantes\n3. Producto con lote\n4. Producto con vencimiento\n5. Producto serializado\n6. Pack\n7. Fórmula\n8. Fabricado\n9. Control mixto

No intentes implementar todos los controles en la primera pantalla. Usa pestañas o secciones.

Datos generales\nControl logístico\nPrecios\nVariantes\nPack / Fórmula\nInventario

Fase 5 — Almacenes

warehouses\nwarehouse_zones\nwarehouse_locations

Primera versión: almacén, código de ubicación, descripción y estado. Luego agregas rack, columna, nivel y posición.

Fase 6 — Inventario

inventory_stock\ninventory_movements\ninventory_lots\ninventory_serials\nstock_reservations
Regla central: ningún movimiento modifica stock sin registrar kardex.
Ingreso\nSalida\nAjuste\nTransferencia\nReserva\nLiberación de reserva

Fase 7 — Cotización y pedido

quotes\nquote_items\norders\norder_items
Cotización borrador
Cotización enviada
Cotización aceptada
Pedido creado
Validación de stock
Reserva de stock

Fase 8 — Compras y abastecimiento

purchase_orders\npurchase_order_items\ngoods_receipts\ngoods_receipt_items

Debe cubrir pedido de compra, recepción, ingreso a inventario y registro de lote, serie o vencimiento si aplica.

Fase 9 — Fabricación de anclajes

manufacturing_orders\nmanufacturing_order_items\nmanufacturing_operations\nmanufacturing_operation_logs\nproduct_formulas\nproduct_formula_items\nanchor_quote_items

Primera versión: cotizar anclaje, crear orden de fabricación, registrar etapas, consumir insumos e ingresar producto terminado.

Fase 10 — Picking y despacho

picking_lists\npicking_items\ndispatches\ndispatch_items\ndispatch_routes\ndelivery_evidence
Pedido listo
Lista de picking
Confirmación de preparación
Packing
Ruta
Despacho
Entrega

Fase 11 — PWA

  • manifest.json
  • service-worker.js
  • offline.html
  • pwa.js

Primera meta: la app se puede instalar y muestra una página offline.

15. MVP recomendado

Para monetizar antes, no intentes cubrir todo desde el inicio.

MVP 1

Login\nMulti-tenant\nSuscripción básica\nUsuarios\nRoles simples\nClientes\nProveedores\nProductos\nTipos de control logístico\nAlmacenes\nUbicaciones\nStock\nKardex\nCotizaciones\nPedidos\nPWA básica

MVP 2

Compras\nReservas de stock\nPicking\nDespacho\nReportes

MVP 3

Fabricación de anclajes\nFórmulas\nEtapas productivas\nCosteo avanzado

MVP 4

Notificaciones push\nAPI pública\nIntegraciones\nDashboards avanzados\nRedis\nColas\nWorkers

16. Recomendación final de orden real

No desarrolles por módulos visibles primero. Desarrolla por dependencia técnica.

1. Base Express + EJS + Phoenix\n2. MySQL + migraciones\n3. Auth\n4. Tenant middleware\n5. Suscripciones\n6. Usuarios / roles\n7. Configuración del tenant\n8. Productos\n9. Almacenes\n10. Inventario\n11. Clientes / proveedores\n12. Cotizaciones\n13. Pedidos\n14. Compras\n15. Picking\n16. Despacho\n17. Fabricación de anclajes\n18. Reportes\n19. PWA
Decisión más importante: el núcleo del sistema no debe ser “productos + stock”, sino “productos con reglas de control logístico + movimientos trazables”.

Ese será el punto que diferencie la aplicación frente a sistemas más genéricos.