feat: ajout de la gestion de la préparation d'un événement avec page permettant de le gérer
This commit is contained in:
@@ -1,6 +1,15 @@
|
||||
import 'package:cloud_firestore/cloud_firestore.dart';
|
||||
import 'package:em2rp/models/event_model.dart';
|
||||
import 'package:em2rp/models/equipment_model.dart';
|
||||
import 'package:em2rp/models/container_model.dart';
|
||||
|
||||
/// Type de conflit
|
||||
enum ConflictType {
|
||||
equipmentUnavailable, // Équipement non quantifiable utilisé
|
||||
insufficientQuantity, // Quantité insuffisante pour consommable/câble
|
||||
containerFullyUsed, // Boîte complète utilisée
|
||||
containerPartiallyUsed, // Certains équipements de la boîte utilisés
|
||||
}
|
||||
|
||||
/// Informations sur un conflit de disponibilité
|
||||
class AvailabilityConflict {
|
||||
@@ -8,13 +17,48 @@ class AvailabilityConflict {
|
||||
final String equipmentName;
|
||||
final EventModel conflictingEvent;
|
||||
final int overlapDays;
|
||||
final ConflictType type;
|
||||
|
||||
// Pour les quantités (consommables/câbles)
|
||||
final int? totalQuantity;
|
||||
final int? availableQuantity;
|
||||
final int? requestedQuantity;
|
||||
final int? reservedQuantity;
|
||||
|
||||
// Pour les boîtes
|
||||
final String? containerId;
|
||||
final String? containerName;
|
||||
final List<String>? conflictingChildrenIds;
|
||||
|
||||
AvailabilityConflict({
|
||||
required this.equipmentId,
|
||||
required this.equipmentName,
|
||||
required this.conflictingEvent,
|
||||
required this.overlapDays,
|
||||
this.type = ConflictType.equipmentUnavailable,
|
||||
this.totalQuantity,
|
||||
this.availableQuantity,
|
||||
this.requestedQuantity,
|
||||
this.reservedQuantity,
|
||||
this.containerId,
|
||||
this.containerName,
|
||||
this.conflictingChildrenIds,
|
||||
});
|
||||
|
||||
/// Message descriptif du conflit
|
||||
String get conflictMessage {
|
||||
switch (type) {
|
||||
case ConflictType.equipmentUnavailable:
|
||||
return 'Équipement déjà utilisé';
|
||||
case ConflictType.insufficientQuantity:
|
||||
return 'Stock insuffisant : $availableQuantity/$totalQuantity disponible (demandé: $requestedQuantity)';
|
||||
case ConflictType.containerFullyUsed:
|
||||
return 'Boîte complète déjà utilisée';
|
||||
case ConflictType.containerPartiallyUsed:
|
||||
final count = conflictingChildrenIds?.length ?? 0;
|
||||
return '$count équipement(s) de la boîte déjà utilisé(s)';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Service pour vérifier la disponibilité du matériel
|
||||
@@ -32,21 +76,22 @@ class EventAvailabilityService {
|
||||
final conflicts = <AvailabilityConflict>[];
|
||||
|
||||
try {
|
||||
print('[EventAvailabilityService] Checking availability for equipment: $equipmentId');
|
||||
print('[EventAvailabilityService] Date range: $startDate - $endDate');
|
||||
|
||||
// Récupérer TOUS les événements (on filtre côté client car arrayContains avec objet ne marche pas)
|
||||
final eventsSnapshot = await _firestore.collection('events').get();
|
||||
|
||||
print('[EventAvailabilityService] Found ${eventsSnapshot.docs.length} total events');
|
||||
|
||||
for (var doc in eventsSnapshot.docs) {
|
||||
if (excludeEventId != null && doc.id == excludeEventId) {
|
||||
continue; // Ignorer l'événement en cours d'édition
|
||||
}
|
||||
|
||||
try {
|
||||
final event = EventModel.fromMap(doc.data(), doc.id);
|
||||
final data = doc.data();
|
||||
final event = EventModel.fromMap(data, doc.id);
|
||||
|
||||
// Ignorer les événements annulés
|
||||
if (event.status == EventStatus.canceled) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Vérifier si cet événement contient l'équipement recherché
|
||||
final assignedEquipment = event.assignedEquipment.firstWhere(
|
||||
@@ -54,21 +99,26 @@ class EventAvailabilityService {
|
||||
orElse: () => EventEquipment(equipmentId: ''),
|
||||
);
|
||||
|
||||
// Si l'équipement est assigné et non retourné
|
||||
if (assignedEquipment.equipmentId.isNotEmpty && !assignedEquipment.isReturned) {
|
||||
print('[EventAvailabilityService] Equipment $equipmentId found in event: ${event.name}');
|
||||
// Si l'équipement est assigné à cet événement, il est indisponible
|
||||
// (peu importe le statut de préparation/chargement/retour)
|
||||
if (assignedEquipment.equipmentId.isNotEmpty) {
|
||||
// Calculer les dates réelles avec temps d'installation et démontage
|
||||
final eventRealStartDate = event.startDateTime.subtract(
|
||||
Duration(hours: event.installationTime),
|
||||
);
|
||||
final eventRealEndDate = event.endDateTime.add(
|
||||
Duration(hours: event.disassemblyTime),
|
||||
);
|
||||
|
||||
// Vérifier le chevauchement des dates
|
||||
if (_datesOverlap(startDate, endDate, event.startDateTime, event.endDateTime)) {
|
||||
if (_datesOverlap(startDate, endDate, eventRealStartDate, eventRealEndDate)) {
|
||||
final overlapDays = _calculateOverlapDays(
|
||||
startDate,
|
||||
endDate,
|
||||
event.startDateTime,
|
||||
event.endDateTime,
|
||||
eventRealStartDate,
|
||||
eventRealEndDate,
|
||||
);
|
||||
|
||||
print('[EventAvailabilityService] CONFLICT detected! Overlap: $overlapDays days');
|
||||
|
||||
conflicts.add(AvailabilityConflict(
|
||||
equipmentId: equipmentId,
|
||||
equipmentName: equipmentName,
|
||||
@@ -81,15 +131,18 @@ class EventAvailabilityService {
|
||||
print('[EventAvailabilityService] Error processing event ${doc.id}: $e');
|
||||
}
|
||||
}
|
||||
|
||||
print('[EventAvailabilityService] Total conflicts found for $equipmentId: ${conflicts.length}');
|
||||
} catch (e) {
|
||||
print('[EventAvailabilityService] Error checking equipment availability: $e');
|
||||
print('[EventAvailabilityService] Error checking availability: $e');
|
||||
}
|
||||
|
||||
return conflicts;
|
||||
}
|
||||
|
||||
/// Helper pour formater les dates dans les logs
|
||||
String _formatDate(DateTime date) {
|
||||
return '${date.day.toString().padLeft(2, '0')}/${date.month.toString().padLeft(2, '0')}/${date.year} ${date.hour.toString().padLeft(2, '0')}:${date.minute.toString().padLeft(2, '0')}';
|
||||
}
|
||||
|
||||
/// Vérifie la disponibilité pour une liste d'équipements
|
||||
Future<Map<String, List<AvailabilityConflict>>> checkMultipleEquipmentAvailability({
|
||||
required List<String> equipmentIds,
|
||||
@@ -114,6 +167,7 @@ class EventAvailabilityService {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return allConflicts;
|
||||
}
|
||||
|
||||
@@ -160,14 +214,29 @@ class EventAvailabilityService {
|
||||
try {
|
||||
final event = EventModel.fromMap(doc.data(), doc.id);
|
||||
|
||||
// Ignorer les événements annulés
|
||||
if (event.status == EventStatus.canceled) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Calculer les dates réelles avec temps d'installation et démontage
|
||||
final eventRealStartDate = event.startDateTime.subtract(
|
||||
Duration(hours: event.installationTime),
|
||||
);
|
||||
final eventRealEndDate = event.endDateTime.add(
|
||||
Duration(hours: event.disassemblyTime),
|
||||
);
|
||||
|
||||
// Vérifier le chevauchement des dates
|
||||
if (_datesOverlap(startDate, endDate, event.startDateTime, event.endDateTime)) {
|
||||
if (_datesOverlap(startDate, endDate, eventRealStartDate, eventRealEndDate)) {
|
||||
final assignedEquipment = event.assignedEquipment.firstWhere(
|
||||
(eq) => eq.equipmentId == equipment.id,
|
||||
orElse: () => EventEquipment(equipmentId: ''),
|
||||
);
|
||||
|
||||
if (assignedEquipment.equipmentId.isNotEmpty && !assignedEquipment.isReturned) {
|
||||
// Si l'équipement est assigné, réserver la quantité
|
||||
// (peu importe le statut de préparation/retour)
|
||||
if (assignedEquipment.equipmentId.isNotEmpty) {
|
||||
reservedQuantity += assignedEquipment.quantity;
|
||||
}
|
||||
}
|
||||
@@ -181,5 +250,179 @@ class EventAvailabilityService {
|
||||
|
||||
return totalQuantity - reservedQuantity;
|
||||
}
|
||||
|
||||
/// Vérifie la disponibilité d'un équipement avec gestion des quantités
|
||||
Future<List<AvailabilityConflict>> checkEquipmentAvailabilityWithQuantity({
|
||||
required EquipmentModel equipment,
|
||||
required int requestedQuantity,
|
||||
required DateTime startDate,
|
||||
required DateTime endDate,
|
||||
String? excludeEventId,
|
||||
}) async {
|
||||
final conflicts = <AvailabilityConflict>[];
|
||||
|
||||
// Si équipement quantifiable (consommable/câble)
|
||||
if (equipment.hasQuantity) {
|
||||
final totalQuantity = equipment.totalQuantity ?? 0;
|
||||
final availableQty = await getAvailableQuantity(
|
||||
equipment: equipment,
|
||||
startDate: startDate,
|
||||
endDate: endDate,
|
||||
excludeEventId: excludeEventId,
|
||||
);
|
||||
final reservedQty = totalQuantity - availableQty;
|
||||
|
||||
// ✅ Ne créer un conflit que si la quantité est VRAIMENT insuffisante
|
||||
if (availableQty < requestedQuantity) {
|
||||
// Trouver les événements qui réservent cette quantité
|
||||
final eventsSnapshot = await _firestore.collection('events').get();
|
||||
|
||||
for (var doc in eventsSnapshot.docs) {
|
||||
if (excludeEventId != null && doc.id == excludeEventId) continue;
|
||||
|
||||
try {
|
||||
final event = EventModel.fromMap(doc.data(), doc.id);
|
||||
|
||||
if (_datesOverlap(startDate, endDate, event.startDateTime, event.endDateTime)) {
|
||||
final assignedEquipment = event.assignedEquipment.firstWhere(
|
||||
(eq) => eq.equipmentId == equipment.id,
|
||||
orElse: () => EventEquipment(equipmentId: ''),
|
||||
);
|
||||
|
||||
if (assignedEquipment.equipmentId.isNotEmpty && !assignedEquipment.isReturned) {
|
||||
conflicts.add(AvailabilityConflict(
|
||||
equipmentId: equipment.id,
|
||||
equipmentName: equipment.name,
|
||||
conflictingEvent: event,
|
||||
overlapDays: _calculateOverlapDays(startDate, endDate, event.startDateTime, event.endDateTime),
|
||||
type: ConflictType.insufficientQuantity,
|
||||
totalQuantity: totalQuantity,
|
||||
availableQuantity: availableQty,
|
||||
requestedQuantity: requestedQuantity,
|
||||
reservedQuantity: reservedQty,
|
||||
));
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
print('[EventAvailabilityService] Error processing event ${doc.id}: $e');
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Équipement non quantifiable : vérification classique
|
||||
return await checkEquipmentAvailability(
|
||||
equipmentId: equipment.id,
|
||||
equipmentName: equipment.name,
|
||||
startDate: startDate,
|
||||
endDate: endDate,
|
||||
excludeEventId: excludeEventId,
|
||||
);
|
||||
}
|
||||
|
||||
return conflicts;
|
||||
}
|
||||
|
||||
/// Vérifie la disponibilité d'une boîte et de son contenu
|
||||
Future<List<AvailabilityConflict>> checkContainerAvailability({
|
||||
required ContainerModel container,
|
||||
required List<EquipmentModel> containerEquipment,
|
||||
required DateTime startDate,
|
||||
required DateTime endDate,
|
||||
String? excludeEventId,
|
||||
}) async {
|
||||
final conflicts = <AvailabilityConflict>[];
|
||||
final conflictingChildrenIds = <String>[];
|
||||
|
||||
// Vérifier d'abord si la boîte complète est utilisée
|
||||
final eventsSnapshot = await _firestore.collection('events').get();
|
||||
bool isContainerFullyUsed = false;
|
||||
EventModel? containerConflictingEvent;
|
||||
|
||||
for (var doc in eventsSnapshot.docs) {
|
||||
if (excludeEventId != null && doc.id == excludeEventId) continue;
|
||||
|
||||
try {
|
||||
final event = EventModel.fromMap(doc.data(), doc.id);
|
||||
|
||||
// Ignorer les événements annulés
|
||||
if (event.status == EventStatus.canceled) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Calculer les dates réelles avec temps d'installation et démontage
|
||||
final eventRealStartDate = event.startDateTime.subtract(
|
||||
Duration(hours: event.installationTime),
|
||||
);
|
||||
final eventRealEndDate = event.endDateTime.add(
|
||||
Duration(hours: event.disassemblyTime),
|
||||
);
|
||||
|
||||
// Vérifier si cette boîte est assignée
|
||||
if (event.assignedContainers.contains(container.id)) {
|
||||
if (_datesOverlap(startDate, endDate, eventRealStartDate, eventRealEndDate)) {
|
||||
isContainerFullyUsed = true;
|
||||
containerConflictingEvent = event;
|
||||
break;
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
print('[EventAvailabilityService] Error processing event ${doc.id}: $e');
|
||||
}
|
||||
}
|
||||
|
||||
if (isContainerFullyUsed && containerConflictingEvent != null) {
|
||||
// Boîte complète utilisée
|
||||
conflicts.add(AvailabilityConflict(
|
||||
equipmentId: container.id,
|
||||
equipmentName: container.name,
|
||||
conflictingEvent: containerConflictingEvent,
|
||||
overlapDays: _calculateOverlapDays(
|
||||
startDate,
|
||||
endDate,
|
||||
containerConflictingEvent.startDateTime,
|
||||
containerConflictingEvent.endDateTime,
|
||||
),
|
||||
type: ConflictType.containerFullyUsed,
|
||||
containerId: container.id,
|
||||
containerName: container.name,
|
||||
));
|
||||
} else {
|
||||
// Vérifier chaque équipement enfant individuellement
|
||||
for (var equipment in containerEquipment) {
|
||||
final equipmentConflicts = await checkEquipmentAvailability(
|
||||
equipmentId: equipment.id,
|
||||
equipmentName: equipment.name,
|
||||
startDate: startDate,
|
||||
endDate: endDate,
|
||||
excludeEventId: excludeEventId,
|
||||
);
|
||||
|
||||
if (equipmentConflicts.isNotEmpty) {
|
||||
conflictingChildrenIds.add(equipment.id);
|
||||
conflicts.addAll(equipmentConflicts);
|
||||
}
|
||||
}
|
||||
|
||||
// Si au moins un enfant en conflit, ajouter un conflit pour la boîte
|
||||
if (conflictingChildrenIds.isNotEmpty && conflicts.isNotEmpty) {
|
||||
conflicts.insert(
|
||||
0,
|
||||
AvailabilityConflict(
|
||||
equipmentId: container.id,
|
||||
equipmentName: container.name,
|
||||
conflictingEvent: conflicts.first.conflictingEvent,
|
||||
overlapDays: conflicts.first.overlapDays,
|
||||
type: ConflictType.containerPartiallyUsed,
|
||||
containerId: container.id,
|
||||
containerName: container.name,
|
||||
conflictingChildrenIds: conflictingChildrenIds,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return conflicts;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@ class EventPreparationService {
|
||||
|
||||
// Collection references
|
||||
CollectionReference get _eventsCollection => _firestore.collection('events');
|
||||
CollectionReference get _equipmentCollection => _firestore.collection('equipment');
|
||||
CollectionReference get _equipmentCollection => _firestore.collection('equipments');
|
||||
|
||||
// === PRÉPARATION ===
|
||||
|
||||
@@ -29,9 +29,21 @@ class EventPreparationService {
|
||||
return eq;
|
||||
}).toList();
|
||||
|
||||
await _eventsCollection.doc(eventId).update({
|
||||
// Vérifier si tous les équipements sont préparés
|
||||
final allPrepared = updatedEquipment.every((eq) => eq.isPrepared);
|
||||
|
||||
final updateData = <String, dynamic>{
|
||||
'assignedEquipment': updatedEquipment.map((e) => e.toMap()).toList(),
|
||||
});
|
||||
};
|
||||
|
||||
// Mettre à jour le statut selon la complétion
|
||||
if (allPrepared) {
|
||||
updateData['preparationStatus'] = preparationStatusToString(PreparationStatus.completed);
|
||||
} else {
|
||||
updateData['preparationStatus'] = preparationStatusToString(PreparationStatus.inProgress);
|
||||
}
|
||||
|
||||
await _eventsCollection.doc(eventId).update(updateData);
|
||||
} catch (e) {
|
||||
print('Error validating equipment preparation: $e');
|
||||
rethrow;
|
||||
@@ -56,9 +68,13 @@ class EventPreparationService {
|
||||
'preparationStatus': preparationStatusToString(PreparationStatus.completed),
|
||||
});
|
||||
|
||||
// Mettre à jour le statut des équipements à "inUse"
|
||||
// Mettre à jour le statut des équipements à "inUse" (seulement pour les équipements qui existent)
|
||||
for (var equipment in event.assignedEquipment) {
|
||||
await updateEquipmentStatus(equipment.equipmentId, EquipmentStatus.inUse);
|
||||
// Vérifier si l'équipement existe avant de mettre à jour son statut
|
||||
final doc = await _equipmentCollection.doc(equipment.equipmentId).get();
|
||||
if (doc.exists) {
|
||||
await updateEquipmentStatus(equipment.equipmentId, EquipmentStatus.inUse);
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
print('Error validating all preparation: $e');
|
||||
@@ -85,7 +101,11 @@ class EventPreparationService {
|
||||
// Mettre à jour le statut des équipements préparés à "inUse"
|
||||
for (var equipment in event.assignedEquipment) {
|
||||
if (equipment.isPrepared && !missingEquipmentIds.contains(equipment.equipmentId)) {
|
||||
await updateEquipmentStatus(equipment.equipmentId, EquipmentStatus.inUse);
|
||||
// Vérifier si l'équipement existe avant de mettre à jour son statut
|
||||
final doc = await _equipmentCollection.doc(equipment.equipmentId).get();
|
||||
if (doc.exists) {
|
||||
await updateEquipmentStatus(equipment.equipmentId, EquipmentStatus.inUse);
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
@@ -120,9 +140,21 @@ class EventPreparationService {
|
||||
return eq;
|
||||
}).toList();
|
||||
|
||||
await _eventsCollection.doc(eventId).update({
|
||||
// Vérifier si tous les équipements sont retournés
|
||||
final allReturned = updatedEquipment.every((eq) => eq.isReturned);
|
||||
|
||||
final updateData = <String, dynamic>{
|
||||
'assignedEquipment': updatedEquipment.map((e) => e.toMap()).toList(),
|
||||
});
|
||||
};
|
||||
|
||||
// Mettre à jour le statut selon la complétion
|
||||
if (allReturned) {
|
||||
updateData['returnStatus'] = returnStatusToString(ReturnStatus.completed);
|
||||
} else {
|
||||
updateData['returnStatus'] = returnStatusToString(ReturnStatus.inProgress);
|
||||
}
|
||||
|
||||
await _eventsCollection.doc(eventId).update(updateData);
|
||||
|
||||
// Mettre à jour le stock si c'est un consommable
|
||||
if (returnedQuantity != null) {
|
||||
@@ -176,9 +208,7 @@ class EventPreparationService {
|
||||
|
||||
// Mettre à jour le statut des équipements à "available" et gérer les stocks
|
||||
for (var equipment in updatedEquipment) {
|
||||
await updateEquipmentStatus(equipment.equipmentId, EquipmentStatus.available);
|
||||
|
||||
// Restaurer le stock pour les consommables
|
||||
// Vérifier si le document existe
|
||||
final equipmentDoc = await _equipmentCollection.doc(equipment.equipmentId).get();
|
||||
if (equipmentDoc.exists) {
|
||||
final equipmentData = EquipmentModel.fromMap(
|
||||
@@ -186,10 +216,17 @@ class EventPreparationService {
|
||||
equipmentDoc.id,
|
||||
);
|
||||
|
||||
// Mettre à jour le statut uniquement pour les équipements non quantifiables
|
||||
if (!equipmentData.hasQuantity) {
|
||||
await updateEquipmentStatus(equipment.equipmentId, EquipmentStatus.available);
|
||||
}
|
||||
|
||||
// Restaurer le stock pour les consommables
|
||||
if (equipmentData.hasQuantity && equipment.returnedQuantity != null) {
|
||||
final currentAvailable = equipmentData.availableQuantity ?? 0;
|
||||
await _equipmentCollection.doc(equipment.equipmentId).update({
|
||||
'availableQuantity': currentAvailable + equipment.returnedQuantity!,
|
||||
'updatedAt': Timestamp.fromDate(DateTime.now()),
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -218,27 +255,36 @@ class EventPreparationService {
|
||||
|
||||
// Mettre à jour le statut des équipements retournés à "available"
|
||||
for (var equipment in event.assignedEquipment) {
|
||||
// Vérifier si le document existe
|
||||
final equipmentDoc = await _equipmentCollection.doc(equipment.equipmentId).get();
|
||||
if (!equipmentDoc.exists) {
|
||||
continue; // Passer cet équipement s'il n'existe pas
|
||||
}
|
||||
|
||||
final equipmentData = EquipmentModel.fromMap(
|
||||
equipmentDoc.data() as Map<String, dynamic>,
|
||||
equipmentDoc.id,
|
||||
);
|
||||
|
||||
if (equipment.isReturned && !missingEquipmentIds.contains(equipment.equipmentId)) {
|
||||
await updateEquipmentStatus(equipment.equipmentId, EquipmentStatus.available);
|
||||
// Mettre à jour le statut uniquement pour les équipements non quantifiables
|
||||
if (!equipmentData.hasQuantity) {
|
||||
await updateEquipmentStatus(equipment.equipmentId, EquipmentStatus.available);
|
||||
}
|
||||
|
||||
// Restaurer le stock pour les consommables
|
||||
final equipmentDoc = await _equipmentCollection.doc(equipment.equipmentId).get();
|
||||
if (equipmentDoc.exists) {
|
||||
final equipmentData = EquipmentModel.fromMap(
|
||||
equipmentDoc.data() as Map<String, dynamic>,
|
||||
equipmentDoc.id,
|
||||
);
|
||||
|
||||
if (equipmentData.hasQuantity && equipment.returnedQuantity != null) {
|
||||
final currentAvailable = equipmentData.availableQuantity ?? 0;
|
||||
await _equipmentCollection.doc(equipment.equipmentId).update({
|
||||
'availableQuantity': currentAvailable + equipment.returnedQuantity!,
|
||||
});
|
||||
}
|
||||
if (equipmentData.hasQuantity && equipment.returnedQuantity != null) {
|
||||
final currentAvailable = equipmentData.availableQuantity ?? 0;
|
||||
await _equipmentCollection.doc(equipment.equipmentId).update({
|
||||
'availableQuantity': currentAvailable + equipment.returnedQuantity!,
|
||||
'updatedAt': Timestamp.fromDate(DateTime.now()),
|
||||
});
|
||||
}
|
||||
} else if (missingEquipmentIds.contains(equipment.equipmentId)) {
|
||||
// Marquer comme perdu
|
||||
await updateEquipmentStatus(equipment.equipmentId, EquipmentStatus.lost);
|
||||
// Marquer comme perdu uniquement pour les équipements non quantifiables
|
||||
if (!equipmentData.hasQuantity) {
|
||||
await updateEquipmentStatus(equipment.equipmentId, EquipmentStatus.lost);
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
@@ -252,13 +298,20 @@ class EventPreparationService {
|
||||
/// Mettre à jour le statut d'un équipement
|
||||
Future<void> updateEquipmentStatus(String equipmentId, EquipmentStatus status) async {
|
||||
try {
|
||||
// Vérifier que le document existe avant de le mettre à jour
|
||||
final doc = await _equipmentCollection.doc(equipmentId).get();
|
||||
if (!doc.exists) {
|
||||
print('Warning: Equipment document $equipmentId does not exist, skipping status update');
|
||||
return;
|
||||
}
|
||||
|
||||
await _equipmentCollection.doc(equipmentId).update({
|
||||
'status': equipmentStatusToString(status),
|
||||
'updatedAt': Timestamp.fromDate(DateTime.now()),
|
||||
});
|
||||
} catch (e) {
|
||||
print('Error updating equipment status: $e');
|
||||
rethrow;
|
||||
print('Error updating equipment status for $equipmentId: $e');
|
||||
// Ne pas rethrow pour ne pas bloquer le processus si un équipement n'existe pas
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
240
em2rp/lib/services/event_preparation_service_extended.dart
Normal file
240
em2rp/lib/services/event_preparation_service_extended.dart
Normal file
@@ -0,0 +1,240 @@
|
||||
import 'package:cloud_firestore/cloud_firestore.dart';
|
||||
import 'package:em2rp/models/event_model.dart';
|
||||
import 'package:em2rp/models/equipment_model.dart';
|
||||
|
||||
/// Service étendu pour gérer les 4 étapes : Préparation, Chargement, Déchargement, Retour
|
||||
class EventPreparationServiceExtended {
|
||||
final FirebaseFirestore _firestore = FirebaseFirestore.instance;
|
||||
|
||||
CollectionReference get _eventsCollection => _firestore.collection('events');
|
||||
CollectionReference get _equipmentCollection => _firestore.collection('equipments');
|
||||
|
||||
// === CHARGEMENT (LOADING) ===
|
||||
|
||||
/// Valider un équipement individuel pour le chargement
|
||||
Future<void> validateEquipmentLoading(String eventId, String equipmentId) async {
|
||||
try {
|
||||
final event = await _getEvent(eventId);
|
||||
if (event == null) throw Exception('Event not found');
|
||||
|
||||
final updatedEquipment = event.assignedEquipment.map((eq) {
|
||||
if (eq.equipmentId == equipmentId) {
|
||||
return eq.copyWith(isLoaded: true);
|
||||
}
|
||||
return eq;
|
||||
}).toList();
|
||||
|
||||
// Vérifier si tous les équipements sont chargés
|
||||
final allLoaded = updatedEquipment.every((eq) => eq.isLoaded);
|
||||
|
||||
final updateData = <String, dynamic>{
|
||||
'assignedEquipment': updatedEquipment.map((e) => e.toMap()).toList(),
|
||||
};
|
||||
|
||||
// Si tous sont chargés, mettre à jour le statut
|
||||
if (allLoaded) {
|
||||
updateData['loadingStatus'] = loadingStatusToString(LoadingStatus.completed);
|
||||
} else {
|
||||
updateData['loadingStatus'] = loadingStatusToString(LoadingStatus.inProgress);
|
||||
}
|
||||
|
||||
await _eventsCollection.doc(eventId).update(updateData);
|
||||
} catch (e) {
|
||||
print('Error validating equipment loading: $e');
|
||||
rethrow;
|
||||
}
|
||||
}
|
||||
|
||||
/// Valider tous les équipements pour le chargement
|
||||
Future<void> validateAllLoading(String eventId) async {
|
||||
try {
|
||||
final event = await _getEvent(eventId);
|
||||
if (event == null) throw Exception('Event not found');
|
||||
|
||||
final updatedEquipment = event.assignedEquipment.map((eq) {
|
||||
return eq.copyWith(isLoaded: true);
|
||||
}).toList();
|
||||
|
||||
await _eventsCollection.doc(eventId).update({
|
||||
'assignedEquipment': updatedEquipment.map((e) => e.toMap()).toList(),
|
||||
'loadingStatus': loadingStatusToString(LoadingStatus.completed),
|
||||
});
|
||||
} catch (e) {
|
||||
print('Error validating all loading: $e');
|
||||
rethrow;
|
||||
}
|
||||
}
|
||||
|
||||
// === DÉCHARGEMENT (UNLOADING) ===
|
||||
|
||||
/// Valider un équipement individuel pour le déchargement
|
||||
Future<void> validateEquipmentUnloading(String eventId, String equipmentId) async {
|
||||
try {
|
||||
final event = await _getEvent(eventId);
|
||||
if (event == null) throw Exception('Event not found');
|
||||
|
||||
final updatedEquipment = event.assignedEquipment.map((eq) {
|
||||
if (eq.equipmentId == equipmentId) {
|
||||
return eq.copyWith(isUnloaded: true);
|
||||
}
|
||||
return eq;
|
||||
}).toList();
|
||||
|
||||
// Vérifier si tous les équipements sont déchargés
|
||||
final allUnloaded = updatedEquipment.every((eq) => eq.isUnloaded);
|
||||
|
||||
final updateData = <String, dynamic>{
|
||||
'assignedEquipment': updatedEquipment.map((e) => e.toMap()).toList(),
|
||||
};
|
||||
|
||||
// Si tous sont déchargés, mettre à jour le statut
|
||||
if (allUnloaded) {
|
||||
updateData['unloadingStatus'] = unloadingStatusToString(UnloadingStatus.completed);
|
||||
} else {
|
||||
updateData['unloadingStatus'] = unloadingStatusToString(UnloadingStatus.inProgress);
|
||||
}
|
||||
|
||||
await _eventsCollection.doc(eventId).update(updateData);
|
||||
} catch (e) {
|
||||
print('Error validating equipment unloading: $e');
|
||||
rethrow;
|
||||
}
|
||||
}
|
||||
|
||||
/// Valider tous les équipements pour le déchargement
|
||||
Future<void> validateAllUnloading(String eventId) async {
|
||||
try {
|
||||
final event = await _getEvent(eventId);
|
||||
if (event == null) throw Exception('Event not found');
|
||||
|
||||
final updatedEquipment = event.assignedEquipment.map((eq) {
|
||||
return eq.copyWith(isUnloaded: true);
|
||||
}).toList();
|
||||
|
||||
await _eventsCollection.doc(eventId).update({
|
||||
'assignedEquipment': updatedEquipment.map((e) => e.toMap()).toList(),
|
||||
'unloadingStatus': unloadingStatusToString(UnloadingStatus.completed),
|
||||
});
|
||||
} catch (e) {
|
||||
print('Error validating all unloading: $e');
|
||||
rethrow;
|
||||
}
|
||||
}
|
||||
|
||||
// === PRÉPARATION + CHARGEMENT ===
|
||||
|
||||
/// Valider préparation ET chargement en même temps
|
||||
Future<void> validateAllPreparationAndLoading(String eventId) async {
|
||||
try {
|
||||
final event = await _getEvent(eventId);
|
||||
if (event == null) throw Exception('Event not found');
|
||||
|
||||
final updatedEquipment = event.assignedEquipment.map((eq) {
|
||||
return eq.copyWith(isPrepared: true, isLoaded: true);
|
||||
}).toList();
|
||||
|
||||
await _eventsCollection.doc(eventId).update({
|
||||
'assignedEquipment': updatedEquipment.map((e) => e.toMap()).toList(),
|
||||
'preparationStatus': preparationStatusToString(PreparationStatus.completed),
|
||||
'loadingStatus': loadingStatusToString(LoadingStatus.completed),
|
||||
});
|
||||
|
||||
// Mettre à jour le statut des équipements
|
||||
for (var equipment in event.assignedEquipment) {
|
||||
final doc = await _equipmentCollection.doc(equipment.equipmentId).get();
|
||||
if (doc.exists) {
|
||||
await _updateEquipmentStatus(equipment.equipmentId, EquipmentStatus.inUse);
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
print('Error validating all preparation and loading: $e');
|
||||
rethrow;
|
||||
}
|
||||
}
|
||||
|
||||
// === DÉCHARGEMENT + RETOUR ===
|
||||
|
||||
/// Valider déchargement ET retour en même temps
|
||||
Future<void> validateAllUnloadingAndReturn(
|
||||
String eventId,
|
||||
Map<String, int>? returnedQuantities,
|
||||
) async {
|
||||
try {
|
||||
final event = await _getEvent(eventId);
|
||||
if (event == null) throw Exception('Event not found');
|
||||
|
||||
final updatedEquipment = event.assignedEquipment.map((eq) {
|
||||
final returnedQty = returnedQuantities?[eq.equipmentId] ??
|
||||
eq.returnedQuantity ??
|
||||
eq.quantity;
|
||||
return eq.copyWith(
|
||||
isUnloaded: true,
|
||||
isReturned: true,
|
||||
returnedQuantity: returnedQty,
|
||||
);
|
||||
}).toList();
|
||||
|
||||
await _eventsCollection.doc(eventId).update({
|
||||
'assignedEquipment': updatedEquipment.map((e) => e.toMap()).toList(),
|
||||
'unloadingStatus': unloadingStatusToString(UnloadingStatus.completed),
|
||||
'returnStatus': returnStatusToString(ReturnStatus.completed),
|
||||
});
|
||||
|
||||
// Mettre à jour les statuts et stocks
|
||||
for (var equipment in updatedEquipment) {
|
||||
final equipmentDoc = await _equipmentCollection.doc(equipment.equipmentId).get();
|
||||
if (equipmentDoc.exists) {
|
||||
final equipmentData = EquipmentModel.fromMap(
|
||||
equipmentDoc.data() as Map<String, dynamic>,
|
||||
equipmentDoc.id,
|
||||
);
|
||||
|
||||
if (!equipmentData.hasQuantity) {
|
||||
await _updateEquipmentStatus(equipment.equipmentId, EquipmentStatus.available);
|
||||
}
|
||||
|
||||
if (equipmentData.hasQuantity && equipment.returnedQuantity != null) {
|
||||
final currentAvailable = equipmentData.availableQuantity ?? 0;
|
||||
await _equipmentCollection.doc(equipment.equipmentId).update({
|
||||
'availableQuantity': currentAvailable + equipment.returnedQuantity!,
|
||||
'updatedAt': Timestamp.fromDate(DateTime.now()),
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
print('Error validating all unloading and return: $e');
|
||||
rethrow;
|
||||
}
|
||||
}
|
||||
|
||||
// === HELPERS ===
|
||||
|
||||
Future<void> _updateEquipmentStatus(String equipmentId, EquipmentStatus status) async {
|
||||
try {
|
||||
final doc = await _equipmentCollection.doc(equipmentId).get();
|
||||
if (!doc.exists) return;
|
||||
|
||||
await _equipmentCollection.doc(equipmentId).update({
|
||||
'status': equipmentStatusToString(status),
|
||||
'updatedAt': Timestamp.fromDate(DateTime.now()),
|
||||
});
|
||||
} catch (e) {
|
||||
print('Error updating equipment status: $e');
|
||||
}
|
||||
}
|
||||
|
||||
Future<EventModel?> _getEvent(String eventId) async {
|
||||
try {
|
||||
final doc = await _eventsCollection.doc(eventId).get();
|
||||
if (doc.exists) {
|
||||
return EventModel.fromMap(doc.data() as Map<String, dynamic>, doc.id);
|
||||
}
|
||||
return null;
|
||||
} catch (e) {
|
||||
print('Error getting event: $e');
|
||||
rethrow;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user