Compare commits

..

3 Commits

Author SHA1 Message Date
5ad019d35e commit de 25-07-2025 2025-07-25 11:23:39 +02:00
a52749c415 commit 24/07/2025-2 2025-07-24 21:33:58 +02:00
ab398bddc6 commit 24/07/2025 2025-07-24 20:32:03 +02:00
8 changed files with 4316 additions and 4414 deletions

View File

@ -8,7 +8,8 @@ plugins {
android { android {
namespace = "com.example.my_app" namespace = "com.example.my_app"
compileSdk = flutter.compileSdkVersion compileSdk = flutter.compileSdkVersion
ndkVersion = flutter.ndkVersion ndkVersion = "26.3.11579264"
compileOptions { compileOptions {
sourceCompatibility = JavaVersion.VERSION_11 sourceCompatibility = JavaVersion.VERSION_11

View File

@ -1464,12 +1464,12 @@ class AppDatabase {
commandeMap['clientId'] = clientId; commandeMap['clientId'] = clientId;
final commandeFields = commandeMap.keys.join(', '); final commandeFields = commandeMap.keys.join(', ');
final commandePlaceholders = final commandePlaceholders = List.filled(commandeMap.length, '?').join(', ');
List.filled(commandeMap.length, '?').join(', ');
final commandeResult = await db.query( final commandeResult = await db.query(
'INSERT INTO commandes ($commandeFields) VALUES ($commandePlaceholders)', 'INSERT INTO commandes ($commandeFields) VALUES ($commandePlaceholders)',
commandeMap.values.toList()); commandeMap.values.toList(),
);
final commandeId = commandeResult.insertId!; final commandeId = commandeResult.insertId!;
// 3. Créer les détails de commande avec remises // 3. Créer les détails de commande avec remises
@ -1479,16 +1479,18 @@ class AppDatabase {
detailMap['commandeId'] = commandeId; detailMap['commandeId'] = commandeId;
final detailFields = detailMap.keys.join(', '); final detailFields = detailMap.keys.join(', ');
final detailPlaceholders = final detailPlaceholders = List.filled(detailMap.length, '?').join(', ');
List.filled(detailMap.length, '?').join(', ');
await db.query( await db.query(
'INSERT INTO details_commandes ($detailFields) VALUES ($detailPlaceholders)', 'INSERT INTO details_commandes ($detailFields) VALUES ($detailPlaceholders)',
detailMap.values.toList()); detailMap.values.toList(),
);
// 4. Mettre à jour le stock // 4. Mettre à jour le stock
await db.query('UPDATE products SET stock = stock - ? WHERE id = ?', await db.query(
[detail.quantite, detail.produitId]); 'UPDATE products SET stock = stock - ? WHERE id = ?',
[detail.quantite, detail.produitId],
);
} }
await db.query('COMMIT'); await db.query('COMMIT');
@ -1498,7 +1500,8 @@ class AppDatabase {
print("Erreur lors de la création de la commande complète: $e"); print("Erreur lors de la création de la commande complète: $e");
rethrow; rethrow;
} }
} }
// Méthode pour mettre à jour un détail de commande (utile pour modifier les remises) // Méthode pour mettre à jour un détail de commande (utile pour modifier les remises)
Future<int> updateDetailCommande(DetailCommande detail) async { Future<int> updateDetailCommande(DetailCommande detail) async {

View File

@ -52,6 +52,7 @@ class _ApprobationSortiesPageState extends State<ApprobationSortiesPage> {
Text('Quantité: ${sortie['quantite']}'), Text('Quantité: ${sortie['quantite']}'),
Text('Demandeur: ${sortie['admin_nom']}'), Text('Demandeur: ${sortie['admin_nom']}'),
Text('Motif: ${sortie['motif']}'), Text('Motif: ${sortie['motif']}'),
Text('Note: ${sortie['notes']}'),
const SizedBox(height: 16), const SizedBox(height: 16),
const Text( const Text(
'Confirmer l\'approbation de cette demande de sortie personnelle ?', 'Confirmer l\'approbation de cette demande de sortie personnelle ?',

View File

@ -289,9 +289,13 @@ class _GestionCommandesPageState extends State<GestionCommandesPage> {
? await _database.getUserById(commande.validateurId!) ? await _database.getUserById(commande.validateurId!)
: null; : null;
final iconPhone = await buildIconPhoneText(); // DEBUG: Vérifiez combien de détails vous avez
final iconChecked = await buildIconCheckedText(); print('=== DEBUG BON DE LIVRAISON ===');
final iconGlobe = await buildIconGlobeText(); print('Nombre de détails récupérés: ${details.length}');
for (int i = 0; i < details.length; i++) {
print('Détail $i: ${details[i].produitNom} x${details[i].quantite}');
}
double sousTotal = 0; double sousTotal = 0;
double totalRemises = 0; double totalRemises = 0;
@ -308,43 +312,72 @@ class _GestionCommandesPageState extends State<GestionCommandesPage> {
} }
} }
// CORRECTION PRINCIPALE: Améliorer la récupération des produits
final List<Map<String, dynamic>> detailsAvecProduits = []; final List<Map<String, dynamic>> detailsAvecProduits = [];
for (final detail in details) {
for (int i = 0; i < details.length; i++) {
final detail = details[i];
print('Traitement détail $i: ${detail.produitNom}');
try {
final produit = await _database.getProductById(detail.produitId); final produit = await _database.getProductById(detail.produitId);
if (produit != null) {
detailsAvecProduits.add({ detailsAvecProduits.add({
'detail': detail, 'detail': detail,
'produit': produit, 'produit': produit,
}); });
print(' ✅ Produit trouvé: ${produit.name}');
} else {
// Même si le produit est null, on l'ajoute quand même avec les infos du détail
detailsAvecProduits.add({
'detail': detail,
'produit': null, // On garde null mais on utilisera les infos du détail
});
print(' ⚠️ Produit non trouvé, utilisation des données du détail');
} }
} catch (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({
'detail': detail,
'produit': null,
});
}
}
print('Total detailsAvecProduits: ${detailsAvecProduits.length}');
final pdf = pw.Document(); final pdf = pw.Document();
final imageBytes = await loadImage(); final imageBytes = await loadImage();
final image = pw.MemoryImage(imageBytes); final image = pw.MemoryImage(imageBytes);
final italicFont =
pw.Font.ttf(await rootBundle.load('assets/fonts/Roboto-Italic.ttf'));
// Tailles de texte agrandies pour une meilleure lisibilité // AMÉLIORATION: Gestion des polices avec fallback
final tinyTextStyle = pw.TextStyle(fontSize: 9); pw.Font? italicFont;
final smallTextStyle = pw.TextStyle(fontSize: 10); pw.Font? regularFont;
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 try {
italicFont = 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) {
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
final tinyTextStyle = pw.TextStyle(fontSize: 9, font: regularFont);
final smallTextStyle = pw.TextStyle(fontSize: 10, font: regularFont);
final normalTextStyle = pw.TextStyle(fontSize: 11, font: regularFont);
final boldTextStyle = pw.TextStyle(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 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);
// Fonction pour créer un exemplaire - CORRIGÉE
pw.Widget buildExemplaire(String typeExemplaire) { pw.Widget buildExemplaire(String typeExemplaire) {
return pw.Container( return pw.Container(
height: 380, // Hauteur ajustée pour le mode paysage // 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),
@ -357,9 +390,7 @@ class _GestionCommandesPageState extends State<GestionCommandesPage> {
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" color: typeExemplaire == "CLIENT" ? PdfColors.blue100 : PdfColors.green100,
? PdfColors.blue100
: PdfColors.green100,
), ),
child: pw.Center( child: pw.Center(
child: pw.Text( child: pw.Text(
@ -367,21 +398,19 @@ class _GestionCommandesPageState extends State<GestionCommandesPage> {
style: pw.TextStyle( style: pw.TextStyle(
fontSize: 14, fontSize: 14,
fontWeight: pw.FontWeight.bold, fontWeight: pw.FontWeight.bold,
color: typeExemplaire == "CLIENT" color: typeExemplaire == "CLIENT" ? PdfColors.blue800 : PdfColors.green800,
? PdfColors.blue800 font: regularFont,
: PdfColors.green800,
), ),
), ),
), ),
), ),
pw.Expanded( pw.Padding(
child: pw.Padding(
padding: const pw.EdgeInsets.all(8), padding: const pw.EdgeInsets.all(8),
child: pw.Column( child: pw.Column(
crossAxisAlignment: pw.CrossAxisAlignment.start, crossAxisAlignment: pw.CrossAxisAlignment.start,
children: [ children: [
// En-tête principal // En-tête principal (logo, infos entreprise, client)
pw.Row( pw.Row(
crossAxisAlignment: pw.CrossAxisAlignment.start, crossAxisAlignment: pw.CrossAxisAlignment.start,
mainAxisAlignment: pw.MainAxisAlignment.spaceBetween, mainAxisAlignment: pw.MainAxisAlignment.spaceBetween,
@ -396,26 +425,16 @@ class _GestionCommandesPageState extends State<GestionCommandesPage> {
child: pw.Image(image), child: pw.Image(image),
), ),
pw.SizedBox(height: 3), pw.SizedBox(height: 3),
pw.Text('NOTRE COMPETENCE, A VOTRE SERVICE', pw.Text('NOTRE COMPETENCE, A VOTRE SERVICE', style: italicLogoStyle),
style: italicLogoStyle),
pw.SizedBox(height: 4), pw.SizedBox(height: 4),
pw.Column( pw.Column(
crossAxisAlignment: pw.CrossAxisAlignment.start, crossAxisAlignment: pw.CrossAxisAlignment.start,
children: [ children: [
pw.Text('📍 REMAX Andravoangy', pw.Text('REMAX Andravoangy', style: tinyTextStyle),
style: tinyTextStyle), pw.Text('SUPREME CENTER Behoririka \n BOX 405 | 416 | 119', style: tinyTextStyle),
pw.Text( pw.Text('Tripolisa analankely BOX 7', style: tinyTextStyle),
'📍 SUPREME CENTER Behoririka \n BOX 405 | 416 | 119', pw.Text('033 37 808 18', style: tinyTextStyle),
style: tinyTextStyle), pw.Text('www.guycom.mg', 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: 2),
// pw.Text('NIF: 4000106673 - STAT 95210 11 2017 1 003651',
// style: pw.TextStyle(fontSize: 7, fontWeight: pw.FontWeight.bold)),
], ],
), ),
], ],
@ -425,12 +444,9 @@ class _GestionCommandesPageState extends State<GestionCommandesPage> {
pw.Column( pw.Column(
crossAxisAlignment: pw.CrossAxisAlignment.center, crossAxisAlignment: pw.CrossAxisAlignment.center,
children: [ children: [
pw.Text( pw.Text('Date: ${DateFormat('dd/MM/yyyy').format(DateTime.now())}', style: boldClientStyle),
'Date: ${DateFormat('dd/MM/yyyy').format(DateTime.now())}',
style: boldClientStyle),
pw.SizedBox(height: 4), pw.SizedBox(height: 4),
pw.Container( pw.Container(width: 100, height: 2, color: PdfColors.black),
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),
@ -440,13 +456,10 @@ class _GestionCommandesPageState extends State<GestionCommandesPage> {
child: pw.Column( child: pw.Column(
children: [ children: [
pw.Text('Boutique:', style: frameTextStyle), pw.Text('Boutique:', style: frameTextStyle),
pw.Text('${pointDeVente?['nom'] ?? 'S405A'}', pw.Text('${pointDeVente?['nom'] ?? 'S405A'}', style: boldTextStyle),
style: boldTextStyle),
pw.SizedBox(height: 2), pw.SizedBox(height: 2),
pw.Text('Bon N°:', style: frameTextStyle), pw.Text('Bon N°:', style: frameTextStyle),
pw.Text( pw.Text('${pointDeVente?['nom'] ?? 'S405A'}-P${commande.id}', style: boldTextStyle),
'${pointDeVente?['nom'] ?? 'S405A'}-P${commande.id}',
style: boldTextStyle),
], ],
), ),
), ),
@ -457,8 +470,7 @@ class _GestionCommandesPageState extends State<GestionCommandesPage> {
pw.Container( pw.Container(
width: 120, width: 120,
decoration: pw.BoxDecoration( decoration: pw.BoxDecoration(
border: border: pw.Border.all(color: PdfColors.black, width: 1),
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(
@ -466,20 +478,11 @@ class _GestionCommandesPageState extends State<GestionCommandesPage> {
children: [ children: [
pw.Text('CLIENT', style: frameTextStyle), pw.Text('CLIENT', style: frameTextStyle),
pw.SizedBox(height: 2), pw.SizedBox(height: 2),
pw.Text( pw.Text('ID: ${pointDeVente?['nom'] ?? 'S405A'}-${client?.id ?? 'Non spécifié'}', style: smallTextStyle),
'ID: ${pointDeVente?['nom'] ?? 'S405A'}-${client?.id ?? 'Non spécifié'}', pw.Container(width: 100, height: 1, color: PdfColors.black, margin: const pw.EdgeInsets.symmetric(vertical: 2)),
style: smallTextStyle), pw.Text('${client?.nom} ${client?.prenom}', style: boldTextStyle),
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.SizedBox(height: 2),
pw.Text(client?.telephone ?? 'Non spécifié', pw.Text(client?.telephone ?? 'Non spécifié', style: tinyTextStyle),
style: tinyTextStyle),
], ],
), ),
), ),
@ -488,219 +491,245 @@ class _GestionCommandesPageState extends State<GestionCommandesPage> {
pw.SizedBox(height: 8), pw.SizedBox(height: 8),
// Tableau des produits (ajusté pour le mode paysage) // SOLUTION PRINCIPALE: Tableau avec hauteur dynamique
pw.Expanded( pw.Column(
child: pw.Table( children: [
// Debug: Afficher le nombre d'articles
pw.Text('Articles trouvés: ${detailsAvecProduits.length}',
style: pw.TextStyle(fontSize: 8, color: PdfColors.grey, font: regularFont)),
pw.SizedBox(height: 5),
// TABLE SANS CONTRAINTE DE HAUTEUR - Elle s'adapte au contenu
pw.Table(
border: pw.TableBorder.all(width: 1), border: pw.TableBorder.all(width: 1),
columnWidths: { columnWidths: {
0: const pw.FlexColumnWidth(5), 0: const pw.FlexColumnWidth(5), // Désignations
1: const pw.FlexColumnWidth(1.2), 1: const pw.FlexColumnWidth(1.2), // Quantité
2: const pw.FlexColumnWidth(1.5), 2: const pw.FlexColumnWidth(1.5), // Prix unitaire
3: const pw.FlexColumnWidth(1.5), 3: const pw.FlexColumnWidth(1.5), // Montant
4: const pw.FlexColumnWidth(1.5),
}, },
children: [ children: [
// En-tête du tableau
pw.TableRow( pw.TableRow(
decoration: const pw.BoxDecoration( decoration: const pw.BoxDecoration(color: PdfColors.grey200),
color: PdfColors.grey200),
children: [ children: [
pw.Padding( pw.Padding(
padding: const pw.EdgeInsets.all(3), padding: const pw.EdgeInsets.all(4),
child: pw.Text('Désignations', child: pw.Text('Désignations', style: boldTextStyle)
style: boldTextStyle)), ),
pw.Padding( pw.Padding(
padding: const pw.EdgeInsets.all(3), padding: const pw.EdgeInsets.all(4),
child: pw.Text('Qté', child: pw.Text('Qté', style: boldTextStyle, textAlign: pw.TextAlign.center)
style: boldTextStyle, ),
textAlign: pw.TextAlign.center)),
pw.Padding( pw.Padding(
padding: const pw.EdgeInsets.all(3), padding: const pw.EdgeInsets.all(4),
child: pw.Text('P.U.', child: pw.Text('P.U.', style: boldTextStyle, textAlign: pw.TextAlign.right)
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( pw.Padding(
padding: const pw.EdgeInsets.all(3), padding: const pw.EdgeInsets.all(4),
child: pw.Text('Montant', child: pw.Text('Montant', style: boldTextStyle, textAlign: pw.TextAlign.right)
style: boldTextStyle, ),
textAlign: pw.TextAlign.right)),
], ],
), ),
...detailsAvecProduits.map((item) {
// TOUTES LES LIGNES DE PRODUITS - SANS LIMITATION
...detailsAvecProduits.asMap().entries.map((entry) {
final index = entry.key;
final item = entry.value;
final detail = item['detail'] as DetailCommande; final detail = item['detail'] as DetailCommande;
final produit = item['produit']; final produit = item['produit'];
// Debug pour chaque ligne
print('📋 Ligne PDF $index: ${detail.produitNom} (Quantité: ${detail.quantite})');
return pw.TableRow( return pw.TableRow(
decoration: detail.estCadeau decoration: detail.estCadeau
? const pw.BoxDecoration( ? const pw.BoxDecoration(color: PdfColors.green50)
color: PdfColors.green50)
: detail.aRemise : detail.aRemise
? const pw.BoxDecoration( ? const pw.BoxDecoration(color: PdfColors.orange50)
color: PdfColors.orange50) : index % 2 == 0
? const pw.BoxDecoration(color: PdfColors.grey50)
: null, : null,
children: [ children: [
// Colonne Désignations - Plus compacte
pw.Padding( pw.Padding(
padding: const pw.EdgeInsets.all(3), padding: const pw.EdgeInsets.all(4),
child: pw.Column( child: pw.Column(
crossAxisAlignment: crossAxisAlignment: pw.CrossAxisAlignment.start,
pw.CrossAxisAlignment.start, mainAxisSize: pw.MainAxisSize.min,
children: [ children: [
// Nom du produit avec badge
pw.Row( pw.Row(
children: [ children: [
pw.Expanded( pw.Expanded(
child: pw.Text( child: pw.Text(
detail.produitNom ?? '${detail.produitNom ?? 'Produit inconnu'}',
'Produit inconnu',
style: pw.TextStyle( style: pw.TextStyle(
fontSize: 10, fontSize: 10,
fontWeight: fontWeight: pw.FontWeight.bold,
pw.FontWeight.bold)), font: regularFont
)
),
), ),
if (detail.estCadeau) if (detail.estCadeau)
pw.Container( pw.Container(
padding: padding: const pw.EdgeInsets.symmetric(horizontal: 3, vertical: 1),
const pw.EdgeInsets.symmetric(
horizontal: 2,
vertical: 1),
decoration: pw.BoxDecoration( decoration: pw.BoxDecoration(
color: PdfColors.green, color: PdfColors.green600,
borderRadius: borderRadius: pw.BorderRadius.circular(3),
pw.BorderRadius.circular(2),
), ),
child: pw.Text('🎁', child: pw.Text(
'CADEAU',
style: pw.TextStyle( style: pw.TextStyle(
fontSize: 5, fontSize: 6,
color: PdfColors.white)), color: PdfColors.white,
font: regularFont,
fontWeight: pw.FontWeight.bold
)
),
), ),
], ],
), ),
if (produit?.category != null &&
produit!.category.isNotEmpty) pw.SizedBox(height: 2),
// Informations complémentaires sur une seule ligne
pw.Text( pw.Text(
'${produit.category}${produit?.marque != null && produit!.marque.isNotEmpty ? ' - ${produit.marque}' : ''}', [
style: tinyTextStyle), if (produit?.category?.isNotEmpty == true) produit!.category,
if (produit?.imei != null && if (produit?.marque?.isNotEmpty == true) produit!.marque,
produit!.imei!.isNotEmpty) if (produit?.imei?.isNotEmpty == true) 'IMEI: ${produit!.imei}',
pw.Text('IMEI: ${produit.imei}', ].where((info) => info != null).join(' , '),
style: tinyTextStyle), style: pw.TextStyle(fontSize: 8, color: PdfColors.grey700, font: regularFont),
pw.Row( ),
children: [
if (produit?.ram != null && // Spécifications techniques
produit!.ram!.isNotEmpty) if (produit?.ram?.isNotEmpty == true || produit?.memoireInterne?.isNotEmpty == true || produit?.reference?.isNotEmpty == true)
pw.Text('${produit.ram}',
style: smallTextStyle),
if (produit?.memoireInterne != null &&
produit!
.memoireInterne!.isNotEmpty)
pw.Text( pw.Text(
' | ${produit.memoireInterne}', [
style: smallTextStyle), if (produit?.ram?.isNotEmpty == true) 'RAM: ${produit!.ram}',
pw.Text(' | ${produit.reference}', if (produit?.memoireInterne?.isNotEmpty == true) 'Stockage: ${produit!.memoireInterne}',
style: smallTextStyle), if (produit?.reference?.isNotEmpty == true) 'Ref: ${produit!.reference}',
], ].join(' , '),
style: pw.TextStyle(fontSize: 8, color: PdfColors.grey600, font: regularFont),
), ),
], ],
), ),
), ),
// Colonne Quantité
pw.Padding( pw.Padding(
padding: const pw.EdgeInsets.all(3), padding: const pw.EdgeInsets.all(4),
child: pw.Text('${detail.quantite}', child: pw.Text(
'${detail.quantite}',
style: normalTextStyle, style: normalTextStyle,
textAlign: pw.TextAlign.center), textAlign: pw.TextAlign.center
), ),
),
// Colonne Prix Unitaire
pw.Padding( pw.Padding(
padding: const pw.EdgeInsets.all(3), padding: const pw.EdgeInsets.all(4),
child: pw.Column( child: pw.Column(
crossAxisAlignment: crossAxisAlignment: pw.CrossAxisAlignment.end,
pw.CrossAxisAlignment.end, mainAxisSize: pw.MainAxisSize.min,
children: [ children: [
if (detail.estCadeau) ...[ if (detail.estCadeau) ...[
pw.Text( pw.Text(
'${detail.prixUnitaire.toStringAsFixed(0)}', '${detail.prixUnitaire.toStringAsFixed(0)}',
style: pw.TextStyle( style: pw.TextStyle(
fontSize: 8, fontSize: 8,
decoration: pw decoration: pw.TextDecoration.lineThrough,
.TextDecoration.lineThrough, color: PdfColors.grey600,
color: PdfColors.grey600)), 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: fontWeight: pw.FontWeight.bold,
pw.FontWeight.bold)), font: regularFont
)
),
] else if (detail.aRemise) ...[ ] else if (detail.aRemise) ...[
pw.Text( pw.Text(
'${detail.prixUnitaire.toStringAsFixed(0)}', '${detail.prixUnitaire.toStringAsFixed(0)}',
style: pw.TextStyle( style: pw.TextStyle(
fontSize: 8, fontSize: 8,
decoration: pw decoration: pw.TextDecoration.lineThrough,
.TextDecoration.lineThrough, color: PdfColors.grey600,
color: PdfColors.grey600)), font: regularFont
)
),
pw.Text( pw.Text(
'${(detail.prixFinal / detail.quantite).toStringAsFixed(0)}', '${(detail.prixFinal / detail.quantite).toStringAsFixed(0)}',
style: pw.TextStyle( style: pw.TextStyle(
fontSize: 9, fontSize: 10,
color: PdfColors.orange)), color: PdfColors.orange700,
fontWeight: pw.FontWeight.bold,
font: regularFont
)
),
] else ] else
pw.Text( pw.Text(
'${detail.prixUnitaire.toStringAsFixed(0)}', '${detail.prixUnitaire.toStringAsFixed(0)}',
style: smallTextStyle), style: smallTextStyle
),
], ],
), ),
), ),
// pw.Padding(
// padding: const pw.EdgeInsets.all(3), // Colonne Montant
// 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( pw.Padding(
padding: const pw.EdgeInsets.all(3), padding: const pw.EdgeInsets.all(4),
child: pw.Column( child: pw.Column(
crossAxisAlignment: crossAxisAlignment: pw.CrossAxisAlignment.end,
pw.CrossAxisAlignment.end, mainAxisSize: pw.MainAxisSize.min,
children: [ children: [
if (detail.estCadeau) ...[ if (detail.estCadeau) ...[
pw.Text( pw.Text(
'${detail.sousTotal.toStringAsFixed(0)}', '${detail.sousTotal.toStringAsFixed(0)}',
style: pw.TextStyle( style: pw.TextStyle(
fontSize: 8, fontSize: 8,
decoration: pw decoration: pw.TextDecoration.lineThrough,
.TextDecoration.lineThrough, color: PdfColors.grey600,
color: PdfColors.grey600)), 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
)
),
] else if (detail.aRemise) ...[ ] else if (detail.aRemise) ...[
pw.Text( pw.Text(
'${detail.sousTotal.toStringAsFixed(0)}', '${detail.sousTotal.toStringAsFixed(0)}',
style: pw.TextStyle( style: pw.TextStyle(
fontSize: 8, fontSize: 8,
decoration: pw decoration: pw.TextDecoration.lineThrough,
.TextDecoration.lineThrough, color: PdfColors.grey600,
color: PdfColors.grey600)), font: regularFont
)
),
pw.Text( pw.Text(
'${detail.prixFinal.toStringAsFixed(0)}', '${detail.prixFinal.toStringAsFixed(0)}',
style: pw.TextStyle( style: pw.TextStyle(
fontSize: 9, fontSize: 10,
fontWeight: fontWeight: pw.FontWeight.bold,
pw.FontWeight.bold)), font: regularFont
)
),
] else ] else
pw.Text( pw.Text(
'${detail.prixFinal.toStringAsFixed(0)}', '${detail.prixFinal.toStringAsFixed(0)}',
style: smallTextStyle), style: smallTextStyle
),
], ],
), ),
), ),
@ -709,11 +738,12 @@ class _GestionCommandesPageState extends State<GestionCommandesPage> {
}).toList(), }).toList(),
], ],
), ),
],
), ),
pw.SizedBox(height: 8), pw.SizedBox(height: 12),
// Section finale (ajustée pour le mode paysage) // Section finale - Totaux et signatures
pw.Row( pw.Row(
crossAxisAlignment: pw.CrossAxisAlignment.start, crossAxisAlignment: pw.CrossAxisAlignment.start,
children: [ children: [
@ -727,89 +757,55 @@ class _GestionCommandesPageState extends State<GestionCommandesPage> {
pw.Row( pw.Row(
mainAxisAlignment: pw.MainAxisAlignment.end, mainAxisAlignment: pw.MainAxisAlignment.end,
children: [ children: [
pw.Text('SOUS-TOTAL:', pw.Text('SOUS-TOTAL:', style: smallTextStyle),
style: smallTextStyle),
pw.SizedBox(width: 10), pw.SizedBox(width: 10),
pw.Text('${sousTotal.toStringAsFixed(0)}', pw.Text('${sousTotal.toStringAsFixed(0)}', style: smallTextStyle),
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:', pw.Text('REMISES:', style: pw.TextStyle(color: PdfColors.orange, fontSize: 10, font: regularFont)),
style: pw.TextStyle(
color: PdfColors.orange,
fontSize: 10)),
pw.SizedBox(width: 10), pw.SizedBox(width: 10),
pw.Text( pw.Text('-${totalRemises.toStringAsFixed(0)}', style: pw.TextStyle(color: PdfColors.orange, fontSize: 10, font: regularFont)),
'-${totalRemises.toStringAsFixed(0)}',
style: pw.TextStyle(
color: PdfColors.orange,
fontSize: 10)),
], ],
), ),
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):', pw.Text('CADEAUX ($nombreCadeaux):', style: pw.TextStyle(color: PdfColors.green700, fontSize: 10, font: regularFont)),
style: pw.TextStyle(
color: PdfColors.green700,
fontSize: 10)),
pw.SizedBox(width: 10), pw.SizedBox(width: 10),
pw.Text( pw.Text('-${totalCadeaux.toStringAsFixed(0)}', style: pw.TextStyle(color: PdfColors.green700, fontSize: 10, font: regularFont)),
'-${totalCadeaux.toStringAsFixed(0)}',
style: pw.TextStyle(
color: PdfColors.green700,
fontSize: 10)),
], ],
), ),
pw.SizedBox(height: 2), pw.SizedBox(height: 2),
], ],
pw.Container(
width: 120, pw.Container(width: 120, height: 1.5, color: PdfColors.black, margin: const pw.EdgeInsets.symmetric(vertical: 2)),
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( pw.Text('${commande.montantTotal.toStringAsFixed(0)} MGA', style: boldTextStyle),
'${commande.montantTotal.toStringAsFixed(0)} MGA',
style: boldTextStyle),
], ],
), ),
if (totalCadeaux > 0) ...[
pw.SizedBox(height: 3),
pw.Container(
padding: const pw.EdgeInsets.all(3),
decoration: pw.BoxDecoration(
color: PdfColors.green50,
borderRadius: pw.BorderRadius.circular(3),
),
child: pw.Text(
'🎁 $nombreCadeaux cadeau(s) offert(s) (${totalCadeaux.toStringAsFixed(0)} MGA)',
style: pw.TextStyle(
fontSize: 9, color: PdfColors.green700),
),
),
],
], ],
), ),
), ),
pw.SizedBox(width: 15), pw.SizedBox(width: 15),
// Informations vendeurs et signatures // Section vendeurs et signatures
pw.Expanded( pw.Expanded(
flex: 3, flex: 3,
child: pw.Column( child: pw.Column(
@ -823,48 +819,32 @@ class _GestionCommandesPageState extends State<GestionCommandesPage> {
borderRadius: pw.BorderRadius.circular(3), borderRadius: pw.BorderRadius.circular(3),
), ),
child: pw.Column( child: pw.Column(
crossAxisAlignment: crossAxisAlignment: pw.CrossAxisAlignment.start,
pw.CrossAxisAlignment.start,
children: [ children: [
pw.Text('VENDEURS', pw.Text('VENDEURS', style: pw.TextStyle(fontSize: 10, fontWeight: pw.FontWeight.bold, font: regularFont)),
style: pw.TextStyle(
fontSize: 10,
fontWeight: pw.FontWeight.bold)),
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: crossAxisAlignment: pw.CrossAxisAlignment.start,
pw.CrossAxisAlignment.start,
children: [ children: [
pw.Text('Initiateur:', pw.Text('Initiateur:', style: tinyTextStyle),
style: tinyTextStyle),
pw.Text( pw.Text(
commandeur != null commandeur != null ? '${commandeur.name} ${commandeur.lastName ?? ''}'.trim() : 'N/A',
? '${commandeur.name} ${commandeur.lastName ?? ''}' style: pw.TextStyle(fontSize: 9, font: regularFont),
.trim()
: 'N/A',
style:
pw.TextStyle(fontSize: 9),
), ),
], ],
), ),
), ),
pw.Expanded( pw.Expanded(
child: pw.Column( child: pw.Column(
crossAxisAlignment: crossAxisAlignment: pw.CrossAxisAlignment.start,
pw.CrossAxisAlignment.start,
children: [ children: [
pw.Text('Validateur:', pw.Text('Validateur:', style: tinyTextStyle),
style: tinyTextStyle),
pw.Text( pw.Text(
validateur != null validateur != null ? '${validateur.name} ${validateur.lastName ?? ''}'.trim() : 'N/A',
? '${validateur.name} ${validateur.lastName ?? ''}' style: pw.TextStyle(fontSize: 9, font: regularFont),
.trim()
: 'N/A',
style:
pw.TextStyle(fontSize: 9),
), ),
], ],
), ),
@ -879,33 +859,20 @@ class _GestionCommandesPageState extends State<GestionCommandesPage> {
// Signatures // Signatures
pw.Row( pw.Row(
mainAxisAlignment: mainAxisAlignment: pw.MainAxisAlignment.spaceBetween,
pw.MainAxisAlignment.spaceBetween,
children: [ children: [
pw.Column( pw.Column(
children: [ children: [
pw.Text('Vendeur', pw.Text('Vendeur', style: pw.TextStyle(fontSize: 9, fontWeight: pw.FontWeight.bold, font: regularFont)),
style: pw.TextStyle(
fontSize: 9,
fontWeight: pw.FontWeight.bold)),
pw.SizedBox(height: 15), pw.SizedBox(height: 15),
pw.Container( pw.Container(width: 70, height: 1, color: PdfColors.black),
width: 70,
height: 1,
color: PdfColors.black),
], ],
), ),
pw.Column( pw.Column(
children: [ children: [
pw.Text('Client', pw.Text('Client', style: pw.TextStyle(fontSize: 9, fontWeight: pw.FontWeight.bold, font: regularFont)),
style: pw.TextStyle(
fontSize: 9,
fontWeight: pw.FontWeight.bold)),
pw.SizedBox(height: 15), pw.SizedBox(height: 15),
pw.Container( pw.Container(width: 70, height: 1, color: PdfColors.black),
width: 70,
height: 1,
color: PdfColors.black),
], ],
), ),
], ],
@ -916,7 +883,7 @@ class _GestionCommandesPageState extends State<GestionCommandesPage> {
], ],
), ),
pw.SizedBox(height: 4), pw.SizedBox(height: 6),
// Note finale // Note finale
pw.Text( pw.Text(
@ -926,61 +893,70 @@ class _GestionCommandesPageState extends State<GestionCommandesPage> {
], ],
), ),
), ),
),
], ],
), ),
); );
} }
// PAGE EN MODE PAYSAGE : Les deux exemplaires sur une seule page // PAGE EN MODE PAYSAGE
pdf.addPage( pdf.addPage(
pw.Page( pw.Page(
pageFormat: PdfPageFormat.a4.landscape, // Mode paysage pageFormat: PdfPageFormat.a4.landscape,
margin: const pw.EdgeInsets.all(12), margin: const pw.EdgeInsets.all(12),
build: (pw.Context context) { build: (pw.Context context) {
return pw.Row( return pw.Row(
// Utilisation de Row au lieu de Column pour placer côte à côte
children: [ children: [
// Premier exemplaire (CLIENT) pw.Expanded(child: buildExemplaire("CLIENT")),
pw.Expanded(
child: buildExemplaire("CLIENT"),
),
pw.SizedBox(width: 15), pw.SizedBox(width: 15),
// AMÉLIORATION: Remplacer les caractères Unicode par du texte simple
// Trait de séparation vertical
pw.Container( pw.Container(
width: 2, width: 2,
height: double.infinity, height: double.infinity,
child: pw.Column( child: pw.Column(
mainAxisAlignment: pw.MainAxisAlignment.center, mainAxisAlignment: pw.MainAxisAlignment.center,
children: [ children: [
pw.Text('✂️', style: pw.TextStyle(fontSize: 14)), pw.Container(
pw.SizedBox(height: 10), width: 20,
pw.Transform.rotate( height: 20,
angle: 1.5708, // 90 degrés en radians (π/2) decoration: pw.BoxDecoration(
child: pw.Text('DÉCOUPER ICI', shape: pw.BoxShape.circle,
style: pw.TextStyle( border: pw.Border.all(color: PdfColors.black, width: 2),
fontSize: 10, fontWeight: pw.FontWeight.bold)), ),
child: pw.Center(
child: pw.Text('X', style: pw.TextStyle(fontSize: 12, fontWeight: pw.FontWeight.bold, font: regularFont)),
),
), ),
pw.SizedBox(height: 10), pw.SizedBox(height: 10),
pw.Text('✂️', style: pw.TextStyle(fontSize: 14)), pw.Transform.rotate(
angle: 1.5708,
child: pw.Text('DÉCOUPER ICI', style: pw.TextStyle(fontSize: 10, fontWeight: pw.FontWeight.bold, font: regularFont)),
),
pw.SizedBox(height: 10),
pw.Container(
width: 20,
height: 20,
decoration: pw.BoxDecoration(
shape: pw.BoxShape.circle,
border: pw.Border.all(color: PdfColors.black, width: 2),
),
child: pw.Center(
child: pw.Text('X', style: pw.TextStyle(fontSize: 12, fontWeight: pw.FontWeight.bold, font: regularFont)),
),
),
], ],
), ),
), ),
pw.SizedBox(width: 15), pw.SizedBox(width: 15),
pw.Expanded(child: buildExemplaire("MAGASIN")),
// Deuxième exemplaire (MAGASIN)
pw.Expanded(
child: buildExemplaire("MAGASIN"),
),
], ],
); );
}, },
), ),
); );
print('=== RÉSULTAT FINAL ===');
print('PDF généré avec ${detailsAvecProduits.length} produits');
// Sauvegarder le PDF // Sauvegarder le PDF
final output = await getTemporaryDirectory(); final output = await getTemporaryDirectory();
final file = File("${output.path}/bon_livraison_${commande.id}.pdf"); final file = File("${output.path}/bon_livraison_${commande.id}.pdf");
@ -2284,14 +2260,6 @@ class _GestionCommandesPageState extends State<GestionCommandesPage> {
textAlign: pw.TextAlign.center, textAlign: pw.TextAlign.center,
), ),
]), ]),
pw.Text(
'$nombreCadeaux article(s) offert(s)',
style: pw.TextStyle(
fontSize: 6,
color: PdfColors.green600,
),
textAlign: pw.TextAlign.center,
),
], ],
), ),
), ),

View File

@ -1,25 +1,29 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:get/get.dart'; import 'package:get/get.dart';
import 'package:intl/intl.dart'; import 'package:intl/intl.dart';
import 'package:youmazgestion/Components/app_bar.dart'; import 'package:youmazgestion/Components/app_bar.dart';
import 'package:youmazgestion/Components/appDrawer.dart'; import 'package:youmazgestion/Components/appDrawer.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';
import 'package:youmazgestion/controller/userController.dart';
class HistoriquePage extends StatefulWidget { class HistoriquePage extends StatefulWidget {
const HistoriquePage({super.key}); const HistoriquePage({super.key});
@override @override
_HistoriquePageState createState() => _HistoriquePageState(); _HistoriquePageState createState() => _HistoriquePageState();
} }
class _HistoriquePageState extends State<HistoriquePage> { class _HistoriquePageState extends State<HistoriquePage> {
final AppDatabase _appDatabase = AppDatabase.instance; final AppDatabase _appDatabase = AppDatabase.instance;
// Listes pour les commandes // Listes pour les commandes
final List<Commande> _commandes = []; final List<Commande> _commandes = [];
final List<Commande> _filteredCommandes = []; final List<Commande> _filteredCommandes = [];
List<Map<String, dynamic>> _pointsDeVente = [];
String? _selectedPointDeVente;
final UserController _userController = Get.find<UserController>();
bool _isLoading = true; bool _isLoading = true;
DateTimeRange? _dateRange; DateTimeRange? _dateRange;
@ -38,6 +42,7 @@ class _HistoriquePageState extends State<HistoriquePage> {
void initState() { void initState() {
super.initState(); super.initState();
_loadCommandes(); _loadCommandes();
_loadPointsDeVenteWithDefault();
// Listeners pour les filtres // Listeners pour les filtres
_searchController.addListener(_filterCommandes); _searchController.addListener(_filterCommandes);
@ -45,6 +50,37 @@ class _HistoriquePageState extends State<HistoriquePage> {
_searchCommandeIdController.addListener(_filterCommandes); _searchCommandeIdController.addListener(_filterCommandes);
} }
Future<void> _loadPointsDeVenteWithDefault() async {
try {
print(_userController.userId);
final points = await _appDatabase.getPointsDeVente();
setState(() {
_pointsDeVente = points;
if (points.isNotEmpty) {
if (_userController.pointDeVenteId > 0) {
final userPointDeVente = points.firstWhere(
(point) => point['id'] == _userController.pointDeVenteId,
orElse: () => <String, dynamic>{},
);
if (userPointDeVente.isNotEmpty) {
_selectedPointDeVente = userPointDeVente['nom'] as String;
} else {
_selectedPointDeVente = points[0]['nom'] as String;
}
} else {
_selectedPointDeVente = points[0]['nom'] as String;
}
}
});
} catch (e) {
Get.snackbar('Erreur', 'Impossible de charger les points de vente: $e');
print('❌ Erreur chargement points de vente: $e');
}
}
Future<void> _loadCommandes() async { Future<void> _loadCommandes() async {
setState(() { setState(() {
_isLoading = true; _isLoading = true;
@ -52,6 +88,7 @@ class _HistoriquePageState extends State<HistoriquePage> {
try { try {
final commandes = await _appDatabase.getCommandes(); final commandes = await _appDatabase.getCommandes();
setState(() { setState(() {
_commandes.clear(); _commandes.clear();
_commandes.addAll(commandes); _commandes.addAll(commandes);
@ -498,6 +535,7 @@ class _HistoriquePageState extends State<HistoriquePage> {
children: [ children: [
_buildDetailRow('Client', '${client?.nom} ${client?.prenom}', Icons.person), _buildDetailRow('Client', '${client?.nom} ${client?.prenom}', Icons.person),
_buildDetailRow('Date', DateFormat('dd/MM/yyyy à HH:mm').format(commande.dateCommande), Icons.calendar_today), _buildDetailRow('Date', DateFormat('dd/MM/yyyy à HH:mm').format(commande.dateCommande), Icons.calendar_today),
_buildDetailRow('Client', '${_selectedPointDeVente} ', Icons.person),
Row( Row(
children: [ children: [
Icon(Icons.assignment, size: 16, color: Colors.grey.shade600), Icon(Icons.assignment, size: 16, color: Colors.grey.shade600),
@ -729,6 +767,8 @@ class _HistoriquePageState extends State<HistoriquePage> {
// Widget pour l'item de commande (adapté pour mobile) // Widget pour l'item de commande (adapté pour mobile)
Widget _buildCommandeListItem(Commande commande) { Widget _buildCommandeListItem(Commande commande) {
final isMobile = MediaQuery.of(context).size.width < 600; final isMobile = MediaQuery.of(context).size.width < 600;
// print(commande.commandeurId);
print(_userController.userId);
return Card( return Card(
margin: const EdgeInsets.symmetric(horizontal: 8, vertical: 4), margin: const EdgeInsets.symmetric(horizontal: 8, vertical: 4),
@ -776,6 +816,13 @@ class _HistoriquePageState extends State<HistoriquePage> {
fontSize: 14, fontSize: 14,
), ),
), ),
Text(
'${_selectedPointDeVente}',
style: const TextStyle(
fontWeight: FontWeight.w500,
fontSize: 14,
),
),
Text( Text(
DateFormat('dd/MM/yyyy').format(commande.dateCommande), DateFormat('dd/MM/yyyy').format(commande.dateCommande),
style: TextStyle( style: TextStyle(

View File

@ -177,7 +177,7 @@ void _login() async {
// 6. Navigation immédiate // 6. Navigation immédiate
if (mounted) { if (mounted) {
if (userCredentials['role'] == 'commercial') { if (userCredentials['role'] == 'commercial' || userCredentials['role'] == 'caisse') {
Navigator.pushReplacement( Navigator.pushReplacement(
context, context,
MaterialPageRoute(builder: (context) => const MainLayout()), MaterialPageRoute(builder: (context) => const MainLayout()),

File diff suppressed because it is too large Load Diff

View File

@ -37,10 +37,10 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: async name: async
sha256: "758e6d74e971c3e5aceb4110bfd6698efc7f501675bcfe0c775459a8140750eb" sha256: d2872f9c19731c2e5f10444b14686eb7cc85c76274bd6c16e1816bff9a3bab63
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "2.13.0" version: "2.12.0"
barcode: barcode:
dependency: transitive dependency: transitive
description: description:
@ -181,10 +181,10 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: fake_async name: fake_async
sha256: "5368f224a74523e8d2e7399ea1638b37aecfca824a3cc4dfdf77bf1fa905ac44" sha256: "6a95e56b2449df2273fd8c45a662d6947ce1ebb7aafe80e550a3f68297f3cacc"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "1.3.3" version: "1.3.2"
ffi: ffi:
dependency: transitive dependency: transitive
description: description:
@ -516,10 +516,10 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: leak_tracker name: leak_tracker
sha256: "6bb818ecbdffe216e81182c2f0714a2e62b593f4a4f13098713ff1685dfb6ab0" sha256: c35baad643ba394b40aac41080300150a4f08fd0fd6a10378f8f7c6bc161acec
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "10.0.9" version: "10.0.8"
leak_tracker_flutter_testing: leak_tracker_flutter_testing:
dependency: transitive dependency: transitive
description: description:
@ -1193,10 +1193,10 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: vm_service name: vm_service
sha256: ddfa8d30d89985b96407efce8acbdd124701f96741f2d981ca860662f1c0dc02 sha256: "0968250880a6c5fe7edc067ed0a13d4bae1577fe2771dcf3010d52c4a9d3ca14"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "15.0.0" version: "14.3.1"
web: web:
dependency: transitive dependency: transitive
description: description: