Veuillez lire et accepter notre politique d'utilisation avant d'accéder à votre compte.
+
Confidentialité des données
+
Vos informations personnelles et professionnelles sont protégées. Toute divulgation non autorisée est strictement interdite.
+
Utilisation autorisée
+
L’application est réservée à un usage professionnel. Toute utilisation à des fins personnelles ou non autorisées est prohibée.
+
Sécurité des comptes
+
Ne partagez jamais vos identifiants. Changez votre mot de passe régulièrement et en cas de suspicion d’intrusion.
+
Responsabilités de l’utilisateur
+
Vous êtes responsable des actions effectuées via votre compte. Signalez toute anomalie ou problème à l’équipe RH ou à l’administrateur.
+
Acceptation et conformité
+
En cliquant sur “J’accepte”, vous confirmez avoir lu et accepté cette politique. Le non-respect peut entraîner une suspension ou une révocation de l’accès.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/SIRH/static/templates/menu_principale.js b/SIRH/static/templates/menu_principale.js
new file mode 100644
index 0000000..8a835dc
--- /dev/null
+++ b/SIRH/static/templates/menu_principale.js
@@ -0,0 +1,12 @@
+
+
\ No newline at end of file
diff --git a/SIRH/wsgi.py b/SIRH/wsgi.py
new file mode 100644
index 0000000..5f96c06
--- /dev/null
+++ b/SIRH/wsgi.py
@@ -0,0 +1,16 @@
+"""
+WSGI config for SIRH project.
+
+It exposes the WSGI callable as a module-level variable named ``application``.
+
+For more information on this file, see
+https://docs.djangoproject.com/en/5.2/howto/deployment/wsgi/
+"""
+
+import os
+
+from django.core.wsgi import get_wsgi_application
+
+os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'SIRH.settings')
+
+application = get_wsgi_application()
diff --git a/gestion_conge/__init__.py b/gestion_conge/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/gestion_conge/admin.py b/gestion_conge/admin.py
new file mode 100644
index 0000000..ea5d68b
--- /dev/null
+++ b/gestion_conge/admin.py
@@ -0,0 +1,3 @@
+from django.contrib import admin
+
+# Register your models here.
diff --git a/gestion_conge/apps.py b/gestion_conge/apps.py
new file mode 100644
index 0000000..c320df3
--- /dev/null
+++ b/gestion_conge/apps.py
@@ -0,0 +1,6 @@
+from django.apps import AppConfig
+
+
+class GestionCongeConfig(AppConfig):
+ default_auto_field = 'django.db.models.BigAutoField'
+ name = 'gestion_conge'
diff --git a/gestion_conge/forms.py b/gestion_conge/forms.py
new file mode 100644
index 0000000..101c42a
--- /dev/null
+++ b/gestion_conge/forms.py
@@ -0,0 +1,19 @@
+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',
+ }
\ No newline at end of file
diff --git a/gestion_conge/migrations/0001_initial.py b/gestion_conge/migrations/0001_initial.py
index 7245098..c151424 100644
--- a/gestion_conge/migrations/0001_initial.py
+++ b/gestion_conge/migrations/0001_initial.py
@@ -1,5 +1,6 @@
<<<<<<< HEAD
<<<<<<< HEAD
+<<<<<<< HEAD
# Generated by Django 5.2.13 on 2026-06-16 11:06
=======
# Generated by Django 5.2.13 on 2026-04-17 12:03
@@ -7,6 +8,9 @@
=======
# Generated by Django 5.2.13 on 2026-04-17 12:03
>>>>>>> f6b90e7 (Verification du statut avant la creation d'un contrat)
+=======
+# Generated by Django 5.2.13 on 2026-04-17 12:03
+>>>>>>> 6026af5 (Bug : Affichages des conges)
import django.db.models.deletion
from django.db import migrations, models
diff --git a/gestion_conge/migrations/__init__.py b/gestion_conge/migrations/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/gestion_conge/static/gestion_conge/js/detail_conges.js b/gestion_conge/static/gestion_conge/js/detail_conges.js
new file mode 100644
index 0000000..fd39b94
--- /dev/null
+++ b/gestion_conge/static/gestion_conge/js/detail_conges.js
@@ -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";
+ }
+ })
+}
\ No newline at end of file
diff --git a/gestion_conge/templates/gestion_conge/parts/modalDemandeConge.html b/gestion_conge/templates/gestion_conge/parts/modalDemandeConge.html
new file mode 100644
index 0000000..511d7dc
--- /dev/null
+++ b/gestion_conge/templates/gestion_conge/parts/modalDemandeConge.html
@@ -0,0 +1,26 @@
+
+
+
+
+
+ Nouvelle demande de congé
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/gestion_conge/templates/gestion_conge/parts/modalDetailConge.html b/gestion_conge/templates/gestion_conge/parts/modalDetailConge.html
new file mode 100644
index 0000000..d3d3051
--- /dev/null
+++ b/gestion_conge/templates/gestion_conge/parts/modalDetailConge.html
@@ -0,0 +1,92 @@
+
+
+
+
+
Détails de la demande de congés
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/gestion_conge/templates/gestion_conge/parts/modalModificationConge.html b/gestion_conge/templates/gestion_conge/parts/modalModificationConge.html
new file mode 100644
index 0000000..7a920ca
--- /dev/null
+++ b/gestion_conge/templates/gestion_conge/parts/modalModificationConge.html
@@ -0,0 +1,48 @@
+{% for conge in conges %}
+
\ No newline at end of file
diff --git a/gestion_conge/tests.py b/gestion_conge/tests.py
new file mode 100644
index 0000000..de8bdc0
--- /dev/null
+++ b/gestion_conge/tests.py
@@ -0,0 +1,3 @@
+from django.test import TestCase
+
+# Create your tests here.
diff --git a/gestion_conge/urls.py b/gestion_conge/urls.py
new file mode 100644
index 0000000..c8b0511
--- /dev/null
+++ b/gestion_conge/urls.py
@@ -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'
+ ),
+]
\ No newline at end of file
diff --git a/gestion_employe/__init__.py b/gestion_employe/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/gestion_employe/admin.py b/gestion_employe/admin.py
new file mode 100644
index 0000000..098c721
--- /dev/null
+++ b/gestion_employe/admin.py
@@ -0,0 +1,16 @@
+from django.contrib import admin
+
+from .models import Departement, Employe
+
+
+@admin.register(Departement)
+class DepartementAdmin(admin.ModelAdmin):
+ list_display = ("nom", )
+
+@admin.register(Employe)
+class EmployeAdmin(admin.ModelAdmin):
+ list_display = (
+ "user",
+ "matricule",
+ "fonction",
+ )
\ No newline at end of file
diff --git a/gestion_employe/forms.py b/gestion_employe/forms.py
new file mode 100644
index 0000000..21fa4b2
--- /dev/null
+++ b/gestion_employe/forms.py
@@ -0,0 +1,86 @@
+from django import forms
+
+from .models import Affectation, Contrat, Departement, Employe, Formation
+
+
+class EmployeForm(forms.ModelForm):
+ """Formulaire pour modifier les informations de profil d'un employé"""
+ class Meta:
+ model = Employe
+ fields = (
+ 'adresse',
+ 'telephone',
+ 'CV',
+ 'diplome',
+ 'rib',
+ 'photo',
+ 'casier_judiciaire'
+ )
+
+class AffectationForm(forms.ModelForm):
+ class Meta:
+ model = Affectation
+ fields = (
+ 'projet',
+ 'date_affectation',
+ 'date_fin_daffectation',
+ 'role',
+ 'pourcentage_temps_affectation'
+ )
+
+ widgets = {
+ 'projet': forms.Select(attrs={'class': 'form-select'}),
+ 'date_affectation': forms.DateInput(attrs={'class': 'form-control', 'type': 'date'}),
+ 'date_fin_daffectation': forms.DateInput(attrs={'class': 'form-control', 'type': 'date'}),
+ 'role': forms.Select(attrs={'class': 'form-select'}),
+ 'pourcentage_temps_affectation': forms.NumberInput(attrs={'class': 'form-control'}),
+ }
+
+class ContratForm(forms.ModelForm):
+ class Meta:
+ model = Contrat
+ fields = [
+ "numero_contrat",
+ "type_contrat",
+ "date_debut",
+ "date_fin",
+ "salaire_mensuel",
+ "statut",
+ "fichier_contrat"
+ ]
+
+ widgets = {
+ 'numero_contrat': forms.TextInput(attrs={'class': 'form-control'}),
+ 'type_contrat': forms.Select(attrs={'class': 'form-select'}),
+ 'date_debut': forms.DateInput(attrs={'class': 'form-control', 'type': 'date'}),
+ 'date_fin': forms.DateInput(attrs={'class': 'form-control', 'type': 'date'}),
+ 'salaire_mensuel': forms.TextInput(attrs={'class': 'form-control'}),
+ 'statut': forms.Select(attrs={'class': 'form-select'}),
+ 'fichier_contrat': forms.FileInput(attrs={'class': 'form-control'}),
+ }
+
+class DepartementForm(forms.ModelForm):
+ class Meta:
+ model = Departement
+ fields = ['nom']
+
+class FormationForm(forms.ModelForm):
+ class Meta:
+ model = Formation
+ fields = [
+ 'titre',
+ 'organisme',
+ 'date_obtention',
+ 'date_fin',
+ 'description',
+ 'certificat'
+ ]
+
+ widgets = {
+ 'titre': forms.TextInput(attrs={"class": "form-control"}),
+ 'organisme': forms.TextInput(attrs={"class": "form-control"}),
+ 'date_obtention': forms.DateInput(attrs={"class": "form-control", "type": "date"}),
+ 'date_fin': forms.DateInput(attrs={"class": "form-control", "type": "date"}),
+ 'description': forms.Textarea(attrs={"class": "form-control"}),
+ 'certificat': forms.FileInput(attrs={"class": "form-control"}),
+ }
\ No newline at end of file
diff --git a/gestion_employe/migrations/0001_initial.py b/gestion_employe/migrations/0001_initial.py
index 2c844ab..64863d6 100644
--- a/gestion_employe/migrations/0001_initial.py
+++ b/gestion_employe/migrations/0001_initial.py
@@ -1,5 +1,6 @@
<<<<<<< HEAD
<<<<<<< HEAD
+<<<<<<< HEAD
# Generated by Django 5.2.13 on 2026-06-16 11:06
=======
# Generated by Django 5.2.13 on 2026-04-17 12:03
@@ -7,6 +8,9 @@
=======
# Generated by Django 5.2.13 on 2026-04-17 12:03
>>>>>>> f6b90e7 (Verification du statut avant la creation d'un contrat)
+=======
+# Generated by Django 5.2.13 on 2026-04-17 12:03
+>>>>>>> 6026af5 (Bug : Affichages des conges)
import django.db.models.deletion
from django.conf import settings
@@ -36,6 +40,7 @@ class Migration(migrations.Migration):
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('matricule', models.CharField(blank=True, max_length=10, null=True, unique=True)),
<<<<<<< HEAD
+<<<<<<< HEAD
<<<<<<< HEAD
('fonction', models.CharField(blank=True, choices=[('directeur', 'Directeur'), ('assistant_direction', 'Assistante de direction'), ('assistant_technique_recherche', 'Assistant technique de recherche'), ('comptable', 'Comptable'), ('raf', 'RAF'), ('data_manager', 'Data Manager'), ('logisticien', 'Logisticien'), ('post_doctorant', 'Post-Doctorant'), ('qualiticien', 'Qualiticien'), ('technicien_surface', 'Technicien de surface'), ('chauffeur', 'Chauffeur')], max_length=50, null=True)),
=======
@@ -44,6 +49,9 @@ class Migration(migrations.Migration):
=======
('fonction', models.CharField(blank=True, choices=[('directeur', 'Directeur'), ('assistant_direction', 'Assistante de direction'), ('comptable', 'Comptable'), ('raf', 'RAF'), ('data_manager', 'Data Manager'), ('logisticien', 'Logisticien'), ('post_doctorant', 'Post-Doctorant'), ('qualiticien', 'Qualiticien'), ('technicien_surface', 'Technicien de surface'), ('chauffeur', 'Chauffeur')], max_length=50, null=True)),
>>>>>>> f6b90e7 (Verification du statut avant la creation d'un contrat)
+=======
+ ('fonction', models.CharField(blank=True, choices=[('directeur', 'Directeur'), ('assistant_direction', 'Assistante de direction'), ('comptable', 'Comptable'), ('raf', 'RAF'), ('data_manager', 'Data Manager'), ('logisticien', 'Logisticien'), ('post_doctorant', 'Post-Doctorant'), ('qualiticien', 'Qualiticien'), ('technicien_surface', 'Technicien de surface'), ('chauffeur', 'Chauffeur')], max_length=50, null=True)),
+>>>>>>> 6026af5 (Bug : Affichages des conges)
('date_embauche', models.DateField(blank=True, null=True)),
('adresse', models.CharField(blank=True, max_length=100, null=True)),
('telephone', models.CharField(blank=True, max_length=15, null=True)),
@@ -65,6 +73,7 @@ class Migration(migrations.Migration):
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('numero_contrat', models.CharField(max_length=100, unique=True)),
<<<<<<< HEAD
+<<<<<<< HEAD
<<<<<<< HEAD
('type_contrat', models.CharField(choices=[('contrat_duree_determinee', 'Contrat à Durée Déterminée'), ('contrat_duree_indeterminee', 'Contrat à Durée Indéterminée'), ('contrat_prestation', 'Contrat de Prestation de Service'), ('contrat_stage', 'Contrat de Stage'), ('convention_bourse_entretien', "Convention de bourse d'entretien")], max_length=50)),
=======
@@ -73,6 +82,9 @@ class Migration(migrations.Migration):
=======
('type_contrat', models.CharField(choices=[('contrat_duree_determinee', 'Contrat à Durée Déterminée'), ('contrat_duree_indeterminee', 'Contrat à Durée Indéterminée'), ('contrat_prestation', 'Contrat de Prestation de Service'), ('contrat_stage', 'Contrat de Stage')], max_length=50)),
>>>>>>> f6b90e7 (Verification du statut avant la creation d'un contrat)
+=======
+ ('type_contrat', models.CharField(choices=[('contrat_duree_determinee', 'Contrat à Durée Déterminée'), ('contrat_duree_indeterminee', 'Contrat à Durée Indéterminée'), ('contrat_prestation', 'Contrat de Prestation de Service'), ('contrat_stage', 'Contrat de Stage')], max_length=50)),
+>>>>>>> 6026af5 (Bug : Affichages des conges)
('date_debut', models.DateField()),
('date_fin', models.DateField(blank=True, null=True)),
('salaire_mensuel', models.DecimalField(blank=True, decimal_places=2, max_digits=10, null=True)),
diff --git a/gestion_employe/migrations/0002_alter_contrat_type_contrat.py b/gestion_employe/migrations/0002_alter_contrat_type_contrat.py
new file mode 100644
index 0000000..4fd9717
--- /dev/null
+++ b/gestion_employe/migrations/0002_alter_contrat_type_contrat.py
@@ -0,0 +1,18 @@
+# Generated by Django 6.0.4 on 2026-04-29 10:42
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('gestion_employe', '0001_initial'),
+ ]
+
+ operations = [
+ migrations.AlterField(
+ model_name='contrat',
+ name='type_contrat',
+ field=models.CharField(choices=[('contrat_duree_determinee', 'Contrat à Durée Déterminée'), ('contrat_duree_indeterminee', 'Contrat à Durée Indéterminée'), ('contrat_prestation', 'Contrat de Prestation de Service'), ('contrat_stage', 'Contrat de Stage'), ('convention_bourse_entretien', "Convention de bourse d'entretien")], max_length=50),
+ ),
+ ]
diff --git a/gestion_employe/migrations/__init__.py b/gestion_employe/migrations/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/gestion_employe/models.py b/gestion_employe/models.py
index b473bac..ee75383 100644
--- a/gestion_employe/models.py
+++ b/gestion_employe/models.py
@@ -1,6 +1,7 @@
from django.contrib.auth.models import User
from django.db import models
from django.utils import timezone
+
from gestion_projet.models import Projet
diff --git a/gestion_employe/static/gestion_employe/js/index.js b/gestion_employe/static/gestion_employe/js/index.js
index 7eae701..a37e664 100644
--- a/gestion_employe/static/gestion_employe/js/index.js
+++ b/gestion_employe/static/gestion_employe/js/index.js
@@ -330,4 +330,4 @@ document.getElementById("modalContratsEmploye")
.catch(error => {
console.error("Erreur :", error);
});
-});
\ No newline at end of file
+});
diff --git a/gestion_employe/templates/gestion_employe/index.html b/gestion_employe/templates/gestion_employe/index.html
new file mode 100644
index 0000000..825dec9
--- /dev/null
+++ b/gestion_employe/templates/gestion_employe/index.html
@@ -0,0 +1,67 @@
+{% extends "BASE.html" %}
+{% load static %}
+{% block 'titre_page' %} Gestion des projets {% endblock %}
+{% block 'css' %}
+{% endblock %}
+{% block 'contenu' %}
+
+
Gestion des employés
+ {% if messages %}
+ {% for message in messages %}
+
+ {{ message }}
+
+
+ {% endfor %}
+ {% endif %}
+
+
+
+ Nombre d'employés
+
{{ nombre_employes }}
+
+
+ Nombre de prestataires
+
{{ nombre_cps }}
+
+
+ Nombre de stagiaires
+
{{ nombre_stage }}
+
+
+
+
+
+
+ La liste des employés
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+{% endblock %}
+{% block 'modal' %}
+ {% include "gestion_employe/parts/modalDetailEmploye.html" %}
+ {% include "gestion_employe/parts/modalCreationContrat.html" %}
+ {% include "gestion_employe/parts/modalAffectationProjet.html" %}
+ {% include "gestion_employe/parts/modalDocument.html" %}
+ {% include "gestion_employe/parts/modalListeContratExpirants.html" %}
+{% endblock %}
+{% block 'js' %}
+
+{% endblock %}
\ No newline at end of file
diff --git a/gestion_employe/templates/gestion_employe/modifier-profil.html b/gestion_employe/templates/gestion_employe/modifier-profil.html
new file mode 100644
index 0000000..c73010e
--- /dev/null
+++ b/gestion_employe/templates/gestion_employe/modifier-profil.html
@@ -0,0 +1,105 @@
+{% extends "BASE.html" %}
+{% load static %}
+{% block 'titre_page' %} Gestion des employés - Mon profil {% endblock %}
+{% block 'contenu' %}
+
+
+
+ Modifier mon profil
+
+
+
+
+
+
+{% endblock %}
+{% block 'modal' %}
+{% endblock %}
+{% block 'js' %}
+{% endblock %}
\ No newline at end of file
diff --git a/gestion_employe/templates/gestion_employe/parts/modalAffectationProjet.html b/gestion_employe/templates/gestion_employe/parts/modalAffectationProjet.html
new file mode 100644
index 0000000..2abcf46
--- /dev/null
+++ b/gestion_employe/templates/gestion_employe/parts/modalAffectationProjet.html
@@ -0,0 +1,28 @@
+
+
+
+
+
+ Affectation de projet ()
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/gestion_employe/templates/gestion_employe/parts/modalAjoutFormation.html b/gestion_employe/templates/gestion_employe/parts/modalAjoutFormation.html
new file mode 100644
index 0000000..104b791
--- /dev/null
+++ b/gestion_employe/templates/gestion_employe/parts/modalAjoutFormation.html
@@ -0,0 +1,21 @@
+
+
+
+
+
Ajouter un certificat
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/gestion_employe/templates/gestion_employe/parts/modalCreationContrat.html b/gestion_employe/templates/gestion_employe/parts/modalCreationContrat.html
new file mode 100644
index 0000000..fa0bb2b
--- /dev/null
+++ b/gestion_employe/templates/gestion_employe/parts/modalCreationContrat.html
@@ -0,0 +1,57 @@
+
+
+
+
+
+
+ Contrat de
+
+
+
+
+
+ {% comment %}
+ {% endcomment %}
+
+
+
+
\ No newline at end of file
diff --git a/gestion_employe/templates/gestion_employe/parts/modalDocument.html b/gestion_employe/templates/gestion_employe/parts/modalDocument.html
new file mode 100644
index 0000000..4eeb124
--- /dev/null
+++ b/gestion_employe/templates/gestion_employe/parts/modalDocument.html
@@ -0,0 +1,41 @@
+
+
+
+
+
+ Documents RH de
+
+
+
+
+
+
+
+
+ Diplôme :
+
+
+
+ CV :
+
+
+
+ RIB :
+
+
+
+ Casier judiciaire :
+
+
+
+
+
+
+
+
+
+
diff --git a/gestion_employe/templates/gestion_employe/parts/modalListeContratExpirants.html b/gestion_employe/templates/gestion_employe/parts/modalListeContratExpirants.html
new file mode 100644
index 0000000..9051355
--- /dev/null
+++ b/gestion_employe/templates/gestion_employe/parts/modalListeContratExpirants.html
@@ -0,0 +1,17 @@
+
+
+
+
+
Contrats expirants dans un maximum de 60 jours
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/gestion_employe/templates/gestion_employe/parts/modificationMotPasse.html b/gestion_employe/templates/gestion_employe/parts/modificationMotPasse.html
new file mode 100644
index 0000000..79366ba
--- /dev/null
+++ b/gestion_employe/templates/gestion_employe/parts/modificationMotPasse.html
@@ -0,0 +1,36 @@
+
+
+
+
+
Modifier le mot de passe
+
+
+
+
+
+
+
+
+
diff --git a/gestion_employe/templatetags/tags_personnaliser.py b/gestion_employe/templatetags/tags_personnaliser.py
new file mode 100644
index 0000000..4d9261b
--- /dev/null
+++ b/gestion_employe/templatetags/tags_personnaliser.py
@@ -0,0 +1,25 @@
+"""Tags personnalisés pour les templates d'authentification.
+
+Ce fichier contient des tags personnalisés pour les templates du projet lié à
+la gestion de l'accès des utilisateurs aux différentes pages.
+"""
+
+from django import template
+from django.utils import timezone
+
+from gestion_employe.models import Affectation
+
+register = template.Library()
+
+@register.filter
+def has_group(user, group_name):
+ """Vérifiez si un utilisateur appartient à un groupe spécifique."""
+ return user.groups.filter(name=group_name).exists()
+
+@register.filter
+def is_chef_projet(user):
+ try:
+ affectation = Affectation.objects.get(employe__user__username = user, date_fin_daffectation__gte = timezone.now().date())
+ except Affectation.DoesNotExist:
+ return False
+ return affectation.role == "chef_projet"
\ No newline at end of file
diff --git a/gestion_employe/tests.py b/gestion_employe/tests.py
new file mode 100644
index 0000000..de8bdc0
--- /dev/null
+++ b/gestion_employe/tests.py
@@ -0,0 +1,3 @@
+from django.test import TestCase
+
+# Create your tests here.
diff --git a/gestion_projet/__init__.py b/gestion_projet/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/gestion_projet/admin.py b/gestion_projet/admin.py
new file mode 100644
index 0000000..d0ae965
--- /dev/null
+++ b/gestion_projet/admin.py
@@ -0,0 +1,6 @@
+from django.contrib import admin
+from .models import DomaineDeRecherche
+
+@admin.register(DomaineDeRecherche)
+class DomaineDeRecherche(admin.ModelAdmin):
+ list_display = ('nom',)
diff --git a/gestion_projet/forms.py b/gestion_projet/forms.py
new file mode 100644
index 0000000..65cc4a4
--- /dev/null
+++ b/gestion_projet/forms.py
@@ -0,0 +1,178 @@
+from django import forms
+
+from gestion_projet.models import Projet
+
+from .models import (
+ ActiviteProjet,
+ Bailleur,
+ DocumentProjet,
+ FinancementProjet,
+ LivrablesLivres,
+)
+
+
+class ProjetForm(forms.ModelForm):
+ """Formulaire de création et de modification d'un projet, avec validation des dates et personnalisation des champs."""
+ class Meta:
+ model = Projet
+ fields = (
+ 'id_projet',
+ 'nom_projet',
+ 'date_debut',
+ 'date_fin',
+ 'numero_convention',
+ 'domaine_recherche',
+ 'type_projet',
+ 'budget',
+ 'budget_RH',
+ 'description'
+ )
+ # domaine_recherche = forms.ModelMultipleChoiceField(
+ # queryset=DomaineDeRecherche.objects.all(),
+ # to_field_name="nom",
+ # required=False
+ # )
+ widgets = {
+ 'id_projet': forms.TextInput(attrs={'class': "form-control"}),
+ 'nom_projet': forms.TextInput(attrs={'class': "form-control"}),
+ 'numero_convention': forms.TextInput(attrs={'class': "form-control"}),
+ 'domaine_recherche': forms.SelectMultiple(attrs={'class': "form-control"}),
+ 'type_projet': forms.Select(attrs={'class': "form-select"}),
+ 'budget': forms.NumberInput(attrs={'class': "form-control"}),
+ 'budget_RH': forms.NumberInput(attrs={'class': "form-control"}),
+ 'description': forms.Textarea(attrs={'class': "form-control"}),
+ 'date_debut': forms.DateInput(attrs={'type': 'date', 'class': "form-control"}),
+ 'date_fin': forms.DateInput(attrs={'type': 'date', 'class': "form-control"}),
+ }
+
+class BailleurForm(forms.ModelForm):
+ """
+ Formulaire de création et de modification d'un bailleur,
+ avec validation des champs et personnalisation des labels.
+ """
+ class Meta:
+ model = Bailleur
+ fields = ('nom_organisme', 'contact', 'email', 'pays')
+ widgets = {
+ 'nom_organisme':forms.TextInput(attrs={
+ 'class':'form-control',
+ }),
+ 'contact':forms.TextInput(attrs={
+ 'class':'form-control',
+ }),
+ 'email':forms.TextInput(attrs={
+ 'class':'form-control',
+ }),
+ 'pays':forms.TextInput(attrs={
+ 'class':'form-control',
+ }),
+ }
+
+class DocumentProjetForm(forms.ModelForm):
+ """
+ Formulaire pour ajouter ou modifier un document associé à un projet,
+ avec validation des champs et personnalisation des labels.
+ """
+ class Meta:
+ model = DocumentProjet
+ fields = [
+ 'nom_document',
+ 'numero',
+ 'date_validite',
+ 'fichier',
+ 'description'
+ ]
+ widgets = {
+ 'nom_document': forms.Select(attrs={'class': 'form-select'}),
+ 'description': forms.Textarea(attrs={'class': 'form-control', 'rows': 3}),
+ 'numero': forms.TextInput(attrs={'class': 'form-control'}),
+ 'date_validite': forms.DateInput(attrs={'class': 'form-control', 'type': 'date'}),
+ 'fichier': forms.ClearableFileInput(attrs={'class': 'form-control'}),
+ }
+
+class ActiviteProjetForm(forms.ModelForm):
+ """Formulaire pour créer ou modifier une activité de projet, avec validation des champs et personnalisation des widgets."""
+ class Meta:
+ model = ActiviteProjet
+ fields = (
+ 'titre',
+ 'date_debut',
+ 'date_fin',
+ 'besoin_ressource_materielle',
+ 'budget_prevu',
+ 'description',
+ )
+
+ widgets = {
+ 'titre':forms.TextInput(attrs={
+ 'class':'form-control',
+ 'placeholder':'Titre de l’activité'
+ }),
+ 'description':forms.Textarea(attrs={
+ 'class':'form-control',
+ 'rows':3,
+ 'placeholder':'Description de l’activité'
+ }),
+ 'date_debut':forms.DateInput(attrs={
+ 'class':'form-control',
+ 'type':'date'
+ }),
+ 'date_fin':forms.DateInput(attrs={
+ 'class':'form-control',
+ 'type':'date'
+ }),
+ 'besoin_ressource_materielle': forms.Textarea(attrs={
+ 'class':'form-control',
+ 'rows':3,
+ 'placeholder':'Besoin de ressources matérielles'
+ }),
+ 'budget_prevu': forms.NumberInput(attrs={
+ 'class':'form-control',
+ 'placeholder':'Budget prévu'
+ }),
+ }
+
+class FinancementProjetFrom(forms.ModelForm):
+ """Formulaire pour créer ou modifier le financement relatif à un projet."""
+ class Meta:
+ model = FinancementProjet
+ fields = (
+ 'projet',
+ 'bailleur',
+ 'pourcentage',
+ )
+
+ widgets = {
+ 'projet':forms.Select(attrs={
+ 'class':'form-select',
+ }),
+ 'bailleur':forms.Select(attrs={
+ 'class':'form-select',
+ }),
+ 'pourcentage':forms.NumberInput(attrs={
+ 'class':'form-control',
+ }),
+ }
+
+class LivrablesLivresForm(forms.ModelForm):
+ """Formulaire pour créer ou modifier un livrable livré dans le cadre d'une activité de projet."""
+ class Meta:
+ model = LivrablesLivres
+ fields = (
+ 'activite',
+ 'nom',
+ 'fichier',
+ )
+
+ widgets = {
+ 'activite': forms.Select(attrs={
+ 'class':'form-select',
+ }),
+ 'nom': forms.TextInput(attrs={
+ 'class':'form-control',
+ 'placeholder':'Nom du livrable'
+ }),
+ 'fichier': forms.ClearableFileInput(attrs={
+ 'class':'form-control',
+ }),
+ }
\ No newline at end of file
diff --git a/gestion_projet/migrations/0001_initial.py b/gestion_projet/migrations/0001_initial.py
index 6329c6e..969351f 100644
--- a/gestion_projet/migrations/0001_initial.py
+++ b/gestion_projet/migrations/0001_initial.py
@@ -1,5 +1,6 @@
<<<<<<< HEAD
<<<<<<< HEAD
+<<<<<<< HEAD
# Generated by Django 5.2.13 on 2026-06-16 11:06
=======
# Generated by Django 5.2.13 on 2026-04-17 12:03
@@ -7,6 +8,9 @@
=======
# Generated by Django 5.2.13 on 2026-04-17 12:03
>>>>>>> f6b90e7 (Verification du statut avant la creation d'un contrat)
+=======
+# Generated by Django 5.2.13 on 2026-04-17 12:03
+>>>>>>> 6026af5 (Bug : Affichages des conges)
import django.db.models.deletion
from django.db import migrations, models
diff --git a/gestion_projet/migrations/__init__.py b/gestion_projet/migrations/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/gestion_projet/models.py b/gestion_projet/models.py
new file mode 100644
index 0000000..f9a333d
--- /dev/null
+++ b/gestion_projet/models.py
@@ -0,0 +1,312 @@
+from datetime import datetime
+
+from django.db import models
+from django.utils import timezone
+
+
+class Bailleur(models.Model):
+ """Modèle représentant un bailleur de fonds pour les projets de recherche."""
+ nom_organisme = models.CharField(
+ max_length=200,
+ unique=True
+ )
+ contact = models.CharField(
+ max_length=100,
+ blank=True,
+ null=True
+ )
+ email = models.EmailField(
+ blank=True,
+ null=True
+ )
+ pays = models.CharField(
+ max_length=100,
+ blank=True,
+ null=True
+ )
+
+ def __str__(self):
+ return self.nom_organisme
+
+class DomaineDeRecherche(models.Model):
+ """Modèle représentant les domaines de recherche"""
+
+ DOMAINE_RECHERCHE = (
+ ('sciences_sociales', 'Sciences sociales'),
+ ('naturelles', 'Naturelles'),
+ ('humaines', 'Humaines'),
+ ('veterinaires', 'Vétérinaires')
+ )
+
+ nom = models.CharField(
+ max_length=100,
+ verbose_name="Domaine de recherche",
+ choices=DOMAINE_RECHERCHE,
+ primary_key=True
+ )
+
+ class Meta:
+ verbose_name = 'Domaine de recherche'
+ verbose_name_plural = 'Domaines de recherche'
+
+ def __str__(self):
+ return self.nom
+
+class Projet(models.Model):
+ """Modèle représentant un projet de recherche avec ses caractéristiques et son bailleur associé."""
+ TYPE_PROJET = (
+ ('laboratoire', 'Laboratoire'),
+ ('épidémiologie', 'Épidémiologie'),
+ ('sciences sociales', 'Sciences sociales'),
+ ('cliniques', 'Cliniques'),
+ ('autre', 'Autre'),
+ )
+
+ id_projet = models.CharField(
+ max_length=100,
+ blank=True,
+ unique=True,
+ primary_key=True,
+ verbose_name="ID du projet"
+ )
+ nom_projet = models.CharField(
+ max_length=200,
+ verbose_name="Nom du projet"
+ )
+ date_debut = models.DateField(
+ verbose_name="Date de début"
+ )
+ date_fin = models.DateField(
+ verbose_name="Date de fin"
+ )
+ numero_convention = models.CharField(
+ max_length=100,
+ verbose_name="Numéro de convention"
+ )
+ description = models.TextField(
+ verbose_name="Description"
+ )
+ type_projet = models.CharField(
+ max_length=100,
+ choices=TYPE_PROJET,
+ default='épidémiologie',
+ verbose_name="Type de projet"
+ )
+ domaine_recherche = models.ManyToManyField(DomaineDeRecherche)
+ budget=models.DecimalField(
+ max_digits=12,
+ decimal_places=2,
+ verbose_name="Budget"
+ )
+ budget_RH = models.DecimalField(
+ max_digits=12,
+ decimal_places=2,
+ verbose_name="Budget RH"
+ )
+ created_at = models.DateTimeField(auto_now_add=True)
+ bailleur = models.ForeignKey(
+ Bailleur,
+ on_delete=models.SET_NULL,
+ null=True,
+ blank=True,
+ verbose_name="Bailleur de fonds"
+ )
+
+ @property
+ def statut(self):
+ if self.date_fin < datetime.date.today():
+ return "Terminé"
+ return "En cours"
+
+ @property
+ def avancement(self):
+ aujourd_hui = datetime.date.today()
+ if (self.date_debut and self.date_fin) and (self.date_debut < self.date_fin):
+ duree_projet = (self.date_fin - self.date_debut).days
+ temps_ecoule = (aujourd_hui - self.date_debut).days
+ if duree_projet > 0:
+ return round((temps_ecoule / duree_projet) * 100, 2)
+
+ return 0
+
+ def __str__(self):
+ return f"{self.nom_projet}"
+
+class FinancementProjet(models.Model):
+ """
+ Modèle représentant le financement d'un projet par un bailleur,
+ avec le pourcentage de contribution.
+ """
+ projet = models.ForeignKey(
+ Projet,
+ on_delete=models.CASCADE
+ )
+ bailleur = models.ForeignKey(
+ Bailleur,
+ on_delete=models.CASCADE
+ )
+ pourcentage = models.DecimalField(
+ max_digits = 5,
+ decimal_places=2
+ )
+
+ class Meta:
+ unique_together = ('projet', 'bailleur')
+
+ def __str__(self):
+ return f"{self.bailleur.nom} - {self.projet.nom_projet} ({self.pourcentage}%)"
+
+class DocumentProjet(models.Model):
+ """Modèle représentant un document associé à un projet, avec des métadonnées et un fichier attaché."""
+ NOM_DOCUMENT_CHOICES = (
+ ('protocole', 'Protocole d’étude'),
+ ('ethique', "Approbation du comité d'éthique"),
+ ('autorisation', 'Autorisation (DNLP)'),
+ ('rapport_technique', 'Rapport technique'),
+ ('rapport_financier', 'Rapport financier'),
+ ('rapport_avancement', "Rapport d'avancement"),
+ ('convention', 'Convention'),
+ ('rapport_final', 'Rapport final'),
+ ('autre', 'Autre'),
+ )
+
+ projet = models.ForeignKey(
+ Projet,
+ on_delete=models.CASCADE,
+ related_name='documents',
+ verbose_name="Projet"
+ )
+ date_ajout = models.DateTimeField(
+ auto_now_add=True,
+ verbose_name="Date d'ajout"
+ )
+ nom_document = models.CharField(
+ max_length = 100,
+ choices = NOM_DOCUMENT_CHOICES,
+ verbose_name="Type de document"
+ )
+ description = models.TextField(
+ blank = True,
+ verbose_name = "Description"
+ )
+ numero = models.CharField(
+ max_length = 100,
+ blank = True,
+ null = True,
+ verbose_name = "Numéro du document"
+ )
+ date_validite = models.DateField(
+ blank = True,
+ null = True,
+ verbose_name = "Date de validité"
+ )
+ fichier = models.FileField(
+ upload_to = 'documents_projets/',
+ verbose_name = "Fichier à télécharger"
+ )
+
+ def __str__(self):
+ return f"{self.nom_document} ({self.projet})"
+
+class ActiviteProjet(models.Model):
+ """Modèle représentant le planning d'un projet, avec des activités associées et un statut."""
+ projet = models.ForeignKey(
+ Projet,
+ on_delete = models.CASCADE,
+ verbose_name = "Projet"
+ )
+ titre = models.CharField(
+ max_length = 200,
+ verbose_name = "Titre de l'activité"
+ )
+ description = models.TextField(
+ blank = True,
+ null = True,
+ verbose_name = "Description de l'activité"
+ )
+ date_debut = models.DateField(verbose_name="Date de début")
+ date_fin = models.DateField(verbose_name="Date de fin")
+ annuler = models.BooleanField(
+ default = False,
+ verbose_name = "Annuler l'activité"
+ )
+ motif_annulation = models.TextField(
+ blank = True,
+ null = True,
+ verbose_name = "Motif d'annulation"
+ )
+
+ motif_changement_budget = models.TextField(
+ blank = True,
+ null = True,
+ verbose_name = "Motif de changement de budget"
+ )
+ budget_prevu = models.DecimalField(
+ max_digits = 15,
+ decimal_places = 2,
+ default = 0,
+ verbose_name = "Budget prévu"
+ )
+ budget_depense = models.DecimalField(
+ max_digits = 15,
+ decimal_places = 2,
+ default = 0,
+ verbose_name = "Budget dépensé"
+ )
+ besoin_ressource_materielle = models.TextField(
+ verbose_name="Besoin de ressources matérielles"
+ )
+
+ @property
+ def statut(self):
+ today = timezone.now().date()
+ if not self.annuler:
+ if self.date_fin < today:
+ return 'Terminé'
+ elif self.date_debut > today:
+ return 'À venir'
+ else:
+ return 'En cours'
+ else:
+ return 'Annulé'
+
+ def __str__(self):
+ return f"{self.titre} ({self.projet.nom_projet})"
+
+# class LivrableAttendu(models.Model):
+# """
+# Modèle représentant un livrable attendu pour une activité de projet,
+# avec des critères de validation.
+# """
+# activite = models.ForeignKey(
+# ActiviteProjet,
+# on_delete = models.CASCADE,
+# related_name = "livrables_attendus"
+# )
+# nom = models.CharField(max_length=255)
+
+# def __str__(self):
+# return f"{self.nom} (Activité: {self.activite.titre})"
+
+class LivrablesLivres(models.Model):
+ """Modèle représentant un livrable livré pour une activité de projet."""
+ activite = models.ForeignKey(
+ ActiviteProjet,
+ on_delete = models.CASCADE
+ )
+ # nom = models.ForeignKey(
+ # LivrableAttendu,
+ # on_delete = models.CASCADE
+ # )
+ nom = models.CharField(
+ max_length=255,
+ verbose_name="Nom du livrable"
+ )
+
+ fichier = models.FileField(
+ upload_to = 'fichier_livrables/',
+ blank = True,
+ null = True
+ )
+ def __str__(self):
+ return self.nom
\ No newline at end of file
diff --git a/gestion_projet/static/gestion_projet/js/creation_projet.js b/gestion_projet/static/gestion_projet/js/creation_projet.js
new file mode 100644
index 0000000..36c1a10
--- /dev/null
+++ b/gestion_projet/static/gestion_projet/js/creation_projet.js
@@ -0,0 +1,19 @@
+const boutonEnregistrerProjet = $("btnEnregistrerProjet");
+
+boutonEnregistrerProjet.addEventListener("click", function() {
+ const formulaire = $("formCreationProjet");
+ const formData = new FormData(formulaire);
+ fetch(formulaire.action, {
+ method: "POST",
+ body: formData,
+ headers: {
+ "X-CSRFToken": formData.get("csrfmiddlewaretoken")
+ }
+ })
+ .then(response => {
+ if (response.ok) {
+ window.location.reload();
+ alert("Projet enregistré avec succès !");
+ }
+ });
+});
\ No newline at end of file
diff --git a/gestion_projet/static/gestion_projet/js/enregistrement_financement.js b/gestion_projet/static/gestion_projet/js/enregistrement_financement.js
new file mode 100644
index 0000000..1e9646c
--- /dev/null
+++ b/gestion_projet/static/gestion_projet/js/enregistrement_financement.js
@@ -0,0 +1,22 @@
+const btn_enregistrer_financement = document.getElementById('btn_enregistrer_financement');
+
+btn_enregistrer_financement.addEventListener('click', function() {
+ const form = document.getElementById('form_financement');
+ const formData = new FormData(form);
+ fetch(form.action, {
+ method: 'POST',
+ body: formData,
+ headers: {
+ 'X-CSRFToken': formData.get('csrfmiddlewaretoken')
+ }
+ })
+ .then(response => response.json())
+ .then(data => {
+ if (data.success) {
+ alert(data.message);
+ window.location.reload();
+ }else {
+ alert(data.message);
+ }
+ });
+});
\ No newline at end of file
diff --git a/gestion_projet/static/gestion_projet/js/index.js b/gestion_projet/static/gestion_projet/js/index.js
new file mode 100644
index 0000000..eb6285e
--- /dev/null
+++ b/gestion_projet/static/gestion_projet/js/index.js
@@ -0,0 +1,64 @@
+const $ = (element) => document.getElementById(element)
+const url_liste_projet = $("tableau-liste-projet").dataset.url;
+const tableau_liste_projet = new Tabulator("#tableau-liste-projet", {
+ fitColumns: true,
+ responsiveLayout : true,
+ columns: [
+ {title: "Projet", field: "nom_projet"},
+ {title: "Source de financement", field: "source_financement"},
+ {title: "Budget Total", field: "budget"},
+ {title: "Budget RH", field: "budget_RH"},
+ {title: "Avancement", field: "avancement", formatter: "progress"},
+ {title: "Statut", field: "statut"},
+ ],
+ ajaxURL: url_liste_projet
+})
+
+const employes_affectes_projet = new Tabulator("#employes_affectes_projet", {
+ columns: [
+ {title: "Employé", field: "employe"},
+ {title: "Pourcentage d'affectation", field: "pourcentage_affectation"},
+ ],
+ placeholder: "Aucun employé affecté pour ce projet",
+})
+
+const bailleurs_projet = new Tabulator("#bailleurs_projet", {
+ columns: [
+ {title: "Bailleur", field: "bailleur"},
+ {title: "Pourcentage de financement", field: "pourcentage_financement"},
+ ],
+ placeholder: "Aucun bailleur attribué pour ce projet",
+})
+
+tableau_liste_projet.on("rowClick", function (row, rowData) {
+ const data = rowData.getData();
+ const modal = new bootstrap.Modal($("modalDetailProjet"));
+
+ $("detail_id_projet").value = data.id_projet;
+ $("detail_nom_projet").value = data.nom_projet;
+ $("detail_date_debut").value = data.date_debut;
+ $("detail_date_fin").value = data.date_fin;
+ $("detail_numero_convention").value = data.numero_convention;
+ $("detail_type_projet").value = data.type_projet;
+ Array.from($("detail_domaine_recherche").options).forEach(option => {
+ if (data.domaine_recherche.includes(option.value)) {
+ option.selected = true;
+ } else {
+ option.selected = false;
+ }
+ });
+ $("detail_budget").value = data.budget;
+ $("detail_budget_rh").value = data.budget_RH;
+ $("detail_description").value = data.description;
+ $("detail_statut").value = data.statut;
+
+ employes_affectes_projet.setData(`projet/liste-employes-par-projet/${$("detail_id_projet").value}`);
+ bailleurs_projet.setData(`projet/bailleurs/${data.id_projet}/`);
+
+ modal.show();
+})
+
+// $('detail-projet-form').addEventListener('submit', (e) => {
+// e.preventDefault();
+// new FormData($("detail-projet-form"));
+// })
\ No newline at end of file
diff --git a/gestion_projet/static/gestion_projet/js/liste_documents_projet.js b/gestion_projet/static/gestion_projet/js/liste_documents_projet.js
new file mode 100644
index 0000000..e586ae1
--- /dev/null
+++ b/gestion_projet/static/gestion_projet/js/liste_documents_projet.js
@@ -0,0 +1,20 @@
+const urlListeDocument = document.getElementById('listeDocuments').dataset.urllistedocument;
+
+const table_liste_documents = new Tabulator(document.getElementById('listeDocuments'), {
+ layout: "fitColumns",
+ placeholder: "Aucun document trouvé",
+ columns: [
+ { title: "Nom du Document", field: "nom_document" },
+ { title: "Numéro", field: "numero" },
+ { title: "Date de Validité", field: "date_validite", formatter: "datetime", formatterParams: {
+ inputFormat: "yyyy-MM-dd",
+ outputFormat: "dd/MM/yyyy"
+ }
+ },
+ { title: "Lien vers le Document", field: "lien_document", formatter:"link", formatterParams:{
+ target:"_blank",
+ }
+ },
+ ],
+ ajaxURL: urlListeDocument,
+});
\ No newline at end of file
diff --git a/gestion_projet/static/gestion_projet/js/suivi-activites.js b/gestion_projet/static/gestion_projet/js/suivi-activites.js
new file mode 100644
index 0000000..d0775d7
--- /dev/null
+++ b/gestion_projet/static/gestion_projet/js/suivi-activites.js
@@ -0,0 +1,59 @@
+const $ = (element) => document.getElementById(element)
+
+const url_liste_activite = $("tableau-liste-activite").dataset.urllisteactivite
+const tableau_liste_activite = new Tabulator("#tableau-liste-activite", {
+ columns: [
+ {title: "Activité", field: "titre"},
+ {title: "Date début", field: "date_debut"},
+ {title: "Date fin", field: "date_fin"},
+ {title: "Budget prévu", field: "budget_prevu"},
+ {title: "Budget dépensé", field: "budget_depense"},
+ {title: "Motif de changement de budget", field: "motif_changement_budget"},
+ {title: "Statut", field: "statut"},
+ ],
+ ajaxURL: url_liste_activite,
+})
+tableau_liste_activite.on("rowClick", function (row, rowData) {
+ const data = rowData.getData();
+ $("idDetailActivite").value = data.id;
+ $("titreDetailActivite").value = data.titre;
+ $("descriptionDetailActivite").value = data.description;
+ $("date_debutDetailActivite").value = data.date_debut;
+ $("date_finDetailActivite").value = data.date_fin;
+ $("statutDetailActivite").value = data.statut;
+ $("budget_prevuDetailActivite").value = data.budget_prevu;
+ $("besoin_ressources_materiellesDetailActivite").value = data.besoin_ressource_materielle;
+ const modal = new bootstrap.Modal($("modalDetailActivite"));
+ modal.show();
+
+ fetch(`liste-des-livrables/${data.id}/`)
+ .then(response => response.json())
+ .then(livrables => {
+ tableau_liste_livrable.setData(livrables);
+ })
+})
+
+const tableau_liste_livrable = new Tabulator("#listeLivrables", {
+ columns: [
+ {title: "Livrable", field: "titre"},
+ {title: "Lien du livrable", field: "lien", formatter: "link", formatterParams: {blank: true}},
+ ],
+ placeholder: "Aucun livrable trouvé",
+})
+
+$("btnMiseAJourDepense").addEventListener("click", function() {
+ const modal = new bootstrap.Modal($("modalDepenseActivite"));
+ bootstrap.Modal.getOrCreateInstance($("modalDetailActivite")).hide();
+ const idActivite = $("idDetailActivite").value;
+ const budgetPrevu = $("budget_prevuDetailActivite").value;
+
+ $("id_activite_depense").value = idActivite;
+ $("budget_prevu").value = budgetPrevu;
+ modal.show();
+})
+
+$("btnAnnulerActivite").addEventListener("click", function(event) {
+ new bootstrap.Modal($("modalAnnulerActivite")).show();
+ $("id_activite_annulation").value = $("idDetailActivite").value;
+ bootstrap.Modal.getOrCreateInstance($("modalDetailActivite")).hide();
+})
\ No newline at end of file
diff --git a/gestion_projet/templates/gestion_projet/ajout-financement.html b/gestion_projet/templates/gestion_projet/ajout-financement.html
new file mode 100644
index 0000000..642fc29
--- /dev/null
+++ b/gestion_projet/templates/gestion_projet/ajout-financement.html
@@ -0,0 +1,37 @@
+{% extends "BASE.html" %}
+{% load static %}
+{% block 'titre_page' %} Gestion des projets {% endblock %}
+{% block 'contenu' %}
+
+
+ {% if messages %}
+ {% for message in messages %}
+
+ {{ message }}
+
+
+ {% endfor %}
+ {% endif %}
+
+
+
Ajout du financement au projet (Nom du projet ici)
+
+
+
+
+{% endblock %}
+{% block 'modal' %}
+ {% include "gestion_projet/parts/modalAjoutProjet.html" %}
+{% endblock %}
+{% block 'js' %}
+
+{% endblock %}
\ No newline at end of file
diff --git a/gestion_projet/templates/gestion_projet/creation_projet.html b/gestion_projet/templates/gestion_projet/creation_projet.html
new file mode 100644
index 0000000..c04c5d4
--- /dev/null
+++ b/gestion_projet/templates/gestion_projet/creation_projet.html
@@ -0,0 +1,32 @@
+{% extends "BASE.html" %}
+{% load static %}
+{% block 'titre_page' %} Gestion des projets {% endblock %}
+{% block 'contenu' %}
+ {% if messages %}
+ {% for message in messages %}
+
+ {{ message }}
+
+
+ {% endfor %}
+ {% endif %}
+ {% comment %}
Enregistrement d'un nouveau projet
{% endcomment %}
+
+
+
Enregistrement d'un nouveau projet
+
+
+
+
+{% endblock %}
+{% block 'modal' %}
+{% endblock %}
+{% block 'js' %}
+{% endblock %}
\ No newline at end of file
diff --git a/gestion_projet/templates/gestion_projet/index.html b/gestion_projet/templates/gestion_projet/index.html
new file mode 100644
index 0000000..70ba584
--- /dev/null
+++ b/gestion_projet/templates/gestion_projet/index.html
@@ -0,0 +1,61 @@
+{% extends "BASE.html" %}
+{% load static %}
+{% load tags_personnaliser %}
+{% block 'titre_page' %} Gestion des projets {% endblock %}
+{% block 'contenu' %}
+
Gestion des projets
+{% if messages %}
+ {% for message in messages %}
+
+ {{ message }}
+
+
+ {% endfor %}
+{% endif %}
+
+
+ Projets en cours
+
{{projet_en_cours}}
+
+
+ Budget Total (GNF)
+
{{budget_total}}
+
+
+ Personnel sous projet
+
{{nombre_personnel}}
+
+
+
+
+
La liste des projets
+ {% if user|has_group:"ressource_humaine" %}
+
+
+
+
+ {% endif %}
+
+
+
+
+
+
+
+{% endblock %}
+{% block 'modal' %}
+ {% include "gestion_projet/parts/modalAjoutProjet.html" %}
+ {% include "gestion_projet/parts/modalFinancement.html" %}
+ {% include "gestion_projet/parts/creation_bailleur.html" %}
+ {% include "gestion_projet/parts/modalDetailProjet.html" %}
+{% endblock %}
+{% block 'js' %}
+
+
+
+
+{% endblock %}
\ No newline at end of file
diff --git a/gestion_projet/templates/gestion_projet/parts/creation_bailleur.html b/gestion_projet/templates/gestion_projet/parts/creation_bailleur.html
new file mode 100644
index 0000000..6fe4194
--- /dev/null
+++ b/gestion_projet/templates/gestion_projet/parts/creation_bailleur.html
@@ -0,0 +1,20 @@
+
+
+
+
+
Ajouter un Bailleur
+
+
+
+
+
+
+
+
+
diff --git a/gestion_projet/templates/gestion_projet/parts/liste_document_projet.html b/gestion_projet/templates/gestion_projet/parts/liste_document_projet.html
new file mode 100644
index 0000000..eb63ad2
--- /dev/null
+++ b/gestion_projet/templates/gestion_projet/parts/liste_document_projet.html
@@ -0,0 +1,16 @@
+
+
+
+
+
Liste des Documents
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/gestion_projet/templates/gestion_projet/parts/modalAjoutActivite.html b/gestion_projet/templates/gestion_projet/parts/modalAjoutActivite.html
new file mode 100644
index 0000000..d253eb3
--- /dev/null
+++ b/gestion_projet/templates/gestion_projet/parts/modalAjoutActivite.html
@@ -0,0 +1,20 @@
+
+
+
+
+
Ajouter une Activité
+
+
+
+
+
+
\ No newline at end of file
diff --git a/gestion_projet/templates/gestion_projet/parts/modalAjoutDocument.html b/gestion_projet/templates/gestion_projet/parts/modalAjoutDocument.html
new file mode 100644
index 0000000..6787fad
--- /dev/null
+++ b/gestion_projet/templates/gestion_projet/parts/modalAjoutDocument.html
@@ -0,0 +1,20 @@
+
+
+
+
+
Ajouter un Document
+
+
+
+
+
+
\ No newline at end of file
diff --git a/gestion_projet/templates/gestion_projet/parts/modalAjoutLivrable.html b/gestion_projet/templates/gestion_projet/parts/modalAjoutLivrable.html
new file mode 100644
index 0000000..6b2bb68
--- /dev/null
+++ b/gestion_projet/templates/gestion_projet/parts/modalAjoutLivrable.html
@@ -0,0 +1,20 @@
+
+
+
+
+
Ajouter un livrable - (Nom du livrable)
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/gestion_projet/templates/gestion_projet/parts/modalAjoutProjet.html b/gestion_projet/templates/gestion_projet/parts/modalAjoutProjet.html
new file mode 100644
index 0000000..1be46ce
--- /dev/null
+++ b/gestion_projet/templates/gestion_projet/parts/modalAjoutProjet.html
@@ -0,0 +1,21 @@
+
+
+
+
+
+
Ajouter un projet
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/gestion_projet/templates/gestion_projet/parts/modalAnnulerActivite.html b/gestion_projet/templates/gestion_projet/parts/modalAnnulerActivite.html
new file mode 100644
index 0000000..7793aaf
--- /dev/null
+++ b/gestion_projet/templates/gestion_projet/parts/modalAnnulerActivite.html
@@ -0,0 +1,26 @@
+
+
+
+
+
Annuler l'activité
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/gestion_projet/templates/gestion_projet/parts/modalDetailActivite.html b/gestion_projet/templates/gestion_projet/parts/modalDetailActivite.html
new file mode 100644
index 0000000..fba8cd9
--- /dev/null
+++ b/gestion_projet/templates/gestion_projet/parts/modalDetailActivite.html
@@ -0,0 +1,58 @@
+
+
+
+
+
Détails de l'activité
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Liste des livrables :
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/gestion_projet/templates/gestion_projet/parts/modalDetailProjet.html b/gestion_projet/templates/gestion_projet/parts/modalDetailProjet.html
new file mode 100644
index 0000000..a7f6087
--- /dev/null
+++ b/gestion_projet/templates/gestion_projet/parts/modalDetailProjet.html
@@ -0,0 +1,115 @@
+{% load tags_personnaliser %}
+
+
+
+
+
Détails du projet
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/gestion_projet/templates/gestion_projet/parts/modalFinancement.html b/gestion_projet/templates/gestion_projet/parts/modalFinancement.html
new file mode 100644
index 0000000..a075b41
--- /dev/null
+++ b/gestion_projet/templates/gestion_projet/parts/modalFinancement.html
@@ -0,0 +1,20 @@
+
+
+
+
+
Ajout de financement
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/gestion_projet/templates/gestion_projet/parts/modalMiseAJourDepense.html b/gestion_projet/templates/gestion_projet/parts/modalMiseAJourDepense.html
new file mode 100644
index 0000000..d796bb4
--- /dev/null
+++ b/gestion_projet/templates/gestion_projet/parts/modalMiseAJourDepense.html
@@ -0,0 +1,36 @@
+
+
+
+
+
+ Mise à jour des dépenses
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/gestion_projet/templates/gestion_projet/suivi_activite.html b/gestion_projet/templates/gestion_projet/suivi_activite.html
new file mode 100644
index 0000000..24d44a6
--- /dev/null
+++ b/gestion_projet/templates/gestion_projet/suivi_activite.html
@@ -0,0 +1,64 @@
+{% extends "BASE.html" %}
+{% load static %}
+{% block 'titre_page' %} Gestion des projets {% endblock %}
+{% block 'contenu' %}
+ {% if messages %}
+ {% for message in messages %}
+
+ {{ message }}
+
+
+ {% endfor %}
+ {% endif %}
+
+ Suivi des Activités ({{ nom_projet }} )
+
+
+
+ Budget Total (GNF)
+
{{ budget_total }}
+
+
+ Budget RH (GNF)
+
{{ budget_RH }}
+
+
+ Budget Dépensé (GNF)
+
{{budget_depense}}
+
+
+
+
+
La liste des Activités
+
+
+
+
+
+
+
+
+
+
+
+{% endblock %}
+{% block 'modal' %}
+ {% include "gestion_projet/parts/modalAjoutActivite.html" %}
+ {% include "gestion_projet/parts/modalDetailActivite.html" %}
+ {% include "gestion_projet/parts/modalAjoutDocument.html" %}
+ {% include "gestion_projet/parts/modalMiseAJourDepense.html" %}
+ {% include "gestion_projet/parts/modalAjoutLivrable.html" %}
+ {% include "gestion_projet/parts/liste_document_projet.html" %}
+ {% include "gestion_projet/parts/modalAnnulerActivite.html" %}
+{% endblock %}
+{% block 'js' %}
+
+
+{% endblock %}
+
\ No newline at end of file
diff --git a/gestion_projet/tests.py b/gestion_projet/tests.py
new file mode 100644
index 0000000..de8bdc0
--- /dev/null
+++ b/gestion_projet/tests.py
@@ -0,0 +1,3 @@
+from django.test import TestCase
+
+# Create your tests here.
diff --git a/gestion_projet/urls.py b/gestion_projet/urls.py
index 810a3ff..ff20f0d 100644
--- a/gestion_projet/urls.py
+++ b/gestion_projet/urls.py
@@ -1,5 +1,4 @@
from django.urls import path
-
from . import views
app_name = 'gestion_projet'
diff --git a/gestion_salle/__init__.py b/gestion_salle/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/gestion_salle/admin.py b/gestion_salle/admin.py
new file mode 100644
index 0000000..ea5d68b
--- /dev/null
+++ b/gestion_salle/admin.py
@@ -0,0 +1,3 @@
+from django.contrib import admin
+
+# Register your models here.
diff --git a/gestion_salle/apps.py b/gestion_salle/apps.py
new file mode 100644
index 0000000..569d365
--- /dev/null
+++ b/gestion_salle/apps.py
@@ -0,0 +1,5 @@
+from django.apps import AppConfig
+
+
+class GestionSalleConfig(AppConfig):
+ name = 'gestion_salle'
diff --git a/gestion_salle/forms.py b/gestion_salle/forms.py
new file mode 100644
index 0000000..5a40f60
--- /dev/null
+++ b/gestion_salle/forms.py
@@ -0,0 +1,34 @@
+from django import forms
+
+from .models import Reservation
+
+
+class ReservationForm(forms.ModelForm):
+ class Meta:
+ model = Reservation
+ fields = ['salle', 'date_debut', 'date_fin', 'heure_debut', 'heure_fin', 'motif_reservation', 'besoin_zoom', 'besoin_ordi']
+ widgets = {
+ 'date_debut': forms.DateInput(attrs={'type': 'date', 'class': 'form-control'}),
+ 'date_fin': forms.DateInput(attrs={'type': 'date', 'class': 'form-control'}),
+ 'heure_debut': forms.TimeInput(attrs={'type': 'time', 'class': 'form-control'}),
+ 'heure_fin': forms.TimeInput(attrs={'type': 'time', 'class': 'form-control'}),
+ 'motif_reservation': forms.Textarea(attrs={'rows': 3, 'cols': 40, 'style':'resize:none;', 'class': 'form-control'}),
+ 'salle': forms.Select(attrs={'class': 'form-select'}),
+ }
+ besoin_zoom = forms.BooleanField(
+ required=False,
+ label="Besoin d'un lien Zoom ?",
+ widget=forms.CheckboxInput(attrs={'class': 'form-check-input', 'id': 'id_besoin_zoom'})
+ )
+ besoin_ordi = forms.BooleanField(
+ required=False,
+ label="Besoin d'ordinateur ?",
+ widget=forms.CheckboxInput(attrs={'class': 'form-check-input', 'id': 'id_besoin_ordi'})
+ )
+
+class RefusReservationForm(forms.Form):
+ motif_refus = forms.CharField(
+ label= "Motif du refus",
+ widget=forms.Textarea(attrs={'rows': 3, 'cols': 40, 'style': 'resize:none;'}),
+ required=True
+ )
diff --git a/gestion_salle/migrations/0001_initial.py b/gestion_salle/migrations/0001_initial.py
index 402b9ad..1688f51 100644
--- a/gestion_salle/migrations/0001_initial.py
+++ b/gestion_salle/migrations/0001_initial.py
@@ -1,5 +1,6 @@
<<<<<<< HEAD
<<<<<<< HEAD
+<<<<<<< HEAD
# Generated by Django 5.2.13 on 2026-06-16 11:06
=======
# Generated by Django 5.2.13 on 2026-04-17 12:03
@@ -7,6 +8,9 @@
=======
# Generated by Django 5.2.13 on 2026-04-17 12:03
>>>>>>> f6b90e7 (Verification du statut avant la creation d'un contrat)
+=======
+# Generated by Django 5.2.13 on 2026-04-17 12:03
+>>>>>>> 6026af5 (Bug : Affichages des conges)
import django.db.models.deletion
from django.db import migrations, models
diff --git a/gestion_salle/migrations/__init__.py b/gestion_salle/migrations/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/gestion_salle/templates/gestion_salle/parts/ModalRefusReservation.html b/gestion_salle/templates/gestion_salle/parts/ModalRefusReservation.html
new file mode 100644
index 0000000..96d358d
--- /dev/null
+++ b/gestion_salle/templates/gestion_salle/parts/ModalRefusReservation.html
@@ -0,0 +1,22 @@
+
+
+
+
+
Motif du refus
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/gestion_salle/templates/gestion_salle/parts/ModaleAjoutLienZoom.html b/gestion_salle/templates/gestion_salle/parts/ModaleAjoutLienZoom.html
new file mode 100644
index 0000000..0c7c534
--- /dev/null
+++ b/gestion_salle/templates/gestion_salle/parts/ModaleAjoutLienZoom.html
@@ -0,0 +1,26 @@
+
+
+
+
+
+
Ajouter/Modifier le lien Zoom
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/gestion_salle/templates/gestion_salle/parts/modalCreationReservation.html b/gestion_salle/templates/gestion_salle/parts/modalCreationReservation.html
new file mode 100644
index 0000000..0db15f8
--- /dev/null
+++ b/gestion_salle/templates/gestion_salle/parts/modalCreationReservation.html
@@ -0,0 +1,23 @@
+
+
+
+
+
+
+ Nouvelle reservation
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/gestion_salle/templates/gestion_salle/parts/modalDetailResevation.html b/gestion_salle/templates/gestion_salle/parts/modalDetailResevation.html
index 379dcd9..793999e 100644
--- a/gestion_salle/templates/gestion_salle/parts/modalDetailResevation.html
+++ b/gestion_salle/templates/gestion_salle/parts/modalDetailResevation.html
@@ -1,5 +1,8 @@
+<<<<<<< HEAD
{% load tags_personnaliser %}
+=======
+>>>>>>> 6026af5 (Bug : Affichages des conges)
diff --git a/gestion_salle/templates/gestion_salle/parts/modalListeValidation.html b/gestion_salle/templates/gestion_salle/parts/modalListeValidation.html
new file mode 100644
index 0000000..0f50e01
--- /dev/null
+++ b/gestion_salle/templates/gestion_salle/parts/modalListeValidation.html
@@ -0,0 +1,16 @@
+
+
+
+
+
+
+ Liste des reservation en attente de validation
+
, because it just gets in the way.
+ from_box.parentNode.removeChild(p);
+ } else if (p.classList.contains("help")) {
+ // Move help text up to the top so it isn't below the select
+ // boxes or wrapped off on the side to the right of the add
+ // button:
+ from_box.parentNode.insertBefore(p, from_box.parentNode.firstChild);
+ }
+ }
+
+ //
or
+ const selector_div = quickElement('div', from_box.parentNode);
+ // Make sure the selector div is at the beginning so that the
+ // add link would be displayed to the right of the widget.
+ from_box.parentNode.prepend(selector_div);
+ selector_div.className = is_stacked ? 'selector stacked' : 'selector';
+
+ //
");
+ addButton = $this.filter(":last").next().find("a");
+ }
+ }
+ addButton.on('click', addInlineClickHandler);
+ };
+
+ const addInlineClickHandler = function(e) {
+ e.preventDefault();
+ const template = $("#" + options.prefix + "-empty");
+ const row = template.clone(true);
+ row.removeClass(options.emptyCssClass)
+ .addClass(options.formCssClass)
+ .attr("id", options.prefix + "-" + nextIndex);
+ addInlineDeleteButton(row);
+ row.find("*").each(function() {
+ updateElementIndex(this, options.prefix, totalForms.val());
+ });
+ // Insert the new form when it has been fully edited.
+ row.insertBefore($(template));
+ // Update number of total forms.
+ $(totalForms).val(parseInt(totalForms.val(), 10) + 1);
+ nextIndex += 1;
+ // Hide the add button if there's a limit and it's been reached.
+ if ((maxForms.val() !== '') && (maxForms.val() - totalForms.val()) <= 0) {
+ addButton.parent().hide();
+ }
+ // Show the remove buttons if there are more than min_num.
+ toggleDeleteButtonVisibility(row.closest('.inline-group'));
+
+ // Pass the new form to the post-add callback, if provided.
+ if (options.added) {
+ options.added(row);
+ }
+ row.get(0).dispatchEvent(new CustomEvent("formset:added", {
+ bubbles: true,
+ detail: {
+ formsetName: options.prefix
+ }
+ }));
+ };
+
+ /**
+ * The "X" button that is part of every unsaved inline.
+ * (When saved, it is replaced with a "Delete" checkbox.)
+ */
+ const addInlineDeleteButton = function(row) {
+ if (row.is("tr")) {
+ // If the forms are laid out in table rows, insert
+ // the remove button into the last table cell:
+ row.children(":last").append('