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 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 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 findByClient(Long clientId) { return find("#CoachingSession.findByClient", clientId).list(); } /** * Recherche des sessions à venir. * * @return la liste des sessions à venir */ public static List 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 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 + '}'; } }