# 👤 CONCEPT: CLIENT ## 📌 Vue d'ensemble Le concept **CLIENT** représente les clients de l'entreprise BTP, qu'ils soient **particuliers** ou **professionnels**. Il gère toutes les informations de contact, coordonnées, et relations avec les chantiers et devis. **Importance**: ⭐⭐⭐⭐⭐ (Concept fondamental) --- ## 🗂️ Fichiers concernés ### **Entités JPA** (`domain/core/entity/`) | Fichier | Description | Lignes | |---------|-------------|--------| | `Client.java` | Entité principale représentant un client | 113 | | `TypeClient.java` | Enum des types de clients (PARTICULIER, PROFESSIONNEL) | 21 | ### **DTOs** (`domain/shared/dto/`) | Fichier | Description | |---------|-------------| | `ClientCreateDTO.java` | DTO pour créer/modifier un client | ### **Services** (`application/service/`) | Fichier | Description | |---------|-------------| | `ClientService.java` | Service métier pour la gestion des clients | ### **Resources (API REST)** (`adapter/http/`) | Fichier | Description | |---------|-------------| | `ClientResource.java` | Endpoints REST pour les clients | --- ## 📊 Modèle de données ### **Entité Client** ````java @Entity @Table(name = "clients") @Data @Builder public class Client extends PanacheEntityBase { @Id @GeneratedValue(strategy = GenerationType.UUID) private UUID id; @NotBlank(message = "Le nom est obligatoire") @Column(name = "nom", nullable = false, length = 100) private String nom; @NotBlank(message = "Le prénom est obligatoire") @Column(name = "prenom", nullable = false, length = 100) private String prenom; @Column(name = "entreprise", length = 200) private String entreprise; @Email(message = "Email invalide") @Column(name = "email", unique = true, length = 255) private String email; @Pattern(regexp = "^(?:(?:\\+|00)33|0)\\s*[1-9](?:[\\s.-]*\\d{2}){4}$") @Column(name = "telephone", length = 20) private String telephone; @Enumerated(EnumType.STRING) @Column(name = "type_client", length = 20) private TypeClient type = TypeClient.PARTICULIER; // Relations @OneToMany(mappedBy = "client", cascade = CascadeType.ALL) private List chantiers; @OneToMany(mappedBy = "client", cascade = CascadeType.ALL) private List devis; } ```` ### **Enum TypeClient** ````java public enum TypeClient { PARTICULIER("Particulier"), PROFESSIONNEL("Professionnel"); private final String label; TypeClient(String label) { this.label = label; } public String getLabel() { return label; } } ```` ### **Champs principaux** | Champ | Type | Obligatoire | Description | |-------|------|-------------|-------------| | `id` | UUID | Oui | Identifiant unique | | `nom` | String(100) | Oui | Nom du client | | `prenom` | String(100) | Oui | Prénom du client | | `entreprise` | String(200) | Non | Nom de l'entreprise (si professionnel) | | `email` | String(255) | Non | Email (unique) | | `telephone` | String(20) | Non | Téléphone (format français validé) | | `adresse` | String(500) | Non | Adresse postale | | `codePostal` | String(10) | Non | Code postal | | `ville` | String(100) | Non | Ville | | `numeroTVA` | String(20) | Non | Numéro de TVA intracommunautaire | | `siret` | String(14) | Non | Numéro SIRET (entreprises françaises) | | `type` | TypeClient | Oui | Type de client (défaut: PARTICULIER) | | `actif` | Boolean | Oui | Client actif (défaut: true) | | `dateCreation` | LocalDateTime | Auto | Date de création | | `dateModification` | LocalDateTime | Auto | Date de modification | ### **Relations** | Relation | Type | Entité cible | Description | |----------|------|--------------|-------------| | `chantiers` | OneToMany | Chantier | Liste des chantiers du client | | `devis` | OneToMany | Devis | Liste des devis du client | --- ## 🔌 API REST ### **Base URL**: `/api/v1/clients` ### **Endpoints disponibles** | Méthode | Endpoint | Description | Permission | |---------|----------|-------------|------------| | GET | `/api/v1/clients` | Liste tous les clients | CLIENTS_READ | | GET | `/api/v1/clients/{id}` | Détails d'un client | CLIENTS_READ | | POST | `/api/v1/clients` | Créer un client | CLIENTS_CREATE | | PUT | `/api/v1/clients/{id}` | Modifier un client | CLIENTS_UPDATE | | DELETE | `/api/v1/clients/{id}` | Supprimer un client | CLIENTS_DELETE | | GET | `/api/v1/clients/search` | Rechercher des clients | CLIENTS_READ | | GET | `/api/v1/clients/stats` | Statistiques clients | CLIENTS_READ | ### **Paramètres de requête (Query Params)** | Paramètre | Type | Description | Exemple | |-----------|------|-------------|---------| | `page` | Integer | Numéro de page (0-based) | `?page=0` | | `size` | Integer | Taille de la page | `?size=20` | | `search` | String | Terme de recherche | `?search=dupont` | --- ## 💻 Exemples d'utilisation ### **1. Récupérer tous les clients** ```bash curl -X GET http://localhost:8080/api/v1/clients \ -H "Accept: application/json" ``` **Réponse** (200 OK): ```json [ { "id": "550e8400-e29b-41d4-a716-446655440000", "nom": "Dupont", "prenom": "Jean", "email": "jean.dupont@example.com", "telephone": "+33 6 12 34 56 78", "adresse": "123 Rue de la Paix", "codePostal": "75002", "ville": "Paris", "type": "PARTICULIER", "actif": true, "dateCreation": "2025-01-15T10:30:00" }, { "id": "660e8400-e29b-41d4-a716-446655440001", "nom": "Martin", "prenom": "Sophie", "entreprise": "BTP Solutions SARL", "email": "contact@btpsolutions.fr", "telephone": "+33 1 23 45 67 89", "siret": "12345678901234", "numeroTVA": "FR12345678901", "type": "PROFESSIONNEL", "actif": true } ] ``` ### **2. Créer un nouveau client particulier** ```bash curl -X POST http://localhost:8080/api/v1/clients \ -H "Content-Type: application/json" \ -d '{ "nom": "Durand", "prenom": "Pierre", "email": "pierre.durand@example.com", "telephone": "+33 6 98 76 54 32", "adresse": "45 Avenue des Champs", "codePostal": "75008", "ville": "Paris", "type": "PARTICULIER" }' ``` **Réponse** (201 Created): ```json { "id": "generated-uuid", "nom": "Durand", "prenom": "Pierre", "email": "pierre.durand@example.com", "type": "PARTICULIER", "actif": true, "dateCreation": "2025-09-30T14:25:00" } ``` ### **3. Créer un client professionnel** ```bash curl -X POST http://localhost:8080/api/v1/clients \ -H "Content-Type: application/json" \ -d '{ "nom": "Entreprise", "prenom": "Construction", "entreprise": "ABC Construction SA", "email": "contact@abc-construction.fr", "telephone": "+33 1 45 67 89 01", "adresse": "10 Boulevard Haussmann", "codePostal": "75009", "ville": "Paris", "siret": "98765432109876", "numeroTVA": "FR98765432109", "type": "PROFESSIONNEL" }' ``` ### **4. Rechercher des clients** ```bash # Par nom curl -X GET "http://localhost:8080/api/v1/clients/search?search=dupont" # Avec pagination curl -X GET "http://localhost:8080/api/v1/clients?page=0&size=10" ``` ### **5. Obtenir les statistiques** ```bash curl -X GET http://localhost:8080/api/v1/clients/stats ``` **Réponse**: ```json { "totalClients": 156, "clientsActifs": 142, "particuliers": 98, "professionnels": 58, "nouveauxCeMois": 12, "avecChantiers": 87, "sansChantiers": 69 } ``` --- ## 🔧 Services métier ### **ClientService** **Méthodes principales**: | Méthode | Description | Retour | |---------|-------------|--------| | `findAll()` | Récupère tous les clients | `List` | | `findAll(int page, int size)` | Récupère avec pagination | `List` | | `findById(UUID id)` | Récupère par ID | `Optional` | | `findByIdRequired(UUID id)` | Récupère par ID (exception si absent) | `Client` | | `create(ClientCreateDTO dto)` | Crée un client | `Client` | | `update(UUID id, ClientCreateDTO dto)` | Met à jour | `Client` | | `delete(UUID id)` | Supprime (soft delete) | `void` | | `search(String term)` | Recherche textuelle | `List` | | `findByType(TypeClient type)` | Filtre par type | `List` | | `findActifs()` | Clients actifs uniquement | `List` | | `getStatistics()` | Statistiques globales | `Object` | --- ## 🔐 Permissions requises | Permission | Description | Rôles autorisés | |------------|-------------|-----------------| | `CLIENTS_READ` | Lecture des clients | ADMIN, MANAGER, CHEF_CHANTIER, COMPTABLE | | `CLIENTS_CREATE` | Création de clients | ADMIN, MANAGER | | `CLIENTS_UPDATE` | Modification de clients | ADMIN, MANAGER | | `CLIENTS_DELETE` | Suppression de clients | ADMIN, MANAGER | | `CLIENTS_ASSIGN` | Assignation à des chantiers | ADMIN, MANAGER | --- ## 📈 Relations avec autres concepts ### **Dépendances directes**: - **CHANTIER** ➡️ Un client peut avoir plusieurs chantiers - **DEVIS** ➡️ Un client peut avoir plusieurs devis - **FACTURE** ➡️ Un client peut avoir plusieurs factures (via chantiers) ### **Utilisé par**: - **CHANTIER** - Chaque chantier appartient à un client - **DEVIS** - Chaque devis est lié à un client - **FACTURE** - Chaque facture est adressée à un client --- ## ✅ Validations ### **Validations automatiques**: - ✅ **Nom** : Obligatoire, max 100 caractères - ✅ **Prénom** : Obligatoire, max 100 caractères - ✅ **Email** : Format email valide, unique - ✅ **Téléphone** : Format français valide (regex) - ✅ **SIRET** : 14 caractères (si renseigné) - ✅ **Type** : PARTICULIER ou PROFESSIONNEL ### **Règles métier**: - Un client professionnel devrait avoir un SIRET et/ou numéro TVA - Un client particulier n'a généralement pas d'entreprise - L'email doit être unique dans le système - Le téléphone doit respecter le format français --- ## 🧪 Tests ### **Tests unitaires** - Fichier: `ClientServiceTest.java` - Couverture: Logique métier, validations, recherche ### **Tests d'intégration** - Fichier: `ClientResourceTest.java` - Couverture: Endpoints REST, sérialisation JSON ### **Commande pour exécuter les tests**: ```bash cd btpxpress-server ./mvnw test -Dtest=ClientServiceTest ./mvnw test -Dtest=ClientResourceTest ``` --- ## 📚 Références - [API Documentation complète](../API.md#clients) - [Schéma de base de données](../DATABASE.md#table-clients) - [Guide d'architecture](../architecture/domain-model.md#client) - [Service ClientService](../../src/main/java/dev/lions/btpxpress/application/service/ClientService.java) - [Resource ClientResource](../../src/main/java/dev/lions/btpxpress/adapter/http/ClientResource.java) --- **Dernière mise à jour**: 2025-09-30 **Version**: 1.0 **Auteur**: Documentation BTPXpress