4 changed files with 300 additions and 239 deletions
@ -60,6 +60,8 @@ class PlatformPrintService { |
|||||
'ville': 'Madagascar', |
'ville': 'Madagascar', |
||||
'contact': '261348415301', |
'contact': '261348415301', |
||||
'email': '[email protected]', |
'email': '[email protected]', |
||||
|
'nif': '4002141594', |
||||
|
'stat': '10715 33 2025 0 00414', |
||||
}; |
}; |
||||
|
|
||||
final factureNumber = |
final factureNumber = |
||||
@ -69,223 +71,267 @@ class PlatformPrintService { |
|||||
pdf.addPage( |
pdf.addPage( |
||||
pw.Page( |
pw.Page( |
||||
pageFormat: ticket58mmFormat, |
pageFormat: ticket58mmFormat, |
||||
|
margin: const pw.EdgeInsets.all(2), // 🔧 Marges minimales |
||||
build: (pw.Context context) { |
build: (pw.Context context) { |
||||
return pw.Column( |
return pw.Container( |
||||
crossAxisAlignment: pw.CrossAxisAlignment.center, |
width: double.infinity, // 🔧 Forcer la largeur complète |
||||
children: [ |
child: pw.Column( |
||||
// En-tête Restaurant (centré et compact) |
crossAxisAlignment: |
||||
pw.Text( |
pw.CrossAxisAlignment.start, // 🔧 Alignement à gauche |
||||
restaurantInfo['nom']!, |
children: [ |
||||
style: pw.TextStyle( |
// En-tête Restaurant (centré et compact) |
||||
fontSize: titleSize, |
pw.Container( |
||||
fontWeight: pw.FontWeight.bold, |
width: double.infinity, |
||||
), |
child: pw.Text( |
||||
textAlign: pw.TextAlign.center, |
restaurantInfo['nom']!, |
||||
), |
|
||||
|
|
||||
pw.SizedBox(height: 1), |
|
||||
|
|
||||
pw.Text( |
|
||||
restaurantInfo['adresse']!, |
|
||||
style: pw.TextStyle(fontSize: smallSize), |
|
||||
textAlign: pw.TextAlign.center, |
|
||||
), |
|
||||
|
|
||||
pw.Text( |
|
||||
restaurantInfo['ville']!, |
|
||||
style: pw.TextStyle(fontSize: smallSize), |
|
||||
textAlign: pw.TextAlign.center, |
|
||||
), |
|
||||
|
|
||||
pw.Text( |
|
||||
'Tel: ${restaurantInfo['contact']!}', |
|
||||
style: pw.TextStyle(fontSize: smallSize), |
|
||||
textAlign: pw.TextAlign.center, |
|
||||
), |
|
||||
|
|
||||
pw.SizedBox(height: 3), |
|
||||
|
|
||||
// Ligne de séparation |
|
||||
pw.Container( |
|
||||
width: double.infinity, |
|
||||
height: 0.5, |
|
||||
color: PdfColors.black, |
|
||||
), |
|
||||
|
|
||||
pw.SizedBox(height: 2), |
|
||||
|
|
||||
// Informations ticket |
|
||||
pw.Row( |
|
||||
mainAxisAlignment: pw.MainAxisAlignment.spaceBetween, |
|
||||
children: [ |
|
||||
pw.Text( |
|
||||
'Ticket: $factureNumber', |
|
||||
style: pw.TextStyle( |
style: pw.TextStyle( |
||||
fontSize: bodySize, |
fontSize: titleSize, |
||||
fontWeight: pw.FontWeight.bold, |
fontWeight: pw.FontWeight.bold, |
||||
), |
), |
||||
|
textAlign: pw.TextAlign.center, |
||||
), |
), |
||||
pw.Text( |
), |
||||
'Via: ${commande.tablename ?? 'Table inconnue'}', |
|
||||
style: pw.TextStyle(fontSize: bodySize), |
pw.SizedBox(height: 1), |
||||
), |
|
||||
], |
|
||||
), |
|
||||
|
|
||||
pw.SizedBox(height: 1), |
pw.Container( |
||||
|
width: double.infinity, |
||||
|
child: pw.Text( |
||||
|
restaurantInfo['adresse']!, |
||||
|
style: pw.TextStyle(fontSize: smallSize), |
||||
|
textAlign: pw.TextAlign.center, |
||||
|
), |
||||
|
), |
||||
|
|
||||
pw.Row( |
pw.Container( |
||||
mainAxisAlignment: pw.MainAxisAlignment.spaceBetween, |
width: double.infinity, |
||||
children: [ |
child: pw.Text( |
||||
pw.Text( |
restaurantInfo['ville']!, |
||||
_formatDate(dateTime), |
|
||||
style: pw.TextStyle(fontSize: smallSize), |
style: pw.TextStyle(fontSize: smallSize), |
||||
|
textAlign: pw.TextAlign.center, |
||||
), |
), |
||||
pw.Text( |
), |
||||
_formatTime(dateTime), |
|
||||
|
pw.Container( |
||||
|
width: double.infinity, |
||||
|
child: pw.Text( |
||||
|
'Tel: ${restaurantInfo['contact']!}', |
||||
style: pw.TextStyle(fontSize: smallSize), |
style: pw.TextStyle(fontSize: smallSize), |
||||
|
textAlign: pw.TextAlign.center, |
||||
), |
), |
||||
], |
), |
||||
), |
|
||||
|
pw.SizedBox(height: 3), |
||||
pw.SizedBox(height: 2), |
|
||||
|
// Ligne de séparation |
||||
// Ligne de séparation |
pw.Container( |
||||
pw.Container( |
width: double.infinity, |
||||
width: double.infinity, |
height: 0.5, |
||||
height: 0.5, |
color: PdfColors.black, |
||||
color: PdfColors.black, |
), |
||||
), |
|
||||
|
pw.SizedBox(height: 2), |
||||
pw.SizedBox(height: 2), |
|
||||
|
// Informations ticket |
||||
// Articles (format très compact) |
pw.Container( |
||||
...commande.items |
width: double.infinity, |
||||
.map( |
child: pw.Row( |
||||
(item) => pw.Container( |
mainAxisAlignment: pw.MainAxisAlignment.spaceBetween, |
||||
margin: const pw.EdgeInsets.only(bottom: 1), |
children: [ |
||||
child: pw.Column( |
pw.Text( |
||||
crossAxisAlignment: pw.CrossAxisAlignment.start, |
'Ticket: $factureNumber', |
||||
children: [ |
style: pw.TextStyle( |
||||
// Nom du plat |
fontSize: bodySize, |
||||
pw.Text( |
fontWeight: pw.FontWeight.bold, |
||||
'${item.menuNom}', |
), |
||||
style: pw.TextStyle(fontSize: bodySize), |
), |
||||
maxLines: 2, |
], |
||||
), |
), |
||||
|
), |
||||
// Quantité, prix unitaire et total sur une ligne |
|
||||
pw.Row( |
pw.SizedBox(height: 1), |
||||
mainAxisAlignment: |
|
||||
pw.MainAxisAlignment.spaceBetween, |
pw.Container( |
||||
children: [ |
width: double.infinity, |
||||
pw.Text( |
child: pw.Row( |
||||
'${item.quantite}x ${item.prixUnitaire.toStringAsFixed(2)}MGA', |
mainAxisAlignment: pw.MainAxisAlignment.spaceBetween, |
||||
style: pw.TextStyle(fontSize: smallSize), |
children: [ |
||||
|
pw.Text( |
||||
|
_formatDate(dateTime), |
||||
|
style: pw.TextStyle(fontSize: smallSize), |
||||
|
), |
||||
|
pw.Text( |
||||
|
_formatTime(dateTime), |
||||
|
style: pw.TextStyle(fontSize: smallSize), |
||||
|
), |
||||
|
], |
||||
|
), |
||||
|
), |
||||
|
|
||||
|
pw.SizedBox(height: 2), |
||||
|
|
||||
|
// Ligne de séparation |
||||
|
pw.Container( |
||||
|
width: double.infinity, |
||||
|
height: 0.5, |
||||
|
color: PdfColors.black, |
||||
|
), |
||||
|
|
||||
|
pw.SizedBox(height: 2), |
||||
|
|
||||
|
// Articles (format très compact) |
||||
|
...commande.items |
||||
|
.map( |
||||
|
(item) => pw.Container( |
||||
|
width: double.infinity, // 🔧 Largeur complète |
||||
|
margin: const pw.EdgeInsets.only(bottom: 1), |
||||
|
child: pw.Column( |
||||
|
crossAxisAlignment: pw.CrossAxisAlignment.start, |
||||
|
children: [ |
||||
|
// Nom du plat |
||||
|
pw.Container( |
||||
|
width: double.infinity, |
||||
|
child: pw.Text( |
||||
|
'${item.menuNom}', |
||||
|
style: pw.TextStyle(fontSize: bodySize), |
||||
|
maxLines: 2, |
||||
), |
), |
||||
pw.Text( |
), |
||||
'${(item.prixUnitaire * item.quantite).toStringAsFixed(2)}MGA', |
|
||||
style: pw.TextStyle( |
// Quantité, prix unitaire et total sur une ligne |
||||
fontSize: bodySize, |
pw.Container( |
||||
fontWeight: pw.FontWeight.bold, |
width: double.infinity, |
||||
), |
child: pw.Row( |
||||
|
mainAxisAlignment: |
||||
|
pw.MainAxisAlignment.spaceBetween, |
||||
|
children: [ |
||||
|
pw.Text( |
||||
|
'${item.quantite}x ${item.prixUnitaire.toStringAsFixed(2)}MGA', |
||||
|
style: pw.TextStyle(fontSize: smallSize), |
||||
|
), |
||||
|
pw.Text( |
||||
|
'${(item.prixUnitaire * item.quantite).toStringAsFixed(2)}MGA', |
||||
|
style: pw.TextStyle( |
||||
|
fontSize: bodySize, |
||||
|
fontWeight: pw.FontWeight.bold, |
||||
|
), |
||||
|
), |
||||
|
], |
||||
), |
), |
||||
], |
), |
||||
), |
], |
||||
], |
), |
||||
), |
), |
||||
), |
) |
||||
) |
.toList(), |
||||
.toList(), |
|
||||
|
pw.SizedBox(height: 2), |
||||
pw.SizedBox(height: 2), |
|
||||
|
// Ligne de séparation |
||||
// Ligne de séparation |
pw.Container( |
||||
pw.Container( |
width: double.infinity, |
||||
width: double.infinity, |
height: 0.5, |
||||
height: 0.5, |
color: PdfColors.black, |
||||
color: PdfColors.black, |
), |
||||
), |
|
||||
|
pw.SizedBox(height: 2), |
||||
pw.SizedBox(height: 2), |
|
||||
|
// Total |
||||
// Total |
pw.Container( |
||||
pw.Row( |
width: double.infinity, |
||||
mainAxisAlignment: pw.MainAxisAlignment.spaceBetween, |
child: pw.Row( |
||||
children: [ |
mainAxisAlignment: pw.MainAxisAlignment.spaceBetween, |
||||
pw.Text( |
children: [ |
||||
'TOTAL', |
pw.Text( |
||||
style: pw.TextStyle( |
'TOTAL', |
||||
fontSize: titleSize, |
style: pw.TextStyle( |
||||
fontWeight: pw.FontWeight.bold, |
fontSize: titleSize, |
||||
), |
fontWeight: pw.FontWeight.bold, |
||||
|
), |
||||
|
), |
||||
|
pw.Text( |
||||
|
'${commande.totalTtc.toStringAsFixed(2)}MGA', |
||||
|
style: pw.TextStyle( |
||||
|
fontSize: titleSize, |
||||
|
fontWeight: pw.FontWeight.bold, |
||||
|
), |
||||
|
), |
||||
|
], |
||||
|
), |
||||
|
), |
||||
|
|
||||
|
pw.SizedBox(height: 3), |
||||
|
|
||||
|
// Mode de paiement |
||||
|
pw.Container( |
||||
|
width: double.infinity, |
||||
|
child: pw.Text( |
||||
|
'Paiement: ${paymentMethod.toLowerCase()}', |
||||
|
style: pw.TextStyle(fontSize: bodySize), |
||||
|
textAlign: pw.TextAlign.center, |
||||
), |
), |
||||
pw.Text( |
), |
||||
'${commande.totalTtc.toStringAsFixed(2)}MGA', |
|
||||
|
pw.SizedBox(height: 3), |
||||
|
|
||||
|
// Ligne de séparation |
||||
|
pw.Container( |
||||
|
width: double.infinity, |
||||
|
height: 0.5, |
||||
|
color: PdfColors.black, |
||||
|
), |
||||
|
|
||||
|
pw.SizedBox(height: 2), |
||||
|
|
||||
|
// Message de remerciement |
||||
|
pw.Container( |
||||
|
width: double.infinity, |
||||
|
child: pw.Text( |
||||
|
'Merci de votre visite !', |
||||
style: pw.TextStyle( |
style: pw.TextStyle( |
||||
fontSize: titleSize, |
fontSize: bodySize, |
||||
fontWeight: pw.FontWeight.bold, |
fontStyle: pw.FontStyle.italic, |
||||
), |
), |
||||
|
textAlign: pw.TextAlign.center, |
||||
), |
), |
||||
], |
|
||||
), |
|
||||
|
|
||||
pw.SizedBox(height: 3), |
|
||||
|
|
||||
// Mode de paiement |
|
||||
pw.Text( |
|
||||
'Paiement: ${_getPaymentMethodText(paymentMethod)}', |
|
||||
style: pw.TextStyle(fontSize: bodySize), |
|
||||
textAlign: pw.TextAlign.center, |
|
||||
), |
|
||||
|
|
||||
pw.SizedBox(height: 3), |
|
||||
|
|
||||
// Ligne de séparation |
|
||||
pw.Container( |
|
||||
width: double.infinity, |
|
||||
height: 0.5, |
|
||||
color: PdfColors.black, |
|
||||
), |
|
||||
|
|
||||
pw.SizedBox(height: 2), |
|
||||
|
|
||||
// Message de remerciement |
|
||||
pw.Text( |
|
||||
'Merci de votre visite !', |
|
||||
style: pw.TextStyle( |
|
||||
fontSize: bodySize, |
|
||||
fontStyle: pw.FontStyle.italic, |
|
||||
), |
), |
||||
textAlign: pw.TextAlign.center, |
|
||||
), |
pw.Container( |
||||
|
width: double.infinity, |
||||
pw.Text( |
child: pw.Text( |
||||
'A bientôt !', |
'A bientôt !', |
||||
style: pw.TextStyle(fontSize: smallSize), |
style: pw.TextStyle(fontSize: smallSize), |
||||
textAlign: pw.TextAlign.center, |
textAlign: pw.TextAlign.center, |
||||
), |
), |
||||
|
), |
||||
pw.SizedBox(height: 3), |
|
||||
|
pw.SizedBox(height: 3), |
||||
// Code de suivi (optionnel) |
|
||||
pw.Text( |
// Code de suivi (optionnel) |
||||
'Code: ${factureNumber}', |
pw.Container( |
||||
style: pw.TextStyle(fontSize: smallSize), |
width: double.infinity, |
||||
textAlign: pw.TextAlign.center, |
child: pw.Text( |
||||
), |
'Code: ${factureNumber}', |
||||
|
style: pw.TextStyle(fontSize: smallSize), |
||||
pw.SizedBox(height: 4), |
textAlign: pw.TextAlign.center, |
||||
|
), |
||||
// Ligne de découpe |
), |
||||
pw.Text( |
|
||||
'- - - - - - - - - - - - - - - -', |
pw.SizedBox(height: 4), |
||||
style: pw.TextStyle(fontSize: smallSize), |
|
||||
textAlign: pw.TextAlign.center, |
// Ligne de découpe |
||||
), |
pw.Container( |
||||
|
width: double.infinity, |
||||
pw.SizedBox(height: 2), |
child: pw.Text( |
||||
], |
'- - - - - - - - - - - - - - - -', |
||||
|
style: pw.TextStyle(fontSize: smallSize), |
||||
|
textAlign: pw.TextAlign.center, |
||||
|
), |
||||
|
), |
||||
|
|
||||
|
pw.SizedBox(height: 2), |
||||
|
], |
||||
|
), |
||||
); |
); |
||||
}, |
}, |
||||
), |
), |
||||
|
|||||
Loading…
Reference in new issue