feat: add current events section for equipment with dynamic status calculation
This commit is contained in:
235
em2rp/lib/services/equipment_status_calculator.dart
Normal file
235
em2rp/lib/services/equipment_status_calculator.dart
Normal file
@@ -0,0 +1,235 @@
|
||||
import 'package:cloud_firestore/cloud_firestore.dart';
|
||||
import 'package:em2rp/models/equipment_model.dart';
|
||||
import 'package:em2rp/models/event_model.dart';
|
||||
|
||||
/// Service pour calculer dynamiquement le statut réel d'un équipement
|
||||
/// basé sur les événements en cours
|
||||
class EquipmentStatusCalculator {
|
||||
final FirebaseFirestore _firestore = FirebaseFirestore.instance;
|
||||
|
||||
/// Cache des événements pour éviter de multiples requêtes
|
||||
List<EventModel>? _cachedEvents;
|
||||
DateTime? _cacheTime;
|
||||
static const _cacheDuration = Duration(minutes: 1);
|
||||
|
||||
/// Instance statique pour permettre l'invalidation depuis n'importe où
|
||||
static final EquipmentStatusCalculator _instance = EquipmentStatusCalculator._internal();
|
||||
|
||||
factory EquipmentStatusCalculator() {
|
||||
return _instance;
|
||||
}
|
||||
|
||||
EquipmentStatusCalculator._internal();
|
||||
|
||||
/// Calcule le statut réel d'un équipement basé sur les événements
|
||||
Future<EquipmentStatus> calculateRealStatus(EquipmentModel equipment) async {
|
||||
print('[StatusCalculator] Calculating status for: ${equipment.id}');
|
||||
|
||||
// Si l'équipement est marqué comme perdu ou HS, on garde ce statut
|
||||
// car c'est une information métier importante
|
||||
if (equipment.status == EquipmentStatus.lost ||
|
||||
equipment.status == EquipmentStatus.outOfService) {
|
||||
print('[StatusCalculator] ${equipment.id} is lost/outOfService -> keeping status');
|
||||
return equipment.status;
|
||||
}
|
||||
|
||||
// Charger les événements (avec cache)
|
||||
await _loadEventsIfNeeded();
|
||||
print('[StatusCalculator] Events loaded: ${_cachedEvents?.length ?? 0}');
|
||||
|
||||
// Vérifier si l'équipement est utilisé dans un événement en cours
|
||||
final isInUse = await _isEquipmentInUse(equipment.id);
|
||||
print('[StatusCalculator] ${equipment.id} isInUse: $isInUse');
|
||||
|
||||
if (isInUse) {
|
||||
return EquipmentStatus.inUse;
|
||||
}
|
||||
|
||||
// Vérifier si l'équipement est en maintenance
|
||||
if (equipment.status == EquipmentStatus.maintenance) {
|
||||
// On pourrait vérifier si la maintenance est toujours valide
|
||||
// Pour l'instant on garde le statut
|
||||
return EquipmentStatus.maintenance;
|
||||
}
|
||||
|
||||
// Vérifier si l'équipement est loué
|
||||
if (equipment.status == EquipmentStatus.rented) {
|
||||
// On pourrait vérifier une date de retour prévue
|
||||
// Pour l'instant on garde le statut
|
||||
return EquipmentStatus.rented;
|
||||
}
|
||||
|
||||
// Par défaut, l'équipement est disponible
|
||||
print('[StatusCalculator] ${equipment.id} -> AVAILABLE');
|
||||
return EquipmentStatus.available;
|
||||
}
|
||||
|
||||
/// Calcule les statuts pour une liste d'équipements (optimisé)
|
||||
Future<Map<String, EquipmentStatus>> calculateMultipleStatuses(
|
||||
List<EquipmentModel> equipments,
|
||||
) async {
|
||||
await _loadEventsIfNeeded();
|
||||
|
||||
final statuses = <String, EquipmentStatus>{};
|
||||
|
||||
// Trouver tous les équipements en cours d'utilisation
|
||||
final equipmentIdsInUse = <String>{};
|
||||
final containerIdsInUse = <String>{};
|
||||
|
||||
for (var event in _cachedEvents ?? []) {
|
||||
// Un équipement est "en prestation" dès que la préparation est complétée
|
||||
// et jusqu'à ce que le retour soit complété
|
||||
final isPrepared = event.preparationStatus == PreparationStatus.completed ||
|
||||
event.preparationStatus == PreparationStatus.completedWithMissing;
|
||||
|
||||
final isReturned = event.returnStatus == ReturnStatus.completed ||
|
||||
event.returnStatus == ReturnStatus.completedWithMissing;
|
||||
|
||||
final isInProgress = isPrepared && !isReturned;
|
||||
|
||||
if (isInProgress) {
|
||||
// Ajouter les équipements directs
|
||||
for (var eq in event.assignedEquipment) {
|
||||
equipmentIdsInUse.add(eq.equipmentId);
|
||||
}
|
||||
// Ajouter les conteneurs
|
||||
containerIdsInUse.addAll(event.assignedContainers);
|
||||
}
|
||||
}
|
||||
|
||||
// Récupérer les équipements dans les conteneurs en cours d'utilisation
|
||||
if (containerIdsInUse.isNotEmpty) {
|
||||
final containersSnapshot = await _firestore
|
||||
.collection('containers')
|
||||
.where(FieldPath.documentId, whereIn: containerIdsInUse.toList())
|
||||
.get();
|
||||
|
||||
for (var doc in containersSnapshot.docs) {
|
||||
final data = doc.data();
|
||||
final equipmentIds = List<String>.from(data['equipmentIds'] ?? []);
|
||||
equipmentIdsInUse.addAll(equipmentIds);
|
||||
}
|
||||
}
|
||||
|
||||
// Calculer le statut pour chaque équipement
|
||||
for (var equipment in equipments) {
|
||||
// Si perdu ou HS, on garde le statut
|
||||
if (equipment.status == EquipmentStatus.lost ||
|
||||
equipment.status == EquipmentStatus.outOfService) {
|
||||
statuses[equipment.id] = equipment.status;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Si en cours d'utilisation
|
||||
if (equipmentIdsInUse.contains(equipment.id)) {
|
||||
statuses[equipment.id] = EquipmentStatus.inUse;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Si en maintenance ou loué, on garde le statut
|
||||
if (equipment.status == EquipmentStatus.maintenance ||
|
||||
equipment.status == EquipmentStatus.rented) {
|
||||
statuses[equipment.id] = equipment.status;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Par défaut, disponible
|
||||
statuses[equipment.id] = EquipmentStatus.available;
|
||||
}
|
||||
|
||||
return statuses;
|
||||
}
|
||||
|
||||
/// Vérifie si un équipement est actuellement en cours d'utilisation
|
||||
Future<bool> _isEquipmentInUse(String equipmentId) async {
|
||||
print('[StatusCalculator] Checking if $equipmentId is in use...');
|
||||
|
||||
// Vérifier dans les événements directs
|
||||
for (var event in _cachedEvents ?? []) {
|
||||
// Un équipement est "en prestation" dès que la préparation est complétée
|
||||
// et jusqu'à ce que le retour soit complété
|
||||
final isPrepared = event.preparationStatus == PreparationStatus.completed ||
|
||||
event.preparationStatus == PreparationStatus.completedWithMissing;
|
||||
|
||||
final isReturned = event.returnStatus == ReturnStatus.completed ||
|
||||
event.returnStatus == ReturnStatus.completedWithMissing;
|
||||
|
||||
final isInProgress = isPrepared && !isReturned;
|
||||
|
||||
if (!isInProgress) continue;
|
||||
|
||||
print('[StatusCalculator] Event ${event.name} is IN PROGRESS (prepared and not returned)');
|
||||
|
||||
// Vérifier si l'équipement est directement assigné
|
||||
if (event.assignedEquipment.any((eq) => eq.equipmentId == equipmentId)) {
|
||||
print('[StatusCalculator] $equipmentId found DIRECTLY in event ${event.name}');
|
||||
return true;
|
||||
}
|
||||
|
||||
// Vérifier si l'équipement est dans un conteneur assigné
|
||||
if (event.assignedContainers.isNotEmpty) {
|
||||
print('[StatusCalculator] Checking containers for event ${event.name}: ${event.assignedContainers}');
|
||||
final containersSnapshot = await _firestore
|
||||
.collection('containers')
|
||||
.where(FieldPath.documentId, whereIn: event.assignedContainers)
|
||||
.get();
|
||||
|
||||
for (var doc in containersSnapshot.docs) {
|
||||
final data = doc.data();
|
||||
final equipmentIds = List<String>.from(data['equipmentIds'] ?? []);
|
||||
print('[StatusCalculator] Container ${doc.id} contains: $equipmentIds');
|
||||
if (equipmentIds.contains(equipmentId)) {
|
||||
print('[StatusCalculator] $equipmentId found in CONTAINER ${doc.id}');
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
print('[StatusCalculator] $equipmentId is NOT in use');
|
||||
return false;
|
||||
}
|
||||
|
||||
/// Charge les événements si le cache est expiré
|
||||
Future<void> _loadEventsIfNeeded() async {
|
||||
if (_cachedEvents != null &&
|
||||
_cacheTime != null &&
|
||||
DateTime.now().difference(_cacheTime!) < _cacheDuration) {
|
||||
return; // Cache encore valide
|
||||
}
|
||||
|
||||
try {
|
||||
final eventsSnapshot = await _firestore.collection('events').get();
|
||||
|
||||
_cachedEvents = eventsSnapshot.docs
|
||||
.map((doc) {
|
||||
try {
|
||||
return EventModel.fromMap(doc.data(), doc.id);
|
||||
} catch (e) {
|
||||
print('[EquipmentStatusCalculator] Error parsing event ${doc.id}: $e');
|
||||
return null;
|
||||
}
|
||||
})
|
||||
.whereType<EventModel>()
|
||||
.where((event) => event.status != EventStatus.canceled) // Ignorer les événements annulés
|
||||
.toList();
|
||||
|
||||
_cacheTime = DateTime.now();
|
||||
} catch (e) {
|
||||
print('[EquipmentStatusCalculator] Error loading events: $e');
|
||||
_cachedEvents = [];
|
||||
}
|
||||
}
|
||||
|
||||
/// Invalide le cache (à appeler après une modification d'événement)
|
||||
void invalidateCache() {
|
||||
_cachedEvents = null;
|
||||
_cacheTime = null;
|
||||
}
|
||||
|
||||
/// Invalide le cache de l'instance globale (méthode statique)
|
||||
static void invalidateGlobalCache() {
|
||||
_instance.invalidateCache();
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user