fix: change backend port from 8080 to 8090 (DSM conflict)
This commit is contained in:
@@ -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!
|
||||
```
|
||||
@@ -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
|
||||
```
|
||||
Reference in New Issue
Block a user