
CRM + Ecommerce: un solo cliente, no dos
Integración entre HubSpot y la tienda online: pedidos, carritos y valor de cliente dentro del CRM, y automatizaciones que se disparan con datos reales de compra.
El equipo deja de saltar entre dos sistemas
Plataforma multi-sede para una cadena de 17 centros podológicos: agenda, punto de venta, facturación SUNAT, inventario y suscripciones, en web y móvil.

Una cadena de centros podológicos con 17 sedes en Lima y Callao operaba con la agenda, el cobro, el inventario y las suscripciones repartidos entre herramientas distintas. Con una sede eso se sostiene. Con diecisiete, nadie sabe cuánto se facturó hoy sin sumar a mano, el stock se descubre agotado cuando ya falta, y cada boleta emitida depende de que alguien recuerde qué serie corresponde a qué local.
El problema real no era "falta un software". Era que cada dato vivía en un sitio distinto y ninguno cuadraba con el otro.
En un centro podológico conviven cosas que en otros negocios están separadas: una agenda clínica, un punto de venta con facturación electrónica, un almacén y un modelo de suscripciones de tratamientos recurrentes. Y encima, dos razones sociales distintas emitiendo comprobantes, cada una con sus propias series por sede.
Comprar cuatro herramientas y conectarlas después sale caro y se rompe por las costuras. Justo por donde pasa el dinero.
Es un monorepo con cinco aplicaciones sobre una base de datos y un contrato de API compartidos:
| App | Qué es |
|---|---|
api | Backend NestJS 10: REST /v1, WebSockets y los crons de mensajería |
admin-web | Panel administrativo React 18 + Vite, la app principal |
admin-mobile | App del personal en Expo / React Native, distribución interna por EAS |
worker | Jobs en segundo plano con BullMQ |
customer-portal | Autoservicio del paciente — hoy en modo demo |
Debajo, PostgreSQL 15 con Prisma (unos 48 modelos y 26 migraciones) y Redis 7 haciendo tres trabajos a la vez: caché, colas y adapter de Socket.IO para que el realtime funcione con varias instancias del API.
La pieza que más ha rendido es packages/api-client: Swagger escribe el openapi.json, openapi-typescript genera los tipos y openapi-fetch los consume. El contrato entre el backend y el panel web lo verifica el compilador, no una revisión manual. Cuando cambia un endpoint, el frontend deja de compilar antes de que nadie despliegue nada.
branchId con índices compuestos. Añadirlo más tarde habría obligado a migrar todas las tablas.appointment.read, sale.refund…) con usuarios asignados a sedes, espejados en web y móvil desde el mismo paquete compartido.Un sistema en producción con usuarios reales no está "terminado", y conviene decirlo:
Lo que más cambió el sistema no fueron las funciones grandes, sino los detalles que solo aparecen usándolo: que la caja necesitaba egresos además de ingresos, que el stock bajo debía avisar en el panel y no en un reporte, que la agenda se consulta más por día que por semana.
Y una lección que vale para cualquier integración fiscal: la emisión electrónica es un sistema externo que va a fallar. Diseñarla como "la venta no existe hasta que SUNAT responda" habría convertido cada caída del proveedor en una caja parada.
Diecisiete sedes operando sobre un mismo sistema: la agenda, el cobro, la facturación electrónica, el inventario y las suscripciones ocurren en el mismo lugar, en web y en móvil, y al cerrar el día las cifras salen del sistema en vez de reconstruirse a mano.

Integración entre HubSpot y la tienda online: pedidos, carritos y valor de cliente dentro del CRM, y automatizaciones que se disparan con datos reales de compra.
El equipo deja de saltar entre dos sistemas