Browse Source

modification 2026

28062025_02
Stephane 3 weeks ago
parent
commit
58154af680
  1. 38
      lib/Components/commandManagementComponents/CommandDetails.dart
  2. 26
      lib/Models/Client.dart
  3. 123
      lib/Services/stock_managementDatabase.dart
  4. 442
      lib/Views/commandManagement.dart
  5. 28
      lib/Views/historique.dart

38
lib/Components/commandManagementComponents/CommandDetails.dart

@ -1,5 +1,3 @@
// Remplacez complètement votre fichier CommandeDetails par celui-ci :
import 'package:flutter/material.dart';
import 'package:youmazgestion/Models/client.dart';
import 'package:youmazgestion/Services/stock_managementDatabase.dart';
@ -24,7 +22,8 @@ class CommandeDetails extends StatelessWidget {
);
}
Widget _buildTableCell(String text, {bool isAmount = false, Color? textColor}) {
Widget _buildTableCell(String text,
{bool isAmount = false, Color? textColor}) {
return Padding(
padding: const EdgeInsets.all(8.0),
child: Text(
@ -66,7 +65,9 @@ class CommandeDetails extends StatelessWidget {
),
);
} else {
return _buildTableCell('${NumberFormat('#,##0', 'fr_FR').format(detail.prixUnitaire)} MGA', isAmount: true);
return _buildTableCell(
'${NumberFormat('#,##0', 'fr_FR').format(detail.prixUnitaire)} MGA',
isAmount: true);
}
}
@ -130,7 +131,9 @@ class CommandeDetails extends StatelessWidget {
),
);
} else {
return _buildTableCell('${NumberFormat('#,##0', 'fr_FR').format(detail.prixFinal)} MGA', isAmount: true);
return _buildTableCell(
'${NumberFormat('#,##0', 'fr_FR').format(detail.prixFinal)} MGA',
isAmount: true);
}
}
@ -178,21 +181,27 @@ class CommandeDetails extends StatelessWidget {
children: [
Icon(
hasRemises ? Icons.discount : Icons.receipt_long,
color: hasRemises ? Colors.orange.shade700 : Colors.blue.shade700,
color: hasRemises
? Colors.orange.shade700
: Colors.blue.shade700,
),
const SizedBox(width: 8),
Text(
hasRemises ? 'Détails de la commande (avec remises)' : 'Détails de la commande',
hasRemises
? 'Détails de la commande (avec remises)'
: 'Détails de la commande',
style: TextStyle(
fontWeight: FontWeight.bold,
fontSize: 16,
color: hasRemises ? Colors.orange.shade800 : Colors.black87,
color:
hasRemises ? Colors.orange.shade800 : Colors.black87,
),
),
if (hasRemises) ...[
const Spacer(),
Container(
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4),
padding: const EdgeInsets.symmetric(
horizontal: 8, vertical: 4),
decoration: BoxDecoration(
color: Colors.orange.shade100,
borderRadius: BorderRadius.circular(12),
@ -255,6 +264,17 @@ class CommandeDetails extends StatelessWidget {
fontWeight: FontWeight.w500,
),
),
if (detail.produitImei != null) ...[
const SizedBox(height: 2),
Text(
'IMEI: ${detail.produitImei}',
style: const TextStyle(
fontSize: 10,
color: Colors.grey,
fontStyle: FontStyle.italic,
),
)
],
if (detail.aRemise) ...[
const SizedBox(height: 2),
Row(

26
lib/Models/Client.dart

@ -53,7 +53,8 @@ class Client {
return DateTime.fromMillisecondsSinceEpoch(dateValue);
}
print("Type de date non reconnu: ${dateValue.runtimeType}, valeur: $dateValue");
print(
"Type de date non reconnu: ${dateValue.runtimeType}, valeur: $dateValue");
return DateTime.now();
}
@ -73,11 +74,7 @@ class Client {
String get nomComplet => '$prenom $nom';
}
enum StatutCommande {
enAttente,
confirmee,
annulee
}
enum StatutCommande { enAttente, confirmee, annulee }
class Commande {
final int? id;
@ -173,10 +170,7 @@ class Commande {
}
// REMPLACEZ COMPLÈTEMENT votre classe DetailCommande dans Models/client.dart par celle-ci :
enum RemiseType {
pourcentage,
montant
}
enum RemiseType { pourcentage, montant }
class DetailCommande {
final int? id;
@ -193,6 +187,7 @@ class DetailCommande {
final String? produitNom;
final String? produitImage;
final String? produitReference;
final String? produitImei; // NOUVEAU : IMEI du produit, si applicable
DetailCommande({
this.id,
@ -209,6 +204,7 @@ class DetailCommande {
this.produitNom,
this.produitImage,
this.produitReference,
this.produitImei,
});
// Constructeur pour créer un détail sans remise
@ -222,6 +218,7 @@ class DetailCommande {
String? produitNom,
String? produitImage,
String? produitReference,
String? produitImei,
}) {
final sousTotal = quantite * prixUnitaire;
final prixFinal = estCadeau ? 0.0 : sousTotal;
@ -238,6 +235,7 @@ class DetailCommande {
produitNom: produitNom,
produitImage: produitImage,
produitReference: produitReference,
produitImei: produitImei,
);
}
@ -251,6 +249,7 @@ class DetailCommande {
String? produitNom,
String? produitImage,
String? produitReference,
String? produitImei,
}) {
return DetailCommande(
id: id,
@ -264,6 +263,7 @@ class DetailCommande {
produitNom: produitNom,
produitImage: produitImage,
produitReference: produitReference,
produitImei: produitImei,
);
}
@ -301,6 +301,7 @@ class DetailCommande {
produitNom: produitNom,
produitImage: produitImage,
produitReference: produitReference,
produitImei: produitImei,
);
}
@ -321,6 +322,7 @@ class DetailCommande {
produitNom: produitNom,
produitImage: produitImage,
produitReference: produitReference,
produitImei: produitImei,
);
}
@ -341,6 +343,7 @@ class DetailCommande {
produitNom: produitNom,
produitImage: produitImage,
produitReference: produitReference,
produitImei: produitImei,
);
}
@ -361,11 +364,13 @@ class DetailCommande {
produitNom: produitNom,
produitImage: produitImage,
produitReference: produitReference,
produitImei: produitImei,
);
}
// Getters utiles
bool get aRemise => remiseType != null && montantRemise > 0 && !estCadeau;
bool get aimei => produitImei != null;
double get pourcentageRemise {
if (!aRemise) return 0.0;
@ -432,6 +437,7 @@ class DetailCommande {
produitNom: map['produitNom'] as String?,
produitImage: map['produitImage'] as String?,
produitReference: map['produitReference'] as String?,
produitImei: map['produitImei'] as String?,
);
}
}

123
lib/Services/stock_managementDatabase.dart

@ -47,10 +47,9 @@ class AppDatabase {
await insertDefaultPointsDeVente();
}
String _formatDate(DateTime date) {
String _formatDate(DateTime date) {
return DateFormat('yyyy-MM-dd HH:mm:ss').format(date);
}
}
Future<MySqlConnection> _initDB() async {
try {
@ -608,7 +607,8 @@ String _formatDate(DateTime date) {
return [];
}
}
Future<double> getValeurTotaleStock() async {
Future<double> getValeurTotaleStock() async {
final db = await database;
try {
@ -622,8 +622,7 @@ Future<double> getValeurTotaleStock() async {
print('Erreur lors du calcul de la valeur totale du stock : $e');
return 0.0;
}
}
}
// --- STATISTIQUES ---
@ -914,7 +913,8 @@ Future<double> getValeurTotaleStock() async {
dc.*,
p.name as produitNom,
p.image as produitImage,
p.reference as produitReference
p.reference as produitReference,
p.imei as produitImei
FROM details_commandes dc
LEFT JOIN products p ON dc.produitId = p.id
WHERE dc.commandeId = ?
@ -923,13 +923,14 @@ Future<double> getValeurTotaleStock() async {
return result.map((row) => DetailCommande.fromMap(row.fields)).toList();
}
Future<List<Map<String, dynamic>>> getCommandesParPointDeVente(
Future<List<Map<String, dynamic>>> getCommandesParPointDeVente(
int pointVenteId, {
DateTime? dateDebut,
DateTime? dateFin,
bool aujourdHuiSeulement = false,
int limit = 50,
}) async {
}) async {
final db = await database;
try {
@ -939,7 +940,8 @@ Future<List<Map<String, dynamic>>> getCommandesParPointDeVente(
if (aujourdHuiSeulement) {
final today = DateTime.now();
final startOfDay = DateTime(today.year, today.month, today.day);
final endOfDay = DateTime(today.year, today.month, today.day, 23, 59, 59);
final endOfDay =
DateTime(today.year, today.month, today.day, 23, 59, 59);
whereClause += ' AND c.dateCommande >= ? AND c.dateCommande <= ?';
whereArgs.addAll([
@ -947,7 +949,8 @@ Future<List<Map<String, dynamic>>> getCommandesParPointDeVente(
_formatDate(endOfDay),
]);
} else if (dateDebut != null && dateFin != null) {
final adjustedEndDate = DateTime(dateFin.year, dateFin.month, dateFin.day, 23, 59, 59);
final adjustedEndDate =
DateTime(dateFin.year, dateFin.month, dateFin.day, 23, 59, 59);
whereClause += ' AND c.dateCommande >= ? AND c.dateCommande <= ?';
whereArgs.addAll([
_formatDate(dateDebut),
@ -957,7 +960,8 @@ Future<List<Map<String, dynamic>>> getCommandesParPointDeVente(
whereClause += ' AND c.dateCommande >= ?';
whereArgs.add(_formatDate(dateDebut));
} else if (dateFin != null) {
final adjustedEndDate = DateTime(dateFin.year, dateFin.month, dateFin.day, 23, 59, 59);
final adjustedEndDate =
DateTime(dateFin.year, dateFin.month, dateFin.day, 23, 59, 59);
whereClause += ' AND c.dateCommande <= ?';
whereArgs.add(_formatDate(adjustedEndDate));
}
@ -991,7 +995,7 @@ Future<List<Map<String, dynamic>>> getCommandesParPointDeVente(
print('Erreur récupération commandes: $e');
return [];
}
}
}
// --- RECHERCHE PRODUITS ---
@ -1026,9 +1030,10 @@ Future<List<Map<String, dynamic>>> getCommandesParPointDeVente(
Future<List<Map<String, dynamic>>> getPointsDeVentes() async {
final db = await database;
final result = await db.query('SELECT DISTINCT * FROM pointsdevente ORDER BY nom ASC');
final result =
await db.query('SELECT DISTINCT * FROM pointsdevente ORDER BY nom ASC');
return result.map((row) => row.fields).toList();
}
}
Future<List<Product>> getProductsByCategory(String category) async {
final db = await database;
@ -1241,7 +1246,6 @@ Future<List<Map<String, dynamic>>> getCommandesParPointDeVente(
return result.affectedRows!;
}
// Future<int> deletePointDeVente(int id) async {
// final db = await database;
// final result = await db.query('DELETE FROM points_de_vente WHERE id = ?', [id]);
@ -1434,8 +1438,9 @@ Future<List<Map<String, dynamic>>> getCommandesParPointDeVente(
final result =
await db.query('SELECT * FROM points_de_vente WHERE id = ?', [id]);
return result.isNotEmpty ? result.first : null;
}
List<String> parseHeaderInfo(dynamic blobData) {
}
List<String> parseHeaderInfo(dynamic blobData) {
if (blobData == null) return [];
try {
@ -1472,10 +1477,7 @@ List<String> parseHeaderInfo(dynamic blobData) {
print('❌ Erreur lors du parsing des données d\'en-tête: $e');
return [];
}
}
}
Future<int?> getOrCreatePointDeVenteByNom(String nom) async {
final db = await database;
@ -1976,7 +1978,7 @@ List<String> parseHeaderInfo(dynamic blobData) {
}
// 3. Méthodes pour les commandes
Future<int> updateStatutCommande(
Future<int> updateStatutCommande(
int commandeId, StatutCommande statut) async {
final db = await database;
@ -2016,8 +2018,7 @@ Future<int> updateStatutCommande(
print("Erreur lors de la mise à jour du statut de la commande: $e");
rethrow;
}
}
}
Future<List<Commande>> getCommandesByClient(int clientId) async {
final db = await database;
@ -2557,12 +2558,13 @@ Future<int> updateStatutCommande(
return erreurs;
}
// --- MÉTHODES POUR LES VENTES PAR POINT DE VENTE ---
Future<List<Map<String, dynamic>>> getVentesParPointDeVente({
Future<List<Map<String, dynamic>>> getVentesParPointDeVente({
DateTime? dateDebut,
DateTime? dateFin,
bool? aujourdHuiSeulement = false,
}) async {
}) async {
final db = await database;
try {
@ -2573,7 +2575,8 @@ Future<List<Map<String, dynamic>>> getVentesParPointDeVente({
if (aujourdHuiSeulement == true) {
final today = DateTime.now();
final startOfDay = DateTime(today.year, today.month, today.day);
final endOfDay = DateTime(today.year, today.month, today.day, 23, 59, 59);
final endOfDay =
DateTime(today.year, today.month, today.day, 23, 59, 59);
whereClause += ' AND c.dateCommande >= ? AND c.dateCommande <= ?';
whereArgs.addAll([
@ -2615,15 +2618,15 @@ Future<List<Map<String, dynamic>>> getVentesParPointDeVente({
print("Erreur getVentesParPointDeVente: $e");
return [];
}
}
}
Future<List<Map<String, dynamic>>> getTopProduitsParPointDeVente(
Future<List<Map<String, dynamic>>> getTopProduitsParPointDeVente(
int pointDeVenteId, {
int limit = 5,
DateTime? dateDebut,
DateTime? dateFin,
bool? aujourdHuiSeulement = false,
}) async {
}) async {
final db = await database;
try {
@ -2633,7 +2636,8 @@ Future<List<Map<String, dynamic>>> getTopProduitsParPointDeVente(
if (aujourdHuiSeulement == true) {
final today = DateTime.now();
final startOfDay = DateTime(today.year, today.month, today.day);
final endOfDay = DateTime(today.year, today.month, today.day, 23, 59, 59);
final endOfDay =
DateTime(today.year, today.month, today.day, 23, 59, 59);
whereClause += ' AND c.dateCommande >= ? AND c.dateCommande <= ?';
whereArgs.addAll([
@ -2641,7 +2645,8 @@ Future<List<Map<String, dynamic>>> getTopProduitsParPointDeVente(
_formatDate(endOfDay),
]);
} else if (dateDebut != null && dateFin != null) {
final adjustedEndDate = DateTime(dateFin.year, dateFin.month, dateFin.day, 23, 59, 59);
final adjustedEndDate =
DateTime(dateFin.year, dateFin.month, dateFin.day, 23, 59, 59);
whereClause += ' AND c.dateCommande >= ? AND c.dateCommande <= ?';
whereArgs.addAll([
_formatDate(dateDebut),
@ -2672,7 +2677,7 @@ Future<List<Map<String, dynamic>>> getTopProduitsParPointDeVente(
print("Erreur getTopProduitsParPointDeVente: $e");
return [];
}
}
}
Future<List<Map<String, dynamic>>> getVentesParPointDeVenteParMois(
int pointDeVenteId) async {
@ -3374,9 +3379,8 @@ Future<List<Map<String, dynamic>>> getTopProduitsParPointDeVente(
}
}
// 1. Mise à jour de la méthode dans stock_managementDatabase.dart
Future<List<Map<String, dynamic>>> getHistoriqueSortiesPersonnelles({
Future<List<Map<String, dynamic>>> getHistoriqueSortiesPersonnelles({
int? adminId,
String? statut,
int? pointDeVenteId,
@ -3384,7 +3388,7 @@ Future<List<Map<String, dynamic>>> getHistoriqueSortiesPersonnelles({
DateTime? dateFin,
bool aujourdHuiSeulement = false,
int limit = 50,
}) async {
}) async {
final db = await database;
try {
@ -3398,12 +3402,14 @@ Future<List<Map<String, dynamic>>> getHistoriqueSortiesPersonnelles({
}
if (adminId != null) {
whereClause += (whereClause.isEmpty ? 'WHERE' : ' AND') + ' sp.admin_id = ?';
whereClause +=
(whereClause.isEmpty ? 'WHERE' : ' AND') + ' sp.admin_id = ?';
params.add(adminId);
}
if (statut != null) {
whereClause += (whereClause.isEmpty ? 'WHERE' : ' AND') + ' sp.statut = ?';
whereClause +=
(whereClause.isEmpty ? 'WHERE' : ' AND') + ' sp.statut = ?';
params.add(statut);
}
@ -3411,27 +3417,34 @@ Future<List<Map<String, dynamic>>> getHistoriqueSortiesPersonnelles({
if (aujourdHuiSeulement) {
final today = DateTime.now();
final startOfDay = DateTime(today.year, today.month, today.day);
final endOfDay = DateTime(today.year, today.month, today.day, 23, 59, 59);
final endOfDay =
DateTime(today.year, today.month, today.day, 23, 59, 59);
whereClause += (whereClause.isEmpty ? 'WHERE' : ' AND') +
' sp.date_sortie >= ? AND sp.date_sortie <= ?';
params.add(startOfDay.toIso8601String());
params.add(endOfDay.toIso8601String());
} else if (dateDebut != null && dateFin != null) {
final startOfDay = DateTime(dateDebut.year, dateDebut.month, dateDebut.day);
final endOfDay = DateTime(dateFin.year, dateFin.month, dateFin.day, 23, 59, 59);
final startOfDay =
DateTime(dateDebut.year, dateDebut.month, dateDebut.day);
final endOfDay =
DateTime(dateFin.year, dateFin.month, dateFin.day, 23, 59, 59);
whereClause += (whereClause.isEmpty ? 'WHERE' : ' AND') +
' sp.date_sortie >= ? AND sp.date_sortie <= ?';
params.add(startOfDay.toIso8601String());
params.add(endOfDay.toIso8601String());
} else if (dateDebut != null) {
final startOfDay = DateTime(dateDebut.year, dateDebut.month, dateDebut.day);
whereClause += (whereClause.isEmpty ? 'WHERE' : ' AND') + ' sp.date_sortie >= ?';
final startOfDay =
DateTime(dateDebut.year, dateDebut.month, dateDebut.day);
whereClause +=
(whereClause.isEmpty ? 'WHERE' : ' AND') + ' sp.date_sortie >= ?';
params.add(startOfDay.toIso8601String());
} else if (dateFin != null) {
final endOfDay = DateTime(dateFin.year, dateFin.month, dateFin.day, 23, 59, 59);
whereClause += (whereClause.isEmpty ? 'WHERE' : ' AND') + ' sp.date_sortie <= ?';
final endOfDay =
DateTime(dateFin.year, dateFin.month, dateFin.day, 23, 59, 59);
whereClause +=
(whereClause.isEmpty ? 'WHERE' : ' AND') + ' sp.date_sortie <= ?';
params.add(endOfDay.toIso8601String());
}
@ -3459,8 +3472,9 @@ Future<List<Map<String, dynamic>>> getHistoriqueSortiesPersonnelles({
print('Erreur récupération historique sorties: $e');
return [];
}
}
Future<void> updatePointDeVentes(
}
Future<void> updatePointDeVentes(
int id,
String nom,
String code, {
@ -3468,7 +3482,7 @@ Future<void> updatePointDeVentes(
String? livraison,
String? facture,
Uint8List? imagePath,
}) async {
}) async {
final db = await database;
try {
@ -3496,11 +3510,7 @@ Future<void> updatePointDeVentes(
print('Stacktrace : $stacktrace');
rethrow; // si tu veux faire remonter lerreur plus haut
}
}
}
Future<Map<String, dynamic>> getStatistiquesSortiesPersonnelles() async {
final db = await database;
@ -3559,7 +3569,6 @@ Future<void> updatePointDeVentes(
};
}
}
}
class _formatDate {
}
class _formatDate {}

442
lib/Views/commandManagement.dart

@ -189,7 +189,8 @@ class _GestionCommandesPageState extends State<GestionCommandesPage> {
content: Column(
mainAxisSize: MainAxisSize.min,
children: [
Text('Montant total: ${NumberFormat('#,##0', 'fr_FR').format(montantFinal)} MGA'),
Text(
'Montant total: ${NumberFormat('#,##0', 'fr_FR').format(montantFinal)} MGA'),
const SizedBox(height: 10),
TextField(
controller: amountController,
@ -279,7 +280,7 @@ class _GestionCommandesPageState extends State<GestionCommandesPage> {
///
// Dans GestionCommandesPage - Remplacez la méthode _generateBonLivraison complète
Future<void> _generateBonLivraison(Commande commande) async {
Future<void> _generateBonLivraison(Commande commande) async {
final details = await _database.getDetailsCommande(commande.id!);
final client = await _database.getClientById(commande.clientId);
final pointDeVenteId = commande.pointDeVenteId;
@ -288,12 +289,11 @@ Future<void> _generateBonLivraison(Commande commande) async {
// MODIFICATION: Récupération complète des données du point de vente
if (pointDeVenteId != null) {
pointDeVenteComplet = await _database.getPointDeVenteById(pointDeVenteId);
} else {
} else {
print("ce point de vente n'existe pas");
}
}
final pointDeVente = pointDeVenteComplet;
// Récupérer les informations des vendeurs
final commandeur = commande.commandeurId != null
? await _database.getUserById(commande.commandeurId!)
@ -318,7 +318,6 @@ Future<void> _generateBonLivraison(Commande commande) async {
print('===============================');
}
// Infos par défaut si aucune info personnalisée
final infosLivraisonDefaut = [
'REMAX Andravoangy',
@ -394,8 +393,10 @@ Future<void> _generateBonLivraison(Commande commande) async {
pw.Font? regularFont;
try {
italicFont = pw.Font.ttf(await rootBundle.load('assets/fonts/Roboto-Italic.ttf'));
regularFont = pw.Font.ttf(await rootBundle.load('assets/fonts/Roboto-Regular.ttf'));
italicFont =
pw.Font.ttf(await rootBundle.load('assets/fonts/Roboto-Italic.ttf'));
regularFont =
pw.Font.ttf(await rootBundle.load('assets/fonts/Roboto-Regular.ttf'));
} catch (e) {
print('⚠️ Impossible de charger les polices personnalisées: $e');
}
@ -404,11 +405,19 @@ Future<void> _generateBonLivraison(Commande commande) async {
final tinyTextStyle = pw.TextStyle(fontSize: 9, font: regularFont);
final smallTextStyle = pw.TextStyle(fontSize: 10, font: regularFont);
final normalTextStyle = pw.TextStyle(fontSize: 11, font: regularFont);
final boldTextStyle = pw.TextStyle(fontSize: 11, fontWeight: pw.FontWeight.bold, font: regularFont);
final boldClientStyle = pw.TextStyle(fontSize: 12, fontWeight: pw.FontWeight.bold, font: regularFont);
final boldTextStyle = pw.TextStyle(
fontSize: 11, fontWeight: pw.FontWeight.bold, font: regularFont);
final boldClientStyle = pw.TextStyle(
fontSize: 12, fontWeight: pw.FontWeight.bold, font: regularFont);
final frameTextStyle = pw.TextStyle(fontSize: 10, font: regularFont);
final italicTextStyle = pw.TextStyle(fontSize: 9, fontWeight: pw.FontWeight.bold, font: italicFont ?? regularFont);
final italicLogoStyle = pw.TextStyle(fontSize: 8, fontWeight: pw.FontWeight.bold, font: italicFont ?? regularFont);
final italicTextStyle = pw.TextStyle(
fontSize: 9,
fontWeight: pw.FontWeight.bold,
font: italicFont ?? regularFont);
final italicLogoStyle = pw.TextStyle(
fontSize: 8,
fontWeight: pw.FontWeight.bold,
font: italicFont ?? regularFont);
Future<pw.Widget> buildLogoWidget() async {
final logoRaw = pointDeVenteComplet?['logo'];
@ -424,7 +433,8 @@ Future<void> _generateBonLivraison(Commande commande) async {
dynamic blobDynamic = logoRaw;
bytes = blobDynamic.toBytes();
} else {
throw Exception("Format de logo non supporté: ${logoRaw.runtimeType}");
throw Exception(
"Format de logo non supporté: ${logoRaw.runtimeType}");
}
final imageLogo = pw.MemoryImage(bytes);
@ -434,14 +444,14 @@ Future<void> _generateBonLivraison(Commande commande) async {
}
}
return pw.Image(image, width: 100, height: 100);
}
}
final logoWidget = await buildLogoWidget();
final logoWidget = await buildLogoWidget();
// FONCTION POUR CONSTRUIRE L'EN-TÊTE DYNAMIQUE
pw.Widget buildEnteteInfos() {
final infosAUtiliser = infosLivraison.isNotEmpty ? infosLivraison : infosLivraisonDefaut;
final infosAUtiliser =
infosLivraison.isNotEmpty ? infosLivraison : infosLivraisonDefaut;
return pw.Column(
crossAxisAlignment: pw.CrossAxisAlignment.start,
@ -469,7 +479,9 @@ final logoWidget = await buildLogoWidget();
width: double.infinity,
padding: const pw.EdgeInsets.all(5),
decoration: pw.BoxDecoration(
color: typeExemplaire == "CLIENT" ? PdfColors.blue100 : PdfColors.green100,
color: typeExemplaire == "CLIENT"
? PdfColors.blue100
: PdfColors.green100,
),
child: pw.Center(
child: pw.Text(
@ -477,7 +489,9 @@ final logoWidget = await buildLogoWidget();
style: pw.TextStyle(
fontSize: 14,
fontWeight: pw.FontWeight.bold,
color: typeExemplaire == "CLIENT" ? PdfColors.blue800 : PdfColors.green800,
color: typeExemplaire == "CLIENT"
? PdfColors.blue800
: PdfColors.green800,
font: regularFont,
),
),
@ -500,7 +514,8 @@ final logoWidget = await buildLogoWidget();
children: [
logoWidget,
pw.SizedBox(height: 3),
pw.Text('NOTRE COMPETENCE, A VOTRE SERVICE', style: italicLogoStyle),
pw.Text('NOTRE COMPETENCE, A VOTRE SERVICE',
style: italicLogoStyle),
pw.SizedBox(height: 4),
buildEnteteInfos(), // EN-TÊTE DYNAMIQUE ICI
],
@ -510,9 +525,12 @@ final logoWidget = await buildLogoWidget();
pw.Column(
crossAxisAlignment: pw.CrossAxisAlignment.center,
children: [
pw.Text('Date: ${DateFormat('dd/MM/yyyy').format(DateTime.now())}', style: boldClientStyle),
pw.Text(
'Date: ${DateFormat('dd/MM/yyyy').format(DateTime.now())}',
style: boldClientStyle),
pw.SizedBox(height: 4),
pw.Container(width: 100, height: 2, color: PdfColors.black),
pw.Container(
width: 100, height: 2, color: PdfColors.black),
pw.SizedBox(height: 4),
pw.Container(
padding: const pw.EdgeInsets.all(6),
@ -522,10 +540,13 @@ final logoWidget = await buildLogoWidget();
child: pw.Column(
children: [
pw.Text('Boutique:', style: frameTextStyle),
pw.Text('${pointDeVente?['nom'] ?? 'S405A'}', style: boldTextStyle),
pw.Text('${pointDeVente?['nom'] ?? 'S405A'}',
style: boldTextStyle),
pw.SizedBox(height: 2),
pw.Text('Bon N°:', style: frameTextStyle),
pw.Text('${pointDeVente?['nom'] ?? 'S405A'}-P${commande.id}', style: boldTextStyle),
pw.Text(
'${pointDeVente?['nom'] ?? 'S405A'}-P${commande.id}',
style: boldTextStyle),
],
),
),
@ -536,7 +557,8 @@ final logoWidget = await buildLogoWidget();
pw.Container(
width: 120,
decoration: pw.BoxDecoration(
border: pw.Border.all(color: PdfColors.black, width: 1),
border:
pw.Border.all(color: PdfColors.black, width: 1),
),
padding: const pw.EdgeInsets.all(6),
child: pw.Column(
@ -544,11 +566,20 @@ final logoWidget = await buildLogoWidget();
children: [
pw.Text('CLIENT', style: frameTextStyle),
pw.SizedBox(height: 2),
pw.Text('ID: ${pointDeVente?['nom'] ?? 'S405A'}-${client?.id ?? 'Non spécifié'}', style: smallTextStyle),
pw.Container(width: 100, height: 1, color: PdfColors.black, margin: const pw.EdgeInsets.symmetric(vertical: 2)),
pw.Text('${client?.nom} ${client?.prenom}', style: boldTextStyle),
pw.Text(
'ID: ${pointDeVente?['nom'] ?? 'S405A'}-${client?.id ?? 'Non spécifié'}',
style: smallTextStyle),
pw.Container(
width: 100,
height: 1,
color: PdfColors.black,
margin:
const pw.EdgeInsets.symmetric(vertical: 2)),
pw.Text('${client?.nom} ${client?.prenom}',
style: boldTextStyle),
pw.SizedBox(height: 2),
pw.Text(client?.telephone ?? 'Non spécifié', style: tinyTextStyle),
pw.Text(client?.telephone ?? 'Non spécifié',
style: tinyTextStyle),
],
),
),
@ -562,7 +593,10 @@ final logoWidget = await buildLogoWidget();
children: [
// Debug: Afficher le nombre d'articles
pw.Text('Articles trouvés: ${detailsAvecProduits.length}',
style: pw.TextStyle(fontSize: 8, color: PdfColors.grey, font: regularFont)),
style: pw.TextStyle(
fontSize: 8,
color: PdfColors.grey,
font: regularFont)),
pw.SizedBox(height: 5),
// TABLE SANS CONTRAINTE DE HAUTEUR - Elle s'adapte au contenu
@ -577,24 +611,28 @@ final logoWidget = await buildLogoWidget();
children: [
// En-tête du tableau
pw.TableRow(
decoration: const pw.BoxDecoration(color: PdfColors.grey200),
decoration: const pw.BoxDecoration(
color: PdfColors.grey200),
children: [
pw.Padding(
padding: const pw.EdgeInsets.all(4),
child: pw.Text('Désignations', style: boldTextStyle)
),
child: pw.Text('Désignations',
style: boldTextStyle)),
pw.Padding(
padding: const pw.EdgeInsets.all(4),
child: pw.Text('Qté', style: boldTextStyle, textAlign: pw.TextAlign.center)
),
child: pw.Text('Qté',
style: boldTextStyle,
textAlign: pw.TextAlign.center)),
pw.Padding(
padding: const pw.EdgeInsets.all(4),
child: pw.Text('P.U.', style: boldTextStyle, textAlign: pw.TextAlign.right)
),
child: pw.Text('P.U.',
style: boldTextStyle,
textAlign: pw.TextAlign.right)),
pw.Padding(
padding: const pw.EdgeInsets.all(4),
child: pw.Text('Montant', style: boldTextStyle, textAlign: pw.TextAlign.right)
),
child: pw.Text('Montant',
style: boldTextStyle,
textAlign: pw.TextAlign.right)),
],
),
@ -606,22 +644,27 @@ final logoWidget = await buildLogoWidget();
final produit = item['produit'];
// Debug pour chaque ligne
print('📋 Ligne PDF $index: ${detail.produitNom} (Quantité: ${detail.quantite})');
print(
'📋 Ligne PDF $index: ${detail.produitNom} (Quantité: ${detail.quantite})');
return pw.TableRow(
decoration: detail.estCadeau
? const pw.BoxDecoration(color: PdfColors.green50)
? const pw.BoxDecoration(
color: PdfColors.green50)
: detail.aRemise
? const pw.BoxDecoration(color: PdfColors.orange50)
? const pw.BoxDecoration(
color: PdfColors.orange50)
: index % 2 == 0
? const pw.BoxDecoration(color: PdfColors.grey50)
? const pw.BoxDecoration(
color: PdfColors.grey50)
: null,
children: [
// Colonne Désignations - Plus compacte
pw.Padding(
padding: const pw.EdgeInsets.all(4),
child: pw.Column(
crossAxisAlignment: pw.CrossAxisAlignment.start,
crossAxisAlignment:
pw.CrossAxisAlignment.start,
mainAxisSize: pw.MainAxisSize.min,
children: [
// Nom du produit avec badge
@ -632,27 +675,28 @@ final logoWidget = await buildLogoWidget();
'${detail.produitNom ?? 'Produit inconnu'}',
style: pw.TextStyle(
fontSize: 10,
fontWeight: pw.FontWeight.bold,
font: regularFont
)
),
fontWeight:
pw.FontWeight.bold,
font: regularFont)),
),
if (detail.estCadeau)
pw.Container(
padding: const pw.EdgeInsets.symmetric(horizontal: 3, vertical: 1),
padding:
const pw.EdgeInsets.symmetric(
horizontal: 3,
vertical: 1),
decoration: pw.BoxDecoration(
color: PdfColors.green600,
borderRadius: pw.BorderRadius.circular(3),
borderRadius:
pw.BorderRadius.circular(3),
),
child: pw.Text(
'CADEAU',
child: pw.Text('CADEAU',
style: pw.TextStyle(
fontSize: 6,
color: PdfColors.white,
font: regularFont,
fontWeight: pw.FontWeight.bold
)
),
fontWeight:
pw.FontWeight.bold)),
),
],
),
@ -662,22 +706,47 @@ final logoWidget = await buildLogoWidget();
// Informations complémentaires sur une seule ligne
pw.Text(
[
if (produit?.category?.isNotEmpty == true) produit!.category,
if (produit?.marque?.isNotEmpty == true) produit!.marque,
if (produit?.imei?.isNotEmpty == true) 'IMEI: ${produit!.imei}',
].where((info) => info != null).join(' , '),
style: pw.TextStyle(fontSize: 8, color: PdfColors.grey700, font: regularFont),
if (produit?.category?.isNotEmpty ==
true)
produit!.category,
if (produit?.marque?.isNotEmpty ==
true)
produit!.marque,
if (produit?.imei?.isNotEmpty == true)
'IMEI: ${produit!.imei}',
]
.where((info) => info != null)
.join(' , '),
style: pw.TextStyle(
fontSize: 8,
color: PdfColors.grey700,
font: regularFont),
),
// Spécifications techniques
if (produit?.ram?.isNotEmpty == true || produit?.memoireInterne?.isNotEmpty == true || produit?.reference?.isNotEmpty == true)
if (produit?.ram?.isNotEmpty == true ||
produit?.memoireInterne?.isNotEmpty ==
true ||
produit?.reference?.isNotEmpty ==
true)
pw.Text(
[
if (produit?.ram?.isNotEmpty == true) 'RAM: ${produit!.ram}',
if (produit?.memoireInterne?.isNotEmpty == true) 'Stockage: ${produit!.memoireInterne}',
if (produit?.reference?.isNotEmpty == true) 'Ref: ${produit!.reference}',
if (produit?.ram?.isNotEmpty ==
true)
'RAM: ${produit!.ram}',
if (produit?.memoireInterne
?.isNotEmpty ==
true)
'Stockage: ${produit!.memoireInterne}',
if (produit
?.reference?.isNotEmpty ==
true)
'Ref: ${produit!.reference}',
].join(' , '),
style: pw.TextStyle(fontSize: 8, color: PdfColors.grey600, font: regularFont),
style: pw.TextStyle(
fontSize: 8,
color: PdfColors.grey600,
font: regularFont),
),
],
),
@ -686,18 +755,17 @@ final logoWidget = await buildLogoWidget();
// Colonne Quantité
pw.Padding(
padding: const pw.EdgeInsets.all(4),
child: pw.Text(
'${detail.quantite}',
child: pw.Text('${detail.quantite}',
style: normalTextStyle,
textAlign: pw.TextAlign.center
),
textAlign: pw.TextAlign.center),
),
// Colonne Prix Unitaire
pw.Padding(
padding: const pw.EdgeInsets.all(4),
child: pw.Column(
crossAxisAlignment: pw.CrossAxisAlignment.end,
crossAxisAlignment:
pw.CrossAxisAlignment.end,
mainAxisSize: pw.MainAxisSize.min,
children: [
if (detail.estCadeau) ...[
@ -705,44 +773,37 @@ final logoWidget = await buildLogoWidget();
'${NumberFormat('#,##0', 'fr_FR').format(detail.prixUnitaire)}',
style: pw.TextStyle(
fontSize: 8,
decoration: pw.TextDecoration.lineThrough,
decoration: pw
.TextDecoration.lineThrough,
color: PdfColors.grey600,
font: regularFont
)
),
pw.Text(
'GRATUIT',
font: regularFont)),
pw.Text('GRATUIT',
style: pw.TextStyle(
fontSize: 9,
color: PdfColors.green700,
fontWeight: pw.FontWeight.bold,
font: regularFont
)
),
font: regularFont)),
] else if (detail.aRemise) ...[
pw.Text(
'${NumberFormat('#,##0', 'fr_FR').format(detail.prixUnitaire)}',
style: pw.TextStyle(
fontSize: 8,
decoration: pw.TextDecoration.lineThrough,
decoration: pw
.TextDecoration.lineThrough,
color: PdfColors.grey600,
font: regularFont
)
),
font: regularFont)),
pw.Text(
'${NumberFormat('#,##0', 'fr_FR').format(detail.prixFinal / detail.quantite)}',
style: pw.TextStyle(
fontSize: 10,
color: PdfColors.orange700,
fontWeight: pw.FontWeight.bold,
font: regularFont
)
),
font: regularFont)),
] else
pw.Text(
NumberFormat('#,##0', 'fr_FR').format(detail.prixUnitaire),
style: smallTextStyle
),
NumberFormat('#,##0', 'fr_FR')
.format(detail.prixUnitaire),
style: smallTextStyle),
],
),
),
@ -751,51 +812,45 @@ final logoWidget = await buildLogoWidget();
pw.Padding(
padding: const pw.EdgeInsets.all(4),
child: pw.Column(
crossAxisAlignment: pw.CrossAxisAlignment.end,
crossAxisAlignment:
pw.CrossAxisAlignment.end,
mainAxisSize: pw.MainAxisSize.min,
children: [
if (detail.estCadeau) ...[
pw.Text(
NumberFormat('#,##0', 'fr_FR').format(detail.sousTotal),
NumberFormat('#,##0', 'fr_FR')
.format(detail.sousTotal),
style: pw.TextStyle(
fontSize: 8,
decoration: pw.TextDecoration.lineThrough,
decoration: pw
.TextDecoration.lineThrough,
color: PdfColors.grey600,
font: regularFont
)
),
pw.Text(
'GRATUIT',
font: regularFont)),
pw.Text('GRATUIT',
style: pw.TextStyle(
fontSize: 9,
fontWeight: pw.FontWeight.bold,
color: PdfColors.green700,
font: regularFont
)
),
font: regularFont)),
] else if (detail.aRemise) ...[
pw.Text(
'${NumberFormat('#,##0', 'fr_FR').format(detail.sousTotal)}',
style: pw.TextStyle(
fontSize: 8,
decoration: pw.TextDecoration.lineThrough,
decoration: pw
.TextDecoration.lineThrough,
color: PdfColors.grey600,
font: regularFont
)
),
font: regularFont)),
pw.Text(
'${NumberFormat('#,##0', 'fr_FR').format(detail.prixFinal)}',
style: pw.TextStyle(
fontSize: 10,
fontWeight: pw.FontWeight.bold,
font: regularFont
)
),
font: regularFont)),
] else
pw.Text(
'${NumberFormat('#,##0', 'fr_FR').format(detail.prixFinal)}',
style: smallTextStyle
),
style: smallTextStyle),
],
),
),
@ -825,44 +880,67 @@ final logoWidget = await buildLogoWidget();
children: [
pw.Text('SOUS-TOTAL:', style: smallTextStyle),
pw.SizedBox(width: 10),
pw.Text('${NumberFormat('#,##0', 'fr_FR').format(sousTotal)}', style: smallTextStyle),
pw.Text(
'${NumberFormat('#,##0', 'fr_FR').format(sousTotal)}',
style: smallTextStyle),
],
),
pw.SizedBox(height: 2),
],
if (totalRemises > 0) ...[
pw.Row(
mainAxisAlignment: pw.MainAxisAlignment.end,
children: [
pw.Text('REMISES:', style: pw.TextStyle(color: PdfColors.orange, fontSize: 10, font: regularFont)),
pw.Text('REMISES:',
style: pw.TextStyle(
color: PdfColors.orange,
fontSize: 10,
font: regularFont)),
pw.SizedBox(width: 10),
pw.Text('-${NumberFormat('#,##0', 'fr_FR').format(totalRemises)}', style: pw.TextStyle(color: PdfColors.orange, fontSize: 10, font: regularFont)),
pw.Text(
'-${NumberFormat('#,##0', 'fr_FR').format(totalRemises)}',
style: pw.TextStyle(
color: PdfColors.orange,
fontSize: 10,
font: regularFont)),
],
),
pw.SizedBox(height: 2),
],
if (totalCadeaux > 0) ...[
pw.Row(
mainAxisAlignment: pw.MainAxisAlignment.end,
children: [
pw.Text('CADEAUX ($nombreCadeaux):', style: pw.TextStyle(color: PdfColors.green700, fontSize: 10, font: regularFont)),
pw.Text('CADEAUX ($nombreCadeaux):',
style: pw.TextStyle(
color: PdfColors.green700,
fontSize: 10,
font: regularFont)),
pw.SizedBox(width: 10),
pw.Text('-${NumberFormat('#,##0', 'fr_FR').format(totalCadeaux)}', style: pw.TextStyle(color: PdfColors.green700, fontSize: 10, font: regularFont)),
pw.Text(
'-${NumberFormat('#,##0', 'fr_FR').format(totalCadeaux)}',
style: pw.TextStyle(
color: PdfColors.green700,
fontSize: 10,
font: regularFont)),
],
),
pw.SizedBox(height: 2),
],
pw.Container(width: 120, height: 1.5, color: PdfColors.black, margin: const pw.EdgeInsets.symmetric(vertical: 2)),
pw.Container(
width: 120,
height: 1.5,
color: PdfColors.black,
margin:
const pw.EdgeInsets.symmetric(vertical: 2)),
pw.Row(
mainAxisAlignment: pw.MainAxisAlignment.end,
children: [
pw.Text('TOTAL:', style: boldTextStyle),
pw.SizedBox(width: 10),
pw.Text('${NumberFormat('#,##0', 'fr_FR').format(commande.montantTotal)} MGA', style: boldTextStyle),
pw.Text(
'${NumberFormat('#,##0', 'fr_FR').format(commande.montantTotal)} MGA',
style: boldTextStyle),
],
),
],
@ -887,30 +965,48 @@ final logoWidget = await buildLogoWidget();
child: pw.Column(
crossAxisAlignment: pw.CrossAxisAlignment.start,
children: [
pw.Text('VENDEURS', style: pw.TextStyle(fontSize: 10, fontWeight: pw.FontWeight.bold, font: regularFont)),
pw.Text('VENDEURS',
style: pw.TextStyle(
fontSize: 10,
fontWeight: pw.FontWeight.bold,
font: regularFont)),
pw.SizedBox(height: 3),
pw.Row(
children: [
pw.Expanded(
child: pw.Column(
crossAxisAlignment: pw.CrossAxisAlignment.start,
crossAxisAlignment:
pw.CrossAxisAlignment.start,
children: [
pw.Text('Initiateur:', style: tinyTextStyle),
pw.Text('Initiateur:',
style: tinyTextStyle),
pw.Text(
commandeur != null ? '${commandeur.name} ${commandeur.lastName ?? ''}'.trim() : 'N/A',
style: pw.TextStyle(fontSize: 9, font: regularFont),
commandeur != null
? '${commandeur.name} ${commandeur.lastName ?? ''}'
.trim()
: 'N/A',
style: pw.TextStyle(
fontSize: 9,
font: regularFont),
),
],
),
),
pw.Expanded(
child: pw.Column(
crossAxisAlignment: pw.CrossAxisAlignment.start,
crossAxisAlignment:
pw.CrossAxisAlignment.start,
children: [
pw.Text('Validateur:', style: tinyTextStyle),
pw.Text('Validateur:',
style: tinyTextStyle),
pw.Text(
validateur != null ? '${validateur.name} ${validateur.lastName ?? ''}'.trim() : 'N/A',
style: pw.TextStyle(fontSize: 9, font: regularFont),
validateur != null
? '${validateur.name} ${validateur.lastName ?? ''}'
.trim()
: 'N/A',
style: pw.TextStyle(
fontSize: 9,
font: regularFont),
),
],
),
@ -925,20 +1021,35 @@ final logoWidget = await buildLogoWidget();
// Signatures
pw.Row(
mainAxisAlignment: pw.MainAxisAlignment.spaceBetween,
mainAxisAlignment:
pw.MainAxisAlignment.spaceBetween,
children: [
pw.Column(
children: [
pw.Text('Vendeur', style: pw.TextStyle(fontSize: 9, fontWeight: pw.FontWeight.bold, font: regularFont)),
pw.Text('Vendeur',
style: pw.TextStyle(
fontSize: 9,
fontWeight: pw.FontWeight.bold,
font: regularFont)),
pw.SizedBox(height: 15),
pw.Container(width: 70, height: 1, color: PdfColors.black),
pw.Container(
width: 70,
height: 1,
color: PdfColors.black),
],
),
pw.Column(
children: [
pw.Text('Client', style: pw.TextStyle(fontSize: 9, fontWeight: pw.FontWeight.bold, font: regularFont)),
pw.Text('Client',
style: pw.TextStyle(
fontSize: 9,
fontWeight: pw.FontWeight.bold,
font: regularFont)),
pw.SizedBox(height: 15),
pw.Container(width: 70, height: 1, color: PdfColors.black),
pw.Container(
width: 70,
height: 1,
color: PdfColors.black),
],
),
],
@ -989,13 +1100,21 @@ final logoWidget = await buildLogoWidget();
border: pw.Border.all(color: PdfColors.black, width: 2),
),
child: pw.Center(
child: pw.Text('X', style: pw.TextStyle(fontSize: 12, fontWeight: pw.FontWeight.bold, font: regularFont)),
child: pw.Text('X',
style: pw.TextStyle(
fontSize: 12,
fontWeight: pw.FontWeight.bold,
font: regularFont)),
),
),
pw.SizedBox(height: 10),
pw.Transform.rotate(
angle: 1.5708,
child: pw.Text('DÉCOUPER ICI', style: pw.TextStyle(fontSize: 10, fontWeight: pw.FontWeight.bold, font: regularFont)),
child: pw.Text('DÉCOUPER ICI',
style: pw.TextStyle(
fontSize: 10,
fontWeight: pw.FontWeight.bold,
font: regularFont)),
),
pw.SizedBox(height: 10),
pw.Container(
@ -1006,7 +1125,11 @@ final logoWidget = await buildLogoWidget();
border: pw.Border.all(color: PdfColors.black, width: 2),
),
child: pw.Center(
child: pw.Text('X', style: pw.TextStyle(fontSize: 12, fontWeight: pw.FontWeight.bold, font: regularFont)),
child: pw.Text('X',
style: pw.TextStyle(
fontSize: 12,
fontWeight: pw.FontWeight.bold,
font: regularFont)),
),
),
],
@ -1030,7 +1153,7 @@ final logoWidget = await buildLogoWidget();
// Partager ou ouvrir le fichier
await OpenFile.open(file.path);
}
}
//==============================================================
// Modifiez la méthode _generateInvoice dans GestionCommandesPage
@ -1149,11 +1272,13 @@ final logoWidget = await buildLogoWidget();
dynamic blobDynamic = logoRaw;
bytes = blobDynamic.toBytes();
} else {
throw Exception("Format de logo non supporté: ${logoRaw.runtimeType}");
throw Exception(
"Format de logo non supporté: ${logoRaw.runtimeType}");
}
final imageLogo = pw.MemoryImage(bytes);
return pw.Container(width: 200, height: 120, child: pw.Image(imageLogo));
return pw.Container(
width: 200, height: 120, child: pw.Image(imageLogo));
} catch (e) {
print('Erreur chargement logo BDD: $e');
}
@ -1162,8 +1287,9 @@ final logoWidget = await buildLogoWidget();
}
final logoWidget = await buildLogoWidget();
pw.Widget buildEnteteFactureInfos() {
final infosAUtiliser = infosFacture.isNotEmpty ? infosFacture : infosFactureDefaut;
pw.Widget buildEnteteFactureInfos() {
final infosAUtiliser =
infosFacture.isNotEmpty ? infosFacture : infosFactureDefaut;
return pw.Column(
crossAxisAlignment: pw.CrossAxisAlignment.start,
@ -1180,8 +1306,7 @@ pw.Widget buildEnteteFactureInfos() {
pw.SizedBox(height: 2), // ajouté en fin de liste
],
);
}
}
pdf.addPage(
pw.Page(
@ -1566,7 +1691,8 @@ pw.Widget buildEnteteFactureInfos() {
pw.SizedBox(width: 20),
pw.Container(
width: 80,
child: pw.Text('${NumberFormat('#,##0', 'fr_FR').format(sousTotal)}',
child: pw.Text(
'${NumberFormat('#,##0', 'fr_FR').format(sousTotal)}',
style: normalTextStyle,
textAlign: pw.TextAlign.right),
),
@ -2001,7 +2127,6 @@ pw.Widget buildEnteteFactureInfos() {
Future<void> _generateReceipt(
Commande commande, PaymentMethod payment) async {
final details = await _database.getDetailsCommande(commande.id!);
final client = await _database.getClientById(commande.clientId);
final commandeur = commande.commandeurId != null
@ -2242,7 +2367,8 @@ pw.Widget buildEnteteFactureInfos() {
children: [
pw.Text('SOUS-TOTAL:',
style: const pw.TextStyle(fontSize: 8)),
pw.Text('${NumberFormat('#,##0', 'fr_FR').format(sousTotal)} MGA',
pw.Text(
'${NumberFormat('#,##0', 'fr_FR').format(sousTotal)} MGA',
style: const pw.TextStyle(fontSize: 8)),
],
),
@ -2253,7 +2379,8 @@ pw.Widget buildEnteteFactureInfos() {
pw.Text('REMISES:',
style: pw.TextStyle(
fontSize: 8, color: PdfColors.orange)),
pw.Text('-${NumberFormat('#,##0', 'fr_FR').format(totalRemises)} MGA',
pw.Text(
'-${NumberFormat('#,##0', 'fr_FR').format(totalRemises)} MGA',
style: pw.TextStyle(
fontSize: 8, color: PdfColors.orange)),
],
@ -2266,7 +2393,8 @@ pw.Widget buildEnteteFactureInfos() {
pw.Text('CADEAUX ($nombreCadeaux):',
style: pw.TextStyle(
fontSize: 8, color: PdfColors.green700)),
pw.Text('-${NumberFormat('#,##0', 'fr_FR').format(totalCadeaux)} MGA',
pw.Text(
'-${NumberFormat('#,##0', 'fr_FR').format(totalCadeaux)} MGA',
style: pw.TextStyle(
fontSize: 8, color: PdfColors.green700)),
],
@ -2282,7 +2410,8 @@ pw.Widget buildEnteteFactureInfos() {
pw.Text('TOTAL:',
style: pw.TextStyle(
fontSize: 9, fontWeight: pw.FontWeight.bold)),
pw.Text('${NumberFormat('#,##0', 'fr_FR').format(commande.montantTotal)} MGA',
pw.Text(
'${NumberFormat('#,##0', 'fr_FR').format(commande.montantTotal)} MGA',
style: pw.TextStyle(
fontSize: 9, fontWeight: pw.FontWeight.bold)),
],
@ -2974,7 +3103,8 @@ pw.Widget buildEnteteFactureInfos() {
borderRadius: BorderRadius.circular(8),
boxShadow: [
BoxShadow(
color: Colors.black.withOpacity(0.1),
color:
Colors.black.withOpacity(0.1),
blurRadius: 2,
offset: const Offset(0, 1),
),
@ -2985,8 +3115,10 @@ pw.Widget buildEnteteFactureInfos() {
Icons.payment,
color: Colors.green.shade600,
),
onPressed: () => _showPaymentOptions(commande),
tooltip: 'Générer le ticket de la commande',
onPressed: () =>
_showPaymentOptions(commande),
tooltip:
'Générer le ticket de la commande',
),
),
const SizedBox(
@ -3067,8 +3199,8 @@ pw.Widget buildEnteteFactureInfos() {
CommandeActions(
commande: commande,
onStatutChanged: _updateStatut,
onGenerateBonLivraison:_generateBon_lifraisonWithPasswordVerification
),
onGenerateBonLivraison:
_generateBon_lifraisonWithPasswordVerification),
],
),
),

28
lib/Views/historique.dart

@ -92,7 +92,9 @@ class _HistoriquePageState extends State<HistoriquePage> {
final pointDeVenteId = _userController.pointDeVenteId;
final commandes = pointDeVenteId == 0
? allCommandes
: allCommandes.where((cmd) => cmd.pointDeVenteId == pointDeVenteId).toList();
: allCommandes
.where((cmd) => cmd.pointDeVenteId == pointDeVenteId)
.toList();
setState(() {
_commandes.clear();
@ -146,7 +148,8 @@ class _HistoriquePageState extends State<HistoriquePage> {
_filteredCommandes.clear();
for (var commande in _commandes) {
if (pointDeVenteId != 0 && commande.pointDeVenteId != pointDeVenteId) continue;
if (pointDeVenteId != 0 && commande.pointDeVenteId != pointDeVenteId)
continue;
bool matchesSearch = searchText.isEmpty ||
commande.clientNom!.toLowerCase().contains(searchText) ||
commande.clientPrenom!.toLowerCase().contains(searchText) ||
@ -641,10 +644,25 @@ class _HistoriquePageState extends State<HistoriquePage> {
),
child: const Icon(Icons.shopping_bag, size: 20),
),
title: Text(
title: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
detail.produitNom ?? 'Produit inconnu',
style:
const TextStyle(fontWeight: FontWeight.w500),
style: const TextStyle(
fontWeight: FontWeight.w500),
),
if (detail.produitImei != null) ...[
const SizedBox(height: 4),
Text(
'IMEI: ${detail.produitImei}',
style: TextStyle(
fontSize: 12,
color: Colors.grey.shade600,
),
),
],
],
),
subtitle: Text(
'${detail.quantite} x ${NumberFormat('#,##0.00', 'fr_FR').format(detail.prixUnitaire)} MGA',

Loading…
Cancel
Save