From 6b83a1ab5e09bdefc4f8fa23e136b56d89f6a282 Mon Sep 17 00:00:00 2001 From: Stephane Date: Wed, 23 Jul 2025 12:06:32 +0300 Subject: [PATCH] database --- lib/Components/colors.dart | 11 + lib/Services/stock_managementDatabase.dart | 3002 ++++++++++---------- lib/config/DatabaseConfig.dart | 108 +- lib/controller/userController.dart | 72 +- 4 files changed, 1603 insertions(+), 1590 deletions(-) create mode 100644 lib/Components/colors.dart diff --git a/lib/Components/colors.dart b/lib/Components/colors.dart new file mode 100644 index 0000000..e650c50 --- /dev/null +++ b/lib/Components/colors.dart @@ -0,0 +1,11 @@ +import 'package:flutter/material.dart'; + +class AppColors { + static const primaryBlue = + Color(0xFF04365F); // Replace with your exact logo tone + static const secondaryBlue = + Color(0xFF1976D2); // Replace if your logo has a secondary tone + static const neutralGrey = Color(0xFF8A8A8A); + static const accent = + Color(0xFFF7941D); // Example: if your logo has an orange or accent +} diff --git a/lib/Services/stock_managementDatabase.dart b/lib/Services/stock_managementDatabase.dart index 6c4cff0..bbe22cc 100644 --- a/lib/Services/stock_managementDatabase.dart +++ b/lib/Services/stock_managementDatabase.dart @@ -1,8 +1,8 @@ import 'dart:async'; +import 'dart:math' as console; import 'package:get/get.dart'; import 'package:mysql1/mysql1.dart'; import 'package:youmazgestion/controller/userController.dart'; - // Models import '../Models/users.dart'; import '../Models/role.dart'; @@ -35,20 +35,20 @@ class AppDatabase { } Future initDatabase() async { - _connection = await _initDB(); - // await _createDB(); - - - await insertDefaultPermissions(); - await insertDefaultMenus(); - await insertDefaultRoles(); - await insertDefaultSuperAdmin(); - await insertDefaultPointsDeVente(); -} + _connection = await _initDB(); + // await _createDB(); + + await insertDefaultPermissions(); + await insertDefaultMenus(); + await insertDefaultRoles(); + await insertDefaultSuperAdmin(); + await insertDefaultPointsDeVente(); + } Future _initDB() async { try { - final config = DatabaseConfig.getConfig(); + final config = await DatabaseConfig.getSmartConfig(); + // console.log(config as num); final settings = ConnectionSettings( host: config['host'], port: config['port'], @@ -57,7 +57,7 @@ class AppDatabase { db: config['database'], timeout: Duration(seconds: config['timeout']), ); - + final connection = await MySqlConnection.connect(settings); print("Connexion MySQL établie avec succès !"); return connection; @@ -65,60 +65,61 @@ class AppDatabase { print("Erreur de connexion MySQL: $e"); rethrow; } + throw Exception("La connexion MySQL n'a pas pu être établie."); } - - // --- MÉTHODES D'INSERTION PAR DÉFAUT --- - - // + + // Future insertDefaultPermissions() async { - final db = await database; - - try { - // Vérifier et ajouter uniquement les nouvelles permissions si elles n'existent pas - final newPermissions = ['manage', 'read']; - for (var permission in newPermissions) { - final existingPermission = await db.query( - 'SELECT COUNT(*) as count FROM permissions WHERE name = ?', - [permission] - ); - final permCount = existingPermission.first['count'] as int; - if (permCount == 0) { - await db.query('INSERT INTO permissions (name) VALUES (?)', [permission]); - print("Permission ajoutée: $permission"); + final db = await database; + + try { + // Vérifier et ajouter uniquement les nouvelles permissions si elles n'existent pas + final newPermissions = ['manage', 'read']; + for (var permission in newPermissions) { + final existingPermission = await db.query( + 'SELECT COUNT(*) as count FROM permissions WHERE name = ?', + [permission]); + final permCount = existingPermission.first['count'] as int; + if (permCount == 0) { + await db + .query('INSERT INTO permissions (name) VALUES (?)', [permission]); + print("Permission ajoutée: $permission"); + } } + } catch (e) { + print("Erreur insertDefaultPermissions: $e"); } - } catch (e) { - print("Erreur insertDefaultPermissions: $e"); } -} - // + // Future insertDefaultMenus() async { - final db = await database; - - try { - await _addMissingMenus(db); // Seulement ajouter les menus manquants - } catch (e) { - print("Erreur insertDefaultMenus: $e"); + final db = await database; + + try { + await _addMissingMenus(db); // Seulement ajouter les menus manquants + } catch (e) { + print("Erreur insertDefaultMenus: $e"); + } } -} Future insertDefaultRoles() async { final db = await database; - + try { - final existingRoles = await db.query('SELECT COUNT(*) as count FROM roles'); + final existingRoles = + await db.query('SELECT COUNT(*) as count FROM roles'); final count = existingRoles.first['count'] as int; if (count == 0) { // Créer les rôles final roles = ['Super Admin', 'Admin', 'User', 'commercial', 'caisse']; Map roleIds = {}; - + for (String role in roles) { - final result = await db.query('INSERT INTO roles (designation) VALUES (?)', [role]); + final result = await db + .query('INSERT INTO roles (designation) VALUES (?)', [role]); roleIds[role] = result.insertId!; } @@ -138,7 +139,8 @@ class AppDatabase { } // Assigner quelques permissions à l'Admin et à l'User - await _assignBasicPermissionsToRoles(db, roleIds['Admin']!, roleIds['User']!); + await _assignBasicPermissionsToRoles( + db, roleIds['Admin']!, roleIds['User']!); print("Rôles par défaut créés et permissions assignées"); } else { @@ -151,17 +153,19 @@ class AppDatabase { Future insertDefaultPointsDeVente() async { final db = await database; - + try { - final existing = await db.query('SELECT COUNT(*) as count FROM points_de_vente'); + final existing = + await db.query('SELECT COUNT(*) as count FROM points_de_vente'); final count = existing.first['count'] as int; - + if (count == 0) { final defaultPoints = ['405A', '405B', '416', 'S405A', '417']; - + for (var point in defaultPoints) { try { - await db.query('INSERT IGNORE INTO points_de_vente (nom) VALUES (?)', [point]); + await db.query( + 'INSERT IGNORE INTO points_de_vente (nom) VALUES (?)', [point]); } catch (e) { print("Erreur insertion point de vente $point: $e"); } @@ -185,9 +189,7 @@ class AppDatabase { if (existingSuperAdmin.isEmpty) { final superAdminRole = await db.query( - 'SELECT id FROM roles WHERE designation = ?', - ['Super Admin'] - ); + 'SELECT id FROM roles WHERE designation = ?', ['Super Admin']); if (superAdminRole.isNotEmpty) { final superAdminRoleId = superAdminRole.first['id']; @@ -207,7 +209,8 @@ class AppDatabase { print("Super Admin créé avec succès !"); print("Username: superadmin"); print("Password: admin123"); - print("ATTENTION: Changez ce mot de passe après la première connexion !"); + print( + "ATTENTION: Changez ce mot de passe après la première connexion !"); } } else { print("Super Admin existe déjà"); @@ -218,30 +221,29 @@ class AppDatabase { } // --- CRUD USERS --- - + Future createUser(Users user) async { final db = await database; final userMap = user.toMap(); userMap.remove('id'); // Remove ID for auto-increment - + final fields = userMap.keys.join(', '); final placeholders = List.filled(userMap.length, '?').join(', '); - + final result = await db.query( - 'INSERT INTO users ($fields) VALUES ($placeholders)', - userMap.values.toList() - ); + 'INSERT INTO users ($fields) VALUES ($placeholders)', + userMap.values.toList()); return result.insertId!; } - // ✅ CORRIGÉ: Méthode updateUser simplifiée et corrigée + // ✅ CORRIGÉ: Méthode updateUser simplifiée et corrigée Future updateUser(Users user) async { final db = await database; - + try { print("🔄 Mise à jour utilisateur ID: ${user.id}"); print("Données: ${user.toMap()}"); - + final result = await db.query(''' UPDATE users SET @@ -263,23 +265,22 @@ class AppDatabase { user.pointDeVenteId, user.id ]); - - print("✅ Utilisateur mis à jour. Lignes affectées: ${result.affectedRows}"); + + print( + "✅ Utilisateur mis à jour. Lignes affectées: ${result.affectedRows}"); return result.affectedRows!; - } catch (e) { print("❌ Erreur lors de la mise à jour de l'utilisateur: $e"); rethrow; } } - // ✅ AJOUTÉ: Méthode pour vérifier si l'utilisateur existe + + // ✅ AJOUTÉ: Méthode pour vérifier si l'utilisateur existe Future userExists(int userId) async { final db = await database; try { - final result = await db.query( - 'SELECT COUNT(*) as count FROM users WHERE id = ?', - [userId] - ); + final result = await db + .query('SELECT COUNT(*) as count FROM users WHERE id = ?', [userId]); return (result.first['count'] as int) > 0; } catch (e) { print("❌ Erreur vérification existence utilisateur: $e"); @@ -290,31 +291,27 @@ class AppDatabase { // ✅ AJOUTÉ: Méthode pour vérifier les contraintes avant mise à jour Future validateUserUpdate(Users user) async { final db = await database; - + try { // Vérifier si l'email existe déjà pour un autre utilisateur final emailCheck = await db.query( - 'SELECT COUNT(*) as count FROM users WHERE email = ? AND id != ?', - [user.email, user.id] - ); + 'SELECT COUNT(*) as count FROM users WHERE email = ? AND id != ?', + [user.email, user.id]); if ((emailCheck.first['count'] as int) > 0) { return 'Cet email est déjà utilisé par un autre utilisateur'; } // Vérifier si le username existe déjà pour un autre utilisateur final usernameCheck = await db.query( - 'SELECT COUNT(*) as count FROM users WHERE username = ? AND id != ?', - [user.username, user.id] - ); + 'SELECT COUNT(*) as count FROM users WHERE username = ? AND id != ?', + [user.username, user.id]); if ((usernameCheck.first['count'] as int) > 0) { return 'Ce nom d\'utilisateur est déjà utilisé'; } // Vérifier si le rôle existe final roleCheck = await db.query( - 'SELECT COUNT(*) as count FROM roles WHERE id = ?', - [user.roleId] - ); + 'SELECT COUNT(*) as count FROM roles WHERE id = ?', [user.roleId]); if ((roleCheck.first['count'] as int) == 0) { return 'Le rôle sélectionné n\'existe pas'; } @@ -322,9 +319,8 @@ class AppDatabase { // Vérifier si le point de vente existe (si spécifié) if (user.pointDeVenteId != null && user.pointDeVenteId! > 0) { final pointDeVenteCheck = await db.query( - 'SELECT COUNT(*) as count FROM points_de_vente WHERE id = ?', - [user.pointDeVenteId] - ); + 'SELECT COUNT(*) as count FROM points_de_vente WHERE id = ?', + [user.pointDeVenteId]); if ((pointDeVenteCheck.first['count'] as int) == 0) { return 'Le point de vente sélectionné n\'existe pas'; } @@ -336,7 +332,6 @@ class AppDatabase { } } - Future deleteUser(int id) async { final db = await database; final result = await db.query('DELETE FROM users WHERE id = ?', [id]); @@ -351,33 +346,31 @@ class AppDatabase { INNER JOIN roles ON users.role_id = roles.id ORDER BY users.id ASC '''); - + return result.map((row) => Users.fromMap(row.fields)).toList(); } // --- CRUD ROLES --- - + Future createRole(Role role) async { final db = await database; final result = await db.query( - 'INSERT INTO roles (designation) VALUES (?)', - [role.designation] - ); + 'INSERT INTO roles (designation) VALUES (?)', [role.designation]); return result.insertId!; } Future> getRoles() async { final db = await database; - final result = await db.query('SELECT * FROM roles ORDER BY designation ASC'); + final result = + await db.query('SELECT * FROM roles ORDER BY designation ASC'); return result.map((row) => Role.fromMap(row.fields)).toList(); } Future updateRole(Role role) async { final db = await database; final result = await db.query( - 'UPDATE roles SET designation = ? WHERE id = ?', - [role.designation, role.id] - ); + 'UPDATE roles SET designation = ? WHERE id = ?', + [role.designation, role.id]); return result.affectedRows!; } @@ -388,10 +381,11 @@ class AppDatabase { } // --- PERMISSIONS --- - + Future> getAllPermissions() async { final db = await database; - final result = await db.query('SELECT * FROM permissions ORDER BY name ASC'); + final result = + await db.query('SELECT * FROM permissions ORDER BY name ASC'); return result.map((row) => Permission.fromMap(row.fields)).toList(); } @@ -404,11 +398,12 @@ class AppDatabase { WHERE rp.role_id = ? ORDER BY p.name ASC ''', [roleId]); - + return result.map((row) => Permission.fromMap(row.fields)).toList(); } - Future> getPermissionsForRoleAndMenu(int roleId, int menuId) async { + Future> getPermissionsForRoleAndMenu( + int roleId, int menuId) async { final db = await database; final result = await db.query(''' SELECT p.id, p.name @@ -417,12 +412,12 @@ class AppDatabase { WHERE rmp.role_id = ? AND rmp.menu_id = ? ORDER BY p.name ASC ''', [roleId, menuId]); - + return result.map((row) => Permission.fromMap(row.fields)).toList(); } // --- AUTHENTIFICATION --- - + Future verifyUser(String username, String password) async { final db = await database; final result = await db.query(''' @@ -430,7 +425,7 @@ class AppDatabase { FROM users WHERE username = ? AND password = ? ''', [username, password]); - + return (result.first['count'] as int) > 0; } @@ -450,7 +445,8 @@ class AppDatabase { } } - Future?> getUserCredentials(String username, String password) async { + Future?> getUserCredentials( + String username, String password) async { final db = await database; final result = await db.query(''' SELECT users.username, users.id, roles.designation as role_name, roles.id as role_id @@ -473,7 +469,7 @@ class AppDatabase { } // --- CRUD PRODUCTS --- - + Future createProduct(Product product) async { final db = await database; @@ -481,14 +477,13 @@ class AppDatabase { if (product.pointDeVenteId != null && product.pointDeVenteId! > 0) { final productMap = product.toMap(); productMap.remove('id'); - + final fields = productMap.keys.join(', '); final placeholders = List.filled(productMap.length, '?').join(', '); - + final result = await db.query( - 'INSERT INTO products ($fields) VALUES ($placeholders)', - productMap.values.toList() - ); + 'INSERT INTO products ($fields) VALUES ($placeholders)', + productMap.values.toList()); return result.insertId!; } @@ -504,11 +499,10 @@ class AppDatabase { final fields = productData.keys.join(', '); final placeholders = List.filled(productData.length, '?').join(', '); - + final result = await db.query( - 'INSERT INTO products ($fields) VALUES ($placeholders)', - productData.values.toList() - ); + 'INSERT INTO products ($fields) VALUES ($placeholders)', + productData.values.toList()); return result.insertId!; } @@ -522,21 +516,19 @@ class AppDatabase { final db = await database; final productMap = product.toMap(); final id = productMap.remove('id'); - + final setClause = productMap.keys.map((key) => '$key = ?').join(', '); final values = [...productMap.values, id]; - - final result = await db.query( - 'UPDATE products SET $setClause WHERE id = ?', - values - ); + + final result = + await db.query('UPDATE products SET $setClause WHERE id = ?', values); return result.affectedRows!; } Future getProductById(int id) async { final db = await database; final result = await db.query('SELECT * FROM products WHERE id = ?', [id]); - + if (result.isNotEmpty) { return Product.fromMap(result.first.fields); } @@ -550,34 +542,32 @@ class AppDatabase { } // --- CRUD CLIENTS --- - + Future createClient(Client client) async { final db = await database; final clientMap = client.toMap(); clientMap.remove('id'); - + final fields = clientMap.keys.join(', '); final placeholders = List.filled(clientMap.length, '?').join(', '); - + final result = await db.query( - 'INSERT INTO clients ($fields) VALUES ($placeholders)', - clientMap.values.toList() - ); + 'INSERT INTO clients ($fields) VALUES ($placeholders)', + clientMap.values.toList()); return result.insertId!; } Future> getClients() async { final db = await database; final result = await db.query( - 'SELECT * FROM clients WHERE actif = 1 ORDER BY nom ASC, prenom ASC' - ); + 'SELECT * FROM clients WHERE actif = 1 ORDER BY nom ASC, prenom ASC'); return result.map((row) => Client.fromMap(row.fields)).toList(); } Future getClientById(int id) async { final db = await database; final result = await db.query('SELECT * FROM clients WHERE id = ?', [id]); - + if (result.isNotEmpty) { return Client.fromMap(result.first.fields); } @@ -585,21 +575,21 @@ class AppDatabase { } // --- POINTS DE VENTE --- - + Future>> getPointsDeVente() async { final db = await database; try { final result = await db.query( - 'SELECT * FROM points_de_vente WHERE nom IS NOT NULL AND nom != "" ORDER BY nom ASC' - ); - + 'SELECT * FROM points_de_vente WHERE nom IS NOT NULL AND nom != "" ORDER BY nom ASC'); + if (result.isEmpty) { print("Aucun point de vente trouvé dans la base de données"); await insertDefaultPointsDeVente(); - final newResult = await db.query('SELECT * FROM points_de_vente ORDER BY nom ASC'); + final newResult = + await db.query('SELECT * FROM points_de_vente ORDER BY nom ASC'); return newResult.map((row) => row.fields).toList(); } - + return result.map((row) => row.fields).toList(); } catch (e) { print("Erreur lors de la récupération des points de vente: $e"); @@ -608,15 +598,19 @@ class AppDatabase { } // --- STATISTIQUES --- - + Future> getStatistiques() async { final db = await database; - - final totalClients = await db.query('SELECT COUNT(*) as count FROM clients WHERE actif = 1'); - final totalCommandes = await db.query('SELECT COUNT(*) as count FROM commandes'); - final totalProduits = await db.query('SELECT COUNT(*) as count FROM products'); - final chiffreAffaires = await db.query('SELECT SUM(montantTotal) as total FROM commandes WHERE statut != 5'); - + + final totalClients = + await db.query('SELECT COUNT(*) as count FROM clients WHERE actif = 1'); + final totalCommandes = + await db.query('SELECT COUNT(*) as count FROM commandes'); + final totalProduits = + await db.query('SELECT COUNT(*) as count FROM products'); + final chiffreAffaires = await db.query( + 'SELECT SUM(montantTotal) as total FROM commandes WHERE statut != 5'); + return { 'totalClients': totalClients.first['count'], 'totalCommandes': totalCommandes.first['count'], @@ -626,7 +620,7 @@ class AppDatabase { } // --- MÉTHODES UTILITAIRES --- - + // Future _addMissingMenus(MySqlConnection db) async { // final menusToAdd = [ // {'name': 'Nouvelle commande', 'route': '/nouvelle-commande'}, @@ -640,7 +634,7 @@ class AppDatabase { // [menu['route']] // ); // final count = existing.first['count'] as int; - + // if (count == 0) { // await db.query( // 'INSERT INTO menu (name, route) VALUES (?, ?)', @@ -652,31 +646,29 @@ class AppDatabase { // } Future _addMissingMenus(MySqlConnection db) async { - final menusToAdd = [ - {'name': 'Nouvelle commande', 'route': '/nouvelle-commande'}, - {'name': 'Gérer les commandes', 'route': '/gerer-commandes'}, - {'name': 'Points de vente', 'route': '/points-de-vente'}, - ]; - - for (var menu in menusToAdd) { - final existing = await db.query( - 'SELECT COUNT(*) as count FROM menu WHERE route = ?', - [menu['route']] - ); - final count = existing.first['count'] as int; - - if (count == 0) { - await db.query( - 'INSERT INTO menu (name, route) VALUES (?, ?)', - [menu['name'], menu['route']] - ); - print("Menu ajouté: ${menu['name']}"); + final menusToAdd = [ + {'name': 'Nouvelle commande', 'route': '/nouvelle-commande'}, + {'name': 'Gérer les commandes', 'route': '/gerer-commandes'}, + {'name': 'Points de vente', 'route': '/points-de-vente'}, + ]; + + for (var menu in menusToAdd) { + final existing = await db.query( + 'SELECT COUNT(*) as count FROM menu WHERE route = ?', + [menu['route']]); + final count = existing.first['count'] as int; + + if (count == 0) { + await db.query('INSERT INTO menu (name, route) VALUES (?, ?)', + [menu['name'], menu['route']]); + print("Menu ajouté: ${menu['name']}"); + } } } -} Future _updateExistingRolePermissions(MySqlConnection db) async { - final superAdminRole = await db.query('SELECT id FROM roles WHERE designation = ?', ['Super Admin']); + final superAdminRole = await db + .query('SELECT id FROM roles WHERE designation = ?', ['Super Admin']); if (superAdminRole.isNotEmpty) { final superAdminRoleId = superAdminRole.first['id']; final permissions = await db.query('SELECT * FROM permissions'); @@ -689,7 +681,7 @@ class AppDatabase { SELECT COUNT(*) as count FROM role_menu_permissions WHERE role_id = ? AND menu_id = ? AND permission_id = ? ''', [superAdminRoleId, menu['id'], permission['id']]); - + final count = existingPermission.first['count'] as int; if (count == 0) { await db.query(''' @@ -701,24 +693,28 @@ class AppDatabase { } // Assigner les permissions de base aux autres rôles pour les nouveaux menus - final adminRole = await db.query('SELECT id FROM roles WHERE designation = ?', ['Admin']); - final userRole = await db.query('SELECT id FROM roles WHERE designation = ?', ['User']); - + final adminRole = await db + .query('SELECT id FROM roles WHERE designation = ?', ['Admin']); + final userRole = await db + .query('SELECT id FROM roles WHERE designation = ?', ['User']); + if (adminRole.isNotEmpty && userRole.isNotEmpty) { - await _assignBasicPermissionsToRoles(db, adminRole.first['id'], userRole.first['id']); + await _assignBasicPermissionsToRoles( + db, adminRole.first['id'], userRole.first['id']); } print("Permissions mises à jour pour tous les rôles"); } } - Future _assignBasicPermissionsToRoles(MySqlConnection db, int adminRoleId, int userRoleId) async { + Future _assignBasicPermissionsToRoles( + MySqlConnection db, int adminRoleId, int userRoleId) async { // Implémentation similaire mais adaptée pour MySQL print("Permissions de base assignées aux rôles Admin et User"); } // --- FERMETURE --- - + Future close() async { if (_connection != null) { try { @@ -738,19 +734,19 @@ class AppDatabase { try { // Désactiver les contraintes de clés étrangères temporairement await db.query('SET FOREIGN_KEY_CHECKS = 0'); - + // Lister toutes les tables final tables = await db.query('SHOW TABLES'); - + // Supprimer toutes les tables for (var table in tables) { final tableName = table.values?.first; await db.query('DROP TABLE IF EXISTS `$tableName`'); } - + // Réactiver les contraintes de clés étrangères await db.query('SET FOREIGN_KEY_CHECKS = 1'); - + print("Toutes les tables ont été supprimées"); } catch (e) { print("Erreur lors de la suppression des tables: $e"); @@ -763,7 +759,8 @@ class AppDatabase { print("=== INFORMATIONS DE LA BASE DE DONNÉES MYSQL ==="); try { - final userCountResult = await db.query('SELECT COUNT(*) as count FROM users'); + final userCountResult = + await db.query('SELECT COUNT(*) as count FROM users'); final userCount = userCountResult.first['count'] as int; print("Nombre d'utilisateurs: $userCount"); @@ -792,19 +789,18 @@ class AppDatabase { } // --- MÉTHODES SUPPLÉMENTAIRES POUR COMMANDES --- - + Future createCommande(Commande commande) async { final db = await database; final commandeMap = commande.toMap(); commandeMap.remove('id'); - + final fields = commandeMap.keys.join(', '); final placeholders = List.filled(commandeMap.length, '?').join(', '); - + final result = await db.query( - 'INSERT INTO commandes ($fields) VALUES ($placeholders)', - commandeMap.values.toList() - ); + 'INSERT INTO commandes ($fields) VALUES ($placeholders)', + commandeMap.values.toList()); return result.insertId!; } @@ -827,7 +823,7 @@ class AppDatabase { LEFT JOIN clients cl ON c.clientId = cl.id WHERE c.id = ? ''', [id]); - + if (result.isNotEmpty) { return Commande.fromMap(result.first.fields); } @@ -839,15 +835,14 @@ class AppDatabase { final commandeMap = commande.toMap(); final id = commandeMap.remove('id'); final date = DateTime.parse(commandeMap['dateCommande']); - commandeMap['dateCommande'] = DateFormat('yyyy-MM-dd HH:mm:ss').format(date); + commandeMap['dateCommande'] = + DateFormat('yyyy-MM-dd HH:mm:ss').format(date); final setClause = commandeMap.keys.map((key) => '$key = ?').join(', '); final values = [...commandeMap.values, id]; - - final result = await db.query( - 'UPDATE commandes SET $setClause WHERE id = ?', - values - ); + + final result = + await db.query('UPDATE commandes SET $setClause WHERE id = ?', values); return result.affectedRows!; } @@ -858,26 +853,25 @@ class AppDatabase { } // --- DÉTAILS COMMANDES --- - + Future createDetailCommande(DetailCommande detail) async { - final db = await database; - final detailMap = detail.toMap(); - detailMap.remove('id'); - - final fields = detailMap.keys.join(', '); - final placeholders = List.filled(detailMap.length, '?').join(', '); - - final result = await db.query( - 'INSERT INTO details_commandes ($fields) VALUES ($placeholders)', - detailMap.values.toList() - ); - return result.insertId!; -} + final db = await database; + final detailMap = detail.toMap(); + detailMap.remove('id'); - // Méthode mise à jour pour récupérer les détails avec les remises -Future> getDetailsCommande(int commandeId) async { - final db = await database; - final result = await db.query(''' + final fields = detailMap.keys.join(', '); + final placeholders = List.filled(detailMap.length, '?').join(', '); + + final result = await db.query( + 'INSERT INTO details_commandes ($fields) VALUES ($placeholders)', + detailMap.values.toList()); + return result.insertId!; + } + + // Méthode mise à jour pour récupérer les détails avec les remises + Future> getDetailsCommande(int commandeId) async { + final db = await database; + final result = await db.query(''' SELECT dc.*, p.name as produitNom, @@ -888,19 +882,17 @@ Future> getDetailsCommande(int commandeId) async { WHERE dc.commandeId = ? ORDER BY dc.est_cadeau ASC, dc.id ''', [commandeId]); - - return result.map((row) => DetailCommande.fromMap(row.fields)).toList(); -} + + return result.map((row) => DetailCommande.fromMap(row.fields)).toList(); + } // --- RECHERCHE PRODUITS --- - + Future getProductByReference(String reference) async { final db = await database; - final result = await db.query( - 'SELECT * FROM products WHERE reference = ?', - [reference] - ); - + final result = await db + .query('SELECT * FROM products WHERE reference = ?', [reference]); + if (result.isNotEmpty) { return Product.fromMap(result.first.fields); } @@ -909,11 +901,9 @@ Future> getDetailsCommande(int commandeId) async { Future getProductByIMEI(String imei) async { final db = await database; - final result = await db.query( - 'SELECT * FROM products WHERE imei = ?', - [imei] - ); - + final result = + await db.query('SELECT * FROM products WHERE imei = ?', [imei]); + if (result.isNotEmpty) { return Product.fromMap(result.first.fields); } @@ -922,43 +912,39 @@ Future> getDetailsCommande(int commandeId) async { Future> getCategories() async { final db = await database; - final result = await db.query('SELECT DISTINCT category FROM products ORDER BY category'); + final result = await db + .query('SELECT DISTINCT category FROM products ORDER BY category'); return result.map((row) => row['category'] as String).toList(); } Future> getProductsByCategory(String category) async { final db = await database; final result = await db.query( - 'SELECT * FROM products WHERE category = ? ORDER BY name ASC', - [category] - ); + 'SELECT * FROM products WHERE category = ? ORDER BY name ASC', + [category]); return result.map((row) => Product.fromMap(row.fields)).toList(); } // --- RECHERCHE CLIENTS --- - + Future updateClient(Client client) async { final db = await database; final clientMap = client.toMap(); final id = clientMap.remove('id'); - + final setClause = clientMap.keys.map((key) => '$key = ?').join(', '); final values = [...clientMap.values, id]; - - final result = await db.query( - 'UPDATE clients SET $setClause WHERE id = ?', - values - ); + + final result = + await db.query('UPDATE clients SET $setClause WHERE id = ?', values); return result.affectedRows!; } Future deleteClient(int id) async { final db = await database; // Soft delete - final result = await db.query( - 'UPDATE clients SET actif = 0 WHERE id = ?', - [id] - ); + final result = + await db.query('UPDATE clients SET actif = 0 WHERE id = ?', [id]); return result.affectedRows!; } @@ -969,17 +955,16 @@ Future> getDetailsCommande(int commandeId) async { WHERE actif = 1 AND (nom LIKE ? OR prenom LIKE ? OR email LIKE ?) ORDER BY nom ASC, prenom ASC ''', ['%$query%', '%$query%', '%$query%']); - + return result.map((row) => Client.fromMap(row.fields)).toList(); } Future getClientByEmail(String email) async { final db = await database; final result = await db.query( - 'SELECT * FROM clients WHERE email = ? AND actif = 1 LIMIT 1', - [email.trim().toLowerCase()] - ); - + 'SELECT * FROM clients WHERE email = ? AND actif = 1 LIMIT 1', + [email.trim().toLowerCase()]); + if (result.isNotEmpty) { return Client.fromMap(result.first.fields); } @@ -999,36 +984,34 @@ Future> getDetailsCommande(int commandeId) async { return clientByEmail; } } - + // Priorité 2: Recherche par téléphone if (telephone != null && telephone.isNotEmpty) { final db = await database; final result = await db.query( - 'SELECT * FROM clients WHERE telephone = ? AND actif = 1 LIMIT 1', - [telephone.trim()] - ); + 'SELECT * FROM clients WHERE telephone = ? AND actif = 1 LIMIT 1', + [telephone.trim()]); if (result.isNotEmpty) { return Client.fromMap(result.first.fields); } } - + // Priorité 3: Recherche par nom et prénom if (nom != null && nom.isNotEmpty && prenom != null && prenom.isNotEmpty) { final db = await database; final result = await db.query( - 'SELECT * FROM clients WHERE LOWER(nom) = ? AND LOWER(prenom) = ? AND actif = 1 LIMIT 1', - [nom.trim().toLowerCase(), prenom.trim().toLowerCase()] - ); + 'SELECT * FROM clients WHERE LOWER(nom) = ? AND LOWER(prenom) = ? AND actif = 1 LIMIT 1', + [nom.trim().toLowerCase(), prenom.trim().toLowerCase()]); if (result.isNotEmpty) { return Client.fromMap(result.first.fields); } } - + return null; } // --- UTILISATEURS SPÉCIALISÉS --- - + Future> getCommercialUsers() async { final db = await database; final result = await db.query(''' @@ -1063,8 +1046,9 @@ Future> getDetailsCommande(int commandeId) async { } // --- PERMISSIONS AVANCÉES --- - - Future assignRoleMenuPermission(int roleId, int menuId, int permissionId) async { + + Future assignRoleMenuPermission( + int roleId, int menuId, int permissionId) async { final db = await database; await db.query(''' INSERT IGNORE INTO role_menu_permissions (role_id, menu_id, permission_id) @@ -1072,7 +1056,8 @@ Future> getDetailsCommande(int commandeId) async { ''', [roleId, menuId, permissionId]); } - Future removeRoleMenuPermission(int roleId, int menuId, int permissionId) async { + Future removeRoleMenuPermission( + int roleId, int menuId, int permissionId) async { final db = await database; await db.query(''' DELETE FROM role_menu_permissions @@ -1092,7 +1077,8 @@ Future> getDetailsCommande(int commandeId) async { return (result.first['count'] as int) > 0; } - Future hasPermission(String username, String permissionName, String menuRoute) async { + Future hasPermission( + String username, String permissionName, String menuRoute) async { final db = await database; final result = await db.query(''' SELECT COUNT(*) as count @@ -1103,47 +1089,42 @@ Future> getDetailsCommande(int commandeId) async { JOIN menu m ON m.route = ? WHERE u.username = ? AND p.name = ? AND rmp.menu_id = m.id ''', [menuRoute, username, permissionName]); - + return (result.first['count'] as int) > 0; } // --- GESTION STOCK --- - + Future updateStock(int productId, int newStock) async { final db = await database; final result = await db.query( - 'UPDATE products SET stock = ? WHERE id = ?', - [newStock, productId] - ); + 'UPDATE products SET stock = ? WHERE id = ?', [newStock, productId]); return result.affectedRows!; } Future> getLowStockProducts({int threshold = 5}) async { final db = await database; final result = await db.query( - 'SELECT * FROM products WHERE stock <= ? AND stock > 0 ORDER BY stock ASC', - [threshold] - ); + 'SELECT * FROM products WHERE stock <= ? AND stock > 0 ORDER BY stock ASC', + [threshold]); return result.map((row) => Product.fromMap(row.fields)).toList(); } // --- POINTS DE VENTE AVANCÉS --- - + Future createPointDeVente(String designation, String code) async { final db = await database; final result = await db.query( - 'INSERT IGNORE INTO points_de_vente (nom) VALUES (?)', - [designation] - ); + 'INSERT IGNORE INTO points_de_vente (nom) VALUES (?)', [designation]); return result.insertId ?? 0; } - Future updatePointDeVente(int id, String newDesignation, String newCode) async { + Future updatePointDeVente( + int id, String newDesignation, String newCode) async { final db = await database; final result = await db.query( - 'UPDATE points_de_vente SET nom = ? WHERE id = ?', - [newDesignation, id] - ); + 'UPDATE points_de_vente SET nom = ? WHERE id = ?', + [newDesignation, id]); return result.affectedRows!; } @@ -1155,223 +1136,207 @@ Future> getDetailsCommande(int commandeId) async { // Dans votre classe AppDatabase, remplacez la méthode deletePointDeVente par ceci : // SOLUTION 1: Vérification avant suppression avec gestion des produits -Future deletePointDeVente(int id) async { - final db = await database; - - try { - await db.query('START TRANSACTION'); - - // 1. Vérifier s'il y a des produits liés à ce point de vente - final produitsLies = await db.query( - 'SELECT COUNT(*) as count FROM products WHERE point_de_vente_id = ?', - [id] - ); - final nombreProduits = produitsLies.first['count'] as int; - - if (nombreProduits > 0) { - // Option A: Transférer les produits vers un point de vente par défaut - // Récupérer le premier point de vente disponible (ou créer un "point de vente général") - final pointsDeVente = await db.query( - 'SELECT id FROM points_de_vente WHERE id != ? ORDER BY id ASC LIMIT 1', - [id] - ); - - if (pointsDeVente.isNotEmpty) { - final pointDeVenteParDefaut = pointsDeVente.first['id'] as int; - - // Transférer tous les produits vers le point de vente par défaut - await db.query( - 'UPDATE products SET point_de_vente_id = ? WHERE point_de_vente_id = ?', - [pointDeVenteParDefaut, id] - ); - - print("$nombreProduits produits transférés vers le point de vente ID $pointDeVenteParDefaut"); - } else { - // Si aucun autre point de vente, créer un point de vente "Général" - final result = await db.query( - 'INSERT INTO points_de_vente (nom) VALUES (?)', - ['Général'] - ); - final nouveauPointId = result.insertId!; - - await db.query( - 'UPDATE products SET point_de_vente_id = ? WHERE point_de_vente_id = ?', - [nouveauPointId, id] - ); - - print("Point de vente 'Général' créé et $nombreProduits produits transférés"); + Future deletePointDeVente(int id) async { + final db = await database; + + try { + await db.query('START TRANSACTION'); + + // 1. Vérifier s'il y a des produits liés à ce point de vente + final produitsLies = await db.query( + 'SELECT COUNT(*) as count FROM products WHERE point_de_vente_id = ?', + [id]); + final nombreProduits = produitsLies.first['count'] as int; + + if (nombreProduits > 0) { + // Option A: Transférer les produits vers un point de vente par défaut + // Récupérer le premier point de vente disponible (ou créer un "point de vente général") + final pointsDeVente = await db.query( + 'SELECT id FROM points_de_vente WHERE id != ? ORDER BY id ASC LIMIT 1', + [id]); + + if (pointsDeVente.isNotEmpty) { + final pointDeVenteParDefaut = pointsDeVente.first['id'] as int; + + // Transférer tous les produits vers le point de vente par défaut + await db.query( + 'UPDATE products SET point_de_vente_id = ? WHERE point_de_vente_id = ?', + [pointDeVenteParDefaut, id]); + + print( + "$nombreProduits produits transférés vers le point de vente ID $pointDeVenteParDefaut"); + } else { + // Si aucun autre point de vente, créer un point de vente "Général" + final result = await db.query( + 'INSERT INTO points_de_vente (nom) VALUES (?)', ['Général']); + final nouveauPointId = result.insertId!; + + await db.query( + 'UPDATE products SET point_de_vente_id = ? WHERE point_de_vente_id = ?', + [nouveauPointId, id]); + + print( + "Point de vente 'Général' créé et $nombreProduits produits transférés"); + } } + + // 2. Maintenant supprimer le point de vente + final deleteResult = + await db.query('DELETE FROM points_de_vente WHERE id = ?', [id]); + + await db.query('COMMIT'); + return deleteResult.affectedRows!; + } catch (e) { + await db.query('ROLLBACK'); + print('Erreur lors de la suppression du point de vente: $e'); + rethrow; } - - // 2. Maintenant supprimer le point de vente - final deleteResult = await db.query( - 'DELETE FROM points_de_vente WHERE id = ?', - [id] - ); - - await db.query('COMMIT'); - return deleteResult.affectedRows!; - - } catch (e) { - await db.query('ROLLBACK'); - print('Erreur lors de la suppression du point de vente: $e'); - rethrow; } -} // SOLUTION 2: Méthode alternative avec suppression douce (soft delete) -Future deletePointDeVenteSoft(int id) async { - final db = await database; - - try { - // Ajouter une colonne 'actif' si elle n'existe pas déjà - // Cette solution nécessite d'ajouter une colonne, mais c'est moins invasif - - // Pour l'instant, on peut utiliser une astuce en renommant le point de vente - final timestamp = DateTime.now().millisecondsSinceEpoch; - final result = await db.query( - 'UPDATE points_de_vente SET nom = CONCAT(nom, " (Supprimé ", ?, ")") WHERE id = ?', - [timestamp, id] - ); - - return result.affectedRows!; - } catch (e) { - print('Erreur lors de la suppression douce: $e'); - rethrow; + Future deletePointDeVenteSoft(int id) async { + final db = await database; + + try { + // Ajouter une colonne 'actif' si elle n'existe pas déjà + // Cette solution nécessite d'ajouter une colonne, mais c'est moins invasif + + // Pour l'instant, on peut utiliser une astuce en renommant le point de vente + final timestamp = DateTime.now().millisecondsSinceEpoch; + final result = await db.query( + 'UPDATE points_de_vente SET nom = CONCAT(nom, " (Supprimé ", ?, ")") WHERE id = ?', + [timestamp, id]); + + return result.affectedRows!; + } catch (e) { + print('Erreur lors de la suppression douce: $e'); + rethrow; + } } -} // SOLUTION 3: Vérification avec message d'erreur personnalisé -Future> checkCanDeletePointDeVente(int id) async { - final db = await database; - - try { - // Vérifier les produits - final produitsResult = await db.query( - 'SELECT COUNT(*) as count FROM products WHERE point_de_vente_id = ?', - [id] - ); - final nombreProduits = produitsResult.first['count'] as int; - - // Vérifier les utilisateurs - final usersResult = await db.query( - 'SELECT COUNT(*) as count FROM users WHERE point_de_vente_id = ?', - [id] - ); - final nombreUsers = usersResult.first['count'] as int; - - // Vérifier les demandes de transfert - final transfertsResult = await db.query(''' + Future> checkCanDeletePointDeVente(int id) async { + final db = await database; + + try { + // Vérifier les produits + final produitsResult = await db.query( + 'SELECT COUNT(*) as count FROM products WHERE point_de_vente_id = ?', + [id]); + final nombreProduits = produitsResult.first['count'] as int; + + // Vérifier les utilisateurs + final usersResult = await db.query( + 'SELECT COUNT(*) as count FROM users WHERE point_de_vente_id = ?', + [id]); + final nombreUsers = usersResult.first['count'] as int; + + // Vérifier les demandes de transfert + final transfertsResult = await db.query(''' SELECT COUNT(*) as count FROM demandes_transfert WHERE point_de_vente_source_id = ? OR point_de_vente_destination_id = ? ''', [id, id]); - final nombreTransferts = transfertsResult.first['count'] as int; - - if (nombreProduits > 0 || nombreUsers > 0 || nombreTransferts > 0) { + final nombreTransferts = transfertsResult.first['count'] as int; + + if (nombreProduits > 0 || nombreUsers > 0 || nombreTransferts > 0) { + return { + 'canDelete': false, + 'reasons': [ + if (nombreProduits > 0) '$nombreProduits produit(s) associé(s)', + if (nombreUsers > 0) '$nombreUsers utilisateur(s) associé(s)', + if (nombreTransferts > 0) + '$nombreTransferts demande(s) de transfert associée(s)', + ], + 'suggestions': [ + if (nombreProduits > 0) + 'Transférez d\'abord les produits vers un autre point de vente', + if (nombreUsers > 0) + 'Réassignez d\'abord les utilisateurs à un autre point de vente', + if (nombreTransferts > 0) + 'Les demandes de transfert resteront pour l\'historique', + ] + }; + } + + return {'canDelete': true, 'reasons': [], 'suggestions': []}; + } catch (e) { return { 'canDelete': false, - 'reasons': [ - if (nombreProduits > 0) '$nombreProduits produit(s) associé(s)', - if (nombreUsers > 0) '$nombreUsers utilisateur(s) associé(s)', - if (nombreTransferts > 0) '$nombreTransferts demande(s) de transfert associée(s)', - ], - 'suggestions': [ - if (nombreProduits > 0) 'Transférez d\'abord les produits vers un autre point de vente', - if (nombreUsers > 0) 'Réassignez d\'abord les utilisateurs à un autre point de vente', - if (nombreTransferts > 0) 'Les demandes de transfert resteront pour l\'historique', - ] + 'reasons': ['Erreur lors de la vérification: $e'], + 'suggestions': [] }; } - - return { - 'canDelete': true, - 'reasons': [], - 'suggestions': [] - }; - } catch (e) { - return { - 'canDelete': false, - 'reasons': ['Erreur lors de la vérification: $e'], - 'suggestions': [] - }; } -} // SOLUTION 4: Suppression avec transfert automatique vers un point de vente spécifique -Future deletePointDeVenteWithTransfer(int idToDelete, int targetPointDeVenteId) async { - final db = await database; - - try { - await db.query('START TRANSACTION'); - - // 1. Transférer tous les produits - await db.query( - 'UPDATE products SET point_de_vente_id = ? WHERE point_de_vente_id = ?', - [targetPointDeVenteId, idToDelete] - ); - - // 2. Transférer tous les utilisateurs (si applicable) - await db.query( - 'UPDATE users SET point_de_vente_id = ? WHERE point_de_vente_id = ?', - [targetPointDeVenteId, idToDelete] - ); - - // 3. Supprimer le point de vente - final result = await db.query( - 'DELETE FROM points_de_vente WHERE id = ?', - [idToDelete] - ); - - await db.query('COMMIT'); - return result.affectedRows!; - - } catch (e) { - await db.query('ROLLBACK'); - rethrow; + Future deletePointDeVenteWithTransfer( + int idToDelete, int targetPointDeVenteId) async { + final db = await database; + + try { + await db.query('START TRANSACTION'); + + // 1. Transférer tous les produits + await db.query( + 'UPDATE products SET point_de_vente_id = ? WHERE point_de_vente_id = ?', + [targetPointDeVenteId, idToDelete]); + + // 2. Transférer tous les utilisateurs (si applicable) + await db.query( + 'UPDATE users SET point_de_vente_id = ? WHERE point_de_vente_id = ?', + [targetPointDeVenteId, idToDelete]); + + // 3. Supprimer le point de vente + final result = await db + .query('DELETE FROM points_de_vente WHERE id = ?', [idToDelete]); + + await db.query('COMMIT'); + return result.affectedRows!; + } catch (e) { + await db.query('ROLLBACK'); + rethrow; + } } -} // SOLUTION 5: Méthode pour obtenir les points de vente de destination possibles -Future>> getPointsDeVenteForTransfer(int excludeId) async { - final db = await database; - - try { - final result = await db.query( - 'SELECT * FROM points_de_vente WHERE id != ? ORDER BY nom ASC', - [excludeId] - ); - - return result.map((row) => row.fields).toList(); - } catch (e) { - print('Erreur récupération points de vente pour transfert: $e'); - return []; + Future>> getPointsDeVenteForTransfer( + int excludeId) async { + final db = await database; + + try { + final result = await db.query( + 'SELECT * FROM points_de_vente WHERE id != ? ORDER BY nom ASC', + [excludeId]); + + return result.map((row) => row.fields).toList(); + } catch (e) { + print('Erreur récupération points de vente pour transfert: $e'); + return []; + } } -} + Future?> getPointDeVenteById(int id) async { final db = await database; - final result = await db.query('SELECT * FROM points_de_vente WHERE id = ?', [id]); + final result = + await db.query('SELECT * FROM points_de_vente WHERE id = ?', [id]); return result.isNotEmpty ? result.first.fields : null; } Future getOrCreatePointDeVenteByNom(String nom) async { final db = await database; - + // Vérifier si le point de vente existe déjà - final existing = await db.query( - 'SELECT id FROM points_de_vente WHERE nom = ?', - [nom.trim()] - ); - + final existing = await db + .query('SELECT id FROM points_de_vente WHERE nom = ?', [nom.trim()]); + if (existing.isNotEmpty) { return existing.first['id'] as int; } - + // Créer le point de vente s'il n'existe pas try { - final result = await db.query( - 'INSERT INTO points_de_vente (nom) VALUES (?)', - [nom.trim()] - ); + final result = await db + .query('INSERT INTO points_de_vente (nom) VALUES (?)', [nom.trim()]); print("Point de vente créé: $nom (ID: ${result.insertId})"); return result.insertId; } catch (e) { @@ -1382,14 +1347,12 @@ Future>> getPointsDeVenteForTransfer(int excludeId) as Future getPointDeVenteNomById(int id) async { if (id == 0) return null; - + final db = await database; try { - final result = await db.query( - 'SELECT nom FROM points_de_vente WHERE id = ? LIMIT 1', - [id] - ); - + final result = await db + .query('SELECT nom FROM points_de_vente WHERE id = ? LIMIT 1', [id]); + return result.isNotEmpty ? result.first['nom'] as String : null; } catch (e) { print("Erreur getPointDeVenteNomById: $e"); @@ -1398,7 +1361,7 @@ Future>> getPointsDeVenteForTransfer(int excludeId) as } // --- RECHERCHE AVANCÉE --- - + Future> searchProducts({ String? name, String? imei, @@ -1408,169 +1371,156 @@ Future>> getPointsDeVenteForTransfer(int excludeId) as int? pointDeVenteId, }) async { final db = await database; - + List whereConditions = []; List whereArgs = []; - + if (name != null && name.isNotEmpty) { whereConditions.add('name LIKE ?'); whereArgs.add('%$name%'); } - + if (imei != null && imei.isNotEmpty) { whereConditions.add('imei LIKE ?'); whereArgs.add('%$imei%'); } - + if (reference != null && reference.isNotEmpty) { whereConditions.add('reference LIKE ?'); whereArgs.add('%$reference%'); } - + if (onlyInStock) { whereConditions.add('stock > 0'); } - + if (category != null && category.isNotEmpty) { whereConditions.add('category = ?'); whereArgs.add(category); } - + if (pointDeVenteId != null && pointDeVenteId > 0) { whereConditions.add('point_de_vente_id = ?'); whereArgs.add(pointDeVenteId); } - - String whereClause = whereConditions.isNotEmpty - ? 'WHERE ${whereConditions.join(' AND ')}' + + String whereClause = whereConditions.isNotEmpty + ? 'WHERE ${whereConditions.join(' AND ')}' : ''; - + final result = await db.query( - 'SELECT * FROM products $whereClause ORDER BY name ASC', - whereArgs - ); - + 'SELECT * FROM products $whereClause ORDER BY name ASC', whereArgs); + return result.map((row) => Product.fromMap(row.fields)).toList(); } Future findProductByCode(String code) async { final db = await database; - + // Essayer de trouver par référence d'abord - var result = await db.query( - 'SELECT * FROM products WHERE reference = ? LIMIT 1', - [code] - ); - + var result = await db + .query('SELECT * FROM products WHERE reference = ? LIMIT 1', [code]); + if (result.isNotEmpty) { return Product.fromMap(result.first.fields); } - + // Ensuite par IMEI - result = await db.query( - 'SELECT * FROM products WHERE imei = ? LIMIT 1', - [code] - ); - + result = + await db.query('SELECT * FROM products WHERE imei = ? LIMIT 1', [code]); + if (result.isNotEmpty) { return Product.fromMap(result.first.fields); } - + // Enfin par QR code si disponible - result = await db.query( - 'SELECT * FROM products WHERE qrCode = ? LIMIT 1', - [code] - ); - + result = await db + .query('SELECT * FROM products WHERE qrCode = ? LIMIT 1', [code]); + if (result.isNotEmpty) { return Product.fromMap(result.first.fields); } - + return null; } // --- TRANSACTIONS COMPLEXES --- - - + // Méthode pour créer une commande complète avec remises -Future createCommandeComplete(Client client, Commande commande, List details) async { - final db = await database; - - try { - await db.query('START TRANSACTION'); - - // 1. Créer ou récupérer le client - final existingOrNewClient = await createOrGetClient(client); - final clientId = existingOrNewClient.id!; - - // 2. Créer la commande - final commandeMap = commande.toMap(); - commandeMap.remove('id'); - commandeMap['clientId'] = clientId; - - final commandeFields = commandeMap.keys.join(', '); - final commandePlaceholders = List.filled(commandeMap.length, '?').join(', '); - - final commandeResult = await db.query( - 'INSERT INTO commandes ($commandeFields) VALUES ($commandePlaceholders)', - commandeMap.values.toList() - ); - final commandeId = commandeResult.insertId!; - - // 3. Créer les détails de commande avec remises - for (final detail in details) { - final detailMap = detail.toMap(); - detailMap.remove('id'); - detailMap['commandeId'] = commandeId; - - final detailFields = detailMap.keys.join(', '); - final detailPlaceholders = List.filled(detailMap.length, '?').join(', '); - - await db.query( - 'INSERT INTO details_commandes ($detailFields) VALUES ($detailPlaceholders)', - detailMap.values.toList() - ); - - // 4. Mettre à jour le stock - await db.query( - 'UPDATE products SET stock = stock - ? WHERE id = ?', - [detail.quantite, detail.produitId] - ); + Future createCommandeComplete( + Client client, Commande commande, List details) async { + final db = await database; + + try { + await db.query('START TRANSACTION'); + + // 1. Créer ou récupérer le client + final existingOrNewClient = await createOrGetClient(client); + final clientId = existingOrNewClient.id!; + + // 2. Créer la commande + final commandeMap = commande.toMap(); + commandeMap.remove('id'); + commandeMap['clientId'] = clientId; + + final commandeFields = commandeMap.keys.join(', '); + final commandePlaceholders = + List.filled(commandeMap.length, '?').join(', '); + + final commandeResult = await db.query( + 'INSERT INTO commandes ($commandeFields) VALUES ($commandePlaceholders)', + commandeMap.values.toList()); + final commandeId = commandeResult.insertId!; + + // 3. Créer les détails de commande avec remises + for (final detail in details) { + final detailMap = detail.toMap(); + detailMap.remove('id'); + detailMap['commandeId'] = commandeId; + + final detailFields = detailMap.keys.join(', '); + final detailPlaceholders = + List.filled(detailMap.length, '?').join(', '); + + await db.query( + 'INSERT INTO details_commandes ($detailFields) VALUES ($detailPlaceholders)', + detailMap.values.toList()); + + // 4. Mettre à jour le stock + await db.query('UPDATE products SET stock = stock - ? WHERE id = ?', + [detail.quantite, detail.produitId]); + } + + await db.query('COMMIT'); + return commandeId; + } catch (e) { + await db.query('ROLLBACK'); + print("Erreur lors de la création de la commande complète: $e"); + rethrow; } - - await db.query('COMMIT'); - return commandeId; - } catch (e) { - await db.query('ROLLBACK'); - print("Erreur lors de la création de la commande complète: $e"); - rethrow; } -} // Méthode pour mettre à jour un détail de commande (utile pour modifier les remises) -Future updateDetailCommande(DetailCommande detail) async { - final db = await database; - final detailMap = detail.toMap(); - final id = detailMap.remove('id'); - - final setClause = detailMap.keys.map((key) => '$key = ?').join(', '); - final values = [...detailMap.values, id]; - - final result = await db.query( - 'UPDATE details_commandes SET $setClause WHERE id = ?', - values - ); - return result.affectedRows!; -} + Future updateDetailCommande(DetailCommande detail) async { + final db = await database; + final detailMap = detail.toMap(); + final id = detailMap.remove('id'); + final setClause = detailMap.keys.map((key) => '$key = ?').join(', '); + final values = [...detailMap.values, id]; + + final result = await db.query( + 'UPDATE details_commandes SET $setClause WHERE id = ?', values); + return result.affectedRows!; + } // Méthode pour obtenir les statistiques des remises -Future> getRemiseStatistics() async { - final db = await database; - - try { - // Total des remises accordées - final totalRemisesResult = await db.query(''' + Future> getRemiseStatistics() async { + final db = await database; + + try { + // Total des remises accordées + final totalRemisesResult = await db.query(''' SELECT COUNT(*) as nombre_remises, SUM(montant_remise) as total_remises, @@ -1578,9 +1528,9 @@ Future> getRemiseStatistics() async { FROM details_commandes WHERE remise_type IS NOT NULL AND montant_remise > 0 '''); - - // Remises par type - final remisesParTypeResult = await db.query(''' + + // Remises par type + final remisesParTypeResult = await db.query(''' SELECT remise_type, COUNT(*) as nombre, @@ -1590,9 +1540,9 @@ Future> getRemiseStatistics() async { WHERE remise_type IS NOT NULL AND montant_remise > 0 GROUP BY remise_type '''); - - // Produits avec le plus de remises - final produitsRemisesResult = await db.query(''' + + // Produits avec le plus de remises + final produitsRemisesResult = await db.query(''' SELECT p.name as produit_nom, COUNT(*) as nombre_remises, @@ -1604,29 +1554,35 @@ Future> getRemiseStatistics() async { ORDER BY total_remises DESC LIMIT 10 '''); - - return { - 'total_remises': totalRemisesResult.first.fields, - 'remises_par_type': remisesParTypeResult.map((row) => row.fields).toList(), - 'produits_remises': produitsRemisesResult.map((row) => row.fields).toList(), - }; - } catch (e) { - print("Erreur lors du calcul des statistiques de remises: $e"); - return { - 'total_remises': {'nombre_remises': 0, 'total_remises': 0.0, 'moyenne_remise': 0.0}, - 'remises_par_type': [], - 'produits_remises': [], - }; - } -} + return { + 'total_remises': totalRemisesResult.first.fields, + 'remises_par_type': + remisesParTypeResult.map((row) => row.fields).toList(), + 'produits_remises': + produitsRemisesResult.map((row) => row.fields).toList(), + }; + } catch (e) { + print("Erreur lors du calcul des statistiques de remises: $e"); + return { + 'total_remises': { + 'nombre_remises': 0, + 'total_remises': 0.0, + 'moyenne_remise': 0.0 + }, + 'remises_par_type': [], + 'produits_remises': [], + }; + } + } // Méthode pour obtenir les commandes avec le plus de remises -Future>> getCommandesAvecRemises({int limit = 20}) async { - final db = await database; - - try { - final result = await db.query(''' + Future>> getCommandesAvecRemises( + {int limit = 20}) async { + final db = await database; + + try { + final result = await db.query(''' SELECT c.id as commande_id, c.dateCommande, @@ -1644,16 +1600,16 @@ Future>> getCommandesAvecRemises({int limit = 20}) asy ORDER BY total_remises DESC LIMIT ? ''', [limit]); - - return result.map((row) => row.fields).toList(); - } catch (e) { - print("Erreur lors de la récupération des commandes avec remises: $e"); - return []; + + return result.map((row) => row.fields).toList(); + } catch (e) { + print("Erreur lors de la récupération des commandes avec remises: $e"); + return []; + } } -} // --- STATISTIQUES AVANCÉES --- - + Future> getProductCountByCategory() async { final db = await database; final result = await db.query(''' @@ -1662,9 +1618,9 @@ Future>> getCommandesAvecRemises({int limit = 20}) asy GROUP BY category ORDER BY count DESC '''); - - return Map.fromEntries(result.map((row) => - MapEntry(row['category'] as String, row['count'] as int))); + + return Map.fromEntries(result.map( + (row) => MapEntry(row['category'] as String, row['count'] as int))); } Future>> getStockStatsByCategory() async { @@ -1680,7 +1636,7 @@ Future>> getCommandesAvecRemises({int limit = 20}) asy GROUP BY category ORDER BY category '''); - + Map> stats = {}; for (var row in result) { stats[row['category'] as String] = { @@ -1693,7 +1649,8 @@ Future>> getCommandesAvecRemises({int limit = 20}) asy return stats; } - Future>> getMostSoldProducts({int limit = 10}) async { + Future>> getMostSoldProducts( + {int limit = 10}) async { final db = await database; final result = await db.query(''' SELECT @@ -1712,19 +1669,20 @@ Future>> getCommandesAvecRemises({int limit = 20}) asy ORDER BY total_sold DESC LIMIT ? ''', [limit]); - + return result.map((row) => row.fields).toList(); } // --- DÉBOGAGE --- - + Future debugPointsDeVenteTable() async { final db = await database; try { // Compte le nombre d'entrées - final count = await db.query("SELECT COUNT(*) as count FROM points_de_vente"); + final count = + await db.query("SELECT COUNT(*) as count FROM points_de_vente"); print("Nombre de points de vente: ${count.first['count']}"); - + // Affiche le contenu final content = await db.query('SELECT * FROM points_de_vente'); print("Contenu de la table points_de_vente:"); @@ -1735,40 +1693,39 @@ Future>> getCommandesAvecRemises({int limit = 20}) asy print("Erreur debug table points_de_vente: $e"); } } + // 1. Méthodes pour les clients -Future getClientByTelephone(String telephone) async { - final db = await database; - final result = await db.query( - 'SELECT * FROM clients WHERE telephone = ? AND actif = 1 LIMIT 1', - [telephone.trim()] - ); - - if (result.isNotEmpty) { - return Client.fromMap(result.first.fields); - } - return null; -} + Future getClientByTelephone(String telephone) async { + final db = await database; + final result = await db.query( + 'SELECT * FROM clients WHERE telephone = ? AND actif = 1 LIMIT 1', + [telephone.trim()]); -Future getClientByNomPrenom(String nom, String prenom) async { - final db = await database; - final result = await db.query( - 'SELECT * FROM clients WHERE LOWER(nom) = ? AND LOWER(prenom) = ? AND actif = 1 LIMIT 1', - [nom.trim().toLowerCase(), prenom.trim().toLowerCase()] - ); - - if (result.isNotEmpty) { - return Client.fromMap(result.first.fields); - } - return null; -} + if (result.isNotEmpty) { + return Client.fromMap(result.first.fields); + } + return null; + } -Future> suggestClients(String query) async { - if (query.trim().isEmpty) return []; - - final db = await database; - final searchQuery = '%${query.trim().toLowerCase()}%'; - - final result = await db.query(''' + Future getClientByNomPrenom(String nom, String prenom) async { + final db = await database; + final result = await db.query( + 'SELECT * FROM clients WHERE LOWER(nom) = ? AND LOWER(prenom) = ? AND actif = 1 LIMIT 1', + [nom.trim().toLowerCase(), prenom.trim().toLowerCase()]); + + if (result.isNotEmpty) { + return Client.fromMap(result.first.fields); + } + return null; + } + + Future> suggestClients(String query) async { + if (query.trim().isEmpty) return []; + + final db = await database; + final searchQuery = '%${query.trim().toLowerCase()}%'; + + final result = await db.query(''' SELECT * FROM clients WHERE actif = 1 AND ( LOWER(nom) LIKE ? OR @@ -1779,19 +1736,19 @@ Future> suggestClients(String query) async { ORDER BY nom ASC, prenom ASC LIMIT 10 ''', [searchQuery, searchQuery, searchQuery, searchQuery]); - - return result.map((row) => Client.fromMap(row.fields)).toList(); -} -Future> checkPotentialDuplicates({ - required String nom, - required String prenom, - required String email, - required String telephone, -}) async { - final db = await database; - - final result = await db.query(''' + return result.map((row) => Client.fromMap(row.fields)).toList(); + } + + Future> checkPotentialDuplicates({ + required String nom, + required String prenom, + required String email, + required String telephone, + }) async { + final db = await database; + + final result = await db.query(''' SELECT * FROM clients WHERE actif = 1 AND ( (LOWER(nom) = ? AND LOWER(prenom) = ?) OR @@ -1800,42 +1757,43 @@ Future> checkPotentialDuplicates({ ) ORDER BY nom ASC, prenom ASC ''', [ - nom.trim().toLowerCase(), - prenom.trim().toLowerCase(), - email.trim().toLowerCase(), - telephone.trim() - ]); - - return result.map((row) => Client.fromMap(row.fields)).toList(); -} + nom.trim().toLowerCase(), + prenom.trim().toLowerCase(), + email.trim().toLowerCase(), + telephone.trim() + ]); -Future createOrGetClient(Client newClient) async { - final existingClient = await findExistingClient( - email: newClient.email, - telephone: newClient.telephone, - nom: newClient.nom, - prenom: newClient.prenom, - ); - - if (existingClient != null) { - return existingClient; - } - - final clientId = await createClient(newClient); - final createdClient = await getClientById(clientId); - - if (createdClient != null) { - return createdClient; - } else { - throw Exception("Erreur lors de la création du client"); + return result.map((row) => Client.fromMap(row.fields)).toList(); + } + + Future createOrGetClient(Client newClient) async { + final existingClient = await findExistingClient( + email: newClient.email, + telephone: newClient.telephone, + nom: newClient.nom, + prenom: newClient.prenom, + ); + + if (existingClient != null) { + return existingClient; + } + + final clientId = await createClient(newClient); + final createdClient = await getClientById(clientId); + + if (createdClient != null) { + return createdClient; + } else { + throw Exception("Erreur lors de la création du client"); + } } -} // 2. Méthodes pour les produits -Future> getSimilarProducts(Product product, {int limit = 5}) async { - final db = await database; - - final result = await db.query(''' + Future> getSimilarProducts(Product product, + {int limit = 5}) async { + final db = await database; + + final result = await db.query(''' SELECT * FROM products WHERE id != ? @@ -1848,220 +1806,221 @@ Future> getSimilarProducts(Product product, {int limit = 5}) async name ASC LIMIT ? ''', [ - product.id, - product.category, - '%${product.name.split(' ').first}%', - product.category, - limit - ]); - - return result.map((row) => Product.fromMap(row.fields)).toList(); -} + product.id, + product.category, + '%${product.name.split(' ').first}%', + product.category, + limit + ]); + + return result.map((row) => Product.fromMap(row.fields)).toList(); + } // 3. Méthodes pour les commandes -Future updateStatutCommande(int commandeId, StatutCommande statut) async { - final db = await database; - final result = await db.query( - 'UPDATE commandes SET statut = ? WHERE id = ?', - [statut.index, commandeId] - ); - return result.affectedRows!; -} + Future updateStatutCommande( + int commandeId, StatutCommande statut) async { + final db = await database; + final result = await db.query( + 'UPDATE commandes SET statut = ? WHERE id = ?', + [statut.index, commandeId]); + return result.affectedRows!; + } -Future> getCommandesByClient(int clientId) async { - final db = await database; - final result = await db.query(''' + Future> getCommandesByClient(int clientId) async { + final db = await database; + final result = await db.query(''' SELECT c.*, cl.nom as clientNom, cl.prenom as clientPrenom, cl.email as clientEmail FROM commandes c LEFT JOIN clients cl ON c.clientId = cl.id WHERE c.clientId = ? ORDER BY c.dateCommande DESC ''', [clientId]); - - return result.map((row) => Commande.fromMap(row.fields)).toList(); -} -Future> getCommandesByStatut(StatutCommande statut) async { - final db = await database; - final result = await db.query(''' + return result.map((row) => Commande.fromMap(row.fields)).toList(); + } + + Future> getCommandesByStatut(StatutCommande statut) async { + final db = await database; + final result = await db.query(''' SELECT c.*, cl.nom as clientNom, cl.prenom as clientPrenom, cl.email as clientEmail FROM commandes c LEFT JOIN clients cl ON c.clientId = cl.id WHERE c.statut = ? ORDER BY c.dateCommande DESC ''', [statut.index]); - - return result.map((row) => Commande.fromMap(row.fields)).toList(); -} -Future updateValidateurCommande(int commandeId, int validateurId) async { - final db = await database; - final result = await db.query(''' + return result.map((row) => Commande.fromMap(row.fields)).toList(); + } + + Future updateValidateurCommande(int commandeId, int validateurId) async { + final db = await database; + final result = await db.query(''' UPDATE commandes SET validateurId = ?, statut = ? WHERE id = ? ''', [validateurId, StatutCommande.confirmee.index, commandeId]); - - return result.affectedRows!; -} + + return result.affectedRows!; + } // --- CRUD MENUS --- // Ajoutez ces méthodes dans votre classe AppDatabase -Future>> getAllMenus() async { - final db = await database; - try { - final result = await db.query('SELECT * FROM menu ORDER BY name ASC'); - return result.map((row) => row.fields).toList(); - } catch (e) { - print("Erreur lors de la récupération des menus: $e"); - return []; + Future>> getAllMenus() async { + final db = await database; + try { + final result = await db.query('SELECT * FROM menu ORDER BY name ASC'); + return result.map((row) => row.fields).toList(); + } catch (e) { + print("Erreur lors de la récupération des menus: $e"); + return []; + } } -} -Future?> getMenuById(int id) async { - final db = await database; - try { - final result = await db.query('SELECT * FROM menu WHERE id = ? LIMIT 1', [id]); - return result.isNotEmpty ? result.first.fields : null; - } catch (e) { - print("Erreur getMenuById: $e"); - return null; + Future?> getMenuById(int id) async { + final db = await database; + try { + final result = + await db.query('SELECT * FROM menu WHERE id = ? LIMIT 1', [id]); + return result.isNotEmpty ? result.first.fields : null; + } catch (e) { + print("Erreur getMenuById: $e"); + return null; + } } -} -Future?> getMenuByRoute(String route) async { - final db = await database; - try { - final result = await db.query('SELECT * FROM menu WHERE route = ? LIMIT 1', [route]); - return result.isNotEmpty ? result.first.fields : null; - } catch (e) { - print("Erreur getMenuByRoute: $e"); - return null; + Future?> getMenuByRoute(String route) async { + final db = await database; + try { + final result = + await db.query('SELECT * FROM menu WHERE route = ? LIMIT 1', [route]); + return result.isNotEmpty ? result.first.fields : null; + } catch (e) { + print("Erreur getMenuByRoute: $e"); + return null; + } } -} -Future createMenu(String name, String route) async { - final db = await database; - try { - // Vérifier si le menu existe déjà - final existing = await db.query('SELECT COUNT(*) as count FROM menu WHERE route = ?', [route]); - final count = existing.first['count'] as int; - - if (count > 0) { - throw Exception('Un menu avec cette route existe déjà'); - } - - final result = await db.query( - 'INSERT INTO menu (name, route) VALUES (?, ?)', - [name, route] - ); - return result.insertId!; - } catch (e) { - print("Erreur createMenu: $e"); - rethrow; + Future createMenu(String name, String route) async { + final db = await database; + try { + // Vérifier si le menu existe déjà + final existing = await db + .query('SELECT COUNT(*) as count FROM menu WHERE route = ?', [route]); + final count = existing.first['count'] as int; + + if (count > 0) { + throw Exception('Un menu avec cette route existe déjà'); + } + + final result = await db + .query('INSERT INTO menu (name, route) VALUES (?, ?)', [name, route]); + return result.insertId!; + } catch (e) { + print("Erreur createMenu: $e"); + rethrow; + } } -} -Future updateMenu(int id, String name, String route) async { - final db = await database; - try { - final result = await db.query( - 'UPDATE menu SET name = ?, route = ? WHERE id = ?', - [name, route, id] - ); - return result.affectedRows!; - } catch (e) { - print("Erreur updateMenu: $e"); - rethrow; + Future updateMenu(int id, String name, String route) async { + final db = await database; + try { + final result = await db.query( + 'UPDATE menu SET name = ?, route = ? WHERE id = ?', + [name, route, id]); + return result.affectedRows!; + } catch (e) { + print("Erreur updateMenu: $e"); + rethrow; + } } -} -Future deleteMenu(int id) async { - final db = await database; - try { - // D'abord supprimer les permissions associées - await db.query('DELETE FROM role_menu_permissions WHERE menu_id = ?', [id]); - - // Ensuite supprimer le menu - final result = await db.query('DELETE FROM menu WHERE id = ?', [id]); - return result.affectedRows!; - } catch (e) { - print("Erreur deleteMenu: $e"); - rethrow; + Future deleteMenu(int id) async { + final db = await database; + try { + // D'abord supprimer les permissions associées + await db + .query('DELETE FROM role_menu_permissions WHERE menu_id = ?', [id]); + + // Ensuite supprimer le menu + final result = await db.query('DELETE FROM menu WHERE id = ?', [id]); + return result.affectedRows!; + } catch (e) { + print("Erreur deleteMenu: $e"); + rethrow; + } } -} -Future>> getMenusForRole(int roleId) async { - final db = await database; - try { - final result = await db.query(''' + Future>> getMenusForRole(int roleId) async { + final db = await database; + try { + final result = await db.query(''' SELECT DISTINCT m.* FROM menu m INNER JOIN role_menu_permissions rmp ON m.id = rmp.menu_id WHERE rmp.role_id = ? ORDER BY m.name ASC ''', [roleId]); - - return result.map((row) => row.fields).toList(); - } catch (e) { - print("Erreur getMenusForRole: $e"); - return []; + + return result.map((row) => row.fields).toList(); + } catch (e) { + print("Erreur getMenusForRole: $e"); + return []; + } } -} -Future hasMenuAccess(int roleId, String menuRoute) async { - final db = await database; - try { - final result = await db.query(''' + Future hasMenuAccess(int roleId, String menuRoute) async { + final db = await database; + try { + final result = await db.query(''' SELECT COUNT(*) as count FROM role_menu_permissions rmp INNER JOIN menu m ON rmp.menu_id = m.id WHERE rmp.role_id = ? AND m.route = ? ''', [roleId, menuRoute]); - - return (result.first['count'] as int) > 0; - } catch (e) { - print("Erreur hasMenuAccess: $e"); - return false; + + return (result.first['count'] as int) > 0; + } catch (e) { + print("Erreur hasMenuAccess: $e"); + return false; + } + } + + Future findClientByAnyIdentifier({ + String? email, + String? telephone, + String? nom, + String? prenom, + }) async { + // Recherche par email si fourni + if (email != null && email.isNotEmpty) { + final client = await getClientByEmail(email); + if (client != null) return client; + } + + // Recherche par téléphone si fourni + if (telephone != null && telephone.isNotEmpty) { + final client = await getClientByTelephone(telephone); + if (client != null) return client; + } + + // Recherche par nom et prénom si fournis + if (nom != null && nom.isNotEmpty && prenom != null && prenom.isNotEmpty) { + final client = await getClientByNomPrenom(nom, prenom); + if (client != null) return client; + } + + return null; } -} -Future findClientByAnyIdentifier({ - String? email, - String? telephone, - String? nom, - String? prenom, -}) async { - - // Recherche par email si fourni - if (email != null && email.isNotEmpty) { - final client = await getClientByEmail(email); - if (client != null) return client; - } - - // Recherche par téléphone si fourni - if (telephone != null && telephone.isNotEmpty) { - final client = await getClientByTelephone(telephone); - if (client != null) return client; - } - - // Recherche par nom et prénom si fournis - if (nom != null && nom.isNotEmpty && prenom != null && prenom.isNotEmpty) { - final client = await getClientByNomPrenom(nom, prenom); - if (client != null) return client; - } - - return null; -} // // Méthode pour obtenir les statistiques des cadeaux -Future> getCadeauStatistics() async { - final db = await database; - - try { - // Total des cadeaux offerts - final totalCadeauxResult = await db.query(''' + Future> getCadeauStatistics() async { + final db = await database; + + try { + // Total des cadeaux offerts + final totalCadeauxResult = await db.query(''' SELECT COUNT(*) as nombre_cadeaux, SUM(sousTotal) as valeur_totale_cadeaux, @@ -2070,9 +2029,9 @@ Future> getCadeauStatistics() async { FROM details_commandes WHERE est_cadeau = 1 '''); - - // Cadeaux par produit - final cadeauxParProduitResult = await db.query(''' + + // Cadeaux par produit + final cadeauxParProduitResult = await db.query(''' SELECT p.name as produit_nom, p.category as produit_categorie, @@ -2086,9 +2045,9 @@ Future> getCadeauStatistics() async { ORDER BY quantite_totale_offerte DESC LIMIT 10 '''); - - // Commandes avec cadeaux - final commandesAvecCadeauxResult = await db.query(''' + + // Commandes avec cadeaux + final commandesAvecCadeauxResult = await db.query(''' SELECT COUNT(DISTINCT c.id) as nombre_commandes_avec_cadeaux, AVG(cadeau_stats.nombre_cadeaux_par_commande) as moyenne_cadeaux_par_commande, @@ -2104,9 +2063,9 @@ Future> getCadeauStatistics() async { GROUP BY commandeId ) cadeau_stats ON c.id = cadeau_stats.commandeId '''); - - // Évolution des cadeaux par mois - final evolutionMensuelleResult = await db.query(''' + + // Évolution des cadeaux par mois + final evolutionMensuelleResult = await db.query(''' SELECT DATE_FORMAT(c.dateCommande, '%Y-%m') as mois, COUNT(dc.id) as nombre_cadeaux, @@ -2119,29 +2078,42 @@ Future> getCadeauStatistics() async { ORDER BY mois DESC LIMIT 12 '''); - - return { - 'total_cadeaux': totalCadeauxResult.first.fields, - 'cadeaux_par_produit': cadeauxParProduitResult.map((row) => row.fields).toList(), - 'commandes_avec_cadeaux': commandesAvecCadeauxResult.first.fields, - 'evolution_mensuelle': evolutionMensuelleResult.map((row) => row.fields).toList(), - }; - } catch (e) { - print("Erreur lors du calcul des statistiques de cadeaux: $e"); - return { - 'total_cadeaux': {'nombre_cadeaux': 0, 'valeur_totale_cadeaux': 0.0, 'valeur_moyenne_cadeau': 0.0, 'quantite_totale_cadeaux': 0}, - 'cadeaux_par_produit': [], - 'commandes_avec_cadeaux': {'nombre_commandes_avec_cadeaux': 0, 'moyenne_cadeaux_par_commande': 0.0, 'valeur_moyenne_cadeaux_par_commande': 0.0}, - 'evolution_mensuelle': [], - }; + + return { + 'total_cadeaux': totalCadeauxResult.first.fields, + 'cadeaux_par_produit': + cadeauxParProduitResult.map((row) => row.fields).toList(), + 'commandes_avec_cadeaux': commandesAvecCadeauxResult.first.fields, + 'evolution_mensuelle': + evolutionMensuelleResult.map((row) => row.fields).toList(), + }; + } catch (e) { + print("Erreur lors du calcul des statistiques de cadeaux: $e"); + return { + 'total_cadeaux': { + 'nombre_cadeaux': 0, + 'valeur_totale_cadeaux': 0.0, + 'valeur_moyenne_cadeau': 0.0, + 'quantite_totale_cadeaux': 0 + }, + 'cadeaux_par_produit': [], + 'commandes_avec_cadeaux': { + 'nombre_commandes_avec_cadeaux': 0, + 'moyenne_cadeaux_par_commande': 0.0, + 'valeur_moyenne_cadeaux_par_commande': 0.0 + }, + 'evolution_mensuelle': [], + }; + } } -} + // Méthode pour obtenir les commandes avec des cadeaux -Future>> getCommandesAvecCadeaux({int limit = 20}) async { - final db = await database; - - try { - final result = await db.query(''' + Future>> getCommandesAvecCadeaux( + {int limit = 20}) async { + final db = await database; + + try { + final result = await db.query(''' SELECT c.id as commande_id, c.dateCommande, @@ -2167,19 +2139,21 @@ Future>> getCommandesAvecCadeaux({int limit = 20}) asy ORDER BY cadeau_stats.valeur_cadeaux DESC LIMIT ? ''', [limit]); - - return result.map((row) => row.fields).toList(); - } catch (e) { - print("Erreur lors de la récupération des commandes avec cadeaux: $e"); - return []; + + return result.map((row) => row.fields).toList(); + } catch (e) { + print("Erreur lors de la récupération des commandes avec cadeaux: $e"); + return []; + } } -} + // Méthode pour obtenir les produits les plus offerts en cadeau -Future>> getProduitsLesPlusOffertsEnCadeau({int limit = 10}) async { - final db = await database; - - try { - final result = await db.query(''' + Future>> getProduitsLesPlusOffertsEnCadeau( + {int limit = 10}) async { + final db = await database; + + try { + final result = await db.query(''' SELECT p.id, p.name as produit_nom, @@ -2197,19 +2171,21 @@ Future>> getProduitsLesPlusOffertsEnCadeau({int limit ORDER BY quantite_totale_offerte DESC LIMIT ? ''', [limit]); - - return result.map((row) => row.fields).toList(); - } catch (e) { - print("Erreur lors de la récupération des produits les plus offerts: $e"); - return []; + + return result.map((row) => row.fields).toList(); + } catch (e) { + print("Erreur lors de la récupération des produits les plus offerts: $e"); + return []; + } } -} + // Méthode pour obtenir les clients qui ont reçu le plus de cadeaux -Future>> getClientsAvecLePlusDeCadeaux({int limit = 10}) async { - final db = await database; - - try { - final result = await db.query(''' + Future>> getClientsAvecLePlusDeCadeaux( + {int limit = 10}) async { + final db = await database; + + try { + final result = await db.query(''' SELECT cl.id as client_id, cl.nom, @@ -2228,20 +2204,22 @@ Future>> getClientsAvecLePlusDeCadeaux({int limit = 10 ORDER BY valeur_cadeaux_recus DESC LIMIT ? ''', [limit]); - - return result.map((row) => row.fields).toList(); - } catch (e) { - print("Erreur lors de la récupération des clients avec le plus de cadeaux: $e"); - return []; + + return result.map((row) => row.fields).toList(); + } catch (e) { + print( + "Erreur lors de la récupération des clients avec le plus de cadeaux: $e"); + return []; + } } -} + // Méthode pour calculer l'impact des cadeaux sur les ventes -Future> getImpactCadeauxSurVentes() async { - final db = await database; - - try { - // Comparaison des commandes avec et sans cadeaux - final comparisonResult = await db.query(''' + Future> getImpactCadeauxSurVentes() async { + final db = await database; + + try { + // Comparaison des commandes avec et sans cadeaux + final comparisonResult = await db.query(''' SELECT 'avec_cadeaux' as type_commande, COUNT(DISTINCT c.id) as nombre_commandes, @@ -2266,9 +2244,9 @@ Future> getImpactCadeauxSurVentes() async { WHERE dc.commandeId = c.id AND dc.est_cadeau = 1 ) '''); - - // Ratio de conversion (commandes avec cadeaux / total commandes) - final ratioResult = await db.query(''' + + // Ratio de conversion (commandes avec cadeaux / total commandes) + final ratioResult = await db.query(''' SELECT (SELECT COUNT(DISTINCT c.id) FROM commandes c @@ -2279,116 +2257,120 @@ Future> getImpactCadeauxSurVentes() async { ) * 100.0 / COUNT(*) as pourcentage_commandes_avec_cadeaux FROM commandes '''); - - return { - 'comparaison': comparisonResult.map((row) => row.fields).toList(), - 'pourcentage_commandes_avec_cadeaux': ratioResult.first['pourcentage_commandes_avec_cadeaux'] ?? 0.0, - }; - } catch (e) { - print("Erreur lors du calcul de l'impact des cadeaux: $e"); - return { - 'comparaison': [], - 'pourcentage_commandes_avec_cadeaux': 0.0, - }; + + return { + 'comparaison': comparisonResult.map((row) => row.fields).toList(), + 'pourcentage_commandes_avec_cadeaux': + ratioResult.first['pourcentage_commandes_avec_cadeaux'] ?? 0.0, + }; + } catch (e) { + print("Erreur lors du calcul de l'impact des cadeaux: $e"); + return { + 'comparaison': [], + 'pourcentage_commandes_avec_cadeaux': 0.0, + }; + } } -} // Méthode pour créer une commande complète avec cadeaux (mise à jour) -Future createCommandeCompleteAvecCadeaux(Client client, Commande commande, List details) async { - final db = await database; - - try { - await db.query('START TRANSACTION'); - - // 1. Créer ou récupérer le client - final existingOrNewClient = await createOrGetClient(client); - final clientId = existingOrNewClient.id!; - - // 2. Créer la commande - final commandeMap = commande.toMap(); - commandeMap.remove('id'); - commandeMap['clientId'] = clientId; - - final commandeFields = commandeMap.keys.join(', '); - final commandePlaceholders = List.filled(commandeMap.length, '?').join(', '); - - final commandeResult = await db.query( - 'INSERT INTO commandes ($commandeFields) VALUES ($commandePlaceholders)', - commandeMap.values.toList() - ); - final commandeId = commandeResult.insertId!; - - // 3. Créer les détails de commande avec remises et cadeaux - for (final detail in details) { - final detailMap = detail.toMap(); - detailMap.remove('id'); - detailMap['commandeId'] = commandeId; - - final detailFields = detailMap.keys.join(', '); - final detailPlaceholders = List.filled(detailMap.length, '?').join(', '); - - await db.query( - 'INSERT INTO details_commandes ($detailFields) VALUES ($detailPlaceholders)', - detailMap.values.toList() - ); - - // 4. Mettre à jour le stock (même pour les cadeaux) - await db.query( - 'UPDATE products SET stock = stock - ? WHERE id = ?', - [detail.quantite, detail.produitId] - ); - } - - await db.query('COMMIT'); - - // Log des cadeaux offerts (optionnel) - final cadeaux = details.where((d) => d.estCadeau).toList(); - if (cadeaux.isNotEmpty) { - print("Cadeaux offerts dans la commande $commandeId:"); - for (final cadeau in cadeaux) { - print(" - ${cadeau.produitNom} x${cadeau.quantite} (valeur: ${cadeau.sousTotal.toStringAsFixed(2)} MGA)"); + Future createCommandeCompleteAvecCadeaux( + Client client, Commande commande, List details) async { + final db = await database; + + try { + await db.query('START TRANSACTION'); + + // 1. Créer ou récupérer le client + final existingOrNewClient = await createOrGetClient(client); + final clientId = existingOrNewClient.id!; + + // 2. Créer la commande + final commandeMap = commande.toMap(); + commandeMap.remove('id'); + commandeMap['clientId'] = clientId; + + final commandeFields = commandeMap.keys.join(', '); + final commandePlaceholders = + List.filled(commandeMap.length, '?').join(', '); + + final commandeResult = await db.query( + 'INSERT INTO commandes ($commandeFields) VALUES ($commandePlaceholders)', + commandeMap.values.toList()); + final commandeId = commandeResult.insertId!; + + // 3. Créer les détails de commande avec remises et cadeaux + for (final detail in details) { + final detailMap = detail.toMap(); + detailMap.remove('id'); + detailMap['commandeId'] = commandeId; + + final detailFields = detailMap.keys.join(', '); + final detailPlaceholders = + List.filled(detailMap.length, '?').join(', '); + + await db.query( + 'INSERT INTO details_commandes ($detailFields) VALUES ($detailPlaceholders)', + detailMap.values.toList()); + + // 4. Mettre à jour le stock (même pour les cadeaux) + await db.query('UPDATE products SET stock = stock - ? WHERE id = ?', + [detail.quantite, detail.produitId]); } + + await db.query('COMMIT'); + + // Log des cadeaux offerts (optionnel) + final cadeaux = details.where((d) => d.estCadeau).toList(); + if (cadeaux.isNotEmpty) { + print("Cadeaux offerts dans la commande $commandeId:"); + for (final cadeau in cadeaux) { + print( + " - ${cadeau.produitNom} x${cadeau.quantite} (valeur: ${cadeau.sousTotal.toStringAsFixed(2)} MGA)"); + } + } + + return commandeId; + } catch (e) { + await db.query('ROLLBACK'); + print( + "Erreur lors de la création de la commande complète avec cadeaux: $e"); + rethrow; } - - return commandeId; - } catch (e) { - await db.query('ROLLBACK'); - print("Erreur lors de la création de la commande complète avec cadeaux: $e"); - rethrow; } -} // Méthode pour valider la disponibilité des cadeaux avant la commande -Future> verifierDisponibiliteCadeaux(List details) async { - final db = await database; - List erreurs = []; - - try { - for (final detail in details.where((d) => d.estCadeau)) { - final produit = await getProductById(detail.produitId); - - if (produit == null) { - erreurs.add("Produit cadeau introuvable (ID: ${detail.produitId})"); - continue; - } - - if (produit.stock != null && produit.stock! < detail.quantite) { - erreurs.add("Stock insuffisant pour le cadeau: ${produit.name} (demandé: ${detail.quantite}, disponible: ${produit.stock})"); + Future> verifierDisponibiliteCadeaux( + List details) async { + final db = await database; + List erreurs = []; + + try { + for (final detail in details.where((d) => d.estCadeau)) { + final produit = await getProductById(detail.produitId); + + if (produit == null) { + erreurs.add("Produit cadeau introuvable (ID: ${detail.produitId})"); + continue; + } + + if (produit.stock != null && produit.stock! < detail.quantite) { + erreurs.add( + "Stock insuffisant pour le cadeau: ${produit.name} (demandé: ${detail.quantite}, disponible: ${produit.stock})"); + } } + } catch (e) { + erreurs.add("Erreur lors de la vérification des cadeaux: $e"); } - } catch (e) { - erreurs.add("Erreur lors de la vérification des cadeaux: $e"); + + return erreurs; } - - return erreurs; -} // --- MÉTHODES POUR LES VENTES PAR POINT DE VENTE --- -Future>> getVentesParPointDeVente() async { - final db = await database; - - try { - final result = await db.query(''' + Future>> getVentesParPointDeVente() async { + final db = await database; + + try { + final result = await db.query(''' SELECT pv.id as point_vente_id, pv.nom as point_vente_nom, @@ -2407,19 +2389,21 @@ Future>> getVentesParPointDeVente() async { GROUP BY pv.id, pv.nom ORDER BY chiffre_affaires DESC '''); - - return result.map((row) => row.fields).toList(); - } catch (e) { - print("Erreur getVentesParPointDeVente: $e"); - return []; + + return result.map((row) => row.fields).toList(); + } catch (e) { + print("Erreur getVentesParPointDeVente: $e"); + return []; + } } -} -Future>> getTopProduitsParPointDeVente(int pointDeVenteId, {int limit = 5}) async { - final db = await database; - - try { - final result = await db.query(''' + Future>> getTopProduitsParPointDeVente( + int pointDeVenteId, + {int limit = 5}) async { + final db = await database; + + try { + final result = await db.query(''' SELECT p.id, p.name as produit_nom, @@ -2436,19 +2420,20 @@ Future>> getTopProduitsParPointDeVente(int pointDeVent ORDER BY quantite_vendue DESC LIMIT ? ''', [pointDeVenteId, limit]); - - return result.map((row) => row.fields).toList(); - } catch (e) { - print("Erreur getTopProduitsParPointDeVente: $e"); - return []; + + return result.map((row) => row.fields).toList(); + } catch (e) { + print("Erreur getTopProduitsParPointDeVente: $e"); + return []; + } } -} -Future>> getVentesParPointDeVenteParMois(int pointDeVenteId) async { - final db = await database; - - try { - final result = await db.query(''' + Future>> getVentesParPointDeVenteParMois( + int pointDeVenteId) async { + final db = await database; + + try { + final result = await db.query(''' SELECT DATE_FORMAT(c.dateCommande, '%Y-%m') as mois, COUNT(DISTINCT c.id) as nombre_commandes, @@ -2464,45 +2449,46 @@ Future>> getVentesParPointDeVenteParMois(int pointDeVe ORDER BY mois DESC LIMIT 12 ''', [pointDeVenteId]); - - return result.map((row) => row.fields).toList(); - } catch (e) { - print("Erreur getVentesParPointDeVenteParMois: $e"); - return []; + + return result.map((row) => row.fields).toList(); + } catch (e) { + print("Erreur getVentesParPointDeVenteParMois: $e"); + return []; + } } -} + // Dans la classe AppDatabase, ajoutez cette méthode : -Future verifyCurrentUserPassword(String password) async { - final db = await database; - final userController = Get.find(); - - try { - final result = await db.query(''' + Future verifyCurrentUserPassword(String password) async { + final db = await database; + final userController = Get.find(); + + try { + final result = await db.query(''' SELECT COUNT(*) as count FROM users WHERE id = ? AND password = ? ''', [userController.userId, password]); - - return (result.first['count'] as int) > 0; - } catch (e) { - print("Erreur lors de la vérification du mot de passe: $e"); - return false; + + return (result.first['count'] as int) > 0; + } catch (e) { + print("Erreur lors de la vérification du mot de passe: $e"); + return false; + } } -} // Dans AppDatabase -Future createDemandeTransfert({ - required int produitId, - required int pointDeVenteSourceId, - required int pointDeVenteDestinationId, - required int demandeurId, - int quantite = 1, - String? notes, -}) async { - final db = await database; - - try { - final result = await db.query(''' + Future createDemandeTransfert({ + required int produitId, + required int pointDeVenteSourceId, + required int pointDeVenteDestinationId, + required int demandeurId, + int quantite = 1, + String? notes, + }) async { + final db = await database; + + try { + final result = await db.query(''' INSERT INTO demandes_transfert ( produit_id, point_de_vente_source_id, @@ -2514,26 +2500,27 @@ Future createDemandeTransfert({ notes ) VALUES (?, ?, ?, ?, ?, ?, ?, ?) ''', [ - produitId, - pointDeVenteSourceId, - pointDeVenteDestinationId, - demandeurId, - quantite, - 'en_attente', // Statut initial - DateTime.now().toUtc(), - notes, - ]); - - return result.insertId!; - } catch (e) { - print('Erreur création demande transfert: $e'); - rethrow; + produitId, + pointDeVenteSourceId, + pointDeVenteDestinationId, + demandeurId, + quantite, + 'en_attente', // Statut initial + DateTime.now().toUtc(), + notes, + ]); + + return result.insertId!; + } catch (e) { + print('Erreur création demande transfert: $e'); + rethrow; + } } -} -Future>> getDemandesTransfertEnAttente() async { - final db = await database; - try { - final result = await db.query(''' + + Future>> getDemandesTransfertEnAttente() async { + final db = await database; + try { + final result = await db.query(''' SELECT dt.*, p.name as produit_nom, p.reference as produit_reference, @@ -2549,149 +2536,136 @@ Future>> getDemandesTransfertEnAttente() async { WHERE dt.statut = 'en_attente' ORDER BY dt.date_demande DESC '''); - - return result.map((row) => row.fields).toList(); - } catch (e) { - print('Erreur récupération demandes transfert: $e'); - return []; + + return result.map((row) => row.fields).toList(); + } catch (e) { + print('Erreur récupération demandes transfert: $e'); + return []; + } } -} -Future validerTransfert(int demandeId, int validateurId) async { - final db = await database; - - try { - await db.query('START TRANSACTION'); - - // 1. Récupérer les infos de la demande - final demande = await db.query( - 'SELECT * FROM demandes_transfert WHERE id = ? FOR UPDATE', - [demandeId] - ); - - if (demande.isEmpty) { - throw Exception('Demande de transfert introuvable'); - } - - final fields = demande.first.fields; - final produitId = fields['produit_id'] as int; - final quantite = fields['quantite'] as int; - final sourceId = fields['point_de_vente_source_id'] as int; - final destinationId = fields['point_de_vente_destination_id'] as int; - - - final getpointDeventeSource = await db.query( - 'Select point_de_vente_source_id FROM demandes_transfert WHERE id=?',[demandeId] - ); - final getpointDeventeDest = await db.query( - 'Select point_de_vente_destination_id FROM demandes_transfert WHERE id=?',[demandeId] - ); - final getpointDeventeSourceValue = getpointDeventeSource.first.fields['point_de_vente_source_id']; - final getpointDeventedestValue = getpointDeventeDest.first.fields['point_de_vente_destination_id']; - if(getpointDeventeSourceValue==getpointDeventedestValue){ - await db.query('update products set point_de_vente_id=? where id = ?',[getpointDeventedestValue,produitId]); - }else{ + Future validerTransfert(int demandeId, int validateurId) async { + final db = await database; + + try { + await db.query('START TRANSACTION'); + + // 1. Récupérer les infos de la demande + final demande = await db.query( + 'SELECT * FROM demandes_transfert WHERE id = ? FOR UPDATE', + [demandeId]); + + if (demande.isEmpty) { + throw Exception('Demande de transfert introuvable'); + } + + final fields = demande.first.fields; + final produitId = fields['produit_id'] as int; + final quantite = fields['quantite'] as int; + final sourceId = fields['point_de_vente_source_id'] as int; + final destinationId = fields['point_de_vente_destination_id'] as int; + + final getpointDeventeSource = await db.query( + 'Select point_de_vente_source_id FROM demandes_transfert WHERE id=?', + [demandeId]); + final getpointDeventeDest = await db.query( + 'Select point_de_vente_destination_id FROM demandes_transfert WHERE id=?', + [demandeId]); + final getpointDeventeSourceValue = + getpointDeventeSource.first.fields['point_de_vente_source_id']; + final getpointDeventedestValue = + getpointDeventeDest.first.fields['point_de_vente_destination_id']; + if (getpointDeventeSourceValue == getpointDeventedestValue) { + await db.query('update products set point_de_vente_id=? where id = ?', + [getpointDeventedestValue, produitId]); + } else { + // 2. Vérifier le stock source + final stockSource = await db.query( + 'SELECT stock FROM products WHERE id = ? AND point_de_vente_id = ? FOR UPDATE', + [produitId, sourceId]); - + if (stockSource.isEmpty) { + throw Exception('Produit introuvable dans le point de vente source'); + } + final stockDisponible = stockSource.first['stock'] as int; + if (stockDisponible < quantite) { + throw Exception('Stock insuffisant dans le point de vente source'); + } - // 2. Vérifier le stock source - final stockSource = await db.query( - 'SELECT stock FROM products WHERE id = ? AND point_de_vente_id = ? FOR UPDATE', - [produitId, sourceId] - ); - - if (stockSource.isEmpty) { - throw Exception('Produit introuvable dans le point de vente source'); - } - - final stockDisponible = stockSource.first['stock'] as int; - if (stockDisponible < quantite) { - throw Exception('Stock insuffisant dans le point de vente source'); - } - - // 3. Mettre à jour le stock source - await db.query( - 'UPDATE products SET stock = stock - ? WHERE id = ? AND point_de_vente_id = ?', - [quantite, produitId, sourceId] - ); - - // 4. Vérifier si le produit existe déjà dans le point de vente destination - final produitDestination = await db.query( - 'SELECT id, stock FROM products WHERE id = ? AND point_de_vente_id = ?', - [produitId, destinationId] - ); - - if (produitDestination.isNotEmpty) { - // Mettre à jour le stock existant - await db.query( - 'UPDATE products SET stock = stock + ? WHERE id = ? AND point_de_vente_id = ?', - [quantite, produitId, destinationId] - ); - } else { - // Créer une copie du produit dans le nouveau point de vente - final produit = await db.query( - 'SELECT * FROM products WHERE id = ?', - [produitId] - ); - - if (produit.isEmpty) { - throw Exception('Produit introuvable'); - } - - final produitFields = produit.first.fields; - await db.query(''' + // 3. Mettre à jour le stock source + await db.query( + 'UPDATE products SET stock = stock - ? WHERE id = ? AND point_de_vente_id = ?', + [quantite, produitId, sourceId]); + + // 4. Vérifier si le produit existe déjà dans le point de vente destination + final produitDestination = await db.query( + 'SELECT id, stock FROM products WHERE id = ? AND point_de_vente_id = ?', + [produitId, destinationId]); + + if (produitDestination.isNotEmpty) { + // Mettre à jour le stock existant + await db.query( + 'UPDATE products SET stock = stock + ? WHERE id = ? AND point_de_vente_id = ?', + [quantite, produitId, destinationId]); + } else { + // Créer une copie du produit dans le nouveau point de vente + final produit = await db + .query('SELECT * FROM products WHERE id = ?', [produitId]); + + if (produit.isEmpty) { + throw Exception('Produit introuvable'); + } + + final produitFields = produit.first.fields; + await db.query(''' INSERT INTO products ( name, price, image, category, stock, description, qrCode, reference, point_de_vente_id, marque, ram, memoire_interne, imei ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) ''', [ - produitFields['name'], - produitFields['price'], - produitFields['image'], - produitFields['category'], - quantite, // Nouveau stock - produitFields['description'], - produitFields['qrCode'], - produitFields['reference'], - destinationId, - produitFields['marque'], - produitFields['ram'], - produitFields['memoire_interne'], - null, // IMEI doit être unique donc on ne le copie pas - ]); - } - } - // 5. Mettre à jour le statut de la demande - await db.query(''' + produitFields['name'], + produitFields['price'], + produitFields['image'], + produitFields['category'], + quantite, // Nouveau stock + produitFields['description'], + produitFields['qrCode'], + produitFields['reference'], + destinationId, + produitFields['marque'], + produitFields['ram'], + produitFields['memoire_interne'], + null, // IMEI doit être unique donc on ne le copie pas + ]); + } + } + // 5. Mettre à jour le statut de la demande + await db.query(''' UPDATE demandes_transfert SET statut = 'validee', validateur_id = ?, date_validation = ? WHERE id = ? - ''', [ - validateurId, - DateTime.now().toUtc(), - demandeId - ]); - - await db.query('COMMIT'); - return 1; - } catch (e) { - await db.query('ROLLBACK'); - print('Erreur validation transfert: $e'); - rethrow; + ''', [validateurId, DateTime.now().toUtc(), demandeId]); + + await db.query('COMMIT'); + return 1; + } catch (e) { + await db.query('ROLLBACK'); + print('Erreur validation transfert: $e'); + rethrow; + } } -} // Ajoutez ces méthodes dans votre classe AppDatabase // 1. Méthode pour récupérer les demandes de transfert validées -Future>> getDemandesTransfertValidees() async { - final db = await database; - try { - final result = await db.query(''' + Future>> getDemandesTransfertValidees() async { + final db = await database; + try { + final result = await db.query(''' SELECT dt.*, p.name as produit_nom, p.reference as produit_reference, @@ -2710,19 +2684,19 @@ Future>> getDemandesTransfertValidees() async { WHERE dt.statut = 'validee' ORDER BY dt.date_validation DESC '''); - - return result.map((row) => row.fields).toList(); - } catch (e) { - print('Erreur récupération demandes transfert validées: $e'); - return []; + + return result.map((row) => row.fields).toList(); + } catch (e) { + print('Erreur récupération demandes transfert validées: $e'); + return []; + } } -} // 2. Méthode pour récupérer toutes les demandes de transfert -Future>> getToutesDemandesTransfert() async { - final db = await database; - try { - final result = await db.query(''' + Future>> getToutesDemandesTransfert() async { + final db = await database; + try { + final result = await db.query(''' SELECT dt.*, p.name as produit_nom, p.reference as produit_reference, @@ -2746,33 +2720,33 @@ Future>> getToutesDemandesTransfert() async { END, dt.date_demande DESC '''); - - return result.map((row) => row.fields).toList(); - } catch (e) { - print('Erreur récupération toutes demandes transfert: $e'); - return []; + + return result.map((row) => row.fields).toList(); + } catch (e) { + print('Erreur récupération toutes demandes transfert: $e'); + return []; + } } -} // 3. Méthode pour rejeter une demande de transfert -Future rejeterTransfert(int demandeId, int validateurId, String motif) async { - final db = await database; - - try { - await db.query('START TRANSACTION'); - - // Vérifier que la demande existe et est en attente - final demande = await db.query( - 'SELECT * FROM demandes_transfert WHERE id = ? AND statut = ? FOR UPDATE', - [demandeId, 'en_attente'] - ); - - if (demande.isEmpty) { - throw Exception('Demande de transfert introuvable ou déjà traitée'); - } - - // Mettre à jour le statut de la demande - final result = await db.query(''' + Future rejeterTransfert( + int demandeId, int validateurId, String motif) async { + final db = await database; + + try { + await db.query('START TRANSACTION'); + + // Vérifier que la demande existe et est en attente + final demande = await db.query( + 'SELECT * FROM demandes_transfert WHERE id = ? AND statut = ? FOR UPDATE', + [demandeId, 'en_attente']); + + if (demande.isEmpty) { + throw Exception('Demande de transfert introuvable ou déjà traitée'); + } + + // Mettre à jour le statut de la demande + final result = await db.query(''' UPDATE demandes_transfert SET statut = 'refusee', @@ -2783,32 +2757,33 @@ Future rejeterTransfert(int demandeId, int validateurId, String motif) asyn 'Rejetée le ', ?, ' par validateur ID ', ?, ': ', ?) WHERE id = ? ''', [ - validateurId, - DateTime.now().toUtc(), - DateFormat('dd/MM/yyyy HH:mm').format(DateTime.now()), - validateurId, - motif, - demandeId - ]); - - await db.query('COMMIT'); - - print('Demande de transfert $demandeId rejetée par l\'utilisateur $validateurId'); - print('Motif: $motif'); - - return result.affectedRows!; - } catch (e) { - await db.query('ROLLBACK'); - print('Erreur rejet transfert: $e'); - rethrow; + validateurId, + DateTime.now().toUtc(), + DateFormat('dd/MM/yyyy HH:mm').format(DateTime.now()), + validateurId, + motif, + demandeId + ]); + + await db.query('COMMIT'); + + print( + 'Demande de transfert $demandeId rejetée par l\'utilisateur $validateurId'); + print('Motif: $motif'); + + return result.affectedRows!; + } catch (e) { + await db.query('ROLLBACK'); + print('Erreur rejet transfert: $e'); + rethrow; + } } -} // 4. Méthode supplémentaire : récupérer les demandes de transfert refusées -Future>> getDemandesTransfertRefusees() async { - final db = await database; - try { - final result = await db.query(''' + Future>> getDemandesTransfertRefusees() async { + final db = await database; + try { + final result = await db.query(''' SELECT dt.*, p.name as produit_nom, p.reference as produit_reference, @@ -2827,19 +2802,20 @@ Future>> getDemandesTransfertRefusees() async { WHERE dt.statut = 'refusee' ORDER BY dt.date_validation DESC '''); - - return result.map((row) => row.fields).toList(); - } catch (e) { - print('Erreur récupération demandes transfert refusées: $e'); - return []; + + return result.map((row) => row.fields).toList(); + } catch (e) { + print('Erreur récupération demandes transfert refusées: $e'); + return []; + } } -} // 5. Méthode pour récupérer les demandes par statut spécifique -Future>> getDemandesTransfertParStatut(String statut) async { - final db = await database; - try { - final result = await db.query(''' + Future>> getDemandesTransfertParStatut( + String statut) async { + final db = await database; + try { + final result = await db.query(''' SELECT dt.*, p.name as produit_nom, p.reference as produit_reference, @@ -2862,21 +2838,21 @@ Future>> getDemandesTransfertParStatut(String statut) ELSE dt.date_validation END DESC ''', [statut]); - - return result.map((row) => row.fields).toList(); - } catch (e) { - print('Erreur récupération demandes transfert par statut: $e'); - return []; + + return result.map((row) => row.fields).toList(); + } catch (e) { + print('Erreur récupération demandes transfert par statut: $e'); + return []; + } } -} // 6. Méthode pour récupérer les statistiques des transferts -Future> getStatistiquesTransferts() async { - final db = await database; - - try { - // Statistiques générales - final statsGenerales = await db.query(''' + Future> getStatistiquesTransferts() async { + final db = await database; + + try { + // Statistiques générales + final statsGenerales = await db.query(''' SELECT COUNT(*) as total_demandes, SUM(CASE WHEN statut = 'en_attente' THEN 1 ELSE 0 END) as en_attente, @@ -2885,9 +2861,9 @@ Future> getStatistiquesTransferts() async { SUM(CASE WHEN statut = 'validee' THEN quantite ELSE 0 END) as quantite_totale_transferee FROM demandes_transfert '''); - - // Top des produits les plus transférés - final topProduits = await db.query(''' + + // Top des produits les plus transférés + final topProduits = await db.query(''' SELECT p.name as produit_nom, p.category as categorie, @@ -2900,9 +2876,9 @@ Future> getStatistiquesTransferts() async { ORDER BY quantite_totale DESC LIMIT 10 '''); - - // Points de vente les plus actifs - final topPointsVente = await db.query(''' + + // Points de vente les plus actifs + final topPointsVente = await db.query(''' SELECT pv.nom as point_vente, COUNT(dt_source.id) as demandes_sortantes, @@ -2916,33 +2892,34 @@ Future> getStatistiquesTransferts() async { ORDER BY total_activite DESC LIMIT 10 '''); - - return { - 'stats_generales': statsGenerales.first.fields, - 'top_produits': topProduits.map((row) => row.fields).toList(), - 'top_points_vente': topPointsVente.map((row) => row.fields).toList(), - }; - } catch (e) { - print('Erreur récupération statistiques transferts: $e'); - return { - 'stats_generales': { - 'total_demandes': 0, - 'en_attente': 0, - 'validees': 0, - 'refusees': 0, - 'quantite_totale_transferee': 0 - }, - 'top_produits': [], - 'top_points_vente': [], - }; + + return { + 'stats_generales': statsGenerales.first.fields, + 'top_produits': topProduits.map((row) => row.fields).toList(), + 'top_points_vente': topPointsVente.map((row) => row.fields).toList(), + }; + } catch (e) { + print('Erreur récupération statistiques transferts: $e'); + return { + 'stats_generales': { + 'total_demandes': 0, + 'en_attente': 0, + 'validees': 0, + 'refusees': 0, + 'quantite_totale_transferee': 0 + }, + 'top_produits': [], + 'top_points_vente': [], + }; + } } -} // 7. Méthode pour récupérer l'historique des transferts d'un produit -Future>> getHistoriqueTransfertsProduit(int produitId) async { - final db = await database; - try { - final result = await db.query(''' + Future>> getHistoriqueTransfertsProduit( + int produitId) async { + final db = await database; + try { + final result = await db.query(''' SELECT dt.*, pv_source.nom as point_vente_source, pv_dest.nom as point_vente_destination, @@ -2956,69 +2933,69 @@ Future>> getHistoriqueTransfertsProduit(int produitId) WHERE dt.produit_id = ? ORDER BY dt.date_demande DESC ''', [produitId]); - - return result.map((row) => row.fields).toList(); - } catch (e) { - print('Erreur récupération historique transferts produit: $e'); - return []; + + return result.map((row) => row.fields).toList(); + } catch (e) { + print('Erreur récupération historique transferts produit: $e'); + return []; + } } -} // 8. Méthode pour annuler une demande de transfert (si en attente) -Future annulerDemandeTransfert(int demandeId, int utilisateurId) async { - final db = await database; - - try { - // Vérifier que la demande existe et est en attente - final demande = await db.query( - 'SELECT * FROM demandes_transfert WHERE id = ? AND statut = ? AND demandeur_id = ?', - [demandeId, 'en_attente', utilisateurId] - ); - - if (demande.isEmpty) { - throw Exception('Demande introuvable, déjà traitée, ou vous n\'êtes pas autorisé à l\'annuler'); + Future annulerDemandeTransfert(int demandeId, int utilisateurId) async { + final db = await database; + + try { + // Vérifier que la demande existe et est en attente + final demande = await db.query( + 'SELECT * FROM demandes_transfert WHERE id = ? AND statut = ? AND demandeur_id = ?', + [demandeId, 'en_attente', utilisateurId]); + + if (demande.isEmpty) { + throw Exception( + 'Demande introuvable, déjà traitée, ou vous n\'êtes pas autorisé à l\'annuler'); + } + + // Supprimer la demande (ou la marquer comme annulée si vous préférez garder l'historique) + final result = await db.query( + 'DELETE FROM demandes_transfert WHERE id = ? AND statut = ? AND demandeur_id = ?', + [demandeId, 'en_attente', utilisateurId]); + + return result.affectedRows!; + } catch (e) { + print('Erreur annulation demande transfert: $e'); + rethrow; } - - // Supprimer la demande (ou la marquer comme annulée si vous préférez garder l'historique) - final result = await db.query( - 'DELETE FROM demandes_transfert WHERE id = ? AND statut = ? AND demandeur_id = ?', - [demandeId, 'en_attente', utilisateurId] - ); - - return result.affectedRows!; - } catch (e) { - print('Erreur annulation demande transfert: $e'); - rethrow; } -} // --- MÉTHODES POUR SORTIES STOCK PERSONNELLES --- -Future createSortieStockPersonnelle({ - required int produitId, - required int adminId, - required int quantite, - required String motif, - int? pointDeVenteId, - String? notes, -}) async { - final db = await database; - - try { - await db.query('START TRANSACTION'); - - // 1. Vérifier que le produit existe et a assez de stock - final produit = await getProductById(produitId); - if (produit == null) { - throw Exception('Produit introuvable'); - } - - if (produit.stock != null && produit.stock! < quantite) { - throw Exception('Stock insuffisant (disponible: ${produit.stock}, demandé: $quantite)'); - } - - // 2. Créer la demande de sortie - final result = await db.query(''' + Future createSortieStockPersonnelle({ + required int produitId, + required int adminId, + required int quantite, + required String motif, + int? pointDeVenteId, + String? notes, + }) async { + final db = await database; + + try { + await db.query('START TRANSACTION'); + + // 1. Vérifier que le produit existe et a assez de stock + final produit = await getProductById(produitId); + if (produit == null) { + throw Exception('Produit introuvable'); + } + + if (produit.stock != null && produit.stock! < quantite) { + throw Exception( + 'Stock insuffisant (disponible: ${produit.stock}, demandé: $quantite)'); + } + + // 2. Créer la demande de sortie + final result = await db.query(''' INSERT INTO sorties_stock_personnelles ( produit_id, admin_id, @@ -3030,89 +3007,84 @@ Future createSortieStockPersonnelle({ statut ) VALUES (?, ?, ?, ?, ?, ?, ?, ?) ''', [ - produitId, - adminId, - quantite, - motif, - DateTime.now().toUtc(), - pointDeVenteId, - notes, - 'en_attente', // Par défaut en attente d'approbation - ]); - - await db.query('COMMIT'); - return result.insertId!; - } catch (e) { - await db.query('ROLLBACK'); - print('Erreur création sortie personnelle: $e'); - rethrow; + produitId, + adminId, + quantite, + motif, + DateTime.now().toUtc(), + pointDeVenteId, + notes, + 'en_attente', // Par défaut en attente d'approbation + ]); + + await db.query('COMMIT'); + return result.insertId!; + } catch (e) { + await db.query('ROLLBACK'); + print('Erreur création sortie personnelle: $e'); + rethrow; + } } -} -Future approuverSortiePersonnelle(int sortieId, int approbateurId) async { - final db = await database; - - try { - await db.query('START TRANSACTION'); - - // 1. Récupérer les détails de la sortie - final sortie = await db.query( - 'SELECT * FROM sorties_stock_personnelles WHERE id = ? AND statut = ?', - [sortieId, 'en_attente'] - ); - - if (sortie.isEmpty) { - throw Exception('Sortie introuvable ou déjà traitée'); - } - - final fields = sortie.first.fields; - final produitId = fields['produit_id'] as int; - final quantite = fields['quantite'] as int; - - // 2. Vérifier le stock actuel - final produit = await getProductById(produitId); - if (produit == null) { - throw Exception('Produit introuvable'); - } - - if (produit.stock != null && produit.stock! < quantite) { - throw Exception('Stock insuffisant pour approuver cette sortie'); - } - - // 3. Décrémenter le stock - await db.query( - 'UPDATE products SET stock = stock - ? WHERE id = ?', - [quantite, produitId] - ); - - // 4. Marquer la sortie comme approuvée - await db.query(''' + Future approuverSortiePersonnelle( + int sortieId, int approbateurId) async { + final db = await database; + + try { + await db.query('START TRANSACTION'); + + // 1. Récupérer les détails de la sortie + final sortie = await db.query( + 'SELECT * FROM sorties_stock_personnelles WHERE id = ? AND statut = ?', + [sortieId, 'en_attente']); + + if (sortie.isEmpty) { + throw Exception('Sortie introuvable ou déjà traitée'); + } + + final fields = sortie.first.fields; + final produitId = fields['produit_id'] as int; + final quantite = fields['quantite'] as int; + + // 2. Vérifier le stock actuel + final produit = await getProductById(produitId); + if (produit == null) { + throw Exception('Produit introuvable'); + } + + if (produit.stock != null && produit.stock! < quantite) { + throw Exception('Stock insuffisant pour approuver cette sortie'); + } + + // 3. Décrémenter le stock + await db.query('UPDATE products SET stock = stock - ? WHERE id = ?', + [quantite, produitId]); + + // 4. Marquer la sortie comme approuvée + await db.query(''' UPDATE sorties_stock_personnelles SET statut = 'approuvee', approbateur_id = ?, date_approbation = ? WHERE id = ? - ''', [ - approbateurId, - DateTime.now().toUtc(), - sortieId - ]); - - await db.query('COMMIT'); - return 1; - } catch (e) { - await db.query('ROLLBACK'); - print('Erreur approbation sortie: $e'); - rethrow; + ''', [approbateurId, DateTime.now().toUtc(), sortieId]); + + await db.query('COMMIT'); + return 1; + } catch (e) { + await db.query('ROLLBACK'); + print('Erreur approbation sortie: $e'); + rethrow; + } } -} -Future refuserSortiePersonnelle(int sortieId, int approbateurId, String motifRefus) async { - final db = await database; - - try { - final result = await db.query(''' + Future refuserSortiePersonnelle( + int sortieId, int approbateurId, String motifRefus) async { + final db = await database; + + try { + final result = await db.query(''' UPDATE sorties_stock_personnelles SET statut = 'refusee', @@ -3120,25 +3092,20 @@ Future refuserSortiePersonnelle(int sortieId, int approbateurId, String mot date_approbation = ?, notes = CONCAT(COALESCE(notes, ''), '\n--- REFUS ---\n', ?) WHERE id = ? AND statut = 'en_attente' - ''', [ - approbateurId, - DateTime.now().toUtc(), - motifRefus, - sortieId - ]); - - return result.affectedRows!; - } catch (e) { - print('Erreur refus sortie: $e'); - rethrow; + ''', [approbateurId, DateTime.now().toUtc(), motifRefus, sortieId]); + + return result.affectedRows!; + } catch (e) { + print('Erreur refus sortie: $e'); + rethrow; + } } -} -Future>> getSortiesPersonnellesEnAttente() async { - final db = await database; - - try { - final result = await db.query(''' + Future>> getSortiesPersonnellesEnAttente() async { + final db = await database; + + try { + final result = await db.query(''' SELECT sp.*, p.name as produit_nom, p.reference as produit_reference, @@ -3153,36 +3120,37 @@ Future>> getSortiesPersonnellesEnAttente() async { WHERE sp.statut = 'en_attente' ORDER BY sp.date_sortie DESC '''); - - return result.map((row) => row.fields).toList(); - } catch (e) { - print('Erreur récupération sorties en attente: $e'); - return []; + + return result.map((row) => row.fields).toList(); + } catch (e) { + print('Erreur récupération sorties en attente: $e'); + return []; + } } -} -Future>> getHistoriqueSortiesPersonnelles({ - int? adminId, - String? statut, - int limit = 50, -}) async { - final db = await database; - - try { - String whereClause = ''; - List params = []; - - if (adminId != null) { - whereClause = 'WHERE sp.admin_id = ?'; - params.add(adminId); - } - - if (statut != null) { - whereClause += (whereClause.isEmpty ? 'WHERE' : ' AND') + ' sp.statut = ?'; - params.add(statut); - } - - final result = await db.query(''' + Future>> getHistoriqueSortiesPersonnelles({ + int? adminId, + String? statut, + int limit = 50, + }) async { + final db = await database; + + try { + String whereClause = ''; + List params = []; + + if (adminId != null) { + whereClause = 'WHERE sp.admin_id = ?'; + params.add(adminId); + } + + if (statut != null) { + whereClause += + (whereClause.isEmpty ? 'WHERE' : ' AND') + ' sp.statut = ?'; + params.add(statut); + } + + final result = await db.query(''' SELECT sp.*, p.name as produit_nom, p.reference as produit_reference, @@ -3200,20 +3168,20 @@ Future>> getHistoriqueSortiesPersonnelles({ ORDER BY sp.date_sortie DESC LIMIT ? ''', [...params, limit]); - - return result.map((row) => row.fields).toList(); - } catch (e) { - print('Erreur récupération historique sorties: $e'); - return []; + + return result.map((row) => row.fields).toList(); + } catch (e) { + print('Erreur récupération historique sorties: $e'); + return []; + } } -} -Future> getStatistiquesSortiesPersonnelles() async { - final db = await database; - - try { - // Total des sorties par statut - final statsStatut = await db.query(''' + Future> getStatistiquesSortiesPersonnelles() async { + final db = await database; + + try { + // Total des sorties par statut + final statsStatut = await db.query(''' SELECT statut, COUNT(*) as nombre, @@ -3221,9 +3189,9 @@ Future> getStatistiquesSortiesPersonnelles() async { FROM sorties_stock_personnelles GROUP BY statut '''); - - // Sorties par admin - final statsAdmin = await db.query(''' + + // Sorties par admin + final statsAdmin = await db.query(''' SELECT u.name as admin_nom, u.lastname as admin_nom_famille, @@ -3236,9 +3204,9 @@ Future> getStatistiquesSortiesPersonnelles() async { ORDER BY quantite_totale DESC LIMIT 10 '''); - - // Produits les plus sortis - final statsProduits = await db.query(''' + + // Produits les plus sortis + final statsProduits = await db.query(''' SELECT p.name as produit_nom, p.reference as produit_reference, @@ -3250,19 +3218,19 @@ Future> getStatistiquesSortiesPersonnelles() async { ORDER BY quantite_sortie DESC LIMIT 10 '''); - - return { - 'stats_statut': statsStatut.map((row) => row.fields).toList(), - 'stats_admin': statsAdmin.map((row) => row.fields).toList(), - 'stats_produits': statsProduits.map((row) => row.fields).toList(), - }; - } catch (e) { - print('Erreur statistiques sorties: $e'); - return { - 'stats_statut': [], - 'stats_admin': [], - 'stats_produits': [], - }; + + return { + 'stats_statut': statsStatut.map((row) => row.fields).toList(), + 'stats_admin': statsAdmin.map((row) => row.fields).toList(), + 'stats_produits': statsProduits.map((row) => row.fields).toList(), + }; + } catch (e) { + print('Erreur statistiques sorties: $e'); + return { + 'stats_statut': [], + 'stats_admin': [], + 'stats_produits': [], + }; + } } } -} \ No newline at end of file diff --git a/lib/config/DatabaseConfig.dart b/lib/config/DatabaseConfig.dart index 2f53f82..c5dd7e3 100644 --- a/lib/config/DatabaseConfig.dart +++ b/lib/config/DatabaseConfig.dart @@ -1,18 +1,22 @@ -// Config/database_config.dart - Version améliorée +// Config/database_config.dart + +import 'dart:io'; +import 'dart:async'; + class DatabaseConfig { -// static const String host = '10.0.2.2'; - //static const String host = '172.20.10.5'; - static const String host = 'localhost'; - static const int port = 3306; - static const String username = 'root'; - static const String? password = null; - static const String database = 'gico'; + // Local MySQL settings + static const String localHost = '192.168.88.73'; + static const String localUsername = 'guycom'; + static const String? localPassword = '3iV59wjRdbuXAPR'; + static const String localDatabase = 'guycom'; + // Production (public) MySQL settings static const String prodHost = '185.70.105.157'; static const String prodUsername = 'guycom'; static const String prodPassword = '3iV59wjRdbuXAPR'; static const String prodDatabase = 'guycom'; + static const int port = 3306; static const Duration connectionTimeout = Duration(seconds: 30); static const Duration queryTimeout = Duration(seconds: 15); @@ -21,46 +25,72 @@ class DatabaseConfig { static bool get isDevelopment => false; - static Map getConfig() { - if (isDevelopment) { - return { - 'host': host, - 'port': port, - 'user': username, - 'password': password, - 'database': database, - 'timeout': connectionTimeout.inSeconds, - }; + /// Build config map for connection + static Map _buildConfig({ + required String host, + required String user, + required String? password, + required String database, + }) { + return { + 'host': host, + 'port': port, + 'user': user, + 'password': password, + 'database': database, + 'timeout': connectionTimeout.inSeconds, + }; + } + + /// TCP check if MySQL server is reachable + static Future isServerReachable(String host, {int port = 3306}) async { + try { + final socket = + await Socket.connect(host, port, timeout: Duration(seconds: 2)); + socket.destroy(); + return true; + } catch (_) { + return false; + } + } + + /// Get smart config (local if reachable, otherwise public) + static Future> getSmartConfig() async { + if (await isServerReachable(localHost, port: port)) { + return _buildConfig( + host: localHost, + user: localUsername, + password: localPassword, + database: localDatabase, + ); } else { - return { - 'host': prodHost, - 'port': port, - 'user': prodUsername, - 'password': prodPassword, - 'database': prodDatabase, - 'timeout': connectionTimeout.inSeconds, - }; + return _buildConfig( + host: prodHost, + user: prodUsername, + password: prodPassword, + database: prodDatabase, + ); } } - // Validation de la configuration - static bool validateConfig() { + /// Validate any config + static bool validateConfig(Map config) { try { - final config = getConfig(); - return config['host']?.toString().isNotEmpty == true && - config['database']?.toString().isNotEmpty == true && - config['user'] != null; + return config['host']?.toString().isNotEmpty == true && + config['database']?.toString().isNotEmpty == true && + config['user'] != null; } catch (e) { print("Erreur de validation de la configuration: $e"); return false; } } - // Configuration avec retry automatique - static Map getConfigWithRetry() { - final config = getConfig(); - config['retryCount'] = 3; - config['retryDelay'] = 5000; // ms - return config; + /// Add retry config + static Map addRetry(Map config) { + return { + ...config, + 'retryCount': 3, + 'retryDelay': 5000, // ms + }; } -} \ No newline at end of file +} diff --git a/lib/controller/userController.dart b/lib/controller/userController.dart index ba170c1..20f1cbf 100644 --- a/lib/controller/userController.dart +++ b/lib/controller/userController.dart @@ -14,7 +14,7 @@ class UserController extends GetxController { final _userId = 0.obs; final _pointDeVenteId = 0.obs; final _pointDeVenteDesignation = ''.obs; - + // Cache service final PermissionCacheService _cacheService = PermissionCacheService.instance; @@ -39,17 +39,18 @@ class UserController extends GetxController { Future loadUserData() async { try { final prefs = await SharedPreferences.getInstance(); - + final storedUsername = prefs.getString('username') ?? ''; final storedRole = prefs.getString('role') ?? ''; final storedUserId = prefs.getInt('user_id') ?? 0; final storedPointDeVenteId = prefs.getInt('point_de_vente_id') ?? 0; - final storedPointDeVenteDesignation = prefs.getString('point_de_vente_designation') ?? ''; - + final storedPointDeVenteDesignation = + prefs.getString('point_de_vente_designation') ?? ''; + if (storedUsername.isNotEmpty) { try { Users user = await AppDatabase.instance.getUser(storedUsername); - + _username.value = user.username; _email.value = user.email; _name.value = user.name; @@ -59,14 +60,14 @@ class UserController extends GetxController { _userId.value = storedUserId; _pointDeVenteId.value = storedPointDeVenteId; _pointDeVenteDesignation.value = storedPointDeVenteDesignation; - - if (_pointDeVenteDesignation.value.isEmpty && _pointDeVenteId.value > 0) { + + if (_pointDeVenteDesignation.value.isEmpty && + _pointDeVenteId.value > 0) { await loadPointDeVenteDesignation(); } - + // ✅ Précharger les permissions en arrière-plan (non bloquant) _preloadPermissionsInBackground(); - } catch (dbError) { print("❌ Erreur BDD, utilisation du fallback: $dbError"); _username.value = storedUsername; @@ -77,7 +78,7 @@ class UserController extends GetxController { _userId.value = storedUserId; _pointDeVenteId.value = storedPointDeVenteId; _pointDeVenteDesignation.value = storedPointDeVenteDesignation; - + // Précharger quand même _preloadPermissionsInBackground(); } @@ -103,15 +104,17 @@ class UserController extends GetxController { Future loadPointDeVenteDesignation() async { if (_pointDeVenteId.value <= 0) return; - + try { - final pointDeVente = await AppDatabase.instance.getPointDeVenteById(_pointDeVenteId.value); + final pointDeVente = + await AppDatabase.instance.getPointDeVenteById(_pointDeVenteId.value); if (pointDeVente != null) { _pointDeVenteDesignation.value = pointDeVente['nom'] as String; await saveUserData(); } } catch (e) { - print('❌ Erreur lors du chargement de la désignation du point de vente: $e'); + print( + '❌ Erreur lors du chargement de la désignation du point de vente: $e'); } } @@ -125,14 +128,14 @@ class UserController extends GetxController { _password.value = user.password; _userId.value = userId; _pointDeVenteId.value = user.pointDeVenteId ?? 0; - + print("✅ Utilisateur mis à jour avec credentials:"); print(" Username: ${_username.value}"); print(" Role: ${_role.value}"); print(" UserID: ${_userId.value}"); - + saveUserData(); - + // ✅ Précharger immédiatement les permissions après connexion _preloadPermissionsInBackground(); } @@ -144,7 +147,7 @@ class UserController extends GetxController { _name.value = user.name; _lastname.value = user.lastName; _password.value = user.password; - + saveUserData(); _preloadPermissionsInBackground(); } @@ -152,7 +155,7 @@ class UserController extends GetxController { Future saveUserData() async { try { final prefs = await SharedPreferences.getInstance(); - + await prefs.setString('username', _username.value); await prefs.setString('email', _email.value); await prefs.setString('role', _role.value); @@ -160,8 +163,9 @@ class UserController extends GetxController { await prefs.setString('lastname', _lastname.value); await prefs.setInt('user_id', _userId.value); await prefs.setInt('point_de_vente_id', _pointDeVenteId.value); - await prefs.setString('point_de_vente_designation', _pointDeVenteDesignation.value); - + await prefs.setString( + 'point_de_vente_designation', _pointDeVenteDesignation.value); + print("✅ Données sauvegardées avec succès"); } catch (e) { print('❌ Erreur lors de la sauvegarde: $e'); @@ -172,10 +176,10 @@ class UserController extends GetxController { Future clearUserData() async { try { final prefs = await SharedPreferences.getInstance(); - + // ✅ IMPORTANT: Vider le cache de session _cacheService.clearAllCache(); - + // Effacer SharedPreferences await prefs.remove('username'); await prefs.remove('email'); @@ -185,7 +189,7 @@ class UserController extends GetxController { await prefs.remove('user_id'); await prefs.remove('point_de_vente_id'); await prefs.remove('point_de_vente_designation'); - + // Effacer les observables _username.value = ''; _email.value = ''; @@ -196,9 +200,8 @@ class UserController extends GetxController { _userId.value = 0; _pointDeVenteId.value = 0; _pointDeVenteDesignation.value = ''; - + print("✅ Données utilisateur et cache de session vidés"); - } catch (e) { print('❌ Erreur lors de l\'effacement: $e'); } @@ -215,28 +218,28 @@ class UserController extends GetxController { print('⚠️ Username vide, rechargement...'); await loadUserData(); } - + if (_username.value.isEmpty) { print('❌ Utilisateur non connecté'); return false; } - + // Essayer d'abord le cache if (_cacheService.isLoaded) { return _cacheService.hasPermission(_username.value, permission, route); } - + // Si pas encore chargé, charger et essayer de nouveau print("🔄 Cache non chargé, chargement des permissions..."); await _cacheService.loadUserPermissions(_username.value); - + return _cacheService.hasPermission(_username.value, permission, route); - } catch (e) { print('❌ Erreur vérification permission: $e'); // Fallback vers la méthode originale en cas d'erreur try { - return await AppDatabase.instance.hasPermission(_username.value, permission, route); + return await AppDatabase.instance + .hasPermission(_username.value, permission, route); } catch (fallbackError) { print('❌ Erreur fallback permission: $fallbackError'); return false; @@ -245,7 +248,8 @@ class UserController extends GetxController { } /// ✅ Vérification de permissions multiples - Future hasAnyPermission(List permissionNames, String menuRoute) async { + Future hasAnyPermission( + List permissionNames, String menuRoute) async { for (String permissionName in permissionNames) { if (await hasPermission(permissionName, menuRoute)) { return true; @@ -286,8 +290,8 @@ class UserController extends GetxController { print("IsLoggedIn: $isLoggedIn"); print("Cache Ready: $isCacheReady"); print("========================"); - + // Debug du cache _cacheService.debugPrintCache(); } -} \ No newline at end of file +}