bloque 3: lealtad configurable (motor de puntos con multiplicadores por servicio/categoria/sucursal/dia/nivel, niveles VIP con descuento y factor, expiracion, ajuste manual con motivo y reportes) + propinas en punto de venta con reparto entre el personal, desglose en ticket y corte, y opcion en la liquidacion de comisiones; modo clasico y propinas apagadas preservan el comportamiento actual

This commit is contained in:
2026-10-12 01:37:07 +00:00
parent 1bd4cad6a0
commit 705b5a2842
28 changed files with 2748 additions and 59 deletions

View File

@@ -409,6 +409,90 @@ plano por empleado). Al encenderlo se usa el motor de reglas.
(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/<id>/points/adjust` exige motivo y deja el movimiento en el
historial y en la auditoría. El nivel se puede recalcular
(`POST /patients/<id>/lealtad/recalcular`) o asignar a mano
(`PUT /patients/<id>/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