|
|
|
@ -251,6 +251,7 @@ Future<pw.Widget> buildIconGift() async { |
|
|
|
} |
|
|
|
|
|
|
|
// Bon de livraison============================================== |
|
|
|
|
|
|
|
Future<void> _generateBonLivraison(Commande commande) async { |
|
|
|
final details = await _database.getDetailsCommande(commande.id!); |
|
|
|
final client = await _database.getClientById(commande.clientId); |
|
|
|
@ -297,23 +298,25 @@ Future<void> _generateBonLivraison(Commande commande) async { |
|
|
|
final image = pw.MemoryImage(imageBytes); |
|
|
|
final italicFont = pw.Font.ttf(await rootBundle.load('assets/fonts/Roboto-Italic.ttf')); |
|
|
|
|
|
|
|
// Tailles de texte adaptées pour côte à côte |
|
|
|
final tinyTextStyle = pw.TextStyle(fontSize: 5); |
|
|
|
final smallTextStyle = pw.TextStyle(fontSize: 6); |
|
|
|
final normalTextStyle = pw.TextStyle(fontSize: 7); |
|
|
|
final boldTextStyle = pw.TextStyle(fontSize: 7, fontWeight: pw.FontWeight.bold); |
|
|
|
final boldClientStyle = pw.TextStyle(fontSize: 8, fontWeight: pw.FontWeight.bold); |
|
|
|
final frameTextStyle = pw.TextStyle(fontSize: 6); |
|
|
|
final italicTextStyle = pw.TextStyle(fontSize: 5, fontWeight: pw.FontWeight.bold, font: italicFont); |
|
|
|
final italicLogoStyle = pw.TextStyle(fontSize: 4, fontWeight: pw.FontWeight.bold, font: italicFont); |
|
|
|
final titleStyle = pw.TextStyle(fontSize: 9, fontWeight: pw.FontWeight.bold); |
|
|
|
|
|
|
|
// Fonction pour créer un exemplaire |
|
|
|
pw.Widget buildExemplaire(String typeExemplaire, {bool isSecond = false}) { |
|
|
|
// Tailles de texte agrandies pour une meilleure lisibilité |
|
|
|
final tinyTextStyle = pw.TextStyle(fontSize: 9); |
|
|
|
final smallTextStyle = pw.TextStyle(fontSize: 10); |
|
|
|
final normalTextStyle = pw.TextStyle(fontSize: 11); |
|
|
|
final boldTextStyle = pw.TextStyle(fontSize: 11, fontWeight: pw.FontWeight.bold); |
|
|
|
final boldClientStyle = pw.TextStyle(fontSize: 12, fontWeight: pw.FontWeight.bold); |
|
|
|
final frameTextStyle = pw.TextStyle(fontSize: 10); |
|
|
|
final italicTextStyle = pw.TextStyle(fontSize: 9, fontWeight: pw.FontWeight.bold, font: italicFont); |
|
|
|
final italicLogoStyle = pw.TextStyle(fontSize: 8, fontWeight: pw.FontWeight.bold, font: italicFont); |
|
|
|
final titleStyle = pw.TextStyle(fontSize: 14, fontWeight: pw.FontWeight.bold); |
|
|
|
final headerStyle = pw.TextStyle(fontSize: 12, fontWeight: pw.FontWeight.bold); |
|
|
|
|
|
|
|
// Fonction pour créer un exemplaire en mode paysage |
|
|
|
pw.Widget buildExemplaire(String typeExemplaire) { |
|
|
|
return pw.Container( |
|
|
|
height: 380, // Hauteur ajustée pour le mode paysage |
|
|
|
width: double.infinity, |
|
|
|
decoration: pw.BoxDecoration( |
|
|
|
border: pw.Border.all(color: PdfColors.black, width: 1), |
|
|
|
border: pw.Border.all(color: PdfColors.black, width: 1.5), |
|
|
|
), |
|
|
|
child: pw.Column( |
|
|
|
crossAxisAlignment: pw.CrossAxisAlignment.start, |
|
|
|
@ -321,7 +324,7 @@ Future<void> _generateBonLivraison(Commande commande) async { |
|
|
|
// En-tête avec indication de l'exemplaire |
|
|
|
pw.Container( |
|
|
|
width: double.infinity, |
|
|
|
padding: const pw.EdgeInsets.all(3), |
|
|
|
padding: const pw.EdgeInsets.all(5), |
|
|
|
decoration: pw.BoxDecoration( |
|
|
|
color: typeExemplaire == "CLIENT" ? PdfColors.blue100 : PdfColors.green100, |
|
|
|
), |
|
|
|
@ -329,7 +332,7 @@ Future<void> _generateBonLivraison(Commande commande) async { |
|
|
|
child: pw.Text( |
|
|
|
'BON DE LIVRAISON - EXEMPLAIRE $typeExemplaire', |
|
|
|
style: pw.TextStyle( |
|
|
|
fontSize: 8, |
|
|
|
fontSize: 14, |
|
|
|
fontWeight: pw.FontWeight.bold, |
|
|
|
color: typeExemplaire == "CLIENT" ? PdfColors.blue800 : PdfColors.green800, |
|
|
|
), |
|
|
|
@ -337,8 +340,9 @@ Future<void> _generateBonLivraison(Commande commande) async { |
|
|
|
), |
|
|
|
), |
|
|
|
|
|
|
|
pw.Padding( |
|
|
|
padding: const pw.EdgeInsets.all(6), |
|
|
|
pw.Expanded( |
|
|
|
child: pw.Padding( |
|
|
|
padding: const pw.EdgeInsets.all(8), |
|
|
|
child: pw.Column( |
|
|
|
crossAxisAlignment: pw.CrossAxisAlignment.start, |
|
|
|
children: [ |
|
|
|
@ -347,27 +351,29 @@ Future<void> _generateBonLivraison(Commande commande) async { |
|
|
|
crossAxisAlignment: pw.CrossAxisAlignment.start, |
|
|
|
mainAxisAlignment: pw.MainAxisAlignment.spaceBetween, |
|
|
|
children: [ |
|
|
|
// Logo et infos entreprise - très compact |
|
|
|
// Logo et infos entreprise |
|
|
|
pw.Column( |
|
|
|
crossAxisAlignment: pw.CrossAxisAlignment.start, |
|
|
|
children: [ |
|
|
|
pw.Container( |
|
|
|
width: 45, |
|
|
|
height: 45, |
|
|
|
width: 100, |
|
|
|
height: 100, |
|
|
|
child: pw.Image(image), |
|
|
|
), |
|
|
|
pw.Text('NOTRE COMPETENCE, A VOTRE SERVICE', style: italicLogoStyle), |
|
|
|
pw.SizedBox(height: 3), |
|
|
|
pw.Text('NOTRE COMPETENCE, A VOTRE SERVICE', style: italicLogoStyle), |
|
|
|
pw.SizedBox(height: 4), |
|
|
|
pw.Column( |
|
|
|
crossAxisAlignment: pw.CrossAxisAlignment.start, |
|
|
|
children: [ |
|
|
|
pw.Text('📍 REMAX Andravoangy', style: tinyTextStyle), |
|
|
|
pw.Text('📍 SUPREME CENTER Behoririka', style: tinyTextStyle), |
|
|
|
pw.Text('📍 SUPREME CENTER Behoririka \n BOX 405 | 416 | 119', style: tinyTextStyle), |
|
|
|
pw.Text('📍 Tripolisa analankely BOX 7', style: tinyTextStyle), |
|
|
|
pw.Text('📞 033 37 808 18', style: tinyTextStyle), |
|
|
|
pw.Text('🌐 www.guycom.mg', style: tinyTextStyle), |
|
|
|
pw.SizedBox(height: 1), |
|
|
|
// Ajout du NIF |
|
|
|
pw.Text('NIF: 1026/GC78-20-02-22', style: pw.TextStyle(fontSize: 5, fontWeight: pw.FontWeight.bold)), |
|
|
|
pw.SizedBox(height: 2), |
|
|
|
pw.Text('NIF: 1026/GC78-20-02-22', |
|
|
|
style: pw.TextStyle(fontSize: 7, fontWeight: pw.FontWeight.bold)), |
|
|
|
], |
|
|
|
), |
|
|
|
], |
|
|
|
@ -378,11 +384,11 @@ Future<void> _generateBonLivraison(Commande commande) async { |
|
|
|
crossAxisAlignment: pw.CrossAxisAlignment.center, |
|
|
|
children: [ |
|
|
|
pw.Text('Date: ${DateFormat('dd/MM/yyyy').format(DateTime.now())}', style: boldClientStyle), |
|
|
|
pw.SizedBox(height: 3), |
|
|
|
pw.Container(width: 80, height: 1, color: PdfColors.black), |
|
|
|
pw.SizedBox(height: 3), |
|
|
|
pw.SizedBox(height: 4), |
|
|
|
pw.Container(width: 100, height: 2, color: PdfColors.black), |
|
|
|
pw.SizedBox(height: 4), |
|
|
|
pw.Container( |
|
|
|
padding: const pw.EdgeInsets.all(3), |
|
|
|
padding: const pw.EdgeInsets.all(6), |
|
|
|
decoration: pw.BoxDecoration( |
|
|
|
border: pw.Border.all(color: PdfColors.black), |
|
|
|
), |
|
|
|
@ -390,7 +396,7 @@ Future<void> _generateBonLivraison(Commande commande) async { |
|
|
|
children: [ |
|
|
|
pw.Text('Boutique:', style: frameTextStyle), |
|
|
|
pw.Text('${pointDeVente?['nom'] ?? 'S405A'}', style: boldTextStyle), |
|
|
|
pw.SizedBox(height: 1), |
|
|
|
pw.SizedBox(height: 2), |
|
|
|
pw.Text('Bon N°:', style: frameTextStyle), |
|
|
|
pw.Text('${pointDeVente?['nom'] ?? 'S405A'}-P${commande.id}', style: boldTextStyle), |
|
|
|
], |
|
|
|
@ -399,22 +405,22 @@ Future<void> _generateBonLivraison(Commande commande) async { |
|
|
|
], |
|
|
|
), |
|
|
|
|
|
|
|
// Informations client - compact |
|
|
|
// Informations client |
|
|
|
pw.Container( |
|
|
|
width: 100, |
|
|
|
width: 120, |
|
|
|
decoration: pw.BoxDecoration( |
|
|
|
border: pw.Border.all(color: PdfColors.black, width: 1), |
|
|
|
), |
|
|
|
padding: const pw.EdgeInsets.all(4), |
|
|
|
padding: const pw.EdgeInsets.all(6), |
|
|
|
child: pw.Column( |
|
|
|
crossAxisAlignment: pw.CrossAxisAlignment.center, |
|
|
|
children: [ |
|
|
|
pw.Text('CLIENT', style: frameTextStyle), |
|
|
|
pw.SizedBox(height: 1), |
|
|
|
pw.SizedBox(height: 2), |
|
|
|
pw.Text('ID: ${pointDeVente?['nom'] ?? 'S405A'}-${client?.id ?? 'Non spécifié'}', style: smallTextStyle), |
|
|
|
pw.Container(width: 80, height: 1, color: PdfColors.black, margin: const pw.EdgeInsets.symmetric(vertical: 1)), |
|
|
|
pw.Text(client?.nom ?? 'Non spécifié', style: boldTextStyle), |
|
|
|
pw.SizedBox(height: 1), |
|
|
|
pw.Container(width: 100, height: 1, color: PdfColors.black, margin: const pw.EdgeInsets.symmetric(vertical: 2)), |
|
|
|
pw.Text('${client?.nom} \n ${client?.prenom}', style: boldTextStyle), |
|
|
|
pw.SizedBox(height: 2), |
|
|
|
pw.Text(client?.telephone ?? 'Non spécifié', style: tinyTextStyle), |
|
|
|
], |
|
|
|
), |
|
|
|
@ -422,27 +428,33 @@ Future<void> _generateBonLivraison(Commande commande) async { |
|
|
|
], |
|
|
|
), |
|
|
|
|
|
|
|
pw.SizedBox(height: 4), |
|
|
|
pw.SizedBox(height: 8), |
|
|
|
|
|
|
|
// Tableau des produits - très compact |
|
|
|
pw.Table( |
|
|
|
border: pw.TableBorder.all(width: 0.5), |
|
|
|
// Tableau des produits (ajusté pour le mode paysage) |
|
|
|
pw.Expanded( |
|
|
|
child: pw.Table( |
|
|
|
border: pw.TableBorder.all(width: 1), |
|
|
|
columnWidths: { |
|
|
|
0: const pw.FlexColumnWidth(3.5), |
|
|
|
1: const pw.FlexColumnWidth(0.8), |
|
|
|
2: const pw.FlexColumnWidth(1.2), |
|
|
|
0: const pw.FlexColumnWidth(5), |
|
|
|
1: const pw.FlexColumnWidth(1.2), |
|
|
|
2: const pw.FlexColumnWidth(1.5), |
|
|
|
3: const pw.FlexColumnWidth(1.5), |
|
|
|
4: const pw.FlexColumnWidth(1.2), |
|
|
|
4: const pw.FlexColumnWidth(1.5), |
|
|
|
}, |
|
|
|
children: [ |
|
|
|
pw.TableRow( |
|
|
|
decoration: const pw.BoxDecoration(color: PdfColors.grey200), |
|
|
|
children: [ |
|
|
|
pw.Padding(padding: const pw.EdgeInsets.all(1), child: pw.Text('Désignations', style: boldTextStyle)), |
|
|
|
pw.Padding(padding: const pw.EdgeInsets.all(1), child: pw.Text('Qté', style: boldTextStyle, textAlign: pw.TextAlign.center)), |
|
|
|
pw.Padding(padding: const pw.EdgeInsets.all(1), child: pw.Text('P.U.', style: boldTextStyle, textAlign: pw.TextAlign.right)), |
|
|
|
pw.Padding(padding: const pw.EdgeInsets.all(1), child: pw.Text('Remise/Cadeau', style: boldTextStyle, textAlign: pw.TextAlign.center)), |
|
|
|
pw.Padding(padding: const pw.EdgeInsets.all(1), child: pw.Text('Montant', style: boldTextStyle, textAlign: pw.TextAlign.right)), |
|
|
|
pw.Padding(padding: const pw.EdgeInsets.all(3), |
|
|
|
child: pw.Text('Désignations', style: boldTextStyle)), |
|
|
|
pw.Padding(padding: const pw.EdgeInsets.all(3), |
|
|
|
child: pw.Text('Qté', style: boldTextStyle, textAlign: pw.TextAlign.center)), |
|
|
|
pw.Padding(padding: const pw.EdgeInsets.all(3), |
|
|
|
child: pw.Text('P.U.', style: boldTextStyle, textAlign: pw.TextAlign.right)), |
|
|
|
// pw.Padding(padding: const pw.EdgeInsets.all(3), |
|
|
|
// child: pw.Text('Remise/Cadeau', style: boldTextStyle, textAlign: pw.TextAlign.center)), |
|
|
|
pw.Padding(padding: const pw.EdgeInsets.all(3), |
|
|
|
child: pw.Text('Montant', style: boldTextStyle, textAlign: pw.TextAlign.right)), |
|
|
|
], |
|
|
|
), |
|
|
|
|
|
|
|
@ -458,7 +470,7 @@ Future<void> _generateBonLivraison(Commande commande) async { |
|
|
|
: null, |
|
|
|
children: [ |
|
|
|
pw.Padding( |
|
|
|
padding: const pw.EdgeInsets.all(1), |
|
|
|
padding: const pw.EdgeInsets.all(3), |
|
|
|
child: pw.Column( |
|
|
|
crossAxisAlignment: pw.CrossAxisAlignment.start, |
|
|
|
children: [ |
|
|
|
@ -466,78 +478,79 @@ Future<void> _generateBonLivraison(Commande commande) async { |
|
|
|
children: [ |
|
|
|
pw.Expanded( |
|
|
|
child: pw.Text(detail.produitNom ?? 'Produit inconnu', |
|
|
|
style: pw.TextStyle(fontSize: 6, fontWeight: pw.FontWeight.bold)), |
|
|
|
style: pw.TextStyle(fontSize: 10, fontWeight: pw.FontWeight.bold)), |
|
|
|
), |
|
|
|
if (detail.estCadeau) |
|
|
|
pw.Container( |
|
|
|
padding: const pw.EdgeInsets.symmetric(horizontal: 1, vertical: 0.5), |
|
|
|
padding: const pw.EdgeInsets.symmetric(horizontal: 2, vertical: 1), |
|
|
|
decoration: pw.BoxDecoration( |
|
|
|
color: PdfColors.green, |
|
|
|
borderRadius: pw.BorderRadius.circular(2), |
|
|
|
), |
|
|
|
child: pw.Text('🎁', style: pw.TextStyle(fontSize: 4, color: PdfColors.white)), |
|
|
|
child: pw.Text('🎁', style: pw.TextStyle(fontSize: 5, color: PdfColors.white)), |
|
|
|
), |
|
|
|
], |
|
|
|
), |
|
|
|
if (produit?.category != null && produit!.category.isNotEmpty) |
|
|
|
pw.Text('${produit.category}${produit?.marque != null && produit!.marque.isNotEmpty ? ' - ${produit.marque}' : ''}', style: tinyTextStyle), |
|
|
|
pw.Text('${produit.category}${produit?.marque != null && produit!.marque.isNotEmpty ? ' - ${produit.marque}' : ''}', |
|
|
|
style: tinyTextStyle), |
|
|
|
if (produit?.imei != null && produit!.imei!.isNotEmpty) |
|
|
|
pw.Text('IMEI: ${produit.imei}', style: tinyTextStyle), |
|
|
|
], |
|
|
|
), |
|
|
|
), |
|
|
|
pw.Padding( |
|
|
|
padding: const pw.EdgeInsets.all(1), |
|
|
|
padding: const pw.EdgeInsets.all(3), |
|
|
|
child: pw.Text('${detail.quantite}', style: normalTextStyle, textAlign: pw.TextAlign.center), |
|
|
|
), |
|
|
|
pw.Padding( |
|
|
|
padding: const pw.EdgeInsets.all(1), |
|
|
|
padding: const pw.EdgeInsets.all(3), |
|
|
|
child: pw.Column( |
|
|
|
crossAxisAlignment: pw.CrossAxisAlignment.end, |
|
|
|
children: [ |
|
|
|
if (detail.estCadeau) ...[ |
|
|
|
pw.Text('${detail.prixUnitaire.toStringAsFixed(0)}', |
|
|
|
style: pw.TextStyle(fontSize: 4, decoration: pw.TextDecoration.lineThrough, color: PdfColors.grey600)), |
|
|
|
pw.Text('GRATUIT', style: pw.TextStyle(fontSize: 5, color: PdfColors.green700, fontWeight: pw.FontWeight.bold)), |
|
|
|
style: pw.TextStyle(fontSize: 8, decoration: pw.TextDecoration.lineThrough, color: PdfColors.grey600)), |
|
|
|
pw.Text('GRATUIT', style: pw.TextStyle(fontSize: 9, color: PdfColors.green700, fontWeight: pw.FontWeight.bold)), |
|
|
|
] else if (detail.aRemise) ...[ |
|
|
|
pw.Text('${detail.prixUnitaire.toStringAsFixed(0)}', |
|
|
|
style: pw.TextStyle(fontSize: 4, decoration: pw.TextDecoration.lineThrough, color: PdfColors.grey600)), |
|
|
|
style: pw.TextStyle(fontSize: 8, decoration: pw.TextDecoration.lineThrough, color: PdfColors.grey600)), |
|
|
|
pw.Text('${(detail.prixFinal / detail.quantite).toStringAsFixed(0)}', |
|
|
|
style: pw.TextStyle(fontSize: 6, color: PdfColors.orange)), |
|
|
|
style: pw.TextStyle(fontSize: 9, color: PdfColors.orange)), |
|
|
|
] else |
|
|
|
pw.Text('${detail.prixUnitaire.toStringAsFixed(0)}', style: smallTextStyle), |
|
|
|
], |
|
|
|
), |
|
|
|
), |
|
|
|
// pw.Padding( |
|
|
|
// padding: const pw.EdgeInsets.all(3), |
|
|
|
// child: pw.Text( |
|
|
|
// detail.estCadeau |
|
|
|
// ? 'CADEAU' |
|
|
|
// : detail.aRemise |
|
|
|
// ? 'REMISE' |
|
|
|
// : '-', |
|
|
|
// style: pw.TextStyle( |
|
|
|
// fontSize: 9, |
|
|
|
// color: detail.estCadeau ? PdfColors.green700 : detail.aRemise ? PdfColors.orange : PdfColors.grey600, |
|
|
|
// fontWeight: detail.estCadeau ? pw.FontWeight.bold : pw.FontWeight.normal, |
|
|
|
// ), |
|
|
|
// textAlign: pw.TextAlign.center, |
|
|
|
// ), |
|
|
|
// ), |
|
|
|
pw.Padding( |
|
|
|
padding: const pw.EdgeInsets.all(1), |
|
|
|
child: pw.Text( |
|
|
|
detail.estCadeau |
|
|
|
? 'CADEAU' |
|
|
|
: detail.aRemise |
|
|
|
? 'REMISE' |
|
|
|
: '-', |
|
|
|
style: pw.TextStyle( |
|
|
|
fontSize: 5, |
|
|
|
color: detail.estCadeau ? PdfColors.green700 : detail.aRemise ? PdfColors.orange : PdfColors.grey600, |
|
|
|
fontWeight: detail.estCadeau ? pw.FontWeight.bold : pw.FontWeight.normal, |
|
|
|
), |
|
|
|
textAlign: pw.TextAlign.center, |
|
|
|
), |
|
|
|
), |
|
|
|
pw.Padding( |
|
|
|
padding: const pw.EdgeInsets.all(1), |
|
|
|
padding: const pw.EdgeInsets.all(3), |
|
|
|
child: pw.Column( |
|
|
|
crossAxisAlignment: pw.CrossAxisAlignment.end, |
|
|
|
children: [ |
|
|
|
if (detail.estCadeau) ...[ |
|
|
|
pw.Text('${detail.sousTotal.toStringAsFixed(0)}', |
|
|
|
style: pw.TextStyle(fontSize: 4, decoration: pw.TextDecoration.lineThrough, color: PdfColors.grey600)), |
|
|
|
pw.Text('GRATUIT', style: pw.TextStyle(fontSize: 5, fontWeight: pw.FontWeight.bold, color: PdfColors.green700)), |
|
|
|
style: pw.TextStyle(fontSize: 8, decoration: pw.TextDecoration.lineThrough, color: PdfColors.grey600)), |
|
|
|
pw.Text('GRATUIT', style: pw.TextStyle(fontSize: 9, fontWeight: pw.FontWeight.bold, color: PdfColors.green700)), |
|
|
|
] else if (detail.aRemise) ...[ |
|
|
|
pw.Text('${detail.sousTotal.toStringAsFixed(0)}', |
|
|
|
style: pw.TextStyle(fontSize: 4, decoration: pw.TextDecoration.lineThrough, color: PdfColors.grey600)), |
|
|
|
pw.Text('${detail.prixFinal.toStringAsFixed(0)}', style: pw.TextStyle(fontSize: 6, fontWeight: pw.FontWeight.bold)), |
|
|
|
style: pw.TextStyle(fontSize: 8, decoration: pw.TextDecoration.lineThrough, color: PdfColors.grey600)), |
|
|
|
pw.Text('${detail.prixFinal.toStringAsFixed(0)}', style: pw.TextStyle(fontSize: 9, fontWeight: pw.FontWeight.bold)), |
|
|
|
] else |
|
|
|
pw.Text('${detail.prixFinal.toStringAsFixed(0)}', style: smallTextStyle), |
|
|
|
], |
|
|
|
@ -548,10 +561,11 @@ Future<void> _generateBonLivraison(Commande commande) async { |
|
|
|
}).toList(), |
|
|
|
], |
|
|
|
), |
|
|
|
), |
|
|
|
|
|
|
|
pw.SizedBox(height: 4), |
|
|
|
pw.SizedBox(height: 8), |
|
|
|
|
|
|
|
// Section finale - très compacte |
|
|
|
// Section finale (ajustée pour le mode paysage) |
|
|
|
pw.Row( |
|
|
|
crossAxisAlignment: pw.CrossAxisAlignment.start, |
|
|
|
children: [ |
|
|
|
@ -566,44 +580,44 @@ Future<void> _generateBonLivraison(Commande commande) async { |
|
|
|
mainAxisAlignment: pw.MainAxisAlignment.end, |
|
|
|
children: [ |
|
|
|
pw.Text('SOUS-TOTAL:', style: smallTextStyle), |
|
|
|
pw.SizedBox(width: 8), |
|
|
|
pw.SizedBox(width: 10), |
|
|
|
pw.Text('${sousTotal.toStringAsFixed(0)}', style: smallTextStyle), |
|
|
|
], |
|
|
|
), |
|
|
|
pw.SizedBox(height: 1), |
|
|
|
pw.SizedBox(height: 2), |
|
|
|
], |
|
|
|
|
|
|
|
if (totalRemises > 0) ...[ |
|
|
|
pw.Row( |
|
|
|
mainAxisAlignment: pw.MainAxisAlignment.end, |
|
|
|
children: [ |
|
|
|
pw.Text('REMISES:', style: pw.TextStyle(color: PdfColors.orange, fontSize: 6)), |
|
|
|
pw.SizedBox(width: 8), |
|
|
|
pw.Text('-${totalRemises.toStringAsFixed(0)}', style: pw.TextStyle(color: PdfColors.orange, fontSize: 6)), |
|
|
|
pw.Text('REMISES:', style: pw.TextStyle(color: PdfColors.orange, fontSize: 10)), |
|
|
|
pw.SizedBox(width: 10), |
|
|
|
pw.Text('-${totalRemises.toStringAsFixed(0)}', style: pw.TextStyle(color: PdfColors.orange, fontSize: 10)), |
|
|
|
], |
|
|
|
), |
|
|
|
pw.SizedBox(height: 1), |
|
|
|
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: 6)), |
|
|
|
pw.SizedBox(width: 8), |
|
|
|
pw.Text('-${totalCadeaux.toStringAsFixed(0)}', style: pw.TextStyle(color: PdfColors.green700, fontSize: 6)), |
|
|
|
pw.Text('CADEAUX ($nombreCadeaux):', style: pw.TextStyle(color: PdfColors.green700, fontSize: 10)), |
|
|
|
pw.SizedBox(width: 10), |
|
|
|
pw.Text('-${totalCadeaux.toStringAsFixed(0)}', style: pw.TextStyle(color: PdfColors.green700, fontSize: 10)), |
|
|
|
], |
|
|
|
), |
|
|
|
pw.SizedBox(height: 1), |
|
|
|
pw.SizedBox(height: 2), |
|
|
|
], |
|
|
|
|
|
|
|
pw.Container(width: 100, height: 1, color: PdfColors.black, margin: const pw.EdgeInsets.symmetric(vertical: 1)), |
|
|
|
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: 8), |
|
|
|
pw.SizedBox(width: 10), |
|
|
|
pw.Text('${commande.montantTotal.toStringAsFixed(0)} MGA', style: boldTextStyle), |
|
|
|
], |
|
|
|
), |
|
|
|
@ -618,7 +632,7 @@ Future<void> _generateBonLivraison(Commande commande) async { |
|
|
|
), |
|
|
|
child: pw.Text( |
|
|
|
'🎁 $nombreCadeaux cadeau(s) offert(s) (${totalCadeaux.toStringAsFixed(0)} MGA)', |
|
|
|
style: pw.TextStyle(fontSize: 5, color: PdfColors.green700), |
|
|
|
style: pw.TextStyle(fontSize: 9, color: PdfColors.green700), |
|
|
|
), |
|
|
|
), |
|
|
|
], |
|
|
|
@ -626,7 +640,7 @@ Future<void> _generateBonLivraison(Commande commande) async { |
|
|
|
), |
|
|
|
), |
|
|
|
|
|
|
|
pw.SizedBox(width: 10), |
|
|
|
pw.SizedBox(width: 15), |
|
|
|
|
|
|
|
// Informations vendeurs et signatures |
|
|
|
pw.Expanded( |
|
|
|
@ -636,7 +650,7 @@ Future<void> _generateBonLivraison(Commande commande) async { |
|
|
|
children: [ |
|
|
|
// Vendeurs |
|
|
|
pw.Container( |
|
|
|
padding: const pw.EdgeInsets.all(3), |
|
|
|
padding: const pw.EdgeInsets.all(4), |
|
|
|
decoration: pw.BoxDecoration( |
|
|
|
color: PdfColors.grey100, |
|
|
|
borderRadius: pw.BorderRadius.circular(3), |
|
|
|
@ -644,8 +658,8 @@ Future<void> _generateBonLivraison(Commande commande) async { |
|
|
|
child: pw.Column( |
|
|
|
crossAxisAlignment: pw.CrossAxisAlignment.start, |
|
|
|
children: [ |
|
|
|
pw.Text('VENDEURS', style: pw.TextStyle(fontSize: 6, fontWeight: pw.FontWeight.bold)), |
|
|
|
pw.SizedBox(height: 1), |
|
|
|
pw.Text('VENDEURS', style: pw.TextStyle(fontSize: 10, fontWeight: pw.FontWeight.bold)), |
|
|
|
pw.SizedBox(height: 3), |
|
|
|
pw.Row( |
|
|
|
children: [ |
|
|
|
pw.Expanded( |
|
|
|
@ -655,7 +669,7 @@ Future<void> _generateBonLivraison(Commande commande) async { |
|
|
|
pw.Text('Initiateur:', style: tinyTextStyle), |
|
|
|
pw.Text( |
|
|
|
commandeur != null ? '${commandeur.name} ${commandeur.lastName ?? ''}'.trim() : 'N/A', |
|
|
|
style: pw.TextStyle(fontSize: 5), |
|
|
|
style: pw.TextStyle(fontSize: 9), |
|
|
|
), |
|
|
|
], |
|
|
|
), |
|
|
|
@ -667,7 +681,7 @@ Future<void> _generateBonLivraison(Commande commande) async { |
|
|
|
pw.Text('Validateur:', style: tinyTextStyle), |
|
|
|
pw.Text( |
|
|
|
validateur != null ? '${validateur.name} ${validateur.lastName ?? ''}'.trim() : 'N/A', |
|
|
|
style: pw.TextStyle(fontSize: 5), |
|
|
|
style: pw.TextStyle(fontSize: 9), |
|
|
|
), |
|
|
|
], |
|
|
|
), |
|
|
|
@ -678,7 +692,7 @@ Future<void> _generateBonLivraison(Commande commande) async { |
|
|
|
), |
|
|
|
), |
|
|
|
|
|
|
|
pw.SizedBox(height: 6), |
|
|
|
pw.SizedBox(height: 8), |
|
|
|
|
|
|
|
// Signatures |
|
|
|
pw.Row( |
|
|
|
@ -686,16 +700,16 @@ Future<void> _generateBonLivraison(Commande commande) async { |
|
|
|
children: [ |
|
|
|
pw.Column( |
|
|
|
children: [ |
|
|
|
pw.Text('Vendeur', style: pw.TextStyle(fontSize: 5, fontWeight: pw.FontWeight.bold)), |
|
|
|
pw.SizedBox(height: 8), |
|
|
|
pw.Container(width: 50, height: 1, color: PdfColors.black), |
|
|
|
pw.Text('Vendeur', style: pw.TextStyle(fontSize: 9, fontWeight: pw.FontWeight.bold)), |
|
|
|
pw.SizedBox(height: 15), |
|
|
|
pw.Container(width: 70, height: 1, color: PdfColors.black), |
|
|
|
], |
|
|
|
), |
|
|
|
pw.Column( |
|
|
|
children: [ |
|
|
|
pw.Text('Client', style: pw.TextStyle(fontSize: 5, fontWeight: pw.FontWeight.bold)), |
|
|
|
pw.SizedBox(height: 8), |
|
|
|
pw.Container(width: 50, height: 1, color: PdfColors.black), |
|
|
|
pw.Text('Client', style: pw.TextStyle(fontSize: 9, fontWeight: pw.FontWeight.bold)), |
|
|
|
pw.SizedBox(height: 15), |
|
|
|
pw.Container(width: 70, height: 1, color: PdfColors.black), |
|
|
|
], |
|
|
|
), |
|
|
|
], |
|
|
|
@ -706,7 +720,7 @@ Future<void> _generateBonLivraison(Commande commande) async { |
|
|
|
], |
|
|
|
), |
|
|
|
|
|
|
|
pw.SizedBox(height: 3), |
|
|
|
pw.SizedBox(height: 4), |
|
|
|
|
|
|
|
// Note finale |
|
|
|
pw.Text( |
|
|
|
@ -716,47 +730,51 @@ Future<void> _generateBonLivraison(Commande commande) async { |
|
|
|
], |
|
|
|
), |
|
|
|
), |
|
|
|
), |
|
|
|
], |
|
|
|
), |
|
|
|
); |
|
|
|
} |
|
|
|
|
|
|
|
// PAGE EN MODE PAYSAGE : Les deux exemplaires sur une seule page |
|
|
|
pdf.addPage( |
|
|
|
pw.Page( |
|
|
|
pageFormat: PdfPageFormat.a4.landscape, |
|
|
|
margin: const pw.EdgeInsets.all(10), |
|
|
|
pageFormat: PdfPageFormat.a4.landscape, // Mode paysage |
|
|
|
margin: const pw.EdgeInsets.all(12), |
|
|
|
build: (pw.Context context) { |
|
|
|
return pw.Row( |
|
|
|
crossAxisAlignment: pw.CrossAxisAlignment.start, |
|
|
|
return pw.Row( // Utilisation de Row au lieu de Column pour placer côte à côte |
|
|
|
children: [ |
|
|
|
// Exemplaire CLIENT (à gauche) |
|
|
|
// Premier exemplaire (CLIENT) |
|
|
|
pw.Expanded( |
|
|
|
child: buildExemplaire("CLIENT"), |
|
|
|
), |
|
|
|
|
|
|
|
pw.SizedBox(width: 10), |
|
|
|
pw.SizedBox(width: 15), |
|
|
|
|
|
|
|
// Ligne de séparation verticale avec ciseaux |
|
|
|
pw.Column( |
|
|
|
// Trait de séparation vertical |
|
|
|
pw.Container( |
|
|
|
width: 2, |
|
|
|
height: double.infinity, |
|
|
|
child: pw.Column( |
|
|
|
mainAxisAlignment: pw.MainAxisAlignment.center, |
|
|
|
children: [ |
|
|
|
pw.Text('✂️', style: pw.TextStyle(fontSize: 14)), |
|
|
|
pw.SizedBox(height: 10), |
|
|
|
pw.Transform.rotate( |
|
|
|
angle: 3.14159 / 2, // 90 degrés en radians |
|
|
|
child: pw.Text('✂️ DÉCOUPER ICI ✂️', style: pw.TextStyle(fontSize: 8, color: PdfColors.grey600)), |
|
|
|
), |
|
|
|
pw.Container( |
|
|
|
width: 1, |
|
|
|
height: 200, |
|
|
|
color: PdfColors.grey400, |
|
|
|
angle: 1.5708, // 90 degrés en radians (π/2) |
|
|
|
child: pw.Text('DÉCOUPER ICI', style: pw.TextStyle(fontSize: 10, fontWeight: pw.FontWeight.bold)), |
|
|
|
), |
|
|
|
pw.SizedBox(height: 10), |
|
|
|
pw.Text('✂️', style: pw.TextStyle(fontSize: 14)), |
|
|
|
], |
|
|
|
), |
|
|
|
), |
|
|
|
|
|
|
|
pw.SizedBox(width: 10), |
|
|
|
pw.SizedBox(width: 15), |
|
|
|
|
|
|
|
// Exemplaire MAGASIN (à droite) |
|
|
|
// Deuxième exemplaire (MAGASIN) |
|
|
|
pw.Expanded( |
|
|
|
child: buildExemplaire("MAGASIN", isSecond: true), |
|
|
|
child: buildExemplaire("MAGASIN"), |
|
|
|
), |
|
|
|
], |
|
|
|
); |
|
|
|
@ -851,7 +869,7 @@ Future<void> _generateInvoice(Commande commande) async { |
|
|
|
crossAxisAlignment: pw.CrossAxisAlignment.start, |
|
|
|
children: [ |
|
|
|
pw.Container( |
|
|
|
width: 120, |
|
|
|
width: 200, |
|
|
|
height: 120, |
|
|
|
child: pw.Image(image), |
|
|
|
), |
|
|
|
@ -874,6 +892,7 @@ Future<void> _generateInvoice(Commande commande) async { |
|
|
|
pw.SizedBox(height: 8), |
|
|
|
pw.Row(children: [iconPhone, pw.SizedBox(width: 4), pw.Text('033 37 808 18', style: smallTextStyle)]), |
|
|
|
pw.Row(children: [iconGlobe, pw.SizedBox(width: 4), pw.Text('www.guycom.mg', style: smallTextStyle)]), |
|
|
|
pw.Row(children: [iconGlobe, pw.SizedBox(width: 4), pw.Text('NIF: 1026/GC78-20-02-22', style: smallTextStyle)]), |
|
|
|
pw.Text('Facebook: GuyCom', style: smallTextStyle), |
|
|
|
], |
|
|
|
), |
|
|
|
@ -946,7 +965,7 @@ Future<void> _generateInvoice(Commande commande) async { |
|
|
|
pw.SizedBox(height: 6), |
|
|
|
pw.Container(width: 180, height: 1, color: PdfColors.black), |
|
|
|
pw.SizedBox(height: 4), |
|
|
|
pw.Text(client?.nom ?? 'Non spécifié', style: boldClientTextStyle), |
|
|
|
pw.Text('${client?.nom} \n ${client?.prenom}', style: boldTextStyle), |
|
|
|
pw.SizedBox(height: 4), |
|
|
|
pw.Text(client?.telephone ?? 'Non spécifié', style: frameTextStyle), |
|
|
|
], |
|
|
|
|