18 changed files with 5702 additions and 2204 deletions
Binary file not shown.
Binary file not shown.
@ -37,8 +37,8 @@ class AppDatabase { |
|||||
await insertDefaultMenus(); |
await insertDefaultMenus(); |
||||
await insertDefaultRoles(); |
await insertDefaultRoles(); |
||||
await insertDefaultSuperAdmin(); |
await insertDefaultSuperAdmin(); |
||||
await _insertDefaultClients(); |
// await _insertDefaultClients(); |
||||
await _insertDefaultCommandes(); |
// await _insertDefaultCommandes(); |
||||
await insertDefaultPointsDeVente(); // Ajouté ici |
await insertDefaultPointsDeVente(); // Ajouté ici |
||||
} |
} |
||||
|
|
||||
@ -113,9 +113,16 @@ class AppDatabase { |
|||||
if (!tableNames.contains('points_de_vente')) { |
if (!tableNames.contains('points_de_vente')) { |
||||
await db.execute('''CREATE TABLE points_de_vente ( |
await db.execute('''CREATE TABLE points_de_vente ( |
||||
id INTEGER PRIMARY KEY AUTOINCREMENT, |
id INTEGER PRIMARY KEY AUTOINCREMENT, |
||||
designation TEXT NOT NULL UNIQUE |
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 --- |
// --- UTILISATEURS --- |
||||
if (!tableNames.contains('users')) { |
if (!tableNames.contains('users')) { |
||||
@ -140,8 +147,8 @@ class AppDatabase { |
|||||
} |
} |
||||
} |
} |
||||
|
|
||||
// --- PRODUITS --- |
// Dans la méthode _createDB, modifier la partie concernant la table products |
||||
if (!tableNames.contains('products')) { |
if (!tableNames.contains('products')) { |
||||
await db.execute('''CREATE TABLE products ( |
await db.execute('''CREATE TABLE products ( |
||||
id INTEGER PRIMARY KEY AUTOINCREMENT, |
id INTEGER PRIMARY KEY AUTOINCREMENT, |
||||
name TEXT NOT NULL, |
name TEXT NOT NULL, |
||||
@ -151,18 +158,43 @@ class AppDatabase { |
|||||
stock INTEGER NOT NULL DEFAULT 0, |
stock INTEGER NOT NULL DEFAULT 0, |
||||
description TEXT, |
description TEXT, |
||||
qrCode TEXT, |
qrCode TEXT, |
||||
reference TEXT UNIQUE, |
reference TEXT, |
||||
point_de_vente_id INTEGER, |
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) |
FOREIGN KEY (point_de_vente_id) REFERENCES points_de_vente(id) |
||||
)'''); |
)'''); |
||||
} else { |
} else { |
||||
// Si la table existe déjà, ajouter la colonne si elle n'existe pas |
// 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 { |
try { |
||||
await db.execute('ALTER TABLE products ADD COLUMN point_de_vente_id INTEGER REFERENCES points_de_vente(id)'); |
await db.execute('ALTER TABLE products ADD COLUMN point_de_vente_id INTEGER REFERENCES points_de_vente(id)'); |
||||
} catch (e) { |
} catch (e) { |
||||
print("La colonne point_de_vente_id existe déjà dans la table products"); |
print("La colonne point_de_vente_id existe déjà dans la table products"); |
||||
} |
} |
||||
} |
} |
||||
|
|
||||
// --- CLIENTS --- |
// --- CLIENTS --- |
||||
if (!tableNames.contains('clients')) { |
if (!tableNames.contains('clients')) { |
||||
@ -301,25 +333,61 @@ class AppDatabase { |
|||||
}/* Copier depuis ton code */ } |
}/* Copier depuis ton code */ } |
||||
|
|
||||
|
|
||||
Future<void> insertDefaultPointsDeVente() async { |
Future<void> insertDefaultPointsDeVente() async { |
||||
final db = await database; |
final db = await database; |
||||
final existing = await db.query('points_de_vente'); |
final existing = await db.query('points_de_vente'); |
||||
|
|
||||
if (existing.isEmpty) { |
if (existing.isEmpty) { |
||||
final defaultPoints = [ |
final defaultPoints = [ |
||||
{'designation': 'Behoririka'}, |
{'nom': '405A'}, |
||||
{'designation': 'Antanimena'}, |
{'nom': '405B'}, |
||||
{'designation': 'Analakely'}, |
{'nom': '416'}, |
||||
{'designation': 'Andravoahangy'}, |
{'nom': 'S405A'}, |
||||
{'designation': 'Anosy'}, |
{'nom': '417'}, |
||||
]; |
]; |
||||
|
|
||||
for (var point in defaultPoints) { |
for (var point in defaultPoints) { |
||||
await db.insert('points_de_vente', point); |
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"); |
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; |
Future<void> insertDefaultSuperAdmin() async { final db = await database; |
||||
|
|
||||
final existingSuperAdmin = await db.rawQuery(''' |
final existingSuperAdmin = await db.rawQuery(''' |
||||
@ -557,13 +625,17 @@ Future<Users?> getUserById(int id) async { |
|||||
Future<int> createProduct(Product product) async { |
Future<int> createProduct(Product product) async { |
||||
final db = await database; |
final db = await database; |
||||
|
|
||||
// Récupérer le point de vente de l'utilisateur connecté |
// 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 userCtrl = Get.find<UserController>(); |
||||
final currentPointDeVenteId = userCtrl.pointDeVenteId; |
final currentPointDeVenteId = userCtrl.pointDeVenteId; |
||||
|
|
||||
// Si le produit n’a pas de point_de_vente_id, on lui assigne celui de l'utilisateur connecté |
|
||||
final Map<String, dynamic> productData = product.toMap(); |
final Map<String, dynamic> productData = product.toMap(); |
||||
if (currentPointDeVenteId > 0 && (product.pointDeVenteId == null || product.pointDeVenteId == 0)) { |
if (currentPointDeVenteId > 0) { |
||||
productData['point_de_vente_id'] = currentPointDeVenteId; |
productData['point_de_vente_id'] = currentPointDeVenteId; |
||||
} |
} |
||||
|
|
||||
@ -592,6 +664,19 @@ Future<int> updateProduct(Product product) async { |
|||||
// where: 'id = ?', |
// where: 'id = ?', |
||||
// whereArgs: [product.id], |
// whereArgs: [product.id], |
||||
// );/* Copier depuis ton code */ } |
// );/* 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; |
Future<int> deleteProduct(int? id) async { final db = await database; |
||||
return await db.delete( |
return await db.delete( |
||||
'products', |
'products', |
||||
@ -739,6 +824,21 @@ Future<int> deleteCommande(int id) async { |
|||||
} |
} |
||||
return null; |
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 |
// Détails commandes |
||||
// Créer un détail de commande |
// Créer un détail de commande |
||||
Future<int> createDetailCommande(DetailCommande detail) async { |
Future<int> createDetailCommande(DetailCommande detail) async { |
||||
@ -835,110 +935,110 @@ Future<int> updateStock(int productId, int newStock) async { |
|||||
); |
); |
||||
} |
} |
||||
|
|
||||
// Données par défaut |
// // Données par défaut |
||||
Future<void> _insertDefaultClients() async {final db = await database; |
// Future<void> _insertDefaultClients() async {final db = await database; |
||||
final existingClients = await db.query('clients'); |
// final existingClients = await db.query('clients'); |
||||
|
|
||||
if (existingClients.isEmpty) { |
// if (existingClients.isEmpty) { |
||||
final defaultClients = [ |
// final defaultClients = [ |
||||
Client( |
// Client( |
||||
nom: 'Dupont', |
// nom: 'Dupont', |
||||
prenom: 'Jean', |
// prenom: 'Jean', |
||||
email: '[email protected]', |
// email: '[email protected]', |
||||
telephone: '0123456789', |
// telephone: '0123456789', |
||||
adresse: '123 Rue de la Paix, Paris', |
// adresse: '123 Rue de la Paix, Paris', |
||||
dateCreation: DateTime.now(), |
// dateCreation: DateTime.now(), |
||||
), |
// ), |
||||
Client( |
// Client( |
||||
nom: 'Martin', |
// nom: 'Martin', |
||||
prenom: 'Marie', |
// prenom: 'Marie', |
||||
email: '[email protected]', |
// email: '[email protected]', |
||||
telephone: '0987654321', |
// telephone: '0987654321', |
||||
adresse: '456 Avenue des Champs, Lyon', |
// adresse: '456 Avenue des Champs, Lyon', |
||||
dateCreation: DateTime.now(), |
// dateCreation: DateTime.now(), |
||||
), |
// ), |
||||
Client( |
// Client( |
||||
nom: 'Bernard', |
// nom: 'Bernard', |
||||
prenom: 'Pierre', |
// prenom: 'Pierre', |
||||
email: '[email protected]', |
// email: '[email protected]', |
||||
telephone: '0456789123', |
// telephone: '0456789123', |
||||
adresse: '789 Boulevard Saint-Michel, Marseille', |
// adresse: '789 Boulevard Saint-Michel, Marseille', |
||||
dateCreation: DateTime.now(), |
// dateCreation: DateTime.now(), |
||||
), |
// ), |
||||
]; |
// ]; |
||||
|
|
||||
for (var client in defaultClients) { |
// for (var client in defaultClients) { |
||||
await db.insert('clients', client.toMap()); |
// await db.insert('clients', client.toMap()); |
||||
} |
// } |
||||
print("Clients par défaut insérés"); |
// print("Clients par défaut insérés"); |
||||
} /* Copier depuis ton code */ } |
// } /* Copier depuis ton code */ } |
||||
Future<void> _insertDefaultCommandes() async { final db = await database; |
// Future<void> _insertDefaultCommandes() async { final db = await database; |
||||
final existingCommandes = await db.query('commandes'); |
// final existingCommandes = await db.query('commandes'); |
||||
|
|
||||
if (existingCommandes.isEmpty) { |
// if (existingCommandes.isEmpty) { |
||||
// Récupérer quelques produits pour créer des commandes |
// // Récupérer quelques produits pour créer des commandes |
||||
final produits = await db.query('products', limit: 3); |
// final produits = await db.query('products', limit: 3); |
||||
final clients = await db.query('clients', limit: 3); |
// final clients = await db.query('clients', limit: 3); |
||||
|
|
||||
if (produits.isNotEmpty && clients.isNotEmpty) { |
// if (produits.isNotEmpty && clients.isNotEmpty) { |
||||
// Commande 1 |
// // Commande 1 |
||||
final commande1Id = await db.insert('commandes', { |
// final commande1Id = await db.insert('commandes', { |
||||
'clientId': clients[0]['id'], |
// 'clientId': clients[0]['id'], |
||||
'dateCommande': DateTime.now().subtract(Duration(days: 5)).toIso8601String(), |
// 'dateCommande': DateTime.now().subtract(Duration(days: 5)).toIso8601String(), |
||||
'statut': StatutCommande.livree.index, |
// 'statut': StatutCommande.livree.index, |
||||
'montantTotal': 150.0, |
// 'montantTotal': 150.0, |
||||
'notes': 'Commande urgente', |
// 'notes': 'Commande urgente', |
||||
}); |
// }); |
||||
|
|
||||
await db.insert('details_commandes', { |
// await db.insert('details_commandes', { |
||||
'commandeId': commande1Id, |
// 'commandeId': commande1Id, |
||||
'produitId': produits[0]['id'], |
// 'produitId': produits[0]['id'], |
||||
'quantite': 2, |
// 'quantite': 2, |
||||
'prixUnitaire': 75.0, |
// 'prixUnitaire': 75.0, |
||||
'sousTotal': 150.0, |
// 'sousTotal': 150.0, |
||||
}); |
// }); |
||||
|
|
||||
// Commande 2 |
// // Commande 2 |
||||
final commande2Id = await db.insert('commandes', { |
// final commande2Id = await db.insert('commandes', { |
||||
'clientId': clients[1]['id'], |
// 'clientId': clients[1]['id'], |
||||
'dateCommande': DateTime.now().subtract(Duration(days: 2)).toIso8601String(), |
// 'dateCommande': DateTime.now().subtract(Duration(days: 2)).toIso8601String(), |
||||
'statut': StatutCommande.enPreparation.index, |
// 'statut': StatutCommande.enPreparation.index, |
||||
'montantTotal': 225.0, |
// 'montantTotal': 225.0, |
||||
'notes': 'Livraison prévue demain', |
// 'notes': 'Livraison prévue demain', |
||||
}); |
// }); |
||||
|
|
||||
if (produits.length > 1) { |
// if (produits.length > 1) { |
||||
await db.insert('details_commandes', { |
// await db.insert('details_commandes', { |
||||
'commandeId': commande2Id, |
// 'commandeId': commande2Id, |
||||
'produitId': produits[1]['id'], |
// 'produitId': produits[1]['id'], |
||||
'quantite': 3, |
// 'quantite': 3, |
||||
'prixUnitaire': 75.0, |
// 'prixUnitaire': 75.0, |
||||
'sousTotal': 225.0, |
// 'sousTotal': 225.0, |
||||
}); |
// }); |
||||
} |
// } |
||||
|
|
||||
// Commande 3 |
// // Commande 3 |
||||
final commande3Id = await db.insert('commandes', { |
// final commande3Id = await db.insert('commandes', { |
||||
'clientId': clients[2]['id'], |
// 'clientId': clients[2]['id'], |
||||
'dateCommande': DateTime.now().subtract(Duration(hours: 6)).toIso8601String(), |
// 'dateCommande': DateTime.now().subtract(Duration(hours: 6)).toIso8601String(), |
||||
'statut': StatutCommande.confirmee.index, |
// 'statut': StatutCommande.confirmee.index, |
||||
'montantTotal': 300.0, |
// 'montantTotal': 300.0, |
||||
'notes': 'Commande standard', |
// 'notes': 'Commande standard', |
||||
}); |
// }); |
||||
|
|
||||
if (produits.length > 2) { |
// if (produits.length > 2) { |
||||
await db.insert('details_commandes', { |
// await db.insert('details_commandes', { |
||||
'commandeId': commande3Id, |
// 'commandeId': commande3Id, |
||||
'produitId': produits[2]['id'], |
// 'produitId': produits[2]['id'], |
||||
'quantite': 4, |
// 'quantite': 4, |
||||
'prixUnitaire': 75.0, |
// 'prixUnitaire': 75.0, |
||||
'sousTotal': 300.0, |
// 'sousTotal': 300.0, |
||||
}); |
// }); |
||||
} |
// } |
||||
|
|
||||
print("Commandes par défaut insérées"); |
// print("Commandes par défaut insérées"); |
||||
} |
// } |
||||
}/* Copier depuis ton code */ } |
// }/* Copier depuis ton code */ } |
||||
|
|
||||
// Statistiques |
// Statistiques |
||||
Future<Map<String, dynamic>> getStatistiques() async { final db = await database; |
Future<Map<String, dynamic>> getStatistiques() async { final db = await database; |
||||
@ -1094,25 +1194,46 @@ Future<bool> hasPermission(String username, String permissionName, String menuRo |
|||||
print("Base de données product supprimée"); |
print("Base de données product supprimée"); |
||||
}/* Copier depuis ton code */ } |
}/* Copier depuis ton code */ } |
||||
// CRUD Points de vente |
// CRUD Points de vente |
||||
Future<int> createPointDeVente(String designation) async { |
// CRUD Points de vente |
||||
|
Future<int> createPointDeVente(String designation, String code) async { |
||||
final db = await database; |
final db = await database; |
||||
return await db.insert('points_de_vente', { |
return await db.insert('points_de_vente', { |
||||
'designation': designation |
'designation': designation, |
||||
}, |
'code': code |
||||
conflictAlgorithm: ConflictAlgorithm.ignore |
}, conflictAlgorithm: ConflictAlgorithm.ignore); |
||||
); |
|
||||
} |
} |
||||
|
|
||||
Future<List<Map<String, dynamic>>> getPointsDeVente() async { |
Future<List<Map<String, dynamic>>> getPointsDeVente() async { |
||||
final db = await database; |
final db = await database; |
||||
return await db.query('points_de_vente', orderBy: 'designation ASC'); |
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) async { |
Future<int> updatePointDeVente(int id, String newDesignation, String newCode) async { |
||||
final db = await database; |
final db = await database; |
||||
return await db.update( |
return await db.update( |
||||
'points_de_vente', |
'points_de_vente', |
||||
{'designation': newDesignation}, |
{ |
||||
|
'designation': newDesignation, |
||||
|
'code': newCode |
||||
|
}, |
||||
where: 'id = ?', |
where: 'id = ?', |
||||
whereArgs: [id], |
whereArgs: [id], |
||||
); |
); |
||||
@ -1139,6 +1260,8 @@ Future<Map<String, int>> getProductCountByCategory() async { |
|||||
return Map.fromEntries(result.map((e) => |
return Map.fromEntries(result.map((e) => |
||||
MapEntry(e['category'] as String, e['count'] as int))); |
MapEntry(e['category'] as String, e['count'] as int))); |
||||
} |
} |
||||
|
|
||||
|
|
||||
Future<Map<String, dynamic>?> getPointDeVenteById(int id) async { |
Future<Map<String, dynamic>?> getPointDeVenteById(int id) async { |
||||
final db = await database; |
final db = await database; |
||||
final result = await db.query( |
final result = await db.query( |
||||
@ -1148,4 +1271,238 @@ Future<Map<String, dynamic>?> getPointDeVenteById(int id) async { |
|||||
); |
); |
||||
return result.isNotEmpty ? result.first : null; |
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])); |
||||
|
} |
||||
} |
} |
||||
File diff suppressed because it is too large
File diff suppressed because it is too large
Loading…
Reference in new issue