fase 2.5: formularios digitales y consentimientos (modulo skeen_formularios: plantillas con 12 tipos de campo, versionado, respuestas con firma PNG y fotos antes/despues, portal publico con token HMAC y limite de subida, historial por paciente, permisos y notificacion de envio)

This commit is contained in:
2026-10-11 09:48:28 +00:00
parent 91f909a6d0
commit 9286ab8ee2
30 changed files with 3545 additions and 12 deletions

View File

@@ -43,6 +43,7 @@ TIPOS = [
('cumpleanos', 'Cumpleaños'),
('post_visita', 'Post-visita'),
('recuperacion', 'Recuperación'),
('formulario', 'Formulario'),
('manual', 'Manual'),
]
CANALES = [('whatsapp', 'WhatsApp'), ('email', 'Correo')]
@@ -64,6 +65,8 @@ PLANTILLA_POR_TIPO = {
# bloque `notificaciones` solo define cuatro plantillas (recordatorio,
# confirmación, cumpleaños y post-visita).
'recuperacion': 'plantilla_recordatorio',
# El aviso de un formulario digital usa su propia plantilla con {enlace}.
'formulario': 'plantilla_formulario',
'manual': 'plantilla_recordatorio',
}
@@ -129,12 +132,13 @@ class SkeenNotificacionService(models.AbstractModel):
return clinic_get_notificaciones(self.env)
@api.model
def render(self, plantilla, cita=None, partner=None, cfg=None):
def render(self, plantilla, cita=None, partner=None, cfg=None, extra=None):
"""Sustituye las variables de una plantilla con datos reales.
Los nombres de los términos (`{cita}`) salen del glosario vigente; las
demás variables son los datos de la cita y del paciente. Una variable sin
dato queda como cadena vacía.
dato queda como cadena vacía. `extra` añade variables puntuales (p. ej.
`{enlace}` y `{formulario}` para el aviso de un formulario digital).
"""
cfg = cfg or self._cfg()
settings = clinic_get_settings(self.env)
@@ -166,6 +170,10 @@ class SkeenNotificacionService(models.AbstractModel):
contexto['fecha'] = fields.Date.today().strftime('%d/%m/%Y')
contexto['sucursal'] = self._sucursal_default()
if extra:
for clave, valor in extra.items():
contexto[clave] = '' if valor is None else str(valor)
texto = str(plantilla or '')
for clave, valor in contexto.items():
texto = texto.replace('{%s}' % clave, str(valor or ''))
@@ -221,12 +229,14 @@ class SkeenNotificacionService(models.AbstractModel):
# ------------------------------------------------------------------
@api.model
def encolar(self, cita=None, tipo='recordatorio', cuando=None, canal=None,
partner=None, ventana=None, clave_unica=None, asunto=None):
partner=None, ventana=None, clave_unica=None, asunto=None,
contexto_extra=None):
"""Crea las notificaciones pendientes de una cita (una por canal).
Con `activo=False` no hace nada. No duplica: si ya existe la misma
`clave_unica` (misma cita + tipo + ventana + canal) se salta. Devuelve el
recordset de lo creado.
recordset de lo creado. `contexto_extra` alimenta variables adicionales
del render (p. ej. `{enlace}` del formulario digital).
"""
Notif = self.env['skeen.notificacion'].sudo()
cfg = self._cfg()
@@ -239,7 +249,8 @@ class SkeenNotificacionService(models.AbstractModel):
plantilla = cfg.get(PLANTILLA_POR_TIPO.get(tipo, 'plantilla_recordatorio')) or ''
if not plantilla.strip():
return Notif
mensaje = self.render(plantilla, cita=cita, partner=partner, cfg=cfg)
mensaje = self.render(plantilla, cita=cita, partner=partner, cfg=cfg,
extra=contexto_extra)
if asunto is None:
asunto = self._asunto(tipo)

View File

@@ -89,6 +89,12 @@ PERMISSION_GROUPS = [
('notificaciones.enviar', 'Enviar y reintentar notificaciones'),
('notificaciones.editar', 'Configurar recordatorios y plantillas'),
]),
('formularios', 'Formularios y consentimientos', [
('formularios.ver', 'Ver formularios y respuestas'),
('formularios.crear', 'Llenar formularios (recepción)'),
('formularios.editar', 'Administrar plantillas de formularios'),
('formularios.enviar', 'Enviar formularios a pacientes'),
]),
('config', 'Usuarios y configuración', [
('config.usuarios', 'Administrar usuarios'),
('config.roles', 'Administrar roles y menús'),
@@ -116,6 +122,7 @@ READ_PERM_KEYS = (
'ventas.reimprimir',
'whatsapp.ver',
'notificaciones.ver',
'formularios.ver',
)
# ---------------------------------------------------------------------------
@@ -197,6 +204,12 @@ DEFAULT_PERM_ROLES = {
'notificaciones.ver': ALL,
'notificaciones.enviar': W,
'notificaciones.editar': AG,
# Formularios digitales y consentimientos: ver es de todos; llenar/enviar lo
# hace quien atiende al paciente; administrar plantillas es de admin/gerencia.
'formularios.ver': ALL,
'formularios.crear': ('admin', 'medico', 'recepcion', 'enfermeria', 'gerencia'),
'formularios.editar': AG,
'formularios.enviar': ('admin', 'medico', 'recepcion', 'enfermeria', 'gerencia'),
# Usuarios y configuración
'config.usuarios': AG,
'config.roles': AG,