Saltar a contenido

Flujos ERP → WMS

Sincronización de datos desde Tango (SQL Server) hacia el WMS (API HTTP).

Diagrama General

flowchart LR
    subgraph Tango["Tango (SQL Server)"]
        GVA18[GVA18<br/>Provincias]
        GVA05[GVA05<br/>Zonas]
        WEBCLI[WEBCLIENTE<br/>Clientes]
        WEBVIEW[WEBVIEW<br/>Artículos]
        WMS_PED[WMS_Pedidos<br/>Pedidos]
        WMS_OC[WMS_OC_pendientes<br/>OC]
    end

    subgraph Middleware["Sync Engine"]
        POLL[Poller]
        TRANS[Transforms]
        QUEUE[Queue]
    end

    subgraph WMS["WMS API"]
        PROV[/provincias/]
        ZON[/zonas/]
        CLI[/clientes/]
        ART[/articulos/]
        PED[/pedidos/]
        OC[/ordenesDeCompra/]
    end

    GVA18 --> POLL
    GVA05 --> POLL
    WEBCLI --> POLL
    WEBVIEW --> POLL
    WMS_PED --> POLL
    WMS_OC --> POLL

    POLL --> TRANS
    TRANS --> QUEUE

    QUEUE --> PROV
    QUEUE --> ZON
    QUEUE --> CLI
    QUEUE --> ART
    QUEUE --> PED
    QUEUE --> OC

Flujo de Datos

1. Tablas Maestras

Datos que cambian muy poco. Se sincronizan cada hora.

flowchart LR
    A[(GVA18)] -->|SELECT *| B[Poller]
    B -->|POST/PATCH| C[/provincias/]

    style A fill:#f9f,stroke:#333
    style C fill:#9f9,stroke:#333
Entity Tabla Tango Endpoint WMS Intervalo
provincias GVA18 /provincias 1h
zonas GVA05 /zonas 1h
transportes GVA24 /transportes 1h
depositos STA22 /depositos 1h
categorias (estático) /categorias 24h

2. Clientes y Proveedores

Cambios moderados. Se sincronizan cada 5 minutos.

// Flow clientes
{
  name: 'clientes',
  source: { adapter: 'tango' },
  destinations: ['wms', 'mysql-web', 'mysql-development'],  // Múltiples destinos
  pollInterval: 5 * 60 * 1000,
  priority: 'normal',
  alerts: {
    onJobFailed: [
      // Alerta solo cuando falla el sync al WMS
      { type: 'email', to: ['ops@empresa.com'], destinations: ['wms'] },
    ],
  },
}

Mapeo de campos (destino WMS):

Campo WMS Campo Tango Descripción
codigo Cod Cliente Código único
nombre Nombre Razón social
cuit CUIT CUIT/CUIL
direccion Direccion Domicilio
codigoProvincia Cod Provincia FK a provincias
codigoZona Cod Zona FK a zonas

Destino MySQL Web — multi-tabla: la misma fila de WEBCLIENTE se materializa como 3 escrituras transaccionales:

flowchart LR
    A[(WEBCLIENTE<br/>Tango)] -->|clientesMysql| B[Row con _vend_*]
    B -->|TX| C1[REG_CUENTA<br/>subsistema=02<br/>cliente]
    B -->|TX| C2[REG_CUENTA<br/>subsistema=22<br/>vendedor]
    C1 -->|SELECT id_regcuenta| D[REG_CUENTA_RELACIONES]
    C2 -->|SELECT id_regcuenta| D

El transform clientesMysql valida que el cliente tenga vendedor asignado; si no, el job falla y dispara la alerta. Ver Operaciones Múltiples para la configuración completa.

3. Artículos

Cambios frecuentes. Se sincronizan cada minuto.

flowchart TD
    A[(WEBVIEW<br/>35,000 rows)] -->|SELECT *| B[Poller]
    B -->|Hash compare| C{¿Cambió?}
    C -->|No| D[Skip]
    C -->|Sí| E[Job Queue]
    E --> F[WMS /articulos]
    E --> G[MySQL productos]

Mapeo de campos (artículos):

Campo WMS Campo Tango
codigo CODIGO
nombre DESCRIPCION
codigoCategoria COD_CATEGORIA
codigoProveedor COD_PROVEE
stockActual STOCK
precioLista PRECIO_LISTA_1

4. Pedidos

Datos críticos. Se sincronizan cada 30 segundos con agrupación.

flowchart TD
    A[(WMS_Pedidos<br/>Líneas)] -->|SELECT *| B[Poller]
    B -->|agruparPedidos| C[Transform]
    C -->|Por Nro. Pedido| D[Pedidos agrupados]
    D --> E{¿Cambió hash?}
    E -->|Sí| F[Job Queue]
    F -->|POST/PATCH| G[WMS /pedidos]

Transform agruparPedidos:

// Input: líneas planas
[
  { "Nro. Pedido": "001", "Cod Articulo": "A1", Cantidad: 10 },
  { "Nro. Pedido": "001", "Cod Articulo": "A2", Cantidad: 5 },
]

// Output: pedidos con detalles
[
  {
    "_pk": "001",
    "idERP": "001",
    "numero": "0000000001",
    "codigoCliente": "CLI001",
    "fecha": "2025-01-08 10:30:00",
    "detalles": [
      { "codigoArticulo": "A1", "cantidadSolicitada": 10 },
      { "codigoArticulo": "A2", "cantidadSolicitada": 5 },
    ]
  }
]

5. Órdenes de Compra

Similar a pedidos, con agrupación.

{
  name: 'ordenesDeCompra',
  source: { adapter: 'tango' },
  destinations: ['wms'],
  pollInterval: 60 * 1000,
  priority: 'high',
  transform: 'agruparOrdenesCompra',
}

HTTP Adapter - Lógica de Upsert

El adapter WMS intenta POST, si da 409 hace PATCH:

sequenceDiagram
    participant Queue
    participant Adapter
    participant WMS

    Queue->>Adapter: Job (articulo ART001)
    Adapter->>WMS: POST /articulos

    alt Artículo nuevo
        WMS-->>Adapter: 201 Created
        Adapter-->>Queue: Success
    else Ya existe
        WMS-->>Adapter: 409 Conflict
        Adapter->>WMS: PATCH /articulos/ART001
        WMS-->>Adapter: 200 OK
        Adapter-->>Queue: Success
    end

Configuración del Source

// src/config/sources/tango.ts
export const tangoSource: MssqlSourceConfig = {
  name: 'tango',
  type: 'mssql-source',
  config: {
    server: process.env.MSSQL_SERVER,
    database: process.env.MSSQL_DATABASE,
    // ...
  },
  entities: {
    articulos: {
      table: 'WEBVIEW',
      primaryKey: ['CODIGO'],
    },
    clientes: {
      table: 'WEBCLIENTE',
      primaryKey: ['Cod Cliente'],
    },
    pedidos: {
      table: 'WMS_Pedidos',
      primaryKey: ['Nro. Pedido'],
    },
    proveedores: {
      table: 'CPA01',
      condition: 'HABILITADO = 1',  // Solo activos
      primaryKey: ['COD_PROVEE'],
    },
  },
};

Configuración del Destination

// src/config/destinations/wms.ts
export const wmsAdapter: HttpAdapterConfig = {
  name: 'wms',
  type: 'http',
  config: {
    baseUrl: process.env.WMS_API_URL,
    headers: {
      'X-API-KEY': process.env.WMS_API_KEY,
    },
  },
  entities: {
    articulos: {
      endpoint: 'articulos',
      keyField: 'codigo',
      fields: {
        codigo: 'CODIGO',
        nombre: 'DESCRIPCION',
        // ...
      },
    },
    pedidos: {
      endpoint: 'pedidos',
      keyField: 'idERP',
      fields: {
        idERP: 'idERP',
        numero: 'numero',
        // ... (viene del transform)
      },
    },
  },
};

Manejo de Errores

Error de Conexión

flowchart TD
    A[Job falla] --> B{¿Error de red?}
    B -->|Sí| C[Retry con backoff]
    C --> D{¿Intentos < 5?}
    D -->|Sí| E[Esperar 5s, 10s, 20s...]
    E --> F[Reintentar]
    D -->|No| G[Fallo permanente]
    B -->|No| H{¿Error 4xx?}
    H -->|400 Bad Request| I[Loggear y fallar]
    H -->|409 Conflict| J[Intentar PATCH]

Logs de Error

{
  "level": "error",
  "entity": "articulos",
  "adapter": "wms",
  "pk": "ART001",
  "status": 400,
  "responseBody": { "error": "Campo requerido faltante" },
  "requestBody": { "codigo": "ART001" },
  "msg": "HTTP request failed"
}

Monitoreo

Ver en el dashboard:

  • Flows activos: Estado de cada flow
  • Jobs en cola: Pendientes, activos, fallidos
  • Métricas: Duración promedio, P95, errores
  • Bull Board: Detalle de jobs individuales