5 Commits

Author SHA1 Message Date
Fatimac2804
29a42ee6be feat:Ajout notification email équipe informatique
All checks were successful
Organisation/sirh/pipeline/head This commit looks good
Organisation/sirh/pipeline/pr-main This commit looks good
2026-09-10 12:23:45 +02:00
904954c735 Fix: Modification de mot de passe
All checks were successful
Organisation/sirh/pipeline/head This commit looks good
2026-09-08 11:09:53 +00:00
99e764ab4d Merge branch 'main' of https://jenkins.cerfig.org/CERFIG/sirh 2026-09-07 12:10:34 +00:00
0eb745d5f3 Fix: Correction de l'affichage des dates dans les détails de la réservation 2026-09-07 12:07:58 +00:00
097648c03e Fix: Correction de l'affichage des dates dans les détails de la réservation
All checks were successful
Organisation/sirh/pipeline/head This commit looks good
2026-09-07 11:33:21 +00:00
15 changed files with 91 additions and 46 deletions

2
Jenkinsfile vendored
View File

@@ -16,7 +16,7 @@ pipeline
{ {
steps steps
{ {
sh 'echo "Debut du pipeline"' sh 'echo "Debut du pipeline test"'
cleanWs() cleanWs()
checkout scm checkout scm
} }

View File

@@ -15,6 +15,9 @@
<li class="nav-item"> <li class="nav-item">
<a class="nav-link" href="{{ SGL_LIEN }}">SGL</a> <a class="nav-link" href="{{ SGL_LIEN }}">SGL</a>
</li> </li>
<li class="nav-item">
<a class="nav-link" href="#">Laboratoire</a>
</li>
</ul> </ul>
</div> </div>
</div> </div>

View File

@@ -15,6 +15,8 @@ try:
except: except:
pass pass
EMAIL_EQUIPE_INFORMATIQUE = ["support@cerfig.org"]
def solde_conge(employe): def solde_conge(employe):
"""Fonction de calcul du solde de congé restant l'employé""" """Fonction de calcul du solde de congé restant l'employé"""
contrat = Contrat.objects.filter(employe=employe, statut='actif').order_by('-date_debut').first() contrat = Contrat.objects.filter(employe=employe, statut='actif').order_by('-date_debut').first()

View File

@@ -12,7 +12,7 @@
</div> </div>
{% if messages %} {% if messages %}
{% for message in messages %} {% for message in messages %}
<div class="alert alert-{% if message.tags == 'error' %}danger{% else %}success{% endif %} alert-dismissible fade show" role="alert"> <div class="alert alert-{% if message.tags == 'error' %}danger{% else %}success{% endif %} alert-dismissible fade show mt-2" role="alert">
{{ message }} {{ message }}
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button> <button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
</div> </div>

View File

@@ -6,7 +6,7 @@
<button type="button" class="btn-close btn-close-white" data-bs-dismiss="modal"></button> <button type="button" class="btn-close btn-close-white" data-bs-dismiss="modal"></button>
</div> </div>
<div class="modal-body"> <div class="modal-body">
<form method="POST" action="{% url 'gestion_employe:modifier-mot-passe' %}"> <form method="POST" action="{% url 'gestion_employe:modifier-mot-passe' %}" id="formulaire_mot_de_passe">
{% csrf_token %} {% csrf_token %}
<div class="form-group mb-2"> <div class="form-group mb-2">
<label>Votre mot de passe actuel :</label> <label>Votre mot de passe actuel :</label>

View File

@@ -1,11 +1,14 @@
import json import json
from datetime import datetime, timedelta from datetime import datetime, timedelta
import requests
from dateutil.relativedelta import relativedelta from dateutil.relativedelta import relativedelta
from django.contrib import messages from django.contrib import messages
from django.contrib.auth import authenticate from django.contrib.auth import logout
from django.contrib.auth.decorators import login_required from django.contrib.auth.decorators import login_required
from django.contrib.auth.models import User from django.contrib.auth.models import User
from django.contrib.auth.password_validation import validate_password
from django.core.exceptions import ValidationError
from django.db.models import Sum from django.db.models import Sum
from django.forms.models import model_to_dict from django.forms.models import model_to_dict
from django.http import JsonResponse from django.http import JsonResponse
@@ -176,25 +179,6 @@ def mon_profil(request):
"contrat_nb_jours_restant": max(0, contrats.nombre_jours_restant if contrats else 0) "contrat_nb_jours_restant": max(0, contrats.nombre_jours_restant if contrats else 0)
} }
) )
@login_required
def modifier_mot_passe(request):
"""Vue pour permettre à un utilisateur de modifier son mot de passe et ses informations de profil"""
user = User.objects.get(username=request.user)
if request.method == "POST":
ancien_mdp = request.POST["ancien-mdp"]
nouveau_mdp = request.POST["nouveau-mdp"]
confirmation_mdp = request.POST["confirmation-mdp"]
if authenticate(request, username=request.user, password=ancien_mdp) is None:
messages.error(request, "Ancien mot de passe incorrect.")
elif nouveau_mdp != confirmation_mdp:
messages.error(request, "Les deux nouveaux ne correspondent pas.")
else:
user.set_password(nouveau_mdp)
user.save()
messages.success(request, "Mot de passe modifié avec succès.")
return redirect("gestion_employe:mon-profil")
def modifier_employer(request): def modifier_employer(request):
"""Vue pour permettre à un utilisateur de modifier les informations d'un employé""" """Vue pour permettre à un utilisateur de modifier les informations d'un employé"""
@@ -302,7 +286,6 @@ def creation_contrat(request):
'contrat_form': form 'contrat_form': form
}) })
@login_required @login_required
def contrats_employe(request, employe_id): def contrats_employe(request, employe_id):
@@ -341,6 +324,7 @@ def contrats_employe(request, employe_id):
"employe": employe.user.get_full_name(), "employe": employe.user.get_full_name(),
"contrats": data "contrats": data
}) })
@login_required @login_required
def enregistrer_detail_employe(request): def enregistrer_detail_employe(request):
"""Vue pour permettre à un utilisateur de modifier les détails d'un employé via une requête AJAX""" """Vue pour permettre à un utilisateur de modifier les détails d'un employé via une requête AJAX"""
@@ -379,7 +363,6 @@ def enregistrer_detail_employe(request):
else: else:
return JsonResponse({"message": "Méthode non autorisée."}, status=405) return JsonResponse({"message": "Méthode non autorisée."}, status=405)
@login_required @login_required
def liste_employe(request): def liste_employe(request):
""" Vue pour retourner la liste de tous les employés """ """ Vue pour retourner la liste de tous les employés """
@@ -509,3 +492,47 @@ def supprimer_formation(request, id_formation):
formation.delete() formation.delete()
messages.success(request, "Formation supprimée ") messages.success(request, "Formation supprimée ")
return redirect("mes_formations") return redirect("mes_formations")
@login_required
def modifier_mot_passe(request):
"""Vue pour permettre à un employé de modifier son mot de passe"""
if request.method == "POST":
username = request.user.username
ancien_mdp = request.POST.get("ancien-mdp")
nouveau_mdp = request.POST.get("nouveau-mdp")
confirmation_mdp = request.POST.get("confirmation-mdp")
try:
validate_password(nouveau_mdp, user=request.user)
except ValidationError as e:
messages.error(request, f"Erreur : {' '.join(e.messages)}")
return redirect("gestion_employe:mon-profil")
donnees = {
"username": username,
"ancien_mdp": ancien_mdp,
"nouveau_mdp": nouveau_mdp,
"confirmation_mdp": confirmation_mdp
}
headers = {
"Content-Type": "application/json",
"Accept": "application/json",
}
response = requests.post(
"http://localhost:8000/modifier-mot-de-passe/",
json=donnees,
headers=headers
)
data = response.json()
cle = next(iter(data.keys()))
valeur = next(iter(data.values()))
if cle == "success":
messages.success(request, valeur)
else:
messages.error(request, valeur)
return redirect("gestion_employe:mon-profil")

View File

@@ -26,7 +26,7 @@ const calendrier = Schedule(document.getElementById('planning-reservation'), {
$("employe").value=data.employe; $("employe").value=data.employe;
$("salle").value=data.salle; $("salle").value=data.salle;
$("statut-reservation").innerHTML=data.statut; $("statut-reservation").innerHTML=data.statut;
$("date_debut").value=data.date_debut; $("date_evenement").value=data.date_evenement;
$("heure_debut").value=data.heure_debut; $("heure_debut").value=data.heure_debut;
$("heure_fin").value=data.heure_fin; $("heure_fin").value=data.heure_fin;
$("motif_reservation").value=data.motif_reservation; $("motif_reservation").value=data.motif_reservation;
@@ -171,7 +171,7 @@ const tableau_reservation_attente = new Tabulator("#tableau-reservation-attente"
columns: [ columns: [
{title: "Employé", field: "employe"}, {title: "Employé", field: "employe"},
{title: "Salle", field: "salle"}, {title: "Salle", field: "salle"},
{title: "Date de l'evenement", field: "date_debut"}, {title: "Date de l'evenement", field: "date_evenement"},
{title: "Heure de début", field: "heure_debut"}, {title: "Heure de début", field: "heure_debut"},
{title: "Heure de fin", field: "heure_fin"}, {title: "Heure de fin", field: "heure_fin"},
{title: "Motif de reservation", field: "motif_reservation"}, {title: "Motif de reservation", field: "motif_reservation"},
@@ -183,6 +183,8 @@ const tableau_reservation_attente = new Tabulator("#tableau-reservation-attente"
tableau_reservation_attente.on("rowClick", (row, rowData) => { tableau_reservation_attente.on("rowClick", (row, rowData) => {
const data = rowData.getData(); const data = rowData.getData();
console.log(data);
if(data.besoin_zoom === false){ if(data.besoin_zoom === false){
$("lien_zoom_container").className = 'd-none'; $("lien_zoom_container").className = 'd-none';
} }
@@ -196,8 +198,7 @@ tableau_reservation_attente.on("rowClick", (row, rowData) => {
$("employe").value=data.employe; $("employe").value=data.employe;
$("salle").value=data.salle; $("salle").value=data.salle;
$("statut-reservation").innerHTML=data.statut; $("statut-reservation").innerHTML=data.statut;
$("date_debut").value = data.date_debut; $("date_evenement").value = data.date_evenement;
$("date_fin").value = data.date_fin;
$("heure_debut").value=data.heure_debut; $("heure_debut").value=data.heure_debut;
$("heure_fin").value=data.heure_fin; $("heure_fin").value=data.heure_fin;
$("motif_reservation").value=data.motif_reservation; $("motif_reservation").value=data.motif_reservation;

View File

@@ -16,7 +16,7 @@
{% endif %} {% endif %}
<div class="row"> <div class="row">
<div class="col py-2 px-4"> <div class="col py-2 px-4">
<h1 class="text-center">Reservation de salle</h1> <h1 class="text-center">Réservation de salle</h1>
<hr> <hr>
<div class="row d-flex justify-content-around"> <div class="row d-flex justify-content-around">
<div class="form-group col-5 me-2"> <div class="form-group col-5 me-2">
@@ -34,7 +34,7 @@
</div> </div>
<div class="row mt-2"> <div class="row mt-2">
<div class="col d-flex justify-content-center"> <div class="col d-flex justify-content-center">
<button class="btn btn-lg btn-primary me-2" data-bs-toggle="modal" data-bs-target="#modalReservation">Nouvelle reservation</button> <button class="btn btn-lg btn-primary me-2" data-bs-toggle="modal" data-bs-target="#modalReservation">Nouvelle réservation</button>
<button type="button" class="btn btn-warning" data-bs-toggle="modal" data-bs-target="#modalReservationAttente"> <button type="button" class="btn btn-warning" data-bs-toggle="modal" data-bs-target="#modalReservationAttente">
Validation en attente <span class="badge badge-light">{{ nb_reservation_attente }}</span> Validation en attente <span class="badge badge-light">{{ nb_reservation_attente }}</span>
</button> </button>

View File

@@ -4,7 +4,7 @@
<div class="modal-content"> <div class="modal-content">
<div class="modal-header"> <div class="modal-header">
<h1 class="modal-title fs-5" id="modalReservationLabel"> <h1 class="modal-title fs-5" id="modalReservationLabel">
Nouvelle reservation Nouvelle réservation
</h1> </h1>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button> <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div> </div>

View File

@@ -5,7 +5,7 @@
<div class="modal-content"> <div class="modal-content">
<div class="modal-header"> <div class="modal-header">
<h1 class="modal-title fs-5" id="modalDetailReservationLabel"> <h1 class="modal-title fs-5" id="modalDetailReservationLabel">
Détails de la reservation (<span id='statut-reservation'></span>) Détails de la réservation (<span id='statut-reservation'></span>)
</h1> </h1>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button> <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div> </div>
@@ -22,12 +22,8 @@
<input class="form-control" id="salle" readonly> <input class="form-control" id="salle" readonly>
</div> </div>
<div class="form-group mb-2"> <div class="form-group mb-2">
<label>Date de debut :</label> <label>Date de l'évènement :</label>
<input type='date' class="form-control" id="date_debut" readonly > <input type='date' class="form-control" id="date_evenement" readonly >
</div>
<div class="form-group mb-2">
<label>Date de fin :</label>
<input type='date' class="form-control" id="date_fin" readonly >
</div> </div>
<div class="form-group mb-2"> <div class="form-group mb-2">
<label>Heure de début :</label> <label>Heure de début :</label>
@@ -38,7 +34,7 @@
<input type='time' class="form-control" id="heure_fin" readonly > <input type='time' class="form-control" id="heure_fin" readonly >
</div> </div>
<div class="form-group mb-2"> <div class="form-group mb-2">
<label>Motif de la reservation :</label> <label>Motif de la réservation :</label>
<textarea class="form-control" id="motif_reservation" readonly ></textarea> <textarea class="form-control" id="motif_reservation" readonly ></textarea>
</div> </div>
<div class="form-check mb-2"> <div class="form-check mb-2">
@@ -53,6 +49,7 @@
<label class="form-check-label">Besoin d'un ordinateur {{ reservation.statut }}</label> <label class="form-check-label">Besoin d'un ordinateur {{ reservation.statut }}</label>
<input type="checkbox" class="form-check-input" id="besoin_ordinateur" readonly > <input type="checkbox" class="form-check-input" id="besoin_ordinateur" readonly >
</div> </div>
<hr />
<div class='d-flex justify-content-around mt-2'> <div class='d-flex justify-content-around mt-2'>
{% if appartient_au_departement_informatique %} {% if appartient_au_departement_informatique %}
<button class="btn btn-primary" id="ajoutZoom">Ajout du lien zoom</button> <button class="btn btn-primary" id="ajoutZoom">Ajout du lien zoom</button>

View File

@@ -4,7 +4,7 @@
<div class="modal-content"> <div class="modal-content">
<div class="modal-header"> <div class="modal-header">
<h1 class="modal-title fs-5" id="modalReservationAttenteLabel"> <h1 class="modal-title fs-5" id="modalReservationAttenteLabel">
Liste des reservation en attente de validation Liste des réservation en attente de validation
</h1> </h1>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button> <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div> </div>

View File

@@ -78,11 +78,25 @@ def index(request: HttpRequest):
fonctions_utilitaire.envoyer_mail( fonctions_utilitaire.envoyer_mail(
sujet = "Reservation de salle", sujet = "Reservation de salle",
message = f""" message = f"""
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}". 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}".
Veuillez vous connecter à la plateforme pour plus de détails.""", Veuillez vous connecter à la plateforme pour plus de détails.""",
destinataires = fonctions_utilitaire.EMAIL_ASSISTANTE_DE_DIRECTION destinataires = fonctions_utilitaire.EMAIL_ASSISTANTE_DE_DIRECTION
) )
return redirect('gestion_salle:reservation-salle') # return redirect('gestion_salle:reservation-salle')
if salle == "lien_zoom" or besoin_zoom or besoin_ordi :
if 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}
Veuillez vous connecter à la plateforme pour plus de détails.""",
destinataires=fonctions_utilitaire.EMAIL_EQUIPE_INFORMATIQUE
)
return redirect('gestion_salle:reservation-salle')
formulaire_reservation = ReservationForm() formulaire_reservation = ReservationForm()
departement = employe.departement departement = employe.departement
@@ -139,6 +153,7 @@ def liste_reservation_attente(request):
"employe_id": reservation.employe.user.id, "employe_id": reservation.employe.user.id,
"salle": dict(Reservation.TYPE_CHOICES).get(reservation.salle), "salle": dict(Reservation.TYPE_CHOICES).get(reservation.salle),
'statut': dict(Reservation.STATUT).get(reservation.statut), 'statut': dict(Reservation.STATUT).get(reservation.statut),
"date_evenement": reservation.date_debut.strftime('%Y-%m-%d'),
} for reservation in reservations } for reservation in reservations
] ]
@@ -151,7 +166,7 @@ def detail_reservation(request:HttpRequest, reservation_id:int):
reservation_json = { reservation_json = {
'id_reservation': reservation_id, 'id_reservation': reservation_id,
'employe': f"{employe.first_name} {employe.last_name}", 'employe': f"{employe.first_name} {employe.last_name}",
'salle': reservation.salle, 'salle': dict(Reservation.TYPE_CHOICES).get(reservation.salle),
'statut': dict(Reservation.STATUT).get(reservation.statut), 'statut': dict(Reservation.STATUT).get(reservation.statut),
'date_evenement': reservation.date_debut.strftime('%Y-%m-%d'), 'date_evenement': reservation.date_debut.strftime('%Y-%m-%d'),
'heure_debut': reservation.heure_debut.strftime('%H:%M'), 'heure_debut': reservation.heure_debut.strftime('%H:%M'),