Files
sirh/gestion_salle/views.py

489 lines
24 KiB
Python
Raw Normal View History

2026-04-30 13:28:57 +02:00
import json
from datetime import timedelta
2026-04-30 13:28:57 +02:00
from django.contrib import messages
from django.contrib.auth.decorators import login_required
from django.db.models import Q
2026-04-30 13:28:57 +02:00
from django.forms import model_to_dict
from django.http import HttpRequest, JsonResponse
from django.shortcuts import redirect, render
2026-05-07 11:22:14 +00:00
from fonction_utilitaire import fonctions_utilitaire
2026-04-30 13:28:57 +02:00
from gestion_employe.models import Employe
from gestion_salle.forms import ReservationForm
2026-04-30 13:28:57 +02:00
from .models import Reservation
@login_required
def index(request: HttpRequest):
"""Vue d'affichage de la page d'accueil de la gestion des salles"""
try:
employe = Employe.objects.get(user=request.user)
except Employe.DoesNotExist:
messages.error(request, "Profil employé introuvable.")
return redirect('gestion_conges:conge')
est_equipe_info = fonctions_utilitaire.est_equipe_informatique(employe)
est_assistante = fonctions_utilitaire.est_assistante_direction(employe)
if request.method == "POST":
form = ReservationForm(request.POST)
if form.is_valid():
date_debut = form.cleaned_data['date_debut']
date_fin = form.cleaned_data['date_fin']
salle = form.cleaned_data['salle']
heure_debut = form.cleaned_data['heure_debut']
heure_fin = form.cleaned_data['heure_fin']
motif = form.cleaned_data['motif_reservation']
besoin_zoom = form.cleaned_data['besoin_zoom']
besoin_ordi = form.cleaned_data['besoin_ordi']
if date_fin < date_debut:
messages.error(request, "La date de fin est antérieure à la date de début.")
return redirect('gestion_salle:reservation-salle')
if heure_fin <= heure_debut:
messages.error(request, "L'heure de fin est antérieure à l'heure de début.")
return redirect('gestion_salle:reservation-salle')
if not request.user.first_name.strip() or not request.user.last_name.strip():
messages.error(
request,
"Veuillez renseigner votre nom et prénom pour pouvoir faire une réservation."
)
return redirect('gestion_salle:reservation-salle')
created = []
current_date = date_debut
print(f"Création de réservations pour {employe}")
while current_date <= date_fin:
reservation = Reservation.objects.create(
employe=employe,
date_debut=current_date,
date_fin=current_date,
salle=salle,
heure_debut=heure_debut,
heure_fin=heure_fin,
besoin_zoom=besoin_zoom,
besoin_ordi=besoin_ordi,
motif_reservation=motif,
statut="en_attente"
)
created.append(reservation)
current_date += timedelta(days=1)
2026-05-07 11:22:14 +00:00
messages.success(request, "Réservation(s) créée(s) avec succès.")
2026-05-11 15:27:50 +00:00
# Nouvelle logique d'envoi de mails à la création :
# 1. Une demande de salle (formation/réunion) est envoyée à l'assistante de direction pour validation.
# - Si elle nécessite un lien Zoom ou un ordinateur, l'équipe informatique sera notifiée
# seulement après la validation par l'assistante.
# 2. Une demande de lien Zoom directement est envoyée à l'équipe informatique.
if salle != "lien_zoom" and fonctions_utilitaire.EMAIL_ASSISTANTE_DE_DIRECTION:
2026-05-07 11:22:14 +00:00
fonctions_utilitaire.envoyer_mail(
sujet="Demande de réservation de salle",
message=f"""Bonjour,
Une nouvelle demande de réservation de la {dict(Reservation.TYPE_CHOICES).get(salle)} a été effectuée par {employe.user.first_name} {employe.user.last_name} du {form.cleaned_data.get('date_debut').strftime('%d/%m/%Y')} au {form.cleaned_data.get('date_fin').strftime('%d/%m/%Y')} pour motif "{motif}".
{"Cette demande nécessite un lien Zoom et/ou un ordinateur et sera traitée par l'équipe informatique après votre validation." if besoin_zoom or besoin_ordi else ""}
Veuillez vous connecter à la plateforme pour plus de détails.""",
destinataires=fonctions_utilitaire.EMAIL_ASSISTANTE_DE_DIRECTION
2026-05-07 11:22:14 +00:00
)
if salle == "lien_zoom" and fonctions_utilitaire.EMAIL_EQUIPE_INFORMATIQUE:
fonctions_utilitaire.envoyer_mail(
sujet="Demande de lien Zoom",
message=f"""Bonjour,
Une nouvelle demande de lien Zoom a été effectuée par {employe.user.first_name} {employe.user.last_name} du {date_debut.strftime('%d/%m/%Y')} au {date_fin.strftime('%d/%m/%Y')} de {heure_debut.strftime('%H:%M')} à {heure_fin.strftime('%H:%M')} pour le motif {motif}.
Veuillez vous connecter à la plateforme pour traiter cette demande.""",
destinataires=fonctions_utilitaire.EMAIL_EQUIPE_INFORMATIQUE
)
return redirect('gestion_salle:reservation-salle')
formulaire_reservation = ReservationForm()
# departement = employe.departement
liste_demande_reservation = Reservation.objects.filter(
employe=employe,
statut='en_attente'
).values_list('id', flat=True)
context = {
'formulaire_reservation': formulaire_reservation,
'nb_reservation_attente': Reservation.objects.filter(statut='en_attente').count(),
'appartient_au_departement_informatique': est_equipe_info,
'est_assistante_direction': est_assistante,
'est_equipe_informatique': est_equipe_info,
'liste_demande_reservation': list(liste_demande_reservation),
}
return render(request, "gestion_salle/index.html", context)
@login_required
2026-04-30 13:28:57 +02:00
def liste_reservation(request:HttpRequest):
"""Vue d'affichage des creneaux disponibles"""
try:
employe = Employe.objects.get(user=request.user)
reservations = Reservation.objects.filter(
Q(statut="validee")
| Q(statut="en_attente", employe=employe)
)
except Employe.DoesNotExist:
reservations = Reservation.objects.filter(statut="validee")
2026-04-30 13:28:57 +02:00
liste_reservation = []
2026-05-15 17:13:02 +00:00
2026-04-30 13:28:57 +02:00
for reservation in reservations:
if reservation.statut == "en_attente":
color = "#ffc107"
else:
color = "#198754"
2026-04-30 13:28:57 +02:00
liste_reservation.append({
"guid": reservation.pk,
"title": dict(Reservation.TYPE_CHOICES).get(reservation.salle),
"date": reservation.date_debut,
"start": reservation.heure_debut,
"end": reservation.heure_fin,
"color": color,
"statut": dict(Reservation.STATUT).get(reservation.statut),
2026-04-30 13:28:57 +02:00
})
return JsonResponse(liste_reservation, safe=False)
@login_required
def liste_reservation_attente(request):
"""Liste des réservations en attente, filtrée selon le profil :
- Assistante de direction : voit les salles à valider (pas les liens choisis).
- Équipe informatique : voit les liens choisis et les salles demandant zoom/ordi non traités.
"""
try:
employe = Employe.objects.get(user=request.user)
except Employe.DoesNotExist:
return JsonResponse({"success": False, "message": "Profil employé introuvable"})
est_informatique = fonctions_utilitaire.est_equipe_informatique(employe)
est_assistante = fonctions_utilitaire.est_assistante_direction(employe)
2026-04-30 13:28:57 +02:00
reservations = Reservation.objects.filter(statut="en_attente")
if est_assistante:
# L'assistante voit les demandes de salle (formation / réunion)
# Elle ne reçoit pas les demandes de simple lien (lien_zoom) qui vont à l'équipe info.
reservations = reservations.exclude(salle="lien_zoom")
elif est_informatique:
# L'équipe informatique voit :
# - les demandes de lien zoom directs
# - les demandes de salle avec besoin_zoom ou besoin_ordi (déjà validés par l'assistance)
# pour lesquelles le lien_zoom n'a pas encore était fourni (lien_zoom vide)
reservations = reservations.filter(
Q(salle="lien_zoom") | Q(besoin_zoom=True) | Q(besoin_ordi=True)
)
print("Test :", [reservation.employe for reservation in reservations])
2026-04-30 13:28:57 +02:00
liste_reservation = [
{
**model_to_dict(reservation),
"id": reservation.pk,
2026-04-30 13:28:57 +02:00
"employe": f"{reservation.employe.user.first_name} {reservation.employe.user.last_name}",
2026-05-11 15:27:50 +00:00
"employe_id": reservation.employe.user.id,
2026-05-11 13:20:13 +00:00
"salle": dict(Reservation.TYPE_CHOICES).get(reservation.salle),
'statut': dict(Reservation.STATUT).get(reservation.statut),
"date_evenement": reservation.date_debut.strftime('%Y-%m-%d'),
2026-04-30 13:28:57 +02:00
} for reservation in reservations
]
return JsonResponse(liste_reservation, safe=False)
@login_required
def supprimer_reservation(request: HttpRequest, reservation_id: int):
if request.method != "POST":
return JsonResponse(
{"success": False, "message": "Méthode non autorisée."},
status=405,
)
try:
employe = Employe.objects.get(user=request.user)
except Employe.DoesNotExist:
return JsonResponse(
{"success": False, "message": "Profil employé introuvable."},
status=403,
)
try:
reservation = Reservation.objects.get(
pk=reservation_id,
employe=employe,
)
except Reservation.DoesNotExist:
return JsonResponse(
{"success": False, "message": "Réservation introuvable ou accès non autorisé."},
status=404,
)
if reservation.statut != "en_attente":
return JsonResponse(
{
"success": False,
"message": "Seules les réservations en attente peuvent être supprimées.",
},
status=400,
)
reservation.delete()
return JsonResponse(
{"success": True, "message": "Votre réservation en attente a été supprimée."}
)
2026-04-30 13:28:57 +02:00
def detail_reservation(request:HttpRequest, reservation_id:int):
reservation = Reservation.objects.get(id=reservation_id)
employe = reservation.employe.user
2026-05-07 14:44:16 +00:00
reservation_json = {
2026-04-30 13:28:57 +02:00
'id_reservation': reservation_id,
'employe': f"{employe.first_name} {employe.last_name}",
'salle': dict(Reservation.TYPE_CHOICES).get(reservation.salle),
2026-05-07 14:44:16 +00:00
'statut': dict(Reservation.STATUT).get(reservation.statut),
'date_evenement': reservation.date_debut.strftime('%Y-%m-%d'),
2026-04-30 13:28:57 +02:00
'heure_debut': reservation.heure_debut.strftime('%H:%M'),
'heure_fin': reservation.heure_fin.strftime('%H:%M'),
'motif_reservation': reservation.motif_reservation,
'besoin_zoom': reservation.besoin_zoom,
'besoin_ordinateur': reservation.besoin_ordi,
'lien_zoom': reservation.lien_zoom or '',
'est_proprietaire': request.user.is_authenticated and reservation.employe.user_id == request.user.id,
'est_en_attente': reservation.statut == "en_attente",
2026-04-30 13:28:57 +02:00
}
2026-05-11 13:20:13 +00:00
2026-04-30 13:28:57 +02:00
return JsonResponse(reservation_json, safe=True)
@login_required
def ajouter_lien_zoom(request:HttpRequest):
"""Traitement de la réservation par l'équipe informatique :
- Ajoute lien zoom si demandé.
- Valide une réservation qui exigeait un traitement par l'équipe informatique.
- Envoie un mail à toutes les équipes et au demandeur après validation.
"""
reservation = None
2026-04-30 13:28:57 +02:00
if request.method == 'POST':
reservation_id = request.POST.get('id_reservation')
2026-04-30 13:28:57 +02:00
try:
reservation = Reservation.objects.get(id=reservation_id)
except (Reservation.DoesNotExist, ValueError):
messages.error(request, "La réservation sélectionnée n'existe pas.")
return redirect('gestion_salle:reservation-salle')
2026-04-30 13:28:57 +02:00
try:
employe = Employe.objects.get(user=request.user)
except Employe.DoesNotExist:
messages.error(request, "Profil employé introuvable.")
return redirect('gestion_salle:reservation-salle')
# Vérification : seule l'équipe informatique peut traiter ces demandes
if not fonctions_utilitaire.est_equipe_informatique(employe):
messages.error(request, "Vous n'êtes pas autorisé à traiter cette demande.")
return redirect('gestion_salle:reservation-salle')
if reservation.salle == "lien_zoom" or reservation.besoin_zoom:
lien = request.POST.get("lien_zoom")
if not lien:
messages.error(request, "Veuillez fournir un lien Zoom.")
return redirect('gestion_salle:reservation-salle')
reservation.lien_zoom = lien
reservation.statut = "validee"
2026-04-30 13:28:57 +02:00
reservation.save()
messages.success(request, "La demande a été traitée et validée. ")
# Mail au demandeur
fonctions_utilitaire.envoyer_mail(
sujet="Réservation traitée",
message=f"""Bonjour {reservation.employe.user.first_name} {reservation.employe.user.last_name}, votre demande de {dict(Reservation.TYPE_CHOICES).get(reservation.salle)}{' avec lien Zoom' if reservation.lien_zoom else ''} a été traitée. Veuillez vous connecter à la plateforme pour plus de détails.""",
destinataires=[reservation.employe.user.email]
)
# Mail aux emails de l'équipe informatique (hors utilisateur courant)
autres_membres = Employe.objects.filter(departement__nom="Systeme informatique").exclude(id=employe.id).values_list('user__email', flat=True)
if autres_membres:
fonctions_utilitaire.envoyer_mail(
sujet="Réservation validée par l'équipe informatique",
message=f"""Bonjour,
La réservation {dict(Reservation.TYPE_CHOICES).get(reservation.salle)} du {reservation.date_debut.strftime('%d/%m/%Y')} pour {reservation.employe.user.first_name} {reservation.employe.user.last_name} a été traitée et validée par {employe.user.first_name} {employe.user.last_name}.\n\nVeuillez vous connecter à la plateforme pour plus de détails.""",
destinataires=list(autres_membres)
)
2026-04-30 13:28:57 +02:00
return redirect('gestion_salle:reservation-salle')
@login_required
def annuler_reservation(request:HttpRequest):
"""Vue de gestion de l'annulation de la reservation, réservée à l'employé demandeur ou aux validateurs."""
2026-04-30 13:28:57 +02:00
if request.method == 'POST':
reservation_id= request.POST['id_reservation']
try:
reservation = Reservation.objects.get(id=reservation_id)
except Reservation.DoesNotExist:
messages.error(request, "La reservation selectionné n'existe pas.")
return redirect("gestion_salle:reservation-salle")
try:
employe = Employe.objects.get(user=request.user)
except Employe.DoesNotExist:
messages.error(request, "Profil employé introuvable.")
return redirect("gestion_salle:reservation-salle")
# Seul l'employé demandeur ou un validateur (assistante / équipe info) peut annuler
est_demandeur = reservation.employe == employe
est_validateur = fonctions_utilitaire.est_assistante_direction(employe) or fonctions_utilitaire.est_equipe_informatique(employe)
if not est_demandeur and not est_validateur:
messages.error(request, "Vous n'êtes pas autorisé à annuler cette réservation.")
return redirect('gestion_salle:reservation-salle')
2026-04-30 13:28:57 +02:00
reservation.statut = 'annulee'
reservation.save()
messages.success(request, "La réservation a été annulée.")
2026-04-30 13:28:57 +02:00
return redirect('gestion_salle:reservation-salle')
@login_required
def valider_reservation(request: HttpRequest):
"""Validation d'une réservation selon le profil :
- Assistante de direction : valide les demandes de salles (sauf lien_zoom) ;
- Équipe informatique : valide les demandes de lien_zoom et les
demandes de salle avec besoin zoom/ordi restées en attente après la première correction.
"""
if request.method == 'POST':
reservation_id = request.POST.get('id_reservation')
try:
reservation = Reservation.objects.get(id=reservation_id)
except Reservation.DoesNotExist:
messages.error(request, "La réservation sélectionnée n'existe pas.")
return redirect('gestion_salle:reservation-salle')
try:
employe = Employe.objects.get(user=request.user)
except Employe.DoesNotExist:
messages.error(request, "Profil employé introuvable.")
return redirect('gestion_salle:reservation-salle')
2026-05-11 15:27:50 +00:00
est_assistante = fonctions_utilitaire.est_assistante_direction(employe)
est_equipe_info = fonctions_utilitaire.est_equipe_informatique(employe)
if est_assistante:
# L'assistante ne doit pas traiter les simple lien_zoom
if reservation.salle == "lien_zoom":
messages.error(request, "Les demandes de lien Zoom sont traitées par l'équipe informatique.")
return redirect('gestion_salle:reservation-salle')
2026-05-07 11:22:14 +00:00
if reservation.besoin_zoom or reservation.besoin_ordi:
# La demande reste en attente, l'équipe informatique doit être notifiée
# L'assistante a donné sa validation "salle", on garde le statut "en_attente"
# pour que l'équipe informatique puisse traiter, et on envoie le mail à l'équipe.
if fonctions_utilitaire.EMAIL_EQUIPE_INFORMATIQUE:
fonctions_utilitaire.envoyer_mail(
sujet="Réservation de salle à traiter par l'équipe informatique",
message=f"""Bonjour,
L'assistante de direction a validé la réservation de la salle {dict(Reservation.TYPE_CHOICES).get(reservation.salle)} pour {reservation.employe.user.first_name} {reservation.employe.user.last_name} le {reservation.date_debut.strftime('%d/%m/%Y')} (de {reservation.heure_debut.strftime('%H:%M')} à {reservation.heure_fin.strftime('%H:%M')}).
Cette demande nécessite {"un lien zoom" if reservation.besoin_zoom else ""}{" - un ordinateur" if reservation.besoin_ordi else ""}.
Veuillez vous connecter à la plateforme pour traiter cette demande.""",
destinataires=fonctions_utilitaire.EMAIL_EQUIPE_INFORMATIQUE
)
messages.success(request, "Réservation validée, une demande a été envoyée à l'équipe informatique.")
reservation.statut = "en_attente" # reste en attente pour l'équipe info
reservation.save()
else:
# Validation directe sans traitement informatique
reservation.statut = "validee"
reservation.save()
fonctions_utilitaire.envoyer_mail(
sujet="Réservation de salle validée",
message=f"""Bonjour {reservation.employe.user.first_name} {reservation.employe.user.last_name}, votre réservation de la {dict(Reservation.TYPE_CHOICES).get(reservation.salle)} du {reservation.date_debut.strftime('%d/%m/%Y')} au {reservation.date_fin.strftime('%d/%m/%Y')} pour motif "{reservation.motif_reservation}" a été validée. Veuillez vous connecter à la plateforme pour plus de détails.""",
destinataires=[reservation.employe.user.email]
)
elif est_equipe_info:
# L'équipe informatique traite le reste : lien_zoom et besoins info
if reservation.salle == "lien_zoom" or reservation.besoin_zoom or reservation.besoin_ordi:
if reservation.besoin_zoom and not reservation.lien_zoom:
# La demande exige un lien zoom qui n'a pas été encore rempli,
# il est préférable de passer par ajouter_lien_zoom. On laisse traiter.
messages.success(request, "Veuillez ajouter le lien Zoom via le champs dédié.")
return redirect('gestion_salle:reservation-salle')
reservation.statut = "validee"
reservation.save()
# Mail au demandeur + autres membres de l'équipe
fonctions_utilitaire.envoyer_mail(
sujet="Réservation validée",
message=f"""Bonjour {reservation.employe.user.first_name} {reservation.employe.user.last_name}, votre demande de {dict(Reservation.TYPE_CHOICES).get(reservation.salle)} a été traitée et validée. Veuillez vous connecter pour plus de détails.""",
destinataires=[reservation.employe.user.email]
)
autres = Employe.objects.filter(departement__nom="Systeme informatique").exclude(id=employe.id).values_list('user__email', flat=True)
if autres:
fonctions_utilitaire.envoyer_mail(
sujet="Réservation traitée par un collègue",
message=f"""Bonjour {request.user.first_name} {request.user.last_name}, la demande de {dict(Reservation.TYPE_CHOICES).get(reservation.salle)} du {reservation.date_debut.strftime('%d/%m/%Y')} pour {reservation.employe.user.first_name} {reservation.employe.user.last_name} a été traitée et validée par {employe.user.first_name} {employe.user.last_name}. Veuillez vous connecter pour plus de détails.""",
destinataires=list(autres)
)
else:
messages.error(request, "Cette réservation ne relève pas de l'équipe informatique.")
return redirect('gestion_salle:reservation-salle')
else:
messages.error(request, "Vous n'êtes pas autorisé à effectuer cette validation.")
return redirect('gestion_salle:reservation-salle')
messages.success(request, f"La réservation de {request.user.first_name} {request.user.last_name} a été validée avec succès.")
return redirect('gestion_salle:reservation-salle')
@login_required
def refuser_reservation(request:HttpRequest):
"""Vue de gestion de refus de la reservation, autorisée à l'assistant de direction
et l'équipe informatique selon le type de demande."""
data = json.loads(request.body)
reservation_id = data.get("id_reservation")
try:
reservation = Reservation.objects.get(id=reservation_id)
except Reservation.DoesNotExist:
return JsonResponse({"message": "La reservation selectionné n'existe pas."})
2026-05-07 11:22:14 +00:00
try:
employe = Employe.objects.get(user=request.user)
except Employe.DoesNotExist:
return JsonResponse({"message": "Profil employé introuvable."})
est_assistante = fonctions_utilitaire.est_assistante_direction(employe)
est_equipe_info = fonctions_utilitaire.est_equipe_informatique(employe)
peut_refuser = False
if est_assistante and reservation.salle != "lien_zoom":
peut_refuser = True
elif est_equipe_info and (reservation.salle == "lien_zoom" or reservation.besoin_zoom or reservation.besoin_ordi):
peut_refuser = True and (not reservation.besoin_zoom or reservation.lien_zoom)
if not peut_refuser:
return JsonResponse({"message": "Vous n'êtes pas autorisé à refuser cette réservation."})
reservation.statut = "refusee"
reservation.save()
fonctions_utilitaire.envoyer_mail(
sujet = "Réservation refusée",
message = f"""Bonjour {reservation.employe.user.first_name} {reservation.employe.user.last_name}, votre demande de réservation du {reservation.date_debut.strftime('%d/%m/%Y')} de {reservation.date_debut.strftime('%d/%m/%Y')} de {reservation.heure_debut.strftime('%H:%M')} à {reservation.heure_fin.strftime('%H:%M')} a été refusée.""",
destinataires = [reservation.employe.user.email]
)
2026-05-07 11:22:14 +00:00
return JsonResponse({"message": "Réservation refusée avec succès."})