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.
 
 
 
 
 
 

1508 lines
49 KiB

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