Initial commit
This commit is contained in:
@@ -0,0 +1,610 @@
|
||||
package dev.lions.btpxpress.application.service;
|
||||
|
||||
import dev.lions.btpxpress.domain.core.entity.*;
|
||||
import dev.lions.btpxpress.domain.infrastructure.repository.MaterielRepository;
|
||||
import dev.lions.btpxpress.domain.infrastructure.repository.PlanningMaterielRepository;
|
||||
import dev.lions.btpxpress.domain.infrastructure.repository.ReservationMaterielRepository;
|
||||
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.time.temporal.ChronoUnit;
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* Service de gestion des plannings matériel ORCHESTRATION: Logique métier planning, conflits et
|
||||
* optimisation
|
||||
*/
|
||||
@ApplicationScoped
|
||||
public class PlanningMaterielService {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(PlanningMaterielService.class);
|
||||
|
||||
@Inject PlanningMaterielRepository planningRepository;
|
||||
|
||||
@Inject MaterielRepository materielRepository;
|
||||
|
||||
@Inject ReservationMaterielRepository reservationRepository;
|
||||
|
||||
// === OPÉRATIONS CRUD DE BASE ===
|
||||
|
||||
/** Récupère tous les plannings avec pagination */
|
||||
public List<PlanningMateriel> findAll(int page, int size) {
|
||||
logger.debug("Récupération des plannings - page: {}, size: {}", page, size);
|
||||
return planningRepository.findAllActifs(page, size);
|
||||
}
|
||||
|
||||
/** Récupère tous les plannings actifs */
|
||||
public List<PlanningMateriel> findAll() {
|
||||
return planningRepository.find("actif = true").list();
|
||||
}
|
||||
|
||||
/** Trouve un planning par ID avec exception si non trouvé */
|
||||
public PlanningMateriel findByIdRequired(UUID id) {
|
||||
return planningRepository
|
||||
.findByIdOptional(id)
|
||||
.orElseThrow(() -> new NotFoundException("Planning non trouvé avec l'ID: " + id));
|
||||
}
|
||||
|
||||
/** Trouve un planning par ID */
|
||||
public Optional<PlanningMateriel> findById(UUID id) {
|
||||
return planningRepository.findByIdOptional(id);
|
||||
}
|
||||
|
||||
// === RECHERCHES SPÉCIALISÉES ===
|
||||
|
||||
/** Trouve les plannings pour un matériel */
|
||||
public List<PlanningMateriel> findByMateriel(UUID materielId) {
|
||||
logger.debug("Recherche plannings pour matériel: {}", materielId);
|
||||
return planningRepository.findByMateriel(materielId);
|
||||
}
|
||||
|
||||
/** Trouve les plannings sur une période */
|
||||
public List<PlanningMateriel> findByPeriode(LocalDate dateDebut, LocalDate dateFin) {
|
||||
if (dateDebut.isAfter(dateFin)) {
|
||||
throw new BadRequestException("La date de début doit être antérieure à la date de fin");
|
||||
}
|
||||
return planningRepository.findByPeriode(dateDebut, dateFin);
|
||||
}
|
||||
|
||||
/** Trouve les plannings par statut */
|
||||
public List<PlanningMateriel> findByStatut(StatutPlanning statut) {
|
||||
return planningRepository.findByStatut(statut);
|
||||
}
|
||||
|
||||
/** Trouve les plannings par type */
|
||||
public List<PlanningMateriel> findByType(TypePlanning type) {
|
||||
return planningRepository.findByType(type);
|
||||
}
|
||||
|
||||
/** Recherche textuelle dans les plannings */
|
||||
public List<PlanningMateriel> search(String terme) {
|
||||
if (terme == null || terme.trim().isEmpty()) {
|
||||
return findAll();
|
||||
}
|
||||
return planningRepository.search(terme.trim());
|
||||
}
|
||||
|
||||
// === REQUÊTES MÉTIER SPÉCIALISÉES ===
|
||||
|
||||
/** Trouve les plannings avec conflits */
|
||||
public List<PlanningMateriel> findAvecConflits() {
|
||||
return planningRepository.findAvecConflits();
|
||||
}
|
||||
|
||||
/** Trouve les plannings nécessitant attention */
|
||||
public List<PlanningMateriel> findNecessitantAttention() {
|
||||
return planningRepository.findNecessitantAttention();
|
||||
}
|
||||
|
||||
/** Trouve les plannings en retard de validation */
|
||||
public List<PlanningMateriel> findEnRetardValidation() {
|
||||
return planningRepository.findEnRetardValidation();
|
||||
}
|
||||
|
||||
/** Trouve les plannings prioritaires */
|
||||
public List<PlanningMateriel> findPrioritaires() {
|
||||
return planningRepository.findPrioritaires();
|
||||
}
|
||||
|
||||
/** Trouve les plannings en cours */
|
||||
public List<PlanningMateriel> findEnCours() {
|
||||
return planningRepository.findEnCours();
|
||||
}
|
||||
|
||||
// === CRÉATION ET MODIFICATION ===
|
||||
|
||||
/** Crée un nouveau planning matériel */
|
||||
@Transactional
|
||||
public PlanningMateriel createPlanning(
|
||||
UUID materielId,
|
||||
String nomPlanning,
|
||||
String description,
|
||||
LocalDate dateDebut,
|
||||
LocalDate dateFin,
|
||||
TypePlanning type,
|
||||
String planificateur) {
|
||||
logger.info("Création planning matériel: {} pour matériel: {}", nomPlanning, materielId);
|
||||
|
||||
// Validation des données
|
||||
if (dateDebut.isAfter(dateFin)) {
|
||||
throw new BadRequestException("La date de début doit être antérieure à la date de fin");
|
||||
}
|
||||
|
||||
Materiel materiel =
|
||||
materielRepository
|
||||
.findByIdOptional(materielId)
|
||||
.orElseThrow(() -> new NotFoundException("Matériel non trouvé: " + materielId));
|
||||
|
||||
// Création du planning
|
||||
PlanningMateriel planning =
|
||||
PlanningMateriel.builder()
|
||||
.materiel(materiel)
|
||||
.nomPlanning(nomPlanning)
|
||||
.descriptionPlanning(description)
|
||||
.dateDebut(dateDebut)
|
||||
.dateFin(dateFin)
|
||||
.typePlanning(type)
|
||||
.planificateur(planificateur)
|
||||
.creePar(planificateur)
|
||||
.build();
|
||||
|
||||
// Génération automatique du nom si nécessaire
|
||||
planning.genererNomPlanning();
|
||||
|
||||
// Définition de la couleur par défaut selon le type
|
||||
if (planning.getCouleurPlanning() == null) {
|
||||
planning.setCouleurPlanning(type.getCouleurDefaut());
|
||||
}
|
||||
|
||||
planningRepository.persist(planning);
|
||||
|
||||
// Vérification des conflits immédiate
|
||||
verifierConflits(planning);
|
||||
|
||||
logger.info("Planning créé avec succès: {}", planning.getId());
|
||||
return planning;
|
||||
}
|
||||
|
||||
/** Met à jour un planning existant */
|
||||
@Transactional
|
||||
public PlanningMateriel updatePlanning(
|
||||
UUID id,
|
||||
String nomPlanning,
|
||||
String description,
|
||||
LocalDate dateDebut,
|
||||
LocalDate dateFin,
|
||||
String modifiePar) {
|
||||
logger.info("Mise à jour planning: {}", id);
|
||||
|
||||
PlanningMateriel planning = findByIdRequired(id);
|
||||
|
||||
if (!planning.peutEtreModifie()) {
|
||||
throw new BadRequestException(
|
||||
"Ce planning ne peut pas être modifié dans son état actuel: "
|
||||
+ planning.getStatutPlanning());
|
||||
}
|
||||
|
||||
// Validation des nouvelles données
|
||||
if (dateDebut != null && dateFin != null && dateDebut.isAfter(dateFin)) {
|
||||
throw new BadRequestException("La date de début doit être antérieure à la date de fin");
|
||||
}
|
||||
|
||||
// Mise à jour des champs
|
||||
if (nomPlanning != null) planning.setNomPlanning(nomPlanning);
|
||||
if (description != null) planning.setDescriptionPlanning(description);
|
||||
if (dateDebut != null) planning.setDateDebut(dateDebut);
|
||||
if (dateFin != null) planning.setDateFin(dateFin);
|
||||
if (modifiePar != null) planning.setModifiePar(modifiePar);
|
||||
|
||||
// Revérification des conflits après modification
|
||||
verifierConflits(planning);
|
||||
|
||||
return planning;
|
||||
}
|
||||
|
||||
// === GESTION DU WORKFLOW ===
|
||||
|
||||
/** Valide un planning */
|
||||
@Transactional
|
||||
public PlanningMateriel validerPlanning(UUID id, String valideur, String commentaires) {
|
||||
logger.info("Validation planning: {} par: {}", id, valideur);
|
||||
|
||||
PlanningMateriel planning = findByIdRequired(id);
|
||||
|
||||
if (planning.getStatutPlanning() != StatutPlanning.BROUILLON
|
||||
&& planning.getStatutPlanning() != StatutPlanning.EN_REVISION) {
|
||||
throw new BadRequestException("Ce planning ne peut pas être validé dans son état actuel");
|
||||
}
|
||||
|
||||
// Vérification finale des conflits avant validation
|
||||
verifierConflits(planning);
|
||||
|
||||
if (planning.getConflitsDetectes()) {
|
||||
throw new BadRequestException(
|
||||
"Impossible de valider un planning avec des conflits non résolus");
|
||||
}
|
||||
|
||||
planning.valider(valideur, commentaires);
|
||||
|
||||
// Calcul du score d'optimisation initial
|
||||
calculerScoreOptimisation(planning);
|
||||
|
||||
logger.info("Planning validé avec succès: {}", id);
|
||||
return planning;
|
||||
}
|
||||
|
||||
/** Met un planning en révision */
|
||||
@Transactional
|
||||
public PlanningMateriel mettreEnRevision(UUID id, String motif) {
|
||||
logger.info("Mise en révision planning: {}", id);
|
||||
|
||||
PlanningMateriel planning = findByIdRequired(id);
|
||||
planning.mettreEnRevision(motif);
|
||||
|
||||
return planning;
|
||||
}
|
||||
|
||||
/** Archive un planning */
|
||||
@Transactional
|
||||
public PlanningMateriel archiverPlanning(UUID id) {
|
||||
logger.info("Archivage planning: {}", id);
|
||||
|
||||
PlanningMateriel planning = findByIdRequired(id);
|
||||
planning.archiver();
|
||||
|
||||
return planning;
|
||||
}
|
||||
|
||||
/** Suspend un planning */
|
||||
@Transactional
|
||||
public PlanningMateriel suspendrePlanning(UUID id) {
|
||||
logger.info("Suspension planning: {}", id);
|
||||
|
||||
PlanningMateriel planning = findByIdRequired(id);
|
||||
|
||||
if (planning.getStatutPlanning() != StatutPlanning.VALIDE) {
|
||||
throw new BadRequestException("Seuls les plannings validés peuvent être suspendus");
|
||||
}
|
||||
|
||||
planning.setStatutPlanning(StatutPlanning.SUSPENDU);
|
||||
|
||||
return planning;
|
||||
}
|
||||
|
||||
/** Réactive un planning suspendu */
|
||||
@Transactional
|
||||
public PlanningMateriel reactiverPlanning(UUID id) {
|
||||
logger.info("Réactivation planning: {}", id);
|
||||
|
||||
PlanningMateriel planning = findByIdRequired(id);
|
||||
|
||||
if (planning.getStatutPlanning() != StatutPlanning.SUSPENDU) {
|
||||
throw new BadRequestException("Seuls les plannings suspendus peuvent être réactivés");
|
||||
}
|
||||
|
||||
// Revérification des conflits avant réactivation
|
||||
verifierConflits(planning);
|
||||
|
||||
planning.setStatutPlanning(StatutPlanning.VALIDE);
|
||||
|
||||
return planning;
|
||||
}
|
||||
|
||||
// === GESTION DES CONFLITS ===
|
||||
|
||||
/** Vérifie et met à jour les conflits d'un planning */
|
||||
@Transactional
|
||||
public void verifierConflits(PlanningMateriel planning) {
|
||||
logger.debug("Vérification conflits pour planning: {}", planning.getId());
|
||||
|
||||
List<PlanningMateriel> conflits =
|
||||
planningRepository.findConflits(
|
||||
planning.getMateriel().getId(),
|
||||
planning.getDateDebut(),
|
||||
planning.getDateFin(),
|
||||
planning.getId());
|
||||
|
||||
planning.mettreAJourConflits(conflits.size());
|
||||
|
||||
if (!conflits.isEmpty()) {
|
||||
logger.warn(
|
||||
"Conflits détectés pour planning {}: {} conflit(s)", planning.getId(), conflits.size());
|
||||
}
|
||||
}
|
||||
|
||||
/** Trouve les conflits pour un matériel sur une période */
|
||||
public List<PlanningMateriel> checkConflits(
|
||||
UUID materielId, LocalDate dateDebut, LocalDate dateFin, UUID excludeId) {
|
||||
return planningRepository.findConflits(materielId, dateDebut, dateFin, excludeId);
|
||||
}
|
||||
|
||||
/** Analyse la disponibilité d'un matériel sur une période */
|
||||
public Map<String, Object> analyserDisponibilite(
|
||||
UUID materielId, LocalDate dateDebut, LocalDate dateFin) {
|
||||
logger.debug("Analyse disponibilité matériel: {} du {} au {}", materielId, dateDebut, dateFin);
|
||||
|
||||
List<PlanningMateriel> plannings =
|
||||
planningRepository.findByPeriode(dateDebut, dateFin).stream()
|
||||
.filter(p -> p.getMateriel().getId().equals(materielId))
|
||||
.filter(p -> p.getStatutPlanning() == StatutPlanning.VALIDE)
|
||||
.sorted(Comparator.comparing(PlanningMateriel::getDateDebut))
|
||||
.collect(Collectors.toList());
|
||||
|
||||
List<Map<String, Object>> periodesOccupees = new ArrayList<>();
|
||||
List<Map<String, Object>> periodesLibres = new ArrayList<>();
|
||||
|
||||
LocalDate curseur = dateDebut;
|
||||
|
||||
for (PlanningMateriel planning : plannings) {
|
||||
LocalDate debutPlanning =
|
||||
planning.getDateDebut().isBefore(dateDebut) ? dateDebut : planning.getDateDebut();
|
||||
LocalDate finPlanning =
|
||||
planning.getDateFin().isAfter(dateFin) ? dateFin : planning.getDateFin();
|
||||
|
||||
// Période libre avant ce planning
|
||||
if (curseur.isBefore(debutPlanning)) {
|
||||
periodesLibres.add(
|
||||
Map.of(
|
||||
"debut", curseur,
|
||||
"fin", debutPlanning.minusDays(1),
|
||||
"duree", ChronoUnit.DAYS.between(curseur, debutPlanning)));
|
||||
}
|
||||
|
||||
// Période occupée
|
||||
periodesOccupees.add(
|
||||
Map.of(
|
||||
"debut",
|
||||
debutPlanning,
|
||||
"fin",
|
||||
finPlanning,
|
||||
"duree",
|
||||
ChronoUnit.DAYS.between(debutPlanning, finPlanning) + 1,
|
||||
"planning",
|
||||
planning.getResume(),
|
||||
"taux",
|
||||
planning.getTauxUtilisationPrevu()));
|
||||
|
||||
curseur = finPlanning.plusDays(1);
|
||||
}
|
||||
|
||||
// Période libre finale
|
||||
if (curseur.isBefore(dateFin) || curseur.equals(dateFin)) {
|
||||
periodesLibres.add(
|
||||
Map.of(
|
||||
"debut", curseur,
|
||||
"fin", dateFin,
|
||||
"duree", ChronoUnit.DAYS.between(curseur, dateFin) + 1));
|
||||
}
|
||||
|
||||
long totalJours = ChronoUnit.DAYS.between(dateDebut, dateFin) + 1;
|
||||
long joursOccupes = periodesOccupees.stream().mapToLong(p -> (Long) p.get("duree")).sum();
|
||||
double tauxOccupation = totalJours > 0 ? (double) joursOccupes / totalJours * 100.0 : 0.0;
|
||||
|
||||
return Map.of(
|
||||
"materielId", materielId,
|
||||
"periode", Map.of("debut", dateDebut, "fin", dateFin),
|
||||
"totalJours", totalJours,
|
||||
"joursOccupes", joursOccupes,
|
||||
"joursLibres", totalJours - joursOccupes,
|
||||
"tauxOccupation", tauxOccupation,
|
||||
"periodesOccupees", periodesOccupees,
|
||||
"periodesLibres", periodesLibres,
|
||||
"disponible", periodesLibres.size() > 0);
|
||||
}
|
||||
|
||||
// === OPTIMISATION ===
|
||||
|
||||
/** Calcule le score d'optimisation d'un planning */
|
||||
@Transactional
|
||||
public void calculerScoreOptimisation(PlanningMateriel planning) {
|
||||
logger.debug("Calcul score optimisation pour planning: {}", planning.getId());
|
||||
|
||||
double score = 100.0;
|
||||
|
||||
// Pénalité pour les conflits
|
||||
if (planning.getConflitsDetectes()) {
|
||||
score -= planning.getNombreConflits() * 15.0;
|
||||
}
|
||||
|
||||
// Pénalité pour faible taux d'utilisation
|
||||
if (planning.getTauxUtilisationPrevu() != null) {
|
||||
if (planning.getTauxUtilisationPrevu() < 50.0) {
|
||||
score -= (50.0 - planning.getTauxUtilisationPrevu()) * 0.5;
|
||||
}
|
||||
}
|
||||
|
||||
// Bonus pour planification anticipée
|
||||
long joursAvance = ChronoUnit.DAYS.between(LocalDate.now(), planning.getDateDebut());
|
||||
if (joursAvance > planning.getTypePlanning().getDelaiMinimumPreavis() / 24) {
|
||||
score += Math.min(10.0, joursAvance * 0.1);
|
||||
}
|
||||
|
||||
// Pénalité pour dépassement horizon recommandé
|
||||
long duree = planning.getDureePlanningJours();
|
||||
int horizonRecommande = planning.getTypePlanning().getHorizonPlanificationJours();
|
||||
if (duree > horizonRecommande) {
|
||||
score -= (duree - horizonRecommande) * 0.1;
|
||||
}
|
||||
|
||||
// Normalisation du score
|
||||
score = Math.max(0.0, Math.min(100.0, score));
|
||||
|
||||
planning.mettreAJourOptimisation(score);
|
||||
|
||||
logger.debug("Score d'optimisation calculé: {} pour planning: {}", score, planning.getId());
|
||||
}
|
||||
|
||||
/** Optimise automatiquement les plannings éligibles */
|
||||
@Transactional
|
||||
public List<PlanningMateriel> optimiserPlannings() {
|
||||
logger.info("Démarrage optimisation automatique des plannings");
|
||||
|
||||
List<PlanningMateriel> candidats = planningRepository.findCandidatsOptimisation();
|
||||
List<PlanningMateriel> optimises = new ArrayList<>();
|
||||
|
||||
for (PlanningMateriel planning : candidats) {
|
||||
try {
|
||||
optimiserPlanning(planning);
|
||||
optimises.add(planning);
|
||||
} catch (Exception e) {
|
||||
logger.error("Erreur lors de l'optimisation du planning: " + planning.getId(), e);
|
||||
}
|
||||
}
|
||||
|
||||
logger.info(
|
||||
"Optimisation terminée: {} plannings optimisés sur {} candidats",
|
||||
optimises.size(),
|
||||
candidats.size());
|
||||
|
||||
return optimises;
|
||||
}
|
||||
|
||||
/** Optimise un planning spécifique */
|
||||
@Transactional
|
||||
public void optimiserPlanning(PlanningMateriel planning) {
|
||||
logger.debug("Optimisation planning: {}", planning.getId());
|
||||
|
||||
// Recalcul du score d'optimisation
|
||||
calculerScoreOptimisation(planning);
|
||||
|
||||
// Vérification et résolution automatique des conflits si possible
|
||||
if (planning.getResolutionConflitsAuto()) {
|
||||
tenterResolutionConflits(planning);
|
||||
}
|
||||
|
||||
// Optimisation du taux d'utilisation
|
||||
optimiserTauxUtilisation(planning);
|
||||
|
||||
planning.setDerniereOptimisation(LocalDateTime.now());
|
||||
}
|
||||
|
||||
/** Tente de résoudre automatiquement les conflits */
|
||||
private void tenterResolutionConflits(PlanningMateriel planning) {
|
||||
if (!planning.getConflitsDetectes()) {
|
||||
return;
|
||||
}
|
||||
|
||||
logger.debug("Tentative résolution conflits pour planning: {}", planning.getId());
|
||||
|
||||
List<PlanningMateriel> conflits =
|
||||
checkConflits(
|
||||
planning.getMateriel().getId(),
|
||||
planning.getDateDebut(),
|
||||
planning.getDateFin(),
|
||||
planning.getId());
|
||||
|
||||
// Stratégie simple: décaler le planning si possible
|
||||
for (PlanningMateriel conflit : conflits) {
|
||||
if (conflit.getTypePlanning().estPrioritaireSur(planning.getTypePlanning())) {
|
||||
// Le conflit est prioritaire, essayer de décaler notre planning
|
||||
LocalDate nouvelleDate = conflit.getDateFin().plusDays(1);
|
||||
long duree = planning.getDureePlanningJours();
|
||||
|
||||
// Vérifier si le décalage est dans les limites acceptables
|
||||
if (ChronoUnit.DAYS.between(planning.getDateDebut(), nouvelleDate) <= 30) {
|
||||
planning.setDateDebut(nouvelleDate);
|
||||
planning.setDateFin(nouvelleDate.plusDays(duree - 1));
|
||||
|
||||
// Revérifier les conflits après décalage
|
||||
verifierConflits(planning);
|
||||
|
||||
if (!planning.getConflitsDetectes()) {
|
||||
logger.info("Conflit résolu par décalage pour planning: {}", planning.getId());
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Optimise le taux d'utilisation d'un planning */
|
||||
private void optimiserTauxUtilisation(PlanningMateriel planning) {
|
||||
// Analyser les réservations associées pour calculer un taux optimal
|
||||
if (planning.getReservations() != null && !planning.getReservations().isEmpty()) {
|
||||
double tauxMoyen =
|
||||
planning.getReservations().stream()
|
||||
.filter(
|
||||
r ->
|
||||
r.getStatut() == StatutReservationMateriel.VALIDEE
|
||||
|| r.getStatut() == StatutReservationMateriel.EN_COURS)
|
||||
.mapToDouble(r -> 80.0) // Taux standard par réservation
|
||||
.average()
|
||||
.orElse(60.0);
|
||||
|
||||
planning.setTauxUtilisationPrevu(Math.min(100.0, tauxMoyen));
|
||||
}
|
||||
}
|
||||
|
||||
// === STATISTIQUES ET ANALYSES ===
|
||||
|
||||
/** Génère les statistiques des plannings */
|
||||
public Map<String, Object> getStatistiques() {
|
||||
logger.debug("Génération statistiques plannings");
|
||||
|
||||
Map<String, Object> stats = planningRepository.calculerMetriques();
|
||||
Map<StatutPlanning, Long> repartitionStatuts = planningRepository.compterParStatut();
|
||||
List<Object[]> conflitsParType = planningRepository.analyserConflitsParType();
|
||||
|
||||
return Map.of(
|
||||
"metriques", stats,
|
||||
"repartitionStatuts", repartitionStatuts,
|
||||
"conflitsParType", conflitsParType,
|
||||
"dateGeneration", LocalDateTime.now());
|
||||
}
|
||||
|
||||
/** Génère le tableau de bord des plannings */
|
||||
public Map<String, Object> getTableauBordPlannings() {
|
||||
logger.debug("Génération tableau de bord plannings");
|
||||
|
||||
return Map.of(
|
||||
"planningsEnCours", findEnCours(),
|
||||
"planningsAvecConflits", findAvecConflits(),
|
||||
"planningsEnRetard", findEnRetardValidation(),
|
||||
"planningsPrioritaires", findPrioritaires(),
|
||||
"planningsNecessitantAttention", findNecessitantAttention(),
|
||||
"statistiques", getStatistiques());
|
||||
}
|
||||
|
||||
/** Analyse les taux d'utilisation par matériel */
|
||||
public List<Object> analyserTauxUtilisation(LocalDate dateDebut, LocalDate dateFin) {
|
||||
List<Object[]> resultats =
|
||||
planningRepository.calculerTauxUtilisationParMateriel(dateDebut, dateFin);
|
||||
|
||||
return resultats.stream()
|
||||
.map(
|
||||
row ->
|
||||
Map.of(
|
||||
"materiel", row[0],
|
||||
"tauxMoyen", row[1],
|
||||
"nombrePlannings", row[2]))
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
// === GESTION AUTOMATIQUE ===
|
||||
|
||||
/** Vérifie tous les plannings nécessitant une vérification des conflits */
|
||||
@Transactional
|
||||
public void verifierTousConflits() {
|
||||
logger.info("Vérification automatique des conflits pour tous les plannings");
|
||||
|
||||
List<PlanningMateriel> plannings = planningRepository.findNecessitantVerificationConflits();
|
||||
|
||||
for (PlanningMateriel planning : plannings) {
|
||||
try {
|
||||
verifierConflits(planning);
|
||||
} catch (Exception e) {
|
||||
logger.error(
|
||||
"Erreur lors de la vérification des conflits pour planning: " + planning.getId(), e);
|
||||
}
|
||||
}
|
||||
|
||||
logger.info("Vérification des conflits terminée pour {} plannings", plannings.size());
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user