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)
|
||||
|
||||
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
|
||||
|
||||
@@ -66,6 +66,7 @@ TEMPLATES = [
|
||||
'django.template.context_processors.request',
|
||||
'django.contrib.auth.context_processors.auth',
|
||||
'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 = 'simple-sso-login'
|
||||
|
||||
SSO_PRIVATE_KEY = 'D2brFY3LnNDLQBBnvYPXAn185Wi9EpPvnHsUosjEZ9vZMyEtXqkcVfFWkVbN0xpG'
|
||||
SSO_PUBLIC_KEY = 'm9JDP3uGPmGG9ULqPw2BY3ryBNMTGRsuukgIOhk7lIN9lT0wlr2zhoRZiNZ28UCM'
|
||||
SSO_SERVER = 'http://test-authentification.cerfig.org:89/server/'
|
||||
SSO_PRIVATE_KEY = config('SSO_PRIVATE_KEY')
|
||||
SSO_PUBLIC_KEY = config('SSO_PUBLIC_KEY')
|
||||
SSO_SERVER = config('SSO_SERVER')
|
||||
#SSO_LOGOUT_URL = 'http://test-authentification.cerfig.org:89/deconnexion'
|
||||
|
||||
# Configuration de l'email
|
||||
|
||||
20
SIRH/sso.py
20
SIRH/sso.py
@@ -10,19 +10,25 @@ class SIRHClient(Client):
|
||||
def build_user(self, user_data):
|
||||
"""Crée le User et son Profile avec les infos reçues du serveur."""
|
||||
user_data_copy = user_data.copy()
|
||||
user_data.pop("departement")
|
||||
user_data.pop("chef_departement")
|
||||
user_data.pop("groups")
|
||||
|
||||
print("Un petit test", user_data_copy)
|
||||
|
||||
user_data.pop("departement", None)
|
||||
user_data.pop("chef_departement", None)
|
||||
user_data.pop("groups", None)
|
||||
|
||||
user = super().build_user(user_data)
|
||||
departement = Departement.objects.get_or_create(
|
||||
nom=user_data_copy.get("departement", ""))[0]
|
||||
departement = None
|
||||
|
||||
if "departement" in user_data_copy:
|
||||
departement = Departement.objects.get_or_create(
|
||||
nom=user_data_copy.get("departement", ""))[0]
|
||||
|
||||
Employe.objects.update_or_create(
|
||||
user = user,
|
||||
defaults = {
|
||||
"departement": departement,
|
||||
"chef": user_data_copy.get("chef_departement", ""),
|
||||
"chef": user_data_copy.get("chef_departement", None),
|
||||
}
|
||||
)
|
||||
|
||||
@@ -30,4 +36,4 @@ class SIRHClient(Client):
|
||||
group, _ = Group.objects.get_or_create(name=group_name)
|
||||
user.groups.add(group)
|
||||
|
||||
return user
|
||||
return user
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
<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
|
||||
</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
|
||||
</a>
|
||||
</div>
|
||||
|
||||
@@ -42,7 +42,7 @@
|
||||
<i class="bi bi-calendar-check"></i> Réservation
|
||||
</a>
|
||||
{% 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
|
||||
</a>
|
||||
</div>
|
||||
|
||||
22
SIRH/urls.py
22
SIRH/urls.py
@@ -18,7 +18,7 @@ from django.contrib import admin
|
||||
from django.urls import include, path
|
||||
# from django.conf.urls.static import static
|
||||
from django.conf import settings
|
||||
|
||||
from . import views
|
||||
from .sso import SIRHClient
|
||||
|
||||
client_sirh = SIRHClient(
|
||||
@@ -28,15 +28,15 @@ client_sirh = SIRHClient(
|
||||
)
|
||||
|
||||
urlpatterns = [
|
||||
# path('login/',
|
||||
# views.login_view,
|
||||
# name='login'
|
||||
# ),
|
||||
# path(
|
||||
# 'deconnexion/',
|
||||
# views.deconnexion_view,
|
||||
# name='deconnexion'
|
||||
# ),
|
||||
path('login/',
|
||||
views.connexion,
|
||||
name='login'
|
||||
),
|
||||
path(
|
||||
'deconnexion/',
|
||||
views.deconnexion,
|
||||
name='deconnexion'
|
||||
),
|
||||
path(
|
||||
'',
|
||||
include("gestion_employe.urls")
|
||||
@@ -59,4 +59,4 @@ urlpatterns = [
|
||||
),
|
||||
path("client/", include(client_sirh.get_urls())),
|
||||
]
|
||||
# + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
|
||||
# + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
|
||||
|
||||
@@ -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
|
||||
DEBUT_RAPPEL = 60
|
||||
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):
|
||||
"""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");
|
||||
|
||||
enregistrerProfil.addEventListener("click", (e) => {
|
||||
// enregistrerProfil.addEventListener("click", (e) => {
|
||||
|
||||
const url = $("information-personnelles").dataset.url;
|
||||
const csrftoken = document.querySelector("[name='csrfmiddlewaretoken']").value;
|
||||
// const url = $("information-personnelles").dataset.url;
|
||||
// const csrftoken = document.querySelector("[name='csrfmiddlewaretoken']").value;
|
||||
|
||||
const formData = new FormData();
|
||||
// const formData = new FormData();
|
||||
|
||||
formData.append("nom", $("nom").value);
|
||||
formData.append("prenom", $("prenom").value);
|
||||
formData.append("email", $("email").value);
|
||||
formData.append("telephone", $("telephone").value);
|
||||
formData.append("adresse", $("adresse").value);
|
||||
formData.append("sexe", $("sexe").value);
|
||||
formData.append("date_naissance", $("date_naissance").value);
|
||||
// formData.append("nom", $("nom").value);
|
||||
// formData.append("prenom", $("prenom").value);
|
||||
// formData.append("email", $("email").value);
|
||||
// formData.append("telephone", $("telephone").value);
|
||||
// formData.append("adresse", $("adresse").value);
|
||||
// formData.append("sexe", $("sexe").value);
|
||||
// formData.append("date_naissance", $("date_naissance").value);
|
||||
|
||||
fetch(url, {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"X-CSRFToken": csrftoken
|
||||
},
|
||||
body: formData
|
||||
})
|
||||
.then(response => response.json())
|
||||
.then(data => alert(data.message))
|
||||
.catch(error => console.error("Erreur:", error));
|
||||
});
|
||||
|
||||
// console.log("---------------------------------------------")
|
||||
// console.log("Données du formulaire:", url);
|
||||
// console.log("Données du formulaire:", formData);
|
||||
|
||||
// fetch(url, {
|
||||
// method: "POST",
|
||||
// headers: {
|
||||
// "X-CSRFToken": csrftoken
|
||||
// },
|
||||
// body: formData
|
||||
// })
|
||||
// .then(response => response.json())
|
||||
// .then(data => alert(data.message))
|
||||
// .catch(error => console.error("Erreur:", error));
|
||||
// });
|
||||
@@ -38,7 +38,7 @@
|
||||
</h2>
|
||||
<div id="collapseProfil" class="accordion-collapse" aria-labelledby="headingContrats" data-bs-parent="#accordionInformationEmploye">
|
||||
<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 %}
|
||||
<div class="col">
|
||||
<div class="form-group mb-2">
|
||||
@@ -50,19 +50,19 @@
|
||||
</div>
|
||||
<div class="form-group mb-2">
|
||||
<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 class="form-group mb-2">
|
||||
<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 class="form-group mb-2">
|
||||
<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 class="form-group mb-2">
|
||||
<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='f' {% if employe.sexe == 'f' %}selected{% endif %}>Femme</option>
|
||||
</select>
|
||||
@@ -72,27 +72,27 @@
|
||||
<div class="col">
|
||||
<div class="form-group mb-2">
|
||||
<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 class="form-group mb-2">
|
||||
<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 class="form-group mb-2">
|
||||
<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 class="form-group mb-2">
|
||||
<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 class="form-group mb-2">
|
||||
<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>
|
||||
<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>
|
||||
|
||||
Binary file not shown.
@@ -20,6 +20,9 @@ from fonction_utilitaire import fonctions_utilitaire
|
||||
@login_required
|
||||
def index(request):
|
||||
"""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')
|
||||
nombre_employes = Employe.objects.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:
|
||||
messages.warning(
|
||||
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')
|
||||
|
||||
@@ -191,25 +194,26 @@ def modifier_employer(request):
|
||||
except Employe.DoesNotExist:
|
||||
return JsonResponse({"message": "Employé non trouvé."})
|
||||
if request.method == "POST":
|
||||
data = json.loads(request.body)
|
||||
user = User.objects.get(username=request.user)
|
||||
user.last_name = data['nom']
|
||||
user.first_name = data['prenom']
|
||||
user.email = data['email']
|
||||
employe.telephone = data['telephone']
|
||||
employe.adresse = data['adresse']
|
||||
employe.sexe = data['sexe']
|
||||
user.last_name = request.POST['nom']
|
||||
user.first_name = request.POST['prenom']
|
||||
user.email = request.POST['email']
|
||||
employe.telephone = request.POST['telephone']
|
||||
employe.adresse = request.POST['adresse']
|
||||
employe.sexe = request.POST['sexe']
|
||||
if request.FILES.get("photo"):
|
||||
employe.photo = request.FILES["photo"]
|
||||
if data['date_naissance']:
|
||||
difference = relativedelta(timezone.now().date(), datetime.strptime(data['date_naissance'], "%Y-%m-%d").date())
|
||||
if request.POST['date_naissance']:
|
||||
difference = relativedelta(timezone.now().date(), datetime.strptime(request.POST['date_naissance'], "%Y-%m-%d").date())
|
||||
if difference.years >= 18:
|
||||
employe.date_naissance = data['date_naissance']
|
||||
employe.date_naissance = request.POST['date_naissance']
|
||||
else:
|
||||
return JsonResponse({"message": "Veuillez entrez une date de naissance correcte."})
|
||||
messages.error(request, "Veuillez entrez une date de naissance correcte.")
|
||||
employe.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):
|
||||
"""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) {
|
||||
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;
|
||||
$("titreDetailActivite").value = data.titre;
|
||||
$("descriptionDetailActivite").value = data.description;
|
||||
@@ -22,14 +27,79 @@ tableau_liste_activite.on("rowClick", function (row, rowData) {
|
||||
$("date_finDetailActivite").value = data.date_fin;
|
||||
$("statutDetailActivite").value = data.statut;
|
||||
$("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;
|
||||
const modal = new bootstrap.Modal($("modalDetailActivite"));
|
||||
modal.show();
|
||||
|
||||
fetch(`liste-des-livrables/${data.id}/`)
|
||||
.then(response => response.json())
|
||||
const url_livrables = `/gestion-projet/activite/liste-des-livrables/${data.id}/`;
|
||||
console.log('URL des livrables:', url_livrables);
|
||||
|
||||
fetch(url_livrables)
|
||||
.then(response => {
|
||||
console.log('Réponse status:', response.status);
|
||||
return response.json();
|
||||
})
|
||||
.then(livrables => {
|
||||
console.log('Livrables reçus:', livrables);
|
||||
console.log('Nombre de livrables:', livrables.length);
|
||||
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é",
|
||||
})
|
||||
console.log('✓ Tableau des livrables initialisé');
|
||||
|
||||
$("btnMiseAJourDepense").addEventListener("click", function() {
|
||||
const modal = new bootstrap.Modal($("modalDepenseActivite"));
|
||||
@@ -56,4 +127,5 @@ $("btnAnnulerActivite").addEventListener("click", function(event) {
|
||||
new bootstrap.Modal($("modalAnnulerActivite")).show();
|
||||
$("id_activite_annulation").value = $("idDetailActivite").value;
|
||||
bootstrap.Modal.getOrCreateInstance($("modalDetailActivite")).hide();
|
||||
})
|
||||
})
|
||||
|
||||
|
||||
@@ -1,58 +1,111 @@
|
||||
<div class="modal fade" id="modalDetailActivite" tabindex="-1">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal fade" id="modalDetailActivite" tabindex="-1"
|
||||
aria-labelledby="modalDetailActiviteLabel" aria-hidden="true">
|
||||
<div class="modal-dialog modal-lg modal-dialog-centered">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header bg-orange-dark">
|
||||
<h5 class="modal-title">Détails de l'activité</h5>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
|
||||
<div class="modal-header bg-orange-dark text-black">
|
||||
<h5 class="modal-title" id="modalDetailActiviteLabel">
|
||||
<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 class="modal-body">
|
||||
<div class="modal-body" data-url-livrables="{% url 'gestion_projet:liste-livrables-activite' activite_id=0 %}">
|
||||
<input type="hidden" id="idDetailActivite">
|
||||
<div class="row g-3">
|
||||
<div class="">
|
||||
<input type="hidden" id="idDetailActivite">
|
||||
<div class="form-group mb-2">
|
||||
<label>Titre :</label>
|
||||
<input type="text" class="form-control" id="titreDetailActivite">
|
||||
<div class="col-12">
|
||||
<label for="titreDetailActivite" class="form-label fw-bold">
|
||||
Titre :
|
||||
</label>
|
||||
<input type="text"class="form-control" id="titreDetailActivite"readonly>
|
||||
</div>
|
||||
<div class="col-12">
|
||||
<label for="descriptionDetailActivite" class="form-label fw-bold">
|
||||
Description :
|
||||
</label>
|
||||
<textarea class="form-control"id="descriptionDetailActivite" rows="3" readonly></textarea>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<label for="date_debutDetailActivite"class="form-label fw-bold">
|
||||
Date de début :
|
||||
</label>
|
||||
<input type="date"class="form-control"id="date_debutDetailActivite"readonly>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<label for="date_finDetailActivite"class="form-label fw-bold">
|
||||
Date de fin :
|
||||
</label>
|
||||
<input type="date"class="form-control"id="date_finDetailActivite"readonly>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<label for="statutDetailActivite" class="form-label fw-bold">
|
||||
Statut :
|
||||
</label>
|
||||
<input type="text"class="form-control"id="statutDetailActivite"readonly>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<label for="budget_prevuDetailActivite"class="form-label fw-bold">
|
||||
Budget prévu :
|
||||
</label>
|
||||
<input type="number"class="form-control" id="budget_prevuDetailActivite"readonly>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<label for="budget_depenseDetailActivite"class="form-label fw-bold">
|
||||
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 class="form-group mb-2">
|
||||
<label>Description :</label>
|
||||
<textarea class="form-control" id="descriptionDetailActivite"></textarea>
|
||||
</div>
|
||||
<div class="form-group mb-2">
|
||||
<label>Date de début :</label>
|
||||
<input type="date" class="form-control" id="date_debutDetailActivite">
|
||||
</div>
|
||||
<div class="form-group mb-2">
|
||||
<label>Date de fin :</label>
|
||||
<input type="date" class="form-control" id="date_finDetailActivite">
|
||||
</div>
|
||||
<div class="form-group mb-2">
|
||||
<label>Statut:</label>
|
||||
<input type="text" class="form-control" id="statutDetailActivite">
|
||||
</div>
|
||||
<div class="form-group mb-2">
|
||||
<label>Budget prévu :</label>
|
||||
<input type="number" class="form-control" id="budget_prevuDetailActivite">
|
||||
</div>
|
||||
<div class="form-group mb-2">
|
||||
<label>Besoin de ressources matérielles:</label>
|
||||
<textarea class="form-control" id="besoin_ressources_materiellesDetailActivite"></textarea>
|
||||
</div>
|
||||
<p><strong>Liste des livrables :</strong></p>
|
||||
<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é
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button class="btn btn-success" id="btnMiseAJourDepense">
|
||||
<i class="bi bi-plus-circle"></i> Mise à jour du budget
|
||||
<button type="button"class="btn btn-success"id="btnMiseAJourDepense">
|
||||
<i class="bi bi-cash-stack me-2"></i>
|
||||
Mise à jour du budget
|
||||
</button>
|
||||
<button class="btn btn-primary" data-bs-toggle="modal" data-bs-target="#modalAjouterLivrable">
|
||||
<i class="bi bi-plus-circle"></i> Ajouter un livrable
|
||||
<button type="button"class="btn btn-primary"id="btnOuvrirAjouterLivrable"data-bs-toggle="modal"
|
||||
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>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
@@ -381,15 +381,32 @@ def liste_activites_projet(request):
|
||||
@login_required
|
||||
def liste_livrables_activite(request, activite_id):
|
||||
"""Vue pour retourner la liste des livrables attendus d'une activité spécifique"""
|
||||
livrables = LivrablesLivres.objects.filter(activite__id=activite_id)
|
||||
data = []
|
||||
for livrable in livrables:
|
||||
print(livrable.fichier.url)
|
||||
data.append({
|
||||
"titre": livrable.nom,
|
||||
"lien": livrable.fichier.url if livrable.fichier else "",
|
||||
})
|
||||
return JsonResponse(data, safe=False)
|
||||
try:
|
||||
livrables = LivrablesLivres.objects.filter(activite__id=activite_id)
|
||||
data = []
|
||||
|
||||
for livrable in livrables:
|
||||
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({
|
||||
"titre": livrable.nom,
|
||||
"lien": lien,
|
||||
})
|
||||
|
||||
print(f"[DEBUG] ✓ Livrables activité {activite_id}: {len(data)} trouvés")
|
||||
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
|
||||
def mises_a_jour_depense_activite(request):
|
||||
|
||||
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