Files
AgendaPro/AUDIT.md
T
AgendaPro DevandClaude Opus 5 6d67b23e55 feat(platform): multi-tenancy con credenciales por negocio y sincronización por id
El backend Postgres de `platform/` asumía un solo negocio con un solo token del
CRM. Este cambio lo convierte en una plataforma multi-cuenta y añade la
sincronización selectiva de las cinco entidades del encargo.

## Multi-tenancy

El `locationId` ya era por negocio, pero el token vivía en la variable de entorno
`CRM_TOKEN`, una sola para todo el proceso. Con dos negocios eso usaba el token
del primero contra la subcuenta del segundo: 401 en el mejor caso, escritura en
la subcuenta equivocada en el peor.

- `lib/crypto.ts` — AES-256-GCM para los tokens. Autenticado a propósito: una
  fila manipulada hace que el descifrado FALLE, en vez de devolver basura que
  acabaríamos mandando como credencial al CRM. La clave maestra vive en
  `CRM_MASTER_KEY`, fuera de la base.
- `crm/ctx.ts` — `CrmCtx { businessId, locationId, token }` sustituye al
  `locationId: string` suelto que viajaba por once firmas. Es un objeto y no dos
  parámetros porque dos `string` seguidos se cruzan sin que el compilador diga
  nada, y cruzarlos aquí manda el token de un cliente a la subcuenta de otro. Es
  el único sitio donde el token existe descifrado, y solo en memoria.
- `crm/client.ts` — `CrmOptions.token` pasa a ser OBLIGATORIO, sin valor por
  defecto: olvidarlo es ahora un error de compilación. El estrangulador pasa a
  ser por token y aprende la cuota de las cabeceras `x-ratelimit-*`, que declaran
  100 peticiones por 10 s — el cliente iba 6,5x por debajo con una estimación.
- Migración 003: credencial cifrada, calendario y la red de seguridad de mensajes
  POR NEGOCIO. Como variable global decidía por todas las cuentas a la vez.

Lo único de la credencial que sale del servidor es la huella de 6 caracteres.

## Consola de superadministración

`/api/admin`, solo para el rol `admin`: alta de cuentas con su dueña en una
transacción, vínculo, desvínculo y suspensión. Las credenciales se COMPRUEBAN
contra el CRM antes de guardarse — un token sin validar traslada el fallo al
primer intento de sincronizar, lejos de donde se cometió. El error distingue
«token inválido» de «subcuenta inexistente» de «token de otra subcuenta».

Pantalla en `/admin/cuentas`, verificada en navegador: el campo del token es de
contraseña y viene vacío, porque no hay valor que traer.

## Sincronización por identificador

`POST /api/crm/sync/:entidad/:id` para contacto, conversación, mensaje, cita y
servicio. La dirección la decide la entidad: las tres primeras se TRAEN porque el
CRM es su dueño; las dos últimas se EMPUJAN, porque el calendario del CRM tiene
una sola cita en dos años y su catálogo de servicios está vacío.

- `crm/conversations.ts` — lectura por id de conversaciones y mensajes sueltos.
- `crm/syncConversations.ts` — el espejo persistido. Las tablas existían desde
  002_crm.sql y nadie escribía en ellas: la bandeja consultaba el CRM en vivo.
- `crm/calendars.ts` — escritura de citas al calendario. `isoConDesplazamiento`
  escribe la hora de pared del negocio con su desplazamiento; `toISOString()`
  habría movido la hora que el CRM enseña en su interfaz.
- `crm/services.ts` — publicación de servicios al catálogo.

## Verificado contra la subcuenta real, no deducido

Las cinco entidades se ejercieron contra el CRM del cliente. Las escrituras van
en un ciclo crear → releer → borrar → confirmar borrado, con la limpieza en un
`finally`, y antes se comprobó que el borrado existe: preguntar si se puede
deshacer ANTES de escribir en el CRM de un cliente, no después. La subcuenta
quedó como estaba.

47 hallazgos medidos en `crm/HALLAZGOS.md`, y la referencia de endpoints en
`crm/API.md`, con la lista explícita de dónde la documentación oficial falla.

110 pruebas de plataforma en verde, typecheck limpio, build correcto. El backend
de demo de `server/` no se ha tocado y sigue con sus 43 pruebas.

## Deuda conocida, dicha sin rodeos

- La bandeja de mensajes todavía lee en vivo del CRM, no del espejo.
- La autenticación sigue siendo el id del usuario en texto plano, también para el
  rol admin. Esta consola crea cuentas y guarda credenciales de clientes encima
  de esa base: no debe quedar expuesta a internet hasta endurecerla.

Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
2026-08-30 15:07:20 -06:00

194 lines
14 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# AgendaPro — Auditoría de producto e investigación de mercado
> Revisión: **2026-07-28**. Sustituye la versión anterior de este archivo, que daba por faltantes
> cinco módulos que ya están construidos (caja, comisiones, recordatorios, booking público y política
> de cancelación). Cada fila de las tablas de abajo se verificó contra el esquema de
> [server/db.ts](server/db.ts), los endpoints montados en [server/index.ts](server/index.ts) y las
> páginas de [src/pages/](src/pages/) — no contra memoria ni contra el documento anterior.
---
## 1. El producto que emulamos
AgendaPro es el software de agendamiento líder en Latinoamérica: **+20.000 negocios**, presencia en
**+100 países**, foco en México, Colombia, Argentina y Chile. Su promesa comercial es *«el único
software que ordena tu negocio y acelera su crecimiento un 82%»*.
**Verticales:** salones de belleza, spas, barberías, peluquerías, centros de estética, clínicas,
psicólogos, nutricionistas, fisioterapeutas, podólogos y bienestar en general.
### 1.1 La escalera de planes (esto *es* el producto)
El modelo de negocio de AgendaPro es la escalera de planes, y cada módulo está deliberadamente
asignado a un escalón. Precios de lista en USD (LatAm) y EUR (España):
| Plan | USD/mes | EUR/mes | Profesionales | Correos mkt | Qué añade sobre el plan anterior |
|---|---|---|---|---|---|
| **Individual** | $9 | €10 | 1 | 500 | Agenda ilimitada + presencia en Marketplace, CRM, recordatorios automáticos, sitio de reservas, reportes de gestión, sistema de caja, niveles de acceso, control de ocupación, gestión de presupuesto |
| **Básico** | $29 | €19 | hasta 20 | 1.000 | **Inventario** (control + alertas de stock bajo), **Comisiones** (cálculo automático) |
| **Premium** ★ | $59 | €59 | hasta 20 | 2.000 | **Encuestas de satisfacción**, **Fichas personalizables**, **Ficha clínica + consentimiento informado**, **Giftcards**, **Presupuestos**, email automático de cumpleaños, sitio con URL y colores propios |
| **Pro** | $199 | €199 | hasta 20 | 5.000 | **Acceso a API**, soporte personalizado, integración Google Analytics / Meta Pixel |
★ = el que ellos marcan como «más popular».
**Complementos que se cobran aparte** (dato relevante: lo que su marketing presenta como bandera
central no viene incluido en ningún plan):
| Add-on | Precio | Detalle |
|---|---|---|
| WhatsApp | desde $7 USD / €5 al mes | **50 mensajes mensuales** |
| Videoconferencia | desde $11 USD al mes | pack de 2.500 min |
| Charly (asistente de marketing con IA) | desde $55 USD al mes | pago por resultados |
| Facturación electrónica | «próximamente» | — |
Prueba gratuita: **7 días**.
### 1.2 Módulos que anuncian, agrupados como ellos los agrupan
- **Citas:** agenda online, sitio de reservas 24/7, recordatorios automáticos por WhatsApp y email,
«IA de recordatorios por WhatsApp», administración de horarios.
- **CRM:** base de datos de clientes, historial de visitas, control de sesiones y tratamientos, ficha
del cliente, promociones personalizadas.
- **Inventario:** control de inventario, alertas de inventario bajo, comisiones por venta de productos.
- **Marketing y fidelización:** integración con Google Reserve en Google My Business, LinkPro (tarjeta
de presentación para redes), campañas de email marketing, programas de lealtad y giftcards, acceso
al **marketplace** de servicios.
- **Pagos:** registro y reportes de pagos, pagos con terminal, pagos online y link de pago,
facturación / CFDI, pago de sesiones.
- **Control del negocio:** control de caja, reportes de ingresos y egresos, reportes de ventas con IA,
control de múltiples sucursales, cálculo automático de comisiones.
- **Gimnasios / fitness:** agenda de clases grupales, control de membresías, gestión de entrenadores.
- **Multi-sucursal:** varias sedes desde una sola cuenta, con reportes consolidados.
### 1.3 Dónde AgendaPro es débil (según reseñas de usuarios)
Esto importa: son los huecos donde un competidor puede ganar en lugar de empatar.
1. **Fichas clínicas genéricas.** No tienen CIE-10 electrónico ni plantillas por especialidad. Es la
queja más concreta y sistemática.
2. **Permisos poco granulares por sede.** Problema real para clínicas medianas y grandes: no se puede
acotar bien qué ve cada persona en cada sucursal.
3. **Precio y alzas periódicas.** Usuarios reportan subidas recurrentes y retiro de funcionalidades de
planes que ya pagaban.
4. **Prueba de 7 días**, contra 14+ de la competencia.
5. **WhatsApp de pago y racionado** (50 mensajes/mes desde $7), siendo el canal que su propio marketing
pone al frente.
6. **Curva de costo para negocios chicos:** los planes intermedios superan los $40–80 USD/mes, lo que
los saca de rango para un negocio de 1–3 personas.
### 1.4 El marco competitivo, y la parte que no se resuelve con features
| | Modelo | Implicación |
|---|---|---|
| **AgendaPro** | Suscripción por escalones + add-ons | Ingreso predecible; el cliente paga antes de ver valor |
| **Fresha** | **$0 de mensualidad**; comisión sobre clientes nuevos del marketplace + ~2,19% + $0,20 USD por transacción | Sin barrera de entrada; monetiza adquisición y pagos |
La conclusión honesta de la investigación: **el foso de los dos líderes no es el software, es el
marketplace.** Fresha y AgendaPro traen clientes nuevos al negocio; eso no se replica implementando
módulos. Cualquier plan de equivalencia funcional debe asumir que empata en producto y no en
distribución.
### 1.5 Datos de industria (con su fuente, y una corrección)
- **No-shows: 10%–30%** según sector; **15%–25%** en belleza y estética. En clínicas de bienestar
(España) 12%–19%, hasta **23%** en odontología y masajes.
- **Recordatorios por WhatsApp a 3 días y 24 h antes reducen las ausencias entre 30% y 50%.**
- **Depósitos recomendados: 20%–30%** del valor del servicio, y el depósito debe **abonarse al costo
final**, no ser un cargo extra.
- **Ventana de cancelación estándar: 24–48 h**; 72–96 h en sectores de alta demanda.
- **Escala de penalización de ejemplo:** gratis con más de 48 h; 25% entre 48 y 24 h; 50% entre 24 y
12 h; 100% el mismo día.
- Coste ilustrativo: 5 citas perdidas por semana a 60 € ≈ **18.000 €/año** de ingreso perdido.
> **Corrección al documento anterior:** la versión previa de este archivo afirmaba «en MX la
> inasistencia es 15–35%» sin fuente. No encontré respaldo para el techo del 35%. Los rangos de arriba
> sí están sostenidos. Además, el artículo de política de cancelaciones de AgendaPro —citado antes como
> fuente de cifras— **no contiene estadísticas de no-show**: solo recomendaciones. Las cifras de arriba
> vienen de fuentes de industria independientes.
---
## 2. Auditoría: qué tiene hoy AgendaMax
Verificado contra esquema, endpoints y páginas.
### 2.1 Construido y funcionando
| Capacidad de AgendaPro | Estado | Evidencia en el repo |
|---|---|---|
| Agenda / calendario | ✅ | [CalendarPage.tsx](src/pages/CalendarPage.tsx) con FullCalendar, 3 breakpoints, drag & drop; `/api/appointments` |
| Sitio de reservas público 24/7 | ✅ | `/b/:slug` → [BookingPage.tsx](src/pages/public/BookingPage.tsx); `/api/public/:slug`, `/slots`, `/book` |
| Guard anti doble-reserva | ✅ **superior** | `runInTransaction` + `BEGIN IMMEDIATE` + `isAvailable` + INSERT en la misma tx ([scheduling.ts](server/lib/scheduling.ts)) |
| Auto-asignación de especialista | ✅ **no lo tiene AgendaPro** | `autoAssign`, `pickBestSlotEmployee`, `scoreCandidate` con especialidades y `efficiency_score` |
| Horarios por negocio y por empleado | ✅ | `businesses.working_hours`, `employees.working_hours` (JSON 1..7, `null` = hereda) |
| CRM / ficha e historial de cliente | ✅ | `clients` + [ClientDetailPage.tsx](src/pages/ClientDetailPage.tsx) con `no_show_count` |
| Comisiones | ✅ | `services.commission_pct`, `employees.commission_pct`, snapshot en `tickets.commission`; `/api/dashboard/commissions`, `/api/me/commissions`, [MyPerformancePage.tsx](src/pages/MyPerformancePage.tsx) |
| Control de caja / cierre diario | ✅ | `cash_sessions` (apertura, cierre, `expected_amount` → descuadre) + `cash_entries` (income/expense); `/api/cash/*`; [CashPage.tsx](src/pages/CashPage.tsx) |
| Reportes de gestión | ✅ | 8 endpoints en `/api/dashboard/*` + [DashboardPage.tsx](src/pages/DashboardPage.tsx) con recharts |
| Reseñas / ratings | ✅ | tabla `reviews` (AgendaPro no lo vende como módulo) |
| Multi-tenant + consola de plataforma | ✅ **superior** | `/api/admin/*`, plantillas por vertical, alta de negocio, `reset-demo`, cascade |
| Sitio de reservas con marca propia | ✅ | landing + booking con paleta heredada del panel (AgendaPro lo cobra en Premium) |
| App instalable | ✅ | PWA con service worker y prompt de instalación (equivalente funcional a su app nativa) |
| Aislamiento por tenant verificado | ✅ | `test:e2e` y `test:admin` comprueban 403 y no-visibilidad cruzada |
### 2.2 Construido a medias — el hueco entre «configurable» y «operante»
Estas tres son las más engañosas de la auditoría: existen en el esquema y en la UI, pero **no cierran
el ciclo**. Un demo puede mostrarlas y no hacen nada.
| Capacidad | Qué hay | Qué falta |
|---|---|---|
| **Política de cancelación y depósito** | `businesses.cancel_window_hours`, `cancel_penalty_pct`, `require_deposit`, `deposit_pct`; `GET /api/appointments/:id/cancellation-policy`; editor en [SettingsPage.tsx](src/pages/SettingsPage.tsx) | **Nunca se cobra nada.** No hay captura de depósito en el booking, ni aplicación de la penalización a un ticket, ni registro del cargo. La política se configura y se consulta; no tiene efecto económico. |
| **Recordatorios automáticos** | tabla `notifications` (canales `whatsapp`/`sms`/`email`, tipos `confirmation`/`reminder`/`cancellation`/`follow_up`/`review`), `/api/notifications` con `regenerate`/`send`/`cancel`/`stats`, [NotificationsPage.tsx](src/pages/NotificationsPage.tsx) | Es una **bandeja simulada**: `send` marca `sent` sin salir a ningún proveedor. Aceptable para demo, pero no equivale al módulo real. |
| **Niveles de acceso y permisos** | 3 roles fijos (`admin`/`owner`/`employee`) con autorización real en middlewares y aislamiento probado | Sin permisos granulares. Nota: **AgendaPro también es débil aquí** (§1.3), así que es una oportunidad, no solo una deuda. |
### 2.3 Brechas reales — lo que no existe
Ordenado por el escalón de plan al que pertenece en AgendaPro, porque eso define qué se puede
reclamar como «equivalente al plan X».
| # | Brecha | Plan AgendaPro | Notas de implementación |
|---|---|---|---|
| 1 | **Inventario de productos + alertas de stock bajo** | Básico | No hay tabla de productos ni movimientos de stock |
| 2 | **Venta multi-ítem (POS real)** | Básico/Individual | **Bloqueo estructural:** hoy `tickets` es 1 ticket = 1 cita = 1 servicio (`service_id` único). No se puede vender «corte + shampoo + giftcard» en una venta |
| 3 | **Comisión por venta de productos** | Básico | Depende de 1 y 2 |
| 4 | **Encuestas de satisfacción / NPS** | Premium | `reviews` cubre la reseña puntual, no la campaña de medición |
| 5 | **Fichas personalizables + ficha clínica + consentimiento informado** | Premium | Hoy solo `clients.notes`, texto libre. **Aquí AgendaPro es débil** (§1.3) |
| 6 | **Giftcards** | Premium | Depende de 2 |
| 7 | **Presupuestos** | Premium | Depende de 2 |
| 8 | **Paquetes, sesiones y membresías** | transversal | «Control de sesiones y tratamientos», «pago de sesiones», membresías de gimnasio. Nada de saldo prepago |
| 9 | **Clases grupales (capacidad > 1)** | fitness | `appointments` es estrictamente 1:1. Toca la invariante de agendado |
| 10 | **Multi-sucursal con reportes consolidados** | transversal | Tenemos multi-*tenant*, no multi-*branch*. Cambio de esquema profundo |
| 11 | **Email marketing / campañas / cumpleaños** | Individual+ (con cuotas por plan) | `notifications` es transaccional, no de campañas |
| 12 | **Pagos en línea / link de pago / cobro anticipado** | transversal | Cero captura de pago. Es lo que bloquea §2.2 |
| 13 | **Facturación electrónica / CFDI** | add-on | En AgendaPro está «próximamente» |
| 14 | **Google Reserve / Google Calendar** | Individual+ | Integración externa |
| 15 | **Control de ocupación (% de agenda ocupada)** | Individual | [metrics.ts](server/lib/metrics.ts) solo tiene `cancelRate`, `noShowRate`, `completionRate`. **Barato y visible** |
| 16 | **Gating por plan** | — | `businesses.plan` existe pero **no restringe nada**. Sin esto, la escalera de planes —que es el producto de AgendaPro— no se está emulando |
| 17 | **Videoconferencia** | add-on | |
| 18 | **API pública documentada** | Pro | |
| 19 | **Marketplace** | Individual+ | **El foso real (§1.4).** No es un módulo, es un canal de distribución |
| 20 | **Reportes con IA** | add-on (Charly) | |
---
## 3. Lectura de la auditoría
Tres conclusiones que condicionan cualquier plan:
1. **La brecha no es de agenda, es de dinero.** Todo lo que falta cuelga de dos cosas que no existen:
la **venta multi-ítem** (brecha 2) y la **captura de pago** (brecha 12). Inventario, giftcards,
presupuestos, paquetes, comisión de producto y el cierre de la política de cancelación dependen de
una o de las dos. Atacar módulos sueltos antes de eso produce features que no se pueden conectar.
2. **Ya somos mejores en el núcleo de agendado.** Auto-asignación por especialidad y eficiencia, guard
transaccional anti doble-reserva y consola de plataforma multi-tenant no están en la oferta de
AgendaPro. La equivalencia que falta es comercial y administrativa, no de calendario.
3. **La escalera de planes no se está emulando.** Es lo más desalineado del proyecto respecto al
producto real: `businesses.plan` es decorativo. Es además de las cosas más baratas de arreglar y la
que más hace que el demo se lea como un SaaS y no como una app.
El backlog priorizado y la descomposición en sub-proyectos viven en el spec de diseño de esta tanda,
no en este archivo. Este documento es la línea base de hechos.