import 'dart:async'; import 'dart:io'; import 'package:flutter/services.dart'; import 'package:get/get.dart'; import 'package:get/get_core/src/get_main.dart'; import 'package:path/path.dart'; import 'package:path_provider/path_provider.dart'; import 'package:sqflite_common_ffi/sqflite_ffi.dart'; import 'package:youmazgestion/controller/userController.dart'; // Models import '../Models/users.dart'; import '../Models/role.dart'; import '../Models/Permission.dart'; import '../Models/client.dart'; import '../Models/produit.dart'; class AppDatabase { static final AppDatabase instance = AppDatabase._init(); late Database _database; AppDatabase._init() { sqfliteFfiInit(); } Future get database async { if (_database.isOpen) return _database; _database = await _initDB('app_database.db'); return _database; } Future initDatabase() async { _database = await _initDB('app_database.db'); await _createDB(_database, 1); await insertDefaultPermissions(); await insertDefaultMenus(); await insertDefaultRoles(); await insertDefaultSuperAdmin(); // await _insertDefaultClients(); // await _insertDefaultCommandes(); await insertDefaultPointsDeVente(); // Ajouté ici } Future _initDB(String filePath) async { final documentsDirectory = await getApplicationDocumentsDirectory(); final path = join(documentsDirectory.path, filePath); bool dbExists = await File(path).exists(); if (!dbExists) { try { ByteData data = await rootBundle.load('assets/database/$filePath'); List bytes = data.buffer.asUint8List(data.offsetInBytes, data.lengthInBytes); await File(path).writeAsBytes(bytes); } catch (e) { print("Aucune DB pré-chargée trouvée, création d'une nouvelle"); } } return await databaseFactoryFfi.openDatabase(path); } Future _createDB(Database db, int version) async { final tables = await db.rawQuery("SELECT name FROM sqlite_master WHERE type='table'"); final tableNames = tables.map((row) => row['name'] as String).toList(); // --- UTILISATEURS / ROLES / PERMISSIONS --- if (!tableNames.contains('roles')) { await db.execute('''CREATE TABLE roles ( id INTEGER PRIMARY KEY AUTOINCREMENT, designation TEXT NOT NULL UNIQUE )'''); } if (!tableNames.contains('permissions')) { await db.execute('''CREATE TABLE permissions ( id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT NOT NULL UNIQUE )'''); } if (!tableNames.contains('menu')) { await db.execute('''CREATE TABLE menu ( id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT NOT NULL, route TEXT NOT NULL )'''); } if (!tableNames.contains('role_permissions')) { await db.execute('''CREATE TABLE role_permissions ( role_id INTEGER, permission_id INTEGER, PRIMARY KEY (role_id, permission_id), FOREIGN KEY (role_id) REFERENCES roles(id), FOREIGN KEY (permission_id) REFERENCES permissions(id) )'''); } if (!tableNames.contains('role_menu_permissions')) { await db.execute('''CREATE TABLE role_menu_permissions ( role_id INTEGER, menu_id INTEGER, permission_id INTEGER, PRIMARY KEY (role_id, menu_id, permission_id), FOREIGN KEY (role_id) REFERENCES roles(id), FOREIGN KEY (menu_id) REFERENCES menu(id), FOREIGN KEY (permission_id) REFERENCES permissions(id) )'''); } // --- POINTS DE VENTE --- if (!tableNames.contains('points_de_vente')) { await db.execute('''CREATE TABLE points_de_vente ( id INTEGER PRIMARY KEY AUTOINCREMENT, nom TEXT NOT NULL UNIQUE )'''); } else { // Si la table existe déjà, ajouter la colonne code si elle n'existe pas try { await db.execute('ALTER TABLE points_de_vente ADD COLUMN nom TEXT UNIQUE'); } catch (e) { print("La colonne nom existe déjà dans la table points_de_vente"); } } // --- UTILISATEURS --- if (!tableNames.contains('users')) { await db.execute('''CREATE TABLE users ( id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT NOT NULL, lastname TEXT NOT NULL, email TEXT NOT NULL UNIQUE, password TEXT NOT NULL, username TEXT NOT NULL UNIQUE, role_id INTEGER NOT NULL, point_de_vente_id INTEGER, FOREIGN KEY (role_id) REFERENCES roles(id), FOREIGN KEY (point_de_vente_id) REFERENCES points_de_vente(id) )'''); } else { // Si la table existe déjà, ajouter la colonne si elle n'existe pas try { await db.execute('ALTER TABLE users ADD COLUMN point_de_vente_id INTEGER REFERENCES points_de_vente(id)'); } catch (e) { print("La colonne point_de_vente_id existe déjà dans la table users"); } } // Dans la méthode _createDB, modifier la partie concernant la table products if (!tableNames.contains('products')) { await db.execute('''CREATE TABLE products ( id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT NOT NULL, price REAL NOT NULL, image TEXT, category TEXT NOT NULL, stock INTEGER NOT NULL DEFAULT 0, description TEXT, qrCode TEXT, reference TEXT, point_de_vente_id INTEGER, marque TEXT, ram TEXT, memoire_interne TEXT, imei TEXT UNIQUE, FOREIGN KEY (point_de_vente_id) REFERENCES points_de_vente(id) )'''); } else { // Si la table existe déjà, ajouter les colonnes si elles n'existent pas final columns = await db.rawQuery('PRAGMA table_info(products)'); final columnNames = columns.map((col) => col['name'] as String).toList(); final newColumns = [ 'marque', 'ram', 'memoire_interne', 'imei' ]; for (var column in newColumns) { if (!columnNames.contains(column)) { try { await db.execute('ALTER TABLE products ADD COLUMN $column TEXT'); } catch (e) { print("La colonne $column existe déjà dans la table products"); } } } // Vérifier aussi point_de_vente_id au cas où try { await db.execute('ALTER TABLE products ADD COLUMN point_de_vente_id INTEGER REFERENCES points_de_vente(id)'); } catch (e) { print("La colonne point_de_vente_id existe déjà dans la table products"); } } // --- CLIENTS --- if (!tableNames.contains('clients')) { await db.execute('''CREATE TABLE clients ( id INTEGER PRIMARY KEY AUTOINCREMENT, nom TEXT NOT NULL, prenom TEXT NOT NULL, email TEXT NOT NULL UNIQUE, telephone TEXT NOT NULL, adresse TEXT, dateCreation TEXT NOT NULL, actif INTEGER NOT NULL DEFAULT 1 )'''); } // --- COMMANDES --- if (!tableNames.contains('commandes')) { await db.execute('''CREATE TABLE commandes ( id INTEGER PRIMARY KEY AUTOINCREMENT, clientId INTEGER NOT NULL, dateCommande TEXT NOT NULL, statut INTEGER NOT NULL DEFAULT 0, montantTotal REAL NOT NULL, notes TEXT, dateLivraison TEXT, commandeurId INTEGER, validateurId INTEGER, FOREIGN KEY (commandeurId) REFERENCES users(id), FOREIGN KEY (validateurId) REFERENCES users(id), FOREIGN KEY (clientId) REFERENCES clients(id) )'''); } if (!tableNames.contains('details_commandes')) { await db.execute('''CREATE TABLE details_commandes ( id INTEGER PRIMARY KEY AUTOINCREMENT, commandeId INTEGER NOT NULL, produitId INTEGER NOT NULL, quantite INTEGER NOT NULL, prixUnitaire REAL NOT NULL, sousTotal REAL NOT NULL, FOREIGN KEY (commandeId) REFERENCES commandes(id), FOREIGN KEY (produitId) REFERENCES products(id) )'''); } // Indexes await db.execute('CREATE INDEX IF NOT EXISTS idx_products_category ON products(category)'); await db.execute('CREATE INDEX IF NOT EXISTS idx_products_reference ON products(reference)'); await db.execute('CREATE INDEX IF NOT EXISTS idx_commandes_client ON commandes(clientId)'); await db.execute('CREATE INDEX IF NOT EXISTS idx_commandes_date ON commandes(dateCommande)'); await db.execute('CREATE INDEX IF NOT EXISTS idx_details_commande ON details_commandes(commandeId)'); } // --- MÉTHODES UTILISATEURS / ROLES / PERMISSIONS --- Future insertDefaultPermissions() async { final db = await database; final existing = await db.query('permissions'); if (existing.isEmpty) { await db.insert('permissions', {'name': 'view'}); await db.insert('permissions', {'name': 'create'}); await db.insert('permissions', {'name': 'update'}); await db.insert('permissions', {'name': 'delete'}); await db.insert('permissions', {'name': 'admin'}); await db.insert('permissions', {'name': 'manage'}); // Nouvelle permission await db.insert('permissions', {'name': 'read'}); // Nouvelle permission print("Permissions par défaut insérées"); } else { // Vérifier et ajouter les nouvelles permissions si elles n'existent pas final newPermissions = ['manage', 'read']; for (var permission in newPermissions) { final existingPermission = await db.query('permissions', where: 'name = ?', whereArgs: [permission]); if (existingPermission.isEmpty) { await db.insert('permissions', {'name': permission}); print("Permission ajoutée: $permission"); } } }/* Copier depuis ton code */ } Future insertDefaultMenus() async { final db = await database; final existingMenus = await db.query('menu'); if (existingMenus.isEmpty) { // Menus existants await db.insert('menu', {'name': 'Accueil', 'route': '/accueil'}); await db.insert('menu', {'name': 'Ajouter un utilisateur', 'route': '/ajouter-utilisateur'}); await db.insert('menu', {'name': 'Modifier/Supprimer un utilisateur', 'route': '/modifier-utilisateur'}); await db.insert('menu', {'name': 'Ajouter un produit', 'route': '/ajouter-produit'}); await db.insert('menu', {'name': 'Modifier/Supprimer un produit', 'route': '/modifier-produit'}); await db.insert('menu', {'name': 'Bilan', 'route': '/bilan'}); await db.insert('menu', {'name': 'Gérer les rôles', 'route': '/gerer-roles'}); await db.insert('menu', {'name': 'Gestion de stock', 'route': '/gestion-stock'}); await db.insert('menu', {'name': 'Historique', 'route': '/historique'}); await db.insert('menu', {'name': 'Déconnexion', 'route': '/deconnexion'}); // Nouveaux menus ajoutés await db.insert('menu', {'name': 'Nouvelle commande', 'route': '/nouvelle-commande'}); await db.insert('menu', {'name': 'Gérer les commandes', 'route': '/gerer-commandes'}); print("Menus par défaut insérés"); } else { // Si des menus existent déjà, vérifier et ajouter les nouveaux menus manquants await _addMissingMenus(db); } /* Copier depuis ton code */ } Future insertDefaultRoles() async { final db = await database; final existingRoles = await db.query('roles'); if (existingRoles.isEmpty) { int superAdminRoleId = await db.insert('roles', {'designation': 'Super Admin'}); int adminRoleId = await db.insert('roles', {'designation': 'Admin'}); int userRoleId = await db.insert('roles', {'designation': 'User'}); int commercialRoleId = await db.insert('roles', {'designation': 'commercial'}); int caisseRoleId = await db.insert('roles', {'designation': 'caisse'}); final permissions = await db.query('permissions'); final menus = await db.query('menu'); // Assigner toutes les permissions à tous les menus pour le Super Admin for (var menu in menus) { for (var permission in permissions) { await db.insert('role_menu_permissions', { 'role_id': superAdminRoleId, 'menu_id': menu['id'], 'permission_id': permission['id'], }, conflictAlgorithm: ConflictAlgorithm.ignore ); } } // Assigner quelques permissions à l'Admin et à l'User pour les nouveaux menus await _assignBasicPermissionsToRoles(db, adminRoleId, userRoleId); print("Rôles par défaut créés et permissions assignées"); } else { // Si les rôles existent déjà, vérifier et ajouter les permissions manquantes await _updateExistingRolePermissions(db); }/* Copier depuis ton code */ } Future insertDefaultPointsDeVente() async { final db = await database; final existing = await db.query('points_de_vente'); if (existing.isEmpty) { final defaultPoints = [ {'nom': '405A'}, {'nom': '405B'}, {'nom': '416'}, {'nom': 'S405A'}, {'nom': '417'}, ]; for (var point in defaultPoints) { try { await db.insert( 'points_de_vente', point, conflictAlgorithm: ConflictAlgorithm.ignore ); } catch (e) { print("Erreur insertion point de vente ${point['nom']}: $e"); } } print("Points de vente par défaut insérés"); } } Future debugPointsDeVenteTable() async { final db = await database; try { // Vérifie si la table existe final tables = await db.rawQuery( "SELECT name FROM sqlite_master WHERE type='table' AND name='points_de_vente'" ); if (tables.isEmpty) { print("La table points_de_vente n'existe pas!"); return; } // Compte le nombre d'entrées final count = await db.rawQuery("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('points_de_vente'); print("Contenu de la table points_de_vente:"); for (var row in content) { print("ID: ${row['id']}, Nom: ${row['nom']}"); } } catch (e) { print("Erreur debug table points_de_vente: $e"); } } Future insertDefaultSuperAdmin() async { final db = await database; final existingSuperAdmin = await db.rawQuery(''' SELECT u.* FROM users u INNER JOIN roles r ON u.role_id = r.id WHERE r.designation = 'Super Admin' '''); if (existingSuperAdmin.isEmpty) { final superAdminRole = await db.query('roles', where: 'designation = ?', whereArgs: ['Super Admin'] ); if (superAdminRole.isNotEmpty) { final superAdminRoleId = superAdminRole.first['id'] as int; await db.insert('users', { 'name': 'Super', 'lastname': 'Admin', 'email': 'superadmin@youmazgestion.com', 'password': 'admin123', 'username': 'superadmin', 'role_id': superAdminRoleId, }); 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 !"); } } else { print("Super Admin existe déjà"); }/* Copier depuis ton code */ } // CRUD Users // Dans la méthode createUser Future createUser(Users user) async { final db = await database; return await db.insert('users', user.toMap()); } // Dans la méthode updateUser Future updateUser(Users user) async { final db = await database; return await db.update( 'users', user.toMap(), where: 'id = ?', whereArgs: [user.id] ); } Future deleteUser(int id) async { final db = await database; return await db.delete('users', where: 'id = ?', whereArgs: [id]); /* Copier depuis ton code */ } // Future updateUser(Users user) async { final db = await database; // return await db.update('users', user.toMap(), where: 'id = ?', whereArgs: [user.id]); // /* Copier depuis ton code */ } Future> getAllUsers() async { final db = await database; final result = await db.rawQuery(''' SELECT users.*, roles.designation as role_name FROM users INNER JOIN roles ON users.role_id = roles.id ORDER BY users.id ASC '''); return result.map((json) => Users.fromMap(json)).toList(); /* Copier depuis ton code */ } // CRUD Roles Future createRole(Role role) async { final db = await database; return await db.insert('roles', role.toMap());/* Copier depuis ton code */ } Future> getRoles() async { final db = await database; final maps = await db.query('roles', orderBy: 'designation ASC'); return List.generate(maps.length, (i) => Role.fromMap(maps[i])); /* Copier depuis ton code */ } Future updateRole(Role role) async { final db = await database; return await db.update( 'roles', role.toMap(), where: 'id = ?', whereArgs: [role.id], );/* Copier depuis ton code */ } Future deleteRole(int? id) async { final db = await database; return await db.delete( 'roles', where: 'id = ?', whereArgs: [id], );/* Copier depuis ton code */ } Future> getPermissionsForRoleAndMenu(int roleId, int menuId) async { final db = await database; final result = await db.rawQuery(''' SELECT p.id, p.name FROM permissions p JOIN role_menu_permissions rmp ON p.id = rmp.permission_id WHERE rmp.role_id = ? AND rmp.menu_id = ? ORDER BY p.name ASC ''', [roleId, menuId]); return result.map((map) => Permission.fromMap(map)).toList(); } Future assignRoleMenuPermission(int roleId, int menuId, int permissionId) async { final db = await database; await db.insert('role_menu_permissions', { 'role_id': roleId, 'menu_id': menuId, 'permission_id': permissionId, }, conflictAlgorithm: ConflictAlgorithm.ignore); } Future isSuperAdmin(String username) async { final db = await database; final result = await db.rawQuery(''' SELECT COUNT(*) as count FROM users u INNER JOIN roles r ON u.role_id = r.id WHERE u.username = ? AND r.designation = 'Super Admin' ''', [username]); return (result.first['count'] as int) > 0; } Future removeRoleMenuPermission(int roleId, int menuId, int permissionId) async { final db = await database; await db.delete( 'role_menu_permissions', where: 'role_id = ? AND menu_id = ? AND permission_id = ?', whereArgs: [roleId, menuId, permissionId], ); } Future getUserById(int id) async { final db = await database; final result = await db.rawQuery(''' SELECT users.*, roles.designation as role_name FROM users INNER JOIN roles ON users.role_id = roles.id WHERE users.id = ? ''', [id]); if (result.isNotEmpty) { return Users.fromMap(result.first); } return null; } Future getUserCount() async { final db = await database; List> result = await db.rawQuery('SELECT COUNT(*) as count FROM users'); return result.first['count'] as int; } Future printDatabaseInfo() async { final db = await database; print("=== INFORMATIONS DE LA BASE DE DONNÉES ==="); final userCount = await getUserCount(); print("Nombre d'utilisateurs: $userCount"); final users = await getAllUsers(); print("Utilisateurs:"); for (var user in users) { print(" - ${user.username} (${user.name} ) - Email: ${user.email}"); } final roles = await getRoles(); print("Rôles:"); for (var role in roles) { print(" - ${role.designation} (ID: ${role.id})"); } final permissions = await getAllPermissions(); print("Permissions:"); for (var permission in permissions) { print(" - ${permission.name} (ID: ${permission.id})"); } print("========================================="); } // CRUD Permissions Future> getAllPermissions() async { final db = await database; final result = await db.query('permissions', orderBy: 'name ASC'); return result.map((e) => Permission.fromMap(e)).toList(); /* Copier depuis ton code */ } Future> getPermissionsForRole(int roleId) async { final db = await database; final result = await db.rawQuery(''' SELECT p.id, p.name FROM permissions p JOIN role_permissions rp ON p.id = rp.permission_id WHERE rp.role_id = ? ORDER BY p.name ASC ''', [roleId]); return result.map((map) => Permission.fromMap(map)).toList(); /* Copier depuis ton code */ } // Gestion des accès Future verifyUser(String username, String password) async { final db = await database; final result = await db.rawQuery(''' SELECT users.id FROM users WHERE users.username = ? AND users.password = ? ''', [username, password]); return result.isNotEmpty; /* Copier depuis ton code */ } Future getUser(String username) async { final db = await database; final result = await db.rawQuery(''' SELECT users.*, roles.designation as role_name FROM users INNER JOIN roles ON users.role_id = roles.id WHERE users.username = ? ''', [username]); if (result.isNotEmpty) { return Users.fromMap(result.first); } else { throw Exception('User not found'); } /* Copier depuis ton code */ } Future?> getUserCredentials(String username, String password) async { final db = await database; final result = await db.rawQuery(''' SELECT users.username, users.id, roles.designation as role_name, roles.id as role_id FROM users INNER JOIN roles ON users.role_id = roles.id WHERE username = ? AND password = ? ''', [username, password]); if (result.isNotEmpty) { return { 'id': result.first['id'], 'username': result.first['username'] as String, 'role': result.first['role_name'] as String, 'role_id': result.first['role_id'], }; } else { return null; }/* Copier depuis ton code */ } // --- MÉTHODES PRODUITS / CLIENTS / COMMANDES --- // CRUD Produits // Dans la méthode createProduct Future createProduct(Product product) async { final db = await database; // Si le produit a un point_de_vente_id, on l'utilise directement if (product.pointDeVenteId != null && product.pointDeVenteId! > 0) { return await db.insert('products', product.toMap()); } // Sinon, on utilise le point de vente de l'utilisateur connecté final userCtrl = Get.find(); final currentPointDeVenteId = userCtrl.pointDeVenteId; final Map productData = product.toMap(); if (currentPointDeVenteId > 0) { productData['point_de_vente_id'] = currentPointDeVenteId; } return await db.insert('products', productData); } // Dans la méthode updateProduct Future updateProduct(Product product) async { final db = await database; return await db.update( 'products', product.toMap(), where: 'id = ?', whereArgs: [product.id], ); } Future> getProducts() async { final db = await database; final maps = await db.query('products', orderBy: 'name ASC'); return List.generate(maps.length, (i) { return Product.fromMap(maps[i]); });/* Copier depuis ton code */ } // Future updateProduct(Product product) async { final db = await database; // return await db.update( // 'products', // product.toMap(), // where: 'id = ?', // whereArgs: [product.id], // );/* Copier depuis ton code */ } Future getProductById(int id) async { final db = await database; final maps = await db.query( 'products', where: 'id = ?', whereArgs: [id], ); if (maps.isNotEmpty) { return Product.fromMap(maps.first); } return null; } Future deleteProduct(int? id) async { final db = await database; return await db.delete( 'products', where: 'id = ?', whereArgs: [id], );/* Copier depuis ton code */ } Future> getCategories() async { final db = await database; final result = await db.rawQuery('SELECT DISTINCT category FROM products ORDER BY category'); return List.generate( result.length, (index) => result[index]['category'] as String); /* Copier depuis ton code */ } Future> getProductsByCategory(String category) async { final db = await database; final maps = await db .query('products', where: 'category = ?', whereArgs: [category], orderBy: 'name ASC'); return List.generate(maps.length, (i) { return Product.fromMap(maps[i]); });/* Copier depuis ton code */ } // CRUD Clients Future createClient(Client client) async { final db = await database; return await db.insert('clients', client.toMap());/* Copier depuis ton code */ } Future> getClients() async { final db = await database; final maps = await db.query('clients', where: 'actif = 1', orderBy: 'nom ASC, prenom ASC'); return List.generate(maps.length, (i) { return Client.fromMap(maps[i]); });/* Copier depuis ton code */ } Future getClientById(int id) async { final db = await database; final maps = await db.query('clients', where: 'id = ?', whereArgs: [id]); if (maps.isNotEmpty) { return Client.fromMap(maps.first); } return null;/* Copier depuis ton code */ } Future updateClient(Client client) async { final db = await database; return await db.update( 'clients', client.toMap(), where: 'id = ?', whereArgs: [client.id], );/* Copier depuis ton code */ } Future deleteClient(int id) async { final db = await database; // Soft delete return await db.update( 'clients', {'actif': 0}, where: 'id = ?', whereArgs: [id], ); /* Copier depuis ton code */ } Future> searchClients(String query) async { final db = await database; final maps = await db.query( 'clients', where: 'actif = 1 AND (nom LIKE ? OR prenom LIKE ? OR email LIKE ?)', whereArgs: ['%$query%', '%$query%', '%$query%'], orderBy: 'nom ASC, prenom ASC', ); return List.generate(maps.length, (i) { return Client.fromMap(maps[i]); });/* Copier depuis ton code */ } Future> getCommercialUsers() async { final db = await database; final result = await db.rawQuery(''' SELECT users.*, roles.designation as role_name FROM users INNER JOIN roles ON users.role_id = roles.id WHERE roles.designation = 'commercial' ORDER BY users.id ASC '''); return result.map((json) => Users.fromMap(json)).toList(); } // Dans AppDatabase (stock_managementDatabase.dart) // Créer une commande Future createCommande(Commande commande) async { final db = await database; return await db.insert('commandes', commande.toMap()); } // Récupérer toutes les commandes avec les infos client Future> getCommandes() async { final db = await database; final maps = await db.rawQuery(''' 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 ORDER BY c.dateCommande DESC '''); return List.generate(maps.length, (i) => Commande.fromMap(maps[i])); } // Récupérer une commande par son ID Future getCommandeById(int id) async { final db = await database; final maps = await db.rawQuery(''' 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.id = ? ''', [id]); if (maps.isNotEmpty) { return Commande.fromMap(maps.first); } return null; } // Mettre à jour une commande Future updateCommande(Commande commande) async { final db = await database; return await db.update( 'commandes', commande.toMap(), where: 'id = ?', whereArgs: [commande.id], ); } // Mettre à jour seulement le statut Future updateStatutCommande(int commandeId, StatutCommande statut) async { final db = await database; return await db.update( 'commandes', {'statut': statut.index}, where: 'id = ?', whereArgs: [commandeId], ); } // Supprimer une commande Future deleteCommande(int id) async { final db = await database; return await db.delete( 'commandes', where: 'id = ?', whereArgs: [id], ); } Future getProductByReference(String reference) async { final db = await database; final maps = await db.query( 'products', where: 'reference = ?', whereArgs: [reference], ); if (maps.isNotEmpty) { return Product.fromMap(maps.first); } return null; } Future getProductByIMEI(String imei) async { final db = await database; final maps = await db.query( 'products', where: 'imei = ?', whereArgs: [imei], ); if (maps.isNotEmpty) { return Product.fromMap(maps.first); } return null; } // Détails commandes // Créer un détail de commande Future createDetailCommande(DetailCommande detail) async { final db = await database; return await db.insert('details_commandes', detail.toMap()); } // Récupérer les détails d'une commande Future> getDetailsCommande(int commandeId) async { final db = await database; final maps = await db.rawQuery(''' SELECT dc.*, p.name as produitNom, p.image as produitImage, p.reference as produitReference FROM details_commandes dc LEFT JOIN products p ON dc.produitId = p.id WHERE dc.commandeId = ? ORDER BY dc.id ''', [commandeId]); return List.generate(maps.length, (i) => DetailCommande.fromMap(maps[i])); } // Transactions complexes Future createCommandeComplete(Client client, Commande commande, List details) async { final db = await database; return await db.transaction((txn) async { // 1. Créer le client final clientId = await txn.insert('clients', client.toMap()); // 2. Créer la commande avec le bon clientId final commandeMap = commande.toMap(); commandeMap['clientId'] = clientId; final commandeId = await txn.insert('commandes', commandeMap); // 3. Créer les détails de commande for (final detail in details) { final detailMap = detail.toMap(); detailMap['commandeId'] = commandeId; await txn.insert('details_commandes', detailMap); // 4. Mettre à jour le stock await txn.rawUpdate( 'UPDATE products SET stock = stock - ? WHERE id = ?', [detail.quantite, detail.produitId], ); } return commandeId; }); } // Récupérer les commandes d'un client Future> getCommandesByClient(int clientId) async { final db = await database; final maps = await db.rawQuery(''' 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 List.generate(maps.length, (i) => Commande.fromMap(maps[i])); } // Récupérer les commandes par statut Future> getCommandesByStatut(StatutCommande statut) async { final db = await database; final maps = await db.rawQuery(''' 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 List.generate(maps.length, (i) => Commande.fromMap(maps[i])); } Future updateValidateurCommande(int commandeId, int validateurId) async { final db = await database; return await db.update( 'commandes', { 'validateurId': validateurId, 'statut': StatutCommande.confirmee.index, }, where: 'id = ?', whereArgs: [commandeId], ); } Future updateStock(int productId, int newStock) async { final db = await database; return await db.update( 'products', {'stock': newStock}, where: 'id = ?', whereArgs: [productId], ); } // // Données par défaut // Future _insertDefaultClients() async {final db = await database; // final existingClients = await db.query('clients'); // if (existingClients.isEmpty) { // final defaultClients = [ // Client( // nom: 'Dupont', // prenom: 'Jean', // email: 'jean.dupont@email.com', // telephone: '0123456789', // adresse: '123 Rue de la Paix, Paris', // dateCreation: DateTime.now(), // ), // Client( // nom: 'Martin', // prenom: 'Marie', // email: 'marie.martin@email.com', // telephone: '0987654321', // adresse: '456 Avenue des Champs, Lyon', // dateCreation: DateTime.now(), // ), // Client( // nom: 'Bernard', // prenom: 'Pierre', // email: 'pierre.bernard@email.com', // telephone: '0456789123', // adresse: '789 Boulevard Saint-Michel, Marseille', // dateCreation: DateTime.now(), // ), // ]; // for (var client in defaultClients) { // await db.insert('clients', client.toMap()); // } // print("Clients par défaut insérés"); // } /* Copier depuis ton code */ } // Future _insertDefaultCommandes() async { final db = await database; // final existingCommandes = await db.query('commandes'); // if (existingCommandes.isEmpty) { // // Récupérer quelques produits pour créer des commandes // final produits = await db.query('products', limit: 3); // final clients = await db.query('clients', limit: 3); // if (produits.isNotEmpty && clients.isNotEmpty) { // // Commande 1 // final commande1Id = await db.insert('commandes', { // 'clientId': clients[0]['id'], // 'dateCommande': DateTime.now().subtract(Duration(days: 5)).toIso8601String(), // 'statut': StatutCommande.livree.index, // 'montantTotal': 150.0, // 'notes': 'Commande urgente', // }); // await db.insert('details_commandes', { // 'commandeId': commande1Id, // 'produitId': produits[0]['id'], // 'quantite': 2, // 'prixUnitaire': 75.0, // 'sousTotal': 150.0, // }); // // Commande 2 // final commande2Id = await db.insert('commandes', { // 'clientId': clients[1]['id'], // 'dateCommande': DateTime.now().subtract(Duration(days: 2)).toIso8601String(), // 'statut': StatutCommande.enPreparation.index, // 'montantTotal': 225.0, // 'notes': 'Livraison prévue demain', // }); // if (produits.length > 1) { // await db.insert('details_commandes', { // 'commandeId': commande2Id, // 'produitId': produits[1]['id'], // 'quantite': 3, // 'prixUnitaire': 75.0, // 'sousTotal': 225.0, // }); // } // // Commande 3 // final commande3Id = await db.insert('commandes', { // 'clientId': clients[2]['id'], // 'dateCommande': DateTime.now().subtract(Duration(hours: 6)).toIso8601String(), // 'statut': StatutCommande.confirmee.index, // 'montantTotal': 300.0, // 'notes': 'Commande standard', // }); // if (produits.length > 2) { // await db.insert('details_commandes', { // 'commandeId': commande3Id, // 'produitId': produits[2]['id'], // 'quantite': 4, // 'prixUnitaire': 75.0, // 'sousTotal': 300.0, // }); // } // print("Commandes par défaut insérées"); // } // }/* Copier depuis ton code */ } // Statistiques Future> getStatistiques() async { final db = await database; final totalClients = await db.rawQuery('SELECT COUNT(*) as count FROM clients WHERE actif = 1'); final totalCommandes = await db.rawQuery('SELECT COUNT(*) as count FROM commandes'); final totalProduits = await db.rawQuery('SELECT COUNT(*) as count FROM products'); final chiffreAffaires = await db.rawQuery('SELECT SUM(montantTotal) as total FROM commandes WHERE statut != 5'); // 5 = annulée return { 'totalClients': totalClients.first['count'], 'totalCommandes': totalCommandes.first['count'], 'totalProduits': totalProduits.first['count'], 'chiffreAffaires': chiffreAffaires.first['total'] ?? 0.0, };/* Copier depuis ton code */ } // Fermeture Future close() async { if (_database.isOpen) { await _database.close(); } } Future _updateExistingRolePermissions(Database db) async { final superAdminRole = await db.query('roles', where: 'designation = ?', whereArgs: ['Super Admin']); if (superAdminRole.isNotEmpty) { final superAdminRoleId = superAdminRole.first['id'] as int; final permissions = await db.query('permissions'); final menus = await db.query('menu'); // Vérifier et ajouter les permissions manquantes pour le Super Admin sur tous les menus for (var menu in menus) { for (var permission in permissions) { final existingPermission = await db.query( 'role_menu_permissions', where: 'role_id = ? AND menu_id = ? AND permission_id = ?', whereArgs: [superAdminRoleId, menu['id'], permission['id']], ); if (existingPermission.isEmpty) { await db.insert('role_menu_permissions', { 'role_id': superAdminRoleId, 'menu_id': menu['id'], 'permission_id': permission['id'], }, conflictAlgorithm: ConflictAlgorithm.ignore ); } } } // Assigner les permissions de base aux autres rôles pour les nouveaux menus final adminRole = await db.query('roles', where: 'designation = ?', whereArgs: ['Admin']); final userRole = await db.query('roles', where: 'designation = ?', whereArgs: ['User']); if (adminRole.isNotEmpty && userRole.isNotEmpty) { await _assignBasicPermissionsToRoles(db, adminRole.first['id'] as int, userRole.first['id'] as int); } print("Permissions mises à jour pour tous les rôles"); } } // Nouvelle méthode pour assigner les permissions de base aux nouveaux menus Future _assignBasicPermissionsToRoles(Database db, int adminRoleId, int userRoleId) async { final viewPermission = await db.query('permissions', where: 'name = ?', whereArgs: ['view']); final createPermission = await db.query('permissions', where: 'name = ?', whereArgs: ['create']); final updatePermission = await db.query('permissions', where: 'name = ?', whereArgs: ['update']); final managePermission = await db.query('permissions', where: 'name = ?', whereArgs: ['manage']); // Récupérer les IDs des nouveaux menus final nouvelleCommandeMenu = await db.query('menu', where: 'route = ?', whereArgs: ['/nouvelle-commande']); final gererCommandesMenu = await db.query('menu', where: 'route = ?', whereArgs: ['/gerer-commandes']); if (nouvelleCommandeMenu.isNotEmpty && createPermission.isNotEmpty) { // Admin peut créer de nouvelles commandes await db.insert('role_menu_permissions', { 'role_id': adminRoleId, 'menu_id': nouvelleCommandeMenu.first['id'], 'permission_id': createPermission.first['id'], }, conflictAlgorithm: ConflictAlgorithm.ignore ); // User peut aussi créer de nouvelles commandes await db.insert('role_menu_permissions', { 'role_id': userRoleId, 'menu_id': nouvelleCommandeMenu.first['id'], 'permission_id': createPermission.first['id'], }, conflictAlgorithm: ConflictAlgorithm.ignore ); } if (gererCommandesMenu.isNotEmpty && managePermission.isNotEmpty) { // Admin peut gérer les commandes await db.insert('role_menu_permissions', { 'role_id': adminRoleId, 'menu_id': gererCommandesMenu.first['id'], 'permission_id': managePermission.first['id'], }, conflictAlgorithm: ConflictAlgorithm.ignore ); } if (gererCommandesMenu.isNotEmpty && viewPermission.isNotEmpty) { // User peut voir les commandes await db.insert('role_menu_permissions', { 'role_id': userRoleId, 'menu_id': gererCommandesMenu.first['id'], 'permission_id': viewPermission.first['id'], } , conflictAlgorithm: ConflictAlgorithm.ignore ); } } Future _addMissingMenus(Database db) async { final menusToAdd = [ {'name': 'Nouvelle commande', 'route': '/nouvelle-commande'}, {'name': 'Gérer les commandes', 'route': '/gerer-commandes'}, ]; for (var menu in menusToAdd) { final existing = await db.query( 'menu', where: 'route = ?', whereArgs: [menu['route']], ); if (existing.isEmpty) { await db.insert('menu', menu); print("Menu ajouté: ${menu['name']}"); } } } Future hasPermission(String username, String permissionName, String menuRoute) async { final db = await database; final result = await db.rawQuery(''' SELECT COUNT(*) as count FROM permissions p JOIN role_menu_permissions rmp ON p.id = rmp.permission_id JOIN roles r ON rmp.role_id = r.id JOIN users u ON u.role_id = r.id 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; } // Pour réinitialiser la base (débogage) Future deleteDatabaseFile() async { final documentsDirectory = await getApplicationDocumentsDirectory(); final path = join(documentsDirectory.path, 'app_database.db'); final file = File(path); if (await file.exists()) { await file.delete(); print("Base de données product supprimée"); }/* Copier depuis ton code */ } // CRUD Points de vente // CRUD Points de vente Future createPointDeVente(String designation, String code) async { final db = await database; return await db.insert('points_de_vente', { 'designation': designation, 'code': code }, conflictAlgorithm: ConflictAlgorithm.ignore); } Future>> getPointsDeVente() async { final db = await database; try { final result = await db.query( 'points_de_vente', orderBy: 'nom ASC', where: 'nom IS NOT NULL AND nom != ""' // Filtre les noms vides ); if (result.isEmpty) { print("Aucun point de vente trouvé dans la base de données"); // Optionnel: Insérer les points de vente par défaut si table vide await insertDefaultPointsDeVente(); return await db.query('points_de_vente', orderBy: 'nom ASC'); } return result; } catch (e) { print("Erreur lors de la récupération des points de vente: $e"); return []; } } Future updatePointDeVente(int id, String newDesignation, String newCode) async { final db = await database; return await db.update( 'points_de_vente', { 'designation': newDesignation, 'code': newCode }, where: 'id = ?', whereArgs: [id], ); } Future deletePointDeVente(int id) async { final db = await database; return await db.delete( 'points_de_vente', where: 'id = ?', whereArgs: [id], ); } // Dans AppDatabase Future> getProductCountByCategory() async { final db = await database; final result = await db.rawQuery(''' SELECT category, COUNT(*) as count FROM products GROUP BY category ORDER BY count DESC '''); return Map.fromEntries(result.map((e) => MapEntry(e['category'] as String, e['count'] as int))); } Future?> getPointDeVenteById(int id) async { final db = await database; final result = await db.query( 'points_de_vente', where: 'id = ?', whereArgs: [id], ); return result.isNotEmpty ? result.first : 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( 'points_de_vente', where: 'nom = ?', whereArgs: [nom.trim()], ); if (existing.isNotEmpty) { return existing.first['id'] as int; } // Créer le point de vente s'il n'existe pas try { final id = await db.insert('points_de_vente', { 'nom': nom.trim() }); print("Point de vente créé: $nom (ID: $id)"); return id; } catch (e) { print("Erreur lors de la création du point de vente $nom: $e"); return null; } } Future getPointDeVenteNomById(int id) async { if (id == 0 || id == null) return null; final db = await database; try { final result = await db.query( 'points_de_vente', where: 'id = ?', whereArgs: [id], limit: 1, ); return result.isNotEmpty ? result.first['nom'] as String : null; } catch (e) { print("Erreur getPointDeVenteNomById: $e"); return null; } } Future> searchProducts({ String? name, String? imei, String? reference, bool onlyInStock = false, String? category, 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 ? whereConditions.join(' AND ') : ''; final maps = await db.query( 'products', where: whereClause.isNotEmpty ? whereClause : null, whereArgs: whereArgs.isNotEmpty ? whereArgs : null, orderBy: 'name ASC', ); return List.generate(maps.length, (i) => Product.fromMap(maps[i])); } // Obtenir le nombre de produits en stock par catégorie Future>> getStockStatsByCategory() async { final db = await database; final result = await db.rawQuery(''' SELECT category, COUNT(*) as total_products, SUM(CASE WHEN stock > 0 THEN 1 ELSE 0 END) as in_stock, SUM(CASE WHEN stock = 0 OR stock IS NULL THEN 1 ELSE 0 END) as out_of_stock, SUM(stock) as total_stock FROM products GROUP BY category ORDER BY category '''); Map> stats = {}; for (var row in result) { stats[row['category'] as String] = { 'total': row['total_products'] as int, 'in_stock': row['in_stock'] as int, 'out_of_stock': row['out_of_stock'] as int, 'total_stock': row['total_stock'] as int? ?? 0, }; } return stats; } // Recherche rapide par code-barres/QR/IMEI Future findProductByCode(String code) async { final db = await database; // Essayer de trouver par référence d'abord var maps = await db.query( 'products', where: 'reference = ?', whereArgs: [code], limit: 1, ); if (maps.isNotEmpty) { return Product.fromMap(maps.first); } // Ensuite par IMEI maps = await db.query( 'products', where: 'imei = ?', whereArgs: [code], limit: 1, ); if (maps.isNotEmpty) { return Product.fromMap(maps.first); } // Enfin par QR code si disponible maps = await db.query( 'products', where: 'qrCode = ?', whereArgs: [code], limit: 1, ); if (maps.isNotEmpty) { return Product.fromMap(maps.first); } return null; } // Obtenir les produits avec stock faible (seuil personnalisable) Future> getLowStockProducts({int threshold = 5}) async { final db = await database; final maps = await db.query( 'products', where: 'stock <= ? AND stock > 0', whereArgs: [threshold], orderBy: 'stock ASC', ); return List.generate(maps.length, (i) => Product.fromMap(maps[i])); } // Obtenir les produits les plus vendus (basé sur les commandes) Future>> getMostSoldProducts({int limit = 10}) async { final db = await database; final result = await db.rawQuery(''' SELECT p.id, p.name, p.price, p.stock, p.category, SUM(dc.quantite) as total_sold, COUNT(DISTINCT dc.commandeId) as order_count FROM products p INNER JOIN details_commandes dc ON p.id = dc.produitId INNER JOIN commandes c ON dc.commandeId = c.id WHERE c.statut != 5 -- Exclure les commandes annulées GROUP BY p.id, p.name, p.price, p.stock, p.category ORDER BY total_sold DESC LIMIT ? ''', [limit]); return result; } // Recherche de produits similaires (par nom ou catégorie) Future> getSimilarProducts(Product product, {int limit = 5}) async { final db = await database; // Rechercher par catégorie et nom similaire, exclure le produit actuel final maps = await db.rawQuery(''' SELECT * FROM products WHERE id != ? AND ( category = ? OR name LIKE ? ) ORDER BY CASE WHEN category = ? THEN 1 ELSE 2 END, name ASC LIMIT ? ''', [ product.id, product.category, '%${product.name.split(' ').first}%', product.category, limit ]); return List.generate(maps.length, (i) => Product.fromMap(maps[i])); } }