You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

1960 lines
59 KiB

import 'dart:async';
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';
import '../Models/Permission.dart';
import '../Models/client.dart';
import '../Models/produit.dart';
import '../config/DatabaseConfig.dart';
class AppDatabase {
static final AppDatabase instance = AppDatabase._init();
MySqlConnection? _connection;
AppDatabase._init();
Future<MySqlConnection> get database async {
if (_connection != null) {
try {
// Test si la connexion est toujours active en exécutant une requête simple
await _connection!.query('SELECT 1');
return _connection!;
} catch (e) {
// Si la requête échoue, la connexion est fermée, on la recrée
print("Connexion MySQL fermée, reconnexion...");
_connection = null;
}
}
_connection = await _initDB();
return _connection!;
}
Future<void> initDatabase() async {
_connection = await _initDB();
// await _createDB();
// Effectuer la migration pour les bases existantes
await migrateDatabaseForDiscountAndGift();
await insertDefaultPermissions();
await insertDefaultMenus();
await insertDefaultRoles();
await insertDefaultSuperAdmin();
await insertDefaultPointsDeVente();
}
Future<MySqlConnection> _initDB() async {
try {
final config = DatabaseConfig.getConfig();
final settings = ConnectionSettings(
host: config['host'],
port: config['port'],
user: config['user'],
password: config['password'],
db: config['database'],
timeout: Duration(seconds: config['timeout']),
);
final connection = await MySqlConnection.connect(settings);
print("Connexion MySQL établie avec succès !");
return connection;
} catch (e) {
print("Erreur de connexion MySQL: $e");
rethrow;
}
}
// Méthode mise à jour pour créer les tables avec les nouvelles colonnes
Future<void> _createDB() async {
// final db = await database;
// try {
// // Table roles
// await db.query('''
// CREATE TABLE IF NOT EXISTS roles (
// id INT AUTO_INCREMENT PRIMARY KEY,
// designation VARCHAR(255) NOT NULL UNIQUE
// ) ENGINE=InnoDB
// ''');
// // Table permissions
// await db.query('''
// CREATE TABLE IF NOT EXISTS permissions (
// id INT AUTO_INCREMENT PRIMARY KEY,
// name VARCHAR(255) NOT NULL UNIQUE
// ) ENGINE=InnoDB
// ''');
// // Table menu
// await db.query('''
// CREATE TABLE IF NOT EXISTS menu (
// id INT AUTO_INCREMENT PRIMARY KEY,
// name VARCHAR(255) NOT NULL,
// route VARCHAR(255) NOT NULL
// ) ENGINE=InnoDB
// ''');
// // Table role_permissions
// await db.query('''
// CREATE TABLE IF NOT EXISTS role_permissions (
// role_id INT,
// permission_id INT,
// PRIMARY KEY (role_id, permission_id),
// FOREIGN KEY (role_id) REFERENCES roles(id) ON DELETE CASCADE,
// FOREIGN KEY (permission_id) REFERENCES permissions(id) ON DELETE CASCADE
// ) ENGINE=InnoDB
// ''');
// // Table role_menu_permissions
// await db.query('''
// CREATE TABLE IF NOT EXISTS role_menu_permissions (
// role_id INT,
// menu_id INT,
// permission_id INT,
// PRIMARY KEY (role_id, menu_id, permission_id),
// FOREIGN KEY (role_id) REFERENCES roles(id) ON DELETE CASCADE,
// FOREIGN KEY (menu_id) REFERENCES menu(id) ON DELETE CASCADE,
// FOREIGN KEY (permission_id) REFERENCES permissions(id) ON DELETE CASCADE
// ) ENGINE=InnoDB
// ''');
// // Table points_de_vente
// await db.query('''
// CREATE TABLE IF NOT EXISTS points_de_vente (
// id INT AUTO_INCREMENT PRIMARY KEY,
// nom VARCHAR(255) NOT NULL UNIQUE
// ) ENGINE=InnoDB
// ''');
// // Table users
// await db.query('''
// CREATE TABLE IF NOT EXISTS users (
// id INT AUTO_INCREMENT PRIMARY KEY,
// name VARCHAR(255) NOT NULL,
// lastname VARCHAR(255) NOT NULL,
// email VARCHAR(255) NOT NULL UNIQUE,
// password VARCHAR(255) NOT NULL,
// username VARCHAR(255) NOT NULL UNIQUE,
// role_id INT NOT NULL,
// point_de_vente_id INT,
// FOREIGN KEY (role_id) REFERENCES roles(id),
// FOREIGN KEY (point_de_vente_id) REFERENCES points_de_vente(id)
// ) ENGINE=InnoDB
// ''');
// // Table products
// await db.query('''
// CREATE TABLE IF NOT EXISTS products (
// id INT AUTO_INCREMENT PRIMARY KEY,
// name VARCHAR(255) NOT NULL,
// price DECIMAL(10,2) NOT NULL,
// image VARCHAR(2000),
// category VARCHAR(255) NOT NULL,
// stock INT NOT NULL DEFAULT 0,
// description VARCHAR(1000),
// qrCode VARCHAR(500),
// reference VARCHAR(255),
// point_de_vente_id INT,
// marque VARCHAR(255),
// ram VARCHAR(100),
// memoire_interne VARCHAR(100),
// imei VARCHAR(255) UNIQUE,
// FOREIGN KEY (point_de_vente_id) REFERENCES points_de_vente(id),
// INDEX idx_products_category (category),
// INDEX idx_products_reference (reference),
// INDEX idx_products_imei (imei)
// ) ENGINE=InnoDB
// ''');
// // Table clients
// await db.query('''
// CREATE TABLE IF NOT EXISTS clients (
// id INT AUTO_INCREMENT PRIMARY KEY,
// nom VARCHAR(255) NOT NULL,
// prenom VARCHAR(255) NOT NULL,
// email VARCHAR(255) NOT NULL UNIQUE,
// telephone VARCHAR(255) NOT NULL,
// adresse VARCHAR(500),
// dateCreation DATETIME NOT NULL,
// actif TINYINT(1) NOT NULL DEFAULT 1,
// INDEX idx_clients_email (email),
// INDEX idx_clients_telephone (telephone)
// ) ENGINE=InnoDB
// ''');
// // Table commandes MISE À JOUR avec les champs de remise
// await db.query('''
// CREATE TABLE IF NOT EXISTS commandes (
// id INT AUTO_INCREMENT PRIMARY KEY,
// clientId INT NOT NULL,
// dateCommande DATETIME NOT NULL,
// statut INT NOT NULL DEFAULT 0,
// montantTotal DECIMAL(10,2) NOT NULL,
// notes VARCHAR(1000),
// dateLivraison DATETIME,
// commandeurId INT,
// validateurId INT,
// remisePourcentage DECIMAL(5,2) NULL,
// remiseMontant DECIMAL(10,2) NULL,
// montantApresRemise DECIMAL(10,2) NULL,
// FOREIGN KEY (commandeurId) REFERENCES users(id),
// FOREIGN KEY (validateurId) REFERENCES users(id),
// FOREIGN KEY (clientId) REFERENCES clients(id),
// INDEX idx_commandes_client (clientId),
// INDEX idx_commandes_date (dateCommande)
// ) ENGINE=InnoDB
// ''');
// // Table details_commandes MISE À JOUR avec le champ cadeau
// await db.query('''
// CREATE TABLE IF NOT EXISTS details_commandes (
// id INT AUTO_INCREMENT PRIMARY KEY,
// commandeId INT NOT NULL,
// produitId INT NOT NULL,
// quantite INT NOT NULL,
// prixUnitaire DECIMAL(10,2) NOT NULL,
// sousTotal DECIMAL(10,2) NOT NULL,
// estCadeau TINYINT(1) DEFAULT 0,
// FOREIGN KEY (commandeId) REFERENCES commandes(id) ON DELETE CASCADE,
// FOREIGN KEY (produitId) REFERENCES products(id),
// INDEX idx_details_commande (commandeId)
// ) ENGINE=InnoDB
// ''');
// print("Tables créées avec succès avec les nouveaux champs !");
// } catch (e) {
// print("Erreur lors de la création des tables: $e");
// rethrow;
// }
}
// --- MÉTHODES D'INSERTION PAR DÉFAUT ---
//
Future<void> 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");
}
}
} catch (e) {
print("Erreur insertDefaultPermissions: $e");
}
}
//
Future<void> insertDefaultMenus() async {
final db = await database;
try {
await _addMissingMenus(db); // Seulement ajouter les menus manquants
} catch (e) {
print("Erreur insertDefaultMenus: $e");
}
}
Future<void> insertDefaultRoles() async {
final db = await database;
try {
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<String, int> roleIds = {};
for (String role in roles) {
final result = await db.query('INSERT INTO roles (designation) VALUES (?)', [role]);
roleIds[role] = result.insertId!;
}
// Récupérer les permissions et menus
final permissions = await db.query('SELECT * FROM permissions');
final menus = await db.query('SELECT * FROM menu');
// Assigner toutes les permissions à tous les menus pour le Super Admin
final superAdminRoleId = roleIds['Super Admin']!;
for (var menu in menus) {
for (var permission in permissions) {
await db.query('''
INSERT IGNORE INTO role_menu_permissions (role_id, menu_id, permission_id)
VALUES (?, ?, ?)
''', [superAdminRoleId, menu['id'], permission['id']]);
}
}
// Assigner quelques permissions à l'Admin et à l'User
await _assignBasicPermissionsToRoles(db, roleIds['Admin']!, roleIds['User']!);
print("Rôles par défaut créés et permissions assignées");
} else {
await _updateExistingRolePermissions(db);
}
} catch (e) {
print("Erreur insertDefaultRoles: $e");
}
}
Future<void> insertDefaultPointsDeVente() async {
final db = await database;
try {
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]);
} catch (e) {
print("Erreur insertion point de vente $point: $e");
}
}
print("Points de vente par défaut insérés");
}
} catch (e) {
print("Erreur insertDefaultPointsDeVente: $e");
}
}
Future<void> insertDefaultSuperAdmin() async {
final db = await database;
try {
final existingSuperAdmin = await db.query('''
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(
'SELECT id FROM roles WHERE designation = ?',
['Super Admin']
);
if (superAdminRole.isNotEmpty) {
final superAdminRoleId = superAdminRole.first['id'];
await db.query('''
INSERT INTO users (name, lastname, email, password, username, role_id)
VALUES (?, ?, ?, ?, ?, ?)
''', [
'Super',
'Admin',
'superadmin@youmazgestion.com',
'admin123',
'superadmin',
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à");
}
} catch (e) {
print("Erreur insertDefaultSuperAdmin: $e");
}
}
// --- CRUD USERS ---
Future<int> 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()
);
return result.insertId!;
}
Future<int> updateUser(Users user) async {
final db = await database;
final userMap = user.toMap();
final id = userMap.remove('id');
final setClause = userMap.keys.map((key) => '$key = ?').join(', ');
final values = [...userMap.values, id];
final result = await db.query(
'UPDATE users SET $setClause WHERE id = ?',
values
);
return result.affectedRows!;
}
Future<int> deleteUser(int id) async {
final db = await database;
final result = await db.query('DELETE FROM users WHERE id = ?', [id]);
return result.affectedRows!;
}
Future<List<Users>> getAllUsers() async {
final db = await database;
final result = await db.query('''
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((row) => Users.fromMap(row.fields)).toList();
}
// --- CRUD ROLES ---
Future<int> createRole(Role role) async {
final db = await database;
final result = await db.query(
'INSERT INTO roles (designation) VALUES (?)',
[role.designation]
);
return result.insertId!;
}
Future<List<Role>> getRoles() async {
final db = await database;
final result = await db.query('SELECT * FROM roles ORDER BY designation ASC');
return result.map((row) => Role.fromMap(row.fields)).toList();
}
Future<int> updateRole(Role role) async {
final db = await database;
final result = await db.query(
'UPDATE roles SET designation = ? WHERE id = ?',
[role.designation, role.id]
);
return result.affectedRows!;
}
Future<int> deleteRole(int? id) async {
final db = await database;
final result = await db.query('DELETE FROM roles WHERE id = ?', [id]);
return result.affectedRows!;
}
// --- PERMISSIONS ---
Future<List<Permission>> getAllPermissions() async {
final db = await database;
final result = await db.query('SELECT * FROM permissions ORDER BY name ASC');
return result.map((row) => Permission.fromMap(row.fields)).toList();
}
Future<List<Permission>> getPermissionsForRole(int roleId) async {
final db = await database;
final result = await db.query('''
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((row) => Permission.fromMap(row.fields)).toList();
}
Future<List<Permission>> getPermissionsForRoleAndMenu(int roleId, int menuId) async {
final db = await database;
final result = await db.query('''
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((row) => Permission.fromMap(row.fields)).toList();
}
// --- AUTHENTIFICATION ---
Future<bool> verifyUser(String username, String password) async {
final db = await database;
final result = await db.query('''
SELECT COUNT(*) as count
FROM users
WHERE username = ? AND password = ?
''', [username, password]);
return (result.first['count'] as int) > 0;
}
Future<Users> getUser(String username) async {
final db = await database;
final result = await db.query('''
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.fields);
} else {
throw Exception('User not found');
}
}
Future<Map<String, dynamic>?> 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
FROM users
INNER JOIN roles ON users.role_id = roles.id
WHERE username = ? AND password = ?
''', [username, password]);
if (result.isNotEmpty) {
final row = result.first;
return {
'id': row['id'],
'username': row['username'] as String,
'role': row['role_name'] as String,
'role_id': row['role_id'],
};
} else {
return null;
}
}
// --- CRUD PRODUCTS ---
Future<int> 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) {
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()
);
return result.insertId!;
}
// Sinon, on utilise le point de vente de l'utilisateur connecté
final userCtrl = Get.find<UserController>();
final currentPointDeVenteId = userCtrl.pointDeVenteId;
final Map<String, dynamic> productData = product.toMap();
productData.remove('id');
if (currentPointDeVenteId > 0) {
productData['point_de_vente_id'] = currentPointDeVenteId;
}
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()
);
return result.insertId!;
}
Future<List<Product>> getProducts() async {
final db = await database;
final result = await db.query('SELECT * FROM products ORDER BY name ASC');
return result.map((row) => Product.fromMap(row.fields)).toList();
}
Future<int> updateProduct(Product product) async {
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
);
return result.affectedRows!;
}
Future<Product?> 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);
}
return null;
}
Future<int> deleteProduct(int? id) async {
final db = await database;
final result = await db.query('DELETE FROM products WHERE id = ?', [id]);
return result.affectedRows!;
}
// --- CRUD CLIENTS ---
Future<int> 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()
);
return result.insertId!;
}
Future<List<Client>> getClients() async {
final db = await database;
final result = await db.query(
'SELECT * FROM clients WHERE actif = 1 ORDER BY nom ASC, prenom ASC'
);
return result.map((row) => Client.fromMap(row.fields)).toList();
}
Future<Client?> 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);
}
return null;
}
// --- POINTS DE VENTE ---
Future<List<Map<String, dynamic>>> 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'
);
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');
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");
return [];
}
}
// --- STATISTIQUES ---
Future<Map<String, dynamic>> 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');
return {
'totalClients': totalClients.first['count'],
'totalCommandes': totalCommandes.first['count'],
'totalProduits': totalProduits.first['count'],
'chiffreAffaires': chiffreAffaires.first['total'] ?? 0.0,
};
}
// --- MÉTHODES UTILITAIRES ---
// Future<void> _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']}");
// }
// }
// }
Future<void> _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']}");
}
}
}
Future<void> _updateExistingRolePermissions(MySqlConnection db) async {
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');
final menus = await db.query('SELECT * FROM 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('''
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('''
INSERT IGNORE INTO role_menu_permissions (role_id, menu_id, permission_id)
VALUES (?, ?, ?)
''', [superAdminRoleId, menu['id'], permission['id']]);
}
}
}
// 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']);
if (adminRole.isNotEmpty && userRole.isNotEmpty) {
await _assignBasicPermissionsToRoles(db, adminRole.first['id'], userRole.first['id']);
}
print("Permissions mises à jour pour tous les rôles");
}
}
Future<void> _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<void> close() async {
if (_connection != null) {
try {
await _connection!.close();
_connection = null;
print("Connexion MySQL fermée");
} catch (e) {
print("Erreur lors de la fermeture de la connexion: $e");
_connection = null;
}
}
}
// Pour le débogage - supprimer toutes les tables (équivalent à supprimer la DB)
Future<void> deleteDatabaseFile() async {
final db = await database;
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");
}
}
Future<void> printDatabaseInfo() async {
final db = await database;
print("=== INFORMATIONS DE LA BASE DE DONNÉES MYSQL ===");
try {
final userCountResult = await db.query('SELECT COUNT(*) as count FROM users');
final userCount = userCountResult.first['count'] as int;
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("=========================================");
} catch (e) {
print("Erreur lors de l'affichage des informations: $e");
}
}
// --- MÉTHODES SUPPLÉMENTAIRES POUR COMMANDES ---
Future<int> 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()
);
return result.insertId!;
}
Future<List<Commande>> getCommandes() 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
ORDER BY c.dateCommande DESC
''');
return result.map((row) => Commande.fromMap(row.fields)).toList();
}
Future<Commande?> getCommandeById(int id) 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.id = ?
''', [id]);
if (result.isNotEmpty) {
return Commande.fromMap(result.first.fields);
}
return null;
}
Future<int> updateCommande(Commande commande) async {
final db = await database;
final commandeMap = commande.toMap();
final id = commandeMap.remove('id');
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
);
return result.affectedRows!;
}
Future<int> deleteCommande(int id) async {
final db = await database;
final result = await db.query('DELETE FROM commandes WHERE id = ?', [id]);
return result.affectedRows!;
}
// --- DÉTAILS COMMANDES ---
Future<int> 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!;
}
Future<List<DetailCommande>> getDetailsCommande(int commandeId) async {
final db = await database;
final result = await db.query('''
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 result.map((row) => DetailCommande.fromMap(row.fields)).toList();
}
// --- RECHERCHE PRODUITS ---
Future<Product?> getProductByReference(String reference) async {
final db = await database;
final result = await db.query(
'SELECT * FROM products WHERE reference = ?',
[reference]
);
if (result.isNotEmpty) {
return Product.fromMap(result.first.fields);
}
return null;
}
Future<Product?> getProductByIMEI(String imei) async {
final db = await database;
final result = await db.query(
'SELECT * FROM products WHERE imei = ?',
[imei]
);
if (result.isNotEmpty) {
return Product.fromMap(result.first.fields);
}
return null;
}
Future<List<String>> getCategories() async {
final db = await database;
final result = await db.query('SELECT DISTINCT category FROM products ORDER BY category');
return result.map((row) => row['category'] as String).toList();
}
Future<List<Product>> getProductsByCategory(String category) async {
final db = await database;
final result = await db.query(
'SELECT * FROM products WHERE category = ? ORDER BY name ASC',
[category]
);
return result.map((row) => Product.fromMap(row.fields)).toList();
}
// --- RECHERCHE CLIENTS ---
Future<int> 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
);
return result.affectedRows!;
}
Future<int> deleteClient(int id) async {
final db = await database;
// Soft delete
final result = await db.query(
'UPDATE clients SET actif = 0 WHERE id = ?',
[id]
);
return result.affectedRows!;
}
Future<List<Client>> searchClients(String query) async {
final db = await database;
final result = await db.query('''
SELECT * FROM clients
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<Client?> 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()]
);
if (result.isNotEmpty) {
return Client.fromMap(result.first.fields);
}
return null;
}
Future<Client?> findExistingClient({
String? email,
String? telephone,
String? nom,
String? prenom,
}) async {
// Priorité 1: Recherche par email
if (email != null && email.isNotEmpty) {
final clientByEmail = await getClientByEmail(email);
if (clientByEmail != null) {
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()]
);
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()]
);
if (result.isNotEmpty) {
return Client.fromMap(result.first.fields);
}
}
return null;
}
// --- UTILISATEURS SPÉCIALISÉS ---
Future<List<Users>> getCommercialUsers() async {
final db = await database;
final result = await db.query('''
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((row) => Users.fromMap(row.fields)).toList();
}
Future<Users?> getUserById(int id) async {
final db = await database;
final result = await db.query('''
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.fields);
}
return null;
}
Future<int> getUserCount() async {
final db = await database;
final result = await db.query('SELECT COUNT(*) as count FROM users');
return result.first['count'] as int;
}
// --- PERMISSIONS AVANCÉES ---
Future<void> 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)
VALUES (?, ?, ?)
''', [roleId, menuId, permissionId]);
}
Future<void> removeRoleMenuPermission(int roleId, int menuId, int permissionId) async {
final db = await database;
await db.query('''
DELETE FROM role_menu_permissions
WHERE role_id = ? AND menu_id = ? AND permission_id = ?
''', [roleId, menuId, permissionId]);
}
Future<bool> isSuperAdmin(String username) async {
final db = await database;
final result = await db.query('''
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<bool> hasPermission(String username, String permissionName, String menuRoute) async {
final db = await database;
final result = await db.query('''
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;
}
// --- GESTION STOCK ---
Future<int> updateStock(int productId, int newStock) async {
final db = await database;
final result = await db.query(
'UPDATE products SET stock = ? WHERE id = ?',
[newStock, productId]
);
return result.affectedRows!;
}
Future<List<Product>> 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]
);
return result.map((row) => Product.fromMap(row.fields)).toList();
}
// --- POINTS DE VENTE AVANCÉS ---
Future<int> createPointDeVente(String designation, String code) async {
final db = await database;
final result = await db.query(
'INSERT IGNORE INTO points_de_vente (nom) VALUES (?)',
[designation]
);
return result.insertId ?? 0;
}
Future<int> 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]
);
return result.affectedRows!;
}
Future<int> deletePointDeVente(int id) async {
final db = await database;
final result = await db.query('DELETE FROM points_de_vente WHERE id = ?', [id]);
return result.affectedRows!;
}
Future<Map<String, dynamic>?> getPointDeVenteById(int id) async {
final db = await database;
final result = await db.query('SELECT * FROM points_de_vente WHERE id = ?', [id]);
return result.isNotEmpty ? result.first.fields : null;
}
Future<int?> 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()]
);
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()]
);
print("Point de vente créé: $nom (ID: ${result.insertId})");
return result.insertId;
} catch (e) {
print("Erreur lors de la création du point de vente $nom: $e");
return null;
}
}
Future<String?> 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]
);
return result.isNotEmpty ? result.first['nom'] as String : null;
} catch (e) {
print("Erreur getPointDeVenteNomById: $e");
return null;
}
}
// --- RECHERCHE AVANCÉE ---
Future<List<Product>> searchProducts({
String? name,
String? imei,
String? reference,
bool onlyInStock = false,
String? category,
int? pointDeVenteId,
}) async {
final db = await database;
List<String> whereConditions = [];
List<dynamic> 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 ')}'
: '';
final result = await db.query(
'SELECT * FROM products $whereClause ORDER BY name ASC',
whereArgs
);
return result.map((row) => Product.fromMap(row.fields)).toList();
}
Future<Product?> 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]
);
if (result.isNotEmpty) {
return Product.fromMap(result.first.fields);
}
// Ensuite par IMEI
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]
);
if (result.isNotEmpty) {
return Product.fromMap(result.first.fields);
}
return null;
}
// --- TRANSACTIONS COMPLEXES ---
Future<int> createCommandeComplete(Client client, Commande commande, List<DetailCommande> details) async {
final db = await database;
try {
await db.query('START TRANSACTION');
// 1. Utiliser createOrGetClient au lieu de créer directement
final existingOrNewClient = await createOrGetClient(client);
final clientId = existingOrNewClient.id!;
// 2. Créer la commande avec le bon clientId
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
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;
}
}
// --- STATISTIQUES AVANCÉES ---
Future<Map<String, int>> getProductCountByCategory() async {
final db = await database;
final result = await db.query('''
SELECT category, COUNT(*) as count
FROM products
GROUP BY category
ORDER BY count DESC
''');
return Map.fromEntries(result.map((row) =>
MapEntry(row['category'] as String, row['count'] as int)));
}
Future<Map<String, Map<String, int>>> getStockStatsByCategory() async {
final db = await database;
final result = await db.query('''
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<String, Map<String, int>> 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;
}
Future<List<Map<String, dynamic>>> getMostSoldProducts({int limit = 10}) async {
final db = await database;
final result = await db.query('''
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.map((row) => row.fields).toList();
}
// --- DÉBOGAGE ---
Future<void> 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");
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:");
for (var row in content) {
print("ID: ${row['id']}, Nom: ${row['nom']}");
}
} catch (e) {
print("Erreur debug table points_de_vente: $e");
}
}
// 1. Méthodes pour les clients
Future<Client?> 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<Client?> 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<List<Client>> 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
LOWER(prenom) LIKE ? OR
LOWER(email) LIKE ? OR
telephone LIKE ?
)
ORDER BY nom ASC, prenom ASC
LIMIT 10
''', [searchQuery, searchQuery, searchQuery, searchQuery]);
return result.map((row) => Client.fromMap(row.fields)).toList();
}
Future<List<Client>> 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
email = ? OR
telephone = ?
)
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();
}
Future<Client> 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<List<Product>> getSimilarProducts(Product product, {int limit = 5}) async {
final db = await database;
final result = await db.query('''
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 result.map((row) => Product.fromMap(row.fields)).toList();
}
// 3. Méthodes pour les commandes
Future<int> 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<List<Commande>> 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<List<Commande>> 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<int> 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!;
}
// --- CRUD MENUS ---
// Ajoutez ces méthodes dans votre classe AppDatabase
Future<List<Map<String, dynamic>>> 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<Map<String, dynamic>?> 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<Map<String, dynamic>?> 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<int> 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<int> 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<int> 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<List<Map<String, dynamic>>> 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 [];
}
}
Future<bool> 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;
}
}
Future<Client?> findClientByAnyIdentifier({
String? email,
String? telephone,
String? nom,
String? prenom,
}) async {
final db = await database;
// 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<void> migrateDatabaseForDiscountAndGift() async {
final db = await database;
try {
// Ajouter les colonnes de remise à la table commandes
await db.query('''
ALTER TABLE commandes
ADD COLUMN remisePourcentage DECIMAL(5,2) NULL
''');
await db.query('''
ALTER TABLE commandes
ADD COLUMN remiseMontant DECIMAL(10,2) NULL
''');
await db.query('''
ALTER TABLE commandes
ADD COLUMN montantApresRemise DECIMAL(10,2) NULL
''');
// Ajouter la colonne cadeau à la table details_commandes
await db.query('''
ALTER TABLE details_commandes
ADD COLUMN estCadeau TINYINT(1) DEFAULT 0
''');
print("Migration pour remise et cadeau terminée avec succès");
} catch (e) {
// Les colonnes existent probablement déjà
print("Migration déjà effectuée ou erreur: $e");
}
}
Future<List<DetailCommande>> getDetailsCommandeAvecCadeaux(int commandeId) async {
final db = await database;
final result = await db.query('''
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.estCadeau ASC, dc.id
''', [commandeId]);
return result.map((row) => DetailCommande.fromMap(row.fields)).toList();
}
Future<int> updateCommandeAvecRemise(int commandeId, {
double? remisePourcentage,
double? remiseMontant,
double? montantApresRemise,
}) async {
final db = await database;
List<String> setClauses = [];
List<dynamic> values = [];
if (remisePourcentage != null) {
setClauses.add('remisePourcentage = ?');
values.add(remisePourcentage);
}
if (remiseMontant != null) {
setClauses.add('remiseMontant = ?');
values.add(remiseMontant);
}
if (montantApresRemise != null) {
setClauses.add('montantApresRemise = ?');
values.add(montantApresRemise);
}
if (setClauses.isEmpty) return 0;
values.add(commandeId);
final result = await db.query(
'UPDATE commandes SET ${setClauses.join(', ')} WHERE id = ?',
values
);
return result.affectedRows!;
}
Future<int> createDetailCommandeCadeau(DetailCommande detail) async {
final db = await database;
final detailMap = detail.toMap();
detailMap.remove('id');
detailMap['estCadeau'] = 1; // Marquer comme cadeau
detailMap['prixUnitaire'] = 0.0; // Prix zéro pour les cadeaux
detailMap['sousTotal'] = 0.0; // Sous-total zéro pour les cadeaux
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!;
}
Future<List<DetailCommande>> getCadeauxCommande(int commandeId) async {
final db = await database;
final result = await db.query('''
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 = ? AND dc.estCadeau = 1
ORDER BY dc.id
''', [commandeId]);
return result.map((row) => DetailCommande.fromMap(row.fields)).toList();
}
Future<double> calculateMontantTotalSansCadeaux(int commandeId) async {
final db = await database;
final result = await db.query('''
SELECT SUM(sousTotal) as total
FROM details_commandes
WHERE commandeId = ? AND (estCadeau = 0 OR estCadeau IS NULL)
''', [commandeId]);
final total = result.first['total'];
return total != null ? (total as num).toDouble() : 0.0;
}
Future<int> supprimerRemiseCommande(int commandeId) async {
final db = await database;
final result = await db.query('''
UPDATE commandes
SET remisePourcentage = NULL, remiseMontant = NULL, montantApresRemise = NULL
WHERE id = ?
''', [commandeId]);
return result.affectedRows!;
}
}