From f061f482d9e8867e6c49beca4b6af4b919789eb9 Mon Sep 17 00:00:00 2001 From: Soriba SYLLA Date: Tue, 15 Sep 2026 12:46:19 +0000 Subject: [PATCH] Feat: Hierarchisation des validations des demandes de reservation --- .../fonctions_utilitaire.cpython-310.pyc | Bin 2327 -> 2831 bytes fonction_utilitaire/fonctions_utilitaire.py | 17 +- .../__pycache__/views.cpython-310.pyc | Bin 6041 -> 6041 bytes .../__pycache__/admin.cpython-310.pyc | Bin 199 -> 199 bytes .../__pycache__/models.cpython-310.pyc | Bin 1517 -> 1517 bytes .../__pycache__/views.cpython-310.pyc | Bin 8140 -> 12862 bytes gestion_salle/admin.py | 2 - .../static/gestion_salle/js/index.js | 3 - .../parts/modalDetailResevation.html | 10 +- gestion_salle/views.py | 293 ++++++++++++++---- 10 files changed, 255 insertions(+), 70 deletions(-) diff --git a/fonction_utilitaire/__pycache__/fonctions_utilitaire.cpython-310.pyc b/fonction_utilitaire/__pycache__/fonctions_utilitaire.cpython-310.pyc index 7e69ba46beee686c354a508214ac21b9a02b9e74..348df85974f84fc0e2f952de2f524a7373a55675 100644 GIT binary patch delta 959 zcmZ`%OHUL*5T5FpogMehuJJ2v`il%91hrEFHA{|(RmHd6hi-RKNXuqsblMcRA zR)%Pv4xuj40xhC0(h@DBF3}1dMqQ>OOG<5Y?@Jqc;yP3(f9IDrgAQvk%k@@$-f}wb zdY9E8%u&{Mxb%1(+xGmHcm&mLK)wXmOwPP#YHac{rJ-Bp)a zfopX=i?!NKe{;WQIc=HN#jKXowdj7YU*!}7E|b?tm^?uvRI|vV2Vwju8|#T}^}%+L z1s$8McilF#Pc~7O93@$k@CvHBB>(R_)rcH}Y@y%4G#>n~0jy><$DJT>gO1~M*j5eaS0O=Y6zhwYlX=Tr`ctJEjKm7i{hPLg|hgp zpD$j+TY>CeCmQI)1QWDFJ}VCNT^JLu;-ga$r4;iv{7uH~mg6>~-zbAIL?u8#!$lx* zz!&gnu~rdZ<6AH#?ie#eGk9{$hF_K2E8>GO7Rb>?PsZic_^gBl3CB+#Sy6RkuI1Bi slg-Qd2bU01N(xX&4m9K~g%AsL$%GInfc8ft`k}5GU=Tw?DW}f-1*-k%(*OVf delta 396 zcmW-c&r1S96vv(Q$IhGr1z9K$4$8tii;4S3nu^r0baP1I+H;R0Q1)VPX*%DKK-_!|hwas3Ks!$P^Q2`Fb zNp9*V)H<{W)S$h=)YCquM|O}6*dc~S9@`tRh8M!?yd-jDII_^f2_`?pS(JoZyu{Sr zu!hR;g-1w(j+83RY!%u&V@umD!Q4^ xmAFZUBg?VLQ3OX$`wRS9=BUDx993s7xMrB-RHt>s9{M@>mQ&a#U;!r-=^tdrVafmi diff --git a/fonction_utilitaire/fonctions_utilitaire.py b/fonction_utilitaire/fonctions_utilitaire.py index 9daa4ac..f751c88 100644 --- a/fonction_utilitaire/fonctions_utilitaire.py +++ b/fonction_utilitaire/fonctions_utilitaire.py @@ -15,7 +15,20 @@ try: except: pass -EMAIL_EQUIPE_INFORMATIQUE = ["support@cerfig.org"] +try: + EMAIL_EQUIPE_INFORMATIQUE = list( + Employe.objects.filter(departement__nom="Systeme informatique").values_list('user__email', flat=True) + ) or ["support@cerfig.org"] +except: + EMAIL_EQUIPE_INFORMATIQUE = ["support@cerfig.org"] + +def est_equipe_informatique(employe): + """Vérifie si un employé appartient au département informatique""" + return employe.departement and employe.departement.nom == "Systeme informatique" + +def est_assistante_direction(employe): + """Vérifie si un employé est l'assistante de direction""" + return employe.fonction == "assistant_direction" def solde_conge(employe): """Fonction de calcul du solde de congé restant l'employé""" @@ -60,4 +73,4 @@ def envoyer_mail(sujet, message, destinataires): settings.EMAIL_HOST_USER, destinataires, fail_silently=False, - ) + ) \ No newline at end of file diff --git a/gestion_conge/__pycache__/views.cpython-310.pyc b/gestion_conge/__pycache__/views.cpython-310.pyc index a871557f30dd4c1883836e118dad6970dee0f4bb..2ad7522855d18fc03a63d59987dceaa62311eec5 100644 GIT binary patch delta 20 acmbQKKU1GOpO=@50SI#6F5k%ACk_BMQU#F! delta 20 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 diff --git a/gestion_salle/admin.py b/gestion_salle/admin.py index ea5d68b..f124422 100644 --- a/gestion_salle/admin.py +++ b/gestion_salle/admin.py @@ -1,3 +1 @@ from django.contrib import admin - -# Register your models here. diff --git a/gestion_salle/static/gestion_salle/js/index.js b/gestion_salle/static/gestion_salle/js/index.js index b9b7f80..7770404 100644 --- a/gestion_salle/static/gestion_salle/js/index.js +++ b/gestion_salle/static/gestion_salle/js/index.js @@ -92,9 +92,6 @@ $("bouton-annuler").addEventListener("click", (e) => { const csrf = document.querySelector("[name=csrfmiddlewaretoken]").value; const url_annuler = $("formulaire-details").dataset.urlannuler; - console.log("URL d'annulation :"); - console.log($("formulaire-details")) - e.preventDefault(); fetch( diff --git a/gestion_salle/templates/gestion_salle/parts/modalDetailResevation.html b/gestion_salle/templates/gestion_salle/parts/modalDetailResevation.html index b39d80d..cba39ec 100644 --- a/gestion_salle/templates/gestion_salle/parts/modalDetailResevation.html +++ b/gestion_salle/templates/gestion_salle/parts/modalDetailResevation.html @@ -51,13 +51,17 @@


- {% if appartient_au_departement_informatique %} + {% if est_equipe_informatique %} {% endif %} - {% if appartient_direction %} + {% if est_assistante_direction %} {% endif %} + {% if est_equipe_informatique %} + + + {% endif %}
@@ -65,4 +69,4 @@ - + \ No newline at end of file diff --git a/gestion_salle/views.py b/gestion_salle/views.py index 065fea8..0b3d05c 100644 --- a/gestion_salle/views.py +++ b/gestion_salle/views.py @@ -3,6 +3,7 @@ from datetime import timedelta from django.contrib import messages from django.contrib.auth.decorators import login_required +from django.db.models import Q from django.forms import model_to_dict from django.http import HttpRequest, JsonResponse from django.shortcuts import redirect, render @@ -23,6 +24,9 @@ def index(request: HttpRequest): 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) @@ -54,6 +58,8 @@ def index(request: HttpRequest): 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, @@ -73,29 +79,35 @@ def index(request: HttpRequest): messages.success(request, "Réservation(s) créée(s) avec succès.") - if fonctions_utilitaire.EMAIL_ASSISTANTE_DE_DIRECTION and salle != "lien_zoom": + # 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: fonctions_utilitaire.envoyer_mail( - sujet = "Reservation de salle", - message = f"""Bonjour, + 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 + destinataires=fonctions_utilitaire.EMAIL_ASSISTANTE_DE_DIRECTION ) - if (salle == "lien_zoom" or besoin_zoom or besoin_ordi) and fonctions_utilitaire.EMAIL_EQUIPE_INFORMATIQUE: + 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 réservation de salle nécessitant un 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')} a {heure_fin.strftime('%H:%M')} pour le motif {motif} +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 plus de détails.""", - destinataires=fonctions_utilitaire.EMAIL_EQUIPE_INFORMATIQUE +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 + # departement = employe.departement liste_demande_reservation = Reservation.objects.filter( employe=employe, @@ -105,8 +117,9 @@ Veuillez vous connecter à la plateforme pour plus de détails.""", context = { 'formulaire_reservation': formulaire_reservation, 'nb_reservation_attente': Reservation.objects.filter(statut='en_attente').count(), - 'appartient_au_departement_informatique': departement and departement.nom == "Systeme informatique", - 'appartient_direction': request.user.groups.filter(name='direction').exists(), + '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), } @@ -118,7 +131,6 @@ def liste_reservation(request:HttpRequest): liste_reservation = [] for reservation in reservations: - print(reservation.statut) color = None if reservation.statut == "en_attente": color = "#ffc107" @@ -127,8 +139,6 @@ def liste_reservation(request:HttpRequest): else: color = "#dc3545" - print(dict(Reservation.TYPE_CHOICES).get(reservation.salle)) - liste_reservation.append({ "guid": reservation.pk, "title": dict(Reservation.TYPE_CHOICES).get(reservation.salle), @@ -141,7 +151,35 @@ def liste_reservation(request:HttpRequest): @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) + 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]) + liste_reservation = [ { **model_to_dict(reservation), @@ -177,49 +215,99 @@ def detail_reservation(request:HttpRequest, reservation_id:int): @login_required def ajouter_lien_zoom(request:HttpRequest): - """Vue de gestion de l'ajout du lien zoom""" - 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 resevertion selectionné n'existe pas.") - return redirect("salle") - - if not (reservation.besoin_zoom or reservation.salle == "lien_zoom"): - messages.error(request, "Cette réservation ne nécessite pas de lien Zoom.") - return redirect('gestion_salle:reservation-salle') - elif reservation.statut in ["annulee", "refusee"]: - messages.error(request, "Le lien Zoom ne peut être ajouté pour les réservations annulée ou refusée.") - return redirect('gestion_salle:reservation-salle') + """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 - if request.method == "POST": - lien = request.POST.get("lien_zoom") - reservation.lien_zoom = lien - reservation.statut = "validee" + if request.method == 'POST': + reservation_id = request.POST.get('id_reservation') + 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') + + 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" reservation.save() - messages.success(request, "Le lien Zoom a été ajouté et la réservation a été validée. ") + 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) + ) + return redirect('gestion_salle:reservation-salle') @login_required def annuler_reservation(request:HttpRequest): - """Vue de gestion de l'annulation de la reservation""" + """Vue de gestion de l'annulation de la reservation, réservée à l'employé demandeur ou aux validateurs.""" 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 resevertion selectionné n'existe pas.") - return redirect("salle") - + 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') + reservation.statut = 'annulee' reservation.save() - messages.success(request, "Votre réservation a été annulée.") + messages.success(request, "La réservation a été annulée.") return redirect('gestion_salle:reservation-salle') @login_required def valider_reservation(request: HttpRequest): - """Validation d'une réservation""" + """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') @@ -227,37 +315,122 @@ def valider_reservation(request: HttpRequest): reservation = Reservation.objects.get(id=reservation_id) except Reservation.DoesNotExist: messages.error(request, "La réservation sélectionnée n'existe pas.") - return redirect("salle") + return redirect('gestion_salle:reservation-salle') - reservation.statut = 'validee' - reservation.save() + try: + employe = Employe.objects.get(user=request.user) + except Employe.DoesNotExist: + messages.error(request, "Profil employé introuvable.") + return redirect('gestion_salle:reservation-salle') - fonctions_utilitaire.envoyer_mail( - sujet = "Reservation de salle", - message = f"""Bonjour {reservation.employe.user.first_name} {reservation.employe.user.last_name}, votre reservation de la salle {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] - ) + est_assistante = fonctions_utilitaire.est_assistante_direction(employe) + est_equipe_info = fonctions_utilitaire.est_equipe_informatique(employe) - messages.success(request, f"La réservation de {request.user.first_name} {request.user.last_name} validée avec succès.") + 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') + + 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""" + """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 resevertion selectionné n'existe pas."}) - else: - reservation.statut = "refusee" - reservation.save() + return JsonResponse({"message": "La reservation selectionné n'existe pas."}) - fonctions_utilitaire.envoyer_mail( - sujet = "Reservation de salle", - message = f"""Bonjour {request.user.first_name} {request.user.last_name}, votre reservation de la salle {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é refusée. Veuillez vous connecter à l'Assistante de Direction pour plus de détails.""", - destinataires = [reservation.employe.user.email] - ) + try: + employe = Employe.objects.get(user=request.user) + except Employe.DoesNotExist: + return JsonResponse({"message": "Profil employé introuvable."}) - return JsonResponse({"message": "Réservation refusée avec succès."}) + 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] + ) + + return JsonResponse({"message": "Réservation refusée avec succès."}) \ No newline at end of file