Browse Source

modification 2026

28062025_02
Stephane 3 weeks ago
parent
commit
58154af680
  1. 144
      lib/Components/commandManagementComponents/CommandDetails.dart
  2. 56
      lib/Models/Client.dart
  3. 1607
      lib/Services/stock_managementDatabase.dart
  4. 1482
      lib/Views/commandManagement.dart
  5. 30
      lib/Views/historique.dart

144
lib/Components/commandManagementComponents/CommandDetails.dart

@ -1,5 +1,3 @@
// Remplacez complètement votre fichier CommandeDetails par celui-ci :
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:youmazgestion/Models/client.dart'; import 'package:youmazgestion/Models/client.dart';
import 'package:youmazgestion/Services/stock_managementDatabase.dart'; import 'package:youmazgestion/Services/stock_managementDatabase.dart';
@ -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( return Padding(
padding: const EdgeInsets.all(8.0), padding: const EdgeInsets.all(8.0),
child: Text( child: Text(
@ -66,7 +65,9 @@ class CommandeDetails extends StatelessWidget {
), ),
); );
} else { } 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 { } 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!; final details = snapshot.data!;
// Calculer les totaux // Calculer les totaux
double sousTotal = 0; double sousTotal = 0;
double totalRemises = 0; double totalRemises = 0;
double totalFinal = 0; double totalFinal = 0;
bool hasRemises = false; bool hasRemises = false;
for (final detail in details) { for (final detail in details) {
sousTotal += detail.sousTotal; sousTotal += detail.sousTotal;
totalRemises += detail.montantRemise; totalRemises += detail.montantRemise;
@ -170,7 +173,7 @@ class CommandeDetails extends StatelessWidget {
decoration: BoxDecoration( decoration: BoxDecoration(
color: hasRemises ? Colors.orange.shade50 : Colors.blue.shade50, color: hasRemises ? Colors.orange.shade50 : Colors.blue.shade50,
borderRadius: BorderRadius.circular(8), borderRadius: BorderRadius.circular(8),
border: hasRemises border: hasRemises
? Border.all(color: Colors.orange.shade200) ? Border.all(color: Colors.orange.shade200)
: null, : null,
), ),
@ -178,21 +181,27 @@ class CommandeDetails extends StatelessWidget {
children: [ children: [
Icon( Icon(
hasRemises ? Icons.discount : Icons.receipt_long, 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), const SizedBox(width: 8),
Text( 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( style: TextStyle(
fontWeight: FontWeight.bold, fontWeight: FontWeight.bold,
fontSize: 16, fontSize: 16,
color: hasRemises ? Colors.orange.shade800 : Colors.black87, color:
hasRemises ? Colors.orange.shade800 : Colors.black87,
), ),
), ),
if (hasRemises) ...[ if (hasRemises) ...[
const Spacer(), const Spacer(),
Container( Container(
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4), padding: const EdgeInsets.symmetric(
horizontal: 8, vertical: 4),
decoration: BoxDecoration( decoration: BoxDecoration(
color: Colors.orange.shade100, color: Colors.orange.shade100,
borderRadius: BorderRadius.circular(12), borderRadius: BorderRadius.circular(12),
@ -231,65 +240,76 @@ class CommandeDetails extends StatelessWidget {
], ],
), ),
...details.map((detail) => TableRow( ...details.map((detail) => TableRow(
decoration: detail.aRemise decoration: detail.aRemise
? BoxDecoration( ? BoxDecoration(
color: const Color.fromARGB(255, 243, 191, 114), color: const Color.fromARGB(255, 243, 191, 114),
border: Border( border: Border(
left: BorderSide( left: BorderSide(
color: Colors.orange.shade300, color: Colors.orange.shade300,
width: 3, 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,
), ),
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( Text(
'Avec remise', 'IMEI: ${detail.produitImei}',
style: TextStyle( style: const TextStyle(
fontSize: 10, fontSize: 10,
color: Colors.teal.shade700, color: Colors.grey,
fontStyle: FontStyle.italic, 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),
_buildTableCell('${detail.quantite}'), if (hasRemises) _buildRemiseColumn(detail),
_buildPriceColumn(detail), _buildTotalColumn(detail),
if (hasRemises) _buildRemiseColumn(detail), ],
_buildTotalColumn(detail), )),
],
)),
], ],
), ),
), ),
const SizedBox(height: 12), const SizedBox(height: 12),
// Section des totaux // Section des totaux
Container( Container(
padding: const EdgeInsets.all(12), padding: const EdgeInsets.all(12),
@ -355,7 +375,7 @@ class CommandeDetails extends StatelessWidget {
), ),
const Divider(height: 16), const Divider(height: 16),
], ],
// Total final // Total final
Row( Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween, mainAxisAlignment: MainAxisAlignment.spaceBetween,
@ -385,4 +405,4 @@ class CommandeDetails extends StatelessWidget {
}, },
); );
} }
} }

56
lib/Models/Client.dart

@ -36,9 +36,9 @@ class Client {
// Fonction helper améliorée pour parser les dates // Fonction helper améliorée pour parser les dates
static DateTime _parseDateTime(dynamic dateValue) { static DateTime _parseDateTime(dynamic dateValue) {
if (dateValue == null) return DateTime.now(); if (dateValue == null) return DateTime.now();
if (dateValue is DateTime) return dateValue; if (dateValue is DateTime) return dateValue;
if (dateValue is String) { if (dateValue is String) {
try { try {
return DateTime.parse(dateValue); return DateTime.parse(dateValue);
@ -47,13 +47,14 @@ class Client {
return DateTime.now(); return DateTime.now();
} }
} }
// Pour MySQL qui peut retourner un Timestamp // Pour MySQL qui peut retourner un Timestamp
if (dateValue is int) { if (dateValue is int) {
return DateTime.fromMillisecondsSinceEpoch(dateValue); 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(); return DateTime.now();
} }
@ -73,11 +74,7 @@ class Client {
String get nomComplet => '$prenom $nom'; String get nomComplet => '$prenom $nom';
} }
enum StatutCommande { enum StatutCommande { enAttente, confirmee, annulee }
enAttente,
confirmee,
annulee
}
class Commande { class Commande {
final int? id; final int? id;
@ -156,7 +153,7 @@ class Commande {
statut: StatutCommande.values[(map['statut'] as int)], statut: StatutCommande.values[(map['statut'] as int)],
montantTotal: (map['montantTotal'] as num).toDouble(), montantTotal: (map['montantTotal'] as num).toDouble(),
notes: map['notes'] as String?, notes: map['notes'] as String?,
dateLivraison: map['dateLivraison'] != null dateLivraison: map['dateLivraison'] != null
? Client._parseDateTime(map['dateLivraison']) ? Client._parseDateTime(map['dateLivraison'])
: null, : null,
commandeurId: map['commandeurId'] as int?, commandeurId: map['commandeurId'] as int?,
@ -173,10 +170,7 @@ class Commande {
} }
// REMPLACEZ COMPLÈTEMENT votre classe DetailCommande dans Models/client.dart par celle-ci : // REMPLACEZ COMPLÈTEMENT votre classe DetailCommande dans Models/client.dart par celle-ci :
enum RemiseType { enum RemiseType { pourcentage, montant }
pourcentage,
montant
}
class DetailCommande { class DetailCommande {
final int? id; final int? id;
@ -193,6 +187,7 @@ class DetailCommande {
final String? produitNom; final String? produitNom;
final String? produitImage; final String? produitImage;
final String? produitReference; final String? produitReference;
final String? produitImei; // NOUVEAU : IMEI du produit, si applicable
DetailCommande({ DetailCommande({
this.id, this.id,
@ -209,6 +204,7 @@ class DetailCommande {
this.produitNom, this.produitNom,
this.produitImage, this.produitImage,
this.produitReference, this.produitReference,
this.produitImei,
}); });
// Constructeur pour créer un détail sans remise // Constructeur pour créer un détail sans remise
@ -222,10 +218,11 @@ class DetailCommande {
String? produitNom, String? produitNom,
String? produitImage, String? produitImage,
String? produitReference, String? produitReference,
String? produitImei,
}) { }) {
final sousTotal = quantite * prixUnitaire; final sousTotal = quantite * prixUnitaire;
final prixFinal = estCadeau ? 0.0 : sousTotal; final prixFinal = estCadeau ? 0.0 : sousTotal;
return DetailCommande( return DetailCommande(
id: id, id: id,
commandeId: commandeId, commandeId: commandeId,
@ -238,6 +235,7 @@ class DetailCommande {
produitNom: produitNom, produitNom: produitNom,
produitImage: produitImage, produitImage: produitImage,
produitReference: produitReference, produitReference: produitReference,
produitImei: produitImei,
); );
} }
@ -251,6 +249,7 @@ class DetailCommande {
String? produitNom, String? produitNom,
String? produitImage, String? produitImage,
String? produitReference, String? produitReference,
String? produitImei,
}) { }) {
return DetailCommande( return DetailCommande(
id: id, id: id,
@ -264,6 +263,7 @@ class DetailCommande {
produitNom: produitNom, produitNom: produitNom,
produitImage: produitImage, produitImage: produitImage,
produitReference: produitReference, produitReference: produitReference,
produitImei: produitImei,
); );
} }
@ -274,18 +274,18 @@ class DetailCommande {
}) { }) {
// Les remises ne s'appliquent pas aux cadeaux // Les remises ne s'appliquent pas aux cadeaux
if (estCadeau) return this; if (estCadeau) return this;
double montantRemiseCalcule = 0.0; double montantRemiseCalcule = 0.0;
if (type == RemiseType.pourcentage) { if (type == RemiseType.pourcentage) {
final pourcentage = valeur.clamp(0.0, 100.0); final pourcentage = valeur.clamp(0.0, 100.0);
montantRemiseCalcule = sousTotal * (pourcentage / 100); montantRemiseCalcule = sousTotal * (pourcentage / 100);
} else { } else {
montantRemiseCalcule = valeur.clamp(0.0, sousTotal); montantRemiseCalcule = valeur.clamp(0.0, sousTotal);
} }
final prixFinalCalcule = sousTotal - montantRemiseCalcule; final prixFinalCalcule = sousTotal - montantRemiseCalcule;
return DetailCommande( return DetailCommande(
id: id, id: id,
commandeId: commandeId, commandeId: commandeId,
@ -301,6 +301,7 @@ class DetailCommande {
produitNom: produitNom, produitNom: produitNom,
produitImage: produitImage, produitImage: produitImage,
produitReference: produitReference, produitReference: produitReference,
produitImei: produitImei,
); );
} }
@ -321,6 +322,7 @@ class DetailCommande {
produitNom: produitNom, produitNom: produitNom,
produitImage: produitImage, produitImage: produitImage,
produitReference: produitReference, produitReference: produitReference,
produitImei: produitImei,
); );
} }
@ -341,6 +343,7 @@ class DetailCommande {
produitNom: produitNom, produitNom: produitNom,
produitImage: produitImage, produitImage: produitImage,
produitReference: produitReference, produitReference: produitReference,
produitImei: produitImei,
); );
} }
@ -361,12 +364,14 @@ class DetailCommande {
produitNom: produitNom, produitNom: produitNom,
produitImage: produitImage, produitImage: produitImage,
produitReference: produitReference, produitReference: produitReference,
produitImei: produitImei,
); );
} }
// Getters utiles // Getters utiles
bool get aRemise => remiseType != null && montantRemise > 0 && !estCadeau; bool get aRemise => remiseType != null && montantRemise > 0 && !estCadeau;
bool get aimei => produitImei != null;
double get pourcentageRemise { double get pourcentageRemise {
if (!aRemise) return 0.0; if (!aRemise) return 0.0;
return (montantRemise / sousTotal) * 100; return (montantRemise / sousTotal) * 100;
@ -375,7 +380,7 @@ class DetailCommande {
String get remiseDescription { String get remiseDescription {
if (estCadeau) return 'CADEAU'; if (estCadeau) return 'CADEAU';
if (!aRemise) return ''; if (!aRemise) return '';
if (remiseType == RemiseType.pourcentage) { if (remiseType == RemiseType.pourcentage) {
return '-${remiseValeur.toStringAsFixed(0)}%'; return '-${remiseValeur.toStringAsFixed(0)}%';
} else { } else {
@ -426,12 +431,13 @@ class DetailCommande {
remiseType: type, remiseType: type,
remiseValeur: (map['remise_valeur'] as num?)?.toDouble() ?? 0.0, remiseValeur: (map['remise_valeur'] as num?)?.toDouble() ?? 0.0,
montantRemise: (map['montant_remise'] as num?)?.toDouble() ?? 0.0, montantRemise: (map['montant_remise'] as num?)?.toDouble() ?? 0.0,
prixFinal: (map['prix_final'] as num?)?.toDouble() ?? prixFinal: (map['prix_final'] as num?)?.toDouble() ??
(map['sousTotal'] as num).toDouble(), (map['sousTotal'] as num).toDouble(),
estCadeau: (map['est_cadeau'] as int?) == 1, estCadeau: (map['est_cadeau'] as int?) == 1,
produitNom: map['produitNom'] as String?, produitNom: map['produitNom'] as String?,
produitImage: map['produitImage'] as String?, produitImage: map['produitImage'] as String?,
produitReference: map['produitReference'] as String?, produitReference: map['produitReference'] as String?,
produitImei: map['produitImei'] as String?,
); );
} }
} }

1607
lib/Services/stock_managementDatabase.dart

File diff suppressed because it is too large

1482
lib/Views/commandManagement.dart

File diff suppressed because it is too large

30
lib/Views/historique.dart

@ -92,7 +92,9 @@ class _HistoriquePageState extends State<HistoriquePage> {
final pointDeVenteId = _userController.pointDeVenteId; final pointDeVenteId = _userController.pointDeVenteId;
final commandes = pointDeVenteId == 0 final commandes = pointDeVenteId == 0
? allCommandes ? allCommandes
: allCommandes.where((cmd) => cmd.pointDeVenteId == pointDeVenteId).toList(); : allCommandes
.where((cmd) => cmd.pointDeVenteId == pointDeVenteId)
.toList();
setState(() { setState(() {
_commandes.clear(); _commandes.clear();
@ -146,7 +148,8 @@ class _HistoriquePageState extends State<HistoriquePage> {
_filteredCommandes.clear(); _filteredCommandes.clear();
for (var commande in _commandes) { for (var commande in _commandes) {
if (pointDeVenteId != 0 && commande.pointDeVenteId != pointDeVenteId) continue; 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) ||
@ -641,10 +644,25 @@ class _HistoriquePageState extends State<HistoriquePage> {
), ),
child: const Icon(Icons.shopping_bag, size: 20), child: const Icon(Icons.shopping_bag, size: 20),
), ),
title: Text( title: Column(
detail.produitNom ?? 'Produit inconnu', crossAxisAlignment: CrossAxisAlignment.start,
style: children: [
const TextStyle(fontWeight: FontWeight.w500), 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( subtitle: Text(
'${detail.quantite} x ${NumberFormat('#,##0.00', 'fr_FR').format(detail.prixUnitaire)} MGA', '${detail.quantite} x ${NumberFormat('#,##0.00', 'fr_FR').format(detail.prixUnitaire)} MGA',

Loading…
Cancel
Save