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 insertDefaultRoles(); |
|||
await insertDefaultSuperAdmin(); |
|||
await _insertDefaultClients(); |
|||
await _insertDefaultCommandes(); |
|||
// await _insertDefaultClients(); |
|||
// await _insertDefaultCommandes(); |
|||
await insertDefaultPointsDeVente(); // Ajouté ici |
|||
} |
|||
|
|||
@ -113,9 +113,16 @@ class AppDatabase { |
|||
if (!tableNames.contains('points_de_vente')) { |
|||
await db.execute('''CREATE TABLE points_de_vente ( |
|||
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 --- |
|||
if (!tableNames.contains('users')) { |
|||
@ -140,8 +147,8 @@ class AppDatabase { |
|||
} |
|||
} |
|||
|
|||
// --- PRODUITS --- |
|||
if (!tableNames.contains('products')) { |
|||
// 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, |
|||
@ -151,18 +158,43 @@ class AppDatabase { |
|||
stock INTEGER NOT NULL DEFAULT 0, |
|||
description TEXT, |
|||
qrCode TEXT, |
|||
reference TEXT UNIQUE, |
|||
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 la colonne si elle n'existe pas |
|||
} 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')) { |
|||
@ -301,25 +333,61 @@ class AppDatabase { |
|||
}/* Copier depuis ton code */ } |
|||
|
|||
|
|||
Future<void> insertDefaultPointsDeVente() async { |
|||
Future<void> insertDefaultPointsDeVente() async { |
|||
final db = await database; |
|||
final existing = await db.query('points_de_vente'); |
|||
|
|||
if (existing.isEmpty) { |
|||
final defaultPoints = [ |
|||
{'designation': 'Behoririka'}, |
|||
{'designation': 'Antanimena'}, |
|||
{'designation': 'Analakely'}, |
|||
{'designation': 'Andravoahangy'}, |
|||
{'designation': 'Anosy'}, |
|||
{'nom': '405A'}, |
|||
{'nom': '405B'}, |
|||
{'nom': '416'}, |
|||
{'nom': 'S405A'}, |
|||
{'nom': '417'}, |
|||
]; |
|||
|
|||
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"); |
|||
} |
|||
} |
|||
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(''' |
|||
@ -557,13 +625,17 @@ Future<Users?> getUserById(int id) async { |
|||
Future<int> createProduct(Product product) async { |
|||
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 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(); |
|||
if (currentPointDeVenteId > 0 && (product.pointDeVenteId == null || product.pointDeVenteId == 0)) { |
|||
if (currentPointDeVenteId > 0) { |
|||
productData['point_de_vente_id'] = currentPointDeVenteId; |
|||
} |
|||
|
|||
@ -592,6 +664,19 @@ Future<int> updateProduct(Product product) async { |
|||
// 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', |
|||
@ -739,6 +824,21 @@ Future<int> deleteCommande(int id) async { |
|||
} |
|||
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 { |
|||
@ -835,110 +935,110 @@ Future<int> updateStock(int productId, int newStock) async { |
|||
); |
|||
} |
|||
|
|||
// 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: '[email protected]', |
|||
telephone: '0123456789', |
|||
adresse: '123 Rue de la Paix, Paris', |
|||
dateCreation: DateTime.now(), |
|||
), |
|||
Client( |
|||
nom: 'Martin', |
|||
prenom: 'Marie', |
|||
email: '[email protected]', |
|||
telephone: '0987654321', |
|||
adresse: '456 Avenue des Champs, Lyon', |
|||
dateCreation: DateTime.now(), |
|||
), |
|||
Client( |
|||
nom: 'Bernard', |
|||
prenom: 'Pierre', |
|||
email: '[email protected]', |
|||
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 */ } |
|||
// // 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: '[email protected]', |
|||
// telephone: '0123456789', |
|||
// adresse: '123 Rue de la Paix, Paris', |
|||
// dateCreation: DateTime.now(), |
|||
// ), |
|||
// Client( |
|||
// nom: 'Martin', |
|||
// prenom: 'Marie', |
|||
// email: '[email protected]', |
|||
// telephone: '0987654321', |
|||
// adresse: '456 Avenue des Champs, Lyon', |
|||
// dateCreation: DateTime.now(), |
|||
// ), |
|||
// Client( |
|||
// nom: 'Bernard', |
|||
// prenom: 'Pierre', |
|||
// email: '[email protected]', |
|||
// 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; |
|||
@ -1094,25 +1194,46 @@ Future<bool> hasPermission(String username, String permissionName, String menuRo |
|||
print("Base de données product supprimée"); |
|||
}/* Copier depuis ton code */ } |
|||
// 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; |
|||
return await db.insert('points_de_vente', { |
|||
'designation': designation |
|||
}, |
|||
conflictAlgorithm: ConflictAlgorithm.ignore |
|||
); |
|||
'designation': designation, |
|||
'code': code |
|||
}, conflictAlgorithm: ConflictAlgorithm.ignore); |
|||
} |
|||
|
|||
Future<List<Map<String, dynamic>>> getPointsDeVente() async { |
|||
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; |
|||
return await db.update( |
|||
'points_de_vente', |
|||
{'designation': newDesignation}, |
|||
{ |
|||
'designation': newDesignation, |
|||
'code': newCode |
|||
}, |
|||
where: 'id = ?', |
|||
whereArgs: [id], |
|||
); |
|||
@ -1139,6 +1260,8 @@ Future<Map<String, int>> getProductCountByCategory() async { |
|||
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( |
|||
@ -1148,4 +1271,238 @@ Future<Map<String, dynamic>?> getPointDeVenteById(int id) async { |
|||
); |
|||
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