# Configuración de una clínica / spa nuevo Guía de puesta en marcha de una instancia del sistema (producto multi-giro). Todo lo que sigue se configura desde la interfaz, **sin tocar código**. Para crear la instancia (base de datos, puerto, servicio, dominio, secretos y primer usuario admin) usa el script de provisioning: ver [`nueva-instancia.md`](../nueva-instancia.md). Esta guía empieza cuando la instancia ya está levantada y con sesión de administrador. La configuración vive en `Configuración` (panel con pestañas). El administrador ve todas; gerencia ve un subconjunto (no ve la pestaña Clínica con los datos de la instancia, ni Catálogos, API o Auditoría; en Licencia solo consulta el estado). Salvo donde se diga lo contrario, cada bloque tiene un interruptor de encendido y **apagado por defecto**: mientras no se active, la instancia se comporta como una instalación base. --- ## 1. Identidad de la clínica **Configuración → Clínica** (sección de identidad) | Qué | Efecto | |---|---| | Nombre comercial | Login, títulos, tickets, documentos y mensajes | | Logo claro / oscuro | Sidebar, login y tema claro | | Pie de página | Login y documentos impresos | | Color primario / acento | Se aplican en vivo sobre los 3 temas; vacío = tema original | | URL pública | Base para enlaces de pago, correos y el enlace de reservas | La marca es **por instancia y no vive en el código**: sale del parámetro `skeen.clinic.brand` (API `/skeen/frontend/v1/brand`). Por eso una misma build del frontend sirve a varias clínicas white-label sin recompilar. ## 2. Terminología (giro del negocio) **Configuración → Terminología** Elige el pack según el negocio y ajusta cualquier término a mano. El glosario tiene **24 términos** configurables (singular y plural de paciente, cita, visita, expediente, médico, personal, sucursal, cabina/sala, servicio, paquete, receta y tratamiento). | Término | Clínica (default) | Spa / Wellness | Estética | Dental | |---|---|---|---|---| | Paciente | Paciente | Cliente | Cliente | Paciente | | Cita | Cita | Reserva | Cita | Cita | | Visita | Visita | Sesión | Sesión | Consulta | | Expediente | Expediente | Ficha | Ficha | Expediente | | Médico | Médico | Terapeuta | Especialista | Odontólogo | | Cabina / sala | Cabina | Sala | Cabina | Consultorio | | Servicio | Servicio | Servicio | Servicio | Tratamiento | | Receta | Receta | Recomendación | Receta | Receta | | Tratamiento | Tratamiento | Tratamiento | Tratamiento | Procedimiento | El pack cambia menús, títulos, botones, estados y también los mensajes de notificación y el portal público de reservas. El pack `custom` parte de Clínica y aplica solo los ajustes manuales. ## 3. Idioma de la interfaz **Menú de usuario → idioma** o **Configuración → Clínica** (selector ES/EN). - El selector cambia el idioma de toda la interfaz a **Español** o **English**. - El idioma es **por navegador** (se guarda en el navegador del equipo), no por clínica ni por usuario; dos equipos pueden ver idiomas distintos. - La i18n y el glosario conviven: el **glosario** define el *vocabulario del negocio* (paciente↔cliente, cita↔reserva) y **solo existe en español**; en inglés se usa un mapa base equivalente. El **idioma** traduce el resto del texto. En español manda el término del pack configurado. ## 4. Operación **Configuración → Clínica** (datos y horarios) - Teléfono, email, dirección, sitio web, RFC, nota de ticket. - **Horario de apertura y cierre**: define la rejilla de la agenda (default 08:00–19:00). - Zona horaria (default `America/Tijuana`) y moneda base (default `MXN`). **Configuración → Sucursales**: alta de sedes (nombre, código, dirección, teléfono) y cuál es la predeterminada. Cada cita pertenece a una sucursal. Las sucursales con histórico no se borran: se desactivan (quedan archivadas). Vienen dos de ejemplo (Rosarito predeterminada y Tijuana). **Configuración → Categorías**: las 4 categorías base (Consulta, Tratamiento, Procedimiento, Paquete) se pueden renombrar y se pueden crear más (p. ej. "Depilación", "Faciales"). Las de sistema no se borran. ## 5. Catálogo y precios - **Servicios**: nombre, categoría, precio, duración (define el tamaño del bloque en la agenda), paquete y tasa de impuesto. - **Inventario**: artículos, consumibles (con caducidad, que bloquea la venta al vencer) y su tasa de impuesto. - Los precios se editan en Servicios / Inventario; el POS y la agenda los toman de ahí. ## 6. Impuestos **Configuración → Clínica → Impuestos** (apagado por defecto) - Activar impuestos, nombre (default `IVA`), tasa por defecto (default `0`) y catálogo de tasas (default `0, 8, 16`). - `Precios incluyen impuesto`: si el precio capturado ya trae impuesto, el sistema lo desglosa automáticamente. - Con impuestos activos: desglose en POS, tickets, estado de cuenta y reporte **Reportes → Impuestos** (por mes, servicio y sucursal, exportable). ## 7. Reservas en línea (portal del cliente) **Configuración → Reservas en línea** (apagado por defecto) - Activar el portal y compartir el enlace `/reservar` de la clínica (el propio panel muestra el enlace listo para copiar). - Opciones (con sus valores por defecto entre paréntesis): título y descripción visibles al cliente, confirmación manual o automática (`requiere_confirmacion` = sí), permitir elegir especialista (sí), permitir elegir sucursal (no), máximo de días a futuro (60), anticipación mínima en horas (2), teléfono de contacto, mensaje de confirmación y política de cancelación (permitir cancelar = sí, hasta 24 h antes). - El cliente recibe un enlace propio para consultar o cancelar su cita. Las reservas que llegan por el portal se distinguen en la agenda. ### 7.1 Anticipo (depósito) al reservar — apagado por defecto Dentro del mismo bloque, la llave `reservas_online.anticipo`. Con `activo = true`: - **Opciones** (defaults entre paréntesis): `tipo` (`porcentaje` | `monto_fijo`), `valor` (0; el % sobre el precio del servicio o el monto fijo), `moneda` (`MXN`), `horas_para_pagar` (24), `reembolsable` (sí), `texto_aviso` (''), y `permitir_simulado` (no, ver más abajo). - Al reservar, la cita queda **retenida sin confirmar** (badge **"Esperando anticipo"** en la agenda y en el detalle) y el portal muestra el monto y el botón para pagar. Al pagarse, la cita pasa a **confirmada** y se avisa por el motor de notificaciones. - Si no se paga dentro de `horas_para_pagar`, un proceso automático cancela la cita y **libera el horario**. - El anticipo se registra como pago real (`skeen.pago`) ligado a la cita y a la venta, por lo que aparece en cortes de caja y reportes; no se duplica con el resto que el cliente pague después en caja. - **Reembolso**: con `reembolsable = true`, al cancelar una cita con anticipo pagado, el pago se marca como reembolsado y la cita como "Anticipo reembolsado". Con `false`, el anticipo se retiene. ## 7 bis. Cuenta del cliente (portal de autoservicio) **Configuración → Portal del cliente** (apagado por defecto) Hoy el cliente final solo llega por enlaces con token (reserva, pago, formulario). Con este bloque se enciende una **cuenta propia** en `/mi-cuenta` donde entra con su teléfono o correo, **sin contraseña**, y ve su información. Apagado, nada cambia: el portal responde como deshabilitado y no aparece ninguna ruta ni enlace nuevo (ni para el cliente ni para el personal). - **Acceso sin contraseña**: el cliente escribe su teléfono (o correo) y recibe por el motor de notificaciones un **código de 6 dígitos** por WhatsApp o un **enlace de acceso** por correo, según `metodo` (`otp_whatsapp` | `enlace_correo` | `ambos`). El código se guarda **con hash**, caduca en 10 minutos, admite como máximo 5 intentos y tiene límite de peticiones por IP y por teléfono/correo (nadie puede probar códigos ni molestar a un cliente). El enlace de correo es de un solo uso y caduca en 30 minutos. - **Sesión propia del cliente**, separada de la del personal: token firmado con alcance limitado al paciente identificado y revocable (cerrar sesión lo invalida de inmediato; el personal puede cerrar las sesiones de un paciente). Un token de cliente **no** funciona en los endpoints del personal, y uno del personal **no** funciona en el portal. El portal nunca confía en un identificador enviado por el navegador: el paciente siempre sale de la sesión. - **Opciones** (defaults entre paréntesis): `activo` (no), `metodo` (`otp_whatsapp`), `requerir_correo_verificado` (no), `permitir_contrasena` (no; permite fijar contraseña tras entrar), `sesion_horas` (24), `permitir_reagendar` (sí), `horas_minimas_cancelar` (24), `mostrar_precios` (sí), `mostrar_recetas` (sí), `permitir_descargar_estado_cuenta` (sí) y `permitir_actualizar_datos` (sí). - **Qué ve el cliente**: Mis citas (próximas/pasadas; cancelar respetando `horas_minimas_cancelar` y reagendar reutilizando la disponibilidad real), Mis pagos (historial, saldo pendiente y estado de cuenta imprimible), Mi monedero (puntos y su equivalente en pesos y dólares), Mis paquetes (sesiones incluidas/usadas/restantes y vigencia), Mis membresías (plan, vigencia, próximo cobro, sesiones y adeudo, con botón para pagar el periodo pendiente por el enlace de cobro existente), Mis formularios (pendientes y respondidos con su firma), Mis documentos (recetas y estado de cuenta) y Mi perfil (datos de contacto y fotografías; edición solo si `permitir_actualizar_datos`, y nunca datos clínicos). - **Para el personal**: en la ficha del paciente (expediente) hay un bloque para **invitar al portal**, ver **si ya entró** (última entrada y sesiones abiertas) y **cerrar sus sesiones**; en la pestaña de configuración hay un listado de quién tiene acceso activo. Requiere los permisos nuevos `portal.invitar` y `portal.sesiones` (por defecto: administración, gerencia y recepción). - **Notificaciones**: usa dos plantillas nuevas del bloque de notificaciones, `plantilla_codigo_acceso` y `plantilla_enlace_acceso` (con advertencia de no compartir el código/enlace). En modo prueba del motor, la pantalla de acceso muestra el código o el enlace en pantalla para poder probar sin enviar nada real; con el modo prueba apagado nunca se expone. - **Enlaces cruzados**: el portal de reservas (`/reservar`) enlaza a la cuenta y la pantalla de acceso del personal muestra un aviso para clientes que llegan ahí por error. ## 8. Recursos (cabinas, salas y equipos) **Configuración → Recursos** (apagado por defecto) Sirve para agendar **recursos físicos** además del médico: cabinas, salas, consultorios o equipos. Con el módulo apagado la agenda se ve igual que siempre y no se valida nada. - **Catálogo** (`skeen.recurso`): nombre, código, tipo (Cabina / Sala / Consultorio / Equipo / Otro), activo, secuencia, sucursales donde aplica (vacío = todas), capacidad, costo por hora, color y notas. Vienen dos recursos semilla (Cabina y Consultoría). - **Opciones del bloque** (defaults): activo (no), `validar_solape` (sí), `obligatorio_al_agendar` (no), `permitir_multiples` (sí), `mostrar_en_agenda` (sí), `filtrar_como_doctores` (sí). - **Asignación**: en el modal de la cita se eligen uno o varios recursos. - **Validación de solape**: con `validar_solape` encendido el sistema impide dos citas en el mismo recurso a la misma hora e informa con qué cita choca. También respeta los bloqueos del recurso. - **Bloqueos por recurso**: un bloqueo de agenda puede ser de un médico, de un recurso o de ambos (`skeen.cita.bloqueo`): fecha, todo el día o rango horario, motivo y serie (grupo). - **Vista por recurso**: la agenda puede mostrar el recurso en la tarjeta y ofrecer el filtro por recurso junto al de doctores. El reporte **Reportes → Recursos** muestra ocupación e ingresos estimados por recurso. ## 9. Usuarios, roles y permisos **Configuración → Usuarios**: alta de usuarios, rol, menús visibles y, para agenda, las acciones específicas (consultar, agendar, editar, mover, eliminar, generar visitas, ver todas, bloquear). **Configuración → Roles**: tres pestañas: 1. **Roles**: crear roles propios del negocio (p. ej. Terapeuta, Recepción de fin de semana), renombrar, ajustar jerarquía y activar/desactivar. Vienen 10 roles base (admin, médico, recepción, enfermería, cosmetología, psicóloga, farmacia, gerencia, marketing, lectura). 2. **Permisos**: **75 permisos por acción agrupados en 17 grupos** (Agenda, Recursos, Pacientes, Visitas, Ventas, Inventario, Servicios, Reportes, WhatsApp, Notificaciones, Formularios, Membresías, Compras, Comisiones, Cobros y enlaces de pago, Configuración, Plataforma), con botones todo/ninguno. El backend los hace cumplir aunque el usuario manipule la interfaz. 3. **Menús**: qué sección ve cada rol. Los permisos de **Cobros** (defaults): `cobros.ver` (todos), `cobros.administrar` (admin/gerencia), `cobros.forzar` (admin/recepción/gerencia) y `cobros.cancelar` (admin/gerencia). Recomendación: partir del rol más parecido, ajustar y guardar. ## 10. Notificaciones **Configuración → Notificaciones** (apagado por defecto, con modo prueba) - Recordatorios automáticos antes de la cita (ventanas configurables de hasta 168 h; default 24 h y 2 h), confirmación al reservar en línea, felicitación de cumpleaños, seguimiento post-visita (horas configurable) y recuperación de pacientes que no se presentaron. - Canales: WhatsApp, correo o ambos (default WhatsApp). Plantillas editables con vista previa y variables (`{nombre}`, `{cita}`, `{servicio}`, `{fecha}`, `{hora}`, `{doctor}`, `{sucursal}`, `{telefono}`, `{enlace}`, `{formulario}` y las de membresía `{plan}`, `{membresia}`, `{monto}`, `{sesiones}`, `{periodo}`). - **Modo prueba** (default): registra lo que se habría enviado sin contactar a nadie — úsalo para revisar textos antes de encenderlo. - **Configuración → Notificaciones enviadas**: historial por paciente y por cita, con reintento. ## 11. Membresías **Configuración → Membresías** (apagado por defecto) y **Membresías** (menú). Una membresía **no es un paquete**: es un plan recurrente con vigencia, sesiones incluidas, cobro periódico, congelamiento y renovación. - **Planes** (`skeen.membresia.plan`): precio y moneda, periodicidad (semanal, mensual, trimestral, semestral, anual), sesiones incluidas por periodo (0 = ilimitadas), servicios y categorías incluidos, descuento en servicios no incluidos, cargo de inscripción, meses mínimos, permite congelamiento, sucursales y servicio con el que se cobra. - **Opciones del bloque** (defaults): activo (no), `dias_aviso_renovacion` (`[7, 1]`), `permitir_congelamiento` (sí), `dias_max_congelamiento_anio` (30), `renovacion_automatica_default` (no), `descuento_porcentaje_default` (0), `permitir_pausa_pago` (no), `generar_cargo_al_renovar` (sí), `dias_gracia` (5). - **Alta**: la membresía recibe folio `MEM-` y calcula su periodo actual, próximo cobro y fin de compromiso. - **Cobro**: genera una venta y un pago normales (aparece en cortes de caja como cualquier venta). También cobra la inscripción la primera vez. - **Consumo desde POS/expediente**: el consumo de sesiones **no se descuenta en silencio**; el POS o la cita llaman explícitamente a "usar sesión de membresía". Al cancelar una cita que usó una sesión, la sesión se libera. - **Congelamiento**: se congela con rango y motivo, respetando el tope anual; al descongelar se corre el fin de periodo y el próximo cobro. - **Renovación**: manual (avanza el periodo y genera el cargo si aplica) o automática por cron. - **Vencimientos y avisos**: un cron diario marca vencidas (con días de gracia), envía avisos de renovación en cada ventana y descongela al terminar la ventana. - **Reporte**: **Membresías → reporte** con ingreso recurrente por periodo. ### 11.1 Cobro automático de membresías — apagado por defecto Dentro del bloque, la llave `membresias.cobro_automatico`. Con `activo = true`: - **Opciones** (defaults entre paréntesis): `dias_antes` (3), `metodo` (`enlace` | `tarjeta_guardada`), `reintentos_dias` (`[1, 3, 7]`), `cancelar_si_no_paga` (no), `dias_gracia` (5) y `permitir_simulado` (no). - **Método `enlace`**: al acercarse el vencimiento el cron genera el **enlace de pago del periodo** y lo envía al paciente por el motor de notificaciones (plantilla `plantilla_cobro_membresia`, variable `{enlace}`). El periodo queda **pendiente** hasta que se pague; al pagarse pasa a **pagado** y la membresía **se renueva sola**. Reintentos: se vuelve a avisar en los días indicados respecto al vencimiento. Al agotarse los reintentos y los días de gracia, con `cancelar_si_no_paga` la membresía pasa a **vencida**; apagado, solo se marca el periodo y es el proceso normal de vencimiento el que la vence. - **Método `tarjeta_guardada`**: cargo sin presencia del cliente. **Requiere credenciales reales de Stripe y tarjetas guardadas** (que hoy no se persisten); sin ellas queda **inerte** (no cobra ni marca nada como pagado). ## 11 bis. Cobros en línea y pasarelas de pago Los anticipos de reservas y el cobro automático de membresías usan un mismo objeto, el **enlace de cobro** (`skeen.cobro.enlace`): origen (reserva, membresía o venta), monto y moneda, token firmado con caducidad, estado (pendiente/pagado/expirado/cancelado), proveedor (Stripe, MercadoPago o **simulado**) y bitácora. Al pagarse se registra un `skeen.pago` real ligado a la cita y a la venta. - **Página pública de pago**: `/pagar/` (sin sesión). Muestra concepto, monto, estado y el botón de pago; tras pagar muestra el comprobante. Respeta marca, glosario e idioma y funciona en celular. - **Proveedor `simulado`**: cuando no hay credenciales de pasarela, el enlace nace `simulado`. Para poder probar el flujo completo sin dinero real hay que encender `reservas_online.anticipo.permitir_simulado` y/o `membresias.cobro_automatico.permitir_simulado`. En producción déjalos **apagados**: nunca se simula un pago sin permiso explícito. - **Configurar Stripe** (Odoo → Ajustes técnicos → Parámetros del sistema): - `skeen.stripe_secret_key`: llave secreta (`sk_live_…`/`sk_test_…`). - `skeen.stripe_webhook_secret`: secreto del endpoint de webhook (`whsec_…`). - Webhook a `https:///skeen/frontend/v1/public/cobro/webhook/stripe` con los eventos `checkout.session.completed` y `payment_intent.succeeded`. La firma (`Stripe-Signature`) se verifica; sin secreto el webhook responde 400. - **Configurar MercadoPago** (mismos parámetros): - `skeen.mercadopago_access_token`: access token de producción/prueba. - `skeen.mercadopago_webhook_secret` (opcional): si se define, se verifica la firma `x-signature`; si no, se confirma consultando el pago con el token. - Webhook a `https:///skeen/frontend/v1/public/cobro/webhook/mercadopago`. - **Sin credenciales**: ambos webhooks existen pero rechazan la notificación (400) y el checkout real no se crea; el flujo queda inerte salvo el modo simulado explicado arriba. - **Reporte**: **Reportes → Cobros** lista enlaces pendientes y expirados (anticipos y membresías), con totales, exportable a Excel, y permite **forzar** el cobro o **cancelar** la retención según los permisos de Cobros. ## 12. Compras y proveedores **Configuración → Compras** (apagado por defecto) y **Compras** (menú). - **Proveedores**: sobre el contacto (`res.partner`) con marca propia de proveedor, días de crédito, contacto de compras, banco, cuenta/CLABE y notas. Un proveedor con historial no se borra: se desactiva. - **Órdenes de compra** (`skeen.compra`): folio `OC-`, proveedor, líneas de artículo, moneda (MXN/USD), descuento e impuesto por línea. Estados: Borrador → Enviada → Recibida parcial / Recibida → Cancelada. Cancelar solo se permite si no hubo recepciones (si ya entró mercancía se usa una baja/ajuste). - **Recepción total o parcial**: cada recepción es acumulativa; al recibir se carga inventario (movimiento tipo compra) y se puede capturar lote y caducidad. - **Costo promedio**: al recibir se recalcula el costo promedio ponderado del artículo y se guarda el último costo (si `actualizar_costo_promedio` está encendido). - **Pagos a proveedor**: egresos separados de los cobros a pacientes (no aparecen en corte de caja ni en estado de cuenta del paciente). - **Cuentas por pagar**: saldo por proveedor y reporte con antigüedad. - **Reportes**: **Reportes → Compras** y margen por artículo (contra costo promedio y precio de venta sin impuesto). - **Opciones del bloque** (defaults): activo (no), `actualizar_costo_promedio` (sí), `permitir_recepcion_parcial` (sí), `requiere_autorizacion` (no), `dias_credito_default` (0), `margen_minimo_alerta` (0), `permitir_compras_sin_orden` (no). ## 13. Comisiones **Configuración → Comisiones** (apagado por defecto), **Reportes → Comisiones** y **Liquidaciones** (menú). Con el módulo apagado se conserva el reporte de comisiones de siempre (porcentaje plano por empleado). Al encenderlo se usa el motor de reglas. - **Reglas** (`skeen.comision.regla`): alcance global, por categoría, por servicio, por artículo, por plan de membresía o por paquete; tipo porcentaje, monto fijo o escalonado por metas; prioridad; dirigidas a un empleado, a un tipo de staff (doctor/cosmetóloga) o a todos; vigencia; y base propia opcional. La regla ganadora se decide por prioridad, luego por especificidad (artículo > servicio > membresía/paquete > categoría > global) y luego por empleado > tipo de staff > genérica. Sin ninguna regla aplicable se usa el porcentaje de comisión del empleado (respaldo de compatibilidad). - **Base**: `cobrado` (default, sobre lo efectivamente pagado) o `vendido` (subtotal de la línea). Opcionalmente suma impuestos y resta la parte devuelta. - **Escalonados**: el porcentaje se decide con la venta total del periodo del empleado y se aplica a cada línea cubierta. - **Metas y bonos** (`skeen.comision.meta`): por empleado y periodo (`AAAA-MM` o `mensual`), monto objetivo y bono fijo o porcentaje sobre la venta. - **Ajustes** manuales firmados (+/−), que suman o restan a la liquidación. - **Liquidaciones**: se generan por empleado y periodo; al generarlas el desglose queda **congelado** (ya no cambia aunque después se editen reglas o precios). Estados Borrador → Pagada, o Borrador → Cancelada (una pagada no se cancela: se registra un ajuste). Folio `LIQ-`. - **Opciones del bloque** (defaults): activo (no), `base` (`cobrado`), `incluir_impuestos` (no), `neto_de_devoluciones` (sí), `comisionar_membresias` (sí), `comisionar_paquetes` (sí), `comisionar_productos` (sí), `permitir_ajustes_manuales` (sí), `dia_corte` (1), `redondeo` (2 decimales), `pago_minimo` (0). ## 13 bis. Lealtad (monedero, puntos y niveles VIP) **Configuración → Lealtad**, la tarjeta del paciente (saldo, nivel e historial de movimientos), la cuenta del cliente y **Reportes → Puntos**. El programa de puntos vive en el bloque `lealtad` de los ajustes de la clínica. Trae dos modos: - **Clásico** (por defecto): reproduce exactamente el comportamiento de siempre — 1 punto por cada $10 pagados, 1 punto = $1, canje máximo del 50 % y sin niveles ni multiplicadores. Con este modo ninguna cifra cambia. - **Configurable**: usa los valores del bloque. **Opciones del bloque** (defaults entre paréntesis): - `activo` (sí), `modo` (`clasico` | `configurable`). - `puntos_por_unidad` (0.1 = 1 punto por cada $10), `valor_punto` (1.0 pesos por punto al canjear), `moneda_base` (`MXN`). - `canje_max_pct` (50): tope de canje sobre el total de la venta. - `expira_meses` (0 = no expiran): con un valor mayor, el proceso diario vence los puntos **no usados más antiguos** que ese plazo y lo registra en la bitácora. En modo clásico nunca se expira nada. - `acumular_sobre` (`pagado` | `vendido`), `aplicar_a` (`servicios`, `productos`, `membresias`, `paquetes`), `redondear` (`abajo` | `normal`). **Multiplicadores** (`multiplicadores[]`): reglas con `alcance` (servicio, artículo, categoría de servicio, nivel, sucursal, rango de fechas o día de la semana), `valor`, `factor` (2 = doble de puntos; < 1 reduce) y vigencia (`fecha_desde`/`fecha_hasta`). **Precedencia**: gana la más específica (servicio > artículo > categoría > nivel > sucursal > rango de fechas > día de la semana) y, a igual especificidad, la última de la lista. **Niveles VIP** (`niveles[]`): cada nivel define `codigo`, `nombre`, `gasto_minimo`, `ventana_meses` (por defecto 12), `descuento_pct` (se aplica automáticamente en el punto de venta), `factor_puntos`, `vigencia_meses` y `color`. El nivel se recalcula al cerrar cada venta y por el proceso diario. Un cliente **no baja de inmediato**: el nivel conserva su vigencia y solo al vencer se recalcula a la baja. La **asignación manual** (con fecha de revisión) no se sobreescribe sola hasta esa fecha. **Puntos a mano y auditoría** (`lealtad.ajustar`): el endpoint `POST /patients//points/adjust` exige motivo y deja el movimiento en el historial y en la auditoría. El nivel se puede recalcular (`POST /patients//lealtad/recalcular`) o asignar a mano (`PUT /patients//lealtad`). **Reportes** (`Reportes → Puntos`): puntos otorgados, canjeados y expirados del periodo, el pasivo en pesos (puntos activos × valor del punto) y los clientes por nivel. En modo clásico coincide con las cifras de siempre. **Permisos**: `lealtad.ver` (todos), `lealtad.ajustar`, `lealtad.nivel` y `lealtad.configurar` (admin y gerencia). ## 13 ter. Propinas en el punto de venta **Configuración → Propinas**, el cobro del punto de venta y **Reportes → Propinas** (exportable a Excel). Con el módulo **apagado** (por defecto) el punto de venta se ve y se comporta exactamente igual que hoy: no aparece ningún campo de propina y el total no cambia. Al encenderlo, al cobrar se puede agregar una propina por porcentaje sugerido o por monto libre. - La propina se guarda **aparte** del importe de los servicios (campos `propina` y `propina_moneda` de la venta): el **total cobrado al cliente sí la incluye** y se muestra desglosada en el ticket y en el corte de caja, pero las cifras de servicios no cambian y los puntos se acumulan solo por los servicios. - **Reparto** (`ninguno` | `equitativo` | `por_participacion`): al cobrar, la propina se reparte entre el personal que atendió y quien cobró; el detalle (quién, cuánto y de qué venta) queda en `skeen.propina.reparto` y en el reporte por empleado. - `incluir_en_liquidacion_comisiones` (no): al encenderlo, la propina del empleado se suma a su liquidación de comisiones **sin alterar** el cálculo de la comisión; apagado, no cambia ninguna liquidación. **Opciones del bloque** (defaults): `activo` (no), `sugerencias_pct` ([10, 15, 20]), `permitir_personalizado` (sí), `permitir_sin_propina` (sí), `reparto` (`ninguno`), `incluir_en_liquidacion_comisiones` (no) y `cuenta_propinas` (etiqueta de la cuenta destino). **Permisos**: `propinas.ver` (todos), `propinas.aplicar` (recepción y demás roles de cobro) y `propinas.configurar` (admin y gerencia). ## 14. Formularios y consentimientos **Configuración → Formularios** y, en el expediente del paciente, la pestaña de formularios. - **Plantillas** (`skeen.formulario.plantilla`): tipo consentimiento informado, nota de expediente, encuesta o autorización; con campo de **texto legal**, requerir firma y/o foto, duración estimada y asociación opcional a sucursales y servicios. Las plantillas se **versionan**: cada respuesta guarda la versión con la que se llenó. - **Tipos de campo** (13): texto, texto largo, número, fecha, selección, multiselección, casilla, escala, **recomendación (NPS 0-10)**, firma, foto, archivo y consentimiento. Los campos de foto/archivo pueden marcarse como antes / después / otro, lo que arma la **galería antes/después** del expediente. El campo de recomendación (NPS) es un tipo propio: se puede analizar sin depender del texto (ver la sección de encuestas de salida). - **Envío**: por WhatsApp, correo o enlace. El enlace es un token firmado (HMAC, vigencia 180 días) y deja de servir al llenarse. - **Llenado por el paciente**: página pública `/formulario/` (también funciona en el celular) con firma capturada como PNG (hasta 3 MB) y fotos (hasta 5 MB cada una, máximo 10). El sistema valida campos obligatorios, firma y fotos requeridas. - **Historial**: respuestas por paciente con canal, fecha, firma y fotos. ## 14 bis. Campañas por segmentos — apagado por defecto **Configuración → Campañas** (interruptor) y **Campañas** (menú, sección "Comunicación"), con dos pestañas: **Campañas** y **Segmentos**. Con el bloque apagado (`campanas.activo = false`, el default) no aparece ninguna pantalla ni enlace, no corre ningún proceso automático y no se envía nada. - **Segmentos** (`skeen.segmento`): un segmento guarda nombre, criterios y una **previsualización** (cuántos pacientes y una muestra de 5) que se calcula *antes* de usarlo. Criterios disponibles (todos opcionales): - última visita hace más de N días, o sin visita en N meses (incluye a quien nunca ha venido); - cumpleaños del mes en curso o en los próximos N días; - nivel de lealtad; - gasto acumulado (suma de ventas no canceladas) mayor o igual a X; - sucursal (con al menos una cita ahí); - servicio que **sí** ha tomado y servicio que **nunca** ha tomado; - con o sin membresía activa; - con o sin paquetes con sesiones disponibles; - con o sin saldo pendiente; - con o sin consentimiento de marketing. La evaluación corre en el servidor y su conteo coincide con lo que responde la base de datos. - **Campañas** (`skeen.campana`): nombre, segmento, canal (WhatsApp, correo o ambos), mensaje con variables (`{nombre}`, `{servicio}`, `{sucursal}`, `{enlace}`, `{doctor}`, `{fecha}`, `{hora}`, `{telefono}`), programación (una sola vez en una fecha, mensual, semanal o **según el propio segmento**, p. ej. cumpleaños) y estado (borrador, programada, en ejecución, pausada, terminada, cancelada). - **Envío de prueba**: manda el mensaje a un número o correo propio antes de lanzar. Queda registrado en la bitácora marcado como prueba (no cuenta en la medición). - **Medición por campaña**: pacientes alcanzados, enviados, errores y pendientes; **citas generadas como consecuencia** (atribuidas por paciente y dentro de una **ventana configurable**, por defecto 7 días) y su valor en dinero; y la tasa de conversión. Lo que no se puede medir con lo que hay (entrega real y respuestas del paciente) se muestra como **no disponible** en lugar de inventarse. - **Cuidado de WhatsApp**: **límite de envíos por minuto** configurable, **pausa automática** si se acumulan errores seguidos y **respeto al consentimiento** (ver abajo). - **Opciones del bloque** (defaults): `activo` (no), `envios_por_minuto` (20), `pausar_tras_errores` (5), `ventana_atribucion_dias` (7), `exigir_consentimiento` (sí) y `hora_envio` (`10:00`, para las recurrentes). ### 14 bis.1 Consentimiento de marketing por canal En el paciente (`res.partner`) hay un interruptor **general** y uno por canal: - `consentimiento_marketing` (general), `consentimiento_whatsapp` y `consentimiento_email`, más la fecha y el origen del consentimiento. Un paciente **sin consentimiento de marketing nunca recibe una campaña**, aunque cumpla el criterio del segmento. Los **avisos transaccionales** (recordatorio de cita, confirmación, encuesta de salida) **no dependen** de esto: son parte del servicio que el paciente ya contrató. El consentimiento nace **apagado**. ### 14 bis.2 Permisos - `campanas.ver`: ver campañas y segmentos (por defecto: todos los roles). - `campanas.gestionar`: crear y editar campañas y segmentos (admin, gerencia y marketing). - `campanas.enviar`: lanzar campañas y enviar pruebas (admin, gerencia y marketing). - `campanas.medir`: ver la medición (por defecto: todos los roles). ## 14 ter. Encuestas de salida y satisfacción (NPS) — apagado por defecto **Configuración → Encuestas** (interruptor) y **Satisfacción** (menú). Con el bloque apagado (`encuestas.activo = false`, el default) no se envía ninguna encuesta tras completar una cita y la pantalla de Satisfacción no aparece. - **Envío automático**: cuando una cita pasa a completada, tras **N horas** (default 2) se envía al paciente la encuesta configurada, por WhatsApp, correo o ambos. Hay **recordatorio opcional** si no responde (default 48 h) y una opción de **enviarla solo a la primera visita**. - **Captura**: reutiliza la página pública de formularios `/formulario/` (no hay otro mecanismo de captura). El mensaje de agradecimiento es configurable. - **Pregunta de recomendación (NPS)**: tipo de campo propio `nps` (puntaje de 0 a 10) más un comentario abierto (texto largo). Se calcula de forma estándar: **promotores 9-10**, pasivos 7-8, **detractores 0-6**, y **NPS = % promotores − % detractores** (de −100 a 100). - **Reporte de satisfacción**: resultado del periodo, **tendencia por mes**, desglose por **sucursal, médico y servicio**, y la lista de respuestas con su comentario y un enlace a la ficha del paciente para dar seguimiento (incluida la posibilidad de **responderle por WhatsApp** desde ahí). Exportable a Excel. - **Opciones del bloque** (defaults): `activo` (no), `enviar_tras_horas` (2), `plantilla_id` (ninguna), `canal` (`whatsapp`), `recordatorio_horas` (48), `solo_primera_visita` (no) y `agradecimiento` (vacío). - **Permisos**: `encuestas.ver` y `encuestas.medir` (todos los roles); `encuestas.gestionar` (admin y gerencia). Ambas piezas **pasan por el motor de notificaciones**: nada se envía en silencio, todo respeta el **modo prueba** y queda en la bitácora con su campaña o encuesta de origen. ## 15. Tickets y documentos **Configuración → Tickets**: ancho (58/80 mm), fuente, logo y qué secciones se imprimen (incluida la de impuestos). Se aplica al ticket de venta y al de reimpresión. Los perfiles de ticket son **por instancia**, con defaults neutros: una instancia nueva no arrastra datos de SKEEN. ## 16. Licencia **Configuración → Licencia** (admin; gerencia también ve el estado). Cada instancia white-label puede llevar una **licencia firmada** que decide qué módulos incluye y sus límites de uso. Sin licencia aplicada el sistema se comporta con todas las funciones habilitadas. - **Planes**: `basico`, `profesional`, `enterprise` y `custom`. - Básico: agenda, pacientes, POS, inventario y reportes. - Profesional: Básico + WhatsApp, notificaciones, reservas en línea y recursos. - Enterprise: todos los módulos. - Custom: lo que diga la clave. - **Módulos gateables** (controlables por licencia): WhatsApp, notificaciones, reservas en línea, recursos, membresías, compras, comisiones y formularios. Login, agenda, pacientes, POS, inventario, reportes y configuración **nunca** se bloquean. - **Límites**: usuarios, sucursales, citas del mes y almacenamiento (MB). `0` = sin límite. - **Modo**: `aviso` (default; solo advierte y registra en el log) o `bloqueo` (oculta menús, responde 403 en los endpoints del módulo y aplica los límites). - **Cómo aplicar una clave**: pegar la clave `SKN1...` en Configuración → Licencia y guardar. La clave se verifica contra la firma y la vigencia; si es inválida o vencida responde 400 y no sobrescribe la licencia previa. Un campo vacío quita la licencia y vuelve a habilitar todo. El detalle del formato de la clave, la herramienta del vendedor y los endpoints está en [`licencias.md`](licencias.md). ## 17. API para terceros **Configuración → API** (admin). Gestión de claves con alcances (*scopes*), vencimiento y revocación, sobre la API pública `/skeen/api/v1/*` que consume Hermes. La clave se muestra una sola vez al crearla. Alcances disponibles: `citas.read`, `citas.write`, `pacientes.read`, `servicios.read` y `monedero.write`. El detalle está en [`api-terceros.md`](api-terceros.md). ## 18. Webhooks **Configuración → Webhooks** (admin/gerencia). Notificación de eventos de la clínica hacia sistemas externos, con patrón outbox + reintentos + bitácora y firma HMAC de cada entrega. Sin webhooks activos no se emite ni se envía nada. El catálogo de eventos, la verificación de la firma y los endpoints están en [`webhooks.md`](webhooks.md). ## 19. PWA (instalable en tablet y celular) El panel es una **aplicación web instalable** (PWA), sin tienda de aplicaciones. - **Instalar**: abrir el panel en el navegador del dispositivo. En Android/Chrome aparece el aviso "Instalar aplicación"; en iPhone/Safari se usa "Compartir → Añadir a pantalla de inicio" (el aviso trae la guía de iOS). - **Comportamiento offline**: un service worker precachea el *app shell* y los assets con hash. La navegación es *network-first* (un deploy nuevo se ve al primer refresh); si no hay red se sirve la última copia del shell o una página `offline.html`. Las llamadas al API (`/api/*`) y cualquier petición con credenciales **nunca** se cachean. - **Limitación**: los iconos y el nombre del manifest son **fijos por build** ("Panel Clínico" e iconos genéricos). No cambian con la marca de la instancia; cambiarlos requiere recompilar el frontend. La marca visual (nombre, logos, colores) sí es por instancia y no requiere build. --- ## Checklist de arranque - [ ] Identidad: nombre, logos, colores, URL pública. - [ ] Terminología: pack del giro + ajustes finos. - [ ] Idioma de la interfaz (si el cliente no trabaja en español). - [ ] Horarios, zona horaria, moneda, sucursales, categorías. - [ ] Catálogo de servicios con precios y duraciones. - [ ] Inventario y consumibles (caducidades y costos reales). - [ ] Impuestos (tasa por defecto + tasas por servicio/artículo). - [ ] Usuarios y roles/permisos. - [ ] Recursos (si la agenda depende de cabinas/salas/equipos). - [ ] Membresías (si vende planes recurrentes). - [ ] Compras y proveedores (si controla abasto y margen). - [ ] Comisiones (reglas, metas y liquidaciones). - [ ] Formularios y consentimientos. - [ ] Campañas por segmentos (si hará marketing): activar y registrar el consentimiento de los pacientes. - [ ] Encuestas de salida: crear la plantilla con la pregunta NPS y activar. - [ ] Reservas en línea (activar y difundir el enlace). - [ ] Portal del cliente (si se ofrece cuenta): activar y difundir `/mi-cuenta`. - [ ] Notificaciones en modo prueba → revisar textos → encender. - [ ] Tickets: formato y secciones. - [ ] Licencia: aplicar la clave del plan contratado. - [ ] API / Webhooks si se integra con terceros. - [ ] Confirmar que las credenciales del admin se cambiaron. ## Estado del producto por fases Todas las fases están **entregadas**. | Fase | Alcance | Estado | |---|---|---| | 1 | Capa de configuración (terminología, sucursales, categorías, ajustes, colores, horarios, URLs de pago), defaults neutros, provisioning de instancias | Entregado | | 2.1 | Roles y permisos editables | Entregado | | 2.2 | Impuestos configurables | Entregado | | 2.3 | Portal público de reservas en línea | Entregado | | 2.4 | Recordatorios y notificaciones automáticas | Entregado | | 2.5 | Formularios digitales y consentimientos con firma y fotos | Entregado | | 2.6 | Membresías y suscripciones | Entregado | | 3.1 | Recursos físicos en la agenda | Entregado | | 3.2 | Compras y proveedores | Entregado | | 3.3 | Comisiones por reglas y liquidación | Entregado | | 3.4 | PWA instalable y branding por instancia | Entregado | | 3.5 | Licenciamiento por plan, API keys gestionables y webhooks | Entregado | | 3.6 | i18n español/inglés | Entregado | | 3.7 | Cuenta del cliente (portal de autoservicio) | Entregado | | 3.8 | Campañas por segmentos con medición y consentimiento de marketing | Entregado | | 3.9 | Encuestas de salida y satisfacción (NPS) | Entregado | | Operación | Backups automáticos verificados y panel/CLI multi-instancia | Entregado | Los pendientes y limitaciones conocidos (los que aún no están cerrados) viven en [`limitaciones-y-pendientes.md`](limitaciones-y-pendientes.md).