Saltar al contenido principal
Logo huiro.dev
Blog
automatizaciónretailinventariobsaledashboard

Sistema de inventario en tiempo real que ahorró 40% del tiempo en reportes

Conecté 3 tiendas retail chilenas con sus sistemas POS para automatizar el control de stock y reportes. 6 semanas de desarrollo que eliminaron 12 horas semanales de trabajo manual.

Mauricio Cifuentes

13 de abril de 20265 min
Sistema de inventario en tiempo real que ahorró 40% del tiempo en reportes

Hace dos meses, Carlos —dueño de 3 tiendas de ropa en Providencia— me contactó porque estaba perdiendo 12 horas a la semana haciendo reportes de inventario a mano. Sus vendedoras le mandaban Excel por WhatsApp, él los consolidaba en la madrugada, y siempre había diferencias que no cuadraban. La paja.

El problema

Cada tienda usaba Bsale como POS, pero Carlos no tenía visibilidad en tiempo real del stock entre las 3 sucursales. Los problemas eran concretos:

  • Productos que se agotaban sin aviso (perdía ventas)
  • Reportes manuales que tomaban 4 horas cada lunes
  • Diferencias de inventario que aparecían semanas después
  • Transferencias entre tiendas coordinadas por WhatsApp
  • Cero visibilidad de productos de baja rotación

En enero perdió $800.000 CLP porque no sabía que se habían agotado las poleras más vendidas en la tienda de Las Condes, mientras en Providencia tenía 50 unidades sin moverse.

Cómo funciona

Buildeé un sistema que conecta directamente con la API de Bsale y procesa cada venta en tiempo real. Nada de imports manuales ni Excel.

Flujo del sistema de inventario automatizado

Cargando diagrama...

El flujo es simple: cada vez que se registra una venta en cualquier POS, Bsale dispara un webhook. Mi sistema valida el stock, actualiza la base de datos y, si algo está bajo el mínimo, manda alertas automáticas por WhatsApp.

Paso a paso

Conexión con Bsale API

Primero configuré la integración con Bsale. Su API es decente, aunque la documentación tiene algunos vacíos.

// lib/bsale-client.ts
export class BsaleClient {
  private baseUrl = 'https://api.bsale.io/v1'
  private token: string

  constructor(token: string) {
    this.token = token
  }

  async getProducts(storeId: number) {
    const response = await fetch(`${this.baseUrl}/products.json?office=${storeId}`, {
      headers: {
        'Authorization': `Bearer ${this.token}`,
        'Content-Type': 'application/json'
      }
    })
    
    if (!response.ok) {
      throw new Error(`Bsale API error: ${response.status}`)
    }
    
    return response.json()
  }

  async getStock(productId: number) {
    const response = await fetch(`${this.baseUrl}/stocks.json?variant=${productId}`)
    return response.json()
  }
}

Webhook Handler para ventas en tiempo real

Lo más crítico era procesar las ventas al instante. Bsale manda un webhook cada vez que se completa una transacción.

// app/api/webhooks/bsale/route.ts
import { NextRequest } from 'next/server'
import { updateInventory } from '@/lib/inventory'
import { sendStockAlert } from '@/lib/notifications'

export async function POST(request: NextRequest) {
  const payload = await request.json()
  
  if (payload.event === 'document_created' && payload.document.documentType.id === 1) {
    // Es una boleta/factura (venta real)
    const details = payload.document.details
    
    for (const item of details) {
      const newStock = await updateInventory({
        productId: item.variant.id,
        storeId: payload.document.office.id,
        quantity: -item.quantity // Resta del inventario
      })
      
      // Alerta si queda poco stock
      if (newStock <= item.variant.minimumStock) {
        await sendStockAlert({
          productName: item.variant.description,
          currentStock: newStock,
          storeId: payload.document.office.id
        })
      }
    }
  }
  
  return Response.json({ received: true })
}

Dashboard de control

El dashboard lo hice con Next.js y Chart.js. Carlos quería ver todo de un vistazo: stock actual, ventas del día, productos críticos.

// components/InventoryDashboard.tsx
export default function InventoryDashboard() {
  const { data: stores } = useSWR('/api/stores', fetcher)
  const { data: lowStock } = useSWR('/api/inventory/low-stock', fetcher)
  const { data: todaySales } = useSWR('/api/sales/today', fetcher)

  return (
    <div className="grid grid-cols-1 lg:grid-cols-3 gap-6">
      <StockAlertsCard items={lowStock} />
      <SalesTodayCard sales={todaySales} />
      <StoreComparisonCard stores={stores} />
      
      <div className="lg:col-span-3">
        <ProductMovementChart />
      </div>
    </div>
  )
}

Reportes automáticos

Configué un cron job que corre todos los días a las 6 AM y genera el reporte consolidado. Se manda por email y queda guardado en el sistema.

// lib/cron-jobs.ts
export async function generateDailyReport() {
  const yesterday = new Date()
  yesterday.setDate(yesterday.getDate() - 1)
  
  const report = await generateReport({
    startDate: yesterday,
    endDate: yesterday,
    includeAllStores: true
  })
  
  await sendReportEmail({
    to: process.env.OWNER_EMAIL,
    subject: `Reporte diario - ${format(yesterday, 'dd/MM/yyyy')}`,
    reportData: report
  })
  
  console.log(`Daily report sent for ${format(yesterday, 'dd/MM/yyyy')}`)
}

Resultados

Después de 6 semanas en producción, los números hablan solos:

  • 40% menos tiempo en reportes: de 12 horas semanales a 7 horas
  • 85% menos errores de inventario (antes 15-20 diferencias por semana, ahora 2-3)
  • $1.2M CLP extra en ventas por mejor distribución de stock entre tiendas
  • Alertas en 30 segundos cuando un producto se agota
  • 100% automatización en reportes diarios

Carlos ahora ve el stock de las 3 tiendas desde el celular y sabe al instante cuándo transferir productos entre sucursales.

Lo que aprendí

  • Los webhooks de Bsale a veces llegan duplicados. Tuve que implementar idempotencia con una tabla de eventos procesados.
  • La API tiene rate limits no documentados. Después de 100 requests por minuto empieza a devolver 429. Implementé un sistema de cola con Redis.
  • Las validaciones de stock son críticas. Un bug me dejó inventario negativo durante 3 días hasta que Carlos me avisó.
  • WhatsApp Business API vale cada peso. Las notificaciones por email se perdían, pero los WhatsApp los leen al toque.
  • El dashboard mobile es fundamental. Carlos revisa el inventario desde el auto entre tiendas, no desde el computador.

Para cerrar

Lo que más me sorprendió fue ver cómo un sistema relativamente simple puede cambiar completamente la operación de un negocio. Carlos pasó de estar estresado con Excel a tener control total de su inventario.

El stack completo está corriendo en un VPS de Hetzner por €20 al mes. Nada fancy, pero funciona perfecto para el volumen que manejan.

¿Tienes algún proceso manual que te está consumiendo horas? Me encantaría escuchar qué estás automatizando en tu negocio.

huiro.dev

¿Necesitas algo así para tu empresa?

Automatización, desarrollo a medida o infraestructura. Diagnóstico gratuito de 30 minutos.