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:
ElPoyo
2026-01-06 10:53:23 +01:00
parent fa1d6a4295
commit 25d395b41a
18 changed files with 2121 additions and 500 deletions

View File

@@ -62,10 +62,6 @@ class _EventAssignedEquipmentSectionState extends State<EventAssignedEquipmentSe
setState(() => _isLoading = true);
try {
print('[EventAssignedEquipmentSection] Loading equipment and containers...');
print('[EventAssignedEquipmentSection] assignedEquipment: ${widget.assignedEquipment.map((e) => e.equipmentId).toList()}');
print('[EventAssignedEquipmentSection] assignedContainers: ${widget.assignedContainers}');
final equipmentProvider = context.read<EquipmentProvider>();
final containerProvider = context.read<ContainerProvider>();
@@ -73,62 +69,40 @@ class _EventAssignedEquipmentSectionState extends State<EventAssignedEquipmentSe
final equipment = await equipmentProvider.equipmentStream.first;
final containers = await containerProvider.containersStream.first;
print('[EventAssignedEquipmentSection] Available equipment count: ${equipment.length}');
print('[EventAssignedEquipmentSection] Available containers count: ${containers.length}');
// Créer le cache des équipements
for (var eq in widget.assignedEquipment) {
print('[EventAssignedEquipmentSection] Looking for equipment: ${eq.equipmentId}');
final equipmentItem = equipment.firstWhere(
(e) {
print('[EventAssignedEquipmentSection] Comparing "${e.id}" with "${eq.equipmentId}"');
return e.id == eq.equipmentId;
},
orElse: () {
print('[EventAssignedEquipmentSection] Equipment NOT FOUND: ${eq.equipmentId}');
return EquipmentModel(
id: eq.equipmentId,
name: 'Équipement inconnu',
category: EquipmentCategory.other,
status: EquipmentStatus.available,
parentBoxIds: [],
maintenanceIds: [],
createdAt: DateTime.now(),
updatedAt: DateTime.now(),
);
},
(e) => e.id == eq.equipmentId,
orElse: () => EquipmentModel(
id: eq.equipmentId,
name: 'Équipement inconnu',
category: EquipmentCategory.other,
status: EquipmentStatus.available,
parentBoxIds: [],
maintenanceIds: [],
createdAt: DateTime.now(),
updatedAt: DateTime.now(),
),
);
_equipmentCache[eq.equipmentId] = equipmentItem;
print('[EventAssignedEquipmentSection] Cached equipment: ${equipmentItem.id} (${equipmentItem.name})');
}
// Créer le cache des conteneurs
for (var containerId in widget.assignedContainers) {
print('[EventAssignedEquipmentSection] Looking for container: $containerId');
final container = containers.firstWhere(
(c) {
print('[EventAssignedEquipmentSection] Comparing "${c.id}" with "$containerId"');
return c.id == containerId;
},
orElse: () {
print('[EventAssignedEquipmentSection] Container NOT FOUND: $containerId');
return ContainerModel(
id: containerId,
name: 'Conteneur inconnu',
type: ContainerType.flightCase,
status: EquipmentStatus.available,
equipmentIds: [],
updatedAt: DateTime.now(),
createdAt: DateTime.now(),
);
},
(c) => c.id == containerId,
orElse: () => ContainerModel(
id: containerId,
name: 'Conteneur inconnu',
type: ContainerType.flightCase,
status: EquipmentStatus.available,
equipmentIds: [],
updatedAt: DateTime.now(),
createdAt: DateTime.now(),
),
);
_containerCache[containerId] = container;
print('[EventAssignedEquipmentSection] Cached container: ${container.id} (${container.name})');
}
print('[EventAssignedEquipmentSection] Equipment cache: ${_equipmentCache.keys.toList()}');
print('[EventAssignedEquipmentSection] Container cache: ${_containerCache.keys.toList()}');
} catch (e) {
print('[EventAssignedEquipmentSection] Error loading equipment/containers: $e');
} finally {
@@ -173,18 +147,16 @@ class _EventAssignedEquipmentSectionState extends State<EventAssignedEquipmentSe
}
}
// Charger les équipements des conteneurs pour vérifier les conflits
// Charger les équipements et conteneurs
final containerProvider = context.read<ContainerProvider>();
final equipmentProvider = context.read<EquipmentProvider>();
final allContainers = await containerProvider.containersStream.first;
final allEquipment = await equipmentProvider.equipmentStream.first;
// Collecter TOUS les équipements à vérifier (directs + enfants des boîtes)
final equipmentIds = newEquipment.map((e) => e.equipmentId).toList();
final equipmentNames = <String, String>{};
final allConflicts = <String, List<AvailabilityConflict>>{};
// Ajouter les équipements directs
// 1. Vérifier les conflits pour les équipements directs
for (var eq in newEquipment) {
final equipment = allEquipment.firstWhere(
(e) => e.id == eq.equipmentId,
@@ -199,10 +171,51 @@ class _EventAssignedEquipmentSectionState extends State<EventAssignedEquipmentSe
updatedAt: DateTime.now(),
),
);
equipmentNames[eq.equipmentId] = equipment.id;
// Pour les équipements quantifiables (consommables/câbles)
if (equipment.hasQuantity) {
// Vérifier la quantité disponible
final availableQty = await _availabilityService.getAvailableQuantity(
equipment: equipment,
startDate: widget.startDate!,
endDate: widget.endDate!,
excludeEventId: widget.eventId,
);
// ⚠️ Ne créer un conflit QUE si la quantité demandée est supérieure à la quantité disponible
if (eq.quantity > availableQty) {
// Il y a vraiment un conflit de quantité
final conflicts = await _availabilityService.checkEquipmentAvailabilityWithQuantity(
equipment: equipment,
requestedQuantity: eq.quantity,
startDate: widget.startDate!,
endDate: widget.endDate!,
excludeEventId: widget.eventId,
);
// Ne garder que les conflits réels (quand il n'y a pas assez de stock)
if (conflicts.isNotEmpty) {
allConflicts[eq.equipmentId] = conflicts;
}
}
// ✅ Sinon, pas de conflit : il y a assez de stock disponible
} else {
// Pour les équipements non quantifiables (vérification classique)
final conflicts = await _availabilityService.checkEquipmentAvailability(
equipmentId: equipment.id,
equipmentName: equipment.name,
startDate: widget.startDate!,
endDate: widget.endDate!,
excludeEventId: widget.eventId,
);
if (conflicts.isNotEmpty) {
allConflicts[eq.equipmentId] = conflicts;
}
}
}
// Ajouter les équipements des conteneurs (par composition)
// 2. Vérifier les conflits pour les boîtes et leur contenu
for (var containerId in newContainers) {
final container = allContainers.firstWhere(
(c) => c.id == containerId,
@@ -217,76 +230,105 @@ class _EventAssignedEquipmentSectionState extends State<EventAssignedEquipmentSe
),
);
// Ajouter tous les équipements enfants pour vérification
for (var childEquipmentId in container.equipmentIds) {
if (!equipmentIds.contains(childEquipmentId)) {
equipmentIds.add(childEquipmentId);
// Récupérer les équipements de la boîte
final containerEquipment = container.equipmentIds
.map((eqId) => allEquipment.firstWhere(
(e) => e.id == eqId,
orElse: () => EquipmentModel(
id: eqId,
name: 'Inconnu',
category: EquipmentCategory.other,
status: EquipmentStatus.available,
parentBoxIds: [],
maintenanceIds: [],
createdAt: DateTime.now(),
updatedAt: DateTime.now(),
),
))
.toList();
final equipment = allEquipment.firstWhere(
(e) => e.id == childEquipmentId,
orElse: () => EquipmentModel(
id: childEquipmentId,
name: 'Inconnu',
category: EquipmentCategory.other,
status: EquipmentStatus.available,
parentBoxIds: [],
maintenanceIds: [],
createdAt: DateTime.now(),
updatedAt: DateTime.now(),
),
// Vérifier chaque équipement de la boîte individuellement
final containerConflicts = <AvailabilityConflict>[];
for (var equipment in containerEquipment) {
if (equipment.hasQuantity) {
// Pour les consommables/câbles, vérifier la quantité disponible
final availableQty = await _availabilityService.getAvailableQuantity(
equipment: equipment,
startDate: widget.startDate!,
endDate: widget.endDate!,
excludeEventId: widget.eventId,
);
equipmentNames[childEquipmentId] = '${equipment.id} (dans ${container.name})';
// La boîte contient 1 unité de cet équipement
// Si la quantité disponible est insuffisante, créer un conflit
if (availableQty < 1) {
final conflicts = await _availabilityService.checkEquipmentAvailability(
equipmentId: equipment.id,
equipmentName: equipment.name,
startDate: widget.startDate!,
endDate: widget.endDate!,
excludeEventId: widget.eventId,
);
containerConflicts.addAll(conflicts);
}
} else {
// Pour les équipements non quantifiables
final conflicts = await _availabilityService.checkEquipmentAvailability(
equipmentId: equipment.id,
equipmentName: equipment.name,
startDate: widget.startDate!,
endDate: widget.endDate!,
excludeEventId: widget.eventId,
);
containerConflicts.addAll(conflicts);
}
}
if (containerConflicts.isNotEmpty) {
allConflicts[containerId] = containerConflicts;
}
}
// Vérifier les conflits pour TOUS les équipements (directs + enfants)
final conflicts = await _availabilityService.checkMultipleEquipmentAvailability(
equipmentIds: equipmentIds,
equipmentNames: equipmentNames,
startDate: widget.startDate!,
endDate: widget.endDate!,
excludeEventId: widget.eventId,
);
if (conflicts.isNotEmpty) {
if (allConflicts.isNotEmpty) {
// Afficher le dialog de conflits
final action = await showDialog<String>(
context: context,
builder: (context) => EquipmentConflictDialog(conflicts: conflicts),
builder: (context) => EquipmentConflictDialog(conflicts: allConflicts),
);
if (action == 'cancel') {
return; // Annuler l'ajout
} else if (action == 'force_removed') {
// Identifier quels équipements retirer
final removedIds = conflicts.keys.toSet();
// Identifier quels équipements/conteneurs retirer
final removedIds = allConflicts.keys.toSet();
// Retirer les équipements directs en conflit
newEquipment.removeWhere((eq) => removedIds.contains(eq.equipmentId));
// Retirer les boîtes dont au moins un équipement enfant est en conflit
final containersToRemove = <String>[];
for (var containerId in newContainers) {
final container = allContainers.firstWhere((c) => c.id == containerId);
final hasConflict = container.equipmentIds.any((eqId) => removedIds.contains(eqId));
// Retirer les boîtes en conflit
newContainers.removeWhere((containerId) => removedIds.contains(containerId));
if (hasConflict) {
containersToRemove.add(containerId);
}
}
for (var containerId in containersToRemove) {
newContainers.remove(containerId);
// Informer l'utilisateur
// Informer l'utilisateur des boîtes retirées
for (var containerId in removedIds.where((id) => newContainers.contains(id))) {
if (mounted) {
final containerName = allContainers.firstWhere((c) => c.id == containerId).name;
final container = allContainers.firstWhere(
(c) => c.id == containerId,
orElse: () => ContainerModel(
id: containerId,
name: 'Inconnu',
type: ContainerType.flightCase,
status: EquipmentStatus.available,
equipmentIds: [],
createdAt: DateTime.now(),
updatedAt: DateTime.now(),
),
);
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text('La boîte "$containerName" a été retirée car elle contient du matériel en conflit.'),
content: Text('La boîte "${container.name}" a été retirée en raison de conflits.'),
backgroundColor: Colors.orange,
duration: const Duration(seconds: 4),
duration: const Duration(seconds: 3),
),
);
}
@@ -299,8 +341,21 @@ class _EventAssignedEquipmentSectionState extends State<EventAssignedEquipmentSe
final updatedEquipment = [...widget.assignedEquipment];
final updatedContainers = [...widget.assignedContainers];
// Pour chaque nouvel équipement
for (var eq in newEquipment) {
if (!updatedEquipment.any((e) => e.equipmentId == eq.equipmentId)) {
final existingIndex = updatedEquipment.indexWhere((e) => e.equipmentId == eq.equipmentId);
if (existingIndex != -1) {
// L'équipement existe déjà : mettre à jour la quantité
updatedEquipment[existingIndex] = EventEquipment(
equipmentId: eq.equipmentId,
quantity: eq.quantity, // Utiliser la nouvelle quantité
isPrepared: updatedEquipment[existingIndex].isPrepared,
isReturned: updatedEquipment[existingIndex].isReturned,
returnedQuantity: updatedEquipment[existingIndex].returnedQuantity,
);
} else {
// L'équipement n'existe pas : l'ajouter
updatedEquipment.add(eq);
}
}
@@ -347,11 +402,6 @@ class _EventAssignedEquipmentSectionState extends State<EventAssignedEquipmentSe
return true;
}).toList();
print('[EventAssignedEquipmentSection] Removing container $containerId');
if (container != null) {
print('[EventAssignedEquipmentSection] Removing ${container.equipmentIds.length} children: ${container.equipmentIds}');
}
print('[EventAssignedEquipmentSection] Equipment before: ${widget.assignedEquipment.length}, after: ${updatedEquipment.length}');
// Notifier le changement avec les deux listes mises à jour
widget.onChanged(updatedEquipment, updatedContainers);