Files
btpxpress-backend/docs/concepts/09-DEVIS.md
2025-10-01 01:37:34 +00:00

279 lines
6.5 KiB
Markdown

# 💰 CONCEPT: DEVIS
## 📌 Vue d'ensemble
Le concept **DEVIS** gère les devis et la facturation des chantiers. Il inclut les lignes de devis, calculs automatiques, et génération PDF.
**Importance**: ⭐⭐⭐⭐ (Concept stratégique)
---
## 🗂️ Fichiers concernés
### **Entités JPA**
| Fichier | Description |
|---------|-------------|
| `Devis.java` | Entité principale devis |
| `LigneDevis.java` | Ligne de devis |
| `StatutDevis.java` | Enum (BROUILLON, ENVOYE, ACCEPTE, REFUSE, EXPIRE) |
| `Facture.java` | Entité facture |
| `LigneFacture.java` | Ligne de facture |
### **Services**
| Fichier | Description |
|---------|-------------|
| `DevisService.java` | Service métier devis |
| `FactureService.java` | Service métier factures |
| `PdfGeneratorService.java` | Génération PDF |
### **Resources**
| Fichier | Description |
|---------|-------------|
| `DevisResource.java` | API REST devis |
| `FactureResource.java` | API REST factures |
---
## 📊 Modèle de données
### **Entité Devis**
```java
@Entity
@Table(name = "devis")
public class Devis extends PanacheEntityBase {
@Id
@GeneratedValue(strategy = GenerationType.UUID)
private UUID id;
@Column(name = "numero", unique = true, nullable = false)
private String numero;
@ManyToOne
@JoinColumn(name = "client_id", nullable = false)
private Client client;
@ManyToOne
@JoinColumn(name = "chantier_id")
private Chantier chantier;
@Column(name = "date_emission", nullable = false)
private LocalDate dateEmission;
@Column(name = "date_validite")
private LocalDate dateValidite;
@Enumerated(EnumType.STRING)
@Column(name = "statut", nullable = false)
private StatutDevis statut = StatutDevis.BROUILLON;
@OneToMany(mappedBy = "devis", cascade = CascadeType.ALL)
private List<LigneDevis> lignes;
@Column(name = "montant_ht", precision = 10, scale = 2)
private BigDecimal montantHT = BigDecimal.ZERO;
@Column(name = "montant_tva", precision = 10, scale = 2)
private BigDecimal montantTVA = BigDecimal.ZERO;
@Column(name = "montant_ttc", precision = 10, scale = 2)
private BigDecimal montantTTC = BigDecimal.ZERO;
@Column(name = "taux_tva", precision = 5, scale = 2)
private BigDecimal tauxTVA = new BigDecimal("20.00");
}
```
### **Enum StatutDevis**
```java
public enum StatutDevis {
BROUILLON, // En cours de rédaction
ENVOYE, // Envoyé au client
ACCEPTE, // Accepté par le client
REFUSE, // Refusé par le client
EXPIRE // Expiré (date de validité dépassée)
}
```
---
## 🔌 API REST
### **Endpoints Devis**
| Méthode | Endpoint | Description |
|---------|----------|-------------|
| GET | `/api/v1/devis` | Liste devis |
| GET | `/api/v1/devis/{id}` | Détails |
| POST | `/api/v1/devis` | Créer |
| PUT | `/api/v1/devis/{id}` | Modifier |
| PUT | `/api/v1/devis/{id}/envoyer` | Envoyer au client |
| PUT | `/api/v1/devis/{id}/accepter` | Accepter |
| PUT | `/api/v1/devis/{id}/refuser` | Refuser |
| GET | `/api/v1/devis/{id}/pdf` | Générer PDF |
| GET | `/api/v1/devis/stats` | Statistiques |
### **Endpoints Factures**
| Méthode | Endpoint | Description |
|---------|----------|-------------|
| GET | `/api/v1/factures` | Liste factures |
| GET | `/api/v1/factures/{id}` | Détails |
| POST | `/api/v1/factures` | Créer |
| POST | `/api/v1/factures/depuis-devis/{devisId}` | Créer depuis devis |
| GET | `/api/v1/factures/{id}/pdf` | Générer PDF |
---
## 💻 Exemples
### **Créer un devis**
```bash
curl -X POST http://localhost:8080/api/v1/devis \
-H "Content-Type: application/json" \
-d '{
"clientId": "client-uuid",
"chantierId": "chantier-uuid",
"dateEmission": "2025-10-01",
"dateValidite": "2025-11-01",
"tauxTVA": 20.00,
"lignes": [
{
"designation": "Terrassement et fondations",
"quantite": 1,
"unite": "FORFAIT",
"prixUnitaireHT": 5000.00
},
{
"designation": "Maçonnerie murs porteurs",
"quantite": 45,
"unite": "METRE_CARRE",
"prixUnitaireHT": 120.00
},
{
"designation": "Charpente traditionnelle",
"quantite": 1,
"unite": "FORFAIT",
"prixUnitaireHT": 8000.00
}
]
}'
```
**Réponse**:
```json
{
"id": "uuid",
"numero": "DEV-2025-001",
"client": "Jean Dupont",
"chantier": "Construction Villa Moderne",
"dateEmission": "2025-10-01",
"dateValidite": "2025-11-01",
"statut": "BROUILLON",
"lignes": [
{
"designation": "Terrassement et fondations",
"quantite": 1,
"prixUnitaireHT": 5000.00,
"montantHT": 5000.00
},
{
"designation": "Maçonnerie murs porteurs",
"quantite": 45,
"prixUnitaireHT": 120.00,
"montantHT": 5400.00
},
{
"designation": "Charpente traditionnelle",
"quantite": 1,
"prixUnitaireHT": 8000.00,
"montantHT": 8000.00
}
],
"montantHT": 18400.00,
"montantTVA": 3680.00,
"montantTTC": 22080.00
}
```
### **Envoyer un devis**
```bash
curl -X PUT http://localhost:8080/api/v1/devis/{id}/envoyer \
-H "Content-Type: application/json" \
-d '{
"emailClient": "jean.dupont@example.com",
"message": "Veuillez trouver ci-joint notre devis"
}'
```
### **Générer PDF**
```bash
curl -X GET http://localhost:8080/api/v1/devis/{id}/pdf \
-H "Accept: application/pdf" \
--output devis.pdf
```
### **Créer facture depuis devis**
```bash
curl -X POST http://localhost:8080/api/v1/factures/depuis-devis/{devisId}
```
---
## 🔧 Services métier
### **DevisService**
**Méthodes principales**:
- `create(DevisDTO)` - Créer devis
- `calculerMontants(UUID id)` - Calculer HT/TVA/TTC
- `envoyer(UUID id)` - Envoyer au client
- `accepter(UUID id)` - Accepter
- `refuser(UUID id)` - Refuser
- `genererPDF(UUID id)` - Générer PDF
### **FactureService**
**Méthodes principales**:
- `create(FactureDTO)` - Créer facture
- `creerDepuisDevis(UUID devisId)` - Créer depuis devis
- `genererPDF(UUID id)` - Générer PDF
---
## 📈 Relations
- **CLIENT** ⬅️ Un devis est adressé à un client
- **CHANTIER** ⬅️ Un devis peut être lié à un chantier
- **FACTURE** ➡️ Un devis accepté génère une facture
---
## ✅ Validations
- ✅ Numéro unique
- ✅ Client obligatoire
- ✅ Au moins une ligne
- ✅ Montants positifs
- ✅ Date validité > date émission
- ✅ Taux TVA entre 0 et 100
---
## 📚 Références
- [Concept CLIENT](./02-CLIENT.md)
- [Concept CHANTIER](./01-CHANTIER.md)
- [Concept BUDGET](./10-BUDGET.md)
---
**Dernière mise à jour**: 2025-09-30
**Version**: 1.0