|
|
@ -5,6 +5,7 @@ import 'package:flutter/material.dart'; |
|
|
import 'package:flutter/services.dart'; |
|
|
import 'package:flutter/services.dart'; |
|
|
import 'package:get/get.dart'; |
|
|
import 'package:get/get.dart'; |
|
|
import 'package:intl/intl.dart'; |
|
|
import 'package:intl/intl.dart'; |
|
|
|
|
|
import 'package:mysql1/mysql1.dart'; |
|
|
import 'package:numbers_to_letters/numbers_to_letters.dart'; |
|
|
import 'package:numbers_to_letters/numbers_to_letters.dart'; |
|
|
import 'package:pdf/pdf.dart'; |
|
|
import 'package:pdf/pdf.dart'; |
|
|
import 'package:pdf/widgets.dart' as pw; |
|
|
import 'package:pdf/widgets.dart' as pw; |
|
|
@ -276,10 +277,22 @@ class _GestionCommandesPageState extends State<GestionCommandesPage> { |
|
|
/// Le PDF est sauvegardé dans un fichier temporaire, qui est partagé |
|
|
/// Le PDF est sauvegardé dans un fichier temporaire, qui est partagé |
|
|
/// via le mécanisme de partage de fichiers du système. |
|
|
/// via le mécanisme de partage de fichiers du système. |
|
|
/// |
|
|
/// |
|
|
Future<void> _generateBonLivraison(Commande commande) async { |
|
|
// Dans GestionCommandesPage - Remplacez la méthode _generateBonLivraison complète |
|
|
|
|
|
|
|
|
|
|
|
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 pointDeVente = await _database.getPointDeVenteById(1); |
|
|
final pointDeVenteId = commande.pointDeVenteId; |
|
|
|
|
|
ResultRow? pointDeVenteComplet; |
|
|
|
|
|
|
|
|
|
|
|
// ✅ MODIFICATION: Récupération complète des données du point de vente |
|
|
|
|
|
if (pointDeVenteId != null) { |
|
|
|
|
|
pointDeVenteComplet = await _database.getPointDeVenteById(pointDeVenteId); |
|
|
|
|
|
} else { |
|
|
|
|
|
print("ce point de vente n'existe pas"); |
|
|
|
|
|
} |
|
|
|
|
|
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 |
|
|
@ -289,6 +302,32 @@ class _GestionCommandesPageState extends State<GestionCommandesPage> { |
|
|
? await _database.getUserById(commande.validateurId!) |
|
|
? await _database.getUserById(commande.validateurId!) |
|
|
: null; |
|
|
: null; |
|
|
|
|
|
|
|
|
|
|
|
// ✅ NOUVELLE FONCTIONNALITÉ: Parser les informations d'en-tête pour livraison |
|
|
|
|
|
List<String> infosLivraison = []; |
|
|
|
|
|
final livraisonBrute = pointDeVenteComplet?['livraison']; |
|
|
|
|
|
print('=== LIVRAISON BRUTE ==='); |
|
|
|
|
|
print(livraisonBrute); |
|
|
|
|
|
print('=== FIN ==='); |
|
|
|
|
|
|
|
|
|
|
|
if (livraisonBrute != null) { |
|
|
|
|
|
infosLivraison = _database.parseHeaderInfo(livraisonBrute); |
|
|
|
|
|
print('=== INFOS LIVRAISON PARSÉES ==='); |
|
|
|
|
|
for (int i = 0; i < infosLivraison.length; i++) { |
|
|
|
|
|
print('Ligne $i: ${infosLivraison[i]}'); |
|
|
|
|
|
} |
|
|
|
|
|
print('==============================='); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Infos par défaut si aucune info personnalisée |
|
|
|
|
|
final infosLivraisonDefaut = [ |
|
|
|
|
|
'REMAX Andravoangy', |
|
|
|
|
|
'SUPREME CENTER Behoririka \n BOX 405 | 416 | 119', |
|
|
|
|
|
'Tripolisa analankely BOX 7', |
|
|
|
|
|
'033 37 808 18', |
|
|
|
|
|
'www.guycom.mg', |
|
|
|
|
|
]; |
|
|
|
|
|
|
|
|
// ✅ DEBUG: Vérifiez combien de détails vous avez |
|
|
// ✅ DEBUG: Vérifiez combien de détails vous avez |
|
|
print('=== DEBUG BON DE LIVRAISON ==='); |
|
|
print('=== DEBUG BON DE LIVRAISON ==='); |
|
|
print('Nombre de détails récupérés: ${details.length}'); |
|
|
print('Nombre de détails récupérés: ${details.length}'); |
|
|
@ -329,16 +368,14 @@ class _GestionCommandesPageState extends State<GestionCommandesPage> { |
|
|
}); |
|
|
}); |
|
|
print(' ✅ Produit trouvé: ${produit.name}'); |
|
|
print(' ✅ Produit trouvé: ${produit.name}'); |
|
|
} else { |
|
|
} else { |
|
|
// ✅ Même si le produit est null, on l'ajoute quand même avec les infos du détail |
|
|
|
|
|
detailsAvecProduits.add({ |
|
|
detailsAvecProduits.add({ |
|
|
'detail': detail, |
|
|
'detail': detail, |
|
|
'produit': null, // On garde null mais on utilisera les infos du détail |
|
|
'produit': null, |
|
|
}); |
|
|
}); |
|
|
print(' ⚠️ Produit non trouvé, utilisation des données du détail'); |
|
|
print(' ⚠️ Produit non trouvé, utilisation des données du détail'); |
|
|
} |
|
|
} |
|
|
} catch (e) { |
|
|
} catch (e) { |
|
|
print(' ❌ Erreur lors de la récupération du produit: $e'); |
|
|
print(' ❌ Erreur lors de la récupération du produit: $e'); |
|
|
// En cas d'erreur, on ajoute quand même le détail |
|
|
|
|
|
detailsAvecProduits.add({ |
|
|
detailsAvecProduits.add({ |
|
|
'detail': detail, |
|
|
'detail': detail, |
|
|
'produit': null, |
|
|
'produit': null, |
|
|
@ -361,10 +398,9 @@ class _GestionCommandesPageState extends State<GestionCommandesPage> { |
|
|
regularFont = pw.Font.ttf(await rootBundle.load('assets/fonts/Roboto-Regular.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'); |
|
|
// Utiliser les polices par défaut |
|
|
|
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
// ✅ DÉFINITION DES STYLES DE TEXTE - Variables globales dans la fonction |
|
|
// ✅ DÉFINITION DES STYLES DE TEXTE |
|
|
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); |
|
|
@ -373,11 +409,54 @@ class _GestionCommandesPageState extends State<GestionCommandesPage> { |
|
|
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(fontSize: 9, fontWeight: pw.FontWeight.bold, font: italicFont ?? regularFont); |
|
|
final italicLogoStyle = pw.TextStyle(fontSize: 8, fontWeight: pw.FontWeight.bold, font: italicFont ?? regularFont); |
|
|
final italicLogoStyle = pw.TextStyle(fontSize: 8, fontWeight: pw.FontWeight.bold, font: italicFont ?? regularFont); |
|
|
|
|
|
Future<pw.Widget> buildLogoWidget() async { |
|
|
|
|
|
final logoRaw = pointDeVenteComplet?['logo']; |
|
|
|
|
|
|
|
|
|
|
|
if (logoRaw != null) { |
|
|
|
|
|
try { |
|
|
|
|
|
Uint8List bytes; |
|
|
|
|
|
if (logoRaw is Uint8List) { |
|
|
|
|
|
bytes = logoRaw; |
|
|
|
|
|
} else if (logoRaw is List<int>) { |
|
|
|
|
|
bytes = Uint8List.fromList(logoRaw); |
|
|
|
|
|
} else if (logoRaw.runtimeType.toString() == 'Blob') { |
|
|
|
|
|
// Cast dynamique pour appeler toBytes() |
|
|
|
|
|
dynamic blobDynamic = logoRaw; |
|
|
|
|
|
bytes = blobDynamic.toBytes(); |
|
|
|
|
|
} else { |
|
|
|
|
|
throw Exception("Format de logo non supporté: ${logoRaw.runtimeType}"); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
final imageLogo = pw.MemoryImage(bytes); |
|
|
|
|
|
return pw.Image(imageLogo, width: 100, height: 100); |
|
|
|
|
|
} catch (e) { |
|
|
|
|
|
print('Erreur chargement logo BDD: $e'); |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
return pw.Image(image, width: 100, height: 100); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
final logoWidget = await buildLogoWidget(); |
|
|
|
|
|
|
|
|
|
|
|
// ✅ FONCTION POUR CONSTRUIRE L'EN-TÊTE DYNAMIQUE |
|
|
|
|
|
pw.Widget buildEnteteInfos() { |
|
|
|
|
|
final infosAUtiliser = infosLivraison.isNotEmpty ? infosLivraison : infosLivraisonDefaut; |
|
|
|
|
|
|
|
|
|
|
|
return pw.Column( |
|
|
|
|
|
crossAxisAlignment: pw.CrossAxisAlignment.start, |
|
|
|
|
|
children: infosAUtiliser.map((info) { |
|
|
|
|
|
return pw.Padding( |
|
|
|
|
|
padding: const pw.EdgeInsets.only(bottom: 1), |
|
|
|
|
|
child: pw.Text(info, style: tinyTextStyle), |
|
|
|
|
|
); |
|
|
|
|
|
}).toList(), |
|
|
|
|
|
); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
// ✅ Fonction pour créer un exemplaire - CORRIGÉE |
|
|
// ✅ Fonction pour créer un exemplaire - AVEC EN-TÊTE DYNAMIQUE |
|
|
pw.Widget buildExemplaire(String typeExemplaire) { |
|
|
pw.Widget buildExemplaire(String typeExemplaire) { |
|
|
return pw.Container( |
|
|
return pw.Container( |
|
|
// ✅ PAS DE HAUTEUR FIXE - Elle s'adapte au contenu |
|
|
|
|
|
width: double.infinity, |
|
|
width: double.infinity, |
|
|
decoration: pw.BoxDecoration( |
|
|
decoration: pw.BoxDecoration( |
|
|
border: pw.Border.all(color: PdfColors.black, width: 1.5), |
|
|
border: pw.Border.all(color: PdfColors.black, width: 1.5), |
|
|
@ -415,28 +494,15 @@ class _GestionCommandesPageState extends State<GestionCommandesPage> { |
|
|
crossAxisAlignment: pw.CrossAxisAlignment.start, |
|
|
crossAxisAlignment: pw.CrossAxisAlignment.start, |
|
|
mainAxisAlignment: pw.MainAxisAlignment.spaceBetween, |
|
|
mainAxisAlignment: pw.MainAxisAlignment.spaceBetween, |
|
|
children: [ |
|
|
children: [ |
|
|
// Logo et infos entreprise |
|
|
// Logo et infos entreprise - ✅ AVEC INFOS DYNAMIQUES |
|
|
pw.Column( |
|
|
pw.Column( |
|
|
crossAxisAlignment: pw.CrossAxisAlignment.start, |
|
|
crossAxisAlignment: pw.CrossAxisAlignment.start, |
|
|
children: [ |
|
|
children: [ |
|
|
pw.Container( |
|
|
logoWidget, |
|
|
width: 100, |
|
|
|
|
|
height: 100, |
|
|
|
|
|
child: pw.Image(image), |
|
|
|
|
|
), |
|
|
|
|
|
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), |
|
|
pw.Column( |
|
|
buildEnteteInfos(), // ✅ EN-TÊTE DYNAMIQUE ICI |
|
|
crossAxisAlignment: pw.CrossAxisAlignment.start, |
|
|
|
|
|
children: [ |
|
|
|
|
|
pw.Text('REMAX Andravoangy', 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), |
|
|
|
|
|
], |
|
|
|
|
|
), |
|
|
|
|
|
], |
|
|
], |
|
|
), |
|
|
), |
|
|
|
|
|
|
|
|
@ -964,14 +1030,22 @@ class _GestionCommandesPageState extends State<GestionCommandesPage> { |
|
|
|
|
|
|
|
|
// 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 |
|
|
Future<void> _generateInvoice(Commande commande) async { |
|
|
Future<void> _generateInvoice(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 pointDeVente = await _database.getPointDeVenteById(1); |
|
|
final pointDeVenteId = commande.pointDeVenteId; |
|
|
|
|
|
ResultRow? pointDeVenteComplet; |
|
|
|
|
|
|
|
|
|
|
|
if (pointDeVenteId != null) { |
|
|
|
|
|
pointDeVenteComplet = await _database.getPointDeVenteById(pointDeVenteId); |
|
|
|
|
|
} else { |
|
|
|
|
|
print("ce point de vente n'existe pas"); |
|
|
|
|
|
} |
|
|
|
|
|
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 |
|
|
@ -981,6 +1055,34 @@ class _GestionCommandesPageState extends State<GestionCommandesPage> { |
|
|
? await _database.getUserById(commande.validateurId!) |
|
|
? await _database.getUserById(commande.validateurId!) |
|
|
: null; |
|
|
: null; |
|
|
|
|
|
|
|
|
|
|
|
List<String> infosFacture = []; |
|
|
|
|
|
final factureBrute = pointDeVenteComplet?['facture']; |
|
|
|
|
|
print('=== FACTURE BRUTE ==='); |
|
|
|
|
|
print(factureBrute); |
|
|
|
|
|
print('=== FIN ==='); |
|
|
|
|
|
|
|
|
|
|
|
if (factureBrute != null) { |
|
|
|
|
|
infosFacture = _database.parseHeaderInfo(factureBrute); |
|
|
|
|
|
print('=== INFOS FACTURE PARSÉES ==='); |
|
|
|
|
|
for (int i = 0; i < infosFacture.length; i++) { |
|
|
|
|
|
print('Ligne $i: ${infosFacture[i]}'); |
|
|
|
|
|
} |
|
|
|
|
|
print('==============================='); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// Infos par défaut si aucune info personnalisée |
|
|
|
|
|
final infosFactureDefaut = [ |
|
|
|
|
|
'REMAX by GUYCOM Andravoangy', |
|
|
|
|
|
'SUPREME CENTER Behoririka box 405', |
|
|
|
|
|
'SUPREME CENTER Behoririka box 416', |
|
|
|
|
|
'SUPREME CENTER Behoririka box 119', |
|
|
|
|
|
'TRIPOLITSA Analakely BOX 7', |
|
|
|
|
|
'033 37 808 18', |
|
|
|
|
|
'www.guycom.mg', |
|
|
|
|
|
'NIF: 4000106673 - STAT 95210 11 2017 1 003651', |
|
|
|
|
|
'Facebook: GuyCom', |
|
|
|
|
|
]; |
|
|
|
|
|
|
|
|
final iconPhone = await buildIconPhoneText(); |
|
|
final iconPhone = await buildIconPhoneText(); |
|
|
final iconChecked = await buildIconCheckedText(); |
|
|
final iconChecked = await buildIconCheckedText(); |
|
|
final iconGlobe = await buildIconGlobeText(); |
|
|
final iconGlobe = await buildIconGlobeText(); |
|
|
@ -1032,6 +1134,55 @@ class _GestionCommandesPageState extends State<GestionCommandesPage> { |
|
|
final emojifont = pw.TextStyle( |
|
|
final emojifont = pw.TextStyle( |
|
|
fontSize: 8, fontWeight: pw.FontWeight.bold, font: emojiSuportFont); |
|
|
fontSize: 8, fontWeight: pw.FontWeight.bold, font: emojiSuportFont); |
|
|
|
|
|
|
|
|
|
|
|
Future<pw.Widget> buildLogoWidget() async { |
|
|
|
|
|
final logoRaw = pointDeVenteComplet?['logo']; |
|
|
|
|
|
|
|
|
|
|
|
if (logoRaw != null) { |
|
|
|
|
|
try { |
|
|
|
|
|
Uint8List bytes; |
|
|
|
|
|
if (logoRaw is Uint8List) { |
|
|
|
|
|
bytes = logoRaw; |
|
|
|
|
|
} else if (logoRaw is List<int>) { |
|
|
|
|
|
bytes = Uint8List.fromList(logoRaw); |
|
|
|
|
|
} else if (logoRaw.runtimeType.toString() == 'Blob') { |
|
|
|
|
|
// Cast dynamique pour appeler toBytes() |
|
|
|
|
|
dynamic blobDynamic = logoRaw; |
|
|
|
|
|
bytes = blobDynamic.toBytes(); |
|
|
|
|
|
} else { |
|
|
|
|
|
throw Exception("Format de logo non supporté: ${logoRaw.runtimeType}"); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
final imageLogo = pw.MemoryImage(bytes); |
|
|
|
|
|
return pw.Container(width: 200, height: 120, child: pw.Image(imageLogo)); |
|
|
|
|
|
} catch (e) { |
|
|
|
|
|
print('Erreur chargement logo BDD: $e'); |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
return pw.Container(width: 200, height: 100, child: pw.Image(image)); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
final logoWidget = await buildLogoWidget(); |
|
|
|
|
|
pw.Widget buildEnteteFactureInfos() { |
|
|
|
|
|
final infosAUtiliser = infosFacture.isNotEmpty ? infosFacture : infosFactureDefaut; |
|
|
|
|
|
|
|
|
|
|
|
return pw.Column( |
|
|
|
|
|
crossAxisAlignment: pw.CrossAxisAlignment.start, |
|
|
|
|
|
children: [ |
|
|
|
|
|
...infosAUtiliser.map((info) { |
|
|
|
|
|
return pw.Row( |
|
|
|
|
|
children: [ |
|
|
|
|
|
iconChecked, |
|
|
|
|
|
pw.SizedBox(width: 4), |
|
|
|
|
|
pw.Text(info, style: smallTextStyle), |
|
|
|
|
|
], |
|
|
|
|
|
); |
|
|
|
|
|
}), |
|
|
|
|
|
pw.SizedBox(height: 2), // ajouté en fin de liste |
|
|
|
|
|
], |
|
|
|
|
|
); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
pdf.addPage( |
|
|
pdf.addPage( |
|
|
pw.Page( |
|
|
pw.Page( |
|
|
pageFormat: PdfPageFormat.a4, // Mode portrait |
|
|
pageFormat: PdfPageFormat.a4, // Mode portrait |
|
|
@ -1049,71 +1200,11 @@ class _GestionCommandesPageState extends State<GestionCommandesPage> { |
|
|
pw.Column( |
|
|
pw.Column( |
|
|
crossAxisAlignment: pw.CrossAxisAlignment.start, |
|
|
crossAxisAlignment: pw.CrossAxisAlignment.start, |
|
|
children: [ |
|
|
children: [ |
|
|
pw.Container( |
|
|
logoWidget, |
|
|
width: 200, |
|
|
|
|
|
height: 120, |
|
|
|
|
|
child: pw.Image(image), |
|
|
|
|
|
), |
|
|
|
|
|
pw.Text(' NOTRE COMPETENCE, A VOTRE SERVICE', |
|
|
pw.Text(' NOTRE COMPETENCE, A VOTRE SERVICE', |
|
|
style: italicTextStyleLogo), |
|
|
style: italicTextStyleLogo), |
|
|
pw.SizedBox(height: 10), |
|
|
pw.SizedBox(height: 10), |
|
|
pw.Column( |
|
|
buildEnteteFactureInfos(), |
|
|
crossAxisAlignment: pw.CrossAxisAlignment.start, |
|
|
|
|
|
children: [ |
|
|
|
|
|
pw.Row(children: [ |
|
|
|
|
|
iconChecked, |
|
|
|
|
|
pw.SizedBox(width: 4), |
|
|
|
|
|
pw.Text('REMAX by GUYCOM Andravoangy', |
|
|
|
|
|
style: smallTextStyle) |
|
|
|
|
|
]), |
|
|
|
|
|
pw.SizedBox(height: 2), |
|
|
|
|
|
pw.Row(children: [ |
|
|
|
|
|
iconChecked, |
|
|
|
|
|
pw.SizedBox(width: 4), |
|
|
|
|
|
pw.Text('SUPREME CENTER Behoririka box 405', |
|
|
|
|
|
style: smallTextStyle) |
|
|
|
|
|
]), |
|
|
|
|
|
pw.SizedBox(height: 2), |
|
|
|
|
|
pw.Row(children: [ |
|
|
|
|
|
iconChecked, |
|
|
|
|
|
pw.SizedBox(width: 4), |
|
|
|
|
|
pw.Text('SUPREME CENTER Behoririka box 416', |
|
|
|
|
|
style: smallTextStyle) |
|
|
|
|
|
]), |
|
|
|
|
|
pw.SizedBox(height: 2), |
|
|
|
|
|
pw.Row(children: [ |
|
|
|
|
|
iconChecked, |
|
|
|
|
|
pw.SizedBox(width: 4), |
|
|
|
|
|
pw.Text('SUPREME CENTER Behoririka box 119', |
|
|
|
|
|
style: smallTextStyle) |
|
|
|
|
|
]), |
|
|
|
|
|
pw.SizedBox(height: 2), |
|
|
|
|
|
pw.Row(children: [ |
|
|
|
|
|
iconChecked, |
|
|
|
|
|
pw.SizedBox(width: 4), |
|
|
|
|
|
pw.Text('TRIPOLITSA Analakely BOX 7', |
|
|
|
|
|
style: smallTextStyle) |
|
|
|
|
|
]), |
|
|
|
|
|
], |
|
|
|
|
|
), |
|
|
|
|
|
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: 4000106673 - STAT 95210 11 2017 1 003651', |
|
|
|
|
|
style: smallTextStyle) |
|
|
|
|
|
]), |
|
|
|
|
|
pw.Text('Facebook: GuyCom', style: smallTextStyle), |
|
|
|
|
|
], |
|
|
], |
|
|
), |
|
|
), |
|
|
|
|
|
|
|
|
@ -1906,9 +1997,11 @@ class _GestionCommandesPageState extends State<GestionCommandesPage> { |
|
|
return 'MÉTHODE INCONNUE (${payment.type.toString()})'; // Debug info |
|
|
return 'MÉTHODE INCONNUE (${payment.type.toString()})'; // Debug info |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
// Dans GestionCommandesPage - Remplacez la méthode _generateReceipt complète |
|
|
|
|
|
|
|
|
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 |
|
|
@ -2875,6 +2968,30 @@ class _GestionCommandesPageState extends State<GestionCommandesPage> { |
|
|
trailing: Row( |
|
|
trailing: Row( |
|
|
mainAxisSize: MainAxisSize.min, |
|
|
mainAxisSize: MainAxisSize.min, |
|
|
children: [ |
|
|
children: [ |
|
|
|
|
|
Container( |
|
|
|
|
|
decoration: BoxDecoration( |
|
|
|
|
|
color: Colors.white, |
|
|
|
|
|
borderRadius: BorderRadius.circular(8), |
|
|
|
|
|
boxShadow: [ |
|
|
|
|
|
BoxShadow( |
|
|
|
|
|
color: Colors.black.withOpacity(0.1), |
|
|
|
|
|
blurRadius: 2, |
|
|
|
|
|
offset: const Offset(0, 1), |
|
|
|
|
|
), |
|
|
|
|
|
], |
|
|
|
|
|
), |
|
|
|
|
|
child: IconButton( |
|
|
|
|
|
icon: Icon( |
|
|
|
|
|
Icons.payment, |
|
|
|
|
|
color: Colors.green.shade600, |
|
|
|
|
|
), |
|
|
|
|
|
onPressed: () => _showPaymentOptions(commande), |
|
|
|
|
|
tooltip: 'Générer le ticket de la commande', |
|
|
|
|
|
), |
|
|
|
|
|
), |
|
|
|
|
|
const SizedBox( |
|
|
|
|
|
width: 10, |
|
|
|
|
|
), |
|
|
Container( |
|
|
Container( |
|
|
decoration: BoxDecoration( |
|
|
decoration: BoxDecoration( |
|
|
color: Colors.white, |
|
|
color: Colors.white, |
|
|
@ -2950,8 +3067,7 @@ class _GestionCommandesPageState extends State<GestionCommandesPage> { |
|
|
CommandeActions( |
|
|
CommandeActions( |
|
|
commande: commande, |
|
|
commande: commande, |
|
|
onStatutChanged: _updateStatut, |
|
|
onStatutChanged: _updateStatut, |
|
|
onPaymentSelected: |
|
|
onGenerateBonLivraison:_generateBon_lifraisonWithPasswordVerification |
|
|
_showPaymentOptions, |
|
|
|
|
|
), |
|
|
), |
|
|
], |
|
|
], |
|
|
), |
|
|
), |
|
|
|