Compare commits

...

2 Commits

Author SHA1 Message Date
ElPoyo 0551f0b9c1 feat: Mise à jour à la version 1.1.20 et amélioration de la recherche d'équipements
- Mise à jour de la version de l'application à `1.1.20` dans `app_version.dart`, `version.json` et `CHANGELOG.md`.
- Optimisation de la fonction Cloud `getEquipmentsPaginated` pour supporter la recherche par ID exact (document ID ou ID legacy) et améliorer la recherche textuelle avec filtrage par lots.
- Amélioration de la gestion des alertes dans `processEquipmentValidation.js` :
    - Ajout d'un statut `NOT_TAKEN` pour éviter les fausses alertes d'équipements perdus s'ils n'ont jamais été emportés.
    - Refonte complète du parsing des dates Firestore pour une meilleure robustesse dans les alertes.
    - Correction de la validation des quantités (vérification du type `number`).
- Ajout de méthodes statiques dans `EventPreparationService` (`shouldMarkEquipmentAsLost`, `isEquipmentNotTakenToEvent`) pour centraliser la logique de détermination du statut des équipements au retour.
- Mise à jour de `EventPreparationPage` pour intégrer le nouveau statut `NOT_TAKEN` et utiliser la logique centralisée du service de préparation.
- Mise à jour des fichiers de cache Firebase Hosting.
2026-03-30 17:12:48 +02:00
ElPoyo cf13b4a986 .env gitignore 2026-03-28 21:38:56 +01:00
9 changed files with 325 additions and 66 deletions
+13 -13
View File
@@ -34,16 +34,16 @@ assets/assets/images/tshirt-incrust.webp,1737393735487,af7cb34adfca19c0b41c8eb63
assets/assets/icons/truss.svg,1761734811263,8ddfbbb4f96de5614348eb23fa55f61b2eb1edb064719a8bbd791c35883ec4cc assets/assets/icons/truss.svg,1761734811263,8ddfbbb4f96de5614348eb23fa55f61b2eb1edb064719a8bbd791c35883ec4cc
assets/assets/icons/tape.svg,1761734809221,631183f0ff972aa4dc3f9f51dc7abd41a607df749d1f9a44fa7e77202d95ccde assets/assets/icons/tape.svg,1761734809221,631183f0ff972aa4dc3f9f51dc7abd41a607df749d1f9a44fa7e77202d95ccde
assets/assets/icons/flight-case.svg,1761734822495,0cef47fdf5d7efdd110763c32f792ef9735df35c4f42ae7d02d5fbda40e6148d assets/assets/icons/flight-case.svg,1761734822495,0cef47fdf5d7efdd110763c32f792ef9735df35c4f42ae7d02d5fbda40e6148d
version.json,1773324020831,d5cd7334d7c3a990dbff0821b9aaab39129803e306b0d96599b8adc6d4f433a6 version.json,1774883074073,049c47e9089dc5497475a6cf7733e11235bc9cfa30d458cc9a8eae761214c2b8
index.html,1773324025840,4e8c00552c71ef134bead8bc03706952e7a415d70fca602a3839dc02a3f7ae10 flutter_service_worker.js,1774883173949,00cc791f6cc0d2beb4b16cc382b049268125aa6a7c5b73cd4bc89a003fc70f3a
flutter_service_worker.js,1773324116910,8582e401e070055f59183c207cf7a7e6a9219a50f5089a24a77d91d3ff77dcbc index.html,1774883102020,4e8c00552c71ef134bead8bc03706952e7a415d70fca602a3839dc02a3f7ae10
flutter_bootstrap.js,1773324025827,74eaa66055c715df232ee96fc4114d5473f67717278fb4effa38d8b1b362e303 flutter_bootstrap.js,1774883102005,80bbca812eb76632e250fe5c6b726db647443cbabc7f90010618e6a6f445d222
assets/FontManifest.json,1773324113335,e38b95988f5d060cf9b7ce97cb5ac9236d6f4cc04a11d69567df97b2b4cbc5e5 assets/FontManifest.json,1774883170660,e38b95988f5d060cf9b7ce97cb5ac9236d6f4cc04a11d69567df97b2b4cbc5e5
assets/AssetManifest.json,1773324113335,0e35e7214421c832bf41b0af7c03037e66fee508b857d3143f40f6862e454dd6 assets/AssetManifest.bin,1774883170657,205908d2fcf1ca9708b7d1f91ec7ea80c5f07eaf6cfc1458cb9364a4d9106907
assets/AssetManifest.bin.json,1773324113335,3a244f5f866d93c17f420cc01b1ba318584b4da92af9512d9ba4acd099b49d53 assets/AssetManifest.bin.json,1774883170660,3a244f5f866d93c17f420cc01b1ba318584b4da92af9512d9ba4acd099b49d53
assets/AssetManifest.bin,1773324113335,205908d2fcf1ca9708b7d1f91ec7ea80c5f07eaf6cfc1458cb9364a4d9106907 assets/shaders/ink_sparkle.frag,1774883170848,591c7517d5cb43eb91ea451e0d3f9f585cbf8298cf6c46a9144b77cb0775a406
assets/packages/cupertino_icons/assets/CupertinoIcons.ttf,1773324115847,d41473de1f7708a0702d7f19327693486512db442f6ab0cf7774e6d6576f9fcb assets/packages/cupertino_icons/assets/CupertinoIcons.ttf,1774883173201,d41473de1f7708a0702d7f19327693486512db442f6ab0cf7774e6d6576f9fcb
assets/shaders/ink_sparkle.frag,1773324113551,591c7517d5cb43eb91ea451e0d3f9f585cbf8298cf6c46a9144b77cb0775a406 assets/AssetManifest.json,1774883170657,0e35e7214421c832bf41b0af7c03037e66fee508b857d3143f40f6862e454dd6
assets/fonts/MaterialIcons-Regular.otf,1773324115852,d1409c3c8050990bdc63a413539d600245a27c9794a053c211299cc86d4f6a5c assets/fonts/MaterialIcons-Regular.otf,1774883173207,d1409c3c8050990bdc63a413539d600245a27c9794a053c211299cc86d4f6a5c
assets/NOTICES,1773324113339,1d9a08da58db7959b9607f0f1f342f96243af76dc608ed659614d586ec58cd79 assets/NOTICES,1774883170660,1d9a08da58db7959b9607f0f1f342f96243af76dc608ed659614d586ec58cd79
main.dart.js,1773324112059,bfc66ab7e817db63dee4b996af3dea0629c4c4e87ba91070c15b133ab5104848 main.dart.js,1774883168025,bc4bc60206728a982496fe5977f48e690fe8abdfd1167a9226de18fe0052cdcf
+2
View File
@@ -45,3 +45,5 @@ app.*.map.json
# Environment configuration with credentials # Environment configuration with credentials
lib/config/env.dev.dart lib/config/env.dev.dart
functions/.env functions/.env
.env
env.dart
+3
View File
@@ -2,6 +2,9 @@
Toutes les modifications notables de ce projet seront documentées dans ce fichier. Toutes les modifications notables de ce projet seront documentées dans ce fichier.
## 30/03/2026
Patch bug envoi d'alerte equipement perdu, date dans les alertes, recherche par ID d'équipement.
## 24/03/2026 ## 24/03/2026
Fix BUG : Problème de cache avec les équipements non affichés dans le dialog de sélection d'équipement. Amélioration de la gestion du cache pour éviter les problèmes d'affichage. Fix BUG : Problème de cache avec les équipements non affichés dans le dialog de sélection d'équipement. Amélioration de la gestion du cache pour éviter les problèmes d'affichage.
+185 -36
View File
@@ -3825,18 +3825,97 @@ exports.getEquipmentsPaginated = onRequest(httpOptions, withCors(async (req, res
// Convertir en majuscules pour correspondre au format Firestore // Convertir en majuscules pour correspondre au format Firestore
const category = params.category ? params.category.toUpperCase() : null; const category = params.category ? params.category.toUpperCase() : null;
const status = params.status ? params.status.toUpperCase() : null; const status = params.status ? params.status.toUpperCase() : null;
const searchQuery = params.searchQuery?.toLowerCase() || null; const rawSearchQuery = typeof params.searchQuery === 'string' ? params.searchQuery.trim() : '';
const searchQuery = rawSearchQuery ? rawSearchQuery.toLowerCase() : null;
const compactSearchQuery = searchQuery ? searchQuery.replace(/[\s_-]+/g, '') : null;
const sortBy = params.sortBy || 'id'; const sortBy = params.sortBy || 'id';
const sortOrder = params.sortOrder === 'desc' ? 'desc' : 'asc'; const sortOrder = params.sortOrder === 'desc' ? 'desc' : 'asc';
logger.info(`[getEquipmentsPaginated] Params: limit=${limit}, startAfter=${startAfterId}, category=${category}, status=${status}, search=${searchQuery}`); logger.info(`[getEquipmentsPaginated] Params: limit=${limit}, startAfter=${startAfterId}, category=${category}, status=${status}, search=${searchQuery}`);
// Fast-path pour une recherche d'ID exact: évite le cap queryLimit lors d'une recherche précise.
if (searchQuery && !startAfterId) {
const exactIdCandidates = Array.from(new Set([
rawSearchQuery,
rawSearchQuery.toUpperCase(),
rawSearchQuery.toLowerCase()
].filter(Boolean)));
for (const candidateId of exactIdCandidates) {
const exactDoc = await db.collection('equipments').doc(candidateId).get();
if (!exactDoc.exists) {
continue;
}
const exactData = exactDoc.data() || {};
const matchesCategory = !category || exactData.category === category;
const matchesStatus = !status || exactData.status === status;
if (!matchesCategory || !matchesStatus) {
continue;
}
if (!canManage) {
delete exactData.purchasePrice;
delete exactData.rentalPrice;
}
const exactEquipment = {
...helpers.serializeTimestamps(exactData, ['purchaseDate', 'nextMaintenanceDate', 'lastMaintenanceDate', 'createdAt', 'updatedAt']),
id: exactDoc.id
};
logger.info(`[getEquipmentsPaginated] Exact ID hit for ${exactDoc.id}`);
res.status(200).json({
equipments: [exactEquipment],
hasMore: false,
lastVisible: exactDoc.id,
total: 1
});
return;
}
// Compatibilité legacy: certains documents peuvent stocker un ancien champ `id` différent du document ID.
for (const legacyId of exactIdCandidates) {
let legacyIdQuery = db.collection('equipments').where('id', '==', legacyId);
if (category) {
legacyIdQuery = legacyIdQuery.where('category', '==', category);
}
if (status) {
legacyIdQuery = legacyIdQuery.where('status', '==', status);
}
const legacySnapshot = await legacyIdQuery.limit(1).get();
if (legacySnapshot.empty) {
continue;
}
const exactDoc = legacySnapshot.docs[0];
const exactData = exactDoc.data() || {};
if (!canManage) {
delete exactData.purchasePrice;
delete exactData.rentalPrice;
}
const exactEquipment = {
...helpers.serializeTimestamps(exactData, ['purchaseDate', 'nextMaintenanceDate', 'lastMaintenanceDate', 'createdAt', 'updatedAt']),
id: exactDoc.id
};
logger.info(`[getEquipmentsPaginated] Exact legacy ID hit for ${legacyId} -> ${exactDoc.id}`);
res.status(200).json({
equipments: [exactEquipment],
hasMore: false,
lastVisible: exactDoc.id,
total: 1
});
return;
}
}
// Construire la requête Firestore // Construire la requête Firestore
let query = db.collection('equipments'); let query = db.collection('equipments');
// Si recherche textuelle, on augmente la limite pour filtrer ensuite
const queryLimit = searchQuery ? Math.min(limit * 10, 200) : limit;
// Appliquer les filtres // Appliquer les filtres
if (category) { if (category) {
query = query.where('category', '==', category); query = query.where('category', '==', category);
@@ -3861,20 +3940,10 @@ exports.getEquipmentsPaginated = onRequest(httpOptions, withCors(async (req, res
} }
} }
// Limiter les résultats const timestampFields = ['purchaseDate', 'nextMaintenanceDate', 'lastMaintenanceDate', 'createdAt', 'updatedAt'];
query = query.limit(queryLimit + 1);
const snapshot = await query.get(); const mapEquipmentDoc = (doc) => {
const data = {...(doc.data() || {})};
// Déterminer hasMore basé sur le nombre de documents Firestore
const rawDocCount = snapshot.docs.length;
const hasMoreDocs = rawDocCount > queryLimit;
const docsToProcess = hasMoreDocs ? snapshot.docs.slice(0, queryLimit) : snapshot.docs;
logger.info(`[getEquipmentsPaginated] Firestore returned ${rawDocCount} docs, hasMore=${hasMoreDocs}`);
let equipments = docsToProcess.map(doc => {
const data = doc.data();
// Masquer les prix si l'utilisateur n'a pas manage_equipment // Masquer les prix si l'utilisateur n'a pas manage_equipment
if (!canManage) { if (!canManage) {
@@ -3882,32 +3951,50 @@ exports.getEquipmentsPaginated = onRequest(httpOptions, withCors(async (req, res
delete data.rentalPrice; delete data.rentalPrice;
} }
return { const legacyId = typeof data.id === 'string' ? data.id : '';
id: doc.id,
...helpers.serializeTimestamps(data, ['purchaseDate', 'nextMaintenanceDate', 'lastMaintenanceDate', 'createdAt', 'updatedAt'])
};
});
// Filtrage textuel côté serveur return {
if (searchQuery) { ...helpers.serializeTimestamps(data, timestampFields),
equipments = equipments.filter(eq => { id: doc.id,
_legacyId: legacyId
};
};
const matchesSearchQuery = (equipment) => {
const searchableText = [ const searchableText = [
eq.name || '', equipment.name || '',
eq.id || '', equipment.id || '',
eq.model || '', equipment._legacyId || '',
eq.brand || '', equipment.model || '',
eq.subCategory || '' equipment.brand || '',
equipment.subCategory || ''
].join(' ').toLowerCase(); ].join(' ').toLowerCase();
return searchableText.includes(searchQuery);
}); if (searchableText.includes(searchQuery)) {
return true;
} }
// Pour la limite finale après filtrage textuel if (!compactSearchQuery) {
const limitedEquipments = equipments.slice(0, limit); return false;
}
const compactSearchableText = searchableText.replace(/[\s_-]+/g, '');
return compactSearchableText.includes(compactSearchQuery);
};
if (!searchQuery) {
const snapshot = await query.limit(limit + 1).get();
const rawDocCount = snapshot.docs.length;
const hasMoreDocs = rawDocCount > limit;
const docsToProcess = hasMoreDocs ? snapshot.docs.slice(0, limit) : snapshot.docs;
const limitedEquipments = docsToProcess
.map(mapEquipmentDoc)
.map(({_legacyId, ...equipment}) => equipment);
const lastVisible = limitedEquipments.length > 0 ? limitedEquipments[limitedEquipments.length - 1].id : null; const lastVisible = limitedEquipments.length > 0 ? limitedEquipments[limitedEquipments.length - 1].id : null;
// hasMore reste basé sur le nombre de docs Firestore, pas sur le filtrage textuel logger.info(`[getEquipmentsPaginated] Firestore returned ${rawDocCount} docs, hasMore=${hasMoreDocs}`);
logger.info(`[getEquipmentsPaginated] Returning ${limitedEquipments.length} equipments (filtered from ${equipments.length}), hasMore=${hasMoreDocs}`); logger.info(`[getEquipmentsPaginated] Returning ${limitedEquipments.length} equipments, hasMore=${hasMoreDocs}`);
res.status(200).json({ res.status(200).json({
equipments: limitedEquipments, equipments: limitedEquipments,
@@ -3915,6 +4002,68 @@ exports.getEquipmentsPaginated = onRequest(httpOptions, withCors(async (req, res
lastVisible, lastVisible,
total: limitedEquipments.length total: limitedEquipments.length
}); });
return;
}
// En mode recherche, scanner la collection par lots jusqu'à obtenir `limit + 1` matchs
// afin de garantir des résultats même si les documents pertinents sont loin dans l'ordre de tri.
const searchBatchSize = Math.min(Math.max(limit * 10, limit), 200);
const matchedEquipments = [];
let scannedDocuments = 0;
let searchQueryRef = query;
let hasMoreMatches = false;
let hasMoreDocsToScan = true;
while (hasMoreDocsToScan && !hasMoreMatches) {
const snapshot = await searchQueryRef.limit(searchBatchSize).get();
if (snapshot.empty) {
hasMoreDocsToScan = false;
break;
}
scannedDocuments += snapshot.docs.length;
for (const doc of snapshot.docs) {
const equipment = mapEquipmentDoc(doc);
if (!matchesSearchQuery(equipment)) {
continue;
}
matchedEquipments.push(equipment);
if (matchedEquipments.length > limit) {
hasMoreMatches = true;
break;
}
}
if (hasMoreMatches) {
break;
}
if (snapshot.docs.length < searchBatchSize) {
hasMoreDocsToScan = false;
break;
}
const lastDocInBatch = snapshot.docs[snapshot.docs.length - 1];
searchQueryRef = query.startAfter(lastDocInBatch);
}
const limitedEquipments = matchedEquipments
.slice(0, limit)
.map(({_legacyId, ...equipment}) => equipment);
const lastVisible = limitedEquipments.length > 0 ? limitedEquipments[limitedEquipments.length - 1].id : null;
logger.info(`[getEquipmentsPaginated] Search scan read ${scannedDocuments} docs and found ${matchedEquipments.length} matches`);
logger.info(`[getEquipmentsPaginated] Returning ${limitedEquipments.length} equipments, hasMore=${hasMoreMatches}`);
res.status(200).json({
equipments: limitedEquipments,
hasMore: hasMoreMatches,
lastVisible,
total: limitedEquipments.length
});
} catch (error) { } catch (error) {
logger.error("Error fetching paginated equipments:", error); logger.error("Error fetching paginated equipments:", error);
+51 -6
View File
@@ -50,6 +50,11 @@ exports.processEquipmentValidation = onCall({
for (const equipment of equipmentList) { for (const equipment of equipmentList) {
const {equipmentId, status, quantity, expectedQuantity} = equipment; const {equipmentId, status, quantity, expectedQuantity} = equipment;
// Équipement non emporté: pas d'alerte de perte/manquant au retour.
if (status === 'NOT_TAKEN') {
continue;
}
// Cas 1: Équipement PERDU // Cas 1: Équipement PERDU
if (status === 'LOST') { if (status === 'LOST') {
const alertData = await createAlertInFirestore({ const alertData = await createAlertInFirestore({
@@ -91,7 +96,9 @@ exports.processEquipmentValidation = onCall({
} }
// Cas 3: Quantité incorrecte // Cas 3: Quantité incorrecte
if (expectedQuantity && quantity !== expectedQuantity) { const hasExpectedQuantity = typeof expectedQuantity === 'number';
const hasActualQuantity = typeof quantity === 'number';
if (hasExpectedQuantity && hasActualQuantity && quantity !== expectedQuantity) {
const alertData = await createAlertInFirestore({ const alertData = await createAlertInFirestore({
type: 'QUANTITY_MISMATCH', type: 'QUANTITY_MISMATCH',
severity: 'INFO', severity: 'INFO',
@@ -409,10 +416,48 @@ async function sendAlertEmails(alert, userIds) {
* Formate la date d'un événement * Formate la date d'un événement
*/ */
function formatEventDate(event) { function formatEventDate(event) {
if (event.startDate) { const rawDate =
const date = event.startDate.toDate ? event.startDate.toDate() : new Date(event.startDate); event?.StartDateTime ||
return date.toLocaleDateString('fr-FR', {day: 'numeric', month: 'numeric', year: 'numeric'}); event?.startDateTime ||
} event?.startDate ||
return 'Date inconnue'; event?.eventDate;
const parsedDate = parseFirestoreDate(rawDate);
const safeDate = parsedDate || new Date();
return safeDate.toLocaleDateString('fr-FR', {
day: 'numeric',
month: 'numeric',
year: 'numeric',
});
}
function parseFirestoreDate(value) {
if (!value) {
return null;
}
if (typeof value.toDate === 'function') {
return value.toDate();
}
if (value instanceof Date) {
return value;
}
if (typeof value === 'string' || typeof value === 'number') {
const date = new Date(value);
return Number.isNaN(date.getTime()) ? null : date;
}
if (typeof value === 'object' && typeof value.seconds === 'number') {
return new Date(value.seconds * 1000);
}
if (typeof value === 'object' && typeof value._seconds === 'number') {
return new Date(value._seconds * 1000);
}
return null;
} }
+1 -1
View File
@@ -1,6 +1,6 @@
/// Configuration de la version de l'application /// Configuration de la version de l'application
class AppVersion { class AppVersion {
static const String version = '1.1.19'; static const String version = '1.1.20';
/// Retourne la version complète de l'application /// Retourne la version complète de l'application
static String get fullVersion => 'v$version'; static String get fullVersion => 'v$version';
@@ -4,6 +4,44 @@ import 'package:em2rp/services/api_service.dart';
class EventPreparationService { class EventPreparationService {
final ApiService _apiService = apiService; final ApiService _apiService = apiService;
/// Retourne true si l'équipement était absent du flux événementiel.
///
/// Cas typique: matériel jamais emporté au départ, donc absent au retour,
/// mais qui ne doit jamais être classé en [LOST].
static bool isEquipmentNotTakenToEvent({
required bool isMissingAtReturn,
required bool isLoaded,
required bool isMissingAtLoading,
int? quantityAtLoading,
}) {
if (!isMissingAtReturn) {
return false;
}
final loadedQuantity = quantityAtLoading ?? 0;
return !isLoaded || isMissingAtLoading || loadedQuantity <= 0;
}
/// Retourne true uniquement si l'équipement doit être classé perdu.
static bool shouldMarkEquipmentAsLost({
required bool isReturnValidationStep,
required bool isMissingAtReturn,
required bool isLoaded,
required bool isMissingAtLoading,
int? quantityAtLoading,
}) {
if (!isReturnValidationStep || !isMissingAtReturn) {
return false;
}
return !isEquipmentNotTakenToEvent(
isMissingAtReturn: isMissingAtReturn,
isLoaded: isLoaded,
isMissingAtLoading: isMissingAtLoading,
quantityAtLoading: quantityAtLoading,
);
}
// === PRÉPARATION === // === PRÉPARATION ===
/// Valider un équipement individuel en préparation /// Valider un équipement individuel en préparation
+27 -5
View File
@@ -13,6 +13,7 @@ import 'package:em2rp/services/qr_code_processing_service.dart';
import 'package:em2rp/services/audio_feedback_service.dart'; import 'package:em2rp/services/audio_feedback_service.dart';
import 'package:em2rp/services/smart_text_to_speech_service.dart'; import 'package:em2rp/services/smart_text_to_speech_service.dart';
import 'package:em2rp/services/equipment_service.dart'; import 'package:em2rp/services/equipment_service.dart';
import 'package:em2rp/services/event_preparation_service.dart';
import 'package:em2rp/views/widgets/equipment/equipment_checklist_item.dart' show EquipmentChecklistItem, ChecklistStep; import 'package:em2rp/views/widgets/equipment/equipment_checklist_item.dart' show EquipmentChecklistItem, ChecklistStep;
import 'package:em2rp/views/widgets/equipment/container_checklist_item.dart'; import 'package:em2rp/views/widgets/equipment/container_checklist_item.dart';
import 'package:em2rp/views/widgets/common/qr_code_scanner_dialog.dart'; import 'package:em2rp/views/widgets/common/qr_code_scanner_dialog.dart';
@@ -1097,6 +1098,10 @@ class _EventPreparationPageState extends State<EventPreparationPage> with Single
/// Détermine le statut d'un équipement selon l'étape actuelle /// Détermine le statut d'un équipement selon l'étape actuelle
String _determineEquipmentStatus(EventEquipment eq) { String _determineEquipmentStatus(EventEquipment eq) {
if (_isNotTakenToEventAtReturn(eq)) {
return 'NOT_TAKEN';
}
// Vérifier d'abord si l'équipement est perdu (LOST) // Vérifier d'abord si l'équipement est perdu (LOST)
if (_shouldMarkAsLost(eq)) { if (_shouldMarkAsLost(eq)) {
return 'LOST'; return 'LOST';
@@ -1118,14 +1123,31 @@ class _EventPreparationPageState extends State<EventPreparationPage> with Single
/// Vérifie si un équipement doit être marqué comme LOST /// Vérifie si un équipement doit être marqué comme LOST
bool _shouldMarkAsLost(EventEquipment eq) { bool _shouldMarkAsLost(EventEquipment eq) {
// Seulement aux étapes de retour return EventPreparationService.shouldMarkEquipmentAsLost(
if (_currentStep != PreparationStep.return_ && isReturnValidationStep: _isReturnValidationStep,
!(_currentStep == PreparationStep.unloadingReturn && _loadSimultaneously)) { isMissingAtReturn: eq.isMissingAtReturn,
isLoaded: eq.isLoaded,
isMissingAtLoading: eq.isMissingAtLoading,
quantityAtLoading: eq.quantityAtLoading,
);
}
bool _isNotTakenToEventAtReturn(EventEquipment eq) {
if (!_isReturnValidationStep) {
return false; return false;
} }
// Si manquant maintenant mais PAS manquant à la préparation = LOST return EventPreparationService.isEquipmentNotTakenToEvent(
return eq.isMissingAtReturn && !eq.isMissingAtPreparation; isMissingAtReturn: eq.isMissingAtReturn,
isLoaded: eq.isLoaded,
isMissingAtLoading: eq.isMissingAtLoading,
quantityAtLoading: eq.quantityAtLoading,
);
}
bool get _isReturnValidationStep {
return _currentStep == PreparationStep.return_ ||
(_currentStep == PreparationStep.unloadingReturn && _loadSimultaneously);
} }
/// Vérifie si un équipement est manquant à l'étape actuelle /// Vérifie si un équipement est manquant à l'étape actuelle
+3 -3
View File
@@ -1,7 +1,7 @@
{ {
"version": "1.1.19", "version": "1.1.20",
"updateUrl": "https://app.em2events.fr", "updateUrl": "https://app.em2events.fr",
"forceUpdate": true, "forceUpdate": true,
"releaseNotes": "Fix BUG : Problème de cache avec les équipements non affichés dans le dialog de sélection d'équipement. Amélioration de la gestion du cache pour éviter les problèmes d'affichage.", "releaseNotes": "Patch bug envoi d'alerte equipement perdu, date dans les alertes, recherche par ID d'équipement.",
"timestamp": "2026-03-24T11:14:01.828Z" "timestamp": "2026-03-30T15:04:34.073Z"
} }