Mon commit initial
This commit is contained in:
0
SGL/__init__.py
Normal file
0
SGL/__init__.py
Normal file
BIN
SGL/__pycache__/__init__.cpython-310.pyc
Normal file
BIN
SGL/__pycache__/__init__.cpython-310.pyc
Normal file
Binary file not shown.
BIN
SGL/__pycache__/__init__.cpython-313.pyc
Normal file
BIN
SGL/__pycache__/__init__.cpython-313.pyc
Normal file
Binary file not shown.
BIN
SGL/__pycache__/settings.cpython-310.pyc
Normal file
BIN
SGL/__pycache__/settings.cpython-310.pyc
Normal file
Binary file not shown.
BIN
SGL/__pycache__/settings.cpython-313.pyc
Normal file
BIN
SGL/__pycache__/settings.cpython-313.pyc
Normal file
Binary file not shown.
BIN
SGL/__pycache__/urls.cpython-310.pyc
Normal file
BIN
SGL/__pycache__/urls.cpython-310.pyc
Normal file
Binary file not shown.
BIN
SGL/__pycache__/urls.cpython-313.pyc
Normal file
BIN
SGL/__pycache__/urls.cpython-313.pyc
Normal file
Binary file not shown.
BIN
SGL/__pycache__/views.cpython-310.pyc
Normal file
BIN
SGL/__pycache__/views.cpython-310.pyc
Normal file
Binary file not shown.
BIN
SGL/__pycache__/views.cpython-313.pyc
Normal file
BIN
SGL/__pycache__/views.cpython-313.pyc
Normal file
Binary file not shown.
BIN
SGL/__pycache__/wsgi.cpython-310.pyc
Normal file
BIN
SGL/__pycache__/wsgi.cpython-310.pyc
Normal file
Binary file not shown.
BIN
SGL/__pycache__/wsgi.cpython-313.pyc
Normal file
BIN
SGL/__pycache__/wsgi.cpython-313.pyc
Normal file
Binary file not shown.
16
SGL/asgi.py
Normal file
16
SGL/asgi.py
Normal 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
147
SGL/settings.py
Normal 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
114
SGL/templates/BASE.html
Normal 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>
|
||||
20
SGL/templates/emails/email_template.html
Normal file
20
SGL/templates/emails/email_template.html
Normal 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>
|
||||
BIN
SGL/templates/emails/téléchargement.png
Normal file
BIN
SGL/templates/emails/téléchargement.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 4.7 KiB |
BIN
SGL/templates/emails/téléchargement.png:Zone.Identifier
Normal file
BIN
SGL/templates/emails/téléchargement.png:Zone.Identifier
Normal file
Binary file not shown.
206
SGL/templates/index.html
Normal file
206
SGL/templates/index.html
Normal 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
68
SGL/urls.py
Normal 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
334
SGL/views.py
Normal 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
16
SGL/wsgi.py
Normal 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()
|
||||
Reference in New Issue
Block a user