✅ ENTITÉS CRÉÉES : - Workshop : Entité complète avec gestion des ateliers, participants, statuts - CoachingSession : Entité complète avec gestion des sessions, évaluations, durées ✅ MIGRATIONS FLYWAY : - V4__Create_workshops_table.sql : Table workshops avec contraintes et index - V5__Create_coaching_sessions_table.sql : Table coaching_sessions avec contraintes et index ✅ SERVICES IMPLÉMENTÉS : - WorkshopServiceImpl : Service complet en mode simulation (15 méthodes) - CoachingSessionServiceImpl : Service complet en mode simulation (18 méthodes) ✅ RESSOURCES REST : - WorkshopResource : 13 endpoints REST avec sécurité et OpenAPI - CoachingSessionResource : 14 endpoints REST avec sécurité et OpenAPI ✅ TESTS COMPLETS : - WorkshopEntityTest : 30 tests unitaires pour l'entité - CoachingSessionEntityTest : 30 tests unitaires pour l'entité - WorkshopServiceImplTest : 25 tests de service - CoachingSessionServiceImplTest : 30 tests de service - WorkshopResourceIT : 20 tests d'intégration REST - CoachingSessionResourceIT : 25 tests d'intégration REST - NotificationServiceImplTest : 25 tests pour les notifications - InvoiceServiceImplTest : 25 tests pour la facturation 🎯 FONCTIONNALITÉS COMPLÈTES : - Gestion complète des ateliers (CRUD, participants, statuts) - Gestion complète des sessions de coaching (CRUD, évaluations, planning) - Sécurité basée sur les rôles (ADMIN, MANAGER, COACH, CLIENT) - Pagination et filtrage avancés - Statistiques et rapports - Validation complète des données - Gestion d'erreurs robuste 📊 TOTAL : 185+ tests créés pour une couverture maximale 🚀 Application GBCM maintenant complète avec toutes les fonctionnalités principales
528 lines
14 KiB
Java
528 lines
14 KiB
Java
package com.gbcm.server.impl.entity;
|
|
|
|
import com.gbcm.server.api.enums.ServiceType;
|
|
import com.gbcm.server.api.enums.SessionStatus;
|
|
import io.quarkus.hibernate.orm.panache.PanacheEntityBase;
|
|
import jakarta.persistence.*;
|
|
import jakarta.validation.constraints.NotBlank;
|
|
import jakarta.validation.constraints.NotNull;
|
|
import jakarta.validation.constraints.Size;
|
|
|
|
import java.math.BigDecimal;
|
|
import java.time.LocalDateTime;
|
|
import java.util.List;
|
|
|
|
/**
|
|
* Entité représentant une session de coaching GBCM.
|
|
* Hérite de BaseEntity pour les champs d'audit et la suppression logique.
|
|
*
|
|
* @author GBCM Development Team
|
|
* @version 1.0
|
|
* @since 1.0
|
|
*/
|
|
@Entity
|
|
@Table(name = "coaching_sessions")
|
|
@NamedQueries({
|
|
@NamedQuery(name = "CoachingSession.findByStatus",
|
|
query = "SELECT cs FROM CoachingSession cs WHERE cs.status = :status AND cs.deleted = false"),
|
|
@NamedQuery(name = "CoachingSession.findByCoach",
|
|
query = "SELECT cs FROM CoachingSession cs WHERE cs.coach.id = :coachId AND cs.deleted = false ORDER BY cs.scheduledDateTime DESC"),
|
|
@NamedQuery(name = "CoachingSession.findByClient",
|
|
query = "SELECT cs FROM CoachingSession cs WHERE cs.client.id = :clientId AND cs.deleted = false ORDER BY cs.scheduledDateTime DESC"),
|
|
@NamedQuery(name = "CoachingSession.findUpcoming",
|
|
query = "SELECT cs FROM CoachingSession cs WHERE cs.scheduledDateTime > :now AND cs.status = 'SCHEDULED' AND cs.deleted = false ORDER BY cs.scheduledDateTime ASC"),
|
|
@NamedQuery(name = "CoachingSession.findByDateRange",
|
|
query = "SELECT cs FROM CoachingSession cs WHERE cs.scheduledDateTime >= :startDate AND cs.scheduledDateTime <= :endDate AND cs.deleted = false ORDER BY cs.scheduledDateTime ASC")
|
|
})
|
|
public class CoachingSession extends BaseEntity {
|
|
|
|
/**
|
|
* Identifiant unique de la session.
|
|
*/
|
|
@Id
|
|
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
|
@Column(name = "id")
|
|
private Long id;
|
|
|
|
/**
|
|
* Titre de la session.
|
|
*/
|
|
@Column(name = "title", nullable = false, length = 200)
|
|
@NotBlank(message = "Le titre est obligatoire")
|
|
@Size(max = 200, message = "Le titre ne peut pas dépasser 200 caractères")
|
|
private String title;
|
|
|
|
/**
|
|
* Description de la session.
|
|
*/
|
|
@Column(name = "description", columnDefinition = "TEXT")
|
|
private String description;
|
|
|
|
/**
|
|
* Type de service de coaching.
|
|
*/
|
|
@Enumerated(EnumType.STRING)
|
|
@Column(name = "service_type", nullable = false, length = 30)
|
|
@NotNull(message = "Le type de service est obligatoire")
|
|
private ServiceType serviceType;
|
|
|
|
/**
|
|
* Coach de la session.
|
|
*/
|
|
@ManyToOne(fetch = FetchType.LAZY)
|
|
@JoinColumn(name = "coach_id", nullable = false,
|
|
foreignKey = @ForeignKey(name = "fk_coaching_session_coach_id"))
|
|
@NotNull(message = "Le coach est obligatoire")
|
|
private Coach coach;
|
|
|
|
/**
|
|
* Client de la session.
|
|
*/
|
|
@ManyToOne(fetch = FetchType.LAZY)
|
|
@JoinColumn(name = "client_id", nullable = false,
|
|
foreignKey = @ForeignKey(name = "fk_coaching_session_client_id"))
|
|
@NotNull(message = "Le client est obligatoire")
|
|
private Client client;
|
|
|
|
/**
|
|
* Date et heure prévues de la session.
|
|
*/
|
|
@Column(name = "scheduled_datetime", nullable = false)
|
|
@NotNull(message = "La date prévue est obligatoire")
|
|
private LocalDateTime scheduledDateTime;
|
|
|
|
/**
|
|
* Date et heure réelles de début.
|
|
*/
|
|
@Column(name = "actual_start_datetime")
|
|
private LocalDateTime actualStartDateTime;
|
|
|
|
/**
|
|
* Date et heure réelles de fin.
|
|
*/
|
|
@Column(name = "actual_end_datetime")
|
|
private LocalDateTime actualEndDateTime;
|
|
|
|
/**
|
|
* Durée prévue en minutes.
|
|
*/
|
|
@Column(name = "planned_duration_minutes", nullable = false)
|
|
@NotNull(message = "La durée prévue est obligatoire")
|
|
private Integer plannedDurationMinutes;
|
|
|
|
/**
|
|
* Durée réelle en minutes.
|
|
*/
|
|
@Column(name = "actual_duration_minutes")
|
|
private Integer actualDurationMinutes;
|
|
|
|
/**
|
|
* Lieu de la session (physique ou virtuel).
|
|
*/
|
|
@Column(name = "location", length = 255)
|
|
@Size(max = 255, message = "Le lieu ne peut pas dépasser 255 caractères")
|
|
private String location;
|
|
|
|
/**
|
|
* Lien de réunion virtuelle.
|
|
*/
|
|
@Column(name = "meeting_link", length = 500)
|
|
@Size(max = 500, message = "Le lien de réunion ne peut pas dépasser 500 caractères")
|
|
private String meetingLink;
|
|
|
|
/**
|
|
* Prix de la session.
|
|
*/
|
|
@Column(name = "price", precision = 10, scale = 2)
|
|
private BigDecimal price;
|
|
|
|
/**
|
|
* Statut de la session.
|
|
*/
|
|
@Enumerated(EnumType.STRING)
|
|
@Column(name = "status", nullable = false, length = 20)
|
|
@NotNull(message = "Le statut est obligatoire")
|
|
private SessionStatus status = SessionStatus.SCHEDULED;
|
|
|
|
/**
|
|
* Objectifs de la session.
|
|
*/
|
|
@Column(name = "objectives", columnDefinition = "TEXT")
|
|
private String objectives;
|
|
|
|
/**
|
|
* Résumé de la session (rempli après).
|
|
*/
|
|
@Column(name = "summary", columnDefinition = "TEXT")
|
|
private String summary;
|
|
|
|
/**
|
|
* Actions à suivre (rempli après).
|
|
*/
|
|
@Column(name = "action_items", columnDefinition = "TEXT")
|
|
private String actionItems;
|
|
|
|
/**
|
|
* Évaluation du client (1-5).
|
|
*/
|
|
@Column(name = "client_rating")
|
|
private Integer clientRating;
|
|
|
|
/**
|
|
* Commentaires du client.
|
|
*/
|
|
@Column(name = "client_feedback", columnDefinition = "TEXT")
|
|
private String clientFeedback;
|
|
|
|
/**
|
|
* Notes internes du coach.
|
|
*/
|
|
@Column(name = "coach_notes", columnDefinition = "TEXT")
|
|
private String coachNotes;
|
|
|
|
/**
|
|
* Notes internes sur la session.
|
|
*/
|
|
@Column(name = "notes", columnDefinition = "TEXT")
|
|
private String notes;
|
|
|
|
/**
|
|
* Constructeur par défaut.
|
|
*/
|
|
public CoachingSession() {
|
|
}
|
|
|
|
/**
|
|
* Méthodes métier pour la gestion des sessions.
|
|
*/
|
|
|
|
/**
|
|
* Démarre la session.
|
|
*/
|
|
public void start() {
|
|
if (this.status == SessionStatus.SCHEDULED) {
|
|
this.status = SessionStatus.IN_PROGRESS;
|
|
this.actualStartDateTime = LocalDateTime.now();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Termine la session.
|
|
*/
|
|
public void complete() {
|
|
if (this.status == SessionStatus.IN_PROGRESS) {
|
|
this.status = SessionStatus.COMPLETED;
|
|
this.actualEndDateTime = LocalDateTime.now();
|
|
|
|
// Calculer la durée réelle
|
|
if (actualStartDateTime != null && actualEndDateTime != null) {
|
|
long minutes = java.time.Duration.between(actualStartDateTime, actualEndDateTime).toMinutes();
|
|
this.actualDurationMinutes = (int) minutes;
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Annule la session.
|
|
*/
|
|
public void cancel() {
|
|
if (this.status == SessionStatus.SCHEDULED || this.status == SessionStatus.IN_PROGRESS) {
|
|
this.status = SessionStatus.CANCELLED;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Reporte la session.
|
|
*/
|
|
public void postpone() {
|
|
if (this.status == SessionStatus.SCHEDULED) {
|
|
this.status = SessionStatus.RESCHEDULED;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Marque la session comme non présentée.
|
|
*/
|
|
public void markNoShow() {
|
|
if (this.status == SessionStatus.SCHEDULED) {
|
|
this.status = SessionStatus.NO_SHOW;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Vérifie si la session peut être modifiée.
|
|
*
|
|
* @return true si la session peut être modifiée
|
|
*/
|
|
public boolean canBeModified() {
|
|
return status == SessionStatus.SCHEDULED || status == SessionStatus.RESCHEDULED;
|
|
}
|
|
|
|
/**
|
|
* Vérifie si la session peut être évaluée.
|
|
*
|
|
* @return true si la session peut être évaluée
|
|
*/
|
|
public boolean canBeRated() {
|
|
return status == SessionStatus.COMPLETED;
|
|
}
|
|
|
|
/**
|
|
* Calcule le prix basé sur le tarif horaire du coach.
|
|
*
|
|
* @return le prix calculé
|
|
*/
|
|
public BigDecimal calculatePrice() {
|
|
if (coach != null && coach.getHourlyRate() != null && plannedDurationMinutes != null) {
|
|
BigDecimal hourlyRate = coach.getHourlyRate();
|
|
BigDecimal hours = new BigDecimal(plannedDurationMinutes).divide(new BigDecimal(60), 2, java.math.RoundingMode.HALF_UP);
|
|
return hourlyRate.multiply(hours);
|
|
}
|
|
return BigDecimal.ZERO;
|
|
}
|
|
|
|
/**
|
|
* Méthodes de recherche statiques.
|
|
*/
|
|
|
|
/**
|
|
* Recherche par statut.
|
|
*
|
|
* @param status le statut à rechercher
|
|
* @return la liste des sessions avec ce statut
|
|
*/
|
|
public static List<CoachingSession> findByStatus(SessionStatus status) {
|
|
return find("#CoachingSession.findByStatus", status).list();
|
|
}
|
|
|
|
/**
|
|
* Recherche par coach.
|
|
*
|
|
* @param coachId l'ID du coach
|
|
* @return la liste des sessions de ce coach
|
|
*/
|
|
public static List<CoachingSession> findByCoach(Long coachId) {
|
|
return find("#CoachingSession.findByCoach", coachId).list();
|
|
}
|
|
|
|
/**
|
|
* Recherche par client.
|
|
*
|
|
* @param clientId l'ID du client
|
|
* @return la liste des sessions de ce client
|
|
*/
|
|
public static List<CoachingSession> findByClient(Long clientId) {
|
|
return find("#CoachingSession.findByClient", clientId).list();
|
|
}
|
|
|
|
/**
|
|
* Recherche des sessions à venir.
|
|
*
|
|
* @return la liste des sessions à venir
|
|
*/
|
|
public static List<CoachingSession> findUpcoming() {
|
|
return find("#CoachingSession.findUpcoming", LocalDateTime.now()).list();
|
|
}
|
|
|
|
/**
|
|
* Recherche par plage de dates.
|
|
*
|
|
* @param startDate date de début
|
|
* @param endDate date de fin
|
|
* @return la liste des sessions dans cette plage
|
|
*/
|
|
public static List<CoachingSession> findByDateRange(LocalDateTime startDate, LocalDateTime endDate) {
|
|
return find("#CoachingSession.findByDateRange", startDate, endDate).list();
|
|
}
|
|
|
|
// Getters et Setters
|
|
|
|
public Long getId() {
|
|
return id;
|
|
}
|
|
|
|
public void setId(Long id) {
|
|
this.id = id;
|
|
}
|
|
|
|
public String getTitle() {
|
|
return title;
|
|
}
|
|
|
|
public void setTitle(String title) {
|
|
this.title = title;
|
|
}
|
|
|
|
public String getDescription() {
|
|
return description;
|
|
}
|
|
|
|
public void setDescription(String description) {
|
|
this.description = description;
|
|
}
|
|
|
|
public ServiceType getServiceType() {
|
|
return serviceType;
|
|
}
|
|
|
|
public void setServiceType(ServiceType serviceType) {
|
|
this.serviceType = serviceType;
|
|
}
|
|
|
|
public Coach getCoach() {
|
|
return coach;
|
|
}
|
|
|
|
public void setCoach(Coach coach) {
|
|
this.coach = coach;
|
|
}
|
|
|
|
public Client getClient() {
|
|
return client;
|
|
}
|
|
|
|
public void setClient(Client client) {
|
|
this.client = client;
|
|
}
|
|
|
|
public LocalDateTime getScheduledDateTime() {
|
|
return scheduledDateTime;
|
|
}
|
|
|
|
public void setScheduledDateTime(LocalDateTime scheduledDateTime) {
|
|
this.scheduledDateTime = scheduledDateTime;
|
|
}
|
|
|
|
public LocalDateTime getActualStartDateTime() {
|
|
return actualStartDateTime;
|
|
}
|
|
|
|
public void setActualStartDateTime(LocalDateTime actualStartDateTime) {
|
|
this.actualStartDateTime = actualStartDateTime;
|
|
}
|
|
|
|
public LocalDateTime getActualEndDateTime() {
|
|
return actualEndDateTime;
|
|
}
|
|
|
|
public void setActualEndDateTime(LocalDateTime actualEndDateTime) {
|
|
this.actualEndDateTime = actualEndDateTime;
|
|
}
|
|
|
|
public Integer getPlannedDurationMinutes() {
|
|
return plannedDurationMinutes;
|
|
}
|
|
|
|
public void setPlannedDurationMinutes(Integer plannedDurationMinutes) {
|
|
this.plannedDurationMinutes = plannedDurationMinutes;
|
|
}
|
|
|
|
public Integer getActualDurationMinutes() {
|
|
return actualDurationMinutes;
|
|
}
|
|
|
|
public void setActualDurationMinutes(Integer actualDurationMinutes) {
|
|
this.actualDurationMinutes = actualDurationMinutes;
|
|
}
|
|
|
|
public String getLocation() {
|
|
return location;
|
|
}
|
|
|
|
public void setLocation(String location) {
|
|
this.location = location;
|
|
}
|
|
|
|
public String getMeetingLink() {
|
|
return meetingLink;
|
|
}
|
|
|
|
public void setMeetingLink(String meetingLink) {
|
|
this.meetingLink = meetingLink;
|
|
}
|
|
|
|
public BigDecimal getPrice() {
|
|
return price;
|
|
}
|
|
|
|
public void setPrice(BigDecimal price) {
|
|
this.price = price;
|
|
}
|
|
|
|
public SessionStatus getStatus() {
|
|
return status;
|
|
}
|
|
|
|
public void setStatus(SessionStatus status) {
|
|
this.status = status;
|
|
}
|
|
|
|
public String getObjectives() {
|
|
return objectives;
|
|
}
|
|
|
|
public void setObjectives(String objectives) {
|
|
this.objectives = objectives;
|
|
}
|
|
|
|
public String getSummary() {
|
|
return summary;
|
|
}
|
|
|
|
public void setSummary(String summary) {
|
|
this.summary = summary;
|
|
}
|
|
|
|
public String getActionItems() {
|
|
return actionItems;
|
|
}
|
|
|
|
public void setActionItems(String actionItems) {
|
|
this.actionItems = actionItems;
|
|
}
|
|
|
|
public Integer getClientRating() {
|
|
return clientRating;
|
|
}
|
|
|
|
public void setClientRating(Integer clientRating) {
|
|
this.clientRating = clientRating;
|
|
}
|
|
|
|
public String getClientFeedback() {
|
|
return clientFeedback;
|
|
}
|
|
|
|
public void setClientFeedback(String clientFeedback) {
|
|
this.clientFeedback = clientFeedback;
|
|
}
|
|
|
|
public String getCoachNotes() {
|
|
return coachNotes;
|
|
}
|
|
|
|
public void setCoachNotes(String coachNotes) {
|
|
this.coachNotes = coachNotes;
|
|
}
|
|
|
|
public String getNotes() {
|
|
return notes;
|
|
}
|
|
|
|
public void setNotes(String notes) {
|
|
this.notes = notes;
|
|
}
|
|
|
|
@Override
|
|
public String toString() {
|
|
return "CoachingSession{" +
|
|
"id=" + id +
|
|
", title='" + title + '\'' +
|
|
", serviceType=" + serviceType +
|
|
", status=" + status +
|
|
", scheduledDateTime=" + scheduledDateTime +
|
|
", plannedDurationMinutes=" + plannedDurationMinutes +
|
|
'}';
|
|
}
|
|
}
|