Mon commit initial

This commit is contained in:
2025-12-19 09:43:33 +00:00
commit f11d1e62b4
336 changed files with 6247 additions and 0 deletions

3
.gitignore vendored Normal file
View File

@@ -0,0 +1,3 @@
.ruff_cache/
.coverage
db.sqlite3

0
SGL/__init__.py Normal file
View File

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

16
SGL/asgi.py Normal file
View File

@@ -0,0 +1,16 @@
"""
ASGI config for SGL project.
It exposes the ASGI callable as a module-level variable named ``application``.
For more information on this file, see
https://docs.djangoproject.com/en/5.2/howto/deployment/asgi/
"""
import os
from django.core.asgi import get_asgi_application
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "SGL.settings")
application = get_asgi_application()

147
SGL/settings.py Normal file
View File

@@ -0,0 +1,147 @@
"""
Django settings for SGL project.
Generated by 'django-admin startproject' using Django 5.2.6.
For more information on this file, see
https://docs.djangoproject.com/en/5.2/topics/settings/
For the full list of settings and their values, see
https://docs.djangoproject.com/en/5.2/ref/settings/
"""
from pathlib import Path
from os.path import join
# Build paths inside the project like this: BASE_DIR / 'subdir'.
BASE_DIR = Path(__file__).resolve().parent.parent
# Quick-start development settings - unsuitable for production
# See https://docs.djangoproject.com/en/5.2/howto/deployment/checklist/
# SECURITY WARNING: keep the secret key used in production secret!
SECRET_KEY = "django-insecure-3mmf81^pt+k6-^dj$n2&*kvf6m09n5i%*+ajv70(ynaxifj5fg"
# SECURITY WARNING: don't run with debug turned on in production!
DEBUG = True
ALLOWED_HOSTS = []
# Application definition
INSTALLED_APPS = [
"django.contrib.admin",
"django.contrib.auth",
"django.contrib.contenttypes",
"django.contrib.sessions",
"django.contrib.messages",
"django.contrib.staticfiles",
"gestion_produit",
"gestion_fournisseur",
"gestion_vehicule",
"authentification",
]
MIDDLEWARE = [
"django.middleware.security.SecurityMiddleware",
"django.contrib.sessions.middleware.SessionMiddleware",
"django.middleware.common.CommonMiddleware",
"django.middleware.csrf.CsrfViewMiddleware",
"django.contrib.auth.middleware.AuthenticationMiddleware",
"django.contrib.messages.middleware.MessageMiddleware",
"django.middleware.clickjacking.XFrameOptionsMiddleware",
]
ROOT_URLCONF = "SGL.urls"
TEMPLATES = [
{
"BACKEND": "django.template.backends.django.DjangoTemplates",
"DIRS": [join(BASE_DIR, "SGL", "templates")],
"APP_DIRS": True,
"OPTIONS": {
"context_processors": [
"django.template.context_processors.request",
"django.contrib.auth.context_processors.auth",
"django.contrib.messages.context_processors.messages",
],
},
},
]
WSGI_APPLICATION = "SGL.wsgi.application"
# Database
# https://docs.djangoproject.com/en/5.2/ref/settings/#databases
DATABASES = {
"default": {
"ENGINE": "django.db.backends.sqlite3",
"NAME": BASE_DIR / "db.sqlite3",
}
}
# Password validation
# https://docs.djangoproject.com/en/5.2/ref/settings/#auth-password-validators
AUTH_PASSWORD_VALIDATORS = [
{
"NAME": "django.contrib.auth.password_validation.UserAttributeSimilarityValidator",
},
{
"NAME": "django.contrib.auth.password_validation.MinimumLengthValidator",
},
{
"NAME": "django.contrib.auth.password_validation.CommonPasswordValidator",
},
{
"NAME": "django.contrib.auth.password_validation.NumericPasswordValidator",
},
]
# Internationalization
# https://docs.djangoproject.com/en/5.2/topics/i18n/
LANGUAGE_CODE = "en-us"
TIME_ZONE = "UTC"
USE_I18N = True
USE_TZ = True
# Static files (CSS, JavaScript, Images)
# https://docs.djangoproject.com/en/5.2/howto/static-files/
STATIC_URL = "static/"
STATICFILES_DIRS = [
BASE_DIR / "static",
]
# Default primary key field type
# https://docs.djangoproject.com/en/5.2/ref/settings/#default-auto-field
DEFAULT_AUTO_FIELD = "django.db.models.BigAutoField"
MEDIA_URL = "/media/"
MEDIA_ROOT = BASE_DIR / "media"
# Gestion de l'envoi des mails
EMAIL_BACKEND = "django.core.mail.backends.smtp.EmailBackend"
EMAIL_HOST = "ssl0.ovh.net"
EMAIL_PORT = 465
EMAIL_USE_SSL = False
EMAIL_USE_TLS = False
EMAIL_HOST_USER = "support.it@cerfig.org"
EMAIL_HOST_PASSWORD = "Cerfig2025"
DEFAULT_FROM_EMAIL = EMAIL_HOST_USER
EMAIL_TIMEOUT = 10
# ALLOWED_HOSTS = ['support.cerfig.org', '41.223.50.187', 'localhost', '127.0.0.1']

114
SGL/templates/BASE.html Normal file
View File

@@ -0,0 +1,114 @@
{% load static %}
{% load tags_personnaliser %}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="{% static 'css/bootstrap.min.css' %}">
<link rel="stylesheet" href="{% static 'css/tabulator_bootstrap5.min.css' %}">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.13.1/font/bootstrap-icons.min.css">
<title>{% block page_titre %} Tableau de bord {% endblock %}</title>
</head>
<body>
<div class="container-fluid">
<div class="row vh-100">
<div class="col-3 bg-danger d-none d-lg-flex flex-column">
{% if user|has_group:"Administration" %}
<a href="{% url 'index' %}" class="btn btn-danger py-3 mt-4 fw-bold d-flex" style="font-size: 1.2em;">
<i class="bi bi-file-bar-graph d-block" style="width: 50px;"></i>
Tableau de bord
</a>
{% endif %}
{% if user|has_group:"Employe" %}
<a href="{% url 'gestion_produit:index' %}" class="btn btn-danger py-3 mt-1 fw-bold d-flex" style="font-size: 1.2em;">
<i class="bi bi-backpack4 d-block" style="width: 50px;"></i>
Gestion des produits
</a>
{% endif %}
{% if user|has_group:"Administration" %}
<a href="{% url 'gestion_fournisseur:index' %}" class="btn btn-danger py-3 mt-1 fw-bold d-flex" style="font-size: 1.2em;">
<i class="bi bi-truck d-block" style="width: 50px;"></i>
Gestion des fournisseurs
</a>
{% endif %}
{% if user|has_group:"Employe" %}
<a href="{% url 'gestion_vehicule:index' %}" class="btn btn-danger py-3 mt-1 fw-bold d-flex" style="font-size: 1.2em;">
<i class="bi bi-car-front-fill d-block" style="width: 50px;"></i>
Gestion des véhicules
</a>
{% endif %}
<a href="{% url 'authentification:deconnexion' %}" class="btn btn-danger py-3 mt-1 fw-bold d-flex" style="font-size: 1.2em;">
<i class="bi bi-person-dash d-block" style="width: 50px;"></i>
Deconnexion
</a>
</div>
<div class="col-12 col-lg-9" style="overflow-y: auto;">
<!-- --------------------------------------------------- Navigation mobile -->
<nav class="navbar navbar-danger bg-danger d-lg-none">
<div class="container-fluid">
<a class="navbar-brand" href="#">CERFIG</a>
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarTogglerDemo01" aria-controls="navbarTogglerDemo01" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarTogglerDemo01">
<!-- <a class="navbar-brand" href="#">Hidden brand</a> -->
<ul class="navbar-nav me-auto mb-2 mb-lg-0">
{% if user|has_group:"Administration" %}
<li class="nav-item">
<a class="nav-link d-flex text-white fw-bold" aria-current="page" href="{% url 'index' %}">
<i class="bi bi-backpack4 d-block" style="width: 50px;"></i>
Tableau de bord
</a>
</li>
{% endif %}
{% if user|has_group:"Employe" %}
<li class="nav-item">
<a class="nav-link d-flex text-white fw-bold" href="{% url 'gestion_produit:index' %}">
<i class="bi bi-backpack4 d-block" style="width: 50px;"></i>
Gestion des produits
</a>
</li>
{% endif %}
{% if user|has_group:"Administration" %}
<li class="nav-item">
<a class="nav-link d-flex text-white fw-bold" href="{% url 'gestion_fournisseur:index' %}" tabindex="-1" aria-disabled="true">
<i class="bi bi-truck d-block" style="width: 50px;"></i>
Gestion des fournisseurs
</a>
</li>
{% endif %}
{% if user|has_group:"Employe" %}
<li class="nav-item">
<a class="nav-link d-flex text-white fw-bold" href="{% url 'gestion_vehicule:index' %}" tabindex="-1" aria-disabled="true">
<i class="bi bi-truck d-block" style="width: 50px;"></i>
Gestion des véhicules
</a>
</li>
{% endif %}
<li class="nav-item">
<a class="nav-link d-flex text-white fw-bold" href="{% url 'authentification:deconnexion' %}" tabindex="-1" aria-disabled="true">
<i class="bi bi-person-dash d-block" style="width: 50px;"></i>
Deconnexion
</a>
</li>
</ul>
</div>
</div>
</nav>
<!-- ------------------------------------------------------ -->
{% block "contenu" %} {% endblock %}
</div>
</div>
</div>
<script src="{% static 'js\tabulator.min.js' %}"></script>
<script src="{% static 'js\bootstrap.min.js' %}"></script>
{% block "js" %} {% endblock %}
</body>
</html>

View File

@@ -0,0 +1,20 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Message test</title>
</head>
<body>
<div style="background-color: aliceblue; min-height: 100vh; width: 100%; display: flex; justify-content: center;">
<div style="background-color: white; width: 80%; margin-top: 10px; border-radius: 15px; padding: 10px;">
<img src="téléchargement.png" alt="Logo cerfig" style="display: block; margin: auto;">
<h1 style="text-align: center;">Réception de nouveau produit</h1>
<p>
Bonjour M. Sylla Soriba, la livraison de nouveaux ordinateurs au CERFIG a été effectuée
avec succès. Merci à vous
</p>
</div>
</div>
</body>
</html>

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.7 KiB

206
SGL/templates/index.html Normal file
View File

@@ -0,0 +1,206 @@
{% extends "BASE.html" %}
{% load static %}
{% load tags_personnaliser %}
{% block page_titre %} Tableau de bord {% endblock %}
{% block "contenu" %}
<div class="row p-2 d-flex justify-content-around">
<div class="col-5 col-lg-3 border border-3 rounded border-danger p-2 d-flex" style="cursor: pointer;" id="produit_rupture">
<img src="{% static 'images/stock-rupture.png' %}" alt="" style="width: 30%; height: 75%;">
<div class="d-flex justify-content-center flex-column ms-1">
<h3 class="text-center fw-bold">Rupture</h3>
<p class="text-center">{{ nombre_rupture }} produits</p>
</div>
</div>
<div class="col-5 col-lg-3 border border-3 rounded border-warning p-2 d-flex" style="cursor: pointer;" id="produit_pre_rupture">
<img src="{% static 'images/stock-prerupture.png' %}" alt="" style="width: 30%; height: 75%;">
<div class="d-flex justify-content-center flex-column ms-1">
<h3 class="text-center fw-bold">Pré-rupture</h3>
<p class="text-center">{{ nombre_pre_rupture }} produits</p>
</div>
</div>
<div class="col-5 col-lg-3 mt-2 mt-lg-0 border border-3 rounded border-success p-2 d-flex" style="cursor: pointer;" id="produit_normal">
<img src="{% static 'images/stock-bon.png' %}" alt="" style="width: 30%; height: 75%;">
<div class="d-flex justify-content-center flex-column ms-1">
<h3 class="text-center fw-bold">Correct</h3>
<p class="text-center">{{ nombre_normal }} produits</p>
</div>
</div>
</div>
<button class="btn btn-primary m-auto d-block" data-bs-toggle="modal" data-bs-target="#aRendreModal">
<i class="bi bi-eye"></i>
Equipement à rendre ...
</button>
<div class="row mt-5 d-flex flex-column flex-lg-row">
<div class="col-12 col-lg-6">
<h5 class="text-center">Demandes de produit ({{ nb_demande }})</h5>
<div id="demande_produit" data-url="{% url 'demande_en_cours' %}"></div>
</div>
<div class="col-12 col-lg-6 mt-3 mt-lg-0">
<h5 class="text-center">Commandes en attentes ({{ nb_commande }})</h5>
<div id="commande_attentes" data-url="{% url 'commande_en_cours' %}"></div>
</div>
</div>
<!-- ------------- Modal pour la liste des demandes en cours ---------------------- -->
<div class="modal fade" id="listeDemandesModal" tabindex="-1" aria-labelledby="listeDemandesModalLabel" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h1 class="modal-title fs-5" id="listeDemandesModalLabel">Demande de produit</h1>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
<form action="">
<div class="mb-3">
<label for="produit" class="form-label">Demandeur :</label>
<input type="text" class="form-control" id="demandeur" readonly>
</div>
<div class="mb-3">
<label for="produit" class="form-label">Produit</label>
<input type="text" class="form-control" id="produit" readonly>
</div>
<div class="mb-3">
<label for="quantite" class="form-label">Quantité</label>
<input type="number" class="form-control" id="quantite" readonly>
</div>
<div class="mb-3">
<label for="motifs" class="form-label">Motifs</label>
<textarea class="form-control" id="motifs" rows="3" readonly></textarea>
</div>
<div class="mb-3">
<label for="date_demande" class="form-label">Date de mise à disposition souhaitée :</label>
<input type="date" class="form-control" id="date_demande" readonly>
</div>
<div class="mb-3">
<label for="a_rendre" class="form-label">À rendre ?</label>
<input type="checkbox" class="form-check-input" id="a_rendre" disabled>
</div>
<div class="mb-3">
<label for="">Valider par le supérieur hiérarchique</label>
<input type="checkbox" class="form-check-input" id="valider_par_chef" disabled>
</div>
<hr>
<h6 class="text-center">Décision de la logistique</h6>
<div class="d-flex justify-content-around mb-3">
<div class="form-check">
<input class="form-check-input" type="radio" name="radioDefault" id="radioValider">
<label class="form-check-label" for="radioValider">
Valider la demande
</label>
</div>
<div class="form-check">
<input class="form-check-input" type="radio" name="radioDefault" id="radioRefuser" data-url="{% url 'gestion_produit:update-demande' %}">
<label class="form-check-label" for="radioRefuser">
Refuser la demande
</label>
</div>
</div>
<div class="mb-3 d-none" id="box-motif-refus">
<label for="motifs_refus" class="form-label">Motifs de refus</label>
<textarea class="form-control" id="motifs_refus" rows="3"></textarea>
</div>
</form>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Fermer</button>
<button type="button" class="btn btn-primary" id="enregistrerModifs">Enregistrer les modifications</button>
</div>
</div>
</div>
</div>
<!-- ------------- Modal pour la liste des commandes en cours ---------------------- -->
<div class="modal fade" id="listeCommandesModal" tabindex="-1" aria-labelledby="listeCommandesModalLabel" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h1 class="modal-title fs-5" id="listeCommandesModalLabel">Commande de produit</h1>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
<form action="">
<div class="mb-3">
<label for="produit" class="form-label">Produit</label>
<input type="text" class="form-control" id="produitCommande" readonly>
</div>
<div class="mb-3">
<label for="quantite" class="form-label">Fournisseur :</label>
<input type="text" class="form-control" id="fournisseur" readonly>
</div>
<div class="mb-3">
<label for="motifs" class="form-label">Quantité</label>
<input class="form-control" id="quantiteCommande" readonly></input>
</div>
<div class="mb-3">
<label for="date_commande" class="form-label">Date de commande</label>
<input type="date" class="form-control" id="date_commande" readonly>
</div>
<div class="mb-3">
<label for="date_livraison" class="form-label">Date de Livraison</label>
<input type="date" class="form-control" id="date_livraison" readonly>
</div>
<hr>
<h6 class="text-center">Confirmation de la livraison du produit</h6>
<div class="form-check form-switch">
<input class="form-check-input" type="checkbox" role="switch" id="produit_livre">
<label class="form-check-label" for="produit_livre">Le produit a été livré</label>
</div>
</form>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Fermer</button>
<button type="button" class="btn btn-primary" id="enregistrerModifsCommande" data-url="{% url 'enregistrer_livraison_produit' %}">Enregistrer les modifications</button>
</div>
</div>
</div>
</div>
<!-- ------------------------------------- Modal pour stock en rupture ------------------------------ -->
<div class="modal fade" id="produitRuptureModal" tabindex="-1" aria-labelledby="produitRuptureModalLabel" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h1 class="modal-title fs-5" id="produitRuptureModalLabel">Produit en rupture</h1>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
<div id="tableau-produit-rupture" data-url="{% url 'listeProduitRupture' %}"></div>
</div>
</div>
</div>
</div>
<!-- ----------------------------------- Modal équipement à rendre -------------------------------- -->
<div class="modal fade" id="aRendreModal" tabindex="-1" aria-labelledby="aRendreModalLabel">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h1 class="modal-title fs-5" id="aRendreModalLabel">
Equipement à rendre
</h1>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
<div id="tableau-equipement-rendre" data-url="{% url 'equipement-a-rendre' %}"></div>
</div>
{% if user|has_group:"Logisticien" %}
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Fermer</button>
<button type="button" class="btn btn-primary" id="equipementARendreEnregistrer" data-url="{% url 'update-equipement-a-rendre' %}">Enregistrer</button>
</div>
{% endif %}
</div>
</div>
</div>
{% endblock %}
{% block "js" %}
<script src="{% static 'js\index.js' %}"></script>
{% endblock %}

68
SGL/urls.py Normal file
View File

@@ -0,0 +1,68 @@
"""
URL configuration for SGL project.
The `urlpatterns` list routes URLs to views. For more information please see:
https://docs.djangoproject.com/en/5.2/topics/http/urls/
Examples:
Function views
1. Add an import: from my_app import views
2. Add a URL to urlpatterns: path('', views.home, name='home')
Class-based views
1. Add an import: from other_app.views import Home
2. Add a URL to urlpatterns: path('', Home.as_view(), name='home')
Including another URLconf
1. Import the include() function: from django.urls import include, path
2. Add a URL to urlpatterns: path('blog/', include('blog.urls'))
""" #noqa : D212
from django.contrib import admin
from django.urls import path, include
from django.conf import settings
from django.conf.urls.static import static
from . import views
urlpatterns = [
path("", views.index, name="index"),
path("gestion-produit/", include("gestion_produit.urls")),
path("gestion-fournisseur/", include("gestion_fournisseur.urls")),
path("gestion-vehicule/", include("gestion_vehicule.urls")),
path("authentification/", include("authentification.urls")),
path(
"liste-commande-en-cours/",
views.liste_produit_commander,
name="commande_en_cours",
),
path(
"liste-demande-en-cours/",
views.liste_demandes,
name="demande_en_cours"
),
path(
"enregistrer-livraison-produit/",
views.enregistrer_livraison_produit,
name="enregistrer_livraison_produit",
),
path(
"admin/",
admin.site.urls
),
path(
"dashboard-produit/",
views.liste_produit_rupture,
name="listeProduitRupture"
),
path(
"equipement-a-rendre/",
views.produit_a_rendre,
name="equipement-a-rendre"
),
path(
"update-equipement-a-rendre/",
views.produit_a_rendre_update,
name="update-equipement-a-rendre",
),
] + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)

334
SGL/views.py Normal file
View File

@@ -0,0 +1,334 @@
"""Fichier views.py de gestion de la page dashboard de l'application SGL.
Principales fonctionnalités couvertes :
1- Gestion des stocks des produits (rupture, pré-rupture, normal).
2- Affichage des commandes en cours et validations des dites commandes
3- Affichage des demandes en cours et validations des dites demandes.
"""
from django.shortcuts import render, redirect
from django.http import JsonResponse
from gestion_produit.models import Commander, Demander, Produit
from django.views.decorators.csrf import csrf_exempt
from django.contrib.auth.decorators import login_required
import json
from gestion_produit.views import calcul_stock_produit
def dashboard_quantite_produit():
"""Calculez les quantités des produits en rupture, pré-rupture et normal.
Returns
-------
dict
Dictionnaire contenant les nombres et les listes des produits par
catégorie.
"""
tous_les_produits = Produit.objects.all()
nombre_rupture = 0
nombre_pre_rupture = 0
nombre_normal = 0
produit_rupture = []
produit_pre_rupture = []
produit_normal = []
for produit in tous_les_produits:
stock_actuel = calcul_stock_produit(produit)
stock_actuel.pop("image")
if stock_actuel["stockActuel"] == 0:
nombre_rupture += 1
produit_rupture.append(stock_actuel)
elif stock_actuel["stockActuel"] < produit.seuil_pre_rupture:
nombre_pre_rupture += 1
produit_pre_rupture.append(stock_actuel)
else:
nombre_normal += 1
produit_normal.append(stock_actuel)
return {
"nombre_rupture": nombre_rupture,
"nombre_pre_rupture": nombre_pre_rupture,
"nombre_normal": nombre_normal,
"produit_rupture": produit_rupture,
"produit_pre_rupture": produit_pre_rupture,
"produit_normale": produit_normal,
}
@login_required(login_url="authentification:login")
def index(request):
"""Vue pour la page d'accueil du dashboard.
Parameters
----------
request : HttpRequest
La requête HTTP entrante.
Returns
-------
HttpResponse
La réponse HTTP contenant la page d'accueil du dashboard.
"""
if request.user.groups.filter(name="Chauffeur").exists():
return redirect("gestion_vehicule:index")
if not request.user.groups.filter(name="Administration").exists():
return redirect("gestion_produit:index")
commandes_en_cours = Commander.objects.filter(livrer=False)
demandes_en_cours = Demander.objects.filter(
valider_par_chef=True, valider_par_logisticien=None
)
gestion_quantite_produit = dashboard_quantite_produit()
nombre_rupture = gestion_quantite_produit["nombre_rupture"]
nombre_pre_rupture = gestion_quantite_produit["nombre_pre_rupture"]
nombre_normal = gestion_quantite_produit["nombre_normal"]
return render(
request,
"index.html",
{
"nb_commande": len(commandes_en_cours),
"nb_demande": len(demandes_en_cours),
"nombre_rupture": nombre_rupture,
"nombre_pre_rupture": nombre_pre_rupture,
"nombre_normal": nombre_normal,
},
)
@login_required(login_url="login")
def produit_a_rendre(request): #noqa: ARG001
"""Vue pour obtenir la liste des équipements à rendre.
Cette vue est appelée via une requête AJAX et renvoie une liste
d'équipements qui doivent être rendus.
Parameters
----------
request : HttpRequest
La requête HTTP entrante.
Returns
-------
JsonResponse
Une réponse JSON contenant la liste des équipements à rendre.
"""
equipement_a_rendre = Demander.objects.filter(a_rendre=True, rendu=False)
liste_equipement = [
{
"id_demande": equipement.id,
"produit": equipement.produit.libelle,
"quantite": equipement.quantite,
"detenteur": (
f"{equipement.user.user.first_name} "
f"{equipement.user.user.last_name}"
),
"action": equipement.rendu,
}
for equipement in equipement_a_rendre
]
return JsonResponse(liste_equipement, safe=False)
@login_required(login_url="login")
@csrf_exempt
def produit_a_rendre_update(request):
"""Vue pour mettre à jour le statut des équipements à rendre.
Lorsqu'une requête POST est reçue, cette vue met à jour le statut
des équipements à rendre en fonction des données fournies dans la
requête.
Parameters
----------
request : HttpRequest
request.id_demande : int
L'identifiant de la demande d'équipement à mettre à jour.
request.action : bool
Le nouveau statut de rendu de l'équipement.
Returns
-------
JsonResponse
Une réponse JSON indiquant le succès de l'opération.
"""
if request.method == "POST":
data = json.loads(request.body)
for d in data:
demande = Demander.objects.get(id=d["id_demande"])
demande.rendu = d["action"]
demande.save()
return JsonResponse(
{"status": "success", "message": "Produits mis à jour"}
)
@login_required(login_url="login")
def liste_produit_rupture(request):
"""Vue pour obtenir la liste des produits en fonction de leur type.
Cette vue est appelée via une requête AJAX à chaque fois que l'on clique
sur un cadran (rupture, pré-rupture, en stock normal) et renvoie une liste
de produits en fonction du type spécifié dans les paramètres de la
requête.
Parameters
----------
request : HttpRequest
request.type : str
Le statut de produits à récupérer qui sont en "rupture",
"pre_rupture" ou "normal".
Returns
-------
JsonResponse
Une réponse JSON contenant la liste des produits correspondant
au type spécifié.
"""
type_encadrer = request.GET["type"]
dashboard_gestion = dashboard_quantite_produit()
donnees_charger = []
if type_encadrer == "rupture":
donnees_charger = dashboard_gestion["produit_rupture"]
elif type_encadrer == "pre_rupture":
donnees_charger = dashboard_gestion["produit_pre_rupture"]
elif type_encadrer == "normal":
donnees_charger = dashboard_gestion["produit_normale"]
return JsonResponse(donnees_charger, safe=False)
@login_required(login_url="login")
def liste_produit_commander(request):
"""Listez des produits commandés non encore livrés.
Parameters
----------
request : HttpRequest
La requête HTTP entrante.
Returns
-------
JsonResponse
Une réponse JSON contenant la liste des produits commandés
non encore livrés.
"""
commandes_en_cours = Commander.objects.filter(livrer=False)
ma_liste_commandes = [
{
"id_commande": commande.id,
"produit": commande.produit.libelle,
"quantite": commande.quantite_commander,
"date_commande": commande.date_commande,
"date_livraison": commande.date_livraison,
"fournisseur": commande.fournisseur.nom_entreprise,
"is_logisticien": (
request.user.profile.departement.
nom_departement == "Logistique"
),
}
for commande in commandes_en_cours
]
return JsonResponse(ma_liste_commandes, safe=False)
@login_required(login_url="login")
@csrf_exempt
def enregistrer_livraison_produit(request):
"""Enregistrez la livraison d'un produit commandé.
Lorsqu'une requête POST est reçue, cette vue met à jour le statut
de livraison du produit commandé en fonction de l'identifiant de la
commande fourni dans la requête.
Parameters
----------
request : HttpRequest
request.id_commande : int
L'identifiant de la commande dont la livraison doit
être enregistrée.
Returns
-------
JsonResponse
Une réponse JSON indiquant le succès ou l'échec de l'opération.
"""
if request.method == "POST":
id_commande = request.POST.get("id_commande")
try:
commande = Commander.objects.get(id=id_commande)
commande.livrer = True
commande.save()
return JsonResponse(
{
"status": "success",
"message": "Livraison enregistrée avec succès."
}
)
except Commander.DoesNotExist:
return JsonResponse(
{"status": "error", "message": "Commande non trouvée."}
)
return JsonResponse(
{"status": "error", "message": "Méthode non autorisée."}
)
@login_required(login_url="login")
def liste_demandes(request):
"""Listez des demandes en cours de validation.
Parameters
----------
request : HttpRequest
La requête HTTP entrante.
Returns
-------
JsonResponse
Une réponse JSON contenant la liste des demandes en cours
de validation.
"""
demandes_en_cours = Demander.objects.filter(
valider_par_chef=True, valider_par_logisticien=None
)
is_logisticien = (
request.user.profile.departement.nom_departement == "Logistique"
)
ma_liste_demandes = [
{
"id_demande": demande.id,
"demandeur": (
f"{demande.user.user.first_name} "
f"{demande.user.user.last_name}"
),
"produit": demande.produit.libelle,
"quantite": demande.quantite,
"motifs": demande.motifs,
"date_de_mise_a_dispo_souhaite": demande.date_de_mise_a_dispo_souhaite.strftime("%Y-%m-%d"),
"a_rendre": demande.a_rendre,
"valider_par_chef": demande.valider_par_chef,
"valider_par_logisticien": demande.valider_par_logisticien,
"is_logisticien": is_logisticien,
}
for demande in demandes_en_cours
]
return JsonResponse(ma_liste_demandes, safe=False)

16
SGL/wsgi.py Normal file
View File

@@ -0,0 +1,16 @@
"""
WSGI config for SGL 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", "SGL.settings")
application = get_wsgi_application()

View File

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

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

6
authentification/apps.py Normal file
View File

@@ -0,0 +1,6 @@
from django.apps import AppConfig
class AuthentificationConfig(AppConfig):
default_auto_field = "django.db.models.BigAutoField"
name = "authentification"

View File

@@ -0,0 +1,33 @@
# Generated by Django 5.2.7 on 2025-11-11 07:12
import django.db.models.deletion
from django.conf import settings
from django.db import migrations, models
class Migration(migrations.Migration):
initial = True
dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
]
operations = [
migrations.CreateModel(
name='Departement',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('nom_departement', models.TextField(verbose_name='Nom du département :')),
],
),
migrations.CreateModel(
name='Profile',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('chef_departement', models.BooleanField(verbose_name='Cet utilisateur est-il chef de ce département ?')),
('departement', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='authentification.departement')),
('user', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
],
),
]

View File

View File

@@ -0,0 +1,39 @@
"""Modèles pour l'authentification et la gestion des profils utilisateurs."""
from django.db import models
from django.contrib.auth.models import User
# Create your models here.
class Departement(models.Model):
"""Modèle représentant un département."""
nom_departement = models.TextField(verbose_name="Nom du département :")
def __str__(self):
"""Retourne une représentation en chaîne de caractères du dép."""
return self.nom_departement
class Profile(models.Model):
"""Modèle représentant le profil utilisateur.
Il étend le modèle User de Django avec des informations supplémentaires.
En y rajoutant notamment le département de l'utilisateur et s'il en est le
chef.
"""
user = models.OneToOneField(User, on_delete=models.CASCADE)
departement = models.ForeignKey(
Departement, on_delete=models.SET_NULL, null=True, blank=True
)
chef_departement = models.BooleanField(
verbose_name="Cet utilisateur est-il chef de ce département ?"
)
def __str__(self):
"""Retourne une représentation en chaîne de caractères du profil."""
return (
f"{self.user.first_name} {self.user.last_name} "
f"({self.departement.nom_departement})"
)

View File

@@ -0,0 +1,40 @@
{% load static %}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="{% static 'css/bootstrap.min.css' %}">
<title>Connexion: SGL</title>
</head>
<body>
<div class="container-fluid">
<div class="row d-flex justify-content-center align-items-center vh-100 mt-4">
<div class="col-11 col-lg-5 border rounded p-2">
{% if messages %}
{% for message in messages %}
<div class="alert alert-danger">{{ message }}</div>
{% endfor %}
{% endif %}
<div style="height: 30px;">
<h4 class="text-center">Connexion</h4>
</div>
<form action="{% url 'authentification:login' %}" method="post">
{% csrf_token %}
<div class="form-group mt-2">
<label for="username">Nom d'utilisateur :</label>
<input type="text" class="form-control" name="username">
</div>
<div class="form-group mt-2">
<label for="password">Mot de passe :</label>
<input type="password" class="form-control" name="password">
</div>
<button type="submit" class="btn btn-primary mt-3 d-block m-auto">Connexion</button>
</form>
</div>
</div>
</div>
</body>
</html>

View File

@@ -0,0 +1,29 @@
from django import template
from authentification.models import Profile
register = template.Library()
@register.filter
def has_group(user, group_name):
return user.groups.filter(name=group_name).exists()
# On recupère l'utilisateur et le produit
## Si l'utilisateur n'est pas le chef d'un département, afficher simplement ses demandes
## Si l'utilisateur est un chef de département
### Afficher la liste de ses demandes
### Verifier pour chaque demande que l'utilisateur appartient à son departement
#### Si oui, afficher le produit
#### Si non, ne pas afficher le produit
@register.filter
def affichage_demande(user, demande):
if user.profile.chef_departement:
chef_profile = Profile.objects.get(id=user.profile)
departement = chef_profile.departement
ids_profils_departement = Profile.objects.filter(departement=departement)["id"]
return demande.user_id in ids_profils_departement
return user.profile.id == demande.user_id

View File

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

11
authentification/urls.py Normal file
View File

@@ -0,0 +1,11 @@
"""Routes pour l'authentification des utilisateurs."""
from django.urls import path
from . import views
app_name = "authentification"
urlpatterns = [
path("connexion", views.connexion, name="login"),
path("deconnexion", views.deconnexion, name="deconnexion"),
]

34
authentification/views.py Normal file
View File

@@ -0,0 +1,34 @@
"""Vues pour l'authentification des utilisateurs."""
from django.shortcuts import render, redirect
from django.contrib.auth import authenticate, login, logout
from django.contrib import messages
from SGL.views import index
def connexion(request):
"""Gérez la connexion des utilisateurs."""
authentification_echoue = False
if request.method == "POST":
username = request.POST["username"]
password = request.POST["password"]
user = authenticate(username=username, password=password)
if user is not None:
login(request, user)
return redirect(index)
messages.error(request, "Nom d'utilisateur ou mot de passe incorrect.")
return render(
request,
"authentification/login.html",
{"authentification_echoue": authentification_echoue},
)
def deconnexion(request):
"""Gérez la déconnexion des utilisateurs."""
logout(request)
return redirect("authentification:login")

View File

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

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

View File

@@ -0,0 +1,6 @@
from django.apps import AppConfig
class GestionFournisseurConfig(AppConfig):
default_auto_field = "django.db.models.BigAutoField"
name = "gestion_fournisseur"

View File

@@ -0,0 +1,34 @@
"""Gérez les formulaires pour la gestion des fournisseurs."""
from django import forms
from gestion_produit.models import Fournisseur
class FournisseurForm(forms.ModelForm):
"""Formulaire pour le modèle Fournisseur."""
class Meta:
"""Métadonnées pour le formulaire FournisseurForm."""
model = Fournisseur
fields = (
"nom_entreprise",
"nom_prenom_representant",
"fonction",
"telephone",
"email",
"categorie",
"rccm",
"nifp",
)
widgets = {
"nom_entreprise": forms.TextInput(attrs={"class": "form-control"}),
"nom_prenom_representant": forms.TextInput(
attrs={"class": "form-control"}
),
"fonction": forms.TextInput(attrs={"class": "form-control"}),
"telephone": forms.TextInput(attrs={"class": "form-control"}),
"email": forms.EmailInput(attrs={"class": "form-control"}),
# "note": forms.NumberInput(attrs={"class": "form-control"}),
"categorie": forms.SelectMultiple(attrs={"class": "form-select"}),
}

View File

@@ -0,0 +1,7 @@
"""Gérez les modèles pour la gestion des fournisseurs.
Les modèles définis ici ont été déplacés vers gestion_produit.models.py
pour éviter les dépendances circulaires.
"""
# Create your models here.

View File

@@ -0,0 +1,82 @@
{% extends "BASE.html" %}
{% load static %}
{% load tags_personnaliser %}
{% block page_titre %} SGL - Gestion des fournisseurs {% endblock %}
{% block "contenu" %}
<h3 class="text-center my-3">Détails du fournisseur</h3>
<div class="row">
<div class="col-12 col-lg-8 border rounded p-3 mx-auto">
<form action="{% url 'gestion_fournisseur:mise-a-jour-fournisseur' id_fournisseur %}" method="post">
{% csrf_token %}
<div class="mb-3">
<label for="rccm" class="form-label d-block">{{fournisseurForm.nom_entreprise.label}} :</label>
{{ fournisseurForm.nom_entreprise }}
</div>
<div class="mb-3">
<label for="rccm" class="form-label d-block">{{fournisseurForm.nom_prenom_representant.label}} :</label>
{{ fournisseurForm.nom_prenom_representant }}
</div>
<div class="mb-3">
<label for="rccm" class="form-label d-block">{{fournisseurForm.fonction.label}} :</label>
{{ fournisseurForm.fonction }}
</div>
<div class="mb-3">
<label for="rccm" class="form-label d-block">{{fournisseurForm.telephone.label}} :</label>
{{ fournisseurForm.telephone }}
</div>
<div class="mb-3">
<label for="rccm" class="form-label d-block">{{fournisseurForm.email.label}} :</label>
{{ fournisseurForm.email }}
</div>
<div class="mb-3">
<label for="rccm" class="form-label d-block">{{fournisseurForm.categorie.label}} :</label>
{{ fournisseurForm.categorie }}
</div>
<div class="mb-3">
<label for="rccm" class="form-label d-block">RCCM :</label>
<div>
Actuellement :
{% if fournisseur.rccm %}
<a href="{{ fournisseur.rccm.url }}" target="_blank">{{ fournisseur.rccm.name }}</a>
{% else %}
Aucun fichier téléchargé.
{% endif %}
</div>
<input type="file" name="rccm" id="rccm" class="form-control">
</div>
<div class="mb-3">
<label for="rccm" class="form-label d-block">NIFp :</label>
<div>
Actuellement :
{% if fournisseur.rccm %}
<a href="{{ fournisseur.nifp.url }}" target="_blank">{{ fournisseur.nifp.name }}</a>
{% else %}
Aucun fichier téléchargé.
{% endif %}
</div>
<input type="file" name="nifp" id="nifp" class="form-control">
</div>
<input type="submit" value="Mettre à jour" class="btn btn-primary mx-auto d-block">
</form>
</div>
</div>
<div class="row mt-2 d-flex align-items-center">
<div class="col"><h4 class="my-3">Livraison du fournisseur</h4></div>
<div class="col">
<!-- <label for="">Filtrer par date :</label> -->
<input type="month" name="" id="filtreMois" class="form-control">
</div>
</div>
<div id="liste-livraison" data-url = "{% url 'gestion_fournisseur:liste-livraison' id_fournisseur %}" data-idfournisseur="{{id_fournisseur}}"></div>
{% endblock %}
{% block "js" %}
<script src="{% static 'js/details_fournisseur.js' %}"></script>
{% endblock %}

View File

@@ -0,0 +1,64 @@
{% extends "BASE.html" %}
{% load static %}
{% load tags_personnaliser %}
{% block page_titre %} SGL - Gestion des fournisseurs {% endblock %}
{% block "contenu" %}
<div class="row p-2">
<div class="col d-flex justify-content-between">
<h3>LISTE DES FOURNISSEURS </h3>
{% if user|has_group:"Logisticien" %}
<button type="button" class="btn btn-primary" data-bs-toggle="modal" data-bs-target="#enregistrerFournisseur">
<i class="bi bi-person-plus-fill"></i>
Ajouter un fournisseur
</button>
{% endif %}
</div>
</div>
<div class="row p-2">
<div class="col">
<input type="text" class="form-control" placeholder="Rechercher par nom du fournisseur" id="searchFournisseur">
</div>
<div class="col">
<select name="" id="filterCategorie" class="form-select">
<option value="">Selectionner la catégorie du produit</option>
{% for categorie in listeCategorie %}
<option value="{{ categorie.id }}">{{ categorie.categorie }}</option>
{% endfor %}
</select>
</div>
</div>
<div id="liste_fournisseur" data-url="{% url 'gestion_fournisseur:liste_fournisseur' %}"></div>
<!-- Modal ajout d'un nouveau fournisseur -->
<div class="modal fade" id="enregistrerFournisseur" tabindex="-1" aria-labelledby="exampleModalLabel" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h1 class="modal-title fs-5" id="informationFournisseurModalLabel">Information sur le fournisseur</h1>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
<form action="{% url 'gestion_fournisseur:index' %}" method="post" enctype="multipart/form-data">
{% csrf_token %}
{{ form.as_p }}
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Fermer</button>
<button type="submit" class="btn btn-primary">Enregistrer</button>
</div>
</form>
</div>
</div>
</div>
</div>
{% endblock %}
{% block "js" %}
<script src="{% static 'js/gestion_fournisseur_index.js' %}"></script>
{% endblock %}

View File

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

View File

@@ -0,0 +1,33 @@
"""Gérez les URL pour la gestion des fournisseurs."""
from django.urls import path
from . import views
app_name = "gestion_fournisseur"
urlpatterns = [
path("", views.index, name="index"),
path(
"liste-fournisseur", views.liste_fournisseurs, name="liste_fournisseur"
),
path(
"liste-fournisseur-categorie",
views.liste_fournisseur_categorie,
name="liste_fournisseur_categorie",
),
path(
"liste-livraison/<int:id_fournisseur>",
views.liste_livraison_fournisseur,
name="liste-livraison",
),
path(
"details-fournisseur/<int:id_fournisseur>",
views.details_fournisseur,
name="details_fournisseur",
),
path(
"mise-a-jour-fournisseur/<int:id_fournisseur>",
views.mise_a_jour_fournisseur,
name="mise-a-jour-fournisseur",
),
]

View File

@@ -0,0 +1,195 @@
"""Vuews pour la gestion des fournisseurs."""
from django.shortcuts import render, redirect
from gestion_produit.models import Fournisseur, Categorie, Commander
from django.http import JsonResponse
from django.contrib.auth.decorators import login_required
from .forms import FournisseurForm
@login_required(login_url="login")
def index(request):
"""Gérez la vue index de la gestion des fournisseurs.
Parameters
----------
request : HttpRequest
La requête HTTP entrante.
Returns
-------
HttpResponse
La réponse HTTP avec le formulaire de fournisseur.
"""
if request.method == "POST":
form = FournisseurForm(request.POST, request.FILES)
if form.is_valid():
form.save()
form = FournisseurForm()
return render(
request,
"gestion_fournisseur/index.html",
{"form": form, "listeCategorie": Categorie.objects.all()},
)
@login_required(login_url="login")
def liste_fournisseurs(request): # noqa: ARG001
"""Renvoyez la liste des fournisseurs au format JSON.
Parameters
----------
request : HttpRequest
La requête HTTP entrante.
Returns
-------
JsonResponse
La réponse JSON contenant la liste des fournisseurs.
"""
fournisseurs = Fournisseur.objects.all().order_by("-id")
data = [
{
"id": f.id,
"nom_entreprise": f.nom_entreprise,
"representant": f.nom_prenom_representant,
"fonction": f.fonction,
"telephone": f.telephone,
"email": f.email,
"note": f.note,
}
for f in fournisseurs
]
return JsonResponse(data, safe=False)
@login_required(login_url="login")
def liste_fournisseur_categorie(request):
"""Renvoyez la liste des fournisseurs par catégorie au format JSON.
Parameters
----------
request : HttpRequest
La requête HTTP entrante.
Returns
-------
JsonResponse
La réponse JSON contenant la liste des fournisseurs filtrés par
catégorie.
"""
categorie = Categorie.objects.get(id=request.GET["c"])
fournisseur_categorie = Fournisseur.objects.filter(categorie=categorie)
data = [
{
"id": f.id,
"nom_entreprise": f.nom_entreprise,
"representant": f.nom_prenom_representant,
"fonction": f.fonction,
"telephone": f.telephone,
"email": f.email,
"note": f.note,
}
for f in fournisseur_categorie
]
return JsonResponse(data, safe=False)
@login_required(login_url="login")
def details_fournisseur(request, id_fournisseur):
"""Affichez les détails d'un fournisseur spécifique.
Parameters
----------
request : HttpRequest
La requête HTTP entrante.
id_fournisseur : int
L'identifiant du fournisseur.
Returns
-------
HttpResponse
La réponse HTTP avec les détails du fournisseur.
"""
fournisseur = Fournisseur.objects.get(id=id_fournisseur)
fournisseur_form = FournisseurForm(instance=fournisseur)
return render(
request,
"gestion_fournisseur/details_fournisseur.html",
{
"fournisseur": fournisseur,
"fournisseurForm": fournisseur_form,
"id_fournisseur": id_fournisseur,
},
)
def mise_a_jour_fournisseur(request, id_fournisseur):
"""Mettez à jour les informations d'un fournisseur spécifique.
Parameters
----------
request : HttpRequest
La requête HTTP entrante.
id_fournisseur : int
L'identifiant du fournisseur.
Returns
-------
HttpResponse
La réponse HTTP redirigeant vers les détails du fournisseur.
"""
fournisseur = Fournisseur.objects.get(id=id_fournisseur)
if request.method == "POST":
form = FournisseurForm(
request.POST,
request.FILES,
instance=fournisseur
)
if form.is_valid():
form.save()
return redirect(
"gestion_fournisseur:details_fournisseur",
id_fournisseur=id_fournisseur
)
def liste_livraison_fournisseur(request, id_fournisseur): # noqa: ARG001
"""Renvoyez la liste des livraisons d'un fournisseur au format JSON.
Parameters
----------
request : HttpRequest
La requête HTTP entrante.
id_fournisseur : int
L'identifiant du fournisseur.
Returns
-------
JsonResponse
La réponse JSON contenant la liste des livraisons du fournisseur.
"""
commande_fournisseur = [
{
"date": c.date_livraison,
"produit": c.produit.libelle,
"prix_achat": c.prix_achat,
"quantite": c.quantite_commander,
"montant_total": c.quantite_commander * c.prix_achat,
"date_livraison": c.date_livraison,
"statut": c.livrer,
}
for c in Commander.objects.filter(fournisseur__id=id_fournisseur)
]
return JsonResponse(commande_fournisseur, safe=False)

View File

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Some files were not shown because too many files have changed in this diff Show More