fix: change backend port from 8080 to 8090 (DSM conflict)

This commit is contained in:
2026-04-19 20:25:21 +01:00
parent 62ac11a177
commit 467c41cc48
12 changed files with 6946 additions and 1 deletions
+200
View File
@@ -0,0 +1,200 @@
# Documentation Architecture API — ERP Rayhan
**PFE Ali Guennari — SUARL Rayhan**
## Architecture Technique
```
┌─────────────────────────────────────────────────────────┐
│ Application Flutter (Client) │
│ (Windows Desktop / Web / Android) │
└─────────────────────┬───────────────────────────────────┘
│ HTTP/HTTPS (JWT Bearer Token)
┌─────────────────────────────────────────────────────────┐
│ API REST Spring Boot (Port 8090) │
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
│ │Controller│→ │ Service │→ │Repository│ │
│ └──────────┘ └──────────┘ └──────────┘ │
│ ┌──────────┐ │
│ │ JPA/ │ │
│ │Hibernate │ │
└──────────────────────────────┴────┬─────┴──────────────┘
│ JDBC
┌─────────────────────┐
│ MySQL 8 (DB) │
│ rayhan_erp_db │
└─────────────────────┘
```
## Pattern d'Architecture N-Tiers
L'API suit rigoureusement le **pattern Controller → Service → Repository → Model** :
| Couche | Rôle | Exemple |
|--------|------|---------|
| **Controller** | Reçoit les requêtes HTTP, délègue au service, retourne la réponse | `ArticleController.java` |
| **Service** | Contient la logique métier (règles, validations, transactions) | `StockService.java` |
| **Repository** | Abstraction d'accès à la base de données (Spring Data JPA) | `ArticleRepository.java` |
| **Model** | Entités JPA mappées sur les tables MySQL | `Article.java` |
## Tous les Endpoints de l'API
### Authentification (Public — sans token)
| Méthode | URL | Description |
|---------|-----|-------------|
| POST | `/api/auth/signin` | Connexion → retourne JWT |
| POST | `/api/auth/signup` | Créer un utilisateur |
### Articles (Catalogue)
| Méthode | URL | Rôles autorisés |
|---------|-----|-----------------|
| 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) |
### Clients
| Méthode | URL | Rôles autorisés |
|---------|-----|-----------------|
| GET | `/api/clients` | PDG, Vente |
| GET | `/api/clients/search?q=` | PDG, Vente |
| GET | `/api/clients/{id}` | PDG, Vente |
| POST | `/api/clients` | PDG, Vente |
| PUT | `/api/clients/{id}` | PDG, Vente |
### Fournisseurs
| Méthode | URL | Rôles autorisés |
|---------|-----|-----------------|
| GET | `/api/fournisseurs` | PDG, Achat |
| GET | `/api/fournisseurs/search?q=` | PDG, Achat |
| GET | `/api/fournisseurs/{id}` | PDG, Achat |
| POST | `/api/fournisseurs` | PDG, Achat |
| PUT | `/api/fournisseurs/{id}` | PDG, Achat |
### Cycle d'Achat
| Méthode | URL | Rôles autorisés |
|---------|-----|-----------------|
| GET | `/api/purchase-orders` | PDG, Achat |
| POST | `/api/purchase-orders` | PDG, Achat |
| POST | `/api/purchase-orders/{id}/receive` | PDG, Achat, Magasinier |
**Flux automatique lors de la réception :**
`POST /receive` → Crée le BR → `StockService.entreeStock()` → Mise à jour `Article.stockActuel` + création `StockMovement`
### Cycle de Vente
| Méthode | URL | Rôles autorisés |
|---------|-----|-----------------|
| GET | `/api/sales-orders` | PDG, Vente |
| POST | `/api/sales-orders` | PDG, Vente |
| POST | `/api/sales-orders/{id}/deliver` | PDG, Vente, Magasinier |
**Flux automatique lors de la livraison :**
`POST /deliver` → Crée le BL → `StockService.sortieStock()` → Mise à jour `Article.stockActuel` + création `StockMovement`
### Cycle de Production
| Méthode | URL | Rôles autorisés |
|---------|-----|-----------------|
| GET | `/api/production/bom/{produitFiniId}` | PDG, Production |
| POST | `/api/production/bom` | PDG, Production |
| DELETE | `/api/production/bom/{id}` | PDG, Production |
| GET | `/api/production/orders` | PDG, Production |
| POST | `/api/production/orders/plan` | PDG, Production |
| POST | `/api/production/orders/{id}/launch` | PDG, Production |
| POST | `/api/production/orders/{id}/complete` | PDG, Production |
**Cycle de vie d'un OF :**
```
PLANIFIE → (vérif stock MP) → LANCE → (consomme MP) → TERMINE → (entre PF en stock)
```
### Gestion du Stock
| Méthode | URL | Rôles autorisés |
|---------|-----|-----------------|
| GET | `/api/stock/historique/{articleId}` | PDG, Magasinier, Production |
| POST | `/api/stock/adjust` | PDG, Magasinier |
### Tableau de Bord (KPIs)
| Méthode | URL | Rôles autorisés |
|---------|-----|-----------------|
| GET | `/api/dashboard` | PDG uniquement |
---
## Modèle de Données Relationnel
```
users (id, username, email, password, firstName, lastName, enabled)
└── user_roles (user_id, role_id)
roles (id, name: ERole)
tiers (id, raisonSociale, matriculeFiscal, adresse, telephone, email, ville, actif)
├── clients (tiers_id FK, typeClient, plafondCredit, delaiPaiement)
└── fournisseurs (tiers_id FK, pays, categorieProduit, delaiLivraison, modePaiement)
articles (id, reference, designation, type: MP|PSF|PF, uniteMesure, prixUnitaire, stockActuel, stockMinimum, actif)
bom_lines (id, produit_fini_id FK→articles, composant_id FK→articles, quantiteParUnite)
production_orders (id, reference, produit_fini_id FK, quantitePlanifiee, quantiteRealisee, datePlanifiee, dateLancement, dateTerminaison, statut)
purchase_orders (id, reference, fournisseur_id FK, dateCommande, statut, totalHT, totalTVA, totalTTC)
└── purchase_order_lines (id, purchase_order_id FK, article_id FK, quantiteCommandee, quantiteRecue, prixUnitaireHT, tauxTVA)
goods_receipts (id, reference, purchase_order_id FK, dateReception)
└── goods_receipt_lines (id, goods_receipt_id FK, purchase_order_line_id FK, article_id FK, quantiteRecue)
sales_orders (id, reference, client_id FK, dateCommande, statut, totalHT, totalTVA, totalTTC)
└── sales_order_lines (id, sales_order_id FK, article_id FK, quantiteCommandee, quantiteLivree, prixUnitaireHT, tauxTVA)
delivery_notes (id, reference, sales_order_id FK, dateLivraison, statut)
└── delivery_note_lines (id, delivery_note_id FK, sales_order_line_id FK, article_id FK, quantiteLivree)
stock_movements (id, article_id FK, type: IN|OUT, quantite, stockAvant, stockApres, sourceDocument, referenceDocument, motif, dateHeure, user_id FK)
```
---
## Sécurité
### Mécanisme JWT
1. Le client envoie `POST /api/auth/signin` avec username/password
2. Le serveur retourne un JWT signé (valide 24h)
3. Le client inclut `Authorization: Bearer <token>` dans chaque requête
4. `AuthTokenFilter` intercepte et valide le token avant chaque endpoint sécurisé
### Contrôle d'Accès par Rôle (`@PreAuthorize`)
Chaque endpoint est annoté avec les rôles autorisés :
```java
@PreAuthorize("hasAnyRole('ROLE_PDG', 'ROLE_RESPONSABLE_VENTE')")
```
---
## Démarrage de l'Application
```bash
# Via Docker Compose
docker compose up -d
# Vérifier que les conteneurs tournent
docker ps
# Voir les logs
docker logs rayhan-backend -f
# Premier démarrage : admin créé automatiquement
# username: admin | password: Rayhan2024!
```
+343
View File
@@ -0,0 +1,343 @@
# Guide de Tests API — Postman
**PFE Ali Guennari — ERP SUARL Rayhan**
## Configuration de Base
**URL de base** : `http://192.168.100.33:8090`
### 1. Configurer une Variable d'Environnement Postman
Dans Postman, créer un environnement "Rayhan ERP" avec :
- `baseUrl` = `http://192.168.100.33:8090`
- `token` = (sera rempli automatiquement)
---
## Scénario 1 : Authentification
### Se connecter (POST /api/auth/signin)
```
POST {{baseUrl}}/api/auth/signin
Content-Type: application/json
{
"username": "admin",
"password": "Rayhan2024!"
}
```
**Réponse attendue (200 OK) :**
```json
{
"token": "eyJhbGciOiJIUzI1NiJ9...",
"type": "Bearer",
"id": 1,
"username": "admin",
"email": "admin@rayhan.tn",
"roles": ["ROLE_PDG"]
}
```
**Script Postman (Tests tab)** pour sauvegarder le token :
```javascript
var json = pm.response.json();
pm.environment.set("token", json.token);
```
**Utilisation dans toutes les requêtes suivantes :**
```
Authorization: Bearer {{token}}
```
---
## Scénario 2 : Référentiels
### Créer un Article (Matière Première HDPE)
```
POST {{baseUrl}}/api/articles
Authorization: Bearer {{token}}
Content-Type: application/json
{
"reference": "MP-HDPE-001",
"designation": "HDPE Granulés (Polyéthylène Haute Densité)",
"type": "MP",
"uniteMesure": "kg",
"prixUnitaire": 2.850,
"stockMinimum": 500
}
```
### Créer un Article (Produit Fini — Sac Bertel)
```
POST {{baseUrl}}/api/articles
Authorization: Bearer {{token}}
Content-Type: application/json
{
"reference": "PF-SAC-BERTEL-001",
"designation": "Sac Bertel 40x60 cm",
"type": "PF",
"uniteMesure": "unité",
"prixUnitaire": 0.085,
"stockMinimum": 1000
}
```
### Créer un Client
```
POST {{baseUrl}}/api/clients
Authorization: Bearer {{token}}
Content-Type: application/json
{
"raisonSociale": "Supermarché Maghreb Distribution",
"matriculeFiscal": "123456A/A/M/000",
"adresse": "Avenue Habib Bourguiba, Tataouine",
"telephone": "75123456",
"email": "achats@maghreb-distrib.tn",
"ville": "Tataouine",
"typeClient": "Grossiste",
"delaiPaiement": 60
}
```
### Créer un Fournisseur (HDPE)
```
POST {{baseUrl}}/api/fournisseurs
Authorization: Bearer {{token}}
Content-Type: application/json
{
"raisonSociale": "PLASTUNION Tunisie",
"matriculeFiscal": "654321B/A/M/000",
"adresse": "Zone Industrielle Sfax",
"telephone": "74987654",
"email": "ventes@plastunion.tn",
"ville": "Sfax",
"pays": "Tunisie",
"categorieProduit": "Matières plastiques",
"delaiLivraison": 10
}
```
---
## Scénario 3 : Cycle d'Achat Complet
### Étape 3.1 — Créer une Commande Fournisseur
```
POST {{baseUrl}}/api/purchase-orders
Authorization: Bearer {{token}}
Content-Type: application/json
{
"fournisseur": {"id": 1},
"dateLivraisonPrevue": "2024-05-15",
"notes": "Commande mensuelle HDPE",
"lignes": [
{
"article": {"id": 1},
"quantiteCommandee": 1000,
"prixUnitaireHT": 2.850,
"tauxTVA": 19.00
}
]
}
```
**Réponse attendue :** commande avec `reference: "BC-2024-001"`, `statut: "CONFIRMEE"`, `totalTTC: 3391.50`
### Étape 3.2 — Réceptionner la Commande (Bon de Réception)
```
POST {{baseUrl}}/api/purchase-orders/1/receive
Authorization: Bearer {{token}}
Content-Type: application/json
{
"dateReception": "2024-05-14",
"notes": "Réception conforme",
"lignes": [
{
"purchaseOrderLine": {"id": 1},
"article": {"id": 1},
"quantiteRecue": 1000,
"observations": "Qualité OK, sacs conformes"
}
]
}
```
**Vérification :** `GET {{baseUrl}}/api/articles/1``stockActuel` doit être 1000
---
## Scénario 4 : Production (BOM + OF)
### Étape 4.1 — Définir la Nomenclature (BOM)
```
POST {{baseUrl}}/api/production/bom
Authorization: Bearer {{token}}
Content-Type: application/json
{
"produitFini": {"id": 2},
"composant": {"id": 1},
"quantiteParUnite": 0.015,
"uniteMesure": "kg"
}
```
*Signification : pour produire 1 Sac Bertel, il faut 0.015 kg de HDPE*
### Étape 4.2 — Planifier un Ordre de Fabrication
```
POST {{baseUrl}}/api/production/orders/plan
Authorization: Bearer {{token}}
Content-Type: application/json
{
"produitFiniId": 2,
"quantite": 10000,
"datePlanifiee": "2024-05-16"
}
```
**Vérification préalable :** stock HDPE (1000 kg) ≥ 10000 × 0.015 = 150 kg → OK
### Étape 4.3 — Lancer l'OF
```
POST {{baseUrl}}/api/production/orders/1/launch
Authorization: Bearer {{token}}
```
**Vérification :** stock HDPE passe de 1000 à 850 kg (consommation 150 kg)
### Étape 4.4 — Terminer l'OF
```
POST {{baseUrl}}/api/production/orders/1/complete
Authorization: Bearer {{token}}
Content-Type: application/json
{
"quantiteRealisee": 9800
}
```
**Vérification :** stock Sac Bertel passe à 9800 unités
---
## Scénario 5 : Cycle de Vente
### Étape 5.1 — Créer une Commande Client
```
POST {{baseUrl}}/api/sales-orders
Authorization: Bearer {{token}}
Content-Type: application/json
{
"client": {"id": 1},
"dateLivraisonSouhaitee": "2024-05-20",
"notes": "Urgent",
"lignes": [
{
"article": {"id": 2},
"quantiteCommandee": 5000,
"prixUnitaireHT": 0.085,
"tauxTVA": 19.00
}
]
}
```
### Étape 5.2 — Créer un Bon de Livraison
```
POST {{baseUrl}}/api/sales-orders/1/deliver
Authorization: Bearer {{token}}
Content-Type: application/json
{
"dateLivraison": "2024-05-20",
"adresseLivraison": "Avenue Habib Bourguiba, Tataouine",
"notes": "Livraison par camion",
"lignes": [
{
"salesOrderLine": {"id": 1},
"article": {"id": 2},
"quantiteLivree": 5000
}
]
}
```
**Vérification :** stock Sac Bertel passe de 9800 à 4800
---
## Scénario 6 : Tableau de Bord (PDG uniquement)
```
GET {{baseUrl}}/api/dashboard
Authorization: Bearer {{token}}
```
**Réponse attendue :**
```json
{
"ventes": {
"chiffreAffairesMois": 505.75,
"nbCommandesMois": 1,
"commandesEnCours": 0
},
"achats": {
"commandesEnAttente": 0
},
"production": {
"ofPlanifies": 0,
"ofEnCours": 0
},
"stock": {
"articlesEnAlerte": 0,
"articlesEnAlerteDetails": []
}
}
```
---
## Codes d'Erreur Importants
| Code | Signification | Action |
|------|--------------|--------|
| 401 | Non authentifié | Vérifier le token JWT |
| 403 | Accès interdit | Le rôle ne permet pas cette action |
| 400 | Données invalides | Vérifier le body de la requête |
| 500 | Erreur serveur | Voir les logs du container |
## Commandes Docker Utiles
```bash
# Voir les logs de l'API
docker logs rayhan-backend -f
# Accéder à MySQL directement
docker exec -it rayhan-mysql mysql -u root -prayhan_erp_2024 rayhan_erp_db
# Redémarrer l'API
docker restart rayhan-backend
```