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
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]));
|
|
}
|
|
}
|