Saltar a contenido

Módulo Notificaciones (DEHú)

En una frase

Módulo común que entrega notificaciones electrónicas oficiales a la ciudadanía vía DEHú (MINHAP), invocado por los módulos funcionales (Padrón, Registro, …) mediante un helper de un solo punto. Cumple el art. 43 de la Ley 39/2015 (LPAC): ciclo de vida completo, plazo de 10 días con auto-rechazo por silencio y trazabilidad de comparecencia.

1. Propósito y alcance

  • Cliente DEHú abstracto con dos modos: MOCK (simulación local para pruebas) y PRODUCTION (SOAP real contra dehu.redsara.esstub hoy, pendiente de implementación cuando hay cert FNMT + alta MINHAP).
  • Helper único notificar_ciudadano(**kwargs) que cualquier módulo emisor invoca cuando quiere entregar una comunicación electrónica.
  • Bandeja unificada: todas las notificaciones de la entidad, sea cual sea el módulo emisor, viven en la misma tabla y comparten UI.
  • Configuración multi-tenant: cada entidad activa DEHú por su cuenta (dehu_activo=true); con DEHú apagado el helper devuelve None y los emisores siguen con su canal postal habitual.
  • Cron de vigilancia diaria: auto-rechazo por silencio art. 43.2 LPAC y refresco de estados desde DEHú.
  • Best-effort en los módulos emisores: si DEHú falla, el flujo legal postal continúa sin interrupción.

Marco normativo: Ley 39/2015 (LPAC) art. 43, ENS, RGPD.

2. Estructura de archivos

app/modules/notificaciones/
├── __init__.py
├── models.py
│       NotificacionElectronica       # registro de cada notificación
│       NotificacionesConfiguracion   # toggle + URL + DIR3 + cert por entidad
│       CanalNotificacionEnum         # DEHU (extensible)
│       EstadoNotificacionEnum        # 5 estados
├── routes.py                          # Blueprint notificaciones_bp (10 endpoints)
└── servicios/
    ├── cliente_dehu.py                # MOCK + stub PRODUCTION
    ├── helper.py                      # notificar_ciudadano() + cortocircuito
    └── vigilancia.py                  # @register_action cron diario

Integración con módulos emisores:

app/modules/padron_habitantes/servicios/renovaciones.py
  notificar(), notificar_recordatorio() → helper.notificar_ciudadano()

app/modules/registro/servicios/notif_dehu.py
  notificar_interesados_via_dehu() → helper.notificar_ciudadano()
app/modules/registro/routes.py
  crear_registro() invoca notificar_interesados_via_dehu() (best-effort)

3. Modelo de datos

notificacion_electronica

Columna Tipo Propósito
id int PK
entidad_id FK entidades Multi-tenant.
documento_destinatario varchar(50) NIF/NIE/CIF/Pasaporte (denormalizado).
nombre_destinatario varchar(200)
email_destinatario varchar(200) Canal DEHú.
tercero_id FK terceros nullable Vínculo opcional al maestro.
asunto varchar(500)
cuerpo text nullable Texto adicional.
pdf_storage_path varchar(500) Ruta del PDF original notificado.
sha256_pdf char(64) nullable Hash del PDF al momento de notificar.
tamano_bytes int nullable
modulo_origen varchar(50) "padron_habitantes", "registro", …
referencia_origen varchar(200) p. ej. salida:42:interesado:7.
canal enum notif_canal_enum DEHU.
estado enum notif_estado_enum Ver más abajo.
modo varchar(20) "MOCK" / "PRODUCTION".
identificador_externo varchar(200) nullable ID asignado por DEHú.
fecha_envio datetime nullable Cuándo se llamó al canal.
fecha_puesta_disposicion datetime nullable Aceptación del canal.
fecha_limite_acuse datetime nullable puesta + 10 días naturales.
fecha_comparecencia datetime nullable Acto del ciudadano.
fecha_rechazo datetime nullable Rechazo expreso o por silencio.
mensaje_error varchar(500) nullable Diagnóstico si ERROR / motivo cancelación.
usuario_creador_id FK usuarios nullable Operador que disparó la emisión.
creado_en, modificado_en datetime Auditoría.

Enums (nombrados explícitamente para no chocar con otros módulos):

  • notif_canal_enumDEHU.
  • notif_estado_enumPUESTA_A_DISPOSICION, COMPARECIDA, RECHAZADA, VENCIDA, ERROR.

notificaciones_configuracion

Una fila por entidad (unique). Si no existe se crea con defaults seguros (DEHú apagado + MOCK).

Columna Tipo Default Propósito
dehu_activo bool false Toggle principal sí/no.
dehu_modo varchar(20) "MOCK" MOCK o PRODUCTION.
dehu_url varchar(300) null URL real del servicio.
dehu_cert_path varchar(300) null Ruta del .p12 en el servidor.
dehu_organismo_dir3 varchar(10) null Código DIR3 emisor.
modificado_por_id FK usuarios null Auditoría.

Enlaces desde otros módulos

Tabla Columna FK Propósito
padron_hab_renovacion notificacion_dehu_id Aviso principal entregado vía DEHú.
padron_hab_renovacion recordatorio_notificacion_dehu_id Recordatorio entregado vía DEHú.
registro_interesados notificacion_dehu_id Interesado de un Registro de Salida entregado vía DEHú.

Las tres columnas son nullable: si la entidad tiene DEHú apagado o el canal del destinatario no permite entrega electrónica, quedan en NULL y el flujo postal sigue su curso.

Migraciones

Migración Cambio
149_notificaciones_dehu Tabla notificacion_electronica + enums.
150_notificaciones_configuracion Tabla notificaciones_configuracion.
151_padron_renovacion_dehu FKs en padron_hab_renovacion.
152_registro_interesado_dehu FK en registro_interesados.

4. API REST

Blueprint notificaciones_bp, prefijo /api/notificaciones. Todos los endpoints requieren JWT + permiso.

Método Ruta Permiso Propósito
GET /notificaciones notificaciones:notificacion:ver Listado paginado con filtros (estado, módulo, documento, búsqueda libre, rango fechas).
GET /notificaciones/<id> notificaciones:notificacion:ver Detalle completo.
POST /notificaciones/<id>/consultar-estado notificaciones:notificacion:ver Pregunta a DEHú y persiste cambios.
POST /notificaciones/<id>/reenviar notificaciones:notificacion:reenviar Reintenta el envío (solo ERROR). Acepta {"email": "…"} para corregir destinatario.
POST /notificaciones/<id>/cancelar notificaciones:notificacion:cancelar Marca como RECHAZADA con mensaje_error ("Cancelada por X: motivo").
GET /notificaciones/<id>/pdf notificaciones:notificacion:ver Descarga del PDF original.
GET /notificaciones/<id>/acuse notificaciones:notificacion:ver Descarga del acuse firmado por DEHú.
GET /notificaciones/resumen notificaciones:notificacion:ver KPIs por estado y por módulo.
GET /notificaciones/configuracion notificaciones:dehu:admin Configuración de la entidad (sin exponer dehu_cert_path, solo dehu_cert_configurado: bool).
PUT /notificaciones/configuracion notificaciones:dehu:admin Actualiza la configuración con validación PRODUCTION (URL + cert + DIR3 obligatorios para activar).

Códigos de error

code Cuándo
NOTIF-NOT-FOUND ID no existe en la entidad actual.
NOTIF-SIN-IDENT No hay identificador externo para consultar / acuse.
NOTIF-CONSULTA-ERROR DEHú devolvió error al consultar.
NOTIF-ESTADO-INVALIDO Acción no permitida en el estado actual.
NOTIF-PDF-NO-DISP PDF no encontrado en storage.
NOTIF-ACUSE-NO-DISP DEHú no ha emitido acuse todavía.
NOTIF-CFG-MODO-INVALIDO dehu_modo no es MOCK ni PRODUCTION.
NOTIF-CFG-INCOMPLETA Falta URL / cert / DIR3 para activar PRODUCTION.

5. Helper notificar_ciudadano()

Punto de entrada único para los módulos emisores. Firma (todos kwargs):

from app.modules.notificaciones.servicios.helper import notificar_ciudadano

notif = notificar_ciudadano(
    entidad_id=...,
    documento=...,
    nombre=...,
    email=...,
    asunto=...,
    pdf_path=...,
    modulo_origen=...,
    referencia_origen=...,
    tercero_id=None,
    usuario_id=None,
    cuerpo=None,
)
# notif: Optional[NotificacionElectronica]

Flujo interno:

  1. Cortocircuito por entidad: lee NotificacionesConfiguracion. Si dehu_activo=False → devuelve None (silencioso, sin emit_event).
  2. Calcula sha256 y tamano_bytes del PDF si existe.
  3. Crea fila NotificacionElectronica con estado inicial PUESTA_A_DISPOSICION (pesimista, se corrige si el cliente devuelve error).
  4. Llama a cliente_dehu.poner_a_disposicion(notif).
  5. Si OK: persiste identificador_externo, fecha_puesta_disposicion y calcula fecha_limite_acuse = puesta + 10 días.
  6. Si KO: estado pasa a ERROR, guarda mensaje_error. Devuelve la fila igual (para que el emisor pueda enlazarla y luego se reintente).
  7. Emite emit_event (severidad INFO si OK, WARNING si ERROR).

Patrón en módulos emisores

Los emisores envuelven la llamada en try/except y guardan la FK al notif.id si la respuesta no es None. Si el helper devuelve None o levanta una excepción, el módulo sigue con su flujo postal habitual sin incidente.

6. Cliente DEHú

servicios/cliente_dehu.py. Tres operaciones:

Función Para qué
poner_a_disposicion(notif) Crea la notificación en el canal y devuelve identificador_externo.
consultar_estado(entidad_id, identificador) Pregunta el estado actual y la fecha del evento.
descargar_acuse(entidad_id, identificador) Bytes del PDF de acuse firmado por DEHú.

Modo MOCK

Reglas deterministas que permiten ejercitar el flujo sin DEHú real:

Condición Resultado
Sin email ERROR SIN_CANAL.
Documento empieza por "ERR" ERROR MOCK_ERROR.
Cualquier otra Identificador externo MOCK-{n} generado de forma determinista.
Evolución en consultas 60 % siguen en PUESTA_A_DISPOSICION, 25 % COMPARECIDA, 10 % RECHAZADA, 5 % VENCIDA.

Modo PRODUCTION

Hoy es un stub que devuelve siempre ERROR con código PRODUCTION_NO_IMPL. Por implementar cuando exista cert FNMT + alta MINHAP. La interfaz ya está fijada para que la sustitución sea local: solo hay que reemplazar el cuerpo de las tres funciones por las llamadas SOAP/REST reales.

7. Cron de vigilancia

Acción MOS notificaciones.vigilar_vencimientos (servicios/vigilancia.py), registrada con @register_action. Cron por defecto: 0 5 * * * (diario).

Hace dos cosas, cada una por entidad:

  1. _auto_rechazar_vencidas()UPDATE notificacion_electronica SET estado='RECHAZADA', fecha_rechazo=now(), mensaje_error='Vencido plazo art. 43.2 LPAC' WHERE fecha_limite_acuse < now() AND estado='PUESTA_A_DISPOSICION'. Independiente del estado del toggle (es un cumplimiento legal, no requiere DEHú activo).
  2. _refrescar_estados() — solo si dehu_activo=True. Recorre las notificaciones PUESTA_A_DISPOSICION con identificador externo y consulta el estado actual a DEHú; si ha evolucionado, persiste el cambio.

8. Permisos

Código Quién lo tiene típicamente
notificaciones:notificacion:ver Operativos de Padrón y Registro.
notificaciones:notificacion:reenviar Jefes de unidad / Administradores.
notificaciones:notificacion:cancelar Jefes de unidad / Administradores.
notificaciones:dehu:admin Administrador del sistema.

Sembrados en seed.py (PERMISOS_SEED).

9. Integración con MOS

Eventos emitidos por el módulo:

event_code Cuándo Severidad
NOTIF_EMITIDA Notificación creada y entregada al canal. INFO
NOTIF_ERROR_CANAL El canal devolvió error al poner a disposición. WARNING
NOTIF_REENVIADA Reenvío manual desde la UI. INFO
NOTIF_CANCELADA Cancelación manual. WARNING
NOTIF_COMPARECIDA Cambio de estado al consultar. INFO
NOTIF_RECHAZADA_SILENCIO Auto-rechazo del cron por silencio art. 43.2. INFO

Recursos auditados (resource_type="notificacion_electronica").

10. Decisiones de diseño

  • Módulo común, no extra-modular: la bandeja vive en un único sitio para evitar duplicar UI en cada módulo emisor y consolidar la trazabilidad.
  • Opt-in por entidad: el ayuntamiento decide cuándo conectar a DEHú real; hasta entonces no hay riesgo de envíos accidentales.
  • Optional[Notif] en lugar de excepciones: el helper devuelve None cuando DEHú está apagado para no obligar a los emisores a manejar una excepción ruidosa por algo que es una decisión administrativa, no un error.
  • Best-effort en emisores: el try/except envolvente garantiza que un fallo de DEHú nunca rompa el flujo legal postal de Padrón o Registro.
  • Hash + tamaño + ruta: la notificación guarda referencia al PDF realmente entregado para que, ante una reclamación, sea reproducible.
  • mensaje_error reutilizado tanto para errores técnicos como para motivos de cancelación (con prefijo "Cancelada por X:") — simplifica el modelo y la UI solo necesita interpretar el estado para colorear.

11. Pendientes documentados

  • Sustituir el stub PRODUCTION del cliente DEHú por la implementación SOAP real cuando el ayuntamiento tenga alta MINHAP + cert FNMT (ver puesta a producción).
  • Integrar Recaudación cuando exista deuda en vía ejecutiva (DEHú es obligatorio para notificar providencias de apremio).
  • Notificación a representantes legales además del titular cuando los datos de representación estén en el módulo.