clean: remove pycache from tracking
This commit is contained in:
0
gestion_conge/__init__.py
Normal file
0
gestion_conge/__init__.py
Normal file
3
gestion_conge/admin.py
Normal file
3
gestion_conge/admin.py
Normal file
@@ -0,0 +1,3 @@
|
||||
from django.contrib import admin
|
||||
|
||||
# Register your models here.
|
||||
6
gestion_conge/apps.py
Normal file
6
gestion_conge/apps.py
Normal 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
17
gestion_conge/forms.py
Normal 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',
|
||||
}
|
||||
30
gestion_conge/migrations/0001_initial.py
Normal file
30
gestion_conge/migrations/0001_initial.py
Normal 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')),
|
||||
],
|
||||
),
|
||||
]
|
||||
0
gestion_conge/migrations/__init__.py
Normal file
0
gestion_conge/migrations/__init__.py
Normal file
36
gestion_conge/models.py
Normal file
36
gestion_conge/models.py
Normal 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)
|
||||
75
gestion_conge/static/gestion_conge/js/detail_conges.js
Normal file
75
gestion_conge/static/gestion_conge/js/detail_conges.js
Normal 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";
|
||||
}
|
||||
})
|
||||
}
|
||||
71
gestion_conge/static/gestion_conge/js/index.js
Normal file
71
gestion_conge/static/gestion_conge/js/index.js
Normal 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)
|
||||
}
|
||||
})
|
||||
51
gestion_conge/templates/gestion_conge/index.html
Normal file
51
gestion_conge/templates/gestion_conge/index.html
Normal 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 %}
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
@@ -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 %}
|
||||
24
gestion_conge/templates/gestion_conge/parts/modalRefus.html
Normal file
24
gestion_conge/templates/gestion_conge/parts/modalRefus.html
Normal 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
3
gestion_conge/tests.py
Normal file
@@ -0,0 +1,3 @@
|
||||
from django.test import TestCase
|
||||
|
||||
# Create your tests here.
|
||||
27
gestion_conge/urls.py
Normal file
27
gestion_conge/urls.py
Normal 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
225
gestion_conge/views.py
Normal 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."})
|
||||
Reference in New Issue
Block a user