From d043f6f20b84873e761e597eeb46f35876284c7e Mon Sep 17 00:00:00 2001 From: andrymodeste Date: Mon, 28 Jul 2025 11:07:16 +0200 Subject: [PATCH] commit de 18072025 --- lib/Models/Client.dart | 6 + lib/Services/stock_managementDatabase.dart | 34 +- lib/Views/Dashboard.dart | 342 +++++++++++++-------- lib/Views/HandleProduct.dart | 6 +- lib/Views/bilanMois.dart | 3 +- lib/Views/commandManagement.dart | 30 +- lib/Views/historique.dart | 22 +- lib/Views/listCommandeHistory.dart | 5 +- lib/Views/newCommand.dart | 196 ++++++------ lib/Views/produitsCard.dart | 3 +- lib/Views/ticketPage.dart | 22 +- lib/config/DatabaseConfig.dart | 2 +- 12 files changed, 407 insertions(+), 264 deletions(-) diff --git a/lib/Models/Client.dart b/lib/Models/Client.dart index 19b96ab..f05299b 100644 --- a/lib/Models/Client.dart +++ b/lib/Models/Client.dart @@ -92,6 +92,8 @@ class Commande { final String? clientNom; final String? clientPrenom; final String? clientEmail; + final int? pointDeVenteId; + final String? pointDeVenteDesign; Commande({ this.id, @@ -106,6 +108,8 @@ class Commande { this.clientNom, this.clientPrenom, this.clientEmail, + this.pointDeVenteId, + this.pointDeVenteDesign, }); String get clientNomComplet { @@ -156,6 +160,8 @@ class Commande { clientNom: map['clientNom'] as String?, clientPrenom: map['clientPrenom'] as String?, clientEmail: map['clientEmail'] as String?, + pointDeVenteId: map['pointDeVenteId'] as int?, + pointDeVenteDesign: map['pointDeVenteDesign'] as String?, ); } } diff --git a/lib/Services/stock_managementDatabase.dart b/lib/Services/stock_managementDatabase.dart index 628a1e3..88dd8d2 100644 --- a/lib/Services/stock_managementDatabase.dart +++ b/lib/Services/stock_managementDatabase.dart @@ -597,6 +597,31 @@ class AppDatabase { } } +Future getValeurTotaleStock() async { + final db = await database; + + try { + // Récupérer tous les produits + final List> products = (await db.query('products')) as List>; + + double valeurTotale = 0.0; + for (final product in products) { + final stock = (product['stock'] as num?)?.toDouble(); + final price = (product['price'] as num?)?.toDouble(); + + // Vérifier que stock et price ne sont pas null + if (stock != null && price != null) { + valeurTotale += (stock * price); + } + } + + return valeurTotale; + } catch (e) { + print('Erreur lors du calcul de la valeur totale du stock: $e'); + return 0.0; + } +} + // --- STATISTIQUES --- Future> getStatistiques() async { @@ -807,9 +832,16 @@ class AppDatabase { Future> getCommandes() async { final db = await database; final result = await db.query(''' - SELECT c.*, cl.nom as clientNom, cl.prenom as clientPrenom, cl.email as clientEmail + SELECT c.*, + cl.nom AS clientNom, + cl.prenom AS clientPrenom, + cl.email AS clientEmail, + u.point_de_vente_id AS pointDeVenteId, + pv.nom AS pointDeVenteDesign FROM commandes c LEFT JOIN clients cl ON c.clientId = cl.id + LEFT JOIN users u ON c.commandeurId = u.id + LEFT JOIN points_de_vente pv ON u.point_de_vente_id = pv.id ORDER BY c.dateCommande DESC '''); return result.map((row) => Commande.fromMap(row.fields)).toList(); diff --git a/lib/Views/Dashboard.dart b/lib/Views/Dashboard.dart index 2d20280..21e6d1a 100644 --- a/lib/Views/Dashboard.dart +++ b/lib/Views/Dashboard.dart @@ -7,6 +7,7 @@ import 'package:youmazgestion/controller/userController.dart'; import 'package:youmazgestion/Models/users.dart'; import 'package:youmazgestion/Models/client.dart'; import 'package:youmazgestion/Models/produit.dart'; // Ajout de l'import manquant +import 'package:intl/intl.dart'; class DashboardPage extends StatefulWidget { @override @@ -14,6 +15,8 @@ class DashboardPage extends StatefulWidget { } class _DashboardPageState extends State with SingleTickerProviderStateMixin { + DateTimeRange? _dateRange; + bool _showOnlyToday = false; final AppDatabase _database = AppDatabase.instance; final UserController _userController = Get.find(); final GlobalKey _recentClientsKey = GlobalKey(); @@ -60,8 +63,8 @@ void initState() { super.dispose(); } - void _loadData() { - _statsFuture = _database.getStatistiques(); +void _loadData() { + _statsFuture = _database.getStatistiques(); // Plus besoin de calcul supplémentaire _recentOrdersFuture = _database.getCommandes().then((orders) => orders.take(5).toList()); _lowStockProductsFuture = _database.getProducts().then((products) { return products.where((p) => (p.stock ?? 0) < 10).toList(); @@ -69,10 +72,10 @@ void initState() { _recentClientsFuture = _database.getClients().then((clients) => clients.take(5).toList()); _allOrdersFuture = _database.getCommandes(); _productsByCategoryFuture = _database.getProductCountByCategory(); - } +} Future _showCategoryProductsDialog(String category) async { final products = await _database.getProductsByCategory(category); - + showDialog( context: context, builder: (context) => AlertDialog( @@ -90,7 +93,7 @@ Future _showCategoryProductsDialog(String category) async { : CircleAvatar(child: Icon(Icons.inventory)), title: Text(product.name), subtitle: Text('Stock: ${product.stock}'), - trailing: Text('${product.price.toStringAsFixed(2)} MGA'), + trailing: Text('${NumberFormat('#,##0', 'fr_FR').format(product.price)} MGA'), ); }, ), @@ -417,7 +420,7 @@ Future _showCategoryProductsDialog(String category) async { final month = salesData[groupIndex]['month']; final total = salesData[groupIndex]['total']; return BarTooltipItem( - '$month\n${total.toStringAsFixed(2)} MGA', + '$month\n${NumberFormat('#,##0', 'fr_FR').format(total)} MGA', TextStyle(color: Colors.white), ); }, @@ -716,54 +719,60 @@ Future _showCategoryProductsDialog(String category) async { ); } - Widget _buildMiniStatistics() { - return FutureBuilder>( - future: _statsFuture, - builder: (context, snapshot) { - if (snapshot.connectionState == ConnectionState.waiting) { - return Center(child: CircularProgressIndicator()); - } - - if (snapshot.hasError) { - return Text('Erreur de chargement des statistiques'); - } - - final stats = snapshot.data ?? {}; - - return Wrap( - spacing: 12, - runSpacing: 12, - children: [ - _buildMiniStatCard( - title: 'Clients', - value: '${stats['totalClients'] ?? 0}', - icon: Icons.people, - color: Colors.blue, - ), - _buildMiniStatCard( - title: 'Commandes', - value: '${stats['totalCommandes'] ?? 0}', - icon: Icons.shopping_cart, - color: Colors.green, - ), - _buildMiniStatCard( - title: 'Produits', - value: '${stats['totalProduits'] ?? 0}', - icon: Icons.inventory, - color: Colors.orange, - ), - _buildMiniStatCard( - title: 'CA (MGA)', - value: '${(stats['chiffreAffaires'] ?? 0.0).toStringAsFixed(2)}', - icon: Icons.euro_symbol, - color: Colors.purple, - ), - ], - ); - }, - ); - } +Widget _buildMiniStatistics() { + return FutureBuilder>( + future: _statsFuture, + builder: (context, snapshot) { + if (snapshot.connectionState == ConnectionState.waiting) { + return Center(child: CircularProgressIndicator()); + } + + if (snapshot.hasError) { + return Text('Erreur de chargement des statistiques'); + } + + final stats = snapshot.data ?? {}; + return Wrap( + spacing: 12, + runSpacing: 12, + children: [ + _buildMiniStatCard( + title: 'Clients', + value: '${stats['totalClients'] ?? 0}', + icon: Icons.people, + color: Colors.blue, + ), + _buildMiniStatCard( + title: 'Commandes', + value: '${stats['totalCommandes'] ?? 0}', + icon: Icons.shopping_cart, + color: Colors.green, + ), + _buildMiniStatCard( + title: 'Produits', + value: '${stats['totalProduits'] ?? 0}', + icon: Icons.inventory, + color: Colors.orange, + ), + _buildMiniStatCard( + title: 'CA (MGA)', + value: NumberFormat('#,##0', 'fr_FR').format(stats['chiffreAffaires'] ?? 0.0), + icon: Icons.euro_symbol, + color: Colors.purple, + ), + // ✅ NOUVELLE CARTE : Valeur totale du stock + _buildMiniStatCard( + title: 'Valeur Stock (MGA)', + value: NumberFormat('#,##0', 'fr_FR').format(stats['valeurTotaleStock'] ?? 0.0), + icon: Icons.inventory_2, + color: Colors.teal, + ), + ], + ); + }, + ); +} Widget _buildMiniStatCard({required String title, required String value, required IconData icon, required Color color}) { @@ -952,7 +961,7 @@ Future _showCategoryProductsDialog(String category) async { crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( - '${order.montantTotal.toStringAsFixed(2)} MGA', + '${NumberFormat('#,##0', 'fr_FR').format(order.montantTotal)} MGA', style: TextStyle(fontSize: 12), ), Text( @@ -1160,7 +1169,7 @@ Widget _buildVentesParPointDeVenteCard() { final ca = pointVente['chiffre_affaires'] ?? 0.0; final nbCommandes = pointVente['nombre_commandes'] ?? 0; return BarTooltipItem( - '${pointVente['point_vente_nom']}\n${ca.toStringAsFixed(2)} MGA\n$nbCommandes commandes', + '${pointVente['point_vente_nom']}\n${NumberFormat('#,##0', 'fr_FR').format(ca)} MGA\n$nbCommandes commandes', TextStyle(color: Colors.white, fontSize: 12), ); }, @@ -1333,7 +1342,9 @@ Widget _buildTableauVentesPointDeVente(List> ventesData) { Expanded( flex: 2, child: Text( - '${((data['chiffre_affaires'] as num?)?.toDouble() ?? 0.0).toStringAsFixed(2)}', + NumberFormat('#,##0.00', 'fr_FR').format( + (data['chiffre_affaires'] as num?)?.toDouble() ?? 0.0, + ), style: TextStyle(fontSize: 12, fontWeight: FontWeight.w500), ), ), @@ -1354,7 +1365,9 @@ Widget _buildTableauVentesPointDeVente(List> ventesData) { Expanded( flex: 2, child: Text( - '${((data['panier_moyen'] as num?)?.toDouble() ?? 0.0).toStringAsFixed(2)}', + NumberFormat('#,##0.00', 'fr_FR').format( + (data['panier_moyen'] as num?)?.toDouble() ?? 0.0, + ), style: TextStyle(fontSize: 12), ), ), @@ -1403,80 +1416,159 @@ String _formatCurrency(double value) { } } + Future _selectDateRange(BuildContext context) async { + final DateTimeRange? picked = await showDateRangePicker( + context: context, + firstDate: DateTime(2020), + lastDate: DateTime.now().add(const Duration(days: 365)), + initialDateRange: _dateRange ?? + DateTimeRange( + start: DateTime.now().subtract(const Duration(days: 30)), + end: DateTime.now(), + ), + ); + + if (picked != null) { + setState(() { + _dateRange = picked; + }); + } + } + + void _toggleTodayFilter() { + setState(() { + _showOnlyToday = !_showOnlyToday; + }); + } + void _showPointVenteDetails(Map pointVenteData) async { + final isMobile = MediaQuery.of(context).size.width < 600; final pointVenteId = pointVenteData['point_vente_id'] as int; final pointVenteNom = pointVenteData['point_vente_nom'] as String; - - showDialog( - context: context, - builder: (context) => AlertDialog( - title: Text('Détails - $pointVenteNom'), - content: Container( - width: double.maxFinite, - height: 400, - child: SingleChildScrollView( - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - // Statistiques générales - _buildStatRow('Chiffre d\'affaires:', '${((pointVenteData['chiffre_affaires'] as num?)?.toDouble() ?? 0.0).toStringAsFixed(2)} MGA'), - _buildStatRow('Nombre de commandes:', '${pointVenteData['nombre_commandes'] ?? 0}'), - _buildStatRow('Articles vendus:', '${pointVenteData['nombre_articles_vendus'] ?? 0}'), - _buildStatRow('Quantité totale:', '${pointVenteData['quantite_totale_vendue'] ?? 0}'), - _buildStatRow('Panier moyen:', '${((pointVenteData['panier_moyen'] as num?)?.toDouble() ?? 0.0).toStringAsFixed(2)} MGA'), - - SizedBox(height: 16), - Text('Top 5 des produits:', style: TextStyle(fontWeight: FontWeight.bold)), - SizedBox(height: 8), - - // Top produits - FutureBuilder>>( - future: _database.getTopProduitsParPointDeVente(pointVenteId), - builder: (context, snapshot) { - if (snapshot.connectionState == ConnectionState.waiting) { - return Center(child: CircularProgressIndicator()); - } - - if (snapshot.hasError || !snapshot.hasData || snapshot.data!.isEmpty) { - return Text('Aucun produit vendu', style: TextStyle(color: Colors.grey)); - } - - final produits = snapshot.data!; - return Column( - children: produits.map((produit) => Padding( - padding: EdgeInsets.symmetric(vertical: 4), - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Expanded( - child: Text( - produit['produit_nom'] ?? 'N/A', - style: TextStyle(fontSize: 12), - overflow: TextOverflow.ellipsis, - ), - ), - Text( - '${produit['quantite_vendue'] ?? 0} vendus', - style: TextStyle(fontSize: 12, color: Colors.grey), + showDialog( + context: context, + builder: (context) => AlertDialog( + title: Text('Détails - $pointVenteNom'), + content: Container( + width: double.maxFinite, + height: 500, + child: SingleChildScrollView( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Wrap( + spacing: 8, + runSpacing: 8, + children: [ + ElevatedButton.icon( + onPressed: _toggleTodayFilter, + icon: Icon( + _showOnlyToday ? Icons.today : Icons.calendar_today, + size: 20, + ), + label: Text(_showOnlyToday + ? isMobile + ? 'Toutes dates' + : 'Toutes les dates' + : isMobile + ? 'Aujourd\'hui' + : 'Aujourd\'hui seulement'), + style: ElevatedButton.styleFrom( + backgroundColor: _showOnlyToday + ? Colors.green.shade600 + : Colors.blue.shade600, + foregroundColor: Colors.white, + padding: EdgeInsets.symmetric( + horizontal: isMobile ? 12 : 16, + vertical: 8, + ), + ), + ), + ElevatedButton.icon( + onPressed: () => _selectDateRange(context), + icon: const Icon(Icons.date_range, size: 20), + label: Text(_dateRange != null + ? isMobile + ? 'Période' + : 'Période sélectionnée' + : isMobile + ? 'Période' + : 'Choisir période'), + style: ElevatedButton.styleFrom( + backgroundColor: _dateRange != null + ? Colors.orange.shade600 + : Colors.grey.shade600, + foregroundColor: Colors.white, + padding: EdgeInsets.symmetric( + horizontal: isMobile ? 12 : 16, + vertical: 8, + ), + ), + ), + ], + ), + // Statistiques générales + _buildStatRow('Chiffre d\'affaires:', '${NumberFormat('#,##0.00', 'fr_FR').format((pointVenteData['chiffre_affaires'] as num?)?.toDouble() ?? 0.0)} MGA'), + _buildStatRow('Nombre de commandes:', '${pointVenteData['nombre_commandes'] ?? 0}'), + _buildStatRow('Articles vendus:', '${pointVenteData['nombre_articles_vendus'] ?? 0}'), + _buildStatRow('Quantité totale:', '${pointVenteData['quantite_totale_vendue'] ?? 0}'), + _buildStatRow('Panier moyen:', '${NumberFormat('#,##0.00', 'fr_FR').format((pointVenteData['panier_moyen'] as num?)?.toDouble() ?? 0.0)} MGA'), + + SizedBox(height: 16), + Text('Top 5 des produits:', style: TextStyle(fontWeight: FontWeight.bold)), + SizedBox(height: 8), + + + SizedBox(height: 16), + + // ✅ Top produits + FutureBuilder>>( + future: _database.getTopProduitsParPointDeVente(pointVenteId), + builder: (context, snapshot) { + if (snapshot.connectionState == ConnectionState.waiting) { + return Center(child: CircularProgressIndicator()); + } + + if (snapshot.hasError || !snapshot.hasData || snapshot.data!.isEmpty) { + return Text('Aucun produit vendu', style: TextStyle(color: Colors.grey)); + } + + final produits = snapshot.data!; + return Column( + children: produits.map((produit) => Padding( + padding: EdgeInsets.symmetric(vertical: 4), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Expanded( + child: Text( + produit['produit_nom'] ?? 'N/A', + style: TextStyle(fontSize: 12), + overflow: TextOverflow.ellipsis, ), - ], - ), - )).toList(), - ); - }, - ), - ], - ), + ), + Text( + '${produit['quantite_vendue'] ?? 0} vendus', + style: TextStyle(fontSize: 12, color: Colors.grey), + ), + ], + ), + )).toList(), + ); + }, + ), + ], ), ), - actions: [ - TextButton( - onPressed: () => Navigator.pop(context), - child: Text('Fermer'), - ), - ], ), - ); + actions: [ + TextButton( + onPressed: () => Navigator.pop(context), + child: Text('Fermer'), + ), + ], + ), +); } Widget _buildStatRow(String label, String value) { @@ -1582,7 +1674,7 @@ Widget _buildStatRow(String label, String value) { ], ), trailing: Text( - '${product.price.toStringAsFixed(2)} MGA', + '${NumberFormat('#,##0', 'fr_FR').format(product.price)} MGA', style: TextStyle(fontSize: 12), ), ), diff --git a/lib/Views/HandleProduct.dart b/lib/Views/HandleProduct.dart index dd15d6e..e11351b 100644 --- a/lib/Views/HandleProduct.dart +++ b/lib/Views/HandleProduct.dart @@ -307,7 +307,7 @@ Future _showDemandeTransfertDialog(Product product) async { Expanded( child: _buildInfoCard( 'Prix unitaire', - '${product.price.toStringAsFixed(2)} MGA', + '${NumberFormat('#,##0.00', 'fr_FR').format(product.price)} MGA', Icons.attach_money, Colors.green, ), @@ -1823,7 +1823,7 @@ Future _showDemandeTransfertDialog(Product product) async { ), const SizedBox(height: 8), Text('IMEI: ${product.imei}'), - Text('Prix: ${product.price.toStringAsFixed(2)} MGA'), + Text('Prix: ${NumberFormat('#,##0.00', 'fr_FR').format(product.price)} MGA'), const SizedBox(height: 12), Container( padding: const EdgeInsets.all(8), @@ -4476,7 +4476,7 @@ Future _showDemandeTransfertDialog(Product product) async { icon: Icons.info_outline, color: Colors.blue, children: [ - _buildModernInfoRow('Prix', '${product.price} MGA', + _buildModernInfoRow('Prix', '${NumberFormat('#,##0.00', 'fr_FR').format(product.price)} MGA', Icons.payments_outlined), _buildModernInfoRow('Catégorie', product.category, Icons.category_outlined), diff --git a/lib/Views/bilanMois.dart b/lib/Views/bilanMois.dart index ee59886..2137627 100644 --- a/lib/Views/bilanMois.dart +++ b/lib/Views/bilanMois.dart @@ -2,6 +2,7 @@ import 'package:flutter/material.dart'; import 'package:get/get.dart'; import 'package:youmazgestion/Components/app_bar.dart'; import 'package:youmazgestion/controller/HistoryController.dart'; +import 'package:intl/intl.dart'; import 'bilanDesJourne.dart'; @@ -38,7 +39,7 @@ class _BilanMoisState extends State { children: [ _buildInfoCard( title: 'Chiffre réalisé', - value: '${controller.totalSum.value.toStringAsFixed(2)} MGA', + value: '${NumberFormat('#,##0', 'fr_FR').format(controller.totalSum.value)} MGA', color: Colors.green, icon: Icons.monetization_on, ), diff --git a/lib/Views/commandManagement.dart b/lib/Views/commandManagement.dart index dd884df..c79f75d 100644 --- a/lib/Views/commandManagement.dart +++ b/lib/Views/commandManagement.dart @@ -188,7 +188,7 @@ class _GestionCommandesPageState extends State { content: Column( mainAxisSize: MainAxisSize.min, children: [ - Text('Montant total: ${montantFinal.toStringAsFixed(2)} MGA'), + Text('Montant total: ${NumberFormat('#,##0', 'fr_FR').format(montantFinal)} MGA'), const SizedBox(height: 10), TextField( controller: amountController, @@ -207,7 +207,7 @@ class _GestionCommandesPageState extends State { const SizedBox(height: 20), if (amountGiven >= montantFinal) Text( - 'Monnaie à rendre: ${change.toStringAsFixed(2)} MGA', + 'Monnaie à rendre: ${NumberFormat('#,##0', 'fr_FR').format(change)} MGA', style: const TextStyle( fontSize: 18, fontWeight: FontWeight.bold, @@ -674,7 +674,7 @@ class _GestionCommandesPageState extends State { ), ] else pw.Text( - '${detail.prixUnitaire.toStringAsFixed(0)}', + NumberFormat('#,##0', 'fr_FR').format(detail.prixUnitaire), style: smallTextStyle ), ], @@ -690,7 +690,7 @@ class _GestionCommandesPageState extends State { children: [ if (detail.estCadeau) ...[ pw.Text( - '${detail.sousTotal.toStringAsFixed(0)}', + NumberFormat('#,##0', 'fr_FR').format(detail.sousTotal), style: pw.TextStyle( fontSize: 8, decoration: pw.TextDecoration.lineThrough, @@ -796,7 +796,7 @@ class _GestionCommandesPageState extends State { children: [ pw.Text('TOTAL:', style: boldTextStyle), pw.SizedBox(width: 10), - pw.Text('${commande.montantTotal.toStringAsFixed(0)} MGA', style: boldTextStyle), + pw.Text('${NumberFormat('#,##0', 'fr_FR').format(commande.montantTotal)} MGA', style: boldTextStyle), ], ), ], @@ -1552,7 +1552,7 @@ class _GestionCommandesPageState extends State { if (totalRemises > 0 || totalCadeaux > 0) ...[ pw.SizedBox(height: 4), pw.Text( - 'Économies réalisées: ${(totalRemises + totalCadeaux).toStringAsFixed(0)} MGA', + 'Économies réalisées: ${NumberFormat('#,##0', 'fr_FR').format(totalRemises + totalCadeaux)} MGA', style: pw.TextStyle( fontSize: 8, color: PdfColors.green, @@ -1694,7 +1694,7 @@ class _GestionCommandesPageState extends State { pw.Text('🎁 ', style: emojifont), pw.Expanded( child: pw.Text( - 'Merci de votre confiance ! Nous espérons que nos cadeaux vous feront plaisir. ($nombreCadeaux article(s) offert(s) - Valeur: ${totalCadeaux.toStringAsFixed(0)} MGA)', + 'Merci de votre confiance ! Nous espérons que nos cadeaux vous feront plaisir. ($nombreCadeaux article(s) offert(s) - Valeur: ${NumberFormat('#,##0', 'fr_FR').format(totalCadeaux)} MGA)', style: pw.TextStyle( fontSize: 9, fontStyle: pw.FontStyle.italic, @@ -1893,7 +1893,7 @@ class _GestionCommandesPageState extends State { String _getPaymentMethodLabel(PaymentMethod payment) { switch (payment.type) { case PaymentType.cash: - return 'LIQUIDE (${payment.amountGiven.toStringAsFixed(0)} MGA)'; + return 'LIQUIDE (${NumberFormat('#,##0', 'fr_FR').format(payment.amountGiven)} MGA)'; case PaymentType.card: return 'CARTE BANCAIRE'; case PaymentType.mvola: @@ -2149,7 +2149,7 @@ class _GestionCommandesPageState extends State { children: [ pw.Text('SOUS-TOTAL:', style: const pw.TextStyle(fontSize: 8)), - pw.Text('${sousTotal.toStringAsFixed(0)} MGA', + pw.Text('${NumberFormat('#,##0', 'fr_FR').format(sousTotal)} MGA', style: const pw.TextStyle(fontSize: 8)), ], ), @@ -2160,7 +2160,7 @@ class _GestionCommandesPageState extends State { pw.Text('REMISES:', style: pw.TextStyle( fontSize: 8, color: PdfColors.orange)), - pw.Text('-${totalRemises.toStringAsFixed(0)} MGA', + pw.Text('-${NumberFormat('#,##0', 'fr_FR').format(totalRemises)} MGA', style: pw.TextStyle( fontSize: 8, color: PdfColors.orange)), ], @@ -2173,7 +2173,7 @@ class _GestionCommandesPageState extends State { pw.Text('CADEAUX ($nombreCadeaux):', style: pw.TextStyle( fontSize: 8, color: PdfColors.green700)), - pw.Text('-${totalCadeaux.toStringAsFixed(0)} MGA', + pw.Text('-${NumberFormat('#,##0', 'fr_FR').format(totalCadeaux)} MGA', style: pw.TextStyle( fontSize: 8, color: PdfColors.green700)), ], @@ -2189,7 +2189,7 @@ class _GestionCommandesPageState extends State { pw.Text('TOTAL:', style: pw.TextStyle( fontSize: 9, fontWeight: pw.FontWeight.bold)), - pw.Text('${commande.montantTotal.toStringAsFixed(0)} MGA', + pw.Text('${NumberFormat('#,##0', 'fr_FR').format(commande.montantTotal)} MGA', style: pw.TextStyle( fontSize: 9, fontWeight: pw.FontWeight.bold)), ], @@ -2198,7 +2198,7 @@ class _GestionCommandesPageState extends State { if (totalRemises > 0 || totalCadeaux > 0) ...[ pw.SizedBox(height: 4), pw.Text( - 'Économies: ${(totalRemises + totalCadeaux).toStringAsFixed(0)} MGA !', + 'Économies: ${NumberFormat('#,##0', 'fr_FR').format(totalRemises + totalCadeaux)} MGA !', style: pw.TextStyle( fontSize: 7, color: PdfColors.green, @@ -2222,7 +2222,7 @@ class _GestionCommandesPageState extends State { if (payment.type == PaymentType.cash && payment.amountGiven > commande.montantTotal) pw.Text( - 'Monnaie rendue: ${(payment.amountGiven - commande.montantTotal).toStringAsFixed(0)} MGA', + 'Monnaie rendue: ${NumberFormat('#,##0', 'fr_FR').format(payment.amountGiven - commande.montantTotal)} MGA', style: const pw.TextStyle(fontSize: 8)), pw.SizedBox(height: 8), @@ -2827,7 +2827,7 @@ class _GestionCommandesPageState extends State { ), const SizedBox(width: 4), Text( - '${commande.montantTotal.toStringAsFixed(2)} MGA', + '${NumberFormat('#,##0', 'fr_FR').format(commande.montantTotal)} MGA', style: TextStyle( fontSize: 14, fontWeight: FontWeight.bold, diff --git a/lib/Views/historique.dart b/lib/Views/historique.dart index 5351930..66352ab 100644 --- a/lib/Views/historique.dart +++ b/lib/Views/historique.dart @@ -54,7 +54,7 @@ class _HistoriquePageState extends State { Future _loadPointsDeVenteWithDefault() async { try { - print(_userController.userId); + print(_userController.pointDeVenteId); final points = await _appDatabase.getPointsDeVente(); setState(() { _pointsDeVente = points; @@ -88,7 +88,11 @@ class _HistoriquePageState extends State { }); try { - final commandes = await _appDatabase.getCommandes(); + final allCommandes = await _appDatabase.getCommandes(); + final pointDeVenteId = _userController.pointDeVenteId; + final commandes = pointDeVenteId == 0 + ? allCommandes + : allCommandes.where((cmd) => cmd.pointDeVenteId == pointDeVenteId).toList(); setState(() { _commandes.clear(); @@ -136,11 +140,13 @@ class _HistoriquePageState extends State { final searchText = _searchController.text.toLowerCase(); final clientQuery = _searchClientController.text.toLowerCase(); final commandeIdQuery = _searchCommandeIdController.text.toLowerCase(); + final pointDeVenteId = _userController.pointDeVenteId; setState(() { _filteredCommandes.clear(); for (var commande in _commandes) { + if (pointDeVenteId != 0 && commande.pointDeVenteId != pointDeVenteId) continue; bool matchesSearch = searchText.isEmpty || commande.clientNom!.toLowerCase().contains(searchText) || commande.clientPrenom!.toLowerCase().contains(searchText) || @@ -590,7 +596,7 @@ class _HistoriquePageState extends State { ), ), Text( - '${commande.montantTotal.toStringAsFixed(2)} MGA', + '${NumberFormat('#,##0.00', 'fr_FR').format(commande.montantTotal)} MGA', style: const TextStyle( fontSize: 16, fontWeight: FontWeight.bold, @@ -641,10 +647,10 @@ class _HistoriquePageState extends State { const TextStyle(fontWeight: FontWeight.w500), ), subtitle: Text( - '${detail.quantite} x ${detail.prixUnitaire.toStringAsFixed(2)} MGA', + '${detail.quantite} x ${NumberFormat('#,##0.00', 'fr_FR').format(detail.prixUnitaire)} MGA', ), trailing: Text( - '${detail.sousTotal.toStringAsFixed(2)} MGA', + '${NumberFormat('#,##0.00', 'fr_FR').format(detail.sousTotal)} MGA', style: TextStyle( fontWeight: FontWeight.bold, color: Colors.blue.shade800, @@ -789,7 +795,7 @@ class _HistoriquePageState extends State { Widget _buildCommandeListItem(Commande commande) { final isMobile = MediaQuery.of(context).size.width < 600; // print(commande.commandeurId); - print(_userController.userId); + print(_userController.pointDeVenteId); return Card( margin: const EdgeInsets.symmetric(horizontal: 8, vertical: 4), @@ -838,7 +844,7 @@ class _HistoriquePageState extends State { ), ), Text( - 'PV: ${_selectedPointDeVente}', + 'PV: ${commande.pointDeVenteDesign}', style: const TextStyle( fontWeight: FontWeight.w500, fontSize: 14, @@ -858,7 +864,7 @@ class _HistoriquePageState extends State { crossAxisAlignment: CrossAxisAlignment.end, children: [ Text( - '${commande.montantTotal.toStringAsFixed(2)} MGA', + '${NumberFormat('#,##0.00', 'fr_FR').format(commande.montantTotal)} MGA', style: TextStyle( fontWeight: FontWeight.bold, color: Colors.green.shade700, diff --git a/lib/Views/listCommandeHistory.dart b/lib/Views/listCommandeHistory.dart index 7cb6d4f..7d0dff4 100644 --- a/lib/Views/listCommandeHistory.dart +++ b/lib/Views/listCommandeHistory.dart @@ -5,6 +5,7 @@ import 'package:youmazgestion/Views/voirPlus.dart'; import 'package:youmazgestion/controller/HistoryController.dart'; import '../Models/Order.dart'; import 'package:youmazgestion/Views/detailHistory.dart'; +import 'package:intl/intl.dart'; class HistoryDetailPage extends StatelessWidget { final DateTime selectedDate; @@ -112,7 +113,7 @@ class HistoryDetailPage extends StatelessWidget { ), ), Text( - 'Total Somme: $totalSum MGA', + 'Total Somme: ${NumberFormat('#,##0', 'fr_FR').format(totalSum)} MGA', style: const TextStyle( fontSize: 18.0, fontWeight: FontWeight.bold, @@ -197,7 +198,7 @@ class HistoryDetailPage extends StatelessWidget { ), ], ), - subtitle: Text('Total: ${order.totalPrice} MGA'), + subtitle: Text('Total: ${NumberFormat('#,##0', 'fr_FR').format(order.totalPrice)} MGA'), trailing: Text('Date: ${order.dateTime}'), leading: Text('vendeur: ${order.user}'), onTap: () { diff --git a/lib/Views/newCommand.dart b/lib/Views/newCommand.dart index b9ef0d8..5134b4a 100644 --- a/lib/Views/newCommand.dart +++ b/lib/Views/newCommand.dart @@ -10,6 +10,7 @@ import 'package:youmazgestion/Models/users.dart'; import 'package:youmazgestion/Models/produit.dart'; import 'package:youmazgestion/Services/stock_managementDatabase.dart'; import 'package:youmazgestion/controller/userController.dart'; +import 'package:intl/intl.dart'; class NouvelleCommandePage extends StatefulWidget { const NouvelleCommandePage({super.key}); @@ -695,7 +696,7 @@ void _modifierQuantite(int productId, int nouvelleQuantite) { const SizedBox(height: 12), // Détails du produit en grille - _buildProductDetailRow('Prix', '${product.price.toStringAsFixed(2)} MGA'), + _buildProductDetailRow('Prix', '${NumberFormat('#,##0', 'fr_FR').format(product.price)} MGA'), _buildProductDetailRow('Quantité ajoutée', '$newQuantity'), if (product.imei != null && product.imei!.isNotEmpty) @@ -2348,10 +2349,18 @@ Widget _buildRoleBasedHeader() { ), ); } - - // 🎯 MODIFIÉ: Interface produit avec indication visuelle de la commandabilité +// 🎯 MODIFIÉ: Interface produit avec filtrage des produits en rupture ou non assignés Widget _buildProductListItem(Product product, int quantity, bool isMobile) { + // ===== FILTRAGE : Ne pas afficher les produits en rupture ou non assignés ===== final bool isOutOfStock = product.stock != null && product.stock! <= 0; + final bool hasNoPointDeVente = product.pointDeVenteId == null || product.pointDeVenteId == 0; + + // Si le produit est en rupture de stock OU n'a pas de point de vente assigné, on ne l'affiche pas + if (isOutOfStock || hasNoPointDeVente) { + return const SizedBox.shrink(); // Widget vide qui ne prend pas d'espace + } + + // ===== VARIABLES D'ÉTAT ===== final detailPanier = _panierDetails[product.id!]; final int currentQuantity = detailPanier?.quantite ?? 0; final isCurrentUserPointDeVente = product.pointDeVenteId == _userController.pointDeVenteId; @@ -2385,17 +2394,15 @@ Widget _buildProductListItem(Product product, int quantity, bool isMobile) { child: Container( decoration: BoxDecoration( borderRadius: BorderRadius.circular(8), - border: isOutOfStock - ? Border.all(color: Colors.red.shade200, width: 1.5) - : detailPanier?.estCadeau == true - ? Border.all(color: Colors.green.shade300, width: 2) - : detailPanier?.aRemise == true + border: detailPanier?.estCadeau == true + ? Border.all(color: Colors.green.shade300, width: 2) + : detailPanier?.aRemise == true + ? Border.all(color: Colors.orange.shade300, width: 2) + : isCurrentUserPointDeVente ? Border.all(color: Colors.orange.shade300, width: 2) - : isCurrentUserPointDeVente - ? Border.all(color: Colors.orange.shade300, width: 2) - : !isProduitCommandable - ? Border.all(color: Colors.grey.shade200, width: 1) - : null, + : !isProduitCommandable + ? Border.all(color: Colors.grey.shade200, width: 1) + : null, ), child: Padding( padding: const EdgeInsets.all(12.0), @@ -2403,21 +2410,20 @@ Widget _buildProductListItem(Product product, int quantity, bool isMobile) { children: [ Row( children: [ + // ===== ICÔNE PRODUIT ===== Container( width: isMobile ? 40 : 50, height: isMobile ? 40 : 50, decoration: BoxDecoration( color: !isProduitCommandable ? Colors.grey.shade100 - : isOutOfStock - ? Colors.red.shade50 - : detailPanier?.estCadeau == true - ? Colors.green.shade50 - : detailPanier?.aRemise == true + : detailPanier?.estCadeau == true + ? Colors.green.shade50 + : detailPanier?.aRemise == true + ? Colors.orange.shade50 + : isCurrentUserPointDeVente ? Colors.orange.shade50 - : isCurrentUserPointDeVente - ? Colors.orange.shade50 - : Colors.blue.shade50, + : Colors.blue.shade50, borderRadius: BorderRadius.circular(8), ), child: Icon( @@ -2433,22 +2439,23 @@ Widget _buildProductListItem(Product product, int quantity, bool isMobile) { size: isMobile ? 20 : 24, color: !isProduitCommandable ? Colors.grey.shade500 - : isOutOfStock - ? Colors.red - : detailPanier?.estCadeau == true - ? Colors.green.shade700 - : detailPanier?.aRemise == true + : detailPanier?.estCadeau == true + ? Colors.green.shade700 + : detailPanier?.aRemise == true + ? Colors.orange.shade700 + : isCurrentUserPointDeVente ? Colors.orange.shade700 - : isCurrentUserPointDeVente - ? Colors.orange.shade700 - : Colors.blue, + : Colors.blue, ), ), const SizedBox(width: 12), + + // ===== INFORMATIONS PRODUIT ===== Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ + // Nom du produit avec badges de statut Row( children: [ Expanded( @@ -2459,13 +2466,12 @@ Widget _buildProductListItem(Product product, int quantity, bool isMobile) { fontSize: isMobile ? 14 : 16, color: !isProduitCommandable ? Colors.grey.shade600 - : isOutOfStock - ? Colors.red.shade700 - : null, + : null, ), ), ), - // Indicateurs de statut + + // Badge "AUTRE PV" pour produits non commandables if (!isProduitCommandable && !_isUserSuperAdmin()) Container( padding: const EdgeInsets.symmetric(horizontal: 6, vertical: 2), @@ -2489,6 +2495,8 @@ Widget _buildProductListItem(Product product, int quantity, bool isMobile) { ], ), ), + + // Badge "CADEAU" if (detailPanier?.estCadeau == true) Container( padding: const EdgeInsets.symmetric(horizontal: 6, vertical: 2), @@ -2505,6 +2513,8 @@ Widget _buildProductListItem(Product product, int quantity, bool isMobile) { ), ), ), + + // Badge "MON PV" if (isCurrentUserPointDeVente && detailPanier?.estCadeau != true && isProduitCommandable) Container( padding: const EdgeInsets.symmetric(horizontal: 6, vertical: 2), @@ -2529,6 +2539,7 @@ Widget _buildProductListItem(Product product, int quantity, bool isMobile) { Row( children: [ if (detailPanier?.estCadeau == true) ...[ + // Prix gratuit pour les cadeaux Text( 'Gratuit', style: TextStyle( @@ -2539,7 +2550,7 @@ Widget _buildProductListItem(Product product, int quantity, bool isMobile) { ), const SizedBox(width: 8), Text( - '${product.price.toStringAsFixed(2)} MGA', + '${NumberFormat('#,##0', 'fr_FR').format(product.price)} MGA', style: TextStyle( color: Colors.grey.shade500, fontWeight: FontWeight.w600, @@ -2548,8 +2559,9 @@ Widget _buildProductListItem(Product product, int quantity, bool isMobile) { ), ), ] else ...[ + // Prix normal ou avec remise Text( - '${product.price.toStringAsFixed(2)} MGA', + '${NumberFormat('#,##0', 'fr_FR').format(product.price)} MGA', style: TextStyle( color: Colors.green.shade700, fontWeight: FontWeight.w600, @@ -2559,10 +2571,11 @@ Widget _buildProductListItem(Product product, int quantity, bool isMobile) { : null, ), ), + // Prix après remise if (detailPanier?.aRemise == true) ...[ const SizedBox(width: 8), Text( - '${(detailPanier!.prixFinal / detailPanier.quantite).toStringAsFixed(2)} MGA', + '${NumberFormat('#,##0', 'fr_FR').format(detailPanier!.prixFinal / detailPanier.quantite)} MGA', style: TextStyle( color: Colors.orange.shade700, fontWeight: FontWeight.bold, @@ -2574,7 +2587,7 @@ Widget _buildProductListItem(Product product, int quantity, bool isMobile) { ], ), - // Affichage remise + // Description de la remise if (detailPanier?.aRemise == true && !detailPanier!.estCadeau) Text( 'Remise: ${detailPanier!.remiseDescription}', @@ -2585,20 +2598,17 @@ Widget _buildProductListItem(Product product, int quantity, bool isMobile) { ), ), - // Stock + // ===== STOCK (Toujours > 0 grâce au filtrage) ===== if (product.stock != null) Text( - 'Stock: ${product.stock}${isOutOfStock ? ' (Rupture)' : ''}', + 'Stock: ${product.stock}', style: TextStyle( fontSize: isMobile ? 10 : 12, - color: isOutOfStock - ? Colors.red.shade600 - : Colors.grey.shade600, - fontWeight: isOutOfStock ? FontWeight.w600 : FontWeight.normal, + color: Colors.grey.shade600, ), ), - // ===== AFFICHAGE IMEI ET RÉFÉRENCE ===== + // ===== IMEI ET RÉFÉRENCE ===== if (product.imei != null && product.imei!.isNotEmpty) Text( 'IMEI: ${product.imei}', @@ -2617,7 +2627,7 @@ Widget _buildProductListItem(Product product, int quantity, bool isMobile) { ), ), - // Point de vente + // ===== POINT DE VENTE ===== const SizedBox(height: 4), Row( children: [ @@ -2680,7 +2690,7 @@ Widget _buildProductListItem(Product product, int quantity, bool isMobile) { ? Colors.green.shade700 : Colors.grey.shade600, ), - onPressed: isOutOfStock ? null : () => _basculerStatutCadeau(product.id!), + onPressed: () => _basculerStatutCadeau(product.id!), tooltip: detailPanier?.estCadeau == true ? 'Retirer le statut cadeau' : 'Marquer comme cadeau', @@ -2692,6 +2702,7 @@ Widget _buildProductListItem(Product product, int quantity, bool isMobile) { ), ), ), + // Bouton remise (seulement pour les articles non-cadeaux) if (!detailPanier!.estCadeau) Container( @@ -2706,7 +2717,7 @@ Widget _buildProductListItem(Product product, int quantity, bool isMobile) { ? Colors.orange.shade700 : Colors.grey.shade600, ), - onPressed: isOutOfStock ? null : () => _showRemiseDialog(product), + onPressed: () => _showRemiseDialog(product), tooltip: detailPanier.aRemise ? 'Modifier la remise' : 'Ajouter une remise', @@ -2718,6 +2729,7 @@ Widget _buildProductListItem(Product product, int quantity, bool isMobile) { ), ), ), + // Bouton pour ajouter un cadeau à un autre produit Container( margin: const EdgeInsets.only(left: 4), @@ -2727,7 +2739,7 @@ Widget _buildProductListItem(Product product, int quantity, bool isMobile) { size: isMobile ? 16 : 18, color: Colors.green.shade600, ), - onPressed: isOutOfStock ? null : () => _showCadeauDialog(product), + onPressed: () => _showCadeauDialog(product), tooltip: 'Ajouter un cadeau', style: IconButton.styleFrom( backgroundColor: Colors.green.shade50, @@ -2740,30 +2752,30 @@ Widget _buildProductListItem(Product product, int quantity, bool isMobile) { const SizedBox(height: 8), ], - // Contrôles de quantité (seulement si commandable) + // ===== CONTRÔLES DE QUANTITÉ ===== if (isProduitCommandable) Container( decoration: BoxDecoration( - color: isOutOfStock - ? Colors.grey.shade100 - : detailPanier?.estCadeau == true - ? Colors.green.shade50 - : isCurrentUserPointDeVente - ? Colors.orange.shade50 - : Colors.blue.shade50, + color: detailPanier?.estCadeau == true + ? Colors.green.shade50 + : isCurrentUserPointDeVente + ? Colors.orange.shade50 + : Colors.blue.shade50, borderRadius: BorderRadius.circular(20), ), child: Row( mainAxisSize: MainAxisSize.min, children: [ + // Bouton diminuer IconButton( icon: Icon(Icons.remove, size: isMobile ? 16 : 18), - onPressed: isOutOfStock ? null : () { + onPressed: () { if (currentQuantity > 0) { _modifierQuantite(product.id!, currentQuantity - 1); } }, ), + // Quantité actuelle Text( currentQuantity.toString(), style: TextStyle( @@ -2771,9 +2783,10 @@ Widget _buildProductListItem(Product product, int quantity, bool isMobile) { fontSize: isMobile ? 12 : 14, ), ), + // Bouton augmenter IconButton( icon: Icon(Icons.add, size: isMobile ? 16 : 18), - onPressed: isOutOfStock ? null : () { + onPressed: () { if (product.stock == null || currentQuantity < product.stock!) { if (currentQuantity == 0) { _ajouterAuPanier(product, 1); @@ -2795,7 +2808,7 @@ Widget _buildProductListItem(Product product, int quantity, bool isMobile) { ), ) else - // Message informatif pour produits non-commandables + // ===== SECTION PRODUITS NON-COMMANDABLES ===== Container( padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8), decoration: BoxDecoration( @@ -2805,6 +2818,7 @@ Widget _buildProductListItem(Product product, int quantity, bool isMobile) { ), child: Column( children: [ + // Indicateur de consultation Row( mainAxisSize: MainAxisSize.min, children: [ @@ -2821,28 +2835,18 @@ Widget _buildProductListItem(Product product, int quantity, bool isMobile) { ], ), const SizedBox(height: 4), - ElevatedButton.icon( - icon: const Icon(Icons.swap_horiz, size: 14), - label:!isMobile ? const Text('Demander transfertt'):const SizedBox.shrink(), - style: ElevatedButton.styleFrom( - backgroundColor: (product.stock != null && product.stock! >= 1) - ? Colors.blue.shade700 - : Colors.grey.shade400, - foregroundColor: Colors.white, - padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4), - ), - onPressed: (product.stock != null && product.stock! >= 1) - ? () => _showDemandeTransfertDialog(product) - : () { - Get.snackbar( - 'Stock insuffisant', - 'Impossible de demander un transfert : produit en rupture de stock', - snackPosition: SnackPosition.BOTTOM, - backgroundColor: Colors.orange.shade600, - colorText: Colors.white, - ); - }, -), + + // Bouton demande de transfert + ElevatedButton.icon( + icon: const Icon(Icons.swap_horiz, size: 14), + label: !isMobile ? const Text('Demander transfert') : const SizedBox.shrink(), + style: ElevatedButton.styleFrom( + backgroundColor: Colors.blue.shade700, + foregroundColor: Colors.white, + padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4), + ), + onPressed: () => _showDemandeTransfertDialog(product), + ), ], ), ), @@ -2989,7 +2993,7 @@ Future _showDemandeTransfertDialog(Product product) async { Expanded( child: _buildInfoCard( 'Prix unitaire', - '${product.price.toStringAsFixed(2)} MGA', + '${NumberFormat('#,##0', 'fr_FR').format(product.price)} MGA', Icons.attach_money, Colors.green, ), @@ -3513,7 +3517,7 @@ Widget _buildTransferStep(String label, String pointDeVente, IconData icon, Colo ), const SizedBox(width: 8), Text( - '(${detail.prixUnitaire.toStringAsFixed(2)} MGA)', + '(${NumberFormat('#,##0', 'fr_FR').format(detail.prixUnitaire)} MGA)', style: TextStyle( fontSize: 11, color: Colors.grey.shade500, @@ -3530,19 +3534,19 @@ Widget _buildTransferStep(String label, String pointDeVente, IconData icon, Colo ), const Text(' → '), Text( - '${(detail.prixFinal / detail.quantite).toStringAsFixed(2)} MGA', + '${NumberFormat('#,##0', 'fr_FR').format(detail.prixFinal / detail.quantite)} MGA', style: TextStyle( color: Colors.orange.shade700, fontWeight: FontWeight.bold, ), ), ] else - Text('${detail.prixUnitaire.toStringAsFixed(2)} MGA'), + Text('${NumberFormat('#,##0', 'fr_FR').format(detail.prixUnitaire)} MGA'), ], ), if (detail.aRemise && !detail.estCadeau) Text( - 'Remise: ${detail.remiseDescription} (-${detail.montantRemise.toStringAsFixed(2)} MGA)', + 'Remise: ${detail.remiseDescription} (-${NumberFormat('#,##0', 'fr_FR').format(detail.montantRemise)} MGA)', style: TextStyle( fontSize: 11, color: Colors.orange.shade600, @@ -3595,7 +3599,7 @@ Widget _buildTransferStep(String label, String pointDeVente, IconData icon, Colo ], ), Text( - 'Valeur: ${detail.sousTotal.toStringAsFixed(2)} MGA', + 'Valeur: ${NumberFormat('#,##0', 'fr_FR').format(detail.sousTotal)} MGA', style: TextStyle( fontSize: 10, color: Colors.grey.shade500, @@ -3604,7 +3608,7 @@ Widget _buildTransferStep(String label, String pointDeVente, IconData icon, Colo ), ] else if (detail.aRemise && detail.sousTotal != detail.prixFinal) ...[ Text( - '${detail.sousTotal.toStringAsFixed(2)} MGA', + '${NumberFormat('#,##0', 'fr_FR').format(detail.sousTotal)} MGA', style: const TextStyle( fontSize: 11, decoration: TextDecoration.lineThrough, @@ -3612,7 +3616,7 @@ Widget _buildTransferStep(String label, String pointDeVente, IconData icon, Colo ), ), Text( - '${detail.prixFinal.toStringAsFixed(2)} MGA', + '${NumberFormat('#,##0', 'fr_FR').format(detail.prixFinal)} MGA', style: TextStyle( fontWeight: FontWeight.bold, color: Colors.orange.shade700, @@ -3621,7 +3625,7 @@ Widget _buildTransferStep(String label, String pointDeVente, IconData icon, Colo ), ] else Text( - '${detail.prixFinal.toStringAsFixed(2)} MGA', + '${NumberFormat('#,##0', 'fr_FR').format(detail.prixFinal)} MGA', style: TextStyle( fontWeight: FontWeight.bold, color: Colors.blue.shade800, @@ -3673,7 +3677,7 @@ Widget _buildTransferStep(String label, String pointDeVente, IconData icon, Colo children: [ const Text('Sous-total:', style: TextStyle(fontSize: 14)), Text( - '${sousTotal.toStringAsFixed(2)} MGA', + '${NumberFormat('#,##0', 'fr_FR').format(sousTotal)} MGA', style: const TextStyle(fontSize: 14), ), ], @@ -3694,7 +3698,7 @@ Widget _buildTransferStep(String label, String pointDeVente, IconData icon, Colo ), ), Text( - '-${totalRemises.toStringAsFixed(2)} MGA', + '-${NumberFormat('#,##0', 'fr_FR').format(totalRemises)} MGA', style: TextStyle( fontSize: 14, color: Colors.orange.shade700, @@ -3729,7 +3733,7 @@ Widget _buildTransferStep(String label, String pointDeVente, IconData icon, Colo ], ), Text( - '-${totalCadeaux.toStringAsFixed(2)} MGA', + '-${NumberFormat('#,##0', 'fr_FR').format(totalCadeaux)} MGA', style: TextStyle( fontSize: 14, color: Colors.green.shade700, @@ -3753,7 +3757,7 @@ Widget _buildTransferStep(String label, String pointDeVente, IconData icon, Colo style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold), ), Text( - '${total.toStringAsFixed(2)} MGA', + '${NumberFormat('#,##0', 'fr_FR').format(total)} MGA', style: const TextStyle( fontSize: 18, fontWeight: FontWeight.bold, @@ -3791,7 +3795,7 @@ Widget _buildTransferStep(String label, String pointDeVente, IconData icon, Colo ), const SizedBox(width: 4), Text( - 'Économies totales: ${(totalRemises + totalCadeaux).toStringAsFixed(2)} MGA', + 'Économies totales: ${NumberFormat('#,##0', 'fr_FR').format(totalRemises + totalCadeaux)} MGA', style: TextStyle( color: Colors.green.shade700, fontWeight: FontWeight.bold, @@ -3807,7 +3811,7 @@ Widget _buildTransferStep(String label, String pointDeVente, IconData icon, Colo if (totalRemises > 0 && totalCadeaux > 0) ...[ const SizedBox(height: 4), Text( - 'Remises: ${totalRemises.toStringAsFixed(2)} MGA • Cadeaux: ${totalCadeaux.toStringAsFixed(2)} MGA', + 'Remises: ${NumberFormat('#,##0', 'fr_FR').format(totalRemises)} MGA • Cadeaux: ${NumberFormat('#,##0', 'fr_FR').format(totalCadeaux)} MGA', style: TextStyle( color: Colors.grey.shade600, fontSize: 11, diff --git a/lib/Views/produitsCard.dart b/lib/Views/produitsCard.dart index c4011f8..e789b6f 100644 --- a/lib/Views/produitsCard.dart +++ b/lib/Views/produitsCard.dart @@ -1,6 +1,7 @@ import 'package:flutter/material.dart'; import 'dart:io'; import 'package:youmazgestion/Models/produit.dart'; +import 'package:intl/intl.dart'; class ProductCard extends StatefulWidget { final Product product; @@ -221,7 +222,7 @@ class _ProductCardState extends State ), const SizedBox(height: 4), Text( - '${widget.product.price.toStringAsFixed(2)} MGA', + '${NumberFormat('#,##0', 'fr_FR').format(widget.product.price)} MGA', style: const TextStyle( fontWeight: FontWeight.bold, fontSize: 13, diff --git a/lib/Views/ticketPage.dart b/lib/Views/ticketPage.dart index 3d443da..2fe5554 100644 --- a/lib/Views/ticketPage.dart +++ b/lib/Views/ticketPage.dart @@ -1,5 +1,5 @@ import 'dart:io'; - +import 'package:intl/intl.dart'; import 'package:flutter/material.dart'; import 'package:get/get.dart'; @@ -67,8 +67,8 @@ class TicketPage extends StatelessWidget { return [ product.name, quantity.toString(), - '${product.price.toStringAsFixed(2)} MGA', - '${productTotal.toStringAsFixed(2)} MGA', + '${NumberFormat('#,##0.00', 'fr_FR').format(product.price)} MGA', + '${NumberFormat('#,##0.00', 'fr_FR').format(productTotal)} MGA', ]; }).toList(), ], @@ -82,7 +82,7 @@ class TicketPage extends StatelessWidget { pw.Text('Total :', style: pw.TextStyle( fontSize: 18, fontWeight: pw.FontWeight.bold)), - pw.Text('${totalCartPrice.toStringAsFixed(2)} MGA', + pw.Text('${NumberFormat('#,##0.00', 'fr_FR').format(totalCartPrice)} MGA', style: pw.TextStyle( fontSize: 18, fontWeight: pw.FontWeight.bold)), ], @@ -95,7 +95,7 @@ class TicketPage extends StatelessWidget { children: [ pw.Text('Somme remise :', style: const pw.TextStyle(fontSize: 16)), - pw.Text('${amountPaid.toStringAsFixed(2)} MGA', + pw.Text('${NumberFormat('#,##0.00', 'fr_FR').format(amountPaid)} MGA', style: const pw.TextStyle(fontSize: 16)), ], ), @@ -106,7 +106,7 @@ class TicketPage extends StatelessWidget { pw.Text('Somme rendue :', style: const pw.TextStyle(fontSize: 16)), pw.Text( - '${(amountPaid - totalCartPrice).toStringAsFixed(2)} MGA', + '${NumberFormat('#,##0.00', 'fr_FR').format(amountPaid - totalCartPrice)} MGA', style: const pw.TextStyle(fontSize: 16)), ], ), @@ -271,14 +271,14 @@ class TicketPage extends StatelessWidget { TableCell( child: Center( child: Text( - '${product.price.toStringAsFixed(2)} MGA', + '${NumberFormat('#,##0.00', 'fr_FR').format(product.price)} MGA', ), ), ), TableCell( child: Center( child: Text( - '${productTotal.toStringAsFixed(2)} MGA', + '${NumberFormat('#,##0.00', 'fr_FR').format(productTotal)} MGA', ), ), ), @@ -305,7 +305,7 @@ class TicketPage extends StatelessWidget { ), ), Text( - '${totalOrderAmount.toStringAsFixed(2)} MGA', + '${NumberFormat('#,##0.00', 'fr_FR').format(totalOrderAmount)} MGA', style: const TextStyle( fontSize: 18, fontWeight: FontWeight.bold, @@ -330,7 +330,7 @@ class TicketPage extends StatelessWidget { ), ), Text( - '${amountPaid.toStringAsFixed(2)} MGA', + '${NumberFormat('#,##0.00', 'fr_FR').format(amountPaid)} MGA', style: const TextStyle( fontSize: 16, ), @@ -348,7 +348,7 @@ class TicketPage extends StatelessWidget { ), ), Text( - '${change.toStringAsFixed(2)} MGA', + '${NumberFormat('#,##0.00', 'fr_FR').format(change)} MGA', style: const TextStyle( fontSize: 16, ), diff --git a/lib/config/DatabaseConfig.dart b/lib/config/DatabaseConfig.dart index c5b3d0b..92d10ec 100644 --- a/lib/config/DatabaseConfig.dart +++ b/lib/config/DatabaseConfig.dart @@ -5,7 +5,7 @@ import 'dart:async'; class DatabaseConfig { // Local MySQL settings - static const String localHost = '192.168.88.73'; + static const String localHost = '102.17.52.31'; static const String localUsername = 'guycom'; static const String? localPassword = '3iV59wjRdbuXAPR'; static const String localDatabase = 'guycom';