import json 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 from fonction_utilitaire import fonctions_utilitaire from gestion_employe.models import Employe from gestion_salle.forms import ReservationForm 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) messages.success(request, "Réservation(s) créée(s) avec succès.") # 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="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 ) 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 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") liste_reservation = [] for reservation in reservations: if reservation.statut == "en_attente": color = "#ffc107" else: color = "#198754" 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), }) 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) 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), "id": reservation.pk, "employe": f"{reservation.employe.user.first_name} {reservation.employe.user.last_name}", "employe_id": reservation.employe.user.id, "salle": dict(Reservation.TYPE_CHOICES).get(reservation.salle), 'statut': dict(Reservation.STATUT).get(reservation.statut), "date_evenement": reservation.date_debut.strftime('%Y-%m-%d'), } 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."} ) def detail_reservation(request:HttpRequest, reservation_id:int): reservation = Reservation.objects.get(id=reservation_id) employe = reservation.employe.user reservation_json = { 'id_reservation': reservation_id, 'employe': f"{employe.first_name} {employe.last_name}", 'salle': dict(Reservation.TYPE_CHOICES).get(reservation.salle), 'statut': dict(Reservation.STATUT).get(reservation.statut), 'date_evenement': reservation.date_debut.strftime('%Y-%m-%d'), '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", } 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 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, "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, 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 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, "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 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') 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') 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."}) 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] ) return JsonResponse({"message": "Réservation refusée avec succès."})