Livraison d ela gestion des opérations v0.4.0
This commit is contained in:
@@ -2,7 +2,11 @@ import 'dart:async';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:hive/hive.dart';
|
||||
import 'package:geosector_app/core/data/models/operation_model.dart';
|
||||
import 'package:geosector_app/core/data/models/passage_model.dart';
|
||||
import 'package:geosector_app/core/data/models/sector_model.dart';
|
||||
import 'package:geosector_app/core/data/models/user_sector_model.dart';
|
||||
import 'package:geosector_app/core/services/api_service.dart';
|
||||
import 'package:geosector_app/core/services/data_loading_service.dart';
|
||||
import 'package:geosector_app/core/constants/app_keys.dart';
|
||||
|
||||
class OperationRepository extends ChangeNotifier {
|
||||
@@ -13,6 +17,9 @@ class OperationRepository extends ChangeNotifier {
|
||||
return Hive.box<OperationModel>(AppKeys.operationsBoxName);
|
||||
}
|
||||
|
||||
// Getter exposant publiquement la Hive Box
|
||||
Box<OperationModel> get operationBox => _operationBox;
|
||||
|
||||
// Méthode pour vérifier si la boîte est ouverte et l'ouvrir si nécessaire
|
||||
Future<void> _ensureBoxIsOpen() async {
|
||||
const boxName = AppKeys.operationsBoxName;
|
||||
@@ -97,10 +104,14 @@ class OperationRepository extends ChangeNotifier {
|
||||
notifyListeners();
|
||||
|
||||
try {
|
||||
debugPrint('🔄 Traitement de ${operationsData.length} opérations depuis l\'API');
|
||||
|
||||
for (var operationData in operationsData) {
|
||||
final operationJson = operationData as Map<String, dynamic>;
|
||||
final operationId = operationJson['id'] is String ? int.parse(operationJson['id']) : operationJson['id'] as int;
|
||||
|
||||
debugPrint('📝 Traitement opération ID: $operationId, libelle: ${operationJson['libelle']}');
|
||||
|
||||
// Vérifier si l'opération existe déjà
|
||||
OperationModel? existingOperation = getOperationById(operationId);
|
||||
|
||||
@@ -108,20 +119,27 @@ class OperationRepository extends ChangeNotifier {
|
||||
// Créer une nouvelle opération
|
||||
final newOperation = OperationModel.fromJson(operationJson);
|
||||
await saveOperation(newOperation);
|
||||
debugPrint('✅ Nouvelle opération créée: ${newOperation.name}');
|
||||
} else {
|
||||
// Mettre à jour l'opération existante
|
||||
final updatedOperation = existingOperation.copyWith(
|
||||
name: operationJson['name'],
|
||||
name: operationJson['libelle'], // ← Correction: utiliser 'libelle' au lieu de 'name'
|
||||
fkEntite: operationJson['fk_entite'],
|
||||
dateDebut: DateTime.parse(operationJson['date_deb']),
|
||||
dateFin: DateTime.parse(operationJson['date_fin']),
|
||||
isActive: operationJson['chk_active'] == true || operationJson['chk_active'] == 1 || operationJson['chk_active'] == "1",
|
||||
lastSyncedAt: DateTime.now(),
|
||||
isSynced: true,
|
||||
);
|
||||
await saveOperation(updatedOperation);
|
||||
debugPrint('✅ Opération mise à jour: ${updatedOperation.name}');
|
||||
}
|
||||
}
|
||||
|
||||
debugPrint('🎉 Traitement terminé - ${_operationBox.length} opérations dans la box');
|
||||
} catch (e) {
|
||||
debugPrint('Erreur lors du traitement des opérations: $e');
|
||||
debugPrint('❌ Erreur lors du traitement des opérations: $e');
|
||||
debugPrint('❌ Stack trace: ${StackTrace.current}');
|
||||
} finally {
|
||||
_isLoading = false;
|
||||
notifyListeners();
|
||||
@@ -141,40 +159,116 @@ class OperationRepository extends ChangeNotifier {
|
||||
'date_fin': dateFin.toIso8601String().split('T')[0], // Format YYYY-MM-DD
|
||||
};
|
||||
|
||||
debugPrint('🚀 Création d\'une nouvelle opération: $data');
|
||||
|
||||
// Appeler l'API pour créer l'opération
|
||||
final response = await ApiService.instance.post('/operations', data: data);
|
||||
|
||||
if (response.statusCode == 201 || response.statusCode == 200) {
|
||||
// Récupérer l'ID de la nouvelle opération
|
||||
final operationId = response.data['id'] is String ? int.parse(response.data['id']) : response.data['id'] as int;
|
||||
debugPrint('✅ Opération créée avec succès');
|
||||
|
||||
// Créer l'opération localement
|
||||
final newOperation = OperationModel(
|
||||
id: operationId,
|
||||
name: name,
|
||||
dateDebut: dateDebut,
|
||||
dateFin: dateFin,
|
||||
lastSyncedAt: DateTime.now(),
|
||||
isActive: true,
|
||||
isSynced: true,
|
||||
);
|
||||
|
||||
await saveOperation(newOperation);
|
||||
// Traiter la réponse complète de l'API
|
||||
await _processCreationResponse(response.data);
|
||||
return true;
|
||||
}
|
||||
|
||||
debugPrint('❌ Échec de la création - Code: ${response.statusCode}');
|
||||
return false;
|
||||
} catch (e) {
|
||||
debugPrint('Erreur lors de la création de l\'opération: $e');
|
||||
return false;
|
||||
debugPrint('❌ Erreur lors de la création de l\'opération: $e');
|
||||
rethrow;
|
||||
} finally {
|
||||
_isLoading = false;
|
||||
notifyListeners();
|
||||
}
|
||||
}
|
||||
|
||||
// Traiter la réponse complète après création d'opération
|
||||
Future<void> _processCreationResponse(Map<String, dynamic> responseData) async {
|
||||
try {
|
||||
debugPrint('🔄 Traitement de la réponse de création d\'opération');
|
||||
|
||||
// Traiter les opérations (groupe operations)
|
||||
if (responseData['operations'] != null) {
|
||||
await processOperationsFromApi(responseData['operations']);
|
||||
debugPrint('✅ Opérations traitées');
|
||||
}
|
||||
|
||||
// Traiter les secteurs (groupe secteurs) via DataLoadingService
|
||||
if (responseData['secteurs'] != null) {
|
||||
await DataLoadingService.instance.processSectorsFromApi(responseData['secteurs']);
|
||||
debugPrint('✅ Secteurs traités');
|
||||
}
|
||||
|
||||
// Traiter les passages (groupe passages) via DataLoadingService
|
||||
if (responseData['passages'] != null) {
|
||||
await DataLoadingService.instance.processPassagesFromApi(responseData['passages']);
|
||||
debugPrint('✅ Passages traités');
|
||||
}
|
||||
|
||||
// Traiter les users_sectors (groupe users_sectors) via DataLoadingService
|
||||
if (responseData['users_sectors'] != null) {
|
||||
await DataLoadingService.instance.processUserSectorsFromApi(responseData['users_sectors']);
|
||||
debugPrint('✅ Users_sectors traités');
|
||||
}
|
||||
|
||||
debugPrint('🎉 Tous les groupes de données ont été traités avec succès');
|
||||
} catch (e) {
|
||||
debugPrint('❌ Erreur lors du traitement de la réponse: $e');
|
||||
// Ne pas faire échouer la création si le traitement des données supplémentaires échoue
|
||||
}
|
||||
}
|
||||
|
||||
// Méthode universelle pour sauvegarder une opération (création ou mise à jour)
|
||||
Future<bool> saveOperationFromModel(OperationModel operation) async {
|
||||
debugPrint('=== saveOperationFromModel APPELÉ ===');
|
||||
debugPrint('operation.id: ${operation.id}');
|
||||
debugPrint('operation.name: ${operation.name}');
|
||||
|
||||
try {
|
||||
if (operation.id == 0) {
|
||||
// Nouvelle opération - créer
|
||||
debugPrint('=== CRÉATION (POST) ===');
|
||||
return await createOperation(
|
||||
operation.name,
|
||||
operation.dateDebut,
|
||||
operation.dateFin,
|
||||
);
|
||||
} else {
|
||||
// Opération existante - mettre à jour
|
||||
debugPrint('=== MISE À JOUR (PUT) ===');
|
||||
final result = await updateOperation(
|
||||
operation.id,
|
||||
name: operation.name,
|
||||
dateDebut: operation.dateDebut,
|
||||
dateFin: operation.dateFin,
|
||||
isActive: operation.isActive,
|
||||
fkEntite: operation.fkEntite, // ← Inclure fkEntite
|
||||
);
|
||||
debugPrint('=== RÉSULTAT UPDATE: $result ===');
|
||||
return result;
|
||||
}
|
||||
} catch (e) {
|
||||
debugPrint('=== ERREUR dans saveOperationFromModel: $e ===');
|
||||
// Propager l'exception pour que la page parente puisse la gérer
|
||||
rethrow;
|
||||
}
|
||||
}
|
||||
|
||||
// Méthode pour mettre à jour une opération avec un objet OperationModel
|
||||
Future<bool> updateOperationFromModel(OperationModel operation) async {
|
||||
return await updateOperation(
|
||||
operation.id,
|
||||
name: operation.name,
|
||||
dateDebut: operation.dateDebut,
|
||||
dateFin: operation.dateFin,
|
||||
isActive: operation.isActive,
|
||||
fkEntite: operation.fkEntite, // ← Inclure fkEntite
|
||||
);
|
||||
}
|
||||
|
||||
// Mettre à jour une opération
|
||||
Future<bool> updateOperation(int id, {String? name, DateTime? dateDebut, DateTime? dateFin, bool? isActive}) async {
|
||||
Future<bool> updateOperation(int id, {String? name, DateTime? dateDebut, DateTime? dateFin, bool? isActive, int? fkEntite}) async {
|
||||
_isLoading = true;
|
||||
notifyListeners();
|
||||
|
||||
@@ -182,7 +276,8 @@ class OperationRepository extends ChangeNotifier {
|
||||
// Récupérer l'opération existante
|
||||
final existingOperation = getOperationById(id);
|
||||
if (existingOperation == null) {
|
||||
return false;
|
||||
debugPrint('❌ Opération avec l\'ID $id non trouvée');
|
||||
throw Exception('Opération non trouvée');
|
||||
}
|
||||
|
||||
// Préparer les données pour l'API
|
||||
@@ -191,59 +286,203 @@ class OperationRepository extends ChangeNotifier {
|
||||
'name': name ?? existingOperation.name,
|
||||
'date_deb': (dateDebut ?? existingOperation.dateDebut).toIso8601String().split('T')[0],
|
||||
'date_fin': (dateFin ?? existingOperation.dateFin).toIso8601String().split('T')[0],
|
||||
'is_active': isActive ?? existingOperation.isActive,
|
||||
'chk_active': isActive ?? existingOperation.isActive, // Utiliser chk_active comme dans l'API
|
||||
'fk_entite': fkEntite ?? existingOperation.fkEntite, // ← Inclure fkEntite
|
||||
};
|
||||
|
||||
debugPrint('🔄 Mise à jour de l\'opération $id avec les données: $data');
|
||||
// Appeler l'API pour mettre à jour l'opération
|
||||
final response = await ApiService.instance.put('/operations/$id', data: data);
|
||||
|
||||
if (response.statusCode == 200) {
|
||||
debugPrint('✅ Opération $id mise à jour avec succès');
|
||||
// Mettre à jour l'opération localement
|
||||
final updatedOperation = existingOperation.copyWith(
|
||||
name: name,
|
||||
dateDebut: dateDebut,
|
||||
dateFin: dateFin,
|
||||
isActive: isActive,
|
||||
fkEntite: fkEntite,
|
||||
lastSyncedAt: DateTime.now(),
|
||||
isSynced: true,
|
||||
);
|
||||
|
||||
await saveOperation(updatedOperation);
|
||||
return true;
|
||||
} else {
|
||||
debugPrint('❌ Échec de la mise à jour - Code: ${response.statusCode}');
|
||||
throw Exception('Échec de la mise à jour de l\'opération');
|
||||
}
|
||||
|
||||
return false;
|
||||
} catch (e) {
|
||||
debugPrint('Erreur lors de la mise à jour de l\'opération: $e');
|
||||
return false;
|
||||
debugPrint('❌ Erreur lors de la mise à jour de l\'opération: $e');
|
||||
// Propager l'exception pour qu'elle soit gérée par l'interface
|
||||
rethrow;
|
||||
} finally {
|
||||
_isLoading = false;
|
||||
notifyListeners();
|
||||
}
|
||||
}
|
||||
|
||||
// Supprimer une opération via l'API
|
||||
// Supprimer une opération inactive via l'API
|
||||
Future<bool> deleteOperationViaApi(int id) async {
|
||||
_isLoading = true;
|
||||
notifyListeners();
|
||||
|
||||
try {
|
||||
// Appeler l'API pour supprimer l'opération
|
||||
debugPrint('🗑️ Suppression opération inactive $id');
|
||||
|
||||
// Appeler l'API pour supprimer l'opération inactive
|
||||
final response = await ApiService.instance.delete('/operations/$id');
|
||||
|
||||
if (response.statusCode == 200 || response.statusCode == 204) {
|
||||
// Supprimer l'opération localement
|
||||
await deleteOperation(id);
|
||||
debugPrint('✅ Suppression réussie - Traitement de la réponse');
|
||||
|
||||
// Traiter la réponse qui contient les 3 dernières opérations
|
||||
if (response.data != null && response.data['operations'] != null) {
|
||||
// Vider la box des opérations
|
||||
await _operationBox.clear();
|
||||
|
||||
// Recharger les opérations depuis la réponse API
|
||||
await processOperationsFromApi(response.data['operations']);
|
||||
debugPrint('✅ Opérations rechargées après suppression');
|
||||
} else {
|
||||
// Fallback : supprimer localement seulement
|
||||
await deleteOperation(id);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
debugPrint('❌ Échec suppression - Code: ${response.statusCode}');
|
||||
return false;
|
||||
} catch (e) {
|
||||
debugPrint('Erreur lors de la suppression de l\'opération: $e');
|
||||
return false;
|
||||
debugPrint('❌ Erreur lors de la suppression de l\'opération: $e');
|
||||
rethrow;
|
||||
} finally {
|
||||
_isLoading = false;
|
||||
notifyListeners();
|
||||
}
|
||||
}
|
||||
|
||||
// Supprimer une opération active via l'API (avec réactivation automatique)
|
||||
Future<bool> deleteActiveOperationViaApi(int id) async {
|
||||
_isLoading = true;
|
||||
notifyListeners();
|
||||
|
||||
try {
|
||||
debugPrint('🗑️ Suppression opération active $id');
|
||||
|
||||
// Appeler l'API pour supprimer l'opération active
|
||||
final response = await ApiService.instance.delete('/operations/$id');
|
||||
|
||||
if (response.statusCode == 200 || response.statusCode == 204) {
|
||||
debugPrint('✅ Suppression opération active réussie - Traitement complet');
|
||||
|
||||
// Traiter la réponse complète qui contient tous les groupes de données
|
||||
if (response.data != null) {
|
||||
await _processActiveDeleteResponse(response.data);
|
||||
debugPrint('✅ Données rechargées après suppression opération active');
|
||||
} else {
|
||||
// Fallback : supprimer localement seulement
|
||||
await deleteOperation(id);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
debugPrint('❌ Échec suppression opération active - Code: ${response.statusCode}');
|
||||
return false;
|
||||
} catch (e) {
|
||||
debugPrint('❌ Erreur lors de la suppression de l\'opération active: $e');
|
||||
rethrow;
|
||||
} finally {
|
||||
_isLoading = false;
|
||||
notifyListeners();
|
||||
}
|
||||
}
|
||||
|
||||
// Traiter la réponse complète après suppression d'opération active
|
||||
Future<void> _processActiveDeleteResponse(Map<String, dynamic> responseData) async {
|
||||
try {
|
||||
debugPrint('🔄 Traitement de la réponse de suppression d\'opération active');
|
||||
|
||||
// Vider toutes les Box concernées
|
||||
await _clearAllRelatedBoxes();
|
||||
|
||||
// Traiter les opérations (groupe operations)
|
||||
if (responseData['operations'] != null) {
|
||||
await processOperationsFromApi(responseData['operations']);
|
||||
debugPrint('✅ Opérations traitées');
|
||||
}
|
||||
|
||||
// Traiter les secteurs (groupe secteurs) via DataLoadingService
|
||||
if (responseData['secteurs'] != null) {
|
||||
await DataLoadingService.instance.processSectorsFromApi(responseData['secteurs']);
|
||||
debugPrint('✅ Secteurs traités');
|
||||
}
|
||||
|
||||
// Traiter les passages (groupe passages) via DataLoadingService
|
||||
if (responseData['passages'] != null) {
|
||||
await DataLoadingService.instance.processPassagesFromApi(responseData['passages']);
|
||||
debugPrint('✅ Passages traités');
|
||||
}
|
||||
|
||||
// Traiter les users_sectors (groupe users_sectors) via DataLoadingService
|
||||
if (responseData['users_sectors'] != null) {
|
||||
await DataLoadingService.instance.processUserSectorsFromApi(responseData['users_sectors']);
|
||||
debugPrint('✅ Users_sectors traités');
|
||||
}
|
||||
|
||||
debugPrint('🎉 Tous les groupes de données ont été traités après suppression opération active');
|
||||
} catch (e) {
|
||||
debugPrint('❌ Erreur lors du traitement de la réponse de suppression: $e');
|
||||
// Ne pas faire échouer la suppression si le traitement des données supplémentaires échoue
|
||||
}
|
||||
}
|
||||
|
||||
// Vider toutes les Box liées lors de suppression d'opération active
|
||||
Future<void> _clearAllRelatedBoxes() async {
|
||||
try {
|
||||
// Vider les Box respectives avant rechargement complet
|
||||
await _operationBox.clear();
|
||||
|
||||
if (Hive.isBoxOpen(AppKeys.sectorsBoxName)) {
|
||||
final sectorsBox = Hive.box<SectorModel>(AppKeys.sectorsBoxName);
|
||||
await sectorsBox.clear();
|
||||
}
|
||||
|
||||
if (Hive.isBoxOpen(AppKeys.passagesBoxName)) {
|
||||
final passagesBox = Hive.box<PassageModel>(AppKeys.passagesBoxName);
|
||||
await passagesBox.clear();
|
||||
}
|
||||
|
||||
if (Hive.isBoxOpen(AppKeys.userSectorBoxName)) {
|
||||
final userSectorsBox = Hive.box<UserSectorModel>(AppKeys.userSectorBoxName);
|
||||
await userSectorsBox.clear();
|
||||
}
|
||||
|
||||
debugPrint('✅ Toutes les Box ont été vidées');
|
||||
} catch (e) {
|
||||
debugPrint('❌ Erreur lors du vidage des Box: $e');
|
||||
}
|
||||
}
|
||||
|
||||
// Export Excel d'une opération
|
||||
Future<void> exportOperationToExcel(int operationId, String operationName) async {
|
||||
try {
|
||||
debugPrint('📊 Export Excel opération $operationId: $operationName');
|
||||
|
||||
// Générer le nom de fichier avec la date actuelle
|
||||
final now = DateTime.now();
|
||||
final dateStr = '${now.year}-${now.month.toString().padLeft(2, '0')}-${now.day.toString().padLeft(2, '0')}';
|
||||
final fileName = 'operation_${operationName.replaceAll(' ', '_')}_$dateStr.xlsx';
|
||||
|
||||
// Appeler l'API pour télécharger le fichier Excel
|
||||
await ApiService.instance.downloadOperationExcel(operationId, fileName);
|
||||
|
||||
debugPrint('✅ Export Excel terminé pour opération $operationId');
|
||||
} catch (e) {
|
||||
debugPrint('❌ Erreur lors de l\'export Excel: $e');
|
||||
rethrow;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@ import 'package:geosector_app/core/services/api_service.dart';
|
||||
import 'package:geosector_app/core/services/current_user_service.dart';
|
||||
import 'package:geosector_app/core/services/current_amicale_service.dart';
|
||||
import 'package:geosector_app/core/services/data_loading_service.dart';
|
||||
import 'package:geosector_app/core/services/hive_service.dart';
|
||||
import 'package:geosector_app/core/services/hive_reset_state_service.dart';
|
||||
import 'package:geosector_app/core/data/models/user_model.dart';
|
||||
import 'package:geosector_app/core/data/models/amicale_model.dart';
|
||||
@@ -280,7 +281,7 @@ class UserRepository extends ChangeNotifier {
|
||||
}
|
||||
}
|
||||
|
||||
/// Déconnexion simplifiée avec DataLoadingService
|
||||
/// Déconnexion simplifiée avec HiveService
|
||||
Future<bool> logout(BuildContext context) async {
|
||||
_isLoading = true;
|
||||
notifyListeners();
|
||||
@@ -301,8 +302,8 @@ class UserRepository extends ChangeNotifier {
|
||||
await CurrentUserService.instance.clearUser();
|
||||
await CurrentAmicaleService.instance.clearAmicale();
|
||||
|
||||
// Nettoyage complet via DataLoadingService
|
||||
await DataLoadingService.instance.cleanDataAfterLogout();
|
||||
// Nettoyage des données via HiveService (préserve les utilisateurs)
|
||||
await HiveService.instance.cleanDataOnLogout();
|
||||
|
||||
// Réinitialiser l'état de HiveResetStateService
|
||||
hiveResetStateService.reset();
|
||||
|
||||
Reference in New Issue
Block a user