You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
266 lines
8.4 KiB
266 lines
8.4 KiB
// services/pdf_service.dart
|
|
import 'dart:io';
|
|
import 'dart:typed_data';
|
|
import 'package:pdf/pdf.dart';
|
|
import 'package:pdf/widgets.dart' as pw;
|
|
import 'package:printing/printing.dart';
|
|
import 'package:path_provider/path_provider.dart';
|
|
import 'package:share_plus/share_plus.dart';
|
|
import '../models/command_detail.dart';
|
|
|
|
class PdfService {
|
|
static Future<Uint8List> generateFacturePdf({
|
|
required CommandeDetail commande,
|
|
required String paymentMethod,
|
|
}) async {
|
|
final pdf = pw.Document();
|
|
|
|
// Informations du restaurant
|
|
final restaurantInfo = {
|
|
'nom': 'RESTAURANT',
|
|
'adresse': 'Moramanga, Antananarivo',
|
|
'contact': '+261 34 12 34 56',
|
|
};
|
|
|
|
// Générer numéro de facture
|
|
final factureNumber =
|
|
'F${DateTime.now().millisecondsSinceEpoch.toString().substring(7)}';
|
|
final dateTime = DateTime.now();
|
|
|
|
pdf.addPage(
|
|
pw.Page(
|
|
pageFormat: PdfPageFormat.a4,
|
|
margin: const pw.EdgeInsets.all(32),
|
|
build: (pw.Context context) {
|
|
return pw.Column(
|
|
crossAxisAlignment: pw.CrossAxisAlignment.start,
|
|
children: [
|
|
// En-tête Restaurant
|
|
pw.Center(
|
|
child: pw.Column(
|
|
children: [
|
|
pw.Text(
|
|
restaurantInfo['nom']!,
|
|
style: pw.TextStyle(
|
|
fontSize: 24,
|
|
fontWeight: pw.FontWeight.bold,
|
|
),
|
|
),
|
|
pw.SizedBox(height: 8),
|
|
pw.Text(
|
|
'Adresse: ${restaurantInfo['adresse']}',
|
|
style: const pw.TextStyle(fontSize: 12),
|
|
),
|
|
pw.Text(
|
|
'Contact: ${restaurantInfo['contact']}',
|
|
style: const pw.TextStyle(fontSize: 12),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
|
|
pw.SizedBox(height: 30),
|
|
|
|
// Informations facture
|
|
pw.Center(
|
|
child: pw.Column(
|
|
children: [
|
|
pw.Text(
|
|
'Facture n° $factureNumber',
|
|
style: pw.TextStyle(
|
|
fontSize: 14,
|
|
fontWeight: pw.FontWeight.bold,
|
|
),
|
|
),
|
|
pw.SizedBox(height: 4),
|
|
pw.Text(
|
|
'Date: ${_formatDateTime(dateTime)}',
|
|
style: const pw.TextStyle(fontSize: 12),
|
|
),
|
|
pw.SizedBox(height: 4),
|
|
pw.Text(
|
|
'Table: ${commande.numeroCommande}',
|
|
style: const pw.TextStyle(fontSize: 12),
|
|
),
|
|
pw.SizedBox(height: 4),
|
|
pw.Text(
|
|
'Paiement: ${_getPaymentMethodText(paymentMethod)}',
|
|
style: const pw.TextStyle(fontSize: 12),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
|
|
pw.SizedBox(height: 30),
|
|
|
|
// Tableau des articles
|
|
pw.Table(
|
|
border: pw.TableBorder.all(color: PdfColors.grey300),
|
|
columnWidths: {
|
|
0: const pw.FlexColumnWidth(3),
|
|
1: const pw.FlexColumnWidth(1),
|
|
2: const pw.FlexColumnWidth(1),
|
|
},
|
|
children: [
|
|
// En-tête du tableau
|
|
pw.TableRow(
|
|
decoration: const pw.BoxDecoration(
|
|
color: PdfColors.grey100,
|
|
),
|
|
children: [
|
|
pw.Padding(
|
|
padding: const pw.EdgeInsets.all(8),
|
|
child: pw.Text(
|
|
'Qté Désignation',
|
|
style: pw.TextStyle(fontWeight: pw.FontWeight.bold),
|
|
),
|
|
),
|
|
pw.Padding(
|
|
padding: const pw.EdgeInsets.all(8),
|
|
child: pw.Text(
|
|
'Prix',
|
|
style: pw.TextStyle(fontWeight: pw.FontWeight.bold),
|
|
textAlign: pw.TextAlign.right,
|
|
),
|
|
),
|
|
],
|
|
),
|
|
// Lignes des articles
|
|
...commande.items
|
|
.map(
|
|
(item) => pw.TableRow(
|
|
children: [
|
|
pw.Padding(
|
|
padding: const pw.EdgeInsets.all(8),
|
|
child: pw.Text(
|
|
'${item.quantite} TESTNOMCOMMANDE',
|
|
),
|
|
),
|
|
pw.Padding(
|
|
padding: const pw.EdgeInsets.all(8),
|
|
child: pw.Text(
|
|
'${item.prixUnitaire.toStringAsFixed(2)} €',
|
|
textAlign: pw.TextAlign.right,
|
|
),
|
|
),
|
|
],
|
|
),
|
|
)
|
|
.toList(),
|
|
],
|
|
),
|
|
|
|
pw.SizedBox(height: 20),
|
|
|
|
// Total
|
|
pw.Container(
|
|
alignment: pw.Alignment.centerRight,
|
|
child: pw.Container(
|
|
padding: const pw.EdgeInsets.all(12),
|
|
decoration: pw.BoxDecoration(
|
|
border: pw.Border.all(color: PdfColors.grey400),
|
|
color: PdfColors.grey50,
|
|
),
|
|
child: pw.Text(
|
|
'Total: ${commande.totalTtc.toStringAsFixed(2)} €',
|
|
style: pw.TextStyle(
|
|
fontSize: 16,
|
|
fontWeight: pw.FontWeight.bold,
|
|
),
|
|
),
|
|
),
|
|
),
|
|
|
|
pw.Spacer(),
|
|
|
|
// Message de remerciement
|
|
pw.Center(
|
|
child: pw.Text(
|
|
'Merci et à bientôt !',
|
|
style: pw.TextStyle(
|
|
fontSize: 12,
|
|
fontStyle: pw.FontStyle.italic,
|
|
),
|
|
),
|
|
),
|
|
],
|
|
);
|
|
},
|
|
),
|
|
);
|
|
|
|
return pdf.save();
|
|
}
|
|
|
|
static String _formatDateTime(DateTime dateTime) {
|
|
return '${dateTime.day.toString().padLeft(2, '0')}/${dateTime.month.toString().padLeft(2, '0')}/${dateTime.year} ${dateTime.hour.toString().padLeft(2, '0')}:${dateTime.minute.toString().padLeft(2, '0')}';
|
|
}
|
|
|
|
static String _getPaymentMethodText(String method) {
|
|
switch (method) {
|
|
case 'mvola':
|
|
return 'MVola';
|
|
case 'carte':
|
|
return 'CB';
|
|
case 'especes':
|
|
return 'Espèces';
|
|
default:
|
|
return 'CB';
|
|
}
|
|
}
|
|
|
|
// Imprimer directement
|
|
static Future<bool> printFacture({
|
|
required CommandeDetail commande,
|
|
required String paymentMethod,
|
|
}) async {
|
|
try {
|
|
final pdfData = await generateFacturePdf(
|
|
commande: commande,
|
|
paymentMethod: paymentMethod,
|
|
);
|
|
|
|
await Printing.layoutPdf(
|
|
onLayout: (PdfPageFormat format) async => pdfData,
|
|
name:
|
|
'Facture_${commande.numeroCommande}_${DateTime.now().millisecondsSinceEpoch}',
|
|
);
|
|
|
|
return true;
|
|
} catch (e) {
|
|
print('Erreur impression: $e');
|
|
return false;
|
|
}
|
|
}
|
|
|
|
// Sauvegarder et partager le PDF
|
|
static Future<bool> saveAndShareFacture({
|
|
required CommandeDetail commande,
|
|
required String paymentMethod,
|
|
}) async {
|
|
try {
|
|
final pdfData = await generateFacturePdf(
|
|
commande: commande,
|
|
paymentMethod: paymentMethod,
|
|
);
|
|
|
|
final directory = await getApplicationDocumentsDirectory();
|
|
final fileName =
|
|
'Facture_${commande.numeroCommande}_${DateTime.now().millisecondsSinceEpoch}.pdf';
|
|
final file = File('${directory.path}/$fileName');
|
|
|
|
await file.writeAsBytes(pdfData);
|
|
|
|
await Share.shareXFiles(
|
|
[XFile(file.path)],
|
|
subject: 'Facture ${commande.numeroCommande}',
|
|
text: 'Facture de votre commande au restaurant',
|
|
);
|
|
|
|
return true;
|
|
} catch (e) {
|
|
print('Erreur sauvegarde/partage: $e');
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
|