En producción

Vendevo — Ventas a Detalle

Un solo sistema para vender en ruta y en tienda, con inventario por lotes y un cierre diario que el servidor calcula en vez de preguntar.

Cliente
DamshFood
Rol
Diseño y desarrollo completo (solo)
Periodo
Junio — Actualidad
Estado
En producción
Panel de cierres del gestor
TypeScript C# Vue.JS Shell Docker JavaScript HTML TailwindCSS SQL Astro JSX

El punto de partida

Un distribuidor de alimentos vende desde camionetas. Cada mañana sale un vendedor con producto y regresa por la tarde con dinero, mercancía sobrante y una libreta. Vender nunca fue el problema: el problema era cerrar el día. Cuánto salió, cuánto se vendió, cuánto quedó en el camión, cuánto se dañó — y que el vendedor, el almacén y la gerencia estuvieran viendo el mismo número.

El encargo no fue «hagamos un ERP». Fue: que la orden de entrega que arma el almacén sea la misma que el vendedor recibe, que cada venta descuente del camión correcto, y que el cierre del día deje de depender de la memoria de nadie.

Cinco roles, un mismo ciclo operativo

  • Vendedor — recibe la orden de entrega, vende de ruta desde el teléfono y cierra su día en campo
  • Gestor de inventario — maneja el almacén de producto terminado, emite las órdenes de entrega y autoriza los cierres
  • Gerente — lee tableros, resuelve cancelaciones, define metas y ofertas, administra usuarios
  • Vendedor de tienda — el mismo ciclo, pero en punto de venta fijo: sin vehículo y sin visitas a cliente
  • Gestor de vehículos — seguros, verificaciones, odómetro y mantenimientos de la flota

Cada rol ve solo su operación. No hay un menú gigante con opciones deshabilitadas: el sistema decide a qué aplicación entra cada quien desde el momento del login.

El inventario vive en lotes, no en un número

La existencia de un producto no es un campo que se suma y se resta. Es la suma de sus lotes activos, y cada salida se sirve PEPS por fecha de entrada. Eso suena a detalle contable hasta que hay que rastrear una merma o responder qué lote se le entregó a qué cliente: entonces es la diferencia entre tener una respuesta y tener una suposición.

Un número de existencia te dice cuánto hay. Un lote te dice de dónde salió.

La venta se descuenta del camión, no del almacén

Cuando el vendedor cobra, el sistema no valida contra el inventario general: valida contra lo que ese vendedor trae físicamente arriba. La carga del día es un documento por lote, y cada venta lo va consumiendo en orden. El vendedor nunca elige lote ni piensa en ello — simplemente no puede vender lo que no cargó.

El saldo tampoco se pierde entre días. Lo que sobra al cerrar no regresa al almacén: se queda como saldo del vendedor y arranca la jornada siguiente junto con lo que traiga la nueva orden. Solo sale del inventario lo que se documenta como dañado, con motivo y evidencia fotográfica.

El cierre calcula; no pregunta

Ésta fue la decisión de diseño que más cambió la operación. El cierre del día no le pide al vendedor que declare cuánto le quedó: lo deriva en el servidor, lote por lote, a partir de lo que cargó menos lo que vendió menos lo que declaró como merma.

  • El vendedor captura solo la merma, y solo de los lotes que tuvieron alguna
  • Un día normal se cierra sin escribir un solo número
  • El conteo físico del gerente es una función aparte, que compara sistema contra contado

Apelar no frena el día

Si el vendedor cuenta y no le cuadra con lo calculado, puede apelar: manda su cantidad contada y el motivo. El cierre pasa igual — nadie se queda varado en la banqueta a las nueve de la noche discutiendo dos piezas. La fila queda marcada como pendiente y el gestor de inventario la resuelve después: si la acepta, ajusta el saldo por la diferencia; si la rechaza, no se mueve nada.

Los dos números —el calculado y el reclamado— se conservan juntos en el registro. Ninguno pisa al otro. Y el cierre no se puede autorizar mientras quede una diferencia pendiente, de modo que una apelación no se entierra aprobando rápido.

Dos backends sobre una misma base

La ruta corre sobre Node.js con TypeScript, y la operación de tienda y flota sobre una API en C#. Comparten dominio, sesión y base de datos: quien emite los tokens es uno solo, el otro únicamente los valida, y el usuario nunca se entera de que cambió de aplicación.

Que dos servicios escriban las mismas tablas obliga a ser explícito con la concurrencia. Cada escritor de una jornada toma primero un bloqueo de fila en la transacción; los folios se asignan dentro de la misma transacción y se reintentan si chocan; y las resoluciones —cobrar, aprobar, cancelar— se hacen comparando el estado esperado antes de escribir, para que dos gerentes en dos teléfonos no resuelvan lo mismo dos veces.

Los detalles que aparecen a los seis meses

  • Precios congelados por línea — cada venta guarda precio de lista, precio cobrado, descuento y la oferta aplicada, para que un ticket siga siendo auditable aunque la promoción cambie mañana
  • Cancelaciones en dos pasos — el vendedor solicita, el gerente aprueba; al aprobarse, las piezas vuelven al camión y el cierre se recalcula solo si ya existía
  • Auditoría — cada operación deja rastro de quién la hizo y cuándo
  • Zona horaria explícita — el servidor puede arrancar en UTC, pero un día de venta es un día de calendario mexicano, y el sistema los distingue
  • Degradación limpia — las fotos de evidencia viven en almacenamiento externo; si ese servicio no está configurado, se desactiva la carga de imágenes y todo lo demás sigue funcionando

Puesta en producción

El sistema corre en un VPS con Docker Compose, base MariaDB y nginx al frente con HTTPS. Los despliegues, los respaldos y las restauraciones pasan por scripts propios en lugar de comandos a mano, y el login está protegido contra tráfico automatizado.

No es un tablero que alguien consulta. Es la herramienta con la que se vende, se carga y se cierra todos los días: si se detiene, se detiene la ruta.

Las capturas de este caso son una recreación de la interfaz con datos de ejemplo: el sistema real opera con información del cliente y no es pública.

¿Tienes algo parecido en mente?

Trabajamos contigo desde la idea hasta producción

Cuéntanos qué necesitas y valoramos juntos la mejor solución. La cotización es sin costo.