feat: Version 3.5.2 - Configuration Stripe et gestion des immeubles

- Configuration complète Stripe pour les 3 environnements (DEV/REC/PROD)
  * DEV: Clés TEST Pierre (mode test)
  * REC: Clés TEST Client (mode test)
  * PROD: Clés LIVE Client (mode live)
- Ajout de la gestion des bases de données immeubles/bâtiments
  * Configuration buildings_database pour DEV/REC/PROD
  * Service BuildingService pour enrichissement des adresses
- Optimisations pages et améliorations ergonomie
- Mises à jour des dépendances Composer
- Nettoyage des fichiers obsolètes

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
pierre
2025-11-09 18:26:27 +01:00
parent 21657a3820
commit 2f5946a184
812 changed files with 142105 additions and 25992 deletions

View File

@@ -145,8 +145,8 @@ class ApiService {
// Détermine l'environnement actuel (DEV, REC, PROD) en fonction de l'URL
String _determineEnvironment() {
if (!kIsWeb) {
// En mode non-web, utiliser l'environnement de développement par défaut
return 'DEV';
// En mode non-web (iOS/Android), utiliser l'environnement de PRODUCTION par défaut
return 'PROD';
}
final currentUrl = html.window.location.href.toLowerCase();
@@ -196,6 +196,12 @@ class ApiService {
return _baseUrl;
}
// Obtenir l'URL frontend (sans /api) - utile pour les redirections Stripe
String getFrontendUrl() {
// Retirer le /api de l'URL pour obtenir l'URL frontend
return _baseUrl.replaceAll('/api', '');
}
// Obtenir l'identifiant d'application actuel (utile pour le débogage)
String getCurrentAppIdentifier() {
return _appIdentifier;
@@ -208,8 +214,8 @@ class ApiService {
return _connectivityService!.isConnected;
}
// Fallback sur la vérification directe
final connectivityResult = await (Connectivity().checkConnectivity());
return connectivityResult != ConnectivityResult.none;
final connectivityResults = await (Connectivity().checkConnectivity());
return !connectivityResults.contains(ConnectivityResult.none);
}
// Met une requête en file d'attente pour envoi ultérieur
@@ -385,7 +391,8 @@ class ApiService {
// Limiter le nombre de tentatives
if (request.retryCount >= 5) {
debugPrint('⚠️ Nombre maximum de tentatives atteint (5) - Passage à la requête suivante');
debugPrint('⚠️ Nombre maximum de tentatives atteint (5) - Suppression de la requête');
await box.delete(request.key);
continue;
}
@@ -590,7 +597,7 @@ class ApiService {
return await _dio.post(path, data: requestData);
} on DioException catch (e) {
// Si erreur réseau, mettre en file d'attente
if (e.type == DioExceptionType.connectionTimeout ||
if (e.type == DioExceptionType.connectionTimeout ||
e.type == DioExceptionType.connectionError ||
e.type == DioExceptionType.unknown) {
await _queueRequest(
@@ -629,12 +636,12 @@ class ApiService {
data: {'queued': true},
);
}
try {
return await _dio.get(path, queryParameters: queryParameters);
} on DioException catch (e) {
// Si erreur réseau, mettre en file d'attente
if (e.type == DioExceptionType.connectionTimeout ||
if (e.type == DioExceptionType.connectionTimeout ||
e.type == DioExceptionType.connectionError ||
e.type == DioExceptionType.unknown) {
await _queueRequest(
@@ -655,6 +662,19 @@ class ApiService {
}
}
// Méthode GET sans queue (pour les refresh de session)
// Ne met JAMAIS la requête en file d'attente, échoue directement
Future<Response> getWithoutQueue(String path, {Map<String, dynamic>? queryParameters}) async {
try {
return await _dio.get(path, queryParameters: queryParameters);
} on DioException catch (e) {
throw ApiException.fromDioException(e);
} catch (e) {
if (e is ApiException) rethrow;
throw ApiException('Erreur inattendue lors de la requête GET', originalError: e);
}
}
// Méthode PUT générique
Future<Response> put(String path, {dynamic data, String? tempId}) async {
// Vérifier la connectivité
@@ -684,7 +704,7 @@ class ApiService {
return await _dio.put(path, data: requestData);
} on DioException catch (e) {
// Si erreur réseau, mettre en file d'attente
if (e.type == DioExceptionType.connectionTimeout ||
if (e.type == DioExceptionType.connectionTimeout ||
e.type == DioExceptionType.connectionError ||
e.type == DioExceptionType.unknown) {
await _queueRequest(
@@ -728,7 +748,7 @@ class ApiService {
return await _dio.delete(path);
} on DioException catch (e) {
// Si erreur réseau, mettre en file d'attente
if (e.type == DioExceptionType.connectionTimeout ||
if (e.type == DioExceptionType.connectionTimeout ||
e.type == DioExceptionType.connectionError ||
e.type == DioExceptionType.unknown) {
await _queueRequest(
@@ -1068,70 +1088,6 @@ class ApiService {
}
}
// === MÉTHODES DE REFRESH DE SESSION ===
/// Rafraîchit toutes les données de session (pour F5, démarrage)
/// Retourne les mêmes données qu'un login normal
Future<Response> refreshSessionAll() async {
try {
debugPrint('🔄 Refresh complet de session');
// Vérifier qu'on a bien un token/session
if (_sessionId == null) {
throw ApiException('Pas de session active pour le refresh');
}
final response = await post('/session/refresh/all');
// Traiter la réponse comme un login
final data = response.data as Map<String, dynamic>?;
if (data != null && data['status'] == 'success') {
// Si nouveau session_id dans la réponse, le mettre à jour
if (data.containsKey('session_id')) {
final newSessionId = data['session_id'];
if (newSessionId != null) {
setSessionId(newSessionId);
}
}
// Collecter et envoyer les informations du device après refresh réussi
debugPrint('📱 Collecte des informations device après refresh de session...');
DeviceInfoService.instance.collectAndSendDeviceInfo().then((_) {
debugPrint('✅ Informations device collectées et envoyées (refresh)');
}).catchError((error) {
debugPrint('⚠️ Erreur lors de l\'envoi des infos device (refresh): $error');
// Ne pas bloquer le refresh si l'envoi des infos device échoue
});
}
return response;
} catch (e) {
debugPrint('❌ Erreur refresh complet: $e');
rethrow;
}
}
/// Rafraîchit partiellement les données modifiées depuis lastSync
/// Ne retourne que les données modifiées (delta)
Future<Response> refreshSessionPartial(DateTime lastSync) async {
try {
debugPrint('🔄 Refresh partiel depuis: ${lastSync.toIso8601String()}');
// Vérifier qu'on a bien un token/session
if (_sessionId == null) {
throw ApiException('Pas de session active pour le refresh');
}
final response = await post('/session/refresh/partial', data: {
'last_sync': lastSync.toIso8601String(),
});
return response;
} catch (e) {
debugPrint('❌ Erreur refresh partiel: $e');
rethrow;
}
}
// Déconnexion
Future<void> logout() async {