feat: implement comprehensive Firebase Functions backend for equipment management and migrate core repository services
This commit is contained in:
@@ -0,0 +1,350 @@
|
||||
import 'package:em2rp/services/api_service.dart';
|
||||
import 'package:em2rp/utils/debug_log.dart';
|
||||
|
||||
/// Repository pour gérer toutes les opérations sur les équipements.
|
||||
class EquipmentRepository {
|
||||
final ApiService _apiService;
|
||||
|
||||
EquipmentRepository(this._apiService);
|
||||
|
||||
/// Récupère tous les équipements (avec masquage des prix selon permissions)
|
||||
Future<List<Map<String, dynamic>>> getEquipments() async {
|
||||
try {
|
||||
print('[EquipmentRepository] Calling getEquipments API...');
|
||||
final result = await _apiService.call('getEquipments', {});
|
||||
print('[EquipmentRepository] API call successful, parsing result...');
|
||||
final equipments = result['equipments'] as List<dynamic>?;
|
||||
if (equipments == null) {
|
||||
print('[EquipmentRepository] No equipments in result');
|
||||
return [];
|
||||
}
|
||||
print('[EquipmentRepository] Found ${equipments.length} equipments');
|
||||
return equipments.map((e) => e as Map<String, dynamic>).toList();
|
||||
} catch (e) {
|
||||
print('[EquipmentRepository] Error getting equipments: $e');
|
||||
throw Exception('Erreur lors de la récupération des équipements: $e');
|
||||
}
|
||||
}
|
||||
|
||||
/// Récupère plusieurs équipements par leurs IDs
|
||||
Future<List<Map<String, dynamic>>> getEquipmentsByIds(
|
||||
List<String> equipmentIds) async {
|
||||
try {
|
||||
if (equipmentIds.isEmpty) return [];
|
||||
|
||||
print(
|
||||
'[EquipmentRepository] Getting equipments by IDs: ${equipmentIds.length} items');
|
||||
final result = await _apiService.call('getEquipmentsByIds', {
|
||||
'equipmentIds': equipmentIds,
|
||||
});
|
||||
final equipments = result['equipments'] as List<dynamic>?;
|
||||
if (equipments == null) {
|
||||
print('[EquipmentRepository] No equipments in result');
|
||||
return [];
|
||||
}
|
||||
print('[EquipmentRepository] Found ${equipments.length} equipments by IDs');
|
||||
return equipments.map((e) => e as Map<String, dynamic>).toList();
|
||||
} catch (e) {
|
||||
print('[EquipmentRepository] Error getting equipments by IDs: $e');
|
||||
throw Exception('Erreur lors de la récupération des équipements: $e');
|
||||
}
|
||||
}
|
||||
|
||||
/// Récupère les équipements avec pagination et filtrage
|
||||
Future<Map<String, dynamic>> getEquipmentsPaginated({
|
||||
int limit = 20,
|
||||
String? startAfter,
|
||||
String? category,
|
||||
String? status,
|
||||
String? searchQuery,
|
||||
String sortBy = 'id',
|
||||
String sortOrder = 'asc',
|
||||
}) async {
|
||||
try {
|
||||
final params = <String, dynamic>{
|
||||
'limit': limit,
|
||||
'sortBy': sortBy,
|
||||
'sortOrder': sortOrder,
|
||||
};
|
||||
|
||||
if (startAfter != null) params['startAfter'] = startAfter;
|
||||
if (category != null) params['category'] = category;
|
||||
if (status != null) params['status'] = status;
|
||||
if (searchQuery != null && searchQuery.isNotEmpty) {
|
||||
params['searchQuery'] = searchQuery;
|
||||
}
|
||||
|
||||
final result =
|
||||
await (_apiService as FirebaseFunctionsApiService).callPaginated(
|
||||
'getEquipmentsPaginated',
|
||||
params,
|
||||
);
|
||||
|
||||
return {
|
||||
'equipments': (result['equipments'] as List<dynamic>?)
|
||||
?.map((e) => e as Map<String, dynamic>)
|
||||
.toList() ??
|
||||
[],
|
||||
'hasMore': result['hasMore'] as bool? ?? false,
|
||||
'lastVisible': result['lastVisible'] as String?,
|
||||
'total': result['total'] as int? ?? 0,
|
||||
};
|
||||
} catch (e) {
|
||||
DebugLog.error('[EquipmentRepository] Error in getEquipmentsPaginated', e);
|
||||
throw Exception(
|
||||
'Erreur lors de la récupération paginée des équipements: $e');
|
||||
}
|
||||
}
|
||||
|
||||
/// Crée un équipement
|
||||
Future<void> createEquipment(
|
||||
String equipmentId, Map<String, dynamic> data) async {
|
||||
try {
|
||||
final equipmentData = Map<String, dynamic>.from(data);
|
||||
equipmentData['id'] = equipmentId;
|
||||
|
||||
await _apiService.call('createEquipment', equipmentData);
|
||||
} catch (e) {
|
||||
throw Exception('Erreur lors de la création de l\'équipement: $e');
|
||||
}
|
||||
}
|
||||
|
||||
/// Met à jour un équipement
|
||||
Future<void> updateEquipment(
|
||||
String equipmentId, Map<String, dynamic> data) async {
|
||||
try {
|
||||
await _apiService.call('updateEquipment', {
|
||||
'equipmentId': equipmentId,
|
||||
'data': data,
|
||||
});
|
||||
} catch (e) {
|
||||
throw Exception('Erreur lors de la mise à jour de l\'équipement: $e');
|
||||
}
|
||||
}
|
||||
|
||||
/// Supprime un équipement
|
||||
Future<void> deleteEquipment(String equipmentId,
|
||||
{bool forceDelete = false}) async {
|
||||
try {
|
||||
await _apiService.call('deleteEquipment', {
|
||||
'equipmentId': equipmentId,
|
||||
'forceDelete': forceDelete,
|
||||
});
|
||||
} on ApiException {
|
||||
rethrow;
|
||||
} catch (e) {
|
||||
throw Exception('Erreur lors de la suppression de l\'équipement: $e');
|
||||
}
|
||||
}
|
||||
|
||||
/// Met à jour uniquement le statut d'un équipement
|
||||
Future<void> updateEquipmentStatusOnly({
|
||||
required String equipmentId,
|
||||
String? status,
|
||||
int? availableQuantity,
|
||||
}) async {
|
||||
try {
|
||||
final data = <String, dynamic>{'equipmentId': equipmentId};
|
||||
|
||||
if (status != null) data['status'] = status;
|
||||
if (availableQuantity != null) {
|
||||
data['availableQuantity'] = availableQuantity;
|
||||
}
|
||||
|
||||
await _apiService.call('updateEquipmentStatusOnly', data);
|
||||
} catch (e) {
|
||||
throw Exception(
|
||||
'Erreur lors de la mise à jour du statut de l\'équipement: $e');
|
||||
}
|
||||
}
|
||||
|
||||
/// Recherche rapide (autocomplétion)
|
||||
Future<List<Map<String, dynamic>>> quickSearch(
|
||||
String query, {
|
||||
int limit = 10,
|
||||
bool includeEquipments = true,
|
||||
bool includeContainers = true,
|
||||
}) async {
|
||||
try {
|
||||
return await (_apiService as FirebaseFunctionsApiService).quickSearch(
|
||||
query,
|
||||
limit: limit,
|
||||
includeEquipments: includeEquipments,
|
||||
includeContainers: includeContainers,
|
||||
);
|
||||
} catch (e) {
|
||||
DebugLog.error('[EquipmentRepository] Error in quickSearch', e);
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
/// Recherche des équipements pour l'assistant IA avec fallback paginé.
|
||||
Future<List<Map<String, dynamic>>> searchEquipmentsForAssistant({
|
||||
required String query,
|
||||
int limit = 12,
|
||||
}) async {
|
||||
final normalizedQuery = query.trim();
|
||||
if (normalizedQuery.isEmpty) {
|
||||
return [];
|
||||
}
|
||||
|
||||
try {
|
||||
final quickResults = await quickSearch(
|
||||
normalizedQuery,
|
||||
limit: limit,
|
||||
includeEquipments: true,
|
||||
includeContainers: false,
|
||||
);
|
||||
|
||||
final equipmentResults = quickResults
|
||||
.where((item) =>
|
||||
(item['type']?.toString().toLowerCase() ?? '') == 'equipment')
|
||||
.map(_normalizeAssistantEquipment)
|
||||
.toList();
|
||||
|
||||
if (equipmentResults.isNotEmpty) {
|
||||
return equipmentResults;
|
||||
}
|
||||
|
||||
final paginated = await getEquipmentsPaginated(
|
||||
limit: limit,
|
||||
searchQuery: normalizedQuery,
|
||||
sortBy: 'id',
|
||||
sortOrder: 'asc',
|
||||
);
|
||||
|
||||
final equipments =
|
||||
paginated['equipments'] as List<Map<String, dynamic>>? ?? [];
|
||||
return equipments.map(_normalizeAssistantEquipment).toList();
|
||||
} catch (e) {
|
||||
DebugLog.error('[EquipmentRepository] Error in searchEquipmentsForAssistant', e);
|
||||
throw Exception('Erreur lors de la recherche de matériel: $e');
|
||||
}
|
||||
}
|
||||
|
||||
/// Vérifie la disponibilité d'un équipement dans un format normalisé pour l'IA.
|
||||
Future<Map<String, dynamic>> checkEquipmentAvailabilityForAssistant({
|
||||
required String equipmentId,
|
||||
required DateTime startDate,
|
||||
required DateTime endDate,
|
||||
String? excludeEventId,
|
||||
}) async {
|
||||
try {
|
||||
final result = await checkEquipmentAvailability(
|
||||
equipmentId: equipmentId,
|
||||
startDate: startDate,
|
||||
endDate: endDate,
|
||||
excludeEventId: excludeEventId,
|
||||
);
|
||||
|
||||
final available = result['available'] as bool? ?? true;
|
||||
final conflicts = (result['conflicts'] as List<dynamic>? ?? const [])
|
||||
.whereType<Map<String, dynamic>>()
|
||||
.map((conflict) {
|
||||
final eventData =
|
||||
conflict['eventData'] as Map<String, dynamic>? ?? const {};
|
||||
final eventName =
|
||||
(eventData['Name'] ?? conflict['eventName'] ?? '').toString();
|
||||
return {
|
||||
'eventId': conflict['eventId']?.toString() ?? '',
|
||||
'eventName': eventName,
|
||||
'overlapDays': conflict['overlapDays'] as int? ?? 0,
|
||||
};
|
||||
}).toList();
|
||||
|
||||
return {
|
||||
'equipmentId': equipmentId,
|
||||
'available': available,
|
||||
'conflictCount': conflicts.length,
|
||||
'conflicts': conflicts,
|
||||
};
|
||||
} catch (e) {
|
||||
DebugLog.error(
|
||||
'[EquipmentRepository] Error in checkEquipmentAvailabilityForAssistant', e);
|
||||
throw Exception('Erreur lors de la vérification de disponibilité: $e');
|
||||
}
|
||||
}
|
||||
|
||||
/// Vérifie la disponibilité d'un équipement
|
||||
Future<Map<String, dynamic>> checkEquipmentAvailability({
|
||||
required String equipmentId,
|
||||
required DateTime startDate,
|
||||
required DateTime endDate,
|
||||
String? excludeEventId,
|
||||
}) async {
|
||||
try {
|
||||
final result = await _apiService.call('checkEquipmentAvailability', {
|
||||
'equipmentId': equipmentId,
|
||||
'startDate': startDate.toIso8601String(),
|
||||
'endDate': endDate.toIso8601String(),
|
||||
if (excludeEventId != null) 'excludeEventId': excludeEventId,
|
||||
});
|
||||
return result;
|
||||
} catch (e) {
|
||||
throw Exception('Erreur lors de la vérification de disponibilité: $e');
|
||||
}
|
||||
}
|
||||
|
||||
/// Récupère tous les IDs d'équipements et conteneurs en conflit pour une période
|
||||
/// Optimisé : une seule requête au lieu d'une par équipement
|
||||
Future<Map<String, dynamic>> getConflictingEquipmentIds({
|
||||
required DateTime startDate,
|
||||
required DateTime endDate,
|
||||
String? excludeEventId,
|
||||
int installationTime = 0,
|
||||
int disassemblyTime = 0,
|
||||
}) async {
|
||||
try {
|
||||
final result = await _apiService.call('getConflictingEquipmentIds', {
|
||||
'startDate': startDate.toIso8601String(),
|
||||
'endDate': endDate.toIso8601String(),
|
||||
if (excludeEventId != null) 'excludeEventId': excludeEventId,
|
||||
'installationTime': installationTime,
|
||||
'disassemblyTime': disassemblyTime,
|
||||
});
|
||||
return result;
|
||||
} catch (e) {
|
||||
throw Exception(
|
||||
'Erreur lors de la récupération des équipements en conflit: $e');
|
||||
}
|
||||
}
|
||||
|
||||
Map<String, dynamic> _normalizeAssistantEquipment(Map<String, dynamic> item) {
|
||||
return {
|
||||
'id': (item['id'] ?? '').toString(),
|
||||
'name': (item['name'] ?? item['id'] ?? '').toString(),
|
||||
'category': (item['category'] ?? '').toString(),
|
||||
'status': (item['status'] ?? '').toString(),
|
||||
'brand': item['brand']?.toString(),
|
||||
'model': item['model']?.toString(),
|
||||
'availableQuantity': item['availableQuantity'],
|
||||
'totalQuantity': item['totalQuantity'],
|
||||
};
|
||||
}
|
||||
|
||||
/// Récupère toutes les maintenances
|
||||
Future<List<Map<String, dynamic>>> getMaintenances(
|
||||
{String? equipmentId}) async {
|
||||
try {
|
||||
final data = <String, dynamic>{};
|
||||
if (equipmentId != null) data['equipmentId'] = equipmentId;
|
||||
|
||||
final result = await _apiService.call('getMaintenances', data);
|
||||
final maintenances = result['maintenances'] as List<dynamic>?;
|
||||
if (maintenances == null) return [];
|
||||
return maintenances.map((e) => e as Map<String, dynamic>).toList();
|
||||
} catch (e) {
|
||||
throw Exception('Erreur lors de la récupération des maintenances: $e');
|
||||
}
|
||||
}
|
||||
|
||||
/// Supprime une maintenance
|
||||
Future<void> deleteMaintenance(String maintenanceId) async {
|
||||
try {
|
||||
await _apiService
|
||||
.call('deleteMaintenance', {'maintenanceId': maintenanceId});
|
||||
} catch (e) {
|
||||
throw Exception('Erreur lors de la suppression de la maintenance: $e');
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user