Compare commits
12 Commits
643205a4bb
...
affichage_
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1da9d4ca5f | ||
|
|
e81c29a586 | ||
|
|
d28bf85556 | ||
|
|
3b587d2a1a | ||
|
|
e3f0befff6 | ||
|
|
55e5a2819c | ||
|
|
7e28b25497 | ||
| f9ca539670 | |||
| 7b4778252f | |||
| 2c086187e7 | |||
| 5aff49600e | |||
| 8c6e59e15a |
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -27,7 +27,7 @@ SECRET_KEY = config('SECRET_KEY')
|
|||||||
DEBUG = config('DEBUG', default=True, cast=bool)
|
DEBUG = config('DEBUG', default=True, cast=bool)
|
||||||
|
|
||||||
ALLOWED_HOSTS = config('ALLOWED_HOSTS', default=[]).split(',')
|
ALLOWED_HOSTS = config('ALLOWED_HOSTS', default=[]).split(',')
|
||||||
CSRF_TRUSTED_ORIGINS = config("CSRF_TRUSTED_ORIGINS", default=[]).split(",")
|
#CSRF_TRUSTED_ORIGINS = config("CSRF_TRUSTED_ORIGINS", default=[]).split(",")
|
||||||
|
|
||||||
# Application definition
|
# Application definition
|
||||||
|
|
||||||
@@ -66,6 +66,7 @@ TEMPLATES = [
|
|||||||
'django.template.context_processors.request',
|
'django.template.context_processors.request',
|
||||||
'django.contrib.auth.context_processors.auth',
|
'django.contrib.auth.context_processors.auth',
|
||||||
'django.contrib.messages.context_processors.messages',
|
'django.contrib.messages.context_processors.messages',
|
||||||
|
'contexte_universel.contexte_universel',
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -144,9 +145,9 @@ DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField'
|
|||||||
# LOGIN_URL = '/client/'
|
# LOGIN_URL = '/client/'
|
||||||
LOGIN_URL = 'simple-sso-login'
|
LOGIN_URL = 'simple-sso-login'
|
||||||
|
|
||||||
SSO_PRIVATE_KEY = 'D2brFY3LnNDLQBBnvYPXAn185Wi9EpPvnHsUosjEZ9vZMyEtXqkcVfFWkVbN0xpG'
|
SSO_PRIVATE_KEY = config('SSO_PRIVATE_KEY')
|
||||||
SSO_PUBLIC_KEY = 'm9JDP3uGPmGG9ULqPw2BY3ryBNMTGRsuukgIOhk7lIN9lT0wlr2zhoRZiNZ28UCM'
|
SSO_PUBLIC_KEY = config('SSO_PUBLIC_KEY')
|
||||||
SSO_SERVER = 'http://test-authentification.cerfig.org:89/server/'
|
SSO_SERVER = config('SSO_SERVER')
|
||||||
#SSO_LOGOUT_URL = 'http://test-authentification.cerfig.org:89/deconnexion'
|
#SSO_LOGOUT_URL = 'http://test-authentification.cerfig.org:89/deconnexion'
|
||||||
|
|
||||||
# Configuration de l'email
|
# Configuration de l'email
|
||||||
|
|||||||
14
SIRH/sso.py
14
SIRH/sso.py
@@ -10,11 +10,17 @@ class SIRHClient(Client):
|
|||||||
def build_user(self, user_data):
|
def build_user(self, user_data):
|
||||||
"""Crée le User et son Profile avec les infos reçues du serveur."""
|
"""Crée le User et son Profile avec les infos reçues du serveur."""
|
||||||
user_data_copy = user_data.copy()
|
user_data_copy = user_data.copy()
|
||||||
user_data.pop("departement")
|
|
||||||
user_data.pop("chef_departement")
|
print("Un petit test", user_data_copy)
|
||||||
user_data.pop("groups")
|
|
||||||
|
user_data.pop("departement", None)
|
||||||
|
user_data.pop("chef_departement", None)
|
||||||
|
user_data.pop("groups", None)
|
||||||
|
|
||||||
user = super().build_user(user_data)
|
user = super().build_user(user_data)
|
||||||
|
departement = None
|
||||||
|
|
||||||
|
if "departement" in user_data_copy:
|
||||||
departement = Departement.objects.get_or_create(
|
departement = Departement.objects.get_or_create(
|
||||||
nom=user_data_copy.get("departement", ""))[0]
|
nom=user_data_copy.get("departement", ""))[0]
|
||||||
|
|
||||||
@@ -22,7 +28,7 @@ class SIRHClient(Client):
|
|||||||
user = user,
|
user = user,
|
||||||
defaults = {
|
defaults = {
|
||||||
"departement": departement,
|
"departement": departement,
|
||||||
"chef": user_data_copy.get("chef_departement", ""),
|
"chef": user_data_copy.get("chef_departement", None),
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
<div class="col-1 bg-danger border d-flex flex-column vh-100 pt-5 sticky-top">
|
<div class="col-1 bg-danger border d-flex flex-column vh-100 pt-5 sticky-top">
|
||||||
<a href="http://test-sirh.cerfig.org:89/" class="text-white text-center fw-bold text-decoration-none mb-4" style="font-size:1.4em">
|
<a href="{{ SIRH_LIEN }}" class="text-white text-center fw-bold text-decoration-none mb-4" style="font-size:1.4em">
|
||||||
<i class="bi bi-people-fill fs-1 d-block"></i>SIRH
|
<i class="bi bi-people-fill fs-1 d-block"></i>SIRH
|
||||||
</a>
|
</a>
|
||||||
<a href="http://test-sgl.cerfig.org:89/" class="text-white text-center fw-bold text-decoration-none mb-4" style="font-size:1.4em">
|
<a href="{{ SGL_LIEN }}" class="text-white text-center fw-bold text-decoration-none mb-4" style="font-size:1.4em">
|
||||||
<i class="bi bi-building-gear fs-1 d-block"></i> SGL
|
<i class="bi bi-building-gear fs-1 d-block"></i> SGL
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -42,7 +42,7 @@
|
|||||||
<i class="bi bi-calendar-check"></i> Réservation
|
<i class="bi bi-calendar-check"></i> Réservation
|
||||||
</a>
|
</a>
|
||||||
{% comment %} {% url 'deconnexion' %} {% endcomment %}
|
{% comment %} {% url 'deconnexion' %} {% endcomment %}
|
||||||
<a href="http://test-authentification.cerfig.org:89/deconnexion" class="text-white fw-bold text-decoration-none mb-4" style="font-size: 1.2em;">
|
<a href="{% url 'deconnexion' %}" class="text-white fw-bold text-decoration-none mb-4" style="font-size: 1.2em;">
|
||||||
<i class="bi bi-box-arrow-right"></i> Déconnexion
|
<i class="bi bi-box-arrow-right"></i> Déconnexion
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
20
SIRH/urls.py
20
SIRH/urls.py
@@ -18,7 +18,7 @@ from django.contrib import admin
|
|||||||
from django.urls import include, path
|
from django.urls import include, path
|
||||||
# from django.conf.urls.static import static
|
# from django.conf.urls.static import static
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
|
from . import views
|
||||||
from .sso import SIRHClient
|
from .sso import SIRHClient
|
||||||
|
|
||||||
client_sirh = SIRHClient(
|
client_sirh = SIRHClient(
|
||||||
@@ -28,15 +28,15 @@ client_sirh = SIRHClient(
|
|||||||
)
|
)
|
||||||
|
|
||||||
urlpatterns = [
|
urlpatterns = [
|
||||||
# path('login/',
|
path('login/',
|
||||||
# views.login_view,
|
views.connexion,
|
||||||
# name='login'
|
name='login'
|
||||||
# ),
|
),
|
||||||
# path(
|
path(
|
||||||
# 'deconnexion/',
|
'deconnexion/',
|
||||||
# views.deconnexion_view,
|
views.deconnexion,
|
||||||
# name='deconnexion'
|
name='deconnexion'
|
||||||
# ),
|
),
|
||||||
path(
|
path(
|
||||||
'',
|
'',
|
||||||
include("gestion_employe.urls")
|
include("gestion_employe.urls")
|
||||||
|
|||||||
@@ -0,0 +1,16 @@
|
|||||||
|
from django.contrib.auth import logout
|
||||||
|
from django.shortcuts import redirect
|
||||||
|
from django.urls import reverse_lazy
|
||||||
|
|
||||||
|
from django.conf import settings
|
||||||
|
|
||||||
|
|
||||||
|
def connexion(request):
|
||||||
|
"""Redirige vers le serveur SSO pour l'authentification."""
|
||||||
|
return redirect(reverse_lazy("simple-sso-login"))
|
||||||
|
|
||||||
|
def deconnexion(request):
|
||||||
|
"""Gérez la déconnexion des utilisateurs."""
|
||||||
|
logout(request)
|
||||||
|
#return redirect(settings.SSO_LOGOUT_URL)
|
||||||
|
return redirect("http://authentification.cerfig.org/login")
|
||||||
|
|||||||
7
contexte_universel.py
Normal file
7
contexte_universel.py
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
from decouple import config
|
||||||
|
|
||||||
|
def contexte_universel(request):
|
||||||
|
return {
|
||||||
|
"SIRH_LIEN": config("SIRH_LIEN"),
|
||||||
|
"SGL_LIEN": config("SGL_LIEN")
|
||||||
|
}
|
||||||
Binary file not shown.
@@ -7,7 +7,11 @@ QUOTA_CONGE_ANNUEL = 30
|
|||||||
NOMBRE_PAGINATION = 8
|
NOMBRE_PAGINATION = 8
|
||||||
DEBUT_RAPPEL = 60
|
DEBUT_RAPPEL = 60
|
||||||
DUREE_FIN_CONTRAT = 90
|
DUREE_FIN_CONTRAT = 90
|
||||||
# EMAIL_ASSISTANTE_DE_DIRECTION = list(Employe.objects.filter(fonction="assistant_direction").values_list('user__email', flat=True))
|
|
||||||
|
try:
|
||||||
|
EMAIL_ASSISTANTE_DE_DIRECTION = list(Employe.objects.filter(fonction="assistant_direction").values_list('user__email', flat=True))
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
|
||||||
def solde_conge(employe):
|
def solde_conge(employe):
|
||||||
"""Fonction de calcul du solde de congé restant l'employé"""
|
"""Fonction de calcul du solde de congé restant l'employé"""
|
||||||
|
|||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -18,29 +18,34 @@ const tableau_certificat = new Tabulator("#tableau-certificat", {
|
|||||||
})
|
})
|
||||||
const enregistrerProfil = $("enregistrerProfil");
|
const enregistrerProfil = $("enregistrerProfil");
|
||||||
|
|
||||||
enregistrerProfil.addEventListener("click", (e) => {
|
// enregistrerProfil.addEventListener("click", (e) => {
|
||||||
|
|
||||||
const url = $("information-personnelles").dataset.url;
|
// const url = $("information-personnelles").dataset.url;
|
||||||
const csrftoken = document.querySelector("[name='csrfmiddlewaretoken']").value;
|
// const csrftoken = document.querySelector("[name='csrfmiddlewaretoken']").value;
|
||||||
|
|
||||||
const formData = new FormData();
|
// const formData = new FormData();
|
||||||
|
|
||||||
formData.append("nom", $("nom").value);
|
// formData.append("nom", $("nom").value);
|
||||||
formData.append("prenom", $("prenom").value);
|
// formData.append("prenom", $("prenom").value);
|
||||||
formData.append("email", $("email").value);
|
// formData.append("email", $("email").value);
|
||||||
formData.append("telephone", $("telephone").value);
|
// formData.append("telephone", $("telephone").value);
|
||||||
formData.append("adresse", $("adresse").value);
|
// formData.append("adresse", $("adresse").value);
|
||||||
formData.append("sexe", $("sexe").value);
|
// formData.append("sexe", $("sexe").value);
|
||||||
formData.append("date_naissance", $("date_naissance").value);
|
// formData.append("date_naissance", $("date_naissance").value);
|
||||||
|
|
||||||
fetch(url, {
|
|
||||||
method: "POST",
|
// console.log("---------------------------------------------")
|
||||||
headers: {
|
// console.log("Données du formulaire:", url);
|
||||||
"X-CSRFToken": csrftoken
|
// console.log("Données du formulaire:", formData);
|
||||||
},
|
|
||||||
body: formData
|
// fetch(url, {
|
||||||
})
|
// method: "POST",
|
||||||
.then(response => response.json())
|
// headers: {
|
||||||
.then(data => alert(data.message))
|
// "X-CSRFToken": csrftoken
|
||||||
.catch(error => console.error("Erreur:", error));
|
// },
|
||||||
});
|
// body: formData
|
||||||
|
// })
|
||||||
|
// .then(response => response.json())
|
||||||
|
// .then(data => alert(data.message))
|
||||||
|
// .catch(error => console.error("Erreur:", error));
|
||||||
|
// });
|
||||||
@@ -38,7 +38,7 @@
|
|||||||
</h2>
|
</h2>
|
||||||
<div id="collapseProfil" class="accordion-collapse" aria-labelledby="headingContrats" data-bs-parent="#accordionInformationEmploye">
|
<div id="collapseProfil" class="accordion-collapse" aria-labelledby="headingContrats" data-bs-parent="#accordionInformationEmploye">
|
||||||
<div class="accordion-body">
|
<div class="accordion-body">
|
||||||
<div class="row" id="information-personnelles" data-url="{% url 'gestion_employe:modifier-employe' %}">
|
<form class="row" action = "{% url 'gestion_employe:modifier-employe' %}" method = "POST" enctype = "multipart/form-data" id="information-personnelles" data-url="{% url 'gestion_employe:modifier-employe' %}">
|
||||||
{% csrf_token %}
|
{% csrf_token %}
|
||||||
<div class="col">
|
<div class="col">
|
||||||
<div class="form-group mb-2">
|
<div class="form-group mb-2">
|
||||||
@@ -50,19 +50,19 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="form-group mb-2">
|
<div class="form-group mb-2">
|
||||||
<label>Matricule :</label>
|
<label>Matricule :</label>
|
||||||
<input type="text" class="form-control" id="matricule" value="{{ employe.matricule|default:'' }}" readonly>
|
<input type="text" class="form-control" id="matricule" name = 'matricule' value="{{ employe.matricule|default:'' }}" readonly>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group mb-2">
|
<div class="form-group mb-2">
|
||||||
<label>Nom :</label>
|
<label>Nom :</label>
|
||||||
<input type="text" class="form-control" id="nom" value="{{ employe.user.last_name }}">
|
<input type="text" class="form-control" id="nom" name = 'nom' value="{{ employe.user.last_name }}">
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group mb-2">
|
<div class="form-group mb-2">
|
||||||
<label>Prénom :</label>
|
<label>Prénom :</label>
|
||||||
<input type="text" class="form-control" id="prenom" value="{{ employe.user.first_name }}">
|
<input type="text" class="form-control" id="prenom" name = 'prenom' value="{{ employe.user.first_name }}">
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group mb-2">
|
<div class="form-group mb-2">
|
||||||
<label>Sexe :</label>
|
<label>Sexe :</label>
|
||||||
<select id='sexe' class="form-select">
|
<select id='sexe' name = 'sexe' class="form-select">
|
||||||
<option value='h' {% if employe.sexe == 'h' %}selected{% endif %}>Homme</option>
|
<option value='h' {% if employe.sexe == 'h' %}selected{% endif %}>Homme</option>
|
||||||
<option value='f' {% if employe.sexe == 'f' %}selected{% endif %}>Femme</option>
|
<option value='f' {% if employe.sexe == 'f' %}selected{% endif %}>Femme</option>
|
||||||
</select>
|
</select>
|
||||||
@@ -72,27 +72,27 @@
|
|||||||
<div class="col">
|
<div class="col">
|
||||||
<div class="form-group mb-2">
|
<div class="form-group mb-2">
|
||||||
<label>Date de naissance :</label>
|
<label>Date de naissance :</label>
|
||||||
<input type="date" class="form-control" id="date_naissance" value="{{ employe.date_naissance|date:'Y-m-d' }}">
|
<input type="date" class="form-control" id="date_naissance" name = 'date_naissance' value="{{ employe.date_naissance|date:'Y-m-d' }}">
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group mb-2">
|
<div class="form-group mb-2">
|
||||||
<label>Département :</label>
|
<label>Département :</label>
|
||||||
<input type="text" class="form-control" id="departement" value="{{ employe.departement.nom|default:'' }}" readonly>
|
<input type="text" class="form-control" id="departement" name = 'departement' value="{{ employe.departement.nom|default:'' }}" readonly>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group mb-2">
|
<div class="form-group mb-2">
|
||||||
<label>Email :</label>
|
<label>Email :</label>
|
||||||
<input type="email" class="form-control" id="email" value="{{ employe.user.email|default:'' }}">
|
<input type="email" class="form-control" id="email" name = 'email' value="{{ employe.user.email|default:'' }}">
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group mb-2">
|
<div class="form-group mb-2">
|
||||||
<label>Téléphone :</label>
|
<label>Téléphone :</label>
|
||||||
<input type="text" class="form-control" id="telephone" value="{{ employe.telephone|default:'' }}">
|
<input type="text" class="form-control" id="telephone" name = 'telephone' value="{{ employe.telephone|default:'' }}">
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group mb-2">
|
<div class="form-group mb-2">
|
||||||
<label>Adresse :</label>
|
<label>Adresse :</label>
|
||||||
<input type="text" class="form-control" id="adresse" value="{{ employe.adresse|default:'' }}">
|
<input type="text" class="form-control" id="adresse" name = 'adresse' value="{{ employe.adresse|default:'' }}">
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<button type="submit" class="btn btn-success d-block m-auto" id="enregistrerProfil">Enregistrer</button>
|
<button type="submit" class="btn btn-success d-block m-auto" id="enregistrerProfil">Enregistrer</button>
|
||||||
|
</form>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Binary file not shown.
@@ -20,6 +20,9 @@ from fonction_utilitaire import fonctions_utilitaire
|
|||||||
@login_required
|
@login_required
|
||||||
def index(request):
|
def index(request):
|
||||||
"""Vue d'index"""
|
"""Vue d'index"""
|
||||||
|
if not request.user.groups.filter(name="ressource_humaine").exists():
|
||||||
|
return redirect('gestion_employe:mon-profil')
|
||||||
|
|
||||||
employes = Employe.objects.all().order_by('-user__date_joined')
|
employes = Employe.objects.all().order_by('-user__date_joined')
|
||||||
nombre_employes = Employe.objects.count()
|
nombre_employes = Employe.objects.count()
|
||||||
nombre_cps = Contrat.objects.filter(type_contrat='contrat_prestation').count()
|
nombre_cps = Contrat.objects.filter(type_contrat='contrat_prestation').count()
|
||||||
@@ -94,7 +97,7 @@ def affecter_employe_projet(request):
|
|||||||
elif total_affectation + temps_nouveau > 100:
|
elif total_affectation + temps_nouveau > 100:
|
||||||
messages.warning(
|
messages.warning(
|
||||||
request,
|
request,
|
||||||
f"Les pourcentages d'affectation de l'employé {employe.first_name} {employe.last_name} dépasse 100% sur les différents projets ({total_affectation + temps_nouveau}%)."
|
f"Les pourcentages d'affectation de l'employé {employe.user.first_name} {employe.user.last_name} dépasse 100% sur les différents projets ({total_affectation + temps_nouveau}%)."
|
||||||
)
|
)
|
||||||
return redirect('gestion_employe:index')
|
return redirect('gestion_employe:index')
|
||||||
|
|
||||||
@@ -191,25 +194,26 @@ def modifier_employer(request):
|
|||||||
except Employe.DoesNotExist:
|
except Employe.DoesNotExist:
|
||||||
return JsonResponse({"message": "Employé non trouvé."})
|
return JsonResponse({"message": "Employé non trouvé."})
|
||||||
if request.method == "POST":
|
if request.method == "POST":
|
||||||
data = json.loads(request.body)
|
|
||||||
user = User.objects.get(username=request.user)
|
user = User.objects.get(username=request.user)
|
||||||
user.last_name = data['nom']
|
user.last_name = request.POST['nom']
|
||||||
user.first_name = data['prenom']
|
user.first_name = request.POST['prenom']
|
||||||
user.email = data['email']
|
user.email = request.POST['email']
|
||||||
employe.telephone = data['telephone']
|
employe.telephone = request.POST['telephone']
|
||||||
employe.adresse = data['adresse']
|
employe.adresse = request.POST['adresse']
|
||||||
employe.sexe = data['sexe']
|
employe.sexe = request.POST['sexe']
|
||||||
if request.FILES.get("photo"):
|
if request.FILES.get("photo"):
|
||||||
employe.photo = request.FILES["photo"]
|
employe.photo = request.FILES["photo"]
|
||||||
if data['date_naissance']:
|
if request.POST['date_naissance']:
|
||||||
difference = relativedelta(timezone.now().date(), datetime.strptime(data['date_naissance'], "%Y-%m-%d").date())
|
difference = relativedelta(timezone.now().date(), datetime.strptime(request.POST['date_naissance'], "%Y-%m-%d").date())
|
||||||
if difference.years >= 18:
|
if difference.years >= 18:
|
||||||
employe.date_naissance = data['date_naissance']
|
employe.date_naissance = request.POST['date_naissance']
|
||||||
else:
|
else:
|
||||||
return JsonResponse({"message": "Veuillez entrez une date de naissance correcte."})
|
messages.error(request, "Veuillez entrez une date de naissance correcte.")
|
||||||
employe.save()
|
employe.save()
|
||||||
user.save()
|
user.save()
|
||||||
return JsonResponse({"message": "Profil mis à jour avec succès."})
|
messages.success(request, "Profil mis à jour avec succès.")
|
||||||
|
return redirect("gestion_employe:mon-profil")
|
||||||
|
|
||||||
|
|
||||||
def enregistrement_document(request):
|
def enregistrement_document(request):
|
||||||
"""Vue pour permettre à un utilisateur de télécharger et enregistrer des documents liés à son profil"""
|
"""Vue pour permettre à un utilisateur de télécharger et enregistrer des documents liés à son profil"""
|
||||||
|
|||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -15,6 +15,11 @@ const tableau_liste_activite = new Tabulator("#tableau-liste-activite", {
|
|||||||
})
|
})
|
||||||
tableau_liste_activite.on("rowClick", function (row, rowData) {
|
tableau_liste_activite.on("rowClick", function (row, rowData) {
|
||||||
const data = rowData.getData();
|
const data = rowData.getData();
|
||||||
|
console.log('=== ACTIVITÉ SÉLECTIONNÉE ===');
|
||||||
|
console.log('ID:', data.id);
|
||||||
|
console.log('Budget prévu:', data.budget_prevu);
|
||||||
|
console.log('Budget dépensé:', data.budget_depense);
|
||||||
|
|
||||||
$("idDetailActivite").value = data.id;
|
$("idDetailActivite").value = data.id;
|
||||||
$("titreDetailActivite").value = data.titre;
|
$("titreDetailActivite").value = data.titre;
|
||||||
$("descriptionDetailActivite").value = data.description;
|
$("descriptionDetailActivite").value = data.description;
|
||||||
@@ -22,14 +27,79 @@ tableau_liste_activite.on("rowClick", function (row, rowData) {
|
|||||||
$("date_finDetailActivite").value = data.date_fin;
|
$("date_finDetailActivite").value = data.date_fin;
|
||||||
$("statutDetailActivite").value = data.statut;
|
$("statutDetailActivite").value = data.statut;
|
||||||
$("budget_prevuDetailActivite").value = data.budget_prevu;
|
$("budget_prevuDetailActivite").value = data.budget_prevu;
|
||||||
|
$("budget_depenseDetailActivite").value = data.budget_depense || 0;
|
||||||
|
|
||||||
|
// Vérifier si l'activité est annulée
|
||||||
|
const isAnnulee = data.statut && data.statut.toLowerCase().includes('annul');
|
||||||
|
console.log('Activité annulée?', isAnnulee);
|
||||||
|
|
||||||
|
// Masquer/Afficher les boutons d'action selon le statut
|
||||||
|
const btnAnnuler = $("btnAnnulerActivite");
|
||||||
|
const btnMiseAJour = $("btnMiseAJourDepense");
|
||||||
|
const btnAjouterLivrable = $("btnOuvrirAjouterLivrable");
|
||||||
|
|
||||||
|
if (isAnnulee) {
|
||||||
|
// Activité annulée : masquer les boutons
|
||||||
|
if (btnAnnuler) btnAnnuler.style.display = "none";
|
||||||
|
if (btnMiseAJour) btnMiseAJour.style.display = "none";
|
||||||
|
if (btnAjouterLivrable) btnAjouterLivrable.style.display = "none";
|
||||||
|
console.log('✓ Boutons masqués - activité annulée');
|
||||||
|
} else {
|
||||||
|
// Activité active : afficher les boutons
|
||||||
|
if (btnAnnuler) btnAnnuler.style.display = "block";
|
||||||
|
if (btnMiseAJour) btnMiseAJour.style.display = "block";
|
||||||
|
if (btnAjouterLivrable) btnAjouterLivrable.style.display = "block";
|
||||||
|
console.log('✓ Boutons affichés - activité active');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Vérifier si les budgets sont différents
|
||||||
|
const budgetPrevu = parseFloat(data.budget_prevu) || 0;
|
||||||
|
const budgetDepense = parseFloat(data.budget_depense) || 0;
|
||||||
|
const isDifferent = budgetDepense !== budgetPrevu;
|
||||||
|
|
||||||
|
console.log('Budget prévu:', budgetPrevu);
|
||||||
|
console.log('Budget dépensé:', budgetDepense);
|
||||||
|
console.log('Sont-ils différents?', isDifferent);
|
||||||
|
|
||||||
|
// Masquer/Afficher le conteneur du motif
|
||||||
|
const motifContainer = $("motif_container");
|
||||||
|
if (motifContainer) {
|
||||||
|
if (isDifferent) {
|
||||||
|
motifContainer.style.display = "block";
|
||||||
|
$("motif_changement_budgetDetailActivite").value = data.motif_changement_budget || "";
|
||||||
|
console.log('✓ Motif AFFICHÉ - budgets différents');
|
||||||
|
} else {
|
||||||
|
motifContainer.style.display = "none";
|
||||||
|
$("motif_changement_budgetDetailActivite").value = "";
|
||||||
|
console.log('✓ Motif MASQUÉ - budgets identiques');
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
console.error('❌ Élément motif_container introuvable!');
|
||||||
|
}
|
||||||
|
|
||||||
$("besoin_ressources_materiellesDetailActivite").value = data.besoin_ressource_materielle;
|
$("besoin_ressources_materiellesDetailActivite").value = data.besoin_ressource_materielle;
|
||||||
const modal = new bootstrap.Modal($("modalDetailActivite"));
|
const modal = new bootstrap.Modal($("modalDetailActivite"));
|
||||||
modal.show();
|
modal.show();
|
||||||
|
|
||||||
fetch(`liste-des-livrables/${data.id}/`)
|
const url_livrables = `/gestion-projet/activite/liste-des-livrables/${data.id}/`;
|
||||||
.then(response => response.json())
|
console.log('URL des livrables:', url_livrables);
|
||||||
|
|
||||||
|
fetch(url_livrables)
|
||||||
|
.then(response => {
|
||||||
|
console.log('Réponse status:', response.status);
|
||||||
|
return response.json();
|
||||||
|
})
|
||||||
.then(livrables => {
|
.then(livrables => {
|
||||||
|
console.log('Livrables reçus:', livrables);
|
||||||
|
console.log('Nombre de livrables:', livrables.length);
|
||||||
tableau_liste_livrable.setData(livrables);
|
tableau_liste_livrable.setData(livrables);
|
||||||
|
$("nombreLivrables").textContent = livrables.length;
|
||||||
|
console.log('Tableau mis à jour avec', livrables.length, 'livrables');
|
||||||
|
})
|
||||||
|
.catch(error => {
|
||||||
|
console.error(' Erreur lors du chargement des livrables:', error);
|
||||||
|
tableau_liste_livrable.setData([]);
|
||||||
|
$("nombreLivrables").textContent = 0;
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -40,6 +110,7 @@ const tableau_liste_livrable = new Tabulator("#listeLivrables", {
|
|||||||
],
|
],
|
||||||
placeholder: "Aucun livrable trouvé",
|
placeholder: "Aucun livrable trouvé",
|
||||||
})
|
})
|
||||||
|
console.log('✓ Tableau des livrables initialisé');
|
||||||
|
|
||||||
$("btnMiseAJourDepense").addEventListener("click", function() {
|
$("btnMiseAJourDepense").addEventListener("click", function() {
|
||||||
const modal = new bootstrap.Modal($("modalDepenseActivite"));
|
const modal = new bootstrap.Modal($("modalDepenseActivite"));
|
||||||
@@ -57,3 +128,4 @@ $("btnAnnulerActivite").addEventListener("click", function(event) {
|
|||||||
$("id_activite_annulation").value = $("idDetailActivite").value;
|
$("id_activite_annulation").value = $("idDetailActivite").value;
|
||||||
bootstrap.Modal.getOrCreateInstance($("modalDetailActivite")).hide();
|
bootstrap.Modal.getOrCreateInstance($("modalDetailActivite")).hide();
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
@@ -1,58 +1,111 @@
|
|||||||
<div class="modal fade" id="modalDetailActivite" tabindex="-1">
|
<div class="modal fade" id="modalDetailActivite" tabindex="-1"
|
||||||
<div class="modal-dialog">
|
aria-labelledby="modalDetailActiviteLabel" aria-hidden="true">
|
||||||
|
<div class="modal-dialog modal-lg modal-dialog-centered">
|
||||||
<div class="modal-content">
|
<div class="modal-content">
|
||||||
<div class="modal-header bg-orange-dark">
|
<div class="modal-header bg-orange-dark text-black">
|
||||||
<h5 class="modal-title">Détails de l'activité</h5>
|
<h5 class="modal-title" id="modalDetailActiviteLabel">
|
||||||
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
|
<i class="bi bi-clipboard-check me-2"></i>
|
||||||
|
Détails de l'activité
|
||||||
|
</h5>
|
||||||
|
<button type="button"class="btn-close btn-close-white" data-bs-dismiss="modal"
|
||||||
|
aria-label="Fermer">
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-body">
|
<div class="modal-body" data-url-livrables="{% url 'gestion_projet:liste-livrables-activite' activite_id=0 %}">
|
||||||
<div class="row g-3">
|
|
||||||
<div class="">
|
|
||||||
<input type="hidden" id="idDetailActivite">
|
<input type="hidden" id="idDetailActivite">
|
||||||
<div class="form-group mb-2">
|
<div class="row g-3">
|
||||||
<label>Titre :</label>
|
<div class="col-12">
|
||||||
<input type="text" class="form-control" id="titreDetailActivite">
|
<label for="titreDetailActivite" class="form-label fw-bold">
|
||||||
|
Titre :
|
||||||
|
</label>
|
||||||
|
<input type="text"class="form-control" id="titreDetailActivite"readonly>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group mb-2">
|
<div class="col-12">
|
||||||
<label>Description :</label>
|
<label for="descriptionDetailActivite" class="form-label fw-bold">
|
||||||
<textarea class="form-control" id="descriptionDetailActivite"></textarea>
|
Description :
|
||||||
|
</label>
|
||||||
|
<textarea class="form-control"id="descriptionDetailActivite" rows="3" readonly></textarea>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group mb-2">
|
<div class="col-md-6">
|
||||||
<label>Date de début :</label>
|
<label for="date_debutDetailActivite"class="form-label fw-bold">
|
||||||
<input type="date" class="form-control" id="date_debutDetailActivite">
|
Date de début :
|
||||||
|
</label>
|
||||||
|
<input type="date"class="form-control"id="date_debutDetailActivite"readonly>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group mb-2">
|
<div class="col-md-6">
|
||||||
<label>Date de fin :</label>
|
<label for="date_finDetailActivite"class="form-label fw-bold">
|
||||||
<input type="date" class="form-control" id="date_finDetailActivite">
|
Date de fin :
|
||||||
|
</label>
|
||||||
|
<input type="date"class="form-control"id="date_finDetailActivite"readonly>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group mb-2">
|
<div class="col-md-6">
|
||||||
<label>Statut:</label>
|
<label for="statutDetailActivite" class="form-label fw-bold">
|
||||||
<input type="text" class="form-control" id="statutDetailActivite">
|
Statut :
|
||||||
|
</label>
|
||||||
|
<input type="text"class="form-control"id="statutDetailActivite"readonly>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group mb-2">
|
<div class="col-md-6">
|
||||||
<label>Budget prévu :</label>
|
<label for="budget_prevuDetailActivite"class="form-label fw-bold">
|
||||||
<input type="number" class="form-control" id="budget_prevuDetailActivite">
|
Budget prévu :
|
||||||
|
</label>
|
||||||
|
<input type="number"class="form-control" id="budget_prevuDetailActivite"readonly>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group mb-2">
|
<div class="col-md-6">
|
||||||
<label>Besoin de ressources matérielles:</label>
|
<label for="budget_depenseDetailActivite"class="form-label fw-bold">
|
||||||
<textarea class="form-control" id="besoin_ressources_materiellesDetailActivite"></textarea>
|
Montant dépensé (GNF) :
|
||||||
|
</label>
|
||||||
|
<input type="number"class="form-control" id="budget_depenseDetailActivite"readonly>
|
||||||
|
</div>
|
||||||
|
<div class="col-12" id="motif_container">
|
||||||
|
<label for="motif_changement_budgetDetailActivite"class="form-label fw-bold">
|
||||||
|
Motif de différence :
|
||||||
|
</label>
|
||||||
|
<textarea class="form-control"id="motif_changement_budgetDetailActivite"rows="2"readonly></textarea>
|
||||||
|
</div>
|
||||||
|
<div class="col-12">
|
||||||
|
<label for="besoin_ressources_materiellesDetailActivite"class="form-label fw-bold">
|
||||||
|
Besoin de ressources matérielles :
|
||||||
|
</label>
|
||||||
|
<textarea class="form-control"id="besoin_ressources_materiellesDetailActivite"rows="3"readonly></textarea>
|
||||||
|
</div>
|
||||||
|
<div class="col-12 mt-4">
|
||||||
|
|
||||||
|
<div class="d-flex justify-content-between align-items-center mb-3">
|
||||||
|
<h5 class="mb-0 fw-bold">
|
||||||
|
<i class="bi bi-box-seam me-2"></i>
|
||||||
|
Liste des livrables
|
||||||
|
</h5>
|
||||||
|
<span class="badge bg-primary" id="nombreLivrables">
|
||||||
|
0
|
||||||
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<p><strong>Liste des livrables :</strong></p>
|
|
||||||
<div id="listeLivrables"></div>
|
<div id="listeLivrables"></div>
|
||||||
<button class="btn btn-danger d-block mx-auto" id="btnAnnulerActivite">
|
</div>
|
||||||
|
|
||||||
|
<div class="col-12 mt-3 text-center">
|
||||||
|
<button type="button"class="btn btn-danger"id="btnAnnulerActivite">
|
||||||
|
<i class="bi bi-x-circle me-2"></i>
|
||||||
Annuler cette activité
|
Annuler cette activité
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-footer">
|
<div class="modal-footer">
|
||||||
<button class="btn btn-success" id="btnMiseAJourDepense">
|
<button type="button"class="btn btn-success"id="btnMiseAJourDepense">
|
||||||
<i class="bi bi-plus-circle"></i> Mise à jour du budget
|
<i class="bi bi-cash-stack me-2"></i>
|
||||||
|
Mise à jour du budget
|
||||||
</button>
|
</button>
|
||||||
<button class="btn btn-primary" data-bs-toggle="modal" data-bs-target="#modalAjouterLivrable">
|
<button type="button"class="btn btn-primary"id="btnOuvrirAjouterLivrable"data-bs-toggle="modal"
|
||||||
<i class="bi bi-plus-circle"></i> Ajouter un livrable
|
data-bs-target="#modalAjouterLivrable">
|
||||||
|
<i class="bi bi-plus-circle me-2"></i>
|
||||||
|
Ajouter un livrable
|
||||||
|
</button>
|
||||||
|
<button type="button" class="btn btn-secondary"data-bs-dismiss="modal">
|
||||||
|
Fermer
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -381,16 +381,33 @@ def liste_activites_projet(request):
|
|||||||
@login_required
|
@login_required
|
||||||
def liste_livrables_activite(request, activite_id):
|
def liste_livrables_activite(request, activite_id):
|
||||||
"""Vue pour retourner la liste des livrables attendus d'une activité spécifique"""
|
"""Vue pour retourner la liste des livrables attendus d'une activité spécifique"""
|
||||||
|
try:
|
||||||
livrables = LivrablesLivres.objects.filter(activite__id=activite_id)
|
livrables = LivrablesLivres.objects.filter(activite__id=activite_id)
|
||||||
data = []
|
data = []
|
||||||
|
|
||||||
for livrable in livrables:
|
for livrable in livrables:
|
||||||
print(livrable.fichier.url)
|
lien = ""
|
||||||
|
if livrable.fichier:
|
||||||
|
try:
|
||||||
|
lien = livrable.fichier.url
|
||||||
|
except Exception as e:
|
||||||
|
print(f"[DEBUG] Erreur accès URL fichier {livrable.nom}: {e}")
|
||||||
|
lien = ""
|
||||||
|
|
||||||
data.append({
|
data.append({
|
||||||
"titre": livrable.nom,
|
"titre": livrable.nom,
|
||||||
"lien": livrable.fichier.url if livrable.fichier else "",
|
"lien": lien,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
print(f"[DEBUG] ✓ Livrables activité {activite_id}: {len(data)} trouvés")
|
||||||
return JsonResponse(data, safe=False)
|
return JsonResponse(data, safe=False)
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
print(f"[DEBUG] Erreur liste_livrables_activite: {e}")
|
||||||
|
import traceback
|
||||||
|
traceback.print_exc()
|
||||||
|
return JsonResponse({"error": f"Erreur serveur: {str(e)}"}, status=500)
|
||||||
|
|
||||||
@login_required
|
@login_required
|
||||||
def mises_a_jour_depense_activite(request):
|
def mises_a_jour_depense_activite(request):
|
||||||
"""Vue pour retourner la liste des activités d'un projet spécifique avec leurs dépenses mises à jour"""
|
"""Vue pour retourner la liste des activités d'un projet spécifique avec leurs dépenses mises à jour"""
|
||||||
|
|||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Reference in New Issue
Block a user