modification 2026
This commit is contained in:
parent
865410ae93
commit
58154af680
@ -1,5 +1,3 @@
|
||||
// Remplacez complètement votre fichier CommandeDetails par celui-ci :
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:youmazgestion/Models/client.dart';
|
||||
import 'package:youmazgestion/Services/stock_managementDatabase.dart';
|
||||
@ -24,7 +22,8 @@ class CommandeDetails extends StatelessWidget {
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildTableCell(String text, {bool isAmount = false, Color? textColor}) {
|
||||
Widget _buildTableCell(String text,
|
||||
{bool isAmount = false, Color? textColor}) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: Text(
|
||||
@ -66,7 +65,9 @@ class CommandeDetails extends StatelessWidget {
|
||||
),
|
||||
);
|
||||
} else {
|
||||
return _buildTableCell('${NumberFormat('#,##0', 'fr_FR').format(detail.prixUnitaire)} MGA', isAmount: true);
|
||||
return _buildTableCell(
|
||||
'${NumberFormat('#,##0', 'fr_FR').format(detail.prixUnitaire)} MGA',
|
||||
isAmount: true);
|
||||
}
|
||||
}
|
||||
|
||||
@ -130,7 +131,9 @@ class CommandeDetails extends StatelessWidget {
|
||||
),
|
||||
);
|
||||
} else {
|
||||
return _buildTableCell('${NumberFormat('#,##0', 'fr_FR').format(detail.prixFinal)} MGA', isAmount: true);
|
||||
return _buildTableCell(
|
||||
'${NumberFormat('#,##0', 'fr_FR').format(detail.prixFinal)} MGA',
|
||||
isAmount: true);
|
||||
}
|
||||
}
|
||||
|
||||
@ -148,13 +151,13 @@ class CommandeDetails extends StatelessWidget {
|
||||
}
|
||||
|
||||
final details = snapshot.data!;
|
||||
|
||||
|
||||
// Calculer les totaux
|
||||
double sousTotal = 0;
|
||||
double totalRemises = 0;
|
||||
double totalFinal = 0;
|
||||
bool hasRemises = false;
|
||||
|
||||
|
||||
for (final detail in details) {
|
||||
sousTotal += detail.sousTotal;
|
||||
totalRemises += detail.montantRemise;
|
||||
@ -170,7 +173,7 @@ class CommandeDetails extends StatelessWidget {
|
||||
decoration: BoxDecoration(
|
||||
color: hasRemises ? Colors.orange.shade50 : Colors.blue.shade50,
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
border: hasRemises
|
||||
border: hasRemises
|
||||
? Border.all(color: Colors.orange.shade200)
|
||||
: null,
|
||||
),
|
||||
@ -178,21 +181,27 @@ class CommandeDetails extends StatelessWidget {
|
||||
children: [
|
||||
Icon(
|
||||
hasRemises ? Icons.discount : Icons.receipt_long,
|
||||
color: hasRemises ? Colors.orange.shade700 : Colors.blue.shade700,
|
||||
color: hasRemises
|
||||
? Colors.orange.shade700
|
||||
: Colors.blue.shade700,
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
Text(
|
||||
hasRemises ? 'Détails de la commande (avec remises)' : 'Détails de la commande',
|
||||
hasRemises
|
||||
? 'Détails de la commande (avec remises)'
|
||||
: 'Détails de la commande',
|
||||
style: TextStyle(
|
||||
fontWeight: FontWeight.bold,
|
||||
fontSize: 16,
|
||||
color: hasRemises ? Colors.orange.shade800 : Colors.black87,
|
||||
color:
|
||||
hasRemises ? Colors.orange.shade800 : Colors.black87,
|
||||
),
|
||||
),
|
||||
if (hasRemises) ...[
|
||||
const Spacer(),
|
||||
Container(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4),
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: 8, vertical: 4),
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.orange.shade100,
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
@ -231,65 +240,76 @@ class CommandeDetails extends StatelessWidget {
|
||||
],
|
||||
),
|
||||
...details.map((detail) => TableRow(
|
||||
decoration: detail.aRemise
|
||||
? BoxDecoration(
|
||||
color: const Color.fromARGB(255, 243, 191, 114),
|
||||
border: Border(
|
||||
left: BorderSide(
|
||||
color: Colors.orange.shade300,
|
||||
width: 3,
|
||||
),
|
||||
),
|
||||
)
|
||||
: null,
|
||||
children: [
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
detail.produitNom ?? 'Produit inconnu',
|
||||
style: const TextStyle(
|
||||
fontSize: 13,
|
||||
fontWeight: FontWeight.w500,
|
||||
),
|
||||
),
|
||||
if (detail.aRemise) ...[
|
||||
const SizedBox(height: 2),
|
||||
Row(
|
||||
children: [
|
||||
Icon(
|
||||
Icons.local_offer,
|
||||
size: 12,
|
||||
color: Colors.teal.shade700,
|
||||
decoration: detail.aRemise
|
||||
? BoxDecoration(
|
||||
color: const Color.fromARGB(255, 243, 191, 114),
|
||||
border: Border(
|
||||
left: BorderSide(
|
||||
color: Colors.orange.shade300,
|
||||
width: 3,
|
||||
),
|
||||
const SizedBox(width: 4),
|
||||
),
|
||||
)
|
||||
: null,
|
||||
children: [
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
detail.produitNom ?? 'Produit inconnu',
|
||||
style: const TextStyle(
|
||||
fontSize: 13,
|
||||
fontWeight: FontWeight.w500,
|
||||
),
|
||||
),
|
||||
if (detail.produitImei != null) ...[
|
||||
const SizedBox(height: 2),
|
||||
Text(
|
||||
'Avec remise',
|
||||
style: TextStyle(
|
||||
'IMEI: ${detail.produitImei}',
|
||||
style: const TextStyle(
|
||||
fontSize: 10,
|
||||
color: Colors.teal.shade700,
|
||||
color: Colors.grey,
|
||||
fontStyle: FontStyle.italic,
|
||||
),
|
||||
)
|
||||
],
|
||||
if (detail.aRemise) ...[
|
||||
const SizedBox(height: 2),
|
||||
Row(
|
||||
children: [
|
||||
Icon(
|
||||
Icons.local_offer,
|
||||
size: 12,
|
||||
color: Colors.teal.shade700,
|
||||
),
|
||||
const SizedBox(width: 4),
|
||||
Text(
|
||||
'Avec remise',
|
||||
style: TextStyle(
|
||||
fontSize: 10,
|
||||
color: Colors.teal.shade700,
|
||||
fontStyle: FontStyle.italic,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
],
|
||||
),
|
||||
),
|
||||
_buildTableCell('${detail.quantite}'),
|
||||
_buildPriceColumn(detail),
|
||||
if (hasRemises) _buildRemiseColumn(detail),
|
||||
_buildTotalColumn(detail),
|
||||
],
|
||||
)),
|
||||
],
|
||||
),
|
||||
),
|
||||
_buildTableCell('${detail.quantite}'),
|
||||
_buildPriceColumn(detail),
|
||||
if (hasRemises) _buildRemiseColumn(detail),
|
||||
_buildTotalColumn(detail),
|
||||
],
|
||||
)),
|
||||
],
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
|
||||
|
||||
// Section des totaux
|
||||
Container(
|
||||
padding: const EdgeInsets.all(12),
|
||||
@ -355,7 +375,7 @@ class CommandeDetails extends StatelessWidget {
|
||||
),
|
||||
const Divider(height: 16),
|
||||
],
|
||||
|
||||
|
||||
// Total final
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
@ -385,4 +405,4 @@ class CommandeDetails extends StatelessWidget {
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -36,9 +36,9 @@ class Client {
|
||||
// Fonction helper améliorée pour parser les dates
|
||||
static DateTime _parseDateTime(dynamic dateValue) {
|
||||
if (dateValue == null) return DateTime.now();
|
||||
|
||||
|
||||
if (dateValue is DateTime) return dateValue;
|
||||
|
||||
|
||||
if (dateValue is String) {
|
||||
try {
|
||||
return DateTime.parse(dateValue);
|
||||
@ -47,13 +47,14 @@ class Client {
|
||||
return DateTime.now();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Pour MySQL qui peut retourner un Timestamp
|
||||
if (dateValue is int) {
|
||||
return DateTime.fromMillisecondsSinceEpoch(dateValue);
|
||||
}
|
||||
|
||||
print("Type de date non reconnu: ${dateValue.runtimeType}, valeur: $dateValue");
|
||||
|
||||
print(
|
||||
"Type de date non reconnu: ${dateValue.runtimeType}, valeur: $dateValue");
|
||||
return DateTime.now();
|
||||
}
|
||||
|
||||
@ -73,11 +74,7 @@ class Client {
|
||||
String get nomComplet => '$prenom $nom';
|
||||
}
|
||||
|
||||
enum StatutCommande {
|
||||
enAttente,
|
||||
confirmee,
|
||||
annulee
|
||||
}
|
||||
enum StatutCommande { enAttente, confirmee, annulee }
|
||||
|
||||
class Commande {
|
||||
final int? id;
|
||||
@ -156,7 +153,7 @@ class Commande {
|
||||
statut: StatutCommande.values[(map['statut'] as int)],
|
||||
montantTotal: (map['montantTotal'] as num).toDouble(),
|
||||
notes: map['notes'] as String?,
|
||||
dateLivraison: map['dateLivraison'] != null
|
||||
dateLivraison: map['dateLivraison'] != null
|
||||
? Client._parseDateTime(map['dateLivraison'])
|
||||
: null,
|
||||
commandeurId: map['commandeurId'] as int?,
|
||||
@ -173,10 +170,7 @@ class Commande {
|
||||
}
|
||||
|
||||
// REMPLACEZ COMPLÈTEMENT votre classe DetailCommande dans Models/client.dart par celle-ci :
|
||||
enum RemiseType {
|
||||
pourcentage,
|
||||
montant
|
||||
}
|
||||
enum RemiseType { pourcentage, montant }
|
||||
|
||||
class DetailCommande {
|
||||
final int? id;
|
||||
@ -193,6 +187,7 @@ class DetailCommande {
|
||||
final String? produitNom;
|
||||
final String? produitImage;
|
||||
final String? produitReference;
|
||||
final String? produitImei; // NOUVEAU : IMEI du produit, si applicable
|
||||
|
||||
DetailCommande({
|
||||
this.id,
|
||||
@ -209,6 +204,7 @@ class DetailCommande {
|
||||
this.produitNom,
|
||||
this.produitImage,
|
||||
this.produitReference,
|
||||
this.produitImei,
|
||||
});
|
||||
|
||||
// Constructeur pour créer un détail sans remise
|
||||
@ -222,10 +218,11 @@ class DetailCommande {
|
||||
String? produitNom,
|
||||
String? produitImage,
|
||||
String? produitReference,
|
||||
String? produitImei,
|
||||
}) {
|
||||
final sousTotal = quantite * prixUnitaire;
|
||||
final prixFinal = estCadeau ? 0.0 : sousTotal;
|
||||
|
||||
|
||||
return DetailCommande(
|
||||
id: id,
|
||||
commandeId: commandeId,
|
||||
@ -238,6 +235,7 @@ class DetailCommande {
|
||||
produitNom: produitNom,
|
||||
produitImage: produitImage,
|
||||
produitReference: produitReference,
|
||||
produitImei: produitImei,
|
||||
);
|
||||
}
|
||||
|
||||
@ -251,6 +249,7 @@ class DetailCommande {
|
||||
String? produitNom,
|
||||
String? produitImage,
|
||||
String? produitReference,
|
||||
String? produitImei,
|
||||
}) {
|
||||
return DetailCommande(
|
||||
id: id,
|
||||
@ -264,6 +263,7 @@ class DetailCommande {
|
||||
produitNom: produitNom,
|
||||
produitImage: produitImage,
|
||||
produitReference: produitReference,
|
||||
produitImei: produitImei,
|
||||
);
|
||||
}
|
||||
|
||||
@ -274,18 +274,18 @@ class DetailCommande {
|
||||
}) {
|
||||
// Les remises ne s'appliquent pas aux cadeaux
|
||||
if (estCadeau) return this;
|
||||
|
||||
|
||||
double montantRemiseCalcule = 0.0;
|
||||
|
||||
|
||||
if (type == RemiseType.pourcentage) {
|
||||
final pourcentage = valeur.clamp(0.0, 100.0);
|
||||
montantRemiseCalcule = sousTotal * (pourcentage / 100);
|
||||
} else {
|
||||
montantRemiseCalcule = valeur.clamp(0.0, sousTotal);
|
||||
}
|
||||
|
||||
|
||||
final prixFinalCalcule = sousTotal - montantRemiseCalcule;
|
||||
|
||||
|
||||
return DetailCommande(
|
||||
id: id,
|
||||
commandeId: commandeId,
|
||||
@ -301,6 +301,7 @@ class DetailCommande {
|
||||
produitNom: produitNom,
|
||||
produitImage: produitImage,
|
||||
produitReference: produitReference,
|
||||
produitImei: produitImei,
|
||||
);
|
||||
}
|
||||
|
||||
@ -321,6 +322,7 @@ class DetailCommande {
|
||||
produitNom: produitNom,
|
||||
produitImage: produitImage,
|
||||
produitReference: produitReference,
|
||||
produitImei: produitImei,
|
||||
);
|
||||
}
|
||||
|
||||
@ -341,6 +343,7 @@ class DetailCommande {
|
||||
produitNom: produitNom,
|
||||
produitImage: produitImage,
|
||||
produitReference: produitReference,
|
||||
produitImei: produitImei,
|
||||
);
|
||||
}
|
||||
|
||||
@ -361,12 +364,14 @@ class DetailCommande {
|
||||
produitNom: produitNom,
|
||||
produitImage: produitImage,
|
||||
produitReference: produitReference,
|
||||
produitImei: produitImei,
|
||||
);
|
||||
}
|
||||
|
||||
// Getters utiles
|
||||
bool get aRemise => remiseType != null && montantRemise > 0 && !estCadeau;
|
||||
|
||||
bool get aimei => produitImei != null;
|
||||
|
||||
double get pourcentageRemise {
|
||||
if (!aRemise) return 0.0;
|
||||
return (montantRemise / sousTotal) * 100;
|
||||
@ -375,7 +380,7 @@ class DetailCommande {
|
||||
String get remiseDescription {
|
||||
if (estCadeau) return 'CADEAU';
|
||||
if (!aRemise) return '';
|
||||
|
||||
|
||||
if (remiseType == RemiseType.pourcentage) {
|
||||
return '-${remiseValeur.toStringAsFixed(0)}%';
|
||||
} else {
|
||||
@ -426,12 +431,13 @@ class DetailCommande {
|
||||
remiseType: type,
|
||||
remiseValeur: (map['remise_valeur'] as num?)?.toDouble() ?? 0.0,
|
||||
montantRemise: (map['montant_remise'] as num?)?.toDouble() ?? 0.0,
|
||||
prixFinal: (map['prix_final'] as num?)?.toDouble() ??
|
||||
(map['sousTotal'] as num).toDouble(),
|
||||
prixFinal: (map['prix_final'] as num?)?.toDouble() ??
|
||||
(map['sousTotal'] as num).toDouble(),
|
||||
estCadeau: (map['est_cadeau'] as int?) == 1,
|
||||
produitNom: map['produitNom'] as String?,
|
||||
produitImage: map['produitImage'] as String?,
|
||||
produitReference: map['produitReference'] as String?,
|
||||
produitImei: map['produitImei'] as String?,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -92,7 +92,9 @@ class _HistoriquePageState extends State<HistoriquePage> {
|
||||
final pointDeVenteId = _userController.pointDeVenteId;
|
||||
final commandes = pointDeVenteId == 0
|
||||
? allCommandes
|
||||
: allCommandes.where((cmd) => cmd.pointDeVenteId == pointDeVenteId).toList();
|
||||
: allCommandes
|
||||
.where((cmd) => cmd.pointDeVenteId == pointDeVenteId)
|
||||
.toList();
|
||||
|
||||
setState(() {
|
||||
_commandes.clear();
|
||||
@ -146,7 +148,8 @@ class _HistoriquePageState extends State<HistoriquePage> {
|
||||
_filteredCommandes.clear();
|
||||
|
||||
for (var commande in _commandes) {
|
||||
if (pointDeVenteId != 0 && commande.pointDeVenteId != pointDeVenteId) continue;
|
||||
if (pointDeVenteId != 0 && commande.pointDeVenteId != pointDeVenteId)
|
||||
continue;
|
||||
bool matchesSearch = searchText.isEmpty ||
|
||||
commande.clientNom!.toLowerCase().contains(searchText) ||
|
||||
commande.clientPrenom!.toLowerCase().contains(searchText) ||
|
||||
@ -641,10 +644,25 @@ class _HistoriquePageState extends State<HistoriquePage> {
|
||||
),
|
||||
child: const Icon(Icons.shopping_bag, size: 20),
|
||||
),
|
||||
title: Text(
|
||||
detail.produitNom ?? 'Produit inconnu',
|
||||
style:
|
||||
const TextStyle(fontWeight: FontWeight.w500),
|
||||
title: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
detail.produitNom ?? 'Produit inconnu',
|
||||
style: const TextStyle(
|
||||
fontWeight: FontWeight.w500),
|
||||
),
|
||||
if (detail.produitImei != null) ...[
|
||||
const SizedBox(height: 4),
|
||||
Text(
|
||||
'IMEI: ${detail.produitImei}',
|
||||
style: TextStyle(
|
||||
fontSize: 12,
|
||||
color: Colors.grey.shade600,
|
||||
),
|
||||
),
|
||||
],
|
||||
],
|
||||
),
|
||||
subtitle: Text(
|
||||
'${detail.quantite} x ${NumberFormat('#,##0.00', 'fr_FR').format(detail.prixUnitaire)} MGA',
|
||||
|
||||
Loading…
Reference in New Issue
Block a user