analytics
Portafolio de Iván
Proyectos chevron_right Centro de operaciones multicanal

Sistema en producción · 2026

hub Integración multicanal inventory Inventario database PostgreSQL verified 143 tests

Cero sobreventa: centro de operaciones multicanal

Una mercería en México vendía por Mercado Libre con tres inventarios aislados que no se comunicaban. Hoy un inventario central con kardex append-only decide qué existencia ve cada canal, y ninguna escritura sale sin releerse.

Cifras clave

796

publicaciones auditadas en el saneamiento inicial

1,742

movimientos en el kardex append-only

3.5 ms

consulta de 90 días, sin índices nuevos

143

tests automatizados (subieron de 115)

Stack

Node 22TypeScriptExpressPostgreSQLzodpinosharpPDFKitAPI de Mercado Libre
schema

Cómo viaja una venta

El inventario central es la única fuente de verdad. Los canales leen de él y el kardex registra —nunca edita— cada movimiento.

  1. storefront Canal

    Venta en Mercado Libre

    La orden entra por la API del canal, con su publicación y su variación.

  2. receipt_long Kardex

    Movimiento append-only

    Se registra un movimiento nuevo. Nada se edita ni se borra: se corrige agregando.

  3. inventory_2 Central

    Existencia recalculada

    El inventario central recalcula la disponibilidad real de ese SKU.

  4. sync_alt Write-back

    Escritura al canal

    Si procede, se ajusta el canal — y la escritura se vuelve a leer para comprobarla.

  5. shopping_cart Tienda

    La tienda sirve del central

    La tienda web deja de tener su propio stock y publica la disponibilidad del central.

store

Contexto

El negocio es una mercería en México cuyo canal principal de venta es Mercado Libre, con un volumen del orden de 500 unidades vendidas al mes en ese solo canal. La operación diaria la lleva un equipo pequeño que mueve cajas físicas, arma envíos y atiende clientes; nadie está sentado esperando a que un sistema le pida atención.

Cuando entré, la existencia vivía en tres lugares a la vez: un espejo del catálogo de Mercado Libre, el conteo físico de la bodega y el punto de venta. Ninguno le hablaba a los otros. Las ventas del canal no se registraban en ningún sistema propio, y la tienda web ni siquiera descontaba stock cuando alguien pagaba.

El encargo no era «hacer un dashboard». Era construir el lugar donde la existencia por fin significa una sola cosa, sin apagar la operación mientras se construía.

report

El problema

Tres inventarios aislados, y ninguno de los tres era el bueno.

Con tres fuentes desincronizadas, la sobreventa no es un accidente: es cuestión de tiempo. Vender una pieza que ya salió por otro canal significa cancelar, disculparse y pagarlo en reputación de la cuenta, que es el activo más caro de un vendedor en marketplace.

El agravante es que no había forma de reconstruir la historia. Sin registro de movimientos, la pregunta «¿por qué esta caja dice ocho y hay cinco?» no tenía respuesta posible: solo quedaba volver a contar y confiar en el nuevo número hasta la próxima discrepancia.

Y encima estaba el riesgo de la cura: un sistema que empieza a escribir existencias en un canal con 796 publicaciones vivas puede hacer un daño enorme en una sola corrida mal calculada. Cualquier solución tenía que poder estar apagada y seguir siendo útil.

rule

Decisiones clave

Estas cinco son las que explican por qué el sistema se pudo encender sin que nadie perdiera una noche de sueño.

01 Un 2xx no prueba nada

Toda escritura a Mercado Libre se vuelve a leer del canal y se compara contra lo que se quiso escribir. Si no coincide, el movimiento queda marcado como fallido, no como hecho.

psychology Una API de terceros puede aceptar una petición, responder «correcto» y aplicar otra cosa —o nada—. Si el sistema cree en la respuesta en vez de en el estado, el inventario se separa de la realidad justo donde nadie está mirando.

02 Escalera de activación, publicación por publicación

Nada se enciende de golpe. El modo va de apagado a simulación durante tres días, después a «solo reducir» y solo al final a escritura completa. La primera publicación en pasar por la escalera se eligió a mano, y fue una barata.

psychology Si el sistema se equivoca, quiero que se equivoque en el artículo más barato del catálogo y con la operación mirando, no en 796 publicaciones a la vez. La simulación deja ver exactamente qué habría hecho antes de dejarlo hacerlo.

03 No escribir es siempre seguro

El proceso se niega a escribir si sus datos están viejos: catálogo con más de 24 horas, o cursor de bodega con más de 15 minutos de retraso. Además aborta la corrida si moviera más de 20 publicaciones o más de 150 unidades de una vez.

psychology Ante la duda, el peor resultado de no hacer nada es que el canal se queda como estaba. El peor resultado de escribir con datos viejos es sobrevender o vaciar publicaciones sanas. La asimetría es tan grande que no merece discusión.

04 Botón rojo sin redespliegue

Hay un apagado total que corta todas las escrituras al instante, sin tocar el código ni volver a desplegar, y modos intermedios que permiten reducir existencia pero no aumentarla.

psychology Cuando algo va mal en producción, la persona que lo nota necesita poder pararlo en segundos. Si apagar exige un despliegue, en la práctica no se apaga: se aguanta, y el daño crece.

05 Cobertura sin ventas es null, nunca cero

Si un producto no tiene velocidad de venta, la cobertura en días se reporta como dato ausente. Los CSV que consume la operación salen con BOM UTF-8 para que Excel no rompa los acentos.

psychology Un cero se lee como «urgentísimo, se acaba hoy» y manda a alguien a resurtir lo que nadie compra. Un dato ausente se lee como lo que es: aquí no hay información suficiente para opinar.

construction

La solución

El sistema se construyó en seis fases, cada una utilizable por sí sola. Primero el saneamiento de claves de producto: sin SKU confiable no hay nada que cruzar. Después un inventario central de solo lectura, que durante semanas solo miró y comparó, sin autoridad para cambiar nada.

La tercera fase trajo el kardex: un registro append-only de movimientos de venta. No se edita un movimiento equivocado, se agrega el que lo corrige, y la existencia siempre es la suma de la historia. El backfill de 90 días de historia se escribió en unos dos minutos y la consulta de esa misma ventana corre en 3.5 ms sin necesidad de índices nuevos.

Con la historia confiable ya se pudo invertir la relación: la cuarta fase escribe de vuelta al canal con un reconciliador que verifica cada escritura, y la quinta hace que la tienda web deje de tener stock propio y sirva la disponibilidad del central. Las dos últimas abren el camino a un segundo canal y a la capa de analytics.

Todo corre en Node 22 con TypeScript sobre PostgreSQL, con validación de esquemas en las fronteras, registro estructurado de eventos y generación de reportes en CSV y PDF para la gente que opera desde el escritorio, no desde una terminal.

emoji_events

Resultados

Las cifras de venta se publican solo en unidades. Los importes del negocio no se publican, por regla del propio proyecto.

lightbulb

Lo que aprendí

Que la parte difícil de integrar canales no es la API: es decidir quién tiene la razón cuando dos sistemas discrepan. Ese acuerdo hay que tomarlo antes de escribir la primera línea, porque después el código lo toma solo y casi siempre mal.

Que un sistema que puede apagarse rápido se enciende antes. La escalera de activación parecía burocracia y resultó ser lo que hizo posible empezar: nadie tuvo que apostar la cuenta a que el software estaba bien a la primera.

Y que la honestidad de un dato vale más que su disponibilidad. Reportar ausencia en vez de un cero cómodo es incómodo la primera vez que alguien lo ve en pantalla, y es lo que evita que se tomen decisiones con números inventados.

¿Vendes en más de un canal y el inventario no cuadra? Este es exactamente el problema que resuelvo.

mail Hablemos