Initial commit
This commit is contained in:
@@ -0,0 +1,362 @@
|
||||
package dev.lions.btpxpress.application.service;
|
||||
|
||||
import dev.lions.btpxpress.domain.core.entity.Disponibilite;
|
||||
import dev.lions.btpxpress.domain.core.entity.Employe;
|
||||
import dev.lions.btpxpress.domain.core.entity.TypeDisponibilite;
|
||||
import dev.lions.btpxpress.domain.infrastructure.repository.DisponibiliteRepository;
|
||||
import dev.lions.btpxpress.domain.infrastructure.repository.EmployeRepository;
|
||||
import jakarta.enterprise.context.ApplicationScoped;
|
||||
import jakarta.inject.Inject;
|
||||
import jakarta.transaction.Transactional;
|
||||
import jakarta.ws.rs.BadRequestException;
|
||||
import jakarta.ws.rs.NotFoundException;
|
||||
import java.time.LocalDate;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* Service de gestion des disponibilités - Architecture 2025 RH: Logique complète de gestion des
|
||||
* disponibilités employés
|
||||
*/
|
||||
@ApplicationScoped
|
||||
public class DisponibiliteService {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(DisponibiliteService.class);
|
||||
|
||||
@Inject DisponibiliteRepository disponibiliteRepository;
|
||||
|
||||
@Inject EmployeRepository employeRepository;
|
||||
|
||||
// === MÉTHODES DE CONSULTATION ===
|
||||
|
||||
public List<Disponibilite> findAll() {
|
||||
logger.debug("Recherche de toutes les disponibilités");
|
||||
return disponibiliteRepository.findActifs();
|
||||
}
|
||||
|
||||
public List<Disponibilite> findAll(int page, int size) {
|
||||
logger.debug("Recherche des disponibilités - page: {}, taille: {}", page, size);
|
||||
return disponibiliteRepository.findActifs(page, size);
|
||||
}
|
||||
|
||||
public Optional<Disponibilite> findById(UUID id) {
|
||||
logger.debug("Recherche de la disponibilité avec l'ID: {}", id);
|
||||
return disponibiliteRepository.findByIdOptional(id);
|
||||
}
|
||||
|
||||
public Disponibilite findByIdRequired(UUID id) {
|
||||
return findById(id)
|
||||
.orElseThrow(() -> new NotFoundException("Disponibilité non trouvée avec l'ID: " + id));
|
||||
}
|
||||
|
||||
public List<Disponibilite> findByEmployeId(UUID employeId) {
|
||||
logger.debug("Recherche des disponibilités pour l'employé: {}", employeId);
|
||||
return disponibiliteRepository.findByEmployeId(employeId);
|
||||
}
|
||||
|
||||
public List<Disponibilite> findByType(TypeDisponibilite type) {
|
||||
logger.debug("Recherche des disponibilités par type: {}", type);
|
||||
return disponibiliteRepository.findByType(type);
|
||||
}
|
||||
|
||||
public List<Disponibilite> findByDateRange(LocalDateTime dateDebut, LocalDateTime dateFin) {
|
||||
logger.debug("Recherche des disponibilités entre {} et {}", dateDebut, dateFin);
|
||||
validateDateRange(dateDebut, dateFin);
|
||||
return disponibiliteRepository.findByDateRange(dateDebut, dateFin);
|
||||
}
|
||||
|
||||
public List<Disponibilite> findEnAttente() {
|
||||
logger.debug("Recherche des demandes en attente d'approbation");
|
||||
return disponibiliteRepository.findEnAttente();
|
||||
}
|
||||
|
||||
public List<Disponibilite> findApprouvees() {
|
||||
logger.debug("Recherche des disponibilités approuvées");
|
||||
return disponibiliteRepository.findApprouvees();
|
||||
}
|
||||
|
||||
public List<Disponibilite> findActuelles() {
|
||||
logger.debug("Recherche des disponibilités actuellement actives");
|
||||
return disponibiliteRepository.findActuelles();
|
||||
}
|
||||
|
||||
public List<Disponibilite> findFutures() {
|
||||
logger.debug("Recherche des disponibilités futures");
|
||||
return disponibiliteRepository.findFutures();
|
||||
}
|
||||
|
||||
public List<Disponibilite> findPourPeriode(LocalDate dateDebut, LocalDate dateFin) {
|
||||
logger.debug("Recherche des disponibilités pour la période {} - {}", dateDebut, dateFin);
|
||||
validateDateRange(dateDebut, dateFin);
|
||||
return disponibiliteRepository.findPourPeriode(dateDebut, dateFin);
|
||||
}
|
||||
|
||||
// === MÉTHODES CRUD ===
|
||||
|
||||
@Transactional
|
||||
public Disponibilite createDisponibilite(
|
||||
UUID employeId,
|
||||
LocalDateTime dateDebut,
|
||||
LocalDateTime dateFin,
|
||||
String typeStr,
|
||||
String motif) {
|
||||
logger.info("Création d'une nouvelle disponibilité pour l'employé: {}", employeId);
|
||||
|
||||
// Validation des données
|
||||
validateDisponibiliteData(employeId, dateDebut, dateFin, typeStr);
|
||||
TypeDisponibilite type = parseType(typeStr);
|
||||
|
||||
// Récupération de l'employé
|
||||
Employe employe =
|
||||
employeRepository
|
||||
.findByIdOptional(employeId)
|
||||
.orElseThrow(() -> new BadRequestException("Employé non trouvé: " + employeId));
|
||||
|
||||
// Vérification des conflits
|
||||
if (disponibiliteRepository.hasConflicts(employeId, dateDebut, dateFin, null)) {
|
||||
throw new BadRequestException("Une disponibilité existe déjà pour cette période");
|
||||
}
|
||||
|
||||
// Création de la disponibilité
|
||||
Disponibilite disponibilite =
|
||||
Disponibilite.builder()
|
||||
.employe(employe)
|
||||
.dateDebut(dateDebut)
|
||||
.dateFin(dateFin)
|
||||
.type(type)
|
||||
.motif(motif)
|
||||
.approuvee(false) // Par défaut en attente d'approbation
|
||||
.build();
|
||||
|
||||
disponibiliteRepository.persist(disponibilite);
|
||||
|
||||
logger.info(
|
||||
"Disponibilité créée avec succès pour {} du {} au {}",
|
||||
employe.getNom() + " " + employe.getPrenom(),
|
||||
dateDebut,
|
||||
dateFin);
|
||||
|
||||
return disponibilite;
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public Disponibilite updateDisponibilite(
|
||||
UUID id, LocalDateTime dateDebut, LocalDateTime dateFin, String motif) {
|
||||
logger.info("Mise à jour de la disponibilité: {}", id);
|
||||
|
||||
Disponibilite disponibilite = findByIdRequired(id);
|
||||
|
||||
// Validation des nouvelles données si fournies
|
||||
if (dateDebut != null && dateFin != null) {
|
||||
validateDateRange(dateDebut, dateFin);
|
||||
|
||||
// Vérifier les conflits (en excluant la disponibilité actuelle)
|
||||
if (disponibiliteRepository.hasConflicts(
|
||||
disponibilite.getEmploye().getId(), dateDebut, dateFin, id)) {
|
||||
throw new BadRequestException("Conflit avec une autre disponibilité pour cette période");
|
||||
}
|
||||
|
||||
disponibilite.setDateDebut(dateDebut);
|
||||
disponibilite.setDateFin(dateFin);
|
||||
}
|
||||
|
||||
if (motif != null) {
|
||||
disponibilite.setMotif(motif);
|
||||
}
|
||||
|
||||
disponibilite.setDateModification(LocalDateTime.now());
|
||||
disponibiliteRepository.persist(disponibilite);
|
||||
|
||||
logger.info("Disponibilité mise à jour avec succès");
|
||||
|
||||
return disponibilite;
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public Disponibilite approuverDisponibilite(UUID id) {
|
||||
logger.info("Approbation de la disponibilité: {}", id);
|
||||
|
||||
Disponibilite disponibilite = findByIdRequired(id);
|
||||
|
||||
if (disponibilite.getApprouvee()) {
|
||||
throw new BadRequestException("Cette disponibilité est déjà approuvée");
|
||||
}
|
||||
|
||||
disponibilite.setApprouvee(true);
|
||||
disponibilite.setDateModification(LocalDateTime.now());
|
||||
|
||||
disponibiliteRepository.persist(disponibilite);
|
||||
|
||||
logger.info(
|
||||
"Disponibilité approuvée avec succès pour l'employé: {}",
|
||||
disponibilite.getEmploye().getNom() + " " + disponibilite.getEmploye().getPrenom());
|
||||
|
||||
return disponibilite;
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public Disponibilite rejeterDisponibilite(UUID id, String raisonRejet) {
|
||||
logger.info("Rejet de la disponibilité: {} - Raison: {}", id, raisonRejet);
|
||||
|
||||
Disponibilite disponibilite = findByIdRequired(id);
|
||||
|
||||
if (disponibilite.getApprouvee()) {
|
||||
throw new BadRequestException("Impossible de rejeter une disponibilité déjà approuvée");
|
||||
}
|
||||
|
||||
// Ajouter la raison du rejet au motif
|
||||
String nouveauMotif =
|
||||
disponibilite.getMotif() != null
|
||||
? disponibilite.getMotif() + " [REJETÉE: " + raisonRejet + "]"
|
||||
: "[REJETÉE: " + raisonRejet + "]";
|
||||
|
||||
disponibilite.setMotif(nouveauMotif);
|
||||
disponibilite.setDateModification(LocalDateTime.now());
|
||||
|
||||
disponibiliteRepository.persist(disponibilite);
|
||||
|
||||
logger.info(
|
||||
"Disponibilité rejetée pour l'employé: {}",
|
||||
disponibilite.getEmploye().getNom() + " " + disponibilite.getEmploye().getPrenom());
|
||||
|
||||
return disponibilite;
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public void deleteDisponibilite(UUID id) {
|
||||
logger.info("Suppression de la disponibilité: {}", id);
|
||||
|
||||
Disponibilite disponibilite = findByIdRequired(id);
|
||||
|
||||
// Vérifier qu'on ne supprime pas une disponibilité en cours
|
||||
if (disponibilite.isActive()) {
|
||||
throw new BadRequestException("Impossible de supprimer une disponibilité en cours");
|
||||
}
|
||||
|
||||
disponibiliteRepository.delete(disponibilite);
|
||||
|
||||
logger.info("Disponibilité supprimée avec succès");
|
||||
}
|
||||
|
||||
// === MÉTHODES DE VALIDATION ET VÉRIFICATION ===
|
||||
|
||||
public boolean isEmployeDisponible(
|
||||
UUID employeId, LocalDateTime dateDebut, LocalDateTime dateFin) {
|
||||
logger.debug(
|
||||
"Vérification de disponibilité de l'employé {} du {} au {}", employeId, dateDebut, dateFin);
|
||||
|
||||
List<Disponibilite> conflits =
|
||||
disponibiliteRepository.findByEmployeIdAndDateRange(employeId, dateDebut, dateFin);
|
||||
|
||||
// Filtrer seulement les disponibilités approuvées qui rendent l'employé indisponible
|
||||
return conflits.stream()
|
||||
.filter(Disponibilite::getApprouvee)
|
||||
.filter(d -> isTypeBloquant(d.getType()))
|
||||
.findAny()
|
||||
.isEmpty();
|
||||
}
|
||||
|
||||
public List<Disponibilite> getConflicts(
|
||||
UUID employeId, LocalDateTime dateDebut, LocalDateTime dateFin, UUID excludeId) {
|
||||
logger.debug(
|
||||
"Recherche de conflits pour l'employé {} du {} au {}", employeId, dateDebut, dateFin);
|
||||
return disponibiliteRepository.findConflictuelles(employeId, dateDebut, dateFin, excludeId);
|
||||
}
|
||||
|
||||
// === MÉTHODES STATISTIQUES ===
|
||||
|
||||
public Object getStatistics() {
|
||||
logger.debug("Génération des statistiques des disponibilités");
|
||||
|
||||
return new Object() {
|
||||
public final long totalDisponibilites = disponibiliteRepository.count();
|
||||
public final long enAttente = disponibiliteRepository.countEnAttente();
|
||||
public final long approuvees = disponibiliteRepository.countApprouvees();
|
||||
public final long congesPayes =
|
||||
disponibiliteRepository.countByType(TypeDisponibilite.CONGE_PAYE);
|
||||
public final long arretsMaladie =
|
||||
disponibiliteRepository.countByType(TypeDisponibilite.ARRET_MALADIE);
|
||||
public final long formations =
|
||||
disponibiliteRepository.countByType(TypeDisponibilite.FORMATION);
|
||||
public final long absences = disponibiliteRepository.countByType(TypeDisponibilite.ABSENCE);
|
||||
};
|
||||
}
|
||||
|
||||
public List<Object[]> getStatsByType() {
|
||||
logger.debug("Génération des statistiques par type");
|
||||
return disponibiliteRepository.getStatsByType();
|
||||
}
|
||||
|
||||
public List<Object[]> getStatsByEmployee() {
|
||||
logger.debug("Génération des statistiques par employé");
|
||||
return disponibiliteRepository.getStatsByEmployee();
|
||||
}
|
||||
|
||||
public List<Disponibilite> getExpiringRequests(int jours) {
|
||||
logger.debug("Recherche des demandes expirant dans {} jours", jours);
|
||||
return disponibiliteRepository.findExpiringRequests(jours);
|
||||
}
|
||||
|
||||
// === MÉTHODES PRIVÉES DE VALIDATION ===
|
||||
|
||||
private void validateDisponibiliteData(
|
||||
UUID employeId, LocalDateTime dateDebut, LocalDateTime dateFin, String type) {
|
||||
if (employeId == null) {
|
||||
throw new BadRequestException("L'employé est obligatoire");
|
||||
}
|
||||
|
||||
validateDateRange(dateDebut, dateFin);
|
||||
|
||||
if (type == null || type.trim().isEmpty()) {
|
||||
throw new BadRequestException("Le type de disponibilité est obligatoire");
|
||||
}
|
||||
}
|
||||
|
||||
private void validateDateRange(LocalDateTime dateDebut, LocalDateTime dateFin) {
|
||||
if (dateDebut == null || dateFin == null) {
|
||||
throw new BadRequestException("Les dates de début et fin sont obligatoires");
|
||||
}
|
||||
|
||||
if (dateDebut.isAfter(dateFin)) {
|
||||
throw new BadRequestException("La date de début ne peut pas être après la date de fin");
|
||||
}
|
||||
|
||||
if (dateDebut.isBefore(LocalDateTime.now().minusHours(1))) {
|
||||
throw new BadRequestException("La disponibilité ne peut pas être créée dans le passé");
|
||||
}
|
||||
}
|
||||
|
||||
private void validateDateRange(LocalDate dateDebut, LocalDate dateFin) {
|
||||
if (dateDebut == null || dateFin == null) {
|
||||
throw new BadRequestException("Les dates de début et fin sont obligatoires");
|
||||
}
|
||||
|
||||
if (dateDebut.isAfter(dateFin)) {
|
||||
throw new BadRequestException("La date de début ne peut pas être après la date de fin");
|
||||
}
|
||||
}
|
||||
|
||||
private TypeDisponibilite parseType(String typeStr) {
|
||||
try {
|
||||
return TypeDisponibilite.valueOf(typeStr.toUpperCase());
|
||||
} catch (IllegalArgumentException e) {
|
||||
throw new BadRequestException(
|
||||
"Type de disponibilité invalide: "
|
||||
+ typeStr
|
||||
+ ". Valeurs autorisées: CONGE_PAYE, CONGE_SANS_SOLDE, ARRET_MALADIE, FORMATION,"
|
||||
+ " ABSENCE, HORAIRE_REDUIT");
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isTypeBloquant(TypeDisponibilite type) {
|
||||
// Les types qui rendent l'employé indisponible pour le travail
|
||||
return type == TypeDisponibilite.CONGE_PAYE
|
||||
|| type == TypeDisponibilite.CONGE_SANS_SOLDE
|
||||
|| type == TypeDisponibilite.ARRET_MALADIE
|
||||
|| type == TypeDisponibilite.ABSENCE;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user