modification 2026
This commit is contained in:
parent
865410ae93
commit
58154af680
@ -1,5 +1,3 @@
|
|||||||
// Remplacez complètement votre fichier CommandeDetails par celui-ci :
|
|
||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:youmazgestion/Models/client.dart';
|
import 'package:youmazgestion/Models/client.dart';
|
||||||
import 'package:youmazgestion/Services/stock_managementDatabase.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(
|
return Padding(
|
||||||
padding: const EdgeInsets.all(8.0),
|
padding: const EdgeInsets.all(8.0),
|
||||||
child: Text(
|
child: Text(
|
||||||
@ -66,7 +65,9 @@ class CommandeDetails extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
);
|
);
|
||||||
} else {
|
} 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 {
|
} 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: [
|
children: [
|
||||||
Icon(
|
Icon(
|
||||||
hasRemises ? Icons.discount : Icons.receipt_long,
|
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),
|
const SizedBox(width: 8),
|
||||||
Text(
|
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(
|
style: TextStyle(
|
||||||
fontWeight: FontWeight.bold,
|
fontWeight: FontWeight.bold,
|
||||||
fontSize: 16,
|
fontSize: 16,
|
||||||
color: hasRemises ? Colors.orange.shade800 : Colors.black87,
|
color:
|
||||||
|
hasRemises ? Colors.orange.shade800 : Colors.black87,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
if (hasRemises) ...[
|
if (hasRemises) ...[
|
||||||
const Spacer(),
|
const Spacer(),
|
||||||
Container(
|
Container(
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4),
|
padding: const EdgeInsets.symmetric(
|
||||||
|
horizontal: 8, vertical: 4),
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: Colors.orange.shade100,
|
color: Colors.orange.shade100,
|
||||||
borderRadius: BorderRadius.circular(12),
|
borderRadius: BorderRadius.circular(12),
|
||||||
@ -255,6 +264,17 @@ class CommandeDetails extends StatelessWidget {
|
|||||||
fontWeight: FontWeight.w500,
|
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) ...[
|
if (detail.aRemise) ...[
|
||||||
const SizedBox(height: 2),
|
const SizedBox(height: 2),
|
||||||
Row(
|
Row(
|
||||||
|
|||||||
@ -53,7 +53,8 @@ class Client {
|
|||||||
return DateTime.fromMillisecondsSinceEpoch(dateValue);
|
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();
|
return DateTime.now();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -73,11 +74,7 @@ class Client {
|
|||||||
String get nomComplet => '$prenom $nom';
|
String get nomComplet => '$prenom $nom';
|
||||||
}
|
}
|
||||||
|
|
||||||
enum StatutCommande {
|
enum StatutCommande { enAttente, confirmee, annulee }
|
||||||
enAttente,
|
|
||||||
confirmee,
|
|
||||||
annulee
|
|
||||||
}
|
|
||||||
|
|
||||||
class Commande {
|
class Commande {
|
||||||
final int? id;
|
final int? id;
|
||||||
@ -173,10 +170,7 @@ class Commande {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// REMPLACEZ COMPLÈTEMENT votre classe DetailCommande dans Models/client.dart par celle-ci :
|
// REMPLACEZ COMPLÈTEMENT votre classe DetailCommande dans Models/client.dart par celle-ci :
|
||||||
enum RemiseType {
|
enum RemiseType { pourcentage, montant }
|
||||||
pourcentage,
|
|
||||||
montant
|
|
||||||
}
|
|
||||||
|
|
||||||
class DetailCommande {
|
class DetailCommande {
|
||||||
final int? id;
|
final int? id;
|
||||||
@ -193,6 +187,7 @@ class DetailCommande {
|
|||||||
final String? produitNom;
|
final String? produitNom;
|
||||||
final String? produitImage;
|
final String? produitImage;
|
||||||
final String? produitReference;
|
final String? produitReference;
|
||||||
|
final String? produitImei; // NOUVEAU : IMEI du produit, si applicable
|
||||||
|
|
||||||
DetailCommande({
|
DetailCommande({
|
||||||
this.id,
|
this.id,
|
||||||
@ -209,6 +204,7 @@ class DetailCommande {
|
|||||||
this.produitNom,
|
this.produitNom,
|
||||||
this.produitImage,
|
this.produitImage,
|
||||||
this.produitReference,
|
this.produitReference,
|
||||||
|
this.produitImei,
|
||||||
});
|
});
|
||||||
|
|
||||||
// Constructeur pour créer un détail sans remise
|
// Constructeur pour créer un détail sans remise
|
||||||
@ -222,6 +218,7 @@ class DetailCommande {
|
|||||||
String? produitNom,
|
String? produitNom,
|
||||||
String? produitImage,
|
String? produitImage,
|
||||||
String? produitReference,
|
String? produitReference,
|
||||||
|
String? produitImei,
|
||||||
}) {
|
}) {
|
||||||
final sousTotal = quantite * prixUnitaire;
|
final sousTotal = quantite * prixUnitaire;
|
||||||
final prixFinal = estCadeau ? 0.0 : sousTotal;
|
final prixFinal = estCadeau ? 0.0 : sousTotal;
|
||||||
@ -238,6 +235,7 @@ class DetailCommande {
|
|||||||
produitNom: produitNom,
|
produitNom: produitNom,
|
||||||
produitImage: produitImage,
|
produitImage: produitImage,
|
||||||
produitReference: produitReference,
|
produitReference: produitReference,
|
||||||
|
produitImei: produitImei,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -251,6 +249,7 @@ class DetailCommande {
|
|||||||
String? produitNom,
|
String? produitNom,
|
||||||
String? produitImage,
|
String? produitImage,
|
||||||
String? produitReference,
|
String? produitReference,
|
||||||
|
String? produitImei,
|
||||||
}) {
|
}) {
|
||||||
return DetailCommande(
|
return DetailCommande(
|
||||||
id: id,
|
id: id,
|
||||||
@ -264,6 +263,7 @@ class DetailCommande {
|
|||||||
produitNom: produitNom,
|
produitNom: produitNom,
|
||||||
produitImage: produitImage,
|
produitImage: produitImage,
|
||||||
produitReference: produitReference,
|
produitReference: produitReference,
|
||||||
|
produitImei: produitImei,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -301,6 +301,7 @@ class DetailCommande {
|
|||||||
produitNom: produitNom,
|
produitNom: produitNom,
|
||||||
produitImage: produitImage,
|
produitImage: produitImage,
|
||||||
produitReference: produitReference,
|
produitReference: produitReference,
|
||||||
|
produitImei: produitImei,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -321,6 +322,7 @@ class DetailCommande {
|
|||||||
produitNom: produitNom,
|
produitNom: produitNom,
|
||||||
produitImage: produitImage,
|
produitImage: produitImage,
|
||||||
produitReference: produitReference,
|
produitReference: produitReference,
|
||||||
|
produitImei: produitImei,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -341,6 +343,7 @@ class DetailCommande {
|
|||||||
produitNom: produitNom,
|
produitNom: produitNom,
|
||||||
produitImage: produitImage,
|
produitImage: produitImage,
|
||||||
produitReference: produitReference,
|
produitReference: produitReference,
|
||||||
|
produitImei: produitImei,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -361,11 +364,13 @@ class DetailCommande {
|
|||||||
produitNom: produitNom,
|
produitNom: produitNom,
|
||||||
produitImage: produitImage,
|
produitImage: produitImage,
|
||||||
produitReference: produitReference,
|
produitReference: produitReference,
|
||||||
|
produitImei: produitImei,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Getters utiles
|
// Getters utiles
|
||||||
bool get aRemise => remiseType != null && montantRemise > 0 && !estCadeau;
|
bool get aRemise => remiseType != null && montantRemise > 0 && !estCadeau;
|
||||||
|
bool get aimei => produitImei != null;
|
||||||
|
|
||||||
double get pourcentageRemise {
|
double get pourcentageRemise {
|
||||||
if (!aRemise) return 0.0;
|
if (!aRemise) return 0.0;
|
||||||
@ -432,6 +437,7 @@ class DetailCommande {
|
|||||||
produitNom: map['produitNom'] as String?,
|
produitNom: map['produitNom'] as String?,
|
||||||
produitImage: map['produitImage'] as String?,
|
produitImage: map['produitImage'] as String?,
|
||||||
produitReference: map['produitReference'] as String?,
|
produitReference: map['produitReference'] as String?,
|
||||||
|
produitImei: map['produitImei'] as String?,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -47,10 +47,9 @@ class AppDatabase {
|
|||||||
await insertDefaultPointsDeVente();
|
await insertDefaultPointsDeVente();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
String _formatDate(DateTime date) {
|
||||||
String _formatDate(DateTime date) {
|
|
||||||
return DateFormat('yyyy-MM-dd HH:mm:ss').format(date);
|
return DateFormat('yyyy-MM-dd HH:mm:ss').format(date);
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<MySqlConnection> _initDB() async {
|
Future<MySqlConnection> _initDB() async {
|
||||||
try {
|
try {
|
||||||
@ -608,7 +607,8 @@ String _formatDate(DateTime date) {
|
|||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Future<double> getValeurTotaleStock() async {
|
|
||||||
|
Future<double> getValeurTotaleStock() async {
|
||||||
final db = await database;
|
final db = await database;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@ -622,8 +622,7 @@ Future<double> getValeurTotaleStock() async {
|
|||||||
print('Erreur lors du calcul de la valeur totale du stock : $e');
|
print('Erreur lors du calcul de la valeur totale du stock : $e');
|
||||||
return 0.0;
|
return 0.0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// --- STATISTIQUES ---
|
// --- STATISTIQUES ---
|
||||||
|
|
||||||
@ -914,7 +913,8 @@ Future<double> getValeurTotaleStock() async {
|
|||||||
dc.*,
|
dc.*,
|
||||||
p.name as produitNom,
|
p.name as produitNom,
|
||||||
p.image as produitImage,
|
p.image as produitImage,
|
||||||
p.reference as produitReference
|
p.reference as produitReference,
|
||||||
|
p.imei as produitImei
|
||||||
FROM details_commandes dc
|
FROM details_commandes dc
|
||||||
LEFT JOIN products p ON dc.produitId = p.id
|
LEFT JOIN products p ON dc.produitId = p.id
|
||||||
WHERE dc.commandeId = ?
|
WHERE dc.commandeId = ?
|
||||||
@ -923,13 +923,14 @@ Future<double> getValeurTotaleStock() async {
|
|||||||
|
|
||||||
return result.map((row) => DetailCommande.fromMap(row.fields)).toList();
|
return result.map((row) => DetailCommande.fromMap(row.fields)).toList();
|
||||||
}
|
}
|
||||||
Future<List<Map<String, dynamic>>> getCommandesParPointDeVente(
|
|
||||||
|
Future<List<Map<String, dynamic>>> getCommandesParPointDeVente(
|
||||||
int pointVenteId, {
|
int pointVenteId, {
|
||||||
DateTime? dateDebut,
|
DateTime? dateDebut,
|
||||||
DateTime? dateFin,
|
DateTime? dateFin,
|
||||||
bool aujourdHuiSeulement = false,
|
bool aujourdHuiSeulement = false,
|
||||||
int limit = 50,
|
int limit = 50,
|
||||||
}) async {
|
}) async {
|
||||||
final db = await database;
|
final db = await database;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@ -939,7 +940,8 @@ Future<List<Map<String, dynamic>>> getCommandesParPointDeVente(
|
|||||||
if (aujourdHuiSeulement) {
|
if (aujourdHuiSeulement) {
|
||||||
final today = DateTime.now();
|
final today = DateTime.now();
|
||||||
final startOfDay = DateTime(today.year, today.month, today.day);
|
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 <= ?';
|
whereClause += ' AND c.dateCommande >= ? AND c.dateCommande <= ?';
|
||||||
whereArgs.addAll([
|
whereArgs.addAll([
|
||||||
@ -947,7 +949,8 @@ Future<List<Map<String, dynamic>>> getCommandesParPointDeVente(
|
|||||||
_formatDate(endOfDay),
|
_formatDate(endOfDay),
|
||||||
]);
|
]);
|
||||||
} else if (dateDebut != null && dateFin != null) {
|
} 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 <= ?';
|
whereClause += ' AND c.dateCommande >= ? AND c.dateCommande <= ?';
|
||||||
whereArgs.addAll([
|
whereArgs.addAll([
|
||||||
_formatDate(dateDebut),
|
_formatDate(dateDebut),
|
||||||
@ -957,7 +960,8 @@ Future<List<Map<String, dynamic>>> getCommandesParPointDeVente(
|
|||||||
whereClause += ' AND c.dateCommande >= ?';
|
whereClause += ' AND c.dateCommande >= ?';
|
||||||
whereArgs.add(_formatDate(dateDebut));
|
whereArgs.add(_formatDate(dateDebut));
|
||||||
} else if (dateFin != null) {
|
} 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 <= ?';
|
whereClause += ' AND c.dateCommande <= ?';
|
||||||
whereArgs.add(_formatDate(adjustedEndDate));
|
whereArgs.add(_formatDate(adjustedEndDate));
|
||||||
}
|
}
|
||||||
@ -991,7 +995,7 @@ Future<List<Map<String, dynamic>>> getCommandesParPointDeVente(
|
|||||||
print('Erreur récupération commandes: $e');
|
print('Erreur récupération commandes: $e');
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// --- RECHERCHE PRODUITS ---
|
// --- RECHERCHE PRODUITS ---
|
||||||
|
|
||||||
@ -1026,9 +1030,10 @@ Future<List<Map<String, dynamic>>> getCommandesParPointDeVente(
|
|||||||
|
|
||||||
Future<List<Map<String, dynamic>>> getPointsDeVentes() async {
|
Future<List<Map<String, dynamic>>> getPointsDeVentes() async {
|
||||||
final db = await database;
|
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();
|
return result.map((row) => row.fields).toList();
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<List<Product>> getProductsByCategory(String category) async {
|
Future<List<Product>> getProductsByCategory(String category) async {
|
||||||
final db = await database;
|
final db = await database;
|
||||||
@ -1241,7 +1246,6 @@ Future<List<Map<String, dynamic>>> getCommandesParPointDeVente(
|
|||||||
return result.affectedRows!;
|
return result.affectedRows!;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Future<int> deletePointDeVente(int id) async {
|
// Future<int> deletePointDeVente(int id) async {
|
||||||
// final db = await database;
|
// final db = await database;
|
||||||
// final result = await db.query('DELETE FROM points_de_vente WHERE id = ?', [id]);
|
// 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 =
|
final result =
|
||||||
await db.query('SELECT * FROM points_de_vente WHERE id = ?', [id]);
|
await db.query('SELECT * FROM points_de_vente WHERE id = ?', [id]);
|
||||||
return result.isNotEmpty ? result.first : null;
|
return result.isNotEmpty ? result.first : null;
|
||||||
}
|
}
|
||||||
List<String> parseHeaderInfo(dynamic blobData) {
|
|
||||||
|
List<String> parseHeaderInfo(dynamic blobData) {
|
||||||
if (blobData == null) return [];
|
if (blobData == null) return [];
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@ -1472,10 +1477,7 @@ List<String> parseHeaderInfo(dynamic blobData) {
|
|||||||
print('❌ Erreur lors du parsing des données d\'en-tête: $e');
|
print('❌ Erreur lors du parsing des données d\'en-tête: $e');
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Future<int?> getOrCreatePointDeVenteByNom(String nom) async {
|
Future<int?> getOrCreatePointDeVenteByNom(String nom) async {
|
||||||
final db = await database;
|
final db = await database;
|
||||||
@ -1976,7 +1978,7 @@ List<String> parseHeaderInfo(dynamic blobData) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 3. Méthodes pour les commandes
|
// 3. Méthodes pour les commandes
|
||||||
Future<int> updateStatutCommande(
|
Future<int> updateStatutCommande(
|
||||||
int commandeId, StatutCommande statut) async {
|
int commandeId, StatutCommande statut) async {
|
||||||
final db = await database;
|
final db = await database;
|
||||||
|
|
||||||
@ -2016,8 +2018,7 @@ Future<int> updateStatutCommande(
|
|||||||
print("Erreur lors de la mise à jour du statut de la commande: $e");
|
print("Erreur lors de la mise à jour du statut de la commande: $e");
|
||||||
rethrow;
|
rethrow;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
Future<List<Commande>> getCommandesByClient(int clientId) async {
|
Future<List<Commande>> getCommandesByClient(int clientId) async {
|
||||||
final db = await database;
|
final db = await database;
|
||||||
@ -2557,12 +2558,13 @@ Future<int> updateStatutCommande(
|
|||||||
|
|
||||||
return erreurs;
|
return erreurs;
|
||||||
}
|
}
|
||||||
|
|
||||||
// --- MÉTHODES POUR LES VENTES PAR POINT DE VENTE ---
|
// --- MÉTHODES POUR LES VENTES PAR POINT DE VENTE ---
|
||||||
Future<List<Map<String, dynamic>>> getVentesParPointDeVente({
|
Future<List<Map<String, dynamic>>> getVentesParPointDeVente({
|
||||||
DateTime? dateDebut,
|
DateTime? dateDebut,
|
||||||
DateTime? dateFin,
|
DateTime? dateFin,
|
||||||
bool? aujourdHuiSeulement = false,
|
bool? aujourdHuiSeulement = false,
|
||||||
}) async {
|
}) async {
|
||||||
final db = await database;
|
final db = await database;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@ -2573,7 +2575,8 @@ Future<List<Map<String, dynamic>>> getVentesParPointDeVente({
|
|||||||
if (aujourdHuiSeulement == true) {
|
if (aujourdHuiSeulement == true) {
|
||||||
final today = DateTime.now();
|
final today = DateTime.now();
|
||||||
final startOfDay = DateTime(today.year, today.month, today.day);
|
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 <= ?';
|
whereClause += ' AND c.dateCommande >= ? AND c.dateCommande <= ?';
|
||||||
whereArgs.addAll([
|
whereArgs.addAll([
|
||||||
@ -2615,15 +2618,15 @@ Future<List<Map<String, dynamic>>> getVentesParPointDeVente({
|
|||||||
print("Erreur getVentesParPointDeVente: $e");
|
print("Erreur getVentesParPointDeVente: $e");
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<List<Map<String, dynamic>>> getTopProduitsParPointDeVente(
|
Future<List<Map<String, dynamic>>> getTopProduitsParPointDeVente(
|
||||||
int pointDeVenteId, {
|
int pointDeVenteId, {
|
||||||
int limit = 5,
|
int limit = 5,
|
||||||
DateTime? dateDebut,
|
DateTime? dateDebut,
|
||||||
DateTime? dateFin,
|
DateTime? dateFin,
|
||||||
bool? aujourdHuiSeulement = false,
|
bool? aujourdHuiSeulement = false,
|
||||||
}) async {
|
}) async {
|
||||||
final db = await database;
|
final db = await database;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@ -2633,7 +2636,8 @@ Future<List<Map<String, dynamic>>> getTopProduitsParPointDeVente(
|
|||||||
if (aujourdHuiSeulement == true) {
|
if (aujourdHuiSeulement == true) {
|
||||||
final today = DateTime.now();
|
final today = DateTime.now();
|
||||||
final startOfDay = DateTime(today.year, today.month, today.day);
|
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 <= ?';
|
whereClause += ' AND c.dateCommande >= ? AND c.dateCommande <= ?';
|
||||||
whereArgs.addAll([
|
whereArgs.addAll([
|
||||||
@ -2641,7 +2645,8 @@ Future<List<Map<String, dynamic>>> getTopProduitsParPointDeVente(
|
|||||||
_formatDate(endOfDay),
|
_formatDate(endOfDay),
|
||||||
]);
|
]);
|
||||||
} else if (dateDebut != null && dateFin != null) {
|
} 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 <= ?';
|
whereClause += ' AND c.dateCommande >= ? AND c.dateCommande <= ?';
|
||||||
whereArgs.addAll([
|
whereArgs.addAll([
|
||||||
_formatDate(dateDebut),
|
_formatDate(dateDebut),
|
||||||
@ -2672,7 +2677,7 @@ Future<List<Map<String, dynamic>>> getTopProduitsParPointDeVente(
|
|||||||
print("Erreur getTopProduitsParPointDeVente: $e");
|
print("Erreur getTopProduitsParPointDeVente: $e");
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<List<Map<String, dynamic>>> getVentesParPointDeVenteParMois(
|
Future<List<Map<String, dynamic>>> getVentesParPointDeVenteParMois(
|
||||||
int pointDeVenteId) async {
|
int pointDeVenteId) async {
|
||||||
@ -3374,9 +3379,8 @@ Future<List<Map<String, dynamic>>> getTopProduitsParPointDeVente(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// 1. Mise à jour de la méthode dans stock_managementDatabase.dart
|
// 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,
|
int? adminId,
|
||||||
String? statut,
|
String? statut,
|
||||||
int? pointDeVenteId,
|
int? pointDeVenteId,
|
||||||
@ -3384,7 +3388,7 @@ Future<List<Map<String, dynamic>>> getHistoriqueSortiesPersonnelles({
|
|||||||
DateTime? dateFin,
|
DateTime? dateFin,
|
||||||
bool aujourdHuiSeulement = false,
|
bool aujourdHuiSeulement = false,
|
||||||
int limit = 50,
|
int limit = 50,
|
||||||
}) async {
|
}) async {
|
||||||
final db = await database;
|
final db = await database;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@ -3398,12 +3402,14 @@ Future<List<Map<String, dynamic>>> getHistoriqueSortiesPersonnelles({
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (adminId != null) {
|
if (adminId != null) {
|
||||||
whereClause += (whereClause.isEmpty ? 'WHERE' : ' AND') + ' sp.admin_id = ?';
|
whereClause +=
|
||||||
|
(whereClause.isEmpty ? 'WHERE' : ' AND') + ' sp.admin_id = ?';
|
||||||
params.add(adminId);
|
params.add(adminId);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (statut != null) {
|
if (statut != null) {
|
||||||
whereClause += (whereClause.isEmpty ? 'WHERE' : ' AND') + ' sp.statut = ?';
|
whereClause +=
|
||||||
|
(whereClause.isEmpty ? 'WHERE' : ' AND') + ' sp.statut = ?';
|
||||||
params.add(statut);
|
params.add(statut);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -3411,27 +3417,34 @@ Future<List<Map<String, dynamic>>> getHistoriqueSortiesPersonnelles({
|
|||||||
if (aujourdHuiSeulement) {
|
if (aujourdHuiSeulement) {
|
||||||
final today = DateTime.now();
|
final today = DateTime.now();
|
||||||
final startOfDay = DateTime(today.year, today.month, today.day);
|
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') +
|
whereClause += (whereClause.isEmpty ? 'WHERE' : ' AND') +
|
||||||
' sp.date_sortie >= ? AND sp.date_sortie <= ?';
|
' sp.date_sortie >= ? AND sp.date_sortie <= ?';
|
||||||
params.add(startOfDay.toIso8601String());
|
params.add(startOfDay.toIso8601String());
|
||||||
params.add(endOfDay.toIso8601String());
|
params.add(endOfDay.toIso8601String());
|
||||||
} else if (dateDebut != null && dateFin != null) {
|
} else if (dateDebut != null && dateFin != null) {
|
||||||
final startOfDay = DateTime(dateDebut.year, dateDebut.month, dateDebut.day);
|
final startOfDay =
|
||||||
final endOfDay = DateTime(dateFin.year, dateFin.month, dateFin.day, 23, 59, 59);
|
DateTime(dateDebut.year, dateDebut.month, dateDebut.day);
|
||||||
|
final endOfDay =
|
||||||
|
DateTime(dateFin.year, dateFin.month, dateFin.day, 23, 59, 59);
|
||||||
|
|
||||||
whereClause += (whereClause.isEmpty ? 'WHERE' : ' AND') +
|
whereClause += (whereClause.isEmpty ? 'WHERE' : ' AND') +
|
||||||
' sp.date_sortie >= ? AND sp.date_sortie <= ?';
|
' sp.date_sortie >= ? AND sp.date_sortie <= ?';
|
||||||
params.add(startOfDay.toIso8601String());
|
params.add(startOfDay.toIso8601String());
|
||||||
params.add(endOfDay.toIso8601String());
|
params.add(endOfDay.toIso8601String());
|
||||||
} else if (dateDebut != null) {
|
} else if (dateDebut != null) {
|
||||||
final startOfDay = DateTime(dateDebut.year, dateDebut.month, dateDebut.day);
|
final startOfDay =
|
||||||
whereClause += (whereClause.isEmpty ? 'WHERE' : ' AND') + ' sp.date_sortie >= ?';
|
DateTime(dateDebut.year, dateDebut.month, dateDebut.day);
|
||||||
|
whereClause +=
|
||||||
|
(whereClause.isEmpty ? 'WHERE' : ' AND') + ' sp.date_sortie >= ?';
|
||||||
params.add(startOfDay.toIso8601String());
|
params.add(startOfDay.toIso8601String());
|
||||||
} else if (dateFin != null) {
|
} else if (dateFin != null) {
|
||||||
final endOfDay = DateTime(dateFin.year, dateFin.month, dateFin.day, 23, 59, 59);
|
final endOfDay =
|
||||||
whereClause += (whereClause.isEmpty ? 'WHERE' : ' AND') + ' sp.date_sortie <= ?';
|
DateTime(dateFin.year, dateFin.month, dateFin.day, 23, 59, 59);
|
||||||
|
whereClause +=
|
||||||
|
(whereClause.isEmpty ? 'WHERE' : ' AND') + ' sp.date_sortie <= ?';
|
||||||
params.add(endOfDay.toIso8601String());
|
params.add(endOfDay.toIso8601String());
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -3459,8 +3472,9 @@ Future<List<Map<String, dynamic>>> getHistoriqueSortiesPersonnelles({
|
|||||||
print('Erreur récupération historique sorties: $e');
|
print('Erreur récupération historique sorties: $e');
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Future<void> updatePointDeVentes(
|
|
||||||
|
Future<void> updatePointDeVentes(
|
||||||
int id,
|
int id,
|
||||||
String nom,
|
String nom,
|
||||||
String code, {
|
String code, {
|
||||||
@ -3468,7 +3482,7 @@ Future<void> updatePointDeVentes(
|
|||||||
String? livraison,
|
String? livraison,
|
||||||
String? facture,
|
String? facture,
|
||||||
Uint8List? imagePath,
|
Uint8List? imagePath,
|
||||||
}) async {
|
}) async {
|
||||||
final db = await database;
|
final db = await database;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@ -3496,11 +3510,7 @@ Future<void> updatePointDeVentes(
|
|||||||
print('Stacktrace : $stacktrace');
|
print('Stacktrace : $stacktrace');
|
||||||
rethrow; // si tu veux faire remonter l’erreur plus haut
|
rethrow; // si tu veux faire remonter l’erreur plus haut
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Future<Map<String, dynamic>> getStatistiquesSortiesPersonnelles() async {
|
Future<Map<String, dynamic>> getStatistiquesSortiesPersonnelles() async {
|
||||||
final db = await database;
|
final db = await database;
|
||||||
@ -3559,7 +3569,6 @@ Future<void> updatePointDeVentes(
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
class _formatDate {
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class _formatDate {}
|
||||||
|
|||||||
@ -189,7 +189,8 @@ class _GestionCommandesPageState extends State<GestionCommandesPage> {
|
|||||||
content: Column(
|
content: Column(
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
children: [
|
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),
|
const SizedBox(height: 10),
|
||||||
TextField(
|
TextField(
|
||||||
controller: amountController,
|
controller: amountController,
|
||||||
@ -279,7 +280,7 @@ class _GestionCommandesPageState extends State<GestionCommandesPage> {
|
|||||||
///
|
///
|
||||||
// Dans GestionCommandesPage - Remplacez la méthode _generateBonLivraison complète
|
// 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 details = await _database.getDetailsCommande(commande.id!);
|
||||||
final client = await _database.getClientById(commande.clientId);
|
final client = await _database.getClientById(commande.clientId);
|
||||||
final pointDeVenteId = commande.pointDeVenteId;
|
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
|
// ✅ MODIFICATION: Récupération complète des données du point de vente
|
||||||
if (pointDeVenteId != null) {
|
if (pointDeVenteId != null) {
|
||||||
pointDeVenteComplet = await _database.getPointDeVenteById(pointDeVenteId);
|
pointDeVenteComplet = await _database.getPointDeVenteById(pointDeVenteId);
|
||||||
} else {
|
} else {
|
||||||
print("ce point de vente n'existe pas");
|
print("ce point de vente n'existe pas");
|
||||||
}
|
}
|
||||||
final pointDeVente = pointDeVenteComplet;
|
final pointDeVente = pointDeVenteComplet;
|
||||||
|
|
||||||
|
|
||||||
// Récupérer les informations des vendeurs
|
// Récupérer les informations des vendeurs
|
||||||
final commandeur = commande.commandeurId != null
|
final commandeur = commande.commandeurId != null
|
||||||
? await _database.getUserById(commande.commandeurId!)
|
? await _database.getUserById(commande.commandeurId!)
|
||||||
@ -318,7 +318,6 @@ Future<void> _generateBonLivraison(Commande commande) async {
|
|||||||
print('===============================');
|
print('===============================');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Infos par défaut si aucune info personnalisée
|
// Infos par défaut si aucune info personnalisée
|
||||||
final infosLivraisonDefaut = [
|
final infosLivraisonDefaut = [
|
||||||
'REMAX Andravoangy',
|
'REMAX Andravoangy',
|
||||||
@ -394,8 +393,10 @@ Future<void> _generateBonLivraison(Commande commande) async {
|
|||||||
pw.Font? regularFont;
|
pw.Font? regularFont;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
italicFont = pw.Font.ttf(await rootBundle.load('assets/fonts/Roboto-Italic.ttf'));
|
italicFont =
|
||||||
regularFont = pw.Font.ttf(await rootBundle.load('assets/fonts/Roboto-Regular.ttf'));
|
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) {
|
} catch (e) {
|
||||||
print('⚠️ Impossible de charger les polices personnalisées: $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 tinyTextStyle = pw.TextStyle(fontSize: 9, font: regularFont);
|
||||||
final smallTextStyle = pw.TextStyle(fontSize: 10, font: regularFont);
|
final smallTextStyle = pw.TextStyle(fontSize: 10, font: regularFont);
|
||||||
final normalTextStyle = pw.TextStyle(fontSize: 11, font: regularFont);
|
final normalTextStyle = pw.TextStyle(fontSize: 11, font: regularFont);
|
||||||
final boldTextStyle = pw.TextStyle(fontSize: 11, fontWeight: pw.FontWeight.bold, font: regularFont);
|
final boldTextStyle = pw.TextStyle(
|
||||||
final boldClientStyle = pw.TextStyle(fontSize: 12, fontWeight: pw.FontWeight.bold, font: regularFont);
|
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 frameTextStyle = pw.TextStyle(fontSize: 10, font: regularFont);
|
||||||
final italicTextStyle = pw.TextStyle(fontSize: 9, fontWeight: pw.FontWeight.bold, font: italicFont ?? regularFont);
|
final italicTextStyle = pw.TextStyle(
|
||||||
final italicLogoStyle = pw.TextStyle(fontSize: 8, fontWeight: pw.FontWeight.bold, font: italicFont ?? regularFont);
|
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 {
|
Future<pw.Widget> buildLogoWidget() async {
|
||||||
final logoRaw = pointDeVenteComplet?['logo'];
|
final logoRaw = pointDeVenteComplet?['logo'];
|
||||||
|
|
||||||
@ -424,7 +433,8 @@ Future<void> _generateBonLivraison(Commande commande) async {
|
|||||||
dynamic blobDynamic = logoRaw;
|
dynamic blobDynamic = logoRaw;
|
||||||
bytes = blobDynamic.toBytes();
|
bytes = blobDynamic.toBytes();
|
||||||
} else {
|
} else {
|
||||||
throw Exception("Format de logo non supporté: ${logoRaw.runtimeType}");
|
throw Exception(
|
||||||
|
"Format de logo non supporté: ${logoRaw.runtimeType}");
|
||||||
}
|
}
|
||||||
|
|
||||||
final imageLogo = pw.MemoryImage(bytes);
|
final imageLogo = pw.MemoryImage(bytes);
|
||||||
@ -434,14 +444,14 @@ Future<void> _generateBonLivraison(Commande commande) async {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
return pw.Image(image, width: 100, height: 100);
|
return pw.Image(image, width: 100, height: 100);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
final logoWidget = await buildLogoWidget();
|
||||||
final logoWidget = await buildLogoWidget();
|
|
||||||
|
|
||||||
// ✅ FONCTION POUR CONSTRUIRE L'EN-TÊTE DYNAMIQUE
|
// ✅ FONCTION POUR CONSTRUIRE L'EN-TÊTE DYNAMIQUE
|
||||||
pw.Widget buildEnteteInfos() {
|
pw.Widget buildEnteteInfos() {
|
||||||
final infosAUtiliser = infosLivraison.isNotEmpty ? infosLivraison : infosLivraisonDefaut;
|
final infosAUtiliser =
|
||||||
|
infosLivraison.isNotEmpty ? infosLivraison : infosLivraisonDefaut;
|
||||||
|
|
||||||
return pw.Column(
|
return pw.Column(
|
||||||
crossAxisAlignment: pw.CrossAxisAlignment.start,
|
crossAxisAlignment: pw.CrossAxisAlignment.start,
|
||||||
@ -469,7 +479,9 @@ final logoWidget = await buildLogoWidget();
|
|||||||
width: double.infinity,
|
width: double.infinity,
|
||||||
padding: const pw.EdgeInsets.all(5),
|
padding: const pw.EdgeInsets.all(5),
|
||||||
decoration: pw.BoxDecoration(
|
decoration: pw.BoxDecoration(
|
||||||
color: typeExemplaire == "CLIENT" ? PdfColors.blue100 : PdfColors.green100,
|
color: typeExemplaire == "CLIENT"
|
||||||
|
? PdfColors.blue100
|
||||||
|
: PdfColors.green100,
|
||||||
),
|
),
|
||||||
child: pw.Center(
|
child: pw.Center(
|
||||||
child: pw.Text(
|
child: pw.Text(
|
||||||
@ -477,7 +489,9 @@ final logoWidget = await buildLogoWidget();
|
|||||||
style: pw.TextStyle(
|
style: pw.TextStyle(
|
||||||
fontSize: 14,
|
fontSize: 14,
|
||||||
fontWeight: pw.FontWeight.bold,
|
fontWeight: pw.FontWeight.bold,
|
||||||
color: typeExemplaire == "CLIENT" ? PdfColors.blue800 : PdfColors.green800,
|
color: typeExemplaire == "CLIENT"
|
||||||
|
? PdfColors.blue800
|
||||||
|
: PdfColors.green800,
|
||||||
font: regularFont,
|
font: regularFont,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@ -500,7 +514,8 @@ final logoWidget = await buildLogoWidget();
|
|||||||
children: [
|
children: [
|
||||||
logoWidget,
|
logoWidget,
|
||||||
pw.SizedBox(height: 3),
|
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),
|
pw.SizedBox(height: 4),
|
||||||
buildEnteteInfos(), // ✅ EN-TÊTE DYNAMIQUE ICI
|
buildEnteteInfos(), // ✅ EN-TÊTE DYNAMIQUE ICI
|
||||||
],
|
],
|
||||||
@ -510,9 +525,12 @@ final logoWidget = await buildLogoWidget();
|
|||||||
pw.Column(
|
pw.Column(
|
||||||
crossAxisAlignment: pw.CrossAxisAlignment.center,
|
crossAxisAlignment: pw.CrossAxisAlignment.center,
|
||||||
children: [
|
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.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.SizedBox(height: 4),
|
||||||
pw.Container(
|
pw.Container(
|
||||||
padding: const pw.EdgeInsets.all(6),
|
padding: const pw.EdgeInsets.all(6),
|
||||||
@ -522,10 +540,13 @@ final logoWidget = await buildLogoWidget();
|
|||||||
child: pw.Column(
|
child: pw.Column(
|
||||||
children: [
|
children: [
|
||||||
pw.Text('Boutique:', style: frameTextStyle),
|
pw.Text('Boutique:', style: frameTextStyle),
|
||||||
pw.Text('${pointDeVente?['nom'] ?? 'S405A'}', style: boldTextStyle),
|
pw.Text('${pointDeVente?['nom'] ?? 'S405A'}',
|
||||||
|
style: boldTextStyle),
|
||||||
pw.SizedBox(height: 2),
|
pw.SizedBox(height: 2),
|
||||||
pw.Text('Bon N°:', style: frameTextStyle),
|
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(
|
pw.Container(
|
||||||
width: 120,
|
width: 120,
|
||||||
decoration: pw.BoxDecoration(
|
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),
|
padding: const pw.EdgeInsets.all(6),
|
||||||
child: pw.Column(
|
child: pw.Column(
|
||||||
@ -544,11 +566,20 @@ final logoWidget = await buildLogoWidget();
|
|||||||
children: [
|
children: [
|
||||||
pw.Text('CLIENT', style: frameTextStyle),
|
pw.Text('CLIENT', style: frameTextStyle),
|
||||||
pw.SizedBox(height: 2),
|
pw.SizedBox(height: 2),
|
||||||
pw.Text('ID: ${pointDeVente?['nom'] ?? 'S405A'}-${client?.id ?? 'Non spécifié'}', style: smallTextStyle),
|
pw.Text(
|
||||||
pw.Container(width: 100, height: 1, color: PdfColors.black, margin: const pw.EdgeInsets.symmetric(vertical: 2)),
|
'ID: ${pointDeVente?['nom'] ?? 'S405A'}-${client?.id ?? 'Non spécifié'}',
|
||||||
pw.Text('${client?.nom} ${client?.prenom}', style: boldTextStyle),
|
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.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: [
|
children: [
|
||||||
// Debug: Afficher le nombre d'articles
|
// Debug: Afficher le nombre d'articles
|
||||||
pw.Text('Articles trouvés: ${detailsAvecProduits.length}',
|
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),
|
pw.SizedBox(height: 5),
|
||||||
|
|
||||||
// ✅ TABLE SANS CONTRAINTE DE HAUTEUR - Elle s'adapte au contenu
|
// ✅ TABLE SANS CONTRAINTE DE HAUTEUR - Elle s'adapte au contenu
|
||||||
@ -577,24 +611,28 @@ final logoWidget = await buildLogoWidget();
|
|||||||
children: [
|
children: [
|
||||||
// En-tête du tableau
|
// En-tête du tableau
|
||||||
pw.TableRow(
|
pw.TableRow(
|
||||||
decoration: const pw.BoxDecoration(color: PdfColors.grey200),
|
decoration: const pw.BoxDecoration(
|
||||||
|
color: PdfColors.grey200),
|
||||||
children: [
|
children: [
|
||||||
pw.Padding(
|
pw.Padding(
|
||||||
padding: const pw.EdgeInsets.all(4),
|
padding: const pw.EdgeInsets.all(4),
|
||||||
child: pw.Text('Désignations', style: boldTextStyle)
|
child: pw.Text('Désignations',
|
||||||
),
|
style: boldTextStyle)),
|
||||||
pw.Padding(
|
pw.Padding(
|
||||||
padding: const pw.EdgeInsets.all(4),
|
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(
|
pw.Padding(
|
||||||
padding: const pw.EdgeInsets.all(4),
|
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(
|
pw.Padding(
|
||||||
padding: const pw.EdgeInsets.all(4),
|
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'];
|
final produit = item['produit'];
|
||||||
|
|
||||||
// Debug pour chaque ligne
|
// 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(
|
return pw.TableRow(
|
||||||
decoration: detail.estCadeau
|
decoration: detail.estCadeau
|
||||||
? const pw.BoxDecoration(color: PdfColors.green50)
|
? const pw.BoxDecoration(
|
||||||
|
color: PdfColors.green50)
|
||||||
: detail.aRemise
|
: detail.aRemise
|
||||||
? const pw.BoxDecoration(color: PdfColors.orange50)
|
? const pw.BoxDecoration(
|
||||||
|
color: PdfColors.orange50)
|
||||||
: index % 2 == 0
|
: index % 2 == 0
|
||||||
? const pw.BoxDecoration(color: PdfColors.grey50)
|
? const pw.BoxDecoration(
|
||||||
|
color: PdfColors.grey50)
|
||||||
: null,
|
: null,
|
||||||
children: [
|
children: [
|
||||||
// ✅ Colonne Désignations - Plus compacte
|
// ✅ Colonne Désignations - Plus compacte
|
||||||
pw.Padding(
|
pw.Padding(
|
||||||
padding: const pw.EdgeInsets.all(4),
|
padding: const pw.EdgeInsets.all(4),
|
||||||
child: pw.Column(
|
child: pw.Column(
|
||||||
crossAxisAlignment: pw.CrossAxisAlignment.start,
|
crossAxisAlignment:
|
||||||
|
pw.CrossAxisAlignment.start,
|
||||||
mainAxisSize: pw.MainAxisSize.min,
|
mainAxisSize: pw.MainAxisSize.min,
|
||||||
children: [
|
children: [
|
||||||
// Nom du produit avec badge
|
// Nom du produit avec badge
|
||||||
@ -632,27 +675,28 @@ final logoWidget = await buildLogoWidget();
|
|||||||
'${detail.produitNom ?? 'Produit inconnu'}',
|
'${detail.produitNom ?? 'Produit inconnu'}',
|
||||||
style: pw.TextStyle(
|
style: pw.TextStyle(
|
||||||
fontSize: 10,
|
fontSize: 10,
|
||||||
fontWeight: pw.FontWeight.bold,
|
fontWeight:
|
||||||
font: regularFont
|
pw.FontWeight.bold,
|
||||||
)
|
font: regularFont)),
|
||||||
),
|
|
||||||
),
|
),
|
||||||
if (detail.estCadeau)
|
if (detail.estCadeau)
|
||||||
pw.Container(
|
pw.Container(
|
||||||
padding: const pw.EdgeInsets.symmetric(horizontal: 3, vertical: 1),
|
padding:
|
||||||
|
const pw.EdgeInsets.symmetric(
|
||||||
|
horizontal: 3,
|
||||||
|
vertical: 1),
|
||||||
decoration: pw.BoxDecoration(
|
decoration: pw.BoxDecoration(
|
||||||
color: PdfColors.green600,
|
color: PdfColors.green600,
|
||||||
borderRadius: pw.BorderRadius.circular(3),
|
borderRadius:
|
||||||
|
pw.BorderRadius.circular(3),
|
||||||
),
|
),
|
||||||
child: pw.Text(
|
child: pw.Text('CADEAU',
|
||||||
'CADEAU',
|
|
||||||
style: pw.TextStyle(
|
style: pw.TextStyle(
|
||||||
fontSize: 6,
|
fontSize: 6,
|
||||||
color: PdfColors.white,
|
color: PdfColors.white,
|
||||||
font: regularFont,
|
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
|
// Informations complémentaires sur une seule ligne
|
||||||
pw.Text(
|
pw.Text(
|
||||||
[
|
[
|
||||||
if (produit?.category?.isNotEmpty == true) produit!.category,
|
if (produit?.category?.isNotEmpty ==
|
||||||
if (produit?.marque?.isNotEmpty == true) produit!.marque,
|
true)
|
||||||
if (produit?.imei?.isNotEmpty == true) 'IMEI: ${produit!.imei}',
|
produit!.category,
|
||||||
].where((info) => info != null).join(' , '),
|
if (produit?.marque?.isNotEmpty ==
|
||||||
style: pw.TextStyle(fontSize: 8, color: PdfColors.grey700, font: regularFont),
|
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
|
// 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(
|
pw.Text(
|
||||||
[
|
[
|
||||||
if (produit?.ram?.isNotEmpty == true) 'RAM: ${produit!.ram}',
|
if (produit?.ram?.isNotEmpty ==
|
||||||
if (produit?.memoireInterne?.isNotEmpty == true) 'Stockage: ${produit!.memoireInterne}',
|
true)
|
||||||
if (produit?.reference?.isNotEmpty == true) 'Ref: ${produit!.reference}',
|
'RAM: ${produit!.ram}',
|
||||||
|
if (produit?.memoireInterne
|
||||||
|
?.isNotEmpty ==
|
||||||
|
true)
|
||||||
|
'Stockage: ${produit!.memoireInterne}',
|
||||||
|
if (produit
|
||||||
|
?.reference?.isNotEmpty ==
|
||||||
|
true)
|
||||||
|
'Ref: ${produit!.reference}',
|
||||||
].join(' , '),
|
].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é
|
// Colonne Quantité
|
||||||
pw.Padding(
|
pw.Padding(
|
||||||
padding: const pw.EdgeInsets.all(4),
|
padding: const pw.EdgeInsets.all(4),
|
||||||
child: pw.Text(
|
child: pw.Text('${detail.quantite}',
|
||||||
'${detail.quantite}',
|
|
||||||
style: normalTextStyle,
|
style: normalTextStyle,
|
||||||
textAlign: pw.TextAlign.center
|
textAlign: pw.TextAlign.center),
|
||||||
),
|
|
||||||
),
|
),
|
||||||
|
|
||||||
// Colonne Prix Unitaire
|
// Colonne Prix Unitaire
|
||||||
pw.Padding(
|
pw.Padding(
|
||||||
padding: const pw.EdgeInsets.all(4),
|
padding: const pw.EdgeInsets.all(4),
|
||||||
child: pw.Column(
|
child: pw.Column(
|
||||||
crossAxisAlignment: pw.CrossAxisAlignment.end,
|
crossAxisAlignment:
|
||||||
|
pw.CrossAxisAlignment.end,
|
||||||
mainAxisSize: pw.MainAxisSize.min,
|
mainAxisSize: pw.MainAxisSize.min,
|
||||||
children: [
|
children: [
|
||||||
if (detail.estCadeau) ...[
|
if (detail.estCadeau) ...[
|
||||||
@ -705,44 +773,37 @@ final logoWidget = await buildLogoWidget();
|
|||||||
'${NumberFormat('#,##0', 'fr_FR').format(detail.prixUnitaire)}',
|
'${NumberFormat('#,##0', 'fr_FR').format(detail.prixUnitaire)}',
|
||||||
style: pw.TextStyle(
|
style: pw.TextStyle(
|
||||||
fontSize: 8,
|
fontSize: 8,
|
||||||
decoration: pw.TextDecoration.lineThrough,
|
decoration: pw
|
||||||
|
.TextDecoration.lineThrough,
|
||||||
color: PdfColors.grey600,
|
color: PdfColors.grey600,
|
||||||
font: regularFont
|
font: regularFont)),
|
||||||
)
|
pw.Text('GRATUIT',
|
||||||
),
|
|
||||||
pw.Text(
|
|
||||||
'GRATUIT',
|
|
||||||
style: pw.TextStyle(
|
style: pw.TextStyle(
|
||||||
fontSize: 9,
|
fontSize: 9,
|
||||||
color: PdfColors.green700,
|
color: PdfColors.green700,
|
||||||
fontWeight: pw.FontWeight.bold,
|
fontWeight: pw.FontWeight.bold,
|
||||||
font: regularFont
|
font: regularFont)),
|
||||||
)
|
|
||||||
),
|
|
||||||
] else if (detail.aRemise) ...[
|
] else if (detail.aRemise) ...[
|
||||||
pw.Text(
|
pw.Text(
|
||||||
'${NumberFormat('#,##0', 'fr_FR').format(detail.prixUnitaire)}',
|
'${NumberFormat('#,##0', 'fr_FR').format(detail.prixUnitaire)}',
|
||||||
style: pw.TextStyle(
|
style: pw.TextStyle(
|
||||||
fontSize: 8,
|
fontSize: 8,
|
||||||
decoration: pw.TextDecoration.lineThrough,
|
decoration: pw
|
||||||
|
.TextDecoration.lineThrough,
|
||||||
color: PdfColors.grey600,
|
color: PdfColors.grey600,
|
||||||
font: regularFont
|
font: regularFont)),
|
||||||
)
|
|
||||||
),
|
|
||||||
pw.Text(
|
pw.Text(
|
||||||
'${NumberFormat('#,##0', 'fr_FR').format(detail.prixFinal / detail.quantite)}',
|
'${NumberFormat('#,##0', 'fr_FR').format(detail.prixFinal / detail.quantite)}',
|
||||||
style: pw.TextStyle(
|
style: pw.TextStyle(
|
||||||
fontSize: 10,
|
fontSize: 10,
|
||||||
color: PdfColors.orange700,
|
color: PdfColors.orange700,
|
||||||
fontWeight: pw.FontWeight.bold,
|
fontWeight: pw.FontWeight.bold,
|
||||||
font: regularFont
|
font: regularFont)),
|
||||||
)
|
|
||||||
),
|
|
||||||
] else
|
] else
|
||||||
pw.Text(
|
pw.Text(
|
||||||
NumberFormat('#,##0', 'fr_FR').format(detail.prixUnitaire),
|
NumberFormat('#,##0', 'fr_FR')
|
||||||
style: smallTextStyle
|
.format(detail.prixUnitaire),
|
||||||
),
|
style: smallTextStyle),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@ -751,51 +812,45 @@ final logoWidget = await buildLogoWidget();
|
|||||||
pw.Padding(
|
pw.Padding(
|
||||||
padding: const pw.EdgeInsets.all(4),
|
padding: const pw.EdgeInsets.all(4),
|
||||||
child: pw.Column(
|
child: pw.Column(
|
||||||
crossAxisAlignment: pw.CrossAxisAlignment.end,
|
crossAxisAlignment:
|
||||||
|
pw.CrossAxisAlignment.end,
|
||||||
mainAxisSize: pw.MainAxisSize.min,
|
mainAxisSize: pw.MainAxisSize.min,
|
||||||
children: [
|
children: [
|
||||||
if (detail.estCadeau) ...[
|
if (detail.estCadeau) ...[
|
||||||
pw.Text(
|
pw.Text(
|
||||||
NumberFormat('#,##0', 'fr_FR').format(detail.sousTotal),
|
NumberFormat('#,##0', 'fr_FR')
|
||||||
|
.format(detail.sousTotal),
|
||||||
style: pw.TextStyle(
|
style: pw.TextStyle(
|
||||||
fontSize: 8,
|
fontSize: 8,
|
||||||
decoration: pw.TextDecoration.lineThrough,
|
decoration: pw
|
||||||
|
.TextDecoration.lineThrough,
|
||||||
color: PdfColors.grey600,
|
color: PdfColors.grey600,
|
||||||
font: regularFont
|
font: regularFont)),
|
||||||
)
|
pw.Text('GRATUIT',
|
||||||
),
|
|
||||||
pw.Text(
|
|
||||||
'GRATUIT',
|
|
||||||
style: pw.TextStyle(
|
style: pw.TextStyle(
|
||||||
fontSize: 9,
|
fontSize: 9,
|
||||||
fontWeight: pw.FontWeight.bold,
|
fontWeight: pw.FontWeight.bold,
|
||||||
color: PdfColors.green700,
|
color: PdfColors.green700,
|
||||||
font: regularFont
|
font: regularFont)),
|
||||||
)
|
|
||||||
),
|
|
||||||
] else if (detail.aRemise) ...[
|
] else if (detail.aRemise) ...[
|
||||||
pw.Text(
|
pw.Text(
|
||||||
'${NumberFormat('#,##0', 'fr_FR').format(detail.sousTotal)}',
|
'${NumberFormat('#,##0', 'fr_FR').format(detail.sousTotal)}',
|
||||||
style: pw.TextStyle(
|
style: pw.TextStyle(
|
||||||
fontSize: 8,
|
fontSize: 8,
|
||||||
decoration: pw.TextDecoration.lineThrough,
|
decoration: pw
|
||||||
|
.TextDecoration.lineThrough,
|
||||||
color: PdfColors.grey600,
|
color: PdfColors.grey600,
|
||||||
font: regularFont
|
font: regularFont)),
|
||||||
)
|
|
||||||
),
|
|
||||||
pw.Text(
|
pw.Text(
|
||||||
'${NumberFormat('#,##0', 'fr_FR').format(detail.prixFinal)}',
|
'${NumberFormat('#,##0', 'fr_FR').format(detail.prixFinal)}',
|
||||||
style: pw.TextStyle(
|
style: pw.TextStyle(
|
||||||
fontSize: 10,
|
fontSize: 10,
|
||||||
fontWeight: pw.FontWeight.bold,
|
fontWeight: pw.FontWeight.bold,
|
||||||
font: regularFont
|
font: regularFont)),
|
||||||
)
|
|
||||||
),
|
|
||||||
] else
|
] else
|
||||||
pw.Text(
|
pw.Text(
|
||||||
'${NumberFormat('#,##0', 'fr_FR').format(detail.prixFinal)}',
|
'${NumberFormat('#,##0', 'fr_FR').format(detail.prixFinal)}',
|
||||||
style: smallTextStyle
|
style: smallTextStyle),
|
||||||
),
|
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@ -825,44 +880,67 @@ final logoWidget = await buildLogoWidget();
|
|||||||
children: [
|
children: [
|
||||||
pw.Text('SOUS-TOTAL:', style: smallTextStyle),
|
pw.Text('SOUS-TOTAL:', style: smallTextStyle),
|
||||||
pw.SizedBox(width: 10),
|
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),
|
pw.SizedBox(height: 2),
|
||||||
],
|
],
|
||||||
|
|
||||||
if (totalRemises > 0) ...[
|
if (totalRemises > 0) ...[
|
||||||
pw.Row(
|
pw.Row(
|
||||||
mainAxisAlignment: pw.MainAxisAlignment.end,
|
mainAxisAlignment: pw.MainAxisAlignment.end,
|
||||||
children: [
|
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.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),
|
pw.SizedBox(height: 2),
|
||||||
],
|
],
|
||||||
|
|
||||||
if (totalCadeaux > 0) ...[
|
if (totalCadeaux > 0) ...[
|
||||||
pw.Row(
|
pw.Row(
|
||||||
mainAxisAlignment: pw.MainAxisAlignment.end,
|
mainAxisAlignment: pw.MainAxisAlignment.end,
|
||||||
children: [
|
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.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.SizedBox(height: 2),
|
||||||
],
|
],
|
||||||
|
pw.Container(
|
||||||
pw.Container(width: 120, height: 1.5, color: PdfColors.black, margin: const pw.EdgeInsets.symmetric(vertical: 2)),
|
width: 120,
|
||||||
|
height: 1.5,
|
||||||
|
color: PdfColors.black,
|
||||||
|
margin:
|
||||||
|
const pw.EdgeInsets.symmetric(vertical: 2)),
|
||||||
pw.Row(
|
pw.Row(
|
||||||
mainAxisAlignment: pw.MainAxisAlignment.end,
|
mainAxisAlignment: pw.MainAxisAlignment.end,
|
||||||
children: [
|
children: [
|
||||||
pw.Text('TOTAL:', style: boldTextStyle),
|
pw.Text('TOTAL:', style: boldTextStyle),
|
||||||
pw.SizedBox(width: 10),
|
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(
|
child: pw.Column(
|
||||||
crossAxisAlignment: pw.CrossAxisAlignment.start,
|
crossAxisAlignment: pw.CrossAxisAlignment.start,
|
||||||
children: [
|
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.SizedBox(height: 3),
|
||||||
pw.Row(
|
pw.Row(
|
||||||
children: [
|
children: [
|
||||||
pw.Expanded(
|
pw.Expanded(
|
||||||
child: pw.Column(
|
child: pw.Column(
|
||||||
crossAxisAlignment: pw.CrossAxisAlignment.start,
|
crossAxisAlignment:
|
||||||
|
pw.CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
pw.Text('Initiateur:', style: tinyTextStyle),
|
pw.Text('Initiateur:',
|
||||||
|
style: tinyTextStyle),
|
||||||
pw.Text(
|
pw.Text(
|
||||||
commandeur != null ? '${commandeur.name} ${commandeur.lastName ?? ''}'.trim() : 'N/A',
|
commandeur != null
|
||||||
style: pw.TextStyle(fontSize: 9, font: regularFont),
|
? '${commandeur.name} ${commandeur.lastName ?? ''}'
|
||||||
|
.trim()
|
||||||
|
: 'N/A',
|
||||||
|
style: pw.TextStyle(
|
||||||
|
fontSize: 9,
|
||||||
|
font: regularFont),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
pw.Expanded(
|
pw.Expanded(
|
||||||
child: pw.Column(
|
child: pw.Column(
|
||||||
crossAxisAlignment: pw.CrossAxisAlignment.start,
|
crossAxisAlignment:
|
||||||
|
pw.CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
pw.Text('Validateur:', style: tinyTextStyle),
|
pw.Text('Validateur:',
|
||||||
|
style: tinyTextStyle),
|
||||||
pw.Text(
|
pw.Text(
|
||||||
validateur != null ? '${validateur.name} ${validateur.lastName ?? ''}'.trim() : 'N/A',
|
validateur != null
|
||||||
style: pw.TextStyle(fontSize: 9, font: regularFont),
|
? '${validateur.name} ${validateur.lastName ?? ''}'
|
||||||
|
.trim()
|
||||||
|
: 'N/A',
|
||||||
|
style: pw.TextStyle(
|
||||||
|
fontSize: 9,
|
||||||
|
font: regularFont),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
@ -925,20 +1021,35 @@ final logoWidget = await buildLogoWidget();
|
|||||||
|
|
||||||
// Signatures
|
// Signatures
|
||||||
pw.Row(
|
pw.Row(
|
||||||
mainAxisAlignment: pw.MainAxisAlignment.spaceBetween,
|
mainAxisAlignment:
|
||||||
|
pw.MainAxisAlignment.spaceBetween,
|
||||||
children: [
|
children: [
|
||||||
pw.Column(
|
pw.Column(
|
||||||
children: [
|
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.SizedBox(height: 15),
|
||||||
pw.Container(width: 70, height: 1, color: PdfColors.black),
|
pw.Container(
|
||||||
|
width: 70,
|
||||||
|
height: 1,
|
||||||
|
color: PdfColors.black),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
pw.Column(
|
pw.Column(
|
||||||
children: [
|
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.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),
|
border: pw.Border.all(color: PdfColors.black, width: 2),
|
||||||
),
|
),
|
||||||
child: pw.Center(
|
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.SizedBox(height: 10),
|
||||||
pw.Transform.rotate(
|
pw.Transform.rotate(
|
||||||
angle: 1.5708,
|
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.SizedBox(height: 10),
|
||||||
pw.Container(
|
pw.Container(
|
||||||
@ -1006,7 +1125,11 @@ final logoWidget = await buildLogoWidget();
|
|||||||
border: pw.Border.all(color: PdfColors.black, width: 2),
|
border: pw.Border.all(color: PdfColors.black, width: 2),
|
||||||
),
|
),
|
||||||
child: pw.Center(
|
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
|
// Partager ou ouvrir le fichier
|
||||||
await OpenFile.open(file.path);
|
await OpenFile.open(file.path);
|
||||||
}
|
}
|
||||||
//==============================================================
|
//==============================================================
|
||||||
|
|
||||||
// Modifiez la méthode _generateInvoice dans GestionCommandesPage
|
// Modifiez la méthode _generateInvoice dans GestionCommandesPage
|
||||||
@ -1149,11 +1272,13 @@ final logoWidget = await buildLogoWidget();
|
|||||||
dynamic blobDynamic = logoRaw;
|
dynamic blobDynamic = logoRaw;
|
||||||
bytes = blobDynamic.toBytes();
|
bytes = blobDynamic.toBytes();
|
||||||
} else {
|
} else {
|
||||||
throw Exception("Format de logo non supporté: ${logoRaw.runtimeType}");
|
throw Exception(
|
||||||
|
"Format de logo non supporté: ${logoRaw.runtimeType}");
|
||||||
}
|
}
|
||||||
|
|
||||||
final imageLogo = pw.MemoryImage(bytes);
|
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) {
|
} catch (e) {
|
||||||
print('Erreur chargement logo BDD: $e');
|
print('Erreur chargement logo BDD: $e');
|
||||||
}
|
}
|
||||||
@ -1162,8 +1287,9 @@ final logoWidget = await buildLogoWidget();
|
|||||||
}
|
}
|
||||||
|
|
||||||
final logoWidget = await buildLogoWidget();
|
final logoWidget = await buildLogoWidget();
|
||||||
pw.Widget buildEnteteFactureInfos() {
|
pw.Widget buildEnteteFactureInfos() {
|
||||||
final infosAUtiliser = infosFacture.isNotEmpty ? infosFacture : infosFactureDefaut;
|
final infosAUtiliser =
|
||||||
|
infosFacture.isNotEmpty ? infosFacture : infosFactureDefaut;
|
||||||
|
|
||||||
return pw.Column(
|
return pw.Column(
|
||||||
crossAxisAlignment: pw.CrossAxisAlignment.start,
|
crossAxisAlignment: pw.CrossAxisAlignment.start,
|
||||||
@ -1180,8 +1306,7 @@ pw.Widget buildEnteteFactureInfos() {
|
|||||||
pw.SizedBox(height: 2), // ajouté en fin de liste
|
pw.SizedBox(height: 2), // ajouté en fin de liste
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
pdf.addPage(
|
pdf.addPage(
|
||||||
pw.Page(
|
pw.Page(
|
||||||
@ -1566,7 +1691,8 @@ pw.Widget buildEnteteFactureInfos() {
|
|||||||
pw.SizedBox(width: 20),
|
pw.SizedBox(width: 20),
|
||||||
pw.Container(
|
pw.Container(
|
||||||
width: 80,
|
width: 80,
|
||||||
child: pw.Text('${NumberFormat('#,##0', 'fr_FR').format(sousTotal)}',
|
child: pw.Text(
|
||||||
|
'${NumberFormat('#,##0', 'fr_FR').format(sousTotal)}',
|
||||||
style: normalTextStyle,
|
style: normalTextStyle,
|
||||||
textAlign: pw.TextAlign.right),
|
textAlign: pw.TextAlign.right),
|
||||||
),
|
),
|
||||||
@ -2001,7 +2127,6 @@ pw.Widget buildEnteteFactureInfos() {
|
|||||||
|
|
||||||
Future<void> _generateReceipt(
|
Future<void> _generateReceipt(
|
||||||
Commande commande, PaymentMethod payment) async {
|
Commande commande, PaymentMethod payment) async {
|
||||||
|
|
||||||
final details = await _database.getDetailsCommande(commande.id!);
|
final details = await _database.getDetailsCommande(commande.id!);
|
||||||
final client = await _database.getClientById(commande.clientId);
|
final client = await _database.getClientById(commande.clientId);
|
||||||
final commandeur = commande.commandeurId != null
|
final commandeur = commande.commandeurId != null
|
||||||
@ -2242,7 +2367,8 @@ pw.Widget buildEnteteFactureInfos() {
|
|||||||
children: [
|
children: [
|
||||||
pw.Text('SOUS-TOTAL:',
|
pw.Text('SOUS-TOTAL:',
|
||||||
style: const pw.TextStyle(fontSize: 8)),
|
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)),
|
style: const pw.TextStyle(fontSize: 8)),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
@ -2253,7 +2379,8 @@ pw.Widget buildEnteteFactureInfos() {
|
|||||||
pw.Text('REMISES:',
|
pw.Text('REMISES:',
|
||||||
style: pw.TextStyle(
|
style: pw.TextStyle(
|
||||||
fontSize: 8, color: PdfColors.orange)),
|
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(
|
style: pw.TextStyle(
|
||||||
fontSize: 8, color: PdfColors.orange)),
|
fontSize: 8, color: PdfColors.orange)),
|
||||||
],
|
],
|
||||||
@ -2266,7 +2393,8 @@ pw.Widget buildEnteteFactureInfos() {
|
|||||||
pw.Text('CADEAUX ($nombreCadeaux):',
|
pw.Text('CADEAUX ($nombreCadeaux):',
|
||||||
style: pw.TextStyle(
|
style: pw.TextStyle(
|
||||||
fontSize: 8, color: PdfColors.green700)),
|
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(
|
style: pw.TextStyle(
|
||||||
fontSize: 8, color: PdfColors.green700)),
|
fontSize: 8, color: PdfColors.green700)),
|
||||||
],
|
],
|
||||||
@ -2282,7 +2410,8 @@ pw.Widget buildEnteteFactureInfos() {
|
|||||||
pw.Text('TOTAL:',
|
pw.Text('TOTAL:',
|
||||||
style: pw.TextStyle(
|
style: pw.TextStyle(
|
||||||
fontSize: 9, fontWeight: pw.FontWeight.bold)),
|
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(
|
style: pw.TextStyle(
|
||||||
fontSize: 9, fontWeight: pw.FontWeight.bold)),
|
fontSize: 9, fontWeight: pw.FontWeight.bold)),
|
||||||
],
|
],
|
||||||
@ -2974,7 +3103,8 @@ pw.Widget buildEnteteFactureInfos() {
|
|||||||
borderRadius: BorderRadius.circular(8),
|
borderRadius: BorderRadius.circular(8),
|
||||||
boxShadow: [
|
boxShadow: [
|
||||||
BoxShadow(
|
BoxShadow(
|
||||||
color: Colors.black.withOpacity(0.1),
|
color:
|
||||||
|
Colors.black.withOpacity(0.1),
|
||||||
blurRadius: 2,
|
blurRadius: 2,
|
||||||
offset: const Offset(0, 1),
|
offset: const Offset(0, 1),
|
||||||
),
|
),
|
||||||
@ -2985,8 +3115,10 @@ pw.Widget buildEnteteFactureInfos() {
|
|||||||
Icons.payment,
|
Icons.payment,
|
||||||
color: Colors.green.shade600,
|
color: Colors.green.shade600,
|
||||||
),
|
),
|
||||||
onPressed: () => _showPaymentOptions(commande),
|
onPressed: () =>
|
||||||
tooltip: 'Générer le ticket de la commande',
|
_showPaymentOptions(commande),
|
||||||
|
tooltip:
|
||||||
|
'Générer le ticket de la commande',
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(
|
const SizedBox(
|
||||||
@ -3067,8 +3199,8 @@ pw.Widget buildEnteteFactureInfos() {
|
|||||||
CommandeActions(
|
CommandeActions(
|
||||||
commande: commande,
|
commande: commande,
|
||||||
onStatutChanged: _updateStatut,
|
onStatutChanged: _updateStatut,
|
||||||
onGenerateBonLivraison:_generateBon_lifraisonWithPasswordVerification
|
onGenerateBonLivraison:
|
||||||
),
|
_generateBon_lifraisonWithPasswordVerification),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|||||||
@ -92,7 +92,9 @@ class _HistoriquePageState extends State<HistoriquePage> {
|
|||||||
final pointDeVenteId = _userController.pointDeVenteId;
|
final pointDeVenteId = _userController.pointDeVenteId;
|
||||||
final commandes = pointDeVenteId == 0
|
final commandes = pointDeVenteId == 0
|
||||||
? allCommandes
|
? allCommandes
|
||||||
: allCommandes.where((cmd) => cmd.pointDeVenteId == pointDeVenteId).toList();
|
: allCommandes
|
||||||
|
.where((cmd) => cmd.pointDeVenteId == pointDeVenteId)
|
||||||
|
.toList();
|
||||||
|
|
||||||
setState(() {
|
setState(() {
|
||||||
_commandes.clear();
|
_commandes.clear();
|
||||||
@ -146,7 +148,8 @@ class _HistoriquePageState extends State<HistoriquePage> {
|
|||||||
_filteredCommandes.clear();
|
_filteredCommandes.clear();
|
||||||
|
|
||||||
for (var commande in _commandes) {
|
for (var commande in _commandes) {
|
||||||
if (pointDeVenteId != 0 && commande.pointDeVenteId != pointDeVenteId) continue;
|
if (pointDeVenteId != 0 && commande.pointDeVenteId != pointDeVenteId)
|
||||||
|
continue;
|
||||||
bool matchesSearch = searchText.isEmpty ||
|
bool matchesSearch = searchText.isEmpty ||
|
||||||
commande.clientNom!.toLowerCase().contains(searchText) ||
|
commande.clientNom!.toLowerCase().contains(searchText) ||
|
||||||
commande.clientPrenom!.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),
|
child: const Icon(Icons.shopping_bag, size: 20),
|
||||||
),
|
),
|
||||||
title: Text(
|
title: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
detail.produitNom ?? 'Produit inconnu',
|
detail.produitNom ?? 'Produit inconnu',
|
||||||
style:
|
style: const TextStyle(
|
||||||
const TextStyle(fontWeight: FontWeight.w500),
|
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(
|
subtitle: Text(
|
||||||
'${detail.quantite} x ${NumberFormat('#,##0.00', 'fr_FR').format(detail.prixUnitaire)} MGA',
|
'${detail.quantite} x ${NumberFormat('#,##0.00', 'fr_FR').format(detail.prixUnitaire)} MGA',
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user