Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
50 changes: 22 additions & 28 deletions src/email_service.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
# Configuración Flask-Mail
mail = Mail()

# Executor global para gestionar threads de email
# Executor global para gestionar threads de email
# max_workers=3 permite enviar hasta 3 emails simultáneos
email_executor = ThreadPoolExecutor(
max_workers=3,
Expand All @@ -25,12 +25,7 @@ def init_mail(app):

mail.init_app(app)

# ✅ Registrar shutdown del executor al cerrar la app

# ✅ También registrar con atexit como backup
atexit.register(lambda: email_executor.shutdown(wait=False))

# ✅ También registrar con atexit como backup
# Registrar shutdown del executor al cerrar la app
atexit.register(lambda: email_executor.shutdown(wait=False))


Expand All @@ -45,51 +40,50 @@ def _send_async(app, msg):
print(f"✅ Email enviado: {msg.subject} -> {msg.recipients}")
except Exception as e:
print(f"❌ Error enviando email: {e}")
# Opcional: Loggear a archivo o sistema de monitoring
# import logging
# logging.error(f"Email send failed: {e}", exc_info=True)


def enviar_email_solicitud(aprobador, solicitante, solicitud):
def enviar_email_solicitud(aprobadores, solicitante, solicitud):
"""
Envía email de notificación de nueva solicitud al aprobador.
Usa ThreadPoolExecutor para envío asíncrono seguro.
MODIFICADO: Ahora acepta una lista de objetos de usuario (aprobadores).
Envía email de notificación de nueva solicitud a todos los responsables.
"""
# Extraer los correos de la lista de objetos recibida
emails_destinatarios = [a.email for a in aprobadores]

if not emails_destinatarios:
return

# Construir lista de nombres para el cuerpo del mensaje
nombres_aprobadores = ', '.join([a.nombre for a in aprobadores])

msg = Message(
subject=f'Nueva solicitud de vacaciones de {solicitante.nombre}',
sender=current_app.config['MAIL_DEFAULT_SENDER'],
recipients=[aprobador.email]
recipients=emails_destinatarios
)


# Construir lista de aprobadores para el mensaje
if len(aprobadores) > 1:
nombres_aprobadores = ', '.join([a.nombre for a in aprobadores])
nota_aprobadores = f'\n(Notificados: {nombres_aprobadores})\n'
else:
nota_aprobadores = ''

msg.body = f'''
Hola {aprobadores[0].nombre},
Hola,

{solicitante.nombre} ha solicitado vacaciones:

- Desde: {solicitud.fecha_inicio}
- Hasta: {solicitud.fecha_fin}
- Días solicitados: {solicitud.dias_solicitados}
- Motivo: {solicitud.motivo or 'No especificado'}
{nota_aprobadores}

Notificados: {nombres_aprobadores}

Por favor, revisa y responde a esta solicitud en el sistema.

Saludos,
Sistema de Gestión de Fichajes
'''

# Enviar usando el executor (gestiona threads automáticamente)
# Enviar usando el executor (gestiona threads automáticamente)
app = current_app._get_current_object()
future = email_executor.submit(_send_async, app, msg)

# Opcional: añadir callback para manejar errores
def handle_email_result(fut):
try:
fut.result() # Lanza excepción si hubo error
Expand All @@ -104,7 +98,7 @@ def enviar_email_respuesta(usuario, solicitud):
Envía email de notificación de respuesta a solicitud.
Usa ThreadPoolExecutor para envío asíncrono seguro.
"""
estado_texto = "APROBADA" if solicitud.estado == 'aprobada' else "RECHAZADA"
estado_texto = "APROBADA" if solicitud.state == 'aprobada' else "RECHAZADA"

msg = Message(
subject=f'Tu solicitud de vacaciones ha sido {estado_texto}',
Expand All @@ -130,7 +124,7 @@ def enviar_email_respuesta(usuario, solicitud):
Sistema de Gestión de Fichajes
'''

# Enviar usando el executor
# Enviar usando el executor
app = current_app._get_current_object()
future = email_executor.submit(_send_async, app, msg)

Expand Down
10 changes: 10 additions & 0 deletions src/routes/ausencias.py
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,16 @@ def solicitar_vacaciones():

db.session.add(solicitud)
db.session.commit()

# Enviar Email a los aprobadores del usuario objetivo
aprobadores = [rel.aprobador for rel in target_user.aprobadores]
if not aprobadores:
admin = Usuario.query.filter_by(rol='admin').first()
if admin: aprobadores = [admin]

if aprobadores:
from src.email_service import enviar_email_solicitud
enviar_email_solicitud(aprobadores, target_user, solicitud)

# Mensajes Feedback
if saldo_proyectado < 0:
Expand Down