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.
 
 
 
 
 
 

559 lines
17 KiB

import 'dart:async';
import 'dart:io';
import 'package:flutter/services.dart';
import 'package:path/path.dart';
import 'package:path_provider/path_provider.dart';
import 'package:sqflite_common_ffi/sqflite_ffi.dart';
import '../Models/produit.dart';
import '../Models/client.dart';
class ProductDatabase {
static final ProductDatabase instance = ProductDatabase._init();
late Database _database;
ProductDatabase._init() {
sqfliteFfiInit();
}
ProductDatabase();
Future<Database> get database async {
if (_database.isOpen) return _database;
_database = await _initDB('products2.db');
return _database;
}
Future<void> initDatabase() async {
_database = await _initDB('products2.db');
await _createDB(_database, 1);
await _insertDefaultClients();
await _insertDefaultCommandes();
}
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('Pas de fichier DB dans assets, création nouvelle DB');
}
}
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();
// Table products (existante avec améliorations)
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 UNIQUE
)
''');
print("Table 'products' créée.");
} else {
// Vérifier et ajouter les colonnes manquantes
await _updateProductsTable(db);
}
// Table 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
)
''');
print("Table 'clients' créée.");
}
// Table 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,
FOREIGN KEY (clientId) REFERENCES clients(id) ON DELETE CASCADE
)
''');
print("Table 'commandes' créée.");
}
// Table détails commandes
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) ON DELETE CASCADE,
FOREIGN KEY (produitId) REFERENCES products(id) ON DELETE CASCADE
)
''');
print("Table 'details_commandes' créée.");
}
// Créer les index pour optimiser les performances
await _createIndexes(db);
}
Future<void> _updateProductsTable(Database db) async {
final columns = await db.rawQuery('PRAGMA table_info(products)');
final columnNames = columns.map((e) => e['name'] as String).toList();
if (!columnNames.contains('description')) {
await db.execute("ALTER TABLE products ADD COLUMN description TEXT");
print("Colonne 'description' ajoutée.");
}
if (!columnNames.contains('qrCode')) {
await db.execute("ALTER TABLE products ADD COLUMN qrCode TEXT");
print("Colonne 'qrCode' ajoutée.");
}
if (!columnNames.contains('reference')) {
await db.execute("ALTER TABLE products ADD COLUMN reference TEXT");
print("Colonne 'reference' ajoutée.");
}
}
Future<void> _createIndexes(Database db) async {
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)');
print("Index créés pour optimiser les performances.");
}
// =========================
// MÉTHODES PRODUCTS (existantes)
// =========================
Future<int> createProduct(Product product) async {
final db = await database;
return await db.insert('products', product.toMap());
}
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]);
});
}
Future<int> updateProduct(Product product) async {
final db = await database;
return await db.update(
'products',
product.toMap(),
where: 'id = ?',
whereArgs: [product.id],
);
}
Future<int> deleteProduct(int? id) async {
final db = await database;
return await db.delete(
'products',
where: 'id = ?',
whereArgs: [id],
);
}
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);
}
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]);
});
}
Future<int> updateStock(int id, int stock) async {
final db = await database;
return await db
.rawUpdate('UPDATE products SET stock = ? WHERE id = ?', [stock, 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;
}
// =========================
// MÉTHODES CLIENTS
// =========================
Future<int> createClient(Client client) async {
final db = await database;
return await db.insert('clients', client.toMap());
}
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]);
});
}
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;
}
Future<int> updateClient(Client client) async {
final db = await database;
return await db.update(
'clients',
client.toMap(),
where: 'id = ?',
whereArgs: [client.id],
);
}
Future<int> deleteClient(int id) async {
final db = await database;
// Soft delete
return await db.update(
'clients',
{'actif': 0},
where: 'id = ?',
whereArgs: [id],
);
}
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]);
});
}
// =========================
// MÉTHODES COMMANDES
// =========================
Future<int> createCommande(Commande commande) async {
final db = await database;
return await db.insert('commandes', commande.toMap());
}
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) {
return Commande.fromMap(maps[i]);
});
}
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) {
return Commande.fromMap(maps[i]);
});
}
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;
}
Future<int> updateCommande(Commande commande) async {
final db = await database;
return await db.update(
'commandes',
commande.toMap(),
where: 'id = ?',
whereArgs: [commande.id],
);
}
Future<int> updateStatutCommande(int commandeId, StatutCommande statut) async {
final db = await database;
return await db.update(
'commandes',
{'statut': statut.index},
where: 'id = ?',
whereArgs: [commandeId],
);
}
// =========================
// MÉTHODES DÉTAILS COMMANDES
// =========================
Future<int> createDetailCommande(DetailCommande detail) async {
final db = await database;
return await db.insert('details_commandes', detail.toMap());
}
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) {
return DetailCommande.fromMap(maps[i]);
});
}
// =========================
// MÉTHODES TRANSACTION COMPLÈTE
// =========================
Future<int> createCommandeComplete(Client client, Commande commande, List<DetailCommande> details) async {
final db = await database;
return await db.transaction((txn) async {
// Créer le client
final clientId = await txn.insert('clients', client.toMap());
// Créer la commande
final commandeMap = commande.toMap();
commandeMap['clientId'] = clientId;
final commandeId = await txn.insert('commandes', commandeMap);
// Créer les détails et mettre à jour le stock
for (var detail in details) {
final detailMap = detail.toMap();
detailMap['commandeId'] = commandeId; // Ajoute l'ID de la commande
await txn.insert('details_commandes', detailMap);
// Mettre à jour le stock du produit
await txn.rawUpdate(
'UPDATE products SET stock = stock - ? WHERE id = ?',
[detail.quantite, detail.produitId],
);
}
return commandeId;
});
}
// =========================
// 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,
};
}
// =========================
// 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");
}
}
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");
}
}
}
Future<void> close() async {
if (_database.isOpen) {
await _database.close();
}
}
// Ajoutez cette méthode temporaire pour supprimer la DB corrompue
Future<void> deleteDatabaseFile() async {
final documentsDirectory = await getApplicationDocumentsDirectory();
final path = join(documentsDirectory.path, 'products2.db');
final file = File(path);
if (await file.exists()) {
await file.delete();
print("Base de données product supprimée");
}
}
}