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.
 
 
 
 
 
 

1975 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 {
final existing = await db.query('SELECT COUNT(*) as count FROM permissions');
final count = existing.first['count'] as int;
if (count == 0) {
final permissions = ['view', 'create', 'update', 'delete', 'admin', 'manage', 'read'];
for (String permission in permissions) {
await db.query('INSERT INTO permissions (name) VALUES (?)', [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(
'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 {
final existingMenus = await db.query('SELECT COUNT(*) as count FROM menu');
final count = existingMenus.first['count'] as int;
if (count == 0) {
final menus = [
{'name': 'Accueil', 'route': '/accueil'},
{'name': 'Ajouter un utilisateur', 'route': '/ajouter-utilisateur'},
{'name': 'Modifier/Supprimer un utilisateur', 'route': '/modifier-utilisateur'},
{'name': 'Ajouter un produit', 'route': '/ajouter-produit'},
{'name': 'Modifier/Supprimer un produit', 'route': '/modifier-produit'},
{'name': 'Bilan', 'route': '/bilan'},
{'name': 'Gérer les rôles', 'route': '/gerer-roles'},
{'name': 'Gestion de stock', 'route': '/gestion-stock'},
{'name': 'Historique', 'route': '/historique'},
{'name': 'Déconnexion', 'route': '/deconnexion'},
{'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 menus) {
await db.query(
'INSERT INTO menu (name, route) VALUES (?, ?)',
[menu['name'], menu['route']]
);
}
print("Menus par défaut insérés");
} else {
await _addMissingMenus(db);
}
} 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> _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!;
}
}