Premiere version SIRH

This commit is contained in:
2026-04-27 10:17:10 +00:00
commit 9865860254
485 changed files with 46065 additions and 0 deletions

View File

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

3
gestion_conge/admin.py Normal file
View File

@@ -0,0 +1,3 @@
from django.contrib import admin
# Register your models here.

6
gestion_conge/apps.py Normal file
View File

@@ -0,0 +1,6 @@
from django.apps import AppConfig
class GestionCongeConfig(AppConfig):
default_auto_field = 'django.db.models.BigAutoField'
name = 'gestion_conge'

17
gestion_conge/forms.py Normal file
View File

@@ -0,0 +1,17 @@
from django import forms
from .models import Conge
class CongeForm(forms.ModelForm):
"""Formulaire de demande de congé."""
class Meta:
model = Conge
fields =['type', 'date_debut', 'date_fin']
widgets = {
'date_debut': forms.DateInput(attrs={'type': 'date', 'class': 'form-control'}),
'date_fin': forms.DateInput(attrs={'type': 'date', 'class': 'form-control'}),
'type': forms.Select(attrs={'class': 'form-select'}),
}
labels = {
'nombre_jours':'Nombre de jours',
}

View File

@@ -0,0 +1,30 @@
# Generated by Django 5.2.13 on 2026-04-17 12:03
import django.db.models.deletion
from django.db import migrations, models
class Migration(migrations.Migration):
initial = True
dependencies = [
('gestion_employe', '0001_initial'),
]
operations = [
migrations.CreateModel(
name='Conge',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('date_debut', models.DateField(verbose_name='Date de Début')),
('date_fin', models.DateField(verbose_name='Date de Fin')),
('type', models.CharField(choices=[('conge_annuel', 'Conge Annuel')], max_length=100, verbose_name='Type de Congé')),
('date_demande', models.DateField(auto_now_add=True, verbose_name='Date de Demande')),
('validation_hierarchique', models.BooleanField(default=None, null=True)),
('validation_direction', models.BooleanField(default=None, null=True)),
('motif_refus', models.TextField(blank=True, null=True)),
('employe', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='employe', to='gestion_employe.employe')),
],
),
]

View File

36
gestion_conge/models.py Normal file
View File

@@ -0,0 +1,36 @@
import pandas as pd
from django.db import models
from gestion_employe.models import Employe
class Conge(models.Model):
"""Modèle de création des congés"""
TYPE_CHOICES = [
# ('maladie', 'Maladie'),
('conge_annuel', 'Conge Annuel'),
# ('conge_maternite', 'Conge Maternité'),
# ('conge_mariage', 'Conge Mariage'),
# ('conge_naissance', 'Conge de Naissance'),
# ('conge_deces_proche', 'Conge de décès d\'un proche'),
# ('conge_mariage_proche', 'Conge de mariage d\'un proche'),
# ('autre', 'Autre'),
]
employe = models.ForeignKey(
Employe,
on_delete=models.CASCADE,
related_name="employe"
)
date_debut = models.DateField(verbose_name='Date de Début')
date_fin = models.DateField(verbose_name='Date de Fin')
type = models.CharField(max_length=100, choices=TYPE_CHOICES, verbose_name='Type de Congé')
date_demande = models.DateField(auto_now_add=True, verbose_name="Date de Demande")
validation_hierarchique = models.BooleanField(default=None, null=True)
validation_direction = models.BooleanField(default=None, null=True)
motif_refus = models.TextField(blank=True, null=True)
@property
def nombre_jours(self):
if self.date_debut and self.date_fin:
jours = pd.bdate_range(start=self.date_debut, end=self.date_fin)
return len(jours)

View File

@@ -0,0 +1,75 @@
const bouton_enregistrer_detail = document.getElementById("bouton-enregistrer-detail-conge");
if(bouton_enregistrer_detail){
bouton_enregistrer_detail.addEventListener("click", () => {
const form = document.getElementById("form-detail-conge");
const csrftoken = new FormData(form).get("csrfmiddlewaretoken");
const actionUrl = form.action;
const id_conge = document.getElementById("id_conge").value;
const validation_hierarchique_input = document.querySelector('input[name="validation_hierarchique"]:checked');
const validation_hierarchique = validation_hierarchique_input ? validation_hierarchique_input.value : null;
const validation_direction_input = document.querySelector('input[name="validation_direction"]:checked');
const validation_direction = validation_direction_input ? validation_direction_input.value : null;
const motif_refus = document.getElementById("motif_refus").value;
fetch(actionUrl, {
method: "POST",
headers: {
"Content-Type": "application/json",
"X-CSRFToken": csrftoken
},
body: JSON.stringify({
id_conge,
validation_hierarchique,
validation_direction,
motif_refus
})
})
.then(response => response.json())
.then(data => {
alert(data.message);
navigation.reload();
});
})
}
if(document.getElementById("validation_hierarchique_refuse")){
document.getElementById("validation_hierarchique_refuse").addEventListener('click', function(){
if(this.checked){
alert("coucou");
document.getElementById("motif_refus_container").className="d-block form-group mt-3";
}else{
document.getElementById("motif_refus_container").className="d-none";
}
})
}
if(document.getElementById("validation_hierarchique_refuse")){
document.getElementById("validation_hierarchique_refuse").addEventListener('click', function(){
if(this.checked){
document.getElementById("motif_refus_container").className="d-block form-group mt-3";
}else{
document.getElementById("motif_refus_container").className="d-none";
}
})
}
if(document.getElementById("validation_hierarchique_valide")){
document.getElementById("validation_hierarchique_valide").addEventListener('click', function(){
if(this.checked){
document.getElementById("motif_refus_container").className="d-none";
}else{
document.getElementById("motif_refus_container").className="d-block form-group mt-3";
}
})
}
if(document.getElementById("validation_direction_valide")){
document.getElementById("validation_direction_valide").addEventListener('click', function(){
if(this.checked){
document.getElementById("motif_refus_container").className="d-block form-group mt-3";
}else{
document.getElementById("motif_refus_container").className="d-none";
}
})
}

View File

@@ -0,0 +1,71 @@
const $ = (element) => document.getElementById(element);
const url_liste_conge_attente = $("liste-demande-conges").dataset.url
const tableau_liste_demande_conge = new Tabulator("#liste-demande-conges", {
layout : "fitColumns",
columns: [
{"title": "Nom et Prénom", "field": "prenom_nom"},
{"title": "Date de début", "field": "date_debut", formatter:"datetime", formatterParams:{
inputFormat:"yyyy-MM-dd",
outputFormat:"dd/MM/yy",
}},
{"title": "Date de fin", "field": "date_fin", formatter:"datetime", formatterParams:{
inputFormat:"yyyy-MM-dd",
outputFormat:"dd/MM/yy",
}},
{"title": "Type de congé", "field": "type"},
{"title": "Date de la demande", "field": "date_demande"},
{"title": "Validation par supérieur hiérarchique", "field": "validation_hierarchique", formatter:"tickCross", formatterParams :{
allowEmpty : true ,
}},
{"title": "Validation par supérieur hiérarchique", "field": "validation_direction", formatter:"tickCross", formatterParams :{
allowEmpty : true ,
}},
],
pagination: true,
paginationSize: 5
})
const bouton_demande_conges = $("bouton-demande-conge");
bouton_demande_conges.addEventListener("click", (e) => {
var modalDemandeConge = new bootstrap.Modal(document.getElementById('modalDemandeConge'));
modalDemandeConge.show();
})
tableau_liste_demande_conge.on("rowClick", function(row, rowData) {
const data = rowData.getData();
$("id_conge").value = data.id;
$("employe").value = data.prenom_nom;
$("type_conge").value = data.type;
$("date_debut").value = data.date_debut;
$("date_fin").value = data.date_fin;
$("date_demande").value = data.date_demande;
$("nombre_jours").value = data.nombre_jours;
$("solde_restant").value = data.solde_conge;
$("motif_refus").value = data.motif_refus;
if($("validation_hierarchique_valide") & $("validation_hierarchique_refuse")){
$("validation_hierarchique_valide").checked = data.validation_hierarchique === true;
$("validation_hierarchique_refuse").checked = data.validation_hierarchique === false;
}
if($("validation_direction_valide") & $("validation_direction_refuse")){
$("validation_direction_valide").checked = data.validation_direction === true;
$("validation_direction_refuse").checked = data.validation_direction === false;
}
const modal = new bootstrap.Modal(document.getElementById('detailsCongeModal'));
modal.show();
});
fetch(url_liste_conge_attente)
.then(response => response.json())
.then(data => {
if(data.success === true){
tableau_liste_demande_conge.setData(data.data)
}else{
alert(data.message)
}
})

View File

@@ -0,0 +1,51 @@
{% extends "BASE.html" %}
{% load static %}
{% block 'titre_page' %} Gestion des congés {% endblock %}
{% block 'contenu' %}
{% if messages %}
{% for message in messages %}
<div class="alert alert-{% if message.tags == 'error' %}danger{% else %}success{% endif %}">{{message}}</div>
{% endfor %}
{% endif %}
<div class="row d-flex justify-content-center mb-4">
<div class="col text-white bg-danger d-flex flex-column justify-content-center align-items-center border rounded p-4">
<div class="card-header fw-bold">
<i class="bi bi-x-circle me-2"></i> Congés refusés
</div>
<div class="card-body text-center">
<h5 class="fw-bold">{{ nombre_conges_refuse }}</h5>
</div>
</div>
<div class="col text-white bg-warning d-flex flex-column justify-content-center align-items-center border rounded p-4 mx-2">
<div class="card-header fw-bold">
<i class="bi bi-hourglass-split me-2"></i> Congés en attente
</div>
<div class="card-body text-center">
<h5 class="fw-bold">{{ nombre_conges_en_attente }}</h5>
</div>
</div>
<div class="col text-white bg-success d-flex flex-column justify-content-center align-items-center border rounded p-4">
<div class="card-header fw-bold">
<i class="bi bi-check-circle me-2"></i> Congés Validé
</div>
<div class="card-body text-center">
<h5 class="fw-bold">{{ nombre_conges_valide }}</h5>
</div>
</div>
<div class="d-flex justify-content-between my-4">
<h3><i class="bi bi-list-ul"></i> Liste des demandes de congé</h3>
<button class='btn btn-primary' id="bouton-demande-conge">Demande de congé</button>
</div>
<div class="table-responsive">
<div id="liste-demande-conges" data-url="{% url 'gestion_conges:liste-des-conges' %}"></div>
</div>
</div>
{% endblock %}
{% block 'modal' %}
{% include 'gestion_conge/parts/modalDemandeConge.html' %}
{% include 'gestion_conge/parts/modalDetailConge.html' %}
{% endblock %}
{% block 'js' %}
<script type="text/javascript" src="{% static 'gestion_conge/js/index.js' %}"></script>
<script type="text/javascript" src="{% static 'gestion_conge/js/detail_conges.js' %}"></script>
{% endblock %}

View File

@@ -0,0 +1,26 @@
<div class="modal fade" id="modalDemandeConge" tabindex="-1" aria-labelledby="modalDemandeCongeLabel" aria-hidden="true">
<div class="modal-dialog modal-dialog-scrollable">
<div class="modal-content">
<div class="modal-header bg-orange-dark">
<h5 class="modal-title" id="modalDemandeCongeLabel">
<i class="bi bi-calendar-plus me-2"></i> Nouvelle demande de congé
</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Fermer"></button>
</div>
<div class="modal-body">
<form method="post" action="{% url 'gestion_conges:demande-conge' %}" enctype="multipart/form-data">
{% csrf_token %}
{{ formulaire_demande_conge.as_p }}
<div class="modal-footer">
<button type="submit" class="btn btn-success">
<i class="bi bi-send"></i> Soumettre
</button>
<button type="button" class="btn btn-outline-secondary" data-bs-dismiss="modal">
<i class="bi bi-x-circle"></i> Annuler
</button>
</div>
</form>
</div>
</div>
</div>
</div>

View File

@@ -0,0 +1,92 @@
<div class="modal fade" id="detailsCongeModal" tabindex="-1" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header bg-orange-dark">
<h5 class="modal-title">Détails de la demande de congés</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
</div>
<div class="modal-body">
<form method="post" id="form-detail-conge" action="{% url 'gestion_conges:validation-des-conges' %}">
{% csrf_token %}
<input type="hidden" id="id_conge" value="">
<div class="form-group mb-2">
<label for="motif_refus">Employé :</label>
<input type="text" class="form-control" id="employe" value="" readonly>
</div>
<div class="form-group mb-2">
<label for="type_conge">Type de congé :</label>
<select class="form-select" id="type_conge" readonly>
{% comment %} <option value="maladie">Maladie</option> {% endcomment %}
<option value="Conge Annuel">Conge Annuel</option>
{% comment %} <option value="conge_maternite">Conge Maternité</option> {% endcomment %}
{% comment %} <option value="conge_mariage">Conge Mariage</option>
<option value="conge_naissance">Conge Naissance</option>
<option value="conge_deces_proche">Conge de décès d'un proche</option>
<option value="conge_mariage_proche">Conge de mariage d'un proche</option>
<option value="autre">Autre</option> {% endcomment %}
</select>
</div>
<div class="form-group mb-2">
<label for="date_debut">Date début :</label>
<input type="date" class="form-control" id="date_debut" value="" readonly>
</div>
<div class="form-group mb-2">
<label for="date_fin">Date fin :</label>
<input type="date" class="form-control" id="date_fin" value="" readonly>
</div>
<div class="form-group mb-2">
<label for="nombre_jours">Nombre de jours :</label>
<input type="number" class="form-control" id="nombre_jours" value="" readonly>
</div>
<div class="form-group mb-2">
<label for="solde_restant">Solde restant :</label>
<input type="number" class="form-control" id="solde_restant" value="" readonly>
</div>
<div class="form-group mb-2">
<label for="date_demande">Date de la demande :</label>
<input type="text" class="form-control" id="date_demande" value="" readonly>
</div>
{% if employe_est_il_chef or est_chef_projet %}
<hr>
<h5 class="text-center">Validation par le supérieur hiérarchique</h5>
<div class="d-flex align-items-center justify-content-center mb-2">
<div class="form-check form-check-inline">
<input class="form-check-input" type="radio" name="validation_hierarchique" id="validation_hierarchique_valide" value="valide">
<label class="form-check-label" for="validation_hierarchique_valide">Valide</label>
</div>
<div class="form-check form-check-inline">
<input class="form-check-input" type="radio" name="validation_hierarchique" id="validation_hierarchique_refuse" value="refuse" {% if conge.validation_hierarchique == False %}checked{% endif %}>
<label class="form-check-label" for="validation_hierarchique_refuse">Refusé</label>
</div>
</div>
{% endif %}
{% if membre_de_la_direction %}
<hr>
<h5 class="text-center">Validation par le directeur</h5>
<div class="d-flex align-items-center justify-content-center mb-2">
<div class="form-check form-check-inline">
<input class="form-check-input" type="radio" name="validation_direction" id="validation_direction_valide" value="valide" {% if conge.validation_direction == True %}checked{% endif %}>
<label class="form-check-label" for="validation_direction_valide">Valide</label>
</div>
<div class="form-check form-check-inline">
<input class="form-check-input" type="radio" name="validation_direction" id="validation_direction_refuse" value="refuse" {% if conge.validation_direction == False %}checked{% endif %}>
<label class="form-check-label" for="validation_direction_refuse">Refusé</label>
</div>
</div>
{% endif %}
<hr>
<div class="d-none form-group mt-3" id="motif_refus_container">
<label for="motif_refus">Motif de refus (si applicable) :</label>
<textarea class="form-control" id="motif_refus" rows="3"></textarea>
</div>
</form>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Fermer</button>
{% if employe_est_il_chef or membre_de_la_direction or est_chef_projet %}
<button type="button" class="btn btn-success" id="bouton-enregistrer-detail-conge">Enregistrer</button>
{% endif %}
</div>
</div>
</div>
</div>

View File

@@ -0,0 +1,48 @@
{% for conge in conges %}
<div class="modal fade" id="modifierCongeModal{{ conge.id }}" tabindex="-1" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<form method="post">
{% csrf_token %}
<input type="hidden" name="conge_id" value="{{ conge.id }}">
<div class="modal-header bg-orange-dark">
<h5 class="modal-title">Modifier la demande de congé</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
</div>
<div class="modal-body">
<div class="mb-2">
<label>Type de congé</label>
<select name="type" class="form-select">
<option value="Conge Annuel" {% if conge.type == "Conge Annuel" %}selected{% endif %}>Conge Annuel</option>
<option value="Maladie" {% if conge.type == "Maladie" %}selected{% endif %}>Maladie</option>
<option value="Conge maternité" {% if conge.type == "Conge maternité" %}selected{% endif %}>Conge Maternité</option>
<option value="Conge de Mariage" {% if conge.type == "Conge de Mariage" %}selected{% endif %}>Conge Mariage</option>
<option value="conge de naissance" {% if conge.type == "conge de naissance" %}selected{% endif %}>Conge de Naissance</option>
<option value="conge de deces dun proche" {% if conge.type == "conge de deces dun proche" %}selected{% endif %}>Conge de décès d'un proche</option>
<option value="conge de mariage dun proche" {% if conge.type == "conge de mariage dun proche" %}selected{% endif %}>Conge de mariage d'un proche</option>
<option value="conge pour evenements familiaux" {% if conge.type == "conge pour evenements familiaux" %}selected{% endif %}>Conge pour événements familiaux</option>
<option value="Autre" {% if conge.type == "Autre" %}selected{% endif %}>Autre</option>
</select>
</div>
<div class="mb-2">
<label>Nombre de jours</label>
<input type="number" name="nombre_jours" class="form-control" value="{{ conge.nombre_jours }}">
</div>
<div class="mb-2">
<label>Date début</label>
<input type="date" name="date_debut" class="form-control" value="{{ conge.date_debut|date:'Y-m-d' }}">
</div>
<div class="mb-2">
<label>Date fin</label>
<input type="date" name="date_fin" class="form-control" value="{{ conge.date_fin|date:'Y-m-d' }}">
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Annuler</button>
<button type="submit" class="btn bg-orange-dark">Enregistrer</button>
</div>
</form>
</div>
</div>
</div>
{% endfor %}

View File

@@ -0,0 +1,24 @@
<div class="modal fade" id="modalRefuserGlobal" tabindex="-1">
<div class="modal-dialog">
<div class="modal-content">
<form method="POST" id="formRefuser">
{% csrf_token %}
<div class="modal-header bg-orange-dark">
<h5 class="modal-title">Refuser la demande</h5>
<button type="button" class="btn-close btn-close-white" data-bs-dismiss="modal"></button>
</div>
<div class="modal-body">
<p id="texteConge"></p>
<div class="mb-3">
<label class="form-label">Motif du refus</label>
{{ form.motif_refus }}
</div>
</div>
<div class="modal-footer">
<button type="submit" class="btn btn-danger">Refuser</button>
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Annuler</button>
</div>
</form>
</div>
</div>
</div>

3
gestion_conge/tests.py Normal file
View File

@@ -0,0 +1,3 @@
from django.test import TestCase
# Create your tests here.

27
gestion_conge/urls.py Normal file
View File

@@ -0,0 +1,27 @@
from django.urls import path
from . import views
app_name = "gestion_conges"
urlpatterns = [
path(
'',
views.index,
name='conge'
),
path(
'demande_conge/',
views.demander_conge,
name='demande-conge'
),
path(
'liste-des-conges/',
views.liste_demande_conges,
name='liste-des-conges'
),
path(
'validation-des-conges/',
views.validation_de_conge,
name='validation-des-conges'
),
]

225
gestion_conge/views.py Normal file
View File

@@ -0,0 +1,225 @@
import json
from django.http import JsonResponse
from django.shortcuts import redirect, render
from django.contrib import messages
from django.contrib.auth.decorators import login_required
from gestion_conge.forms import CongeForm
from gestion_employe.models import Affectation, Employe
from django.forms.models import model_to_dict
from django.utils import timezone
from django.db.models import Q
from fonction_utilitaire import fonctions_utilitaire
from .models import Conge
@login_required
def index(request):
"""Vue de gestion de l'index"""
employe = Employe.objects.get(user__username = request.user)
membre_direction = 'direction' in employe.user.groups.values_list('name', flat=True)
try:
affectation = Affectation.objects.get(employe = employe, date_fin_daffectation__gte = timezone.now().date())
except Affectation.DoesNotExist:
affectation = None
try:
projet = Affectation.objects.get(employe=employe, date_fin_daffectation__gte = timezone.now().date())
except Affectation.DoesNotExist:
pass
if employe.chef:
nombre_conges_valide = Conge.objects.filter(validation_hierarchique = True, employe__departement = employe.departement).count()
nombre_conges_refuse = Conge.objects.filter(validation_hierarchique = False, employe__departement = employe.departement).count()
conges_en_attente = Conge.objects.filter(validation_hierarchique = None, employe__departement = employe.departement).order_by('-date_demande')
elif membre_direction:
nombre_conges_valide = Conge.objects.filter(validation_direction = True).count()
nombre_conges_refuse = Conge.objects.filter(validation_direction = False).count()
conges_en_attente = Conge.objects.filter(validation_hierarchique = True, validation_direction = None).order_by('-date_demande')
elif affectation and affectation.role == "chef_projet":
employes_du_projet = Affectation.objects.filter(
projet = projet.projet,
date_fin_daffectation__gte = timezone.now().date()
).values('employe')
nombre_conges_valide = Conge.objects.filter(
employe__in = employes_du_projet,
validation_hierarchique = True
).count()
nombre_conges_refuse = Conge.objects.filter(
Q(employe__in = employes_du_projet) &
(Q(validation_hierarchique = False) | Q(validation_direction = False))
).count()
conges_en_attente = Conge.objects.filter(
Q(employe__in = employes_du_projet) &
(
Q(validation_hierarchique__isnull = True) | Q(validation_direction__isnull = True)
)
).exclude(
Q(validation_hierarchique = True) | Q(validation_hierarchique = False) |
Q(validation_direction = True) | Q(validation_direction = False)
).order_by('-date_demande')
else:
nombre_conges_valide = Conge.objects.filter(
employe=employe,
validation_direction = True
).count()
nombre_conges_refuse = Conge.objects.filter(Q(employe=employe) & (
Q(validation_direction = False) | Q(validation_hierarchique = False)
)).count()
conges_en_attente = Conge.objects.filter(
Q(employe = employe) &
(
Q(validation_direction__isnull = True) | Q(validation_hierarchique__isnull = True)
)
).exclude(
Q(validation_hierarchique = True) | Q(validation_hierarchique = False)
).order_by('-date_demande')
return render(request, 'gestion_conge/index.html', {
"nombre_conges_valide": nombre_conges_valide,
"nombre_conges_refuse": nombre_conges_refuse,
"nombre_conges_en_attente": conges_en_attente.count(),
"formulaire_demande_conge": CongeForm,
"employe_est_il_chef": employe.chef,
"membre_de_la_direction": membre_direction,
"est_chef_projet": affectation.role == "chef_projet" if affectation else False,
})
@login_required
def demander_conge(request):
"""Vue de gestion des demandes de congés"""
try:
employe = Employe.objects.get(user__username = request.user)
except Employe.DoesNotExist:
messages.error(request, "Votre demande de congé a échoué car votre profil Utilisateur n'est lié à aucun profil Employé. Veuillez contacter l'administrateur.")
return redirect("gestion_conges:conge")
retour_quota = fonctions_utilitaire.solde_conge(employe)
if retour_quota["success"]:
quota_annuel = retour_quota['quota_annuel']
else:
messages.error(request, retour_quota['message'])
return redirect("gestion_conges:conge")
if request.method == "POST":
form = CongeForm(request.POST, request.FILES)
if form.is_valid():
conge_obj = form.save(commit=False)
conge_obj.employe = employe
if conge_obj.type == "conge_annuel":
if retour_quota["nombre_jours_valide"] + conge_obj.nombre_jours > quota_annuel:
messages.error(request, "Quota annuel dépassé (30 jours max).")
return redirect("gestion_conges:conge")
conge_obj.save()
messages.success(request, "Votre demande de congé a été enregistrée.")
return redirect("gestion_conges:conge")
return redirect("gestion_conges:conge")
@login_required
def liste_demande_conges(request):
"""Vue de liste des demandes de congés en attente de validation selon le statut de l'utilisateur actuel"""
try:
employe = Employe.objects.get(user__username = request.user)
except Employe.DoesNotExist:
return JsonResponse({
"success": False,
"message": "Votre profil Utilisateur n'est lié à aucun profil Employé. Veuillez contacter l'administrateur."
})
try:
affectation = Affectation.objects.get(
employe=employe,
date_fin_daffectation__gte=timezone.now().date()
)
except Affectation.DoesNotExist:
affectation = None
if employe.chef:
print("chef")
conges_en_attente = Conge.objects.filter(
employe__departement = employe.departement,
validation_hierarchique = None
).order_by('-date_demande')
elif affectation and affectation.role == "chef_projet":
employes_du_projet = Affectation.objects.filter(
projet = affectation.projet,
date_fin_daffectation__gte = timezone.now().date()
).values('employe')
conges_en_attente = Conge.objects.filter(
employe__in = employes_du_projet,
validation_hierarchique = None
).order_by('-date_demande')
elif 'direction' in employe.user.groups.values_list('name', flat=True):
conges_en_attente = Conge.objects.filter(
validation_hierarchique = True,
validation_direction = None
).order_by('-date_demande')
else:
conges_en_attente = Conge.objects.filter(
employe__user__username = request.user
).order_by('-date_demande')
return JsonResponse({
"success": True,
"data":[
{
**model_to_dict(conge),
"prenom_nom": f"{conge.employe.user.first_name} {conge.employe.user.last_name}",
"date_demande": conge.date_demande,
"nombre_jours": conge.nombre_jours,
"type": dict(conge.TYPE_CHOICES).get(conge.type),
"solde_conge": fonctions_utilitaire.solde_conge(conge.employe)["quota_annuel"]
}
for conge in conges_en_attente]},
safe=False
)
@login_required
def validation_de_conge(request):
"""
Vue de validation de conges par le superieur hierarchique.
1- Si l'employe appartient à un département, le congé est validé par le chef de département.
2- Si l'employé n'appartient pas à un département, le congé est validé par le chef de projet.
"""
request_data = json.loads(request.body)
conge_id = request_data.get("id_conge", None)
try:
conge = Conge.objects.get(id=conge_id)
except conge.DoesNotExist:
return JsonResponse({"message": "Le congé selectionné n'existe pas."})
if request.method == "POST":
validation_hierarchique = request_data.get("validation_hierarchique", None)
validation_direction = request_data.get("validation_direction", None)
motif_refus = request_data.get("motif_refus", "")
if validation_hierarchique is not None:
conge.validation_hierarchique = True if validation_hierarchique == "valide" else False
if validation_hierarchique == "refuse" and not motif_refus:
return JsonResponse({"message": "Veuillez fournir un motif de refus."})
conge.motif_refus = motif_refus if validation_hierarchique == "refuse" else ""
if validation_direction is not None:
conge.validation_direction = True if validation_direction == "valide" else False
if validation_direction == "refuse" and not motif_refus:
return JsonResponse({"message": "Veuillez fournir un motif de refus."})
conge.motif_refus = motif_refus if validation_direction == "refuse" else ""
conge.save()
return JsonResponse({"message": "La décision a été enregistrée avec succès."})