--- title: "Rapport de Projet — ERP SUARL Rayhan" subtitle: "Projet de Fin d'Études — Ali Guennari" author: "Ali Guennari" date: "Avril 2026" lang: fr --- # Rapport de Projet de Fin d'Études ## ERP Sur Mesure pour SUARL Rayhan **Étudiant :** Ali Guennari **Entreprise d'accueil :** SUARL Rayhan — Plasturgie, Tataouine, Tunisie **Encadrant académique :** À compléter **Encadrant professionnel :** À compléter **Année universitaire :** 2025 / 2026 --- # Chapitre 1 — Présentation du Projet ## 1.1 Contexte SUARL Rayhan est une entreprise tunisienne spécialisée dans la fabrication d'emballages plastiques (sacs Bertel, sacs poubelles, sacs alimentaires, film rétractable) implantée à Tataouine. Face à une gestion manuelle de ses processus métier — achats, ventes, production, stock — la direction a exprimé le besoin d'un système d'information intégré permettant de centraliser et automatiser ces opérations. ## 1.2 Objectif du Projet Ce projet de fin d'études consiste à concevoir et développer un **ERP (Enterprise Resource Planning) sur mesure**, adapté aux spécificités de SUARL Rayhan, couvrant : - La gestion du référentiel articles (matières premières, produits semi-finis, produits finis) - Le cycle d'achat complet (commandes fournisseurs → bons de réception → mise à jour du stock) - Le cycle de vente complet (commandes clients → bons de livraison → sortie de stock) - La gestion de la production (nomenclatures BOM, ordres de fabrication) - Le suivi des stocks en temps réel avec alertes - Un tableau de bord KPI pour la direction ## 1.3 Périmètre Fonctionnel | Module | Description | |--------|-------------| | Authentification | Connexion sécurisée avec rôles différenciés | | Articles | Gestion du catalogue (MP, PSF, PF) | | Clients & Fournisseurs | Annuaire des tiers commerciaux | | Cycle Achat | Commande → Réception → Stock | | Cycle Vente | Commande → Livraison → Stock | | Production | BOM + Ordres de Fabrication | | Stock | Mouvements, historique, alertes seuil minimum | | Tableau de bord | KPIs temps réel pour le PDG | --- # Chapitre 2 — Architecture Technique ## 2.1 Stack Technologique | Couche | Technologie | Version | |--------|-------------|---------| | Backend API | Spring Boot | 3.2.5 | | Langage | Java | 17 LTS | | Sécurité | Spring Security + JWT | JJWT 0.12.5 | | Persistance | Spring Data JPA / Hibernate | 6.4.4 | | Base de données | MySQL | 8.0 | | Conteneurisation | Docker + Docker Compose | — | | Frontend (à venir) | Flutter | 3.x | ## 2.2 Architecture N-Tiers L'API suit rigoureusement le patron d'architecture **Controller → Service → Repository → Model** : ``` ┌─────────────────────────────────────────────────────────┐ │ Application Flutter (Client) │ └─────────────────────┬───────────────────────────────────┘ │ HTTP/HTTPS (JWT Bearer Token) ▼ ┌─────────────────────────────────────────────────────────┐ │ API REST Spring Boot (Port 8090) │ │ ┌──────────┐ ┌──────────┐ ┌──────────┐ │ │ │Controller│→ │ Service │→ │Repository│ │ │ └──────────┘ └──────────┘ └──────────┘ │ │ ┌──────────┐ │ │ │ JPA / │ │ │ │Hibernate │ │ └──────────────────────────────┴────┬─────┴──────────────┘ │ JDBC ▼ ┌─────────────────────┐ │ MySQL 8 │ │ rayhan_erp_db │ └─────────────────────┘ ``` ## 2.3 Sécurité — Mécanisme JWT Le système utilise une authentification **stateless** basée sur les JSON Web Tokens : 1. Le client envoie ses identifiants à `POST /api/auth/signin` 2. Le serveur valide et retourne un JWT signé (valable 24h) 3. Le client inclut `Authorization: Bearer ` dans chaque requête 4. Le filtre `AuthTokenFilter` intercepte et valide le token avant chaque endpoint ### Rôles et Contrôle d'Accès | Rôle | Périmètre d'accès | |------|-------------------| | `ROLE_PDG` | Accès complet + tableau de bord | | `ROLE_RESPONSABLE_VENTE` | Ventes, clients | | `ROLE_RESPONSABLE_ACHAT` | Achats, fournisseurs | | `ROLE_RESPONSABLE_PRODUCTION` | Production, BOM, ordres de fabrication | | `ROLE_MAGASINIER` | Stock, mouvements de stock | | `ROLE_RH` | Ressources humaines, paie | Chaque endpoint est protégé par l'annotation `@PreAuthorize` : ```java @PreAuthorize("hasAnyRole('ROLE_PDG', 'ROLE_RESPONSABLE_VENTE')") ``` ## 2.4 Modèle de Données Relationnel La base de données `rayhan_erp_db` contient les tables suivantes : - **users / roles / user_roles** — authentification et droits - **tiers** (table mère), **clients**, **fournisseurs** — gestion des tiers (héritage JOINED) - **articles** — catalogue produits avec types MP / PSF / PF - **bom_lines** — nomenclatures de production (Bill of Materials) - **production_orders** — ordres de fabrication avec cycle PLANIFIE → LANCE → TERMINE - **purchase_orders / purchase_order_lines** — commandes fournisseurs - **goods_receipts / goods_receipt_lines** — bons de réception - **sales_orders / sales_order_lines** — commandes clients - **delivery_notes / delivery_note_lines** — bons de livraison - **stock_movements** — historique complet de tous les mouvements de stock --- # Chapitre 3 — Endpoints de l'API ## 3.1 Authentification | Méthode | URL | Description | |---------|-----|-------------| | POST | `/api/auth/signin` | Connexion → retourne JWT | | POST | `/api/auth/signup` | Créer un utilisateur | ## 3.2 Articles | Méthode | URL | Rôles | |---------|-----|-------| | GET | `/api/articles` | Tous | | GET | `/api/articles/{id}` | Tous | | GET | `/api/articles/type/{type}` | Tous (MP, PF, PSF) | | GET | `/api/articles/alertes-stock` | PDG, Magasinier, Production | | POST | `/api/articles` | PDG, Production, Magasinier | | PUT | `/api/articles/{id}` | PDG, Production | | DELETE | `/api/articles/{id}` | PDG (désactivation logique) | ## 3.3 Clients & Fournisseurs | Méthode | URL | Rôles | |---------|-----|-------| | GET | `/api/clients` | PDG, Vente | | POST | `/api/clients` | PDG, Vente | | PUT | `/api/clients/{id}` | PDG, Vente | | GET | `/api/fournisseurs` | PDG, Achat | | POST | `/api/fournisseurs` | PDG, Achat | ## 3.4 Cycle d'Achat | Méthode | URL | Description | |---------|-----|-------------| | GET | `/api/purchase-orders` | Liste des commandes | | POST | `/api/purchase-orders` | Créer une commande fournisseur | | POST | `/api/purchase-orders/{id}/receive` | Réceptionner → crée BR + entre en stock | ## 3.5 Cycle de Vente | Méthode | URL | Description | |---------|-----|-------------| | GET | `/api/sales-orders` | Liste des commandes | | POST | `/api/sales-orders` | Créer une commande client | | POST | `/api/sales-orders/{id}/deliver` | Livrer → crée BL + sort du stock | ## 3.6 Production | Méthode | URL | Description | |---------|-----|-------------| | POST | `/api/production/bom` | Définir une nomenclature | | POST | `/api/production/orders/plan` | Planifier un ordre de fabrication | | POST | `/api/production/orders/{id}/launch` | Lancer → consomme les MP | | POST | `/api/production/orders/{id}/complete` | Terminer → entre les PF en stock | ## 3.7 Stock & Tableau de Bord | Méthode | URL | Description | |---------|-----|-------------| | GET | `/api/stock/historique/{articleId}` | Historique des mouvements | | POST | `/api/stock/adjust` | Ajustement manuel | | GET | `/api/dashboard` | KPIs pour le PDG | --- # Chapitre 4 — Déploiement ## 4.1 Infrastructure L'application est déployée via Docker Compose, accessible publiquement derrière un reverse proxy HTTPS. Deux conteneurs sont en production : - **rayhan-mysql** — MySQL 8, base de données `rayhan_erp_db` - **rayhan-backend** — Spring Boot, accessible sur le port **8090** ## 4.2 Docker Compose ```yaml services: mysql: image: mysql:8.0 container_name: rayhan-mysql environment: MYSQL_DATABASE: rayhan_erp_db volumes: - mysql_data:/var/lib/mysql backend: build: ./backend container_name: rayhan-backend ports: - "8090:8080" depends_on: mysql: condition: service_healthy ``` ## 4.3 Dockerfile — Build Multi-Étapes ```dockerfile # Étape 1 : Compilation Maven FROM maven:3.9.6-eclipse-temurin-17 AS build WORKDIR /app COPY pom.xml . RUN mvn dependency:go-offline COPY src ./src RUN mvn clean package -DskipTests # Étape 2 : Image d'exécution légère FROM eclipse-temurin:17-jre-alpine WORKDIR /app COPY --from=build /app/target/erp-1.0.0-SNAPSHOT.jar app.jar EXPOSE 8080 ENTRYPOINT ["java", "-jar", "app.jar"] ``` ## 4.4 Accès à l'Application | Service | URL | |---------|-----| | API REST | https://rayhan-erp.bolbol.tn | | Documentation Swagger UI | https://rayhan-erp.bolbol.tn/swagger-ui/index.html | | Dépôt source (Gitea) | https://gitea.bolbol.tn/bolbol/rayhan-erp | --- # Chapitre 5 — Tests et Validation ## 5.1 Interface Swagger UI L'API intègre **Swagger UI** (SpringDoc OpenAPI 2.5.0), accessible depuis n'importe quel navigateur. Cette interface permet de : - Visualiser tous les endpoints disponibles - Tester chaque endpoint directement depuis le navigateur - S'authentifier avec le JWT via le bouton "Authorize" **Procédure de test :** 1. Ouvrir https://rayhan-erp.bolbol.tn/swagger-ui/index.html 2. Exécuter `POST /api/auth/signin` avec `{"username":"admin","password":"Rayhan2024!"}` 3. Copier le token de la réponse 4. Cliquer sur **Authorize 🔒** → coller le token → Authorize 5. Tester librement tous les endpoints ## 5.2 Scénario de Test Complet | Étape | Action | Résultat attendu | |-------|--------|-----------------| | 1 | Connexion admin | Token JWT retourné | | 2 | Créer article MP-HDPE | Article créé, stock = 0 | | 3 | Créer commande fournisseur | Statut CONFIRMEE | | 4 | Réceptionner commande | Stock MP augmente | | 5 | Créer nomenclature BOM | Lien PF ↔ MP créé | | 6 | Planifier + Lancer OF | Stock MP diminue (consommation) | | 7 | Terminer OF | Stock PF augmente | | 8 | Créer commande client | Statut CONFIRMEE | | 9 | Livrer commande | Stock PF diminue | | 10 | Tableau de bord | KPIs mis à jour | --- # Chapitre 6 — Ce qui reste à faire ## 6.1 Frontend Flutter Le frontend mobile/desktop sera développé avec Flutter et se connectera à l'API REST via des appels HTTP avec token JWT. **Écrans à développer (par priorité) :** 1. **Écran de connexion** — formulaire login/password, stockage du token 2. **Tableau de bord** — affichage des KPIs (chiffre d'affaires, alertes stock, OF en cours) 3. **Articles** — liste, recherche, fiche détail, ajout/modification 4. **Cycle de vente** — liste commandes, création, bon de livraison 5. **Cycle d'achat** — liste commandes, création, bon de réception 6. **Production** — BOM, ordres de fabrication, lancement/terminaison 7. **Stock** — historique des mouvements, ajustements **Architecture Flutter proposée :** - **State management :** Provider ou Riverpod - **HTTP client :** `http` ou `dio` - **Stockage local :** `shared_preferences` (token JWT) - **Navigation :** `go_router` ## 6.2 Modules Complémentaires API | Module | Description | Priorité | |--------|-------------|----------| | Facturation | Génération de factures PDF | Haute | | Paie / RH | Gestion des employés et salaires | Moyenne | | Rapports | Export Excel/PDF des données | Moyenne | --- # Annexes ## A — Initialisation Automatique Au premier démarrage, le système crée automatiquement : - Les 6 rôles dans la base de données - L'utilisateur administrateur : **admin / Rayhan2024!** avec le rôle PDG ## B — Variables d'Environnement Docker | Variable | Valeur | |----------|--------| | `SPRING_DATASOURCE_URL` | `jdbc:mysql://mysql:3306/rayhan_erp_db` | | `SPRING_DATASOURCE_USERNAME` | `root` | | `SPRING_DATASOURCE_PASSWORD` | `rayhan_erp_2024` | | `RAYHAN_ERP_JWTSECRET` | Clé secrète JWT (256 bits) | | `RAYHAN_ERP_JWTEXPIRATIONMS` | `86400000` (24 heures) | ## C — Commandes Docker Utiles ```bash # Démarrer l'application docker compose up -d # Reconstruire après modification du code docker compose up -d --build # Voir les logs en temps réel docker logs rayhan-backend -f # Accéder à MySQL docker exec -it rayhan-mysql mysql -u root -prayhan_erp_2024 rayhan_erp_db ``` --- # Chapitre 5 — Réalisation Frontend (Application Mobile Flutter) ## 5.1 Choix Technologique L'interface utilisateur de l'ERP Rayhan est développée avec **Flutter**, le framework de Google permettant de créer des applications mobiles multiplateformes (Android et iOS) à partir d'une seule base de code Dart. ### Justification du choix Flutter | Critère | Flutter | React Native | Natif Android/iOS | |---------|---------|--------------|-------------------| | Code partagé Android/iOS | 100% | ~85% | 0% | | Performance | Excellente | Bonne | Excellente | | Courbe d'apprentissage | Moyenne | Moyenne | Élevée | | Écosystème packages | Riche | Riche | Natif | | Rendu UI | Moteur propre (Skia) | Bridge natif | Natif | Flutter a été retenu pour sa capacité à produire une application unique couvrant les deux plateformes mobiles majeures, tout en offrant des performances proches du natif grâce à son moteur de rendu indépendant. ## 5.2 Architecture du Projet Flutter L'architecture adoptée suit le pattern **Provider + Services** : ``` frontend/ ├── lib/ │ ├── main.dart # Point d'entrée, routing, thème │ ├── screens/ # Écrans de l'application │ │ ├── login_screen.dart # Écran de connexion │ │ └── dashboard_screen.dart # Tableau de bord KPI │ ├── providers/ # Gestion d'état (Provider) │ │ └── auth_provider.dart # État authentification │ ├── services/ # Appels API │ │ ├── api_client.dart # Client Dio + intercepteur JWT │ │ └── auth_service.dart # Service authentification │ ├── models/ # Modèles de données Dart │ └── widgets/ # Composants réutilisables ├── pubspec.yaml # Dépendances Flutter └── android/ # Configuration Android ``` ### Dépendances principales | Package | Version | Rôle | |---------|---------|------| | `dio` | ^5.4.0 | Client HTTP avec intercepteurs | | `provider` | ^6.1.1 | Gestion d'état réactive | | `shared_preferences` | ^2.2.2 | Stockage local du token JWT | | `go_router` | ^13.2.0 | Navigation déclarative | | `fl_chart` | ^0.67.0 | Graphiques pour le dashboard | | `intl` | ^0.19.0 | Formatage dates et nombres | ## 5.3 Authentification et Sécurité ### Flux d'authentification Le flux d'authentification suit le protocole JWT standard : 1. L'utilisateur saisit son identifiant et mot de passe 2. L'application envoie une requête `POST /api/auth/signin` 3. Le serveur retourne un token JWT signé (validité 24h) 4. Le token est stocké localement via `shared_preferences` 5. Chaque requête API suivante inclut automatiquement `Authorization: Bearer ` 6. À la déconnexion, le token est supprimé du stockage local ### Intercepteur JWT automatique La classe `ApiClient` centralise toutes les communications HTTP et injecte automatiquement le token JWT dans les en-têtes via un intercepteur `Dio` : ```dart class _AuthInterceptor extends Interceptor { @override void onRequest(RequestOptions options, RequestInterceptorHandler handler) async { final prefs = await SharedPreferences.getInstance(); final token = prefs.getString('jwt_token'); if (token != null) { options.headers['Authorization'] = 'Bearer $token'; } handler.next(options); } } ``` Cette approche centralise la gestion du token et évite sa répétition dans chaque appel API. ### Redirection automatique (GoRouter) Le routeur détecte l'état d'authentification et redirige automatiquement : - Un utilisateur non connecté est redirigé vers `/login` - Un utilisateur déjà connecté accédant à `/login` est redirigé vers `/dashboard` ## 5.4 Écran de Connexion L'écran de connexion (`LoginScreen`) présente une interface épurée et professionnelle : **Composants UI :** - Logo de l'application avec icône usine (représentant l'industrie plasturgie) - Titre "RAYHAN ERP" et sous-titre - Formulaire de connexion avec validation : - Champ identifiant avec icône - Champ mot de passe avec bouton afficher/masquer - Message d'erreur contextuel en cas d'échec - Bouton de connexion avec indicateur de chargement - Footer © SUARL Rayhan **Gestion des états :** - État initial : formulaire vide - État chargement : spinner animé, bouton désactivé - État erreur : bandeau rouge avec message explicite - État succès : redirection automatique vers le dashboard --- ## 5.5 Tableau de Bord KPI (Dashboard) ### Objectif Le tableau de bord est l'écran central de l'application, accessible uniquement aux utilisateurs ayant le rôle **PDG**. Il synthétise en temps réel l'état de l'entreprise à travers des indicateurs clés de performance (KPI). ### Données affichées L'écran récupère les données depuis l'endpoint `GET /api/dashboard` et les affiche en trois sections : **Section Ventes (mois en cours)** | KPI | Description | |-----|-------------| | Chiffre d'affaires | Montant total des ventes du mois en TND | | Commandes ce mois | Nombre de bons de commande clients créés | | Commandes en cours | Commandes en attente de livraison | **Section Achats & Production** | KPI | Description | |-----|-------------| | Achats en attente | Bons de commande fournisseurs non réceptionnés | | OF en cours | Ordres de fabrication au statut LANCE | | OF planifiés | Ordres de fabrication au statut PLANIFIE | **Section Stock** - Nombre d'articles dont le stock est inférieur au seuil minimum - Liste détaillée des articles en alerte avec leur quantité en stock actuelle ### Architecture technique La gestion du dashboard suit le pattern **Provider** : ``` GET /api/dashboard │ DashboardService.fetchKpis() │ DashboardProvider (ChangeNotifier) ├── isLoading ├── error └── kpi: DashboardKpi ├── VentesKpi ├── AchatsKpi ├── ProductionKpi └── StockKpi │ DashboardScreen (Consumer) ├── KpiCard × 6 (grille 2 colonnes) └── _StockSection (liste alertes) ``` ### Navigation (Drawer) Un menu latéral (`AppDrawer`) permet la navigation entre tous les modules : - Tableau de bord - Articles - Ventes - Achats - Production - Stock - Déconnexion Le drawer affiche le rôle de l'utilisateur connecté et met en surbrillance la section active. ### Comportement UI - **Pull-to-refresh** : glisser vers le bas recharge les KPIs - **Bouton actualiser** dans l'AppBar - **État chargement** : spinner centré - **État erreur** : message avec bouton "Réessayer" - **Alertes stock** : icône verte si tout va bien, rouge avec liste si alertes --- *[Section à compléter avec captures d'écran lors de la finalisation du rapport]* --- ## 5.6 Module Articles ### Objectif Le module Articles permet de gérer le **catalogue complet** des références de l'entreprise, organisé en trois catégories correspondant au cycle industriel de SUARL Rayhan : | Type | Code | Description | |------|------|-------------| | Matière Première | MP | Granulés PEHD/LDPE, colorants, additifs | | Produit Semi-Fini | PSF | Film tubulaire non découpé | | Produit Fini | PF | Sacs Bertel, sacs poubelles, film rétractable | ### Fonctionnalités - **Liste filtrée** : affichage de tous les articles avec filtre par type (MP / PSF / PF) et barre de recherche en temps réel - **Indicateur d'alerte** : les articles dont le stock est inférieur au seuil minimum sont signalés en rouge avec icône d'avertissement - **Création** : formulaire complet (référence, désignation, type, unité de mesure, prix unitaire, stock initial, seuil d'alerte) - **Modification** : édition de tous les champs sauf la référence (clé métier immuable) - **Archivage** : suppression logique (l'article est marqué `actif=false`, pas effacé de la base) ### Architecture technique ``` ArticleService (Dio) ArticleProvider (ChangeNotifier) ├── fetchAll() ─────► ├── _articles: List
├── fetchByType() ├── _filterType: String ├── create() ├── _search: String ├── update() └── articles (getter filtré) └── delete() │ ArticlesScreen ├── _SearchBar ├── _FilterChips (TOUS / MP / PSF / PF) └── ListView → _ArticleCard └── PopupMenu (Modifier / Supprimer) ArticleFormScreen (création + édition) ├── Section Identification (ref, désignation, type) ├── Section Unité & Prix └── Section Stock (actuel + seuil minimum) ``` ### Modèle de données ```dart class Article { int? id; String reference; // Clé unique (ex: MP-001) String designation; // Libellé String type; // MP | PSF | PF String? uniteMesure; // kg, unité, rouleau, m double prixUnitaire; // En TND double stockActuel; // Quantité en stock double stockMinimum; // Seuil d'alerte bool actif; // Soft delete } ```