commit de 18072025
This commit is contained in:
parent
13296529c3
commit
d043f6f20b
@ -92,6 +92,8 @@ class Commande {
|
|||||||
final String? clientNom;
|
final String? clientNom;
|
||||||
final String? clientPrenom;
|
final String? clientPrenom;
|
||||||
final String? clientEmail;
|
final String? clientEmail;
|
||||||
|
final int? pointDeVenteId;
|
||||||
|
final String? pointDeVenteDesign;
|
||||||
|
|
||||||
Commande({
|
Commande({
|
||||||
this.id,
|
this.id,
|
||||||
@ -106,6 +108,8 @@ class Commande {
|
|||||||
this.clientNom,
|
this.clientNom,
|
||||||
this.clientPrenom,
|
this.clientPrenom,
|
||||||
this.clientEmail,
|
this.clientEmail,
|
||||||
|
this.pointDeVenteId,
|
||||||
|
this.pointDeVenteDesign,
|
||||||
});
|
});
|
||||||
|
|
||||||
String get clientNomComplet {
|
String get clientNomComplet {
|
||||||
@ -156,6 +160,8 @@ class Commande {
|
|||||||
clientNom: map['clientNom'] as String?,
|
clientNom: map['clientNom'] as String?,
|
||||||
clientPrenom: map['clientPrenom'] as String?,
|
clientPrenom: map['clientPrenom'] as String?,
|
||||||
clientEmail: map['clientEmail'] as String?,
|
clientEmail: map['clientEmail'] as String?,
|
||||||
|
pointDeVenteId: map['pointDeVenteId'] as int?,
|
||||||
|
pointDeVenteDesign: map['pointDeVenteDesign'] as String?,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -597,6 +597,31 @@ class AppDatabase {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<double> getValeurTotaleStock() async {
|
||||||
|
final db = await database;
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Récupérer tous les produits
|
||||||
|
final List<Map<String, dynamic>> products = (await db.query('products')) as List<Map<String, dynamic>>;
|
||||||
|
|
||||||
|
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 ---
|
// --- STATISTIQUES ---
|
||||||
|
|
||||||
Future<Map<String, dynamic>> getStatistiques() async {
|
Future<Map<String, dynamic>> getStatistiques() async {
|
||||||
@ -807,9 +832,16 @@ class AppDatabase {
|
|||||||
Future<List<Commande>> getCommandes() async {
|
Future<List<Commande>> getCommandes() async {
|
||||||
final db = await database;
|
final db = await database;
|
||||||
final result = await db.query('''
|
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
|
FROM commandes c
|
||||||
LEFT JOIN clients cl ON c.clientId = cl.id
|
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
|
ORDER BY c.dateCommande DESC
|
||||||
''');
|
''');
|
||||||
return result.map((row) => Commande.fromMap(row.fields)).toList();
|
return result.map((row) => Commande.fromMap(row.fields)).toList();
|
||||||
|
|||||||
@ -7,6 +7,7 @@ import 'package:youmazgestion/controller/userController.dart';
|
|||||||
import 'package:youmazgestion/Models/users.dart';
|
import 'package:youmazgestion/Models/users.dart';
|
||||||
import 'package:youmazgestion/Models/client.dart';
|
import 'package:youmazgestion/Models/client.dart';
|
||||||
import 'package:youmazgestion/Models/produit.dart'; // Ajout de l'import manquant
|
import 'package:youmazgestion/Models/produit.dart'; // Ajout de l'import manquant
|
||||||
|
import 'package:intl/intl.dart';
|
||||||
|
|
||||||
class DashboardPage extends StatefulWidget {
|
class DashboardPage extends StatefulWidget {
|
||||||
@override
|
@override
|
||||||
@ -14,6 +15,8 @@ class DashboardPage extends StatefulWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class _DashboardPageState extends State<DashboardPage> with SingleTickerProviderStateMixin {
|
class _DashboardPageState extends State<DashboardPage> with SingleTickerProviderStateMixin {
|
||||||
|
DateTimeRange? _dateRange;
|
||||||
|
bool _showOnlyToday = false;
|
||||||
final AppDatabase _database = AppDatabase.instance;
|
final AppDatabase _database = AppDatabase.instance;
|
||||||
final UserController _userController = Get.find<UserController>();
|
final UserController _userController = Get.find<UserController>();
|
||||||
final GlobalKey _recentClientsKey = GlobalKey();
|
final GlobalKey _recentClientsKey = GlobalKey();
|
||||||
@ -61,7 +64,7 @@ void initState() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void _loadData() {
|
void _loadData() {
|
||||||
_statsFuture = _database.getStatistiques();
|
_statsFuture = _database.getStatistiques(); // Plus besoin de calcul supplémentaire
|
||||||
_recentOrdersFuture = _database.getCommandes().then((orders) => orders.take(5).toList());
|
_recentOrdersFuture = _database.getCommandes().then((orders) => orders.take(5).toList());
|
||||||
_lowStockProductsFuture = _database.getProducts().then((products) {
|
_lowStockProductsFuture = _database.getProducts().then((products) {
|
||||||
return products.where((p) => (p.stock ?? 0) < 10).toList();
|
return products.where((p) => (p.stock ?? 0) < 10).toList();
|
||||||
@ -90,7 +93,7 @@ Future<void> _showCategoryProductsDialog(String category) async {
|
|||||||
: CircleAvatar(child: Icon(Icons.inventory)),
|
: CircleAvatar(child: Icon(Icons.inventory)),
|
||||||
title: Text(product.name),
|
title: Text(product.name),
|
||||||
subtitle: Text('Stock: ${product.stock}'),
|
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<void> _showCategoryProductsDialog(String category) async {
|
|||||||
final month = salesData[groupIndex]['month'];
|
final month = salesData[groupIndex]['month'];
|
||||||
final total = salesData[groupIndex]['total'];
|
final total = salesData[groupIndex]['total'];
|
||||||
return BarTooltipItem(
|
return BarTooltipItem(
|
||||||
'$month\n${total.toStringAsFixed(2)} MGA',
|
'$month\n${NumberFormat('#,##0', 'fr_FR').format(total)} MGA',
|
||||||
TextStyle(color: Colors.white),
|
TextStyle(color: Colors.white),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
@ -754,10 +757,17 @@ Future<void> _showCategoryProductsDialog(String category) async {
|
|||||||
),
|
),
|
||||||
_buildMiniStatCard(
|
_buildMiniStatCard(
|
||||||
title: 'CA (MGA)',
|
title: 'CA (MGA)',
|
||||||
value: '${(stats['chiffreAffaires'] ?? 0.0).toStringAsFixed(2)}',
|
value: NumberFormat('#,##0', 'fr_FR').format(stats['chiffreAffaires'] ?? 0.0),
|
||||||
icon: Icons.euro_symbol,
|
icon: Icons.euro_symbol,
|
||||||
color: Colors.purple,
|
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,
|
||||||
|
),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
@ -765,7 +775,6 @@ Future<void> _showCategoryProductsDialog(String category) async {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Widget _buildMiniStatCard({required String title, required String value, required IconData icon, required Color color}) {
|
Widget _buildMiniStatCard({required String title, required String value, required IconData icon, required Color color}) {
|
||||||
|
|
||||||
return InkWell(
|
return InkWell(
|
||||||
@ -952,7 +961,7 @@ Future<void> _showCategoryProductsDialog(String category) async {
|
|||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
Text(
|
Text(
|
||||||
'${order.montantTotal.toStringAsFixed(2)} MGA',
|
'${NumberFormat('#,##0', 'fr_FR').format(order.montantTotal)} MGA',
|
||||||
style: TextStyle(fontSize: 12),
|
style: TextStyle(fontSize: 12),
|
||||||
),
|
),
|
||||||
Text(
|
Text(
|
||||||
@ -1160,7 +1169,7 @@ Widget _buildVentesParPointDeVenteCard() {
|
|||||||
final ca = pointVente['chiffre_affaires'] ?? 0.0;
|
final ca = pointVente['chiffre_affaires'] ?? 0.0;
|
||||||
final nbCommandes = pointVente['nombre_commandes'] ?? 0;
|
final nbCommandes = pointVente['nombre_commandes'] ?? 0;
|
||||||
return BarTooltipItem(
|
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),
|
TextStyle(color: Colors.white, fontSize: 12),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
@ -1333,7 +1342,9 @@ Widget _buildTableauVentesPointDeVente(List<Map<String, dynamic>> ventesData) {
|
|||||||
Expanded(
|
Expanded(
|
||||||
flex: 2,
|
flex: 2,
|
||||||
child: Text(
|
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),
|
style: TextStyle(fontSize: 12, fontWeight: FontWeight.w500),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@ -1354,7 +1365,9 @@ Widget _buildTableauVentesPointDeVente(List<Map<String, dynamic>> ventesData) {
|
|||||||
Expanded(
|
Expanded(
|
||||||
flex: 2,
|
flex: 2,
|
||||||
child: Text(
|
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),
|
style: TextStyle(fontSize: 12),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@ -1403,33 +1416,112 @@ String _formatCurrency(double value) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<void> _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<String, dynamic> pointVenteData) async {
|
void _showPointVenteDetails(Map<String, dynamic> pointVenteData) async {
|
||||||
|
final isMobile = MediaQuery.of(context).size.width < 600;
|
||||||
final pointVenteId = pointVenteData['point_vente_id'] as int;
|
final pointVenteId = pointVenteData['point_vente_id'] as int;
|
||||||
final pointVenteNom = pointVenteData['point_vente_nom'] as String;
|
final pointVenteNom = pointVenteData['point_vente_nom'] as String;
|
||||||
|
|
||||||
showDialog(
|
showDialog(
|
||||||
context: context,
|
context: context,
|
||||||
builder: (context) => AlertDialog(
|
builder: (context) => AlertDialog(
|
||||||
title: Text('Détails - $pointVenteNom'),
|
title: Text('Détails - $pointVenteNom'),
|
||||||
content: Container(
|
content: Container(
|
||||||
width: double.maxFinite,
|
width: double.maxFinite,
|
||||||
height: 400,
|
height: 500,
|
||||||
child: SingleChildScrollView(
|
child: SingleChildScrollView(
|
||||||
child: Column(
|
child: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
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
|
// Statistiques générales
|
||||||
_buildStatRow('Chiffre d\'affaires:', '${((pointVenteData['chiffre_affaires'] as num?)?.toDouble() ?? 0.0).toStringAsFixed(2)} MGA'),
|
_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('Nombre de commandes:', '${pointVenteData['nombre_commandes'] ?? 0}'),
|
||||||
_buildStatRow('Articles vendus:', '${pointVenteData['nombre_articles_vendus'] ?? 0}'),
|
_buildStatRow('Articles vendus:', '${pointVenteData['nombre_articles_vendus'] ?? 0}'),
|
||||||
_buildStatRow('Quantité totale:', '${pointVenteData['quantite_totale_vendue'] ?? 0}'),
|
_buildStatRow('Quantité totale:', '${pointVenteData['quantite_totale_vendue'] ?? 0}'),
|
||||||
_buildStatRow('Panier moyen:', '${((pointVenteData['panier_moyen'] as num?)?.toDouble() ?? 0.0).toStringAsFixed(2)} MGA'),
|
_buildStatRow('Panier moyen:', '${NumberFormat('#,##0.00', 'fr_FR').format((pointVenteData['panier_moyen'] as num?)?.toDouble() ?? 0.0)} MGA'),
|
||||||
|
|
||||||
SizedBox(height: 16),
|
SizedBox(height: 16),
|
||||||
Text('Top 5 des produits:', style: TextStyle(fontWeight: FontWeight.bold)),
|
Text('Top 5 des produits:', style: TextStyle(fontWeight: FontWeight.bold)),
|
||||||
SizedBox(height: 8),
|
SizedBox(height: 8),
|
||||||
|
|
||||||
// Top produits
|
|
||||||
|
SizedBox(height: 16),
|
||||||
|
|
||||||
|
// ✅ Top produits
|
||||||
FutureBuilder<List<Map<String, dynamic>>>(
|
FutureBuilder<List<Map<String, dynamic>>>(
|
||||||
future: _database.getTopProduitsParPointDeVente(pointVenteId),
|
future: _database.getTopProduitsParPointDeVente(pointVenteId),
|
||||||
builder: (context, snapshot) {
|
builder: (context, snapshot) {
|
||||||
@ -1582,7 +1674,7 @@ Widget _buildStatRow(String label, String value) {
|
|||||||
],
|
],
|
||||||
),
|
),
|
||||||
trailing: Text(
|
trailing: Text(
|
||||||
'${product.price.toStringAsFixed(2)} MGA',
|
'${NumberFormat('#,##0', 'fr_FR').format(product.price)} MGA',
|
||||||
style: TextStyle(fontSize: 12),
|
style: TextStyle(fontSize: 12),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|||||||
@ -307,7 +307,7 @@ Future<void> _showDemandeTransfertDialog(Product product) async {
|
|||||||
Expanded(
|
Expanded(
|
||||||
child: _buildInfoCard(
|
child: _buildInfoCard(
|
||||||
'Prix unitaire',
|
'Prix unitaire',
|
||||||
'${product.price.toStringAsFixed(2)} MGA',
|
'${NumberFormat('#,##0.00', 'fr_FR').format(product.price)} MGA',
|
||||||
Icons.attach_money,
|
Icons.attach_money,
|
||||||
Colors.green,
|
Colors.green,
|
||||||
),
|
),
|
||||||
@ -1823,7 +1823,7 @@ Future<void> _showDemandeTransfertDialog(Product product) async {
|
|||||||
),
|
),
|
||||||
const SizedBox(height: 8),
|
const SizedBox(height: 8),
|
||||||
Text('IMEI: ${product.imei}'),
|
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),
|
const SizedBox(height: 12),
|
||||||
Container(
|
Container(
|
||||||
padding: const EdgeInsets.all(8),
|
padding: const EdgeInsets.all(8),
|
||||||
@ -4476,7 +4476,7 @@ Future<void> _showDemandeTransfertDialog(Product product) async {
|
|||||||
icon: Icons.info_outline,
|
icon: Icons.info_outline,
|
||||||
color: Colors.blue,
|
color: Colors.blue,
|
||||||
children: [
|
children: [
|
||||||
_buildModernInfoRow('Prix', '${product.price} MGA',
|
_buildModernInfoRow('Prix', '${NumberFormat('#,##0.00', 'fr_FR').format(product.price)} MGA',
|
||||||
Icons.payments_outlined),
|
Icons.payments_outlined),
|
||||||
_buildModernInfoRow('Catégorie', product.category,
|
_buildModernInfoRow('Catégorie', product.category,
|
||||||
Icons.category_outlined),
|
Icons.category_outlined),
|
||||||
|
|||||||
@ -2,6 +2,7 @@ import 'package:flutter/material.dart';
|
|||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
import 'package:youmazgestion/Components/app_bar.dart';
|
import 'package:youmazgestion/Components/app_bar.dart';
|
||||||
import 'package:youmazgestion/controller/HistoryController.dart';
|
import 'package:youmazgestion/controller/HistoryController.dart';
|
||||||
|
import 'package:intl/intl.dart';
|
||||||
|
|
||||||
import 'bilanDesJourne.dart';
|
import 'bilanDesJourne.dart';
|
||||||
|
|
||||||
@ -38,7 +39,7 @@ class _BilanMoisState extends State<BilanMois> {
|
|||||||
children: [
|
children: [
|
||||||
_buildInfoCard(
|
_buildInfoCard(
|
||||||
title: 'Chiffre réalisé',
|
title: 'Chiffre réalisé',
|
||||||
value: '${controller.totalSum.value.toStringAsFixed(2)} MGA',
|
value: '${NumberFormat('#,##0', 'fr_FR').format(controller.totalSum.value)} MGA',
|
||||||
color: Colors.green,
|
color: Colors.green,
|
||||||
icon: Icons.monetization_on,
|
icon: Icons.monetization_on,
|
||||||
),
|
),
|
||||||
|
|||||||
@ -188,7 +188,7 @@ class _GestionCommandesPageState extends State<GestionCommandesPage> {
|
|||||||
content: Column(
|
content: Column(
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
children: [
|
children: [
|
||||||
Text('Montant total: ${montantFinal.toStringAsFixed(2)} MGA'),
|
Text('Montant total: ${NumberFormat('#,##0', 'fr_FR').format(montantFinal)} MGA'),
|
||||||
const SizedBox(height: 10),
|
const SizedBox(height: 10),
|
||||||
TextField(
|
TextField(
|
||||||
controller: amountController,
|
controller: amountController,
|
||||||
@ -207,7 +207,7 @@ class _GestionCommandesPageState extends State<GestionCommandesPage> {
|
|||||||
const SizedBox(height: 20),
|
const SizedBox(height: 20),
|
||||||
if (amountGiven >= montantFinal)
|
if (amountGiven >= montantFinal)
|
||||||
Text(
|
Text(
|
||||||
'Monnaie à rendre: ${change.toStringAsFixed(2)} MGA',
|
'Monnaie à rendre: ${NumberFormat('#,##0', 'fr_FR').format(change)} MGA',
|
||||||
style: const TextStyle(
|
style: const TextStyle(
|
||||||
fontSize: 18,
|
fontSize: 18,
|
||||||
fontWeight: FontWeight.bold,
|
fontWeight: FontWeight.bold,
|
||||||
@ -674,7 +674,7 @@ class _GestionCommandesPageState extends State<GestionCommandesPage> {
|
|||||||
),
|
),
|
||||||
] else
|
] else
|
||||||
pw.Text(
|
pw.Text(
|
||||||
'${detail.prixUnitaire.toStringAsFixed(0)}',
|
NumberFormat('#,##0', 'fr_FR').format(detail.prixUnitaire),
|
||||||
style: smallTextStyle
|
style: smallTextStyle
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
@ -690,7 +690,7 @@ class _GestionCommandesPageState extends State<GestionCommandesPage> {
|
|||||||
children: [
|
children: [
|
||||||
if (detail.estCadeau) ...[
|
if (detail.estCadeau) ...[
|
||||||
pw.Text(
|
pw.Text(
|
||||||
'${detail.sousTotal.toStringAsFixed(0)}',
|
NumberFormat('#,##0', 'fr_FR').format(detail.sousTotal),
|
||||||
style: pw.TextStyle(
|
style: pw.TextStyle(
|
||||||
fontSize: 8,
|
fontSize: 8,
|
||||||
decoration: pw.TextDecoration.lineThrough,
|
decoration: pw.TextDecoration.lineThrough,
|
||||||
@ -796,7 +796,7 @@ class _GestionCommandesPageState extends State<GestionCommandesPage> {
|
|||||||
children: [
|
children: [
|
||||||
pw.Text('TOTAL:', style: boldTextStyle),
|
pw.Text('TOTAL:', style: boldTextStyle),
|
||||||
pw.SizedBox(width: 10),
|
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<GestionCommandesPage> {
|
|||||||
if (totalRemises > 0 || totalCadeaux > 0) ...[
|
if (totalRemises > 0 || totalCadeaux > 0) ...[
|
||||||
pw.SizedBox(height: 4),
|
pw.SizedBox(height: 4),
|
||||||
pw.Text(
|
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(
|
style: pw.TextStyle(
|
||||||
fontSize: 8,
|
fontSize: 8,
|
||||||
color: PdfColors.green,
|
color: PdfColors.green,
|
||||||
@ -1694,7 +1694,7 @@ class _GestionCommandesPageState extends State<GestionCommandesPage> {
|
|||||||
pw.Text('🎁 ', style: emojifont),
|
pw.Text('🎁 ', style: emojifont),
|
||||||
pw.Expanded(
|
pw.Expanded(
|
||||||
child: pw.Text(
|
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(
|
style: pw.TextStyle(
|
||||||
fontSize: 9,
|
fontSize: 9,
|
||||||
fontStyle: pw.FontStyle.italic,
|
fontStyle: pw.FontStyle.italic,
|
||||||
@ -1893,7 +1893,7 @@ class _GestionCommandesPageState extends State<GestionCommandesPage> {
|
|||||||
String _getPaymentMethodLabel(PaymentMethod payment) {
|
String _getPaymentMethodLabel(PaymentMethod payment) {
|
||||||
switch (payment.type) {
|
switch (payment.type) {
|
||||||
case PaymentType.cash:
|
case PaymentType.cash:
|
||||||
return 'LIQUIDE (${payment.amountGiven.toStringAsFixed(0)} MGA)';
|
return 'LIQUIDE (${NumberFormat('#,##0', 'fr_FR').format(payment.amountGiven)} MGA)';
|
||||||
case PaymentType.card:
|
case PaymentType.card:
|
||||||
return 'CARTE BANCAIRE';
|
return 'CARTE BANCAIRE';
|
||||||
case PaymentType.mvola:
|
case PaymentType.mvola:
|
||||||
@ -2149,7 +2149,7 @@ class _GestionCommandesPageState extends State<GestionCommandesPage> {
|
|||||||
children: [
|
children: [
|
||||||
pw.Text('SOUS-TOTAL:',
|
pw.Text('SOUS-TOTAL:',
|
||||||
style: const pw.TextStyle(fontSize: 8)),
|
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)),
|
style: const pw.TextStyle(fontSize: 8)),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
@ -2160,7 +2160,7 @@ class _GestionCommandesPageState extends State<GestionCommandesPage> {
|
|||||||
pw.Text('REMISES:',
|
pw.Text('REMISES:',
|
||||||
style: pw.TextStyle(
|
style: pw.TextStyle(
|
||||||
fontSize: 8, color: PdfColors.orange)),
|
fontSize: 8, color: PdfColors.orange)),
|
||||||
pw.Text('-${totalRemises.toStringAsFixed(0)} MGA',
|
pw.Text('-${NumberFormat('#,##0', 'fr_FR').format(totalRemises)} MGA',
|
||||||
style: pw.TextStyle(
|
style: pw.TextStyle(
|
||||||
fontSize: 8, color: PdfColors.orange)),
|
fontSize: 8, color: PdfColors.orange)),
|
||||||
],
|
],
|
||||||
@ -2173,7 +2173,7 @@ class _GestionCommandesPageState extends State<GestionCommandesPage> {
|
|||||||
pw.Text('CADEAUX ($nombreCadeaux):',
|
pw.Text('CADEAUX ($nombreCadeaux):',
|
||||||
style: pw.TextStyle(
|
style: pw.TextStyle(
|
||||||
fontSize: 8, color: PdfColors.green700)),
|
fontSize: 8, color: PdfColors.green700)),
|
||||||
pw.Text('-${totalCadeaux.toStringAsFixed(0)} MGA',
|
pw.Text('-${NumberFormat('#,##0', 'fr_FR').format(totalCadeaux)} MGA',
|
||||||
style: pw.TextStyle(
|
style: pw.TextStyle(
|
||||||
fontSize: 8, color: PdfColors.green700)),
|
fontSize: 8, color: PdfColors.green700)),
|
||||||
],
|
],
|
||||||
@ -2189,7 +2189,7 @@ class _GestionCommandesPageState extends State<GestionCommandesPage> {
|
|||||||
pw.Text('TOTAL:',
|
pw.Text('TOTAL:',
|
||||||
style: pw.TextStyle(
|
style: pw.TextStyle(
|
||||||
fontSize: 9, fontWeight: pw.FontWeight.bold)),
|
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(
|
style: pw.TextStyle(
|
||||||
fontSize: 9, fontWeight: pw.FontWeight.bold)),
|
fontSize: 9, fontWeight: pw.FontWeight.bold)),
|
||||||
],
|
],
|
||||||
@ -2198,7 +2198,7 @@ class _GestionCommandesPageState extends State<GestionCommandesPage> {
|
|||||||
if (totalRemises > 0 || totalCadeaux > 0) ...[
|
if (totalRemises > 0 || totalCadeaux > 0) ...[
|
||||||
pw.SizedBox(height: 4),
|
pw.SizedBox(height: 4),
|
||||||
pw.Text(
|
pw.Text(
|
||||||
'Économies: ${(totalRemises + totalCadeaux).toStringAsFixed(0)} MGA !',
|
'Économies: ${NumberFormat('#,##0', 'fr_FR').format(totalRemises + totalCadeaux)} MGA !',
|
||||||
style: pw.TextStyle(
|
style: pw.TextStyle(
|
||||||
fontSize: 7,
|
fontSize: 7,
|
||||||
color: PdfColors.green,
|
color: PdfColors.green,
|
||||||
@ -2222,7 +2222,7 @@ class _GestionCommandesPageState extends State<GestionCommandesPage> {
|
|||||||
if (payment.type == PaymentType.cash &&
|
if (payment.type == PaymentType.cash &&
|
||||||
payment.amountGiven > commande.montantTotal)
|
payment.amountGiven > commande.montantTotal)
|
||||||
pw.Text(
|
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)),
|
style: const pw.TextStyle(fontSize: 8)),
|
||||||
|
|
||||||
pw.SizedBox(height: 8),
|
pw.SizedBox(height: 8),
|
||||||
@ -2827,7 +2827,7 @@ class _GestionCommandesPageState extends State<GestionCommandesPage> {
|
|||||||
),
|
),
|
||||||
const SizedBox(width: 4),
|
const SizedBox(width: 4),
|
||||||
Text(
|
Text(
|
||||||
'${commande.montantTotal.toStringAsFixed(2)} MGA',
|
'${NumberFormat('#,##0', 'fr_FR').format(commande.montantTotal)} MGA',
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
fontSize: 14,
|
fontSize: 14,
|
||||||
fontWeight: FontWeight.bold,
|
fontWeight: FontWeight.bold,
|
||||||
|
|||||||
@ -54,7 +54,7 @@ class _HistoriquePageState extends State<HistoriquePage> {
|
|||||||
|
|
||||||
Future<void> _loadPointsDeVenteWithDefault() async {
|
Future<void> _loadPointsDeVenteWithDefault() async {
|
||||||
try {
|
try {
|
||||||
print(_userController.userId);
|
print(_userController.pointDeVenteId);
|
||||||
final points = await _appDatabase.getPointsDeVente();
|
final points = await _appDatabase.getPointsDeVente();
|
||||||
setState(() {
|
setState(() {
|
||||||
_pointsDeVente = points;
|
_pointsDeVente = points;
|
||||||
@ -88,7 +88,11 @@ class _HistoriquePageState extends State<HistoriquePage> {
|
|||||||
});
|
});
|
||||||
|
|
||||||
try {
|
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(() {
|
setState(() {
|
||||||
_commandes.clear();
|
_commandes.clear();
|
||||||
@ -136,11 +140,13 @@ class _HistoriquePageState extends State<HistoriquePage> {
|
|||||||
final searchText = _searchController.text.toLowerCase();
|
final searchText = _searchController.text.toLowerCase();
|
||||||
final clientQuery = _searchClientController.text.toLowerCase();
|
final clientQuery = _searchClientController.text.toLowerCase();
|
||||||
final commandeIdQuery = _searchCommandeIdController.text.toLowerCase();
|
final commandeIdQuery = _searchCommandeIdController.text.toLowerCase();
|
||||||
|
final pointDeVenteId = _userController.pointDeVenteId;
|
||||||
|
|
||||||
setState(() {
|
setState(() {
|
||||||
_filteredCommandes.clear();
|
_filteredCommandes.clear();
|
||||||
|
|
||||||
for (var commande in _commandes) {
|
for (var commande in _commandes) {
|
||||||
|
if (pointDeVenteId != 0 && commande.pointDeVenteId != pointDeVenteId) continue;
|
||||||
bool matchesSearch = searchText.isEmpty ||
|
bool matchesSearch = searchText.isEmpty ||
|
||||||
commande.clientNom!.toLowerCase().contains(searchText) ||
|
commande.clientNom!.toLowerCase().contains(searchText) ||
|
||||||
commande.clientPrenom!.toLowerCase().contains(searchText) ||
|
commande.clientPrenom!.toLowerCase().contains(searchText) ||
|
||||||
@ -590,7 +596,7 @@ class _HistoriquePageState extends State<HistoriquePage> {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
Text(
|
Text(
|
||||||
'${commande.montantTotal.toStringAsFixed(2)} MGA',
|
'${NumberFormat('#,##0.00', 'fr_FR').format(commande.montantTotal)} MGA',
|
||||||
style: const TextStyle(
|
style: const TextStyle(
|
||||||
fontSize: 16,
|
fontSize: 16,
|
||||||
fontWeight: FontWeight.bold,
|
fontWeight: FontWeight.bold,
|
||||||
@ -641,10 +647,10 @@ class _HistoriquePageState extends State<HistoriquePage> {
|
|||||||
const TextStyle(fontWeight: FontWeight.w500),
|
const TextStyle(fontWeight: FontWeight.w500),
|
||||||
),
|
),
|
||||||
subtitle: Text(
|
subtitle: Text(
|
||||||
'${detail.quantite} x ${detail.prixUnitaire.toStringAsFixed(2)} MGA',
|
'${detail.quantite} x ${NumberFormat('#,##0.00', 'fr_FR').format(detail.prixUnitaire)} MGA',
|
||||||
),
|
),
|
||||||
trailing: Text(
|
trailing: Text(
|
||||||
'${detail.sousTotal.toStringAsFixed(2)} MGA',
|
'${NumberFormat('#,##0.00', 'fr_FR').format(detail.sousTotal)} MGA',
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
fontWeight: FontWeight.bold,
|
fontWeight: FontWeight.bold,
|
||||||
color: Colors.blue.shade800,
|
color: Colors.blue.shade800,
|
||||||
@ -789,7 +795,7 @@ class _HistoriquePageState extends State<HistoriquePage> {
|
|||||||
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(commande.commandeurId);
|
||||||
print(_userController.userId);
|
print(_userController.pointDeVenteId);
|
||||||
|
|
||||||
return Card(
|
return Card(
|
||||||
margin: const EdgeInsets.symmetric(horizontal: 8, vertical: 4),
|
margin: const EdgeInsets.symmetric(horizontal: 8, vertical: 4),
|
||||||
@ -838,7 +844,7 @@ class _HistoriquePageState extends State<HistoriquePage> {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
Text(
|
Text(
|
||||||
'PV: ${_selectedPointDeVente}',
|
'PV: ${commande.pointDeVenteDesign}',
|
||||||
style: const TextStyle(
|
style: const TextStyle(
|
||||||
fontWeight: FontWeight.w500,
|
fontWeight: FontWeight.w500,
|
||||||
fontSize: 14,
|
fontSize: 14,
|
||||||
@ -858,7 +864,7 @@ class _HistoriquePageState extends State<HistoriquePage> {
|
|||||||
crossAxisAlignment: CrossAxisAlignment.end,
|
crossAxisAlignment: CrossAxisAlignment.end,
|
||||||
children: [
|
children: [
|
||||||
Text(
|
Text(
|
||||||
'${commande.montantTotal.toStringAsFixed(2)} MGA',
|
'${NumberFormat('#,##0.00', 'fr_FR').format(commande.montantTotal)} MGA',
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
fontWeight: FontWeight.bold,
|
fontWeight: FontWeight.bold,
|
||||||
color: Colors.green.shade700,
|
color: Colors.green.shade700,
|
||||||
|
|||||||
@ -5,6 +5,7 @@ import 'package:youmazgestion/Views/voirPlus.dart';
|
|||||||
import 'package:youmazgestion/controller/HistoryController.dart';
|
import 'package:youmazgestion/controller/HistoryController.dart';
|
||||||
import '../Models/Order.dart';
|
import '../Models/Order.dart';
|
||||||
import 'package:youmazgestion/Views/detailHistory.dart';
|
import 'package:youmazgestion/Views/detailHistory.dart';
|
||||||
|
import 'package:intl/intl.dart';
|
||||||
|
|
||||||
class HistoryDetailPage extends StatelessWidget {
|
class HistoryDetailPage extends StatelessWidget {
|
||||||
final DateTime selectedDate;
|
final DateTime selectedDate;
|
||||||
@ -112,7 +113,7 @@ class HistoryDetailPage extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
Text(
|
Text(
|
||||||
'Total Somme: $totalSum MGA',
|
'Total Somme: ${NumberFormat('#,##0', 'fr_FR').format(totalSum)} MGA',
|
||||||
style: const TextStyle(
|
style: const TextStyle(
|
||||||
fontSize: 18.0,
|
fontSize: 18.0,
|
||||||
fontWeight: FontWeight.bold,
|
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}'),
|
trailing: Text('Date: ${order.dateTime}'),
|
||||||
leading: Text('vendeur: ${order.user}'),
|
leading: Text('vendeur: ${order.user}'),
|
||||||
onTap: () {
|
onTap: () {
|
||||||
|
|||||||
@ -10,6 +10,7 @@ import 'package:youmazgestion/Models/users.dart';
|
|||||||
import 'package:youmazgestion/Models/produit.dart';
|
import 'package:youmazgestion/Models/produit.dart';
|
||||||
import 'package:youmazgestion/Services/stock_managementDatabase.dart';
|
import 'package:youmazgestion/Services/stock_managementDatabase.dart';
|
||||||
import 'package:youmazgestion/controller/userController.dart';
|
import 'package:youmazgestion/controller/userController.dart';
|
||||||
|
import 'package:intl/intl.dart';
|
||||||
|
|
||||||
class NouvelleCommandePage extends StatefulWidget {
|
class NouvelleCommandePage extends StatefulWidget {
|
||||||
const NouvelleCommandePage({super.key});
|
const NouvelleCommandePage({super.key});
|
||||||
@ -695,7 +696,7 @@ void _modifierQuantite(int productId, int nouvelleQuantite) {
|
|||||||
const SizedBox(height: 12),
|
const SizedBox(height: 12),
|
||||||
|
|
||||||
// Détails du produit en grille
|
// 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'),
|
_buildProductDetailRow('Quantité ajoutée', '$newQuantity'),
|
||||||
|
|
||||||
if (product.imei != null && product.imei!.isNotEmpty)
|
if (product.imei != null && product.imei!.isNotEmpty)
|
||||||
@ -2348,10 +2349,18 @@ Widget _buildRoleBasedHeader() {
|
|||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
// 🎯 MODIFIÉ: Interface produit avec filtrage des produits en rupture ou non assignés
|
||||||
// 🎯 MODIFIÉ: Interface produit avec indication visuelle de la commandabilité
|
|
||||||
Widget _buildProductListItem(Product product, int quantity, bool isMobile) {
|
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 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 detailPanier = _panierDetails[product.id!];
|
||||||
final int currentQuantity = detailPanier?.quantite ?? 0;
|
final int currentQuantity = detailPanier?.quantite ?? 0;
|
||||||
final isCurrentUserPointDeVente = product.pointDeVenteId == _userController.pointDeVenteId;
|
final isCurrentUserPointDeVente = product.pointDeVenteId == _userController.pointDeVenteId;
|
||||||
@ -2385,9 +2394,7 @@ Widget _buildProductListItem(Product product, int quantity, bool isMobile) {
|
|||||||
child: Container(
|
child: Container(
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
borderRadius: BorderRadius.circular(8),
|
borderRadius: BorderRadius.circular(8),
|
||||||
border: isOutOfStock
|
border: detailPanier?.estCadeau == true
|
||||||
? Border.all(color: Colors.red.shade200, width: 1.5)
|
|
||||||
: detailPanier?.estCadeau == true
|
|
||||||
? Border.all(color: Colors.green.shade300, width: 2)
|
? Border.all(color: Colors.green.shade300, width: 2)
|
||||||
: detailPanier?.aRemise == true
|
: detailPanier?.aRemise == true
|
||||||
? Border.all(color: Colors.orange.shade300, width: 2)
|
? Border.all(color: Colors.orange.shade300, width: 2)
|
||||||
@ -2403,14 +2410,13 @@ Widget _buildProductListItem(Product product, int quantity, bool isMobile) {
|
|||||||
children: [
|
children: [
|
||||||
Row(
|
Row(
|
||||||
children: [
|
children: [
|
||||||
|
// ===== ICÔNE PRODUIT =====
|
||||||
Container(
|
Container(
|
||||||
width: isMobile ? 40 : 50,
|
width: isMobile ? 40 : 50,
|
||||||
height: isMobile ? 40 : 50,
|
height: isMobile ? 40 : 50,
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: !isProduitCommandable
|
color: !isProduitCommandable
|
||||||
? Colors.grey.shade100
|
? Colors.grey.shade100
|
||||||
: isOutOfStock
|
|
||||||
? Colors.red.shade50
|
|
||||||
: detailPanier?.estCadeau == true
|
: detailPanier?.estCadeau == true
|
||||||
? Colors.green.shade50
|
? Colors.green.shade50
|
||||||
: detailPanier?.aRemise == true
|
: detailPanier?.aRemise == true
|
||||||
@ -2433,8 +2439,6 @@ Widget _buildProductListItem(Product product, int quantity, bool isMobile) {
|
|||||||
size: isMobile ? 20 : 24,
|
size: isMobile ? 20 : 24,
|
||||||
color: !isProduitCommandable
|
color: !isProduitCommandable
|
||||||
? Colors.grey.shade500
|
? Colors.grey.shade500
|
||||||
: isOutOfStock
|
|
||||||
? Colors.red
|
|
||||||
: detailPanier?.estCadeau == true
|
: detailPanier?.estCadeau == true
|
||||||
? Colors.green.shade700
|
? Colors.green.shade700
|
||||||
: detailPanier?.aRemise == true
|
: detailPanier?.aRemise == true
|
||||||
@ -2445,10 +2449,13 @@ Widget _buildProductListItem(Product product, int quantity, bool isMobile) {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(width: 12),
|
const SizedBox(width: 12),
|
||||||
|
|
||||||
|
// ===== INFORMATIONS PRODUIT =====
|
||||||
Expanded(
|
Expanded(
|
||||||
child: Column(
|
child: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
|
// Nom du produit avec badges de statut
|
||||||
Row(
|
Row(
|
||||||
children: [
|
children: [
|
||||||
Expanded(
|
Expanded(
|
||||||
@ -2459,13 +2466,12 @@ Widget _buildProductListItem(Product product, int quantity, bool isMobile) {
|
|||||||
fontSize: isMobile ? 14 : 16,
|
fontSize: isMobile ? 14 : 16,
|
||||||
color: !isProduitCommandable
|
color: !isProduitCommandable
|
||||||
? Colors.grey.shade600
|
? Colors.grey.shade600
|
||||||
: isOutOfStock
|
|
||||||
? Colors.red.shade700
|
|
||||||
: null,
|
: null,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
// Indicateurs de statut
|
|
||||||
|
// Badge "AUTRE PV" pour produits non commandables
|
||||||
if (!isProduitCommandable && !_isUserSuperAdmin())
|
if (!isProduitCommandable && !_isUserSuperAdmin())
|
||||||
Container(
|
Container(
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 6, vertical: 2),
|
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)
|
if (detailPanier?.estCadeau == true)
|
||||||
Container(
|
Container(
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 6, vertical: 2),
|
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)
|
if (isCurrentUserPointDeVente && detailPanier?.estCadeau != true && isProduitCommandable)
|
||||||
Container(
|
Container(
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 6, vertical: 2),
|
padding: const EdgeInsets.symmetric(horizontal: 6, vertical: 2),
|
||||||
@ -2529,6 +2539,7 @@ Widget _buildProductListItem(Product product, int quantity, bool isMobile) {
|
|||||||
Row(
|
Row(
|
||||||
children: [
|
children: [
|
||||||
if (detailPanier?.estCadeau == true) ...[
|
if (detailPanier?.estCadeau == true) ...[
|
||||||
|
// Prix gratuit pour les cadeaux
|
||||||
Text(
|
Text(
|
||||||
'Gratuit',
|
'Gratuit',
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
@ -2539,7 +2550,7 @@ Widget _buildProductListItem(Product product, int quantity, bool isMobile) {
|
|||||||
),
|
),
|
||||||
const SizedBox(width: 8),
|
const SizedBox(width: 8),
|
||||||
Text(
|
Text(
|
||||||
'${product.price.toStringAsFixed(2)} MGA',
|
'${NumberFormat('#,##0', 'fr_FR').format(product.price)} MGA',
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
color: Colors.grey.shade500,
|
color: Colors.grey.shade500,
|
||||||
fontWeight: FontWeight.w600,
|
fontWeight: FontWeight.w600,
|
||||||
@ -2548,8 +2559,9 @@ Widget _buildProductListItem(Product product, int quantity, bool isMobile) {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
] else ...[
|
] else ...[
|
||||||
|
// Prix normal ou avec remise
|
||||||
Text(
|
Text(
|
||||||
'${product.price.toStringAsFixed(2)} MGA',
|
'${NumberFormat('#,##0', 'fr_FR').format(product.price)} MGA',
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
color: Colors.green.shade700,
|
color: Colors.green.shade700,
|
||||||
fontWeight: FontWeight.w600,
|
fontWeight: FontWeight.w600,
|
||||||
@ -2559,10 +2571,11 @@ Widget _buildProductListItem(Product product, int quantity, bool isMobile) {
|
|||||||
: null,
|
: null,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
// Prix après remise
|
||||||
if (detailPanier?.aRemise == true) ...[
|
if (detailPanier?.aRemise == true) ...[
|
||||||
const SizedBox(width: 8),
|
const SizedBox(width: 8),
|
||||||
Text(
|
Text(
|
||||||
'${(detailPanier!.prixFinal / detailPanier.quantite).toStringAsFixed(2)} MGA',
|
'${NumberFormat('#,##0', 'fr_FR').format(detailPanier!.prixFinal / detailPanier.quantite)} MGA',
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
color: Colors.orange.shade700,
|
color: Colors.orange.shade700,
|
||||||
fontWeight: FontWeight.bold,
|
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)
|
if (detailPanier?.aRemise == true && !detailPanier!.estCadeau)
|
||||||
Text(
|
Text(
|
||||||
'Remise: ${detailPanier!.remiseDescription}',
|
'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)
|
if (product.stock != null)
|
||||||
Text(
|
Text(
|
||||||
'Stock: ${product.stock}${isOutOfStock ? ' (Rupture)' : ''}',
|
'Stock: ${product.stock}',
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
fontSize: isMobile ? 10 : 12,
|
fontSize: isMobile ? 10 : 12,
|
||||||
color: isOutOfStock
|
color: Colors.grey.shade600,
|
||||||
? Colors.red.shade600
|
|
||||||
: Colors.grey.shade600,
|
|
||||||
fontWeight: isOutOfStock ? FontWeight.w600 : FontWeight.normal,
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
||||||
// ===== AFFICHAGE IMEI ET RÉFÉRENCE =====
|
// ===== IMEI ET RÉFÉRENCE =====
|
||||||
if (product.imei != null && product.imei!.isNotEmpty)
|
if (product.imei != null && product.imei!.isNotEmpty)
|
||||||
Text(
|
Text(
|
||||||
'IMEI: ${product.imei}',
|
'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),
|
const SizedBox(height: 4),
|
||||||
Row(
|
Row(
|
||||||
children: [
|
children: [
|
||||||
@ -2680,7 +2690,7 @@ Widget _buildProductListItem(Product product, int quantity, bool isMobile) {
|
|||||||
? Colors.green.shade700
|
? Colors.green.shade700
|
||||||
: Colors.grey.shade600,
|
: Colors.grey.shade600,
|
||||||
),
|
),
|
||||||
onPressed: isOutOfStock ? null : () => _basculerStatutCadeau(product.id!),
|
onPressed: () => _basculerStatutCadeau(product.id!),
|
||||||
tooltip: detailPanier?.estCadeau == true
|
tooltip: detailPanier?.estCadeau == true
|
||||||
? 'Retirer le statut cadeau'
|
? 'Retirer le statut cadeau'
|
||||||
: 'Marquer comme cadeau',
|
: 'Marquer comme cadeau',
|
||||||
@ -2692,6 +2702,7 @@ Widget _buildProductListItem(Product product, int quantity, bool isMobile) {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
||||||
// Bouton remise (seulement pour les articles non-cadeaux)
|
// Bouton remise (seulement pour les articles non-cadeaux)
|
||||||
if (!detailPanier!.estCadeau)
|
if (!detailPanier!.estCadeau)
|
||||||
Container(
|
Container(
|
||||||
@ -2706,7 +2717,7 @@ Widget _buildProductListItem(Product product, int quantity, bool isMobile) {
|
|||||||
? Colors.orange.shade700
|
? Colors.orange.shade700
|
||||||
: Colors.grey.shade600,
|
: Colors.grey.shade600,
|
||||||
),
|
),
|
||||||
onPressed: isOutOfStock ? null : () => _showRemiseDialog(product),
|
onPressed: () => _showRemiseDialog(product),
|
||||||
tooltip: detailPanier.aRemise
|
tooltip: detailPanier.aRemise
|
||||||
? 'Modifier la remise'
|
? 'Modifier la remise'
|
||||||
: 'Ajouter une remise',
|
: 'Ajouter une remise',
|
||||||
@ -2718,6 +2729,7 @@ Widget _buildProductListItem(Product product, int quantity, bool isMobile) {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
||||||
// Bouton pour ajouter un cadeau à un autre produit
|
// Bouton pour ajouter un cadeau à un autre produit
|
||||||
Container(
|
Container(
|
||||||
margin: const EdgeInsets.only(left: 4),
|
margin: const EdgeInsets.only(left: 4),
|
||||||
@ -2727,7 +2739,7 @@ Widget _buildProductListItem(Product product, int quantity, bool isMobile) {
|
|||||||
size: isMobile ? 16 : 18,
|
size: isMobile ? 16 : 18,
|
||||||
color: Colors.green.shade600,
|
color: Colors.green.shade600,
|
||||||
),
|
),
|
||||||
onPressed: isOutOfStock ? null : () => _showCadeauDialog(product),
|
onPressed: () => _showCadeauDialog(product),
|
||||||
tooltip: 'Ajouter un cadeau',
|
tooltip: 'Ajouter un cadeau',
|
||||||
style: IconButton.styleFrom(
|
style: IconButton.styleFrom(
|
||||||
backgroundColor: Colors.green.shade50,
|
backgroundColor: Colors.green.shade50,
|
||||||
@ -2740,13 +2752,11 @@ Widget _buildProductListItem(Product product, int quantity, bool isMobile) {
|
|||||||
const SizedBox(height: 8),
|
const SizedBox(height: 8),
|
||||||
],
|
],
|
||||||
|
|
||||||
// Contrôles de quantité (seulement si commandable)
|
// ===== CONTRÔLES DE QUANTITÉ =====
|
||||||
if (isProduitCommandable)
|
if (isProduitCommandable)
|
||||||
Container(
|
Container(
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: isOutOfStock
|
color: detailPanier?.estCadeau == true
|
||||||
? Colors.grey.shade100
|
|
||||||
: detailPanier?.estCadeau == true
|
|
||||||
? Colors.green.shade50
|
? Colors.green.shade50
|
||||||
: isCurrentUserPointDeVente
|
: isCurrentUserPointDeVente
|
||||||
? Colors.orange.shade50
|
? Colors.orange.shade50
|
||||||
@ -2756,14 +2766,16 @@ Widget _buildProductListItem(Product product, int quantity, bool isMobile) {
|
|||||||
child: Row(
|
child: Row(
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
children: [
|
children: [
|
||||||
|
// Bouton diminuer
|
||||||
IconButton(
|
IconButton(
|
||||||
icon: Icon(Icons.remove, size: isMobile ? 16 : 18),
|
icon: Icon(Icons.remove, size: isMobile ? 16 : 18),
|
||||||
onPressed: isOutOfStock ? null : () {
|
onPressed: () {
|
||||||
if (currentQuantity > 0) {
|
if (currentQuantity > 0) {
|
||||||
_modifierQuantite(product.id!, currentQuantity - 1);
|
_modifierQuantite(product.id!, currentQuantity - 1);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
// Quantité actuelle
|
||||||
Text(
|
Text(
|
||||||
currentQuantity.toString(),
|
currentQuantity.toString(),
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
@ -2771,9 +2783,10 @@ Widget _buildProductListItem(Product product, int quantity, bool isMobile) {
|
|||||||
fontSize: isMobile ? 12 : 14,
|
fontSize: isMobile ? 12 : 14,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
// Bouton augmenter
|
||||||
IconButton(
|
IconButton(
|
||||||
icon: Icon(Icons.add, size: isMobile ? 16 : 18),
|
icon: Icon(Icons.add, size: isMobile ? 16 : 18),
|
||||||
onPressed: isOutOfStock ? null : () {
|
onPressed: () {
|
||||||
if (product.stock == null || currentQuantity < product.stock!) {
|
if (product.stock == null || currentQuantity < product.stock!) {
|
||||||
if (currentQuantity == 0) {
|
if (currentQuantity == 0) {
|
||||||
_ajouterAuPanier(product, 1);
|
_ajouterAuPanier(product, 1);
|
||||||
@ -2795,7 +2808,7 @@ Widget _buildProductListItem(Product product, int quantity, bool isMobile) {
|
|||||||
),
|
),
|
||||||
)
|
)
|
||||||
else
|
else
|
||||||
// Message informatif pour produits non-commandables
|
// ===== SECTION PRODUITS NON-COMMANDABLES =====
|
||||||
Container(
|
Container(
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8),
|
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8),
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
@ -2805,6 +2818,7 @@ Widget _buildProductListItem(Product product, int quantity, bool isMobile) {
|
|||||||
),
|
),
|
||||||
child: Column(
|
child: Column(
|
||||||
children: [
|
children: [
|
||||||
|
// Indicateur de consultation
|
||||||
Row(
|
Row(
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
children: [
|
children: [
|
||||||
@ -2821,27 +2835,17 @@ Widget _buildProductListItem(Product product, int quantity, bool isMobile) {
|
|||||||
],
|
],
|
||||||
),
|
),
|
||||||
const SizedBox(height: 4),
|
const SizedBox(height: 4),
|
||||||
|
|
||||||
|
// Bouton demande de transfert
|
||||||
ElevatedButton.icon(
|
ElevatedButton.icon(
|
||||||
icon: const Icon(Icons.swap_horiz, size: 14),
|
icon: const Icon(Icons.swap_horiz, size: 14),
|
||||||
label:!isMobile ? const Text('Demander transfertt'):const SizedBox.shrink(),
|
label: !isMobile ? const Text('Demander transfert') : const SizedBox.shrink(),
|
||||||
style: ElevatedButton.styleFrom(
|
style: ElevatedButton.styleFrom(
|
||||||
backgroundColor: (product.stock != null && product.stock! >= 1)
|
backgroundColor: Colors.blue.shade700,
|
||||||
? Colors.blue.shade700
|
|
||||||
: Colors.grey.shade400,
|
|
||||||
foregroundColor: Colors.white,
|
foregroundColor: Colors.white,
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4),
|
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4),
|
||||||
),
|
),
|
||||||
onPressed: (product.stock != null && product.stock! >= 1)
|
onPressed: () => _showDemandeTransfertDialog(product),
|
||||||
? () => _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,
|
|
||||||
);
|
|
||||||
},
|
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
@ -2989,7 +2993,7 @@ Future<void> _showDemandeTransfertDialog(Product product) async {
|
|||||||
Expanded(
|
Expanded(
|
||||||
child: _buildInfoCard(
|
child: _buildInfoCard(
|
||||||
'Prix unitaire',
|
'Prix unitaire',
|
||||||
'${product.price.toStringAsFixed(2)} MGA',
|
'${NumberFormat('#,##0', 'fr_FR').format(product.price)} MGA',
|
||||||
Icons.attach_money,
|
Icons.attach_money,
|
||||||
Colors.green,
|
Colors.green,
|
||||||
),
|
),
|
||||||
@ -3513,7 +3517,7 @@ Widget _buildTransferStep(String label, String pointDeVente, IconData icon, Colo
|
|||||||
),
|
),
|
||||||
const SizedBox(width: 8),
|
const SizedBox(width: 8),
|
||||||
Text(
|
Text(
|
||||||
'(${detail.prixUnitaire.toStringAsFixed(2)} MGA)',
|
'(${NumberFormat('#,##0', 'fr_FR').format(detail.prixUnitaire)} MGA)',
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
fontSize: 11,
|
fontSize: 11,
|
||||||
color: Colors.grey.shade500,
|
color: Colors.grey.shade500,
|
||||||
@ -3530,19 +3534,19 @@ Widget _buildTransferStep(String label, String pointDeVente, IconData icon, Colo
|
|||||||
),
|
),
|
||||||
const Text(' → '),
|
const Text(' → '),
|
||||||
Text(
|
Text(
|
||||||
'${(detail.prixFinal / detail.quantite).toStringAsFixed(2)} MGA',
|
'${NumberFormat('#,##0', 'fr_FR').format(detail.prixFinal / detail.quantite)} MGA',
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
color: Colors.orange.shade700,
|
color: Colors.orange.shade700,
|
||||||
fontWeight: FontWeight.bold,
|
fontWeight: FontWeight.bold,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
] else
|
] else
|
||||||
Text('${detail.prixUnitaire.toStringAsFixed(2)} MGA'),
|
Text('${NumberFormat('#,##0', 'fr_FR').format(detail.prixUnitaire)} MGA'),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
if (detail.aRemise && !detail.estCadeau)
|
if (detail.aRemise && !detail.estCadeau)
|
||||||
Text(
|
Text(
|
||||||
'Remise: ${detail.remiseDescription} (-${detail.montantRemise.toStringAsFixed(2)} MGA)',
|
'Remise: ${detail.remiseDescription} (-${NumberFormat('#,##0', 'fr_FR').format(detail.montantRemise)} MGA)',
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
fontSize: 11,
|
fontSize: 11,
|
||||||
color: Colors.orange.shade600,
|
color: Colors.orange.shade600,
|
||||||
@ -3595,7 +3599,7 @@ Widget _buildTransferStep(String label, String pointDeVente, IconData icon, Colo
|
|||||||
],
|
],
|
||||||
),
|
),
|
||||||
Text(
|
Text(
|
||||||
'Valeur: ${detail.sousTotal.toStringAsFixed(2)} MGA',
|
'Valeur: ${NumberFormat('#,##0', 'fr_FR').format(detail.sousTotal)} MGA',
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
fontSize: 10,
|
fontSize: 10,
|
||||||
color: Colors.grey.shade500,
|
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) ...[
|
] else if (detail.aRemise && detail.sousTotal != detail.prixFinal) ...[
|
||||||
Text(
|
Text(
|
||||||
'${detail.sousTotal.toStringAsFixed(2)} MGA',
|
'${NumberFormat('#,##0', 'fr_FR').format(detail.sousTotal)} MGA',
|
||||||
style: const TextStyle(
|
style: const TextStyle(
|
||||||
fontSize: 11,
|
fontSize: 11,
|
||||||
decoration: TextDecoration.lineThrough,
|
decoration: TextDecoration.lineThrough,
|
||||||
@ -3612,7 +3616,7 @@ Widget _buildTransferStep(String label, String pointDeVente, IconData icon, Colo
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
Text(
|
Text(
|
||||||
'${detail.prixFinal.toStringAsFixed(2)} MGA',
|
'${NumberFormat('#,##0', 'fr_FR').format(detail.prixFinal)} MGA',
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
fontWeight: FontWeight.bold,
|
fontWeight: FontWeight.bold,
|
||||||
color: Colors.orange.shade700,
|
color: Colors.orange.shade700,
|
||||||
@ -3621,7 +3625,7 @@ Widget _buildTransferStep(String label, String pointDeVente, IconData icon, Colo
|
|||||||
),
|
),
|
||||||
] else
|
] else
|
||||||
Text(
|
Text(
|
||||||
'${detail.prixFinal.toStringAsFixed(2)} MGA',
|
'${NumberFormat('#,##0', 'fr_FR').format(detail.prixFinal)} MGA',
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
fontWeight: FontWeight.bold,
|
fontWeight: FontWeight.bold,
|
||||||
color: Colors.blue.shade800,
|
color: Colors.blue.shade800,
|
||||||
@ -3673,7 +3677,7 @@ Widget _buildTransferStep(String label, String pointDeVente, IconData icon, Colo
|
|||||||
children: [
|
children: [
|
||||||
const Text('Sous-total:', style: TextStyle(fontSize: 14)),
|
const Text('Sous-total:', style: TextStyle(fontSize: 14)),
|
||||||
Text(
|
Text(
|
||||||
'${sousTotal.toStringAsFixed(2)} MGA',
|
'${NumberFormat('#,##0', 'fr_FR').format(sousTotal)} MGA',
|
||||||
style: const TextStyle(fontSize: 14),
|
style: const TextStyle(fontSize: 14),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
@ -3694,7 +3698,7 @@ Widget _buildTransferStep(String label, String pointDeVente, IconData icon, Colo
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
Text(
|
Text(
|
||||||
'-${totalRemises.toStringAsFixed(2)} MGA',
|
'-${NumberFormat('#,##0', 'fr_FR').format(totalRemises)} MGA',
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
fontSize: 14,
|
fontSize: 14,
|
||||||
color: Colors.orange.shade700,
|
color: Colors.orange.shade700,
|
||||||
@ -3729,7 +3733,7 @@ Widget _buildTransferStep(String label, String pointDeVente, IconData icon, Colo
|
|||||||
],
|
],
|
||||||
),
|
),
|
||||||
Text(
|
Text(
|
||||||
'-${totalCadeaux.toStringAsFixed(2)} MGA',
|
'-${NumberFormat('#,##0', 'fr_FR').format(totalCadeaux)} MGA',
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
fontSize: 14,
|
fontSize: 14,
|
||||||
color: Colors.green.shade700,
|
color: Colors.green.shade700,
|
||||||
@ -3753,7 +3757,7 @@ Widget _buildTransferStep(String label, String pointDeVente, IconData icon, Colo
|
|||||||
style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
|
style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
|
||||||
),
|
),
|
||||||
Text(
|
Text(
|
||||||
'${total.toStringAsFixed(2)} MGA',
|
'${NumberFormat('#,##0', 'fr_FR').format(total)} MGA',
|
||||||
style: const TextStyle(
|
style: const TextStyle(
|
||||||
fontSize: 18,
|
fontSize: 18,
|
||||||
fontWeight: FontWeight.bold,
|
fontWeight: FontWeight.bold,
|
||||||
@ -3791,7 +3795,7 @@ Widget _buildTransferStep(String label, String pointDeVente, IconData icon, Colo
|
|||||||
),
|
),
|
||||||
const SizedBox(width: 4),
|
const SizedBox(width: 4),
|
||||||
Text(
|
Text(
|
||||||
'Économies totales: ${(totalRemises + totalCadeaux).toStringAsFixed(2)} MGA',
|
'Économies totales: ${NumberFormat('#,##0', 'fr_FR').format(totalRemises + totalCadeaux)} MGA',
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
color: Colors.green.shade700,
|
color: Colors.green.shade700,
|
||||||
fontWeight: FontWeight.bold,
|
fontWeight: FontWeight.bold,
|
||||||
@ -3807,7 +3811,7 @@ Widget _buildTransferStep(String label, String pointDeVente, IconData icon, Colo
|
|||||||
if (totalRemises > 0 && totalCadeaux > 0) ...[
|
if (totalRemises > 0 && totalCadeaux > 0) ...[
|
||||||
const SizedBox(height: 4),
|
const SizedBox(height: 4),
|
||||||
Text(
|
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(
|
style: TextStyle(
|
||||||
color: Colors.grey.shade600,
|
color: Colors.grey.shade600,
|
||||||
fontSize: 11,
|
fontSize: 11,
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'dart:io';
|
import 'dart:io';
|
||||||
import 'package:youmazgestion/Models/produit.dart';
|
import 'package:youmazgestion/Models/produit.dart';
|
||||||
|
import 'package:intl/intl.dart';
|
||||||
|
|
||||||
class ProductCard extends StatefulWidget {
|
class ProductCard extends StatefulWidget {
|
||||||
final Product product;
|
final Product product;
|
||||||
@ -221,7 +222,7 @@ class _ProductCardState extends State<ProductCard>
|
|||||||
),
|
),
|
||||||
const SizedBox(height: 4),
|
const SizedBox(height: 4),
|
||||||
Text(
|
Text(
|
||||||
'${widget.product.price.toStringAsFixed(2)} MGA',
|
'${NumberFormat('#,##0', 'fr_FR').format(widget.product.price)} MGA',
|
||||||
style: const TextStyle(
|
style: const TextStyle(
|
||||||
fontWeight: FontWeight.bold,
|
fontWeight: FontWeight.bold,
|
||||||
fontSize: 13,
|
fontSize: 13,
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
import 'dart:io';
|
import 'dart:io';
|
||||||
|
import 'package:intl/intl.dart';
|
||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
@ -67,8 +67,8 @@ class TicketPage extends StatelessWidget {
|
|||||||
return [
|
return [
|
||||||
product.name,
|
product.name,
|
||||||
quantity.toString(),
|
quantity.toString(),
|
||||||
'${product.price.toStringAsFixed(2)} MGA',
|
'${NumberFormat('#,##0.00', 'fr_FR').format(product.price)} MGA',
|
||||||
'${productTotal.toStringAsFixed(2)} MGA',
|
'${NumberFormat('#,##0.00', 'fr_FR').format(productTotal)} MGA',
|
||||||
];
|
];
|
||||||
}).toList(),
|
}).toList(),
|
||||||
],
|
],
|
||||||
@ -82,7 +82,7 @@ class TicketPage extends StatelessWidget {
|
|||||||
pw.Text('Total :',
|
pw.Text('Total :',
|
||||||
style: pw.TextStyle(
|
style: pw.TextStyle(
|
||||||
fontSize: 18, fontWeight: pw.FontWeight.bold)),
|
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(
|
style: pw.TextStyle(
|
||||||
fontSize: 18, fontWeight: pw.FontWeight.bold)),
|
fontSize: 18, fontWeight: pw.FontWeight.bold)),
|
||||||
],
|
],
|
||||||
@ -95,7 +95,7 @@ class TicketPage extends StatelessWidget {
|
|||||||
children: [
|
children: [
|
||||||
pw.Text('Somme remise :',
|
pw.Text('Somme remise :',
|
||||||
style: const pw.TextStyle(fontSize: 16)),
|
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)),
|
style: const pw.TextStyle(fontSize: 16)),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
@ -106,7 +106,7 @@ class TicketPage extends StatelessWidget {
|
|||||||
pw.Text('Somme rendue :',
|
pw.Text('Somme rendue :',
|
||||||
style: const pw.TextStyle(fontSize: 16)),
|
style: const pw.TextStyle(fontSize: 16)),
|
||||||
pw.Text(
|
pw.Text(
|
||||||
'${(amountPaid - totalCartPrice).toStringAsFixed(2)} MGA',
|
'${NumberFormat('#,##0.00', 'fr_FR').format(amountPaid - totalCartPrice)} MGA',
|
||||||
style: const pw.TextStyle(fontSize: 16)),
|
style: const pw.TextStyle(fontSize: 16)),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
@ -271,14 +271,14 @@ class TicketPage extends StatelessWidget {
|
|||||||
TableCell(
|
TableCell(
|
||||||
child: Center(
|
child: Center(
|
||||||
child: Text(
|
child: Text(
|
||||||
'${product.price.toStringAsFixed(2)} MGA',
|
'${NumberFormat('#,##0.00', 'fr_FR').format(product.price)} MGA',
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
TableCell(
|
TableCell(
|
||||||
child: Center(
|
child: Center(
|
||||||
child: Text(
|
child: Text(
|
||||||
'${productTotal.toStringAsFixed(2)} MGA',
|
'${NumberFormat('#,##0.00', 'fr_FR').format(productTotal)} MGA',
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@ -305,7 +305,7 @@ class TicketPage extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
Text(
|
Text(
|
||||||
'${totalOrderAmount.toStringAsFixed(2)} MGA',
|
'${NumberFormat('#,##0.00', 'fr_FR').format(totalOrderAmount)} MGA',
|
||||||
style: const TextStyle(
|
style: const TextStyle(
|
||||||
fontSize: 18,
|
fontSize: 18,
|
||||||
fontWeight: FontWeight.bold,
|
fontWeight: FontWeight.bold,
|
||||||
@ -330,7 +330,7 @@ class TicketPage extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
Text(
|
Text(
|
||||||
'${amountPaid.toStringAsFixed(2)} MGA',
|
'${NumberFormat('#,##0.00', 'fr_FR').format(amountPaid)} MGA',
|
||||||
style: const TextStyle(
|
style: const TextStyle(
|
||||||
fontSize: 16,
|
fontSize: 16,
|
||||||
),
|
),
|
||||||
@ -348,7 +348,7 @@ class TicketPage extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
Text(
|
Text(
|
||||||
'${change.toStringAsFixed(2)} MGA',
|
'${NumberFormat('#,##0.00', 'fr_FR').format(change)} MGA',
|
||||||
style: const TextStyle(
|
style: const TextStyle(
|
||||||
fontSize: 16,
|
fontSize: 16,
|
||||||
),
|
),
|
||||||
|
|||||||
@ -5,7 +5,7 @@ import 'dart:async';
|
|||||||
|
|
||||||
class DatabaseConfig {
|
class DatabaseConfig {
|
||||||
// Local MySQL settings
|
// 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 localUsername = 'guycom';
|
||||||
static const String? localPassword = '3iV59wjRdbuXAPR';
|
static const String? localPassword = '3iV59wjRdbuXAPR';
|
||||||
static const String localDatabase = 'guycom';
|
static const String localDatabase = 'guycom';
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user