236 lines
8.4 KiB
Dart
236 lines
8.4 KiB
Dart
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();
|
|
}
|
|
}
|
|
|