// Models/client.dart - Version corrigée pour MySQL class Client { final int? id; final String nom; final String prenom; final String email; final String telephone; final String? adresse; final DateTime dateCreation; final bool actif; Client({ this.id, required this.nom, required this.prenom, required this.email, required this.telephone, this.adresse, required this.dateCreation, this.actif = true, }); Map toMap() { return { 'id': id, 'nom': nom, 'prenom': prenom, 'email': email, 'telephone': telephone, 'adresse': adresse, 'dateCreation': dateCreation.toIso8601String(), 'actif': actif ? 1 : 0, }; } // 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); } catch (e) { print("Erreur parsing date string: $dateValue, erreur: $e"); 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"); return DateTime.now(); } factory Client.fromMap(Map map) { return Client( id: map['id'] as int?, nom: map['nom'] as String, prenom: map['prenom'] as String, email: map['email'] as String, telephone: map['telephone'] as String, adresse: map['adresse'] as String?, dateCreation: _parseDateTime(map['dateCreation']), actif: (map['actif'] as int?) == 1, ); } String get nomComplet => '$prenom $nom'; } enum StatutCommande { enAttente, confirmee, annulee } class Commande { final int? id; final int clientId; final DateTime dateCommande; final StatutCommande statut; final double montantTotal; final String? notes; final DateTime? dateLivraison; final int? commandeurId; final int? validateurId; final String? clientNom; final String? clientPrenom; final String? clientEmail; Commande({ this.id, required this.clientId, required this.dateCommande, required this.statut, required this.montantTotal, this.notes, this.dateLivraison, this.commandeurId, this.validateurId, this.clientNom, this.clientPrenom, this.clientEmail, }); String get clientNomComplet { if (clientNom != null && clientPrenom != null) { return '$clientPrenom $clientNom'; } return 'Client inconnu'; } String get statutLibelle { switch (statut) { case StatutCommande.enAttente: return 'En attente'; case StatutCommande.confirmee: return 'Confirmée'; case StatutCommande.annulee: return 'Annulée'; } } Map toMap() { return { 'id': id, 'clientId': clientId, 'dateCommande': dateCommande.toIso8601String(), 'statut': statut.index, 'montantTotal': montantTotal, 'notes': notes, 'dateLivraison': dateLivraison?.toIso8601String(), 'commandeurId': commandeurId, 'validateurId': validateurId, }; } factory Commande.fromMap(Map map) { return Commande( id: map['id'] as int?, clientId: map['clientId'] as int, dateCommande: Client._parseDateTime(map['dateCommande']), statut: StatutCommande.values[(map['statut'] as int)], montantTotal: (map['montantTotal'] as num).toDouble(), notes: map['notes'] as String?, dateLivraison: map['dateLivraison'] != null ? Client._parseDateTime(map['dateLivraison']) : null, commandeurId: map['commandeurId'] as int?, validateurId: map['validateurId'] as int?, clientNom: map['clientNom'] as String?, clientPrenom: map['clientPrenom'] as String?, clientEmail: map['clientEmail'] as String?, ); } } // REMPLACEZ COMPLÈTEMENT votre classe DetailCommande dans Models/client.dart par celle-ci : enum RemiseType { pourcentage, montant } class DetailCommande { final int? id; final int commandeId; final int produitId; final int quantite; final double prixUnitaire; final double sousTotal; // Prix unitaire × quantité (avant remise) final RemiseType? remiseType; final double remiseValeur; // Valeur de la remise (% ou montant) final double montantRemise; // Montant de la remise calculé final double prixFinal; // Prix final après remise final bool estCadeau; // NOUVEAU : Indique si l'article est un cadeau final String? produitNom; final String? produitImage; final String? produitReference; DetailCommande({ this.id, required this.commandeId, required this.produitId, required this.quantite, required this.prixUnitaire, required this.sousTotal, this.remiseType, this.remiseValeur = 0.0, this.montantRemise = 0.0, required this.prixFinal, this.estCadeau = false, this.produitNom, this.produitImage, this.produitReference, }); // Constructeur pour créer un détail sans remise factory DetailCommande.sansRemise({ int? id, required int commandeId, required int produitId, required int quantite, required double prixUnitaire, bool estCadeau = false, String? produitNom, String? produitImage, String? produitReference, }) { final sousTotal = quantite * prixUnitaire; final prixFinal = estCadeau ? 0.0 : sousTotal; return DetailCommande( id: id, commandeId: commandeId, produitId: produitId, quantite: quantite, prixUnitaire: prixUnitaire, sousTotal: sousTotal, prixFinal: prixFinal, estCadeau: estCadeau, produitNom: produitNom, produitImage: produitImage, produitReference: produitReference, ); } // NOUVEAU : Constructeur pour créer un cadeau factory DetailCommande.cadeau({ int? id, required int commandeId, required int produitId, required int quantite, required double prixUnitaire, String? produitNom, String? produitImage, String? produitReference, }) { return DetailCommande( id: id, commandeId: commandeId, produitId: produitId, quantite: quantite, prixUnitaire: prixUnitaire, sousTotal: quantite * prixUnitaire, prixFinal: 0.0, // Prix final à 0 pour un cadeau estCadeau: true, produitNom: produitNom, produitImage: produitImage, produitReference: produitReference, ); } // Méthode pour appliquer une remise (ne s'applique pas aux cadeaux) DetailCommande appliquerRemise({ required RemiseType type, required double valeur, }) { // 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, produitId: produitId, quantite: quantite, prixUnitaire: prixUnitaire, sousTotal: sousTotal, remiseType: type, remiseValeur: valeur, montantRemise: montantRemiseCalcule, prixFinal: prixFinalCalcule, estCadeau: estCadeau, produitNom: produitNom, produitImage: produitImage, produitReference: produitReference, ); } // Méthode pour supprimer la remise DetailCommande supprimerRemise() { return DetailCommande( id: id, commandeId: commandeId, produitId: produitId, quantite: quantite, prixUnitaire: prixUnitaire, sousTotal: sousTotal, remiseType: null, remiseValeur: 0.0, montantRemise: 0.0, prixFinal: estCadeau ? 0.0 : sousTotal, estCadeau: estCadeau, produitNom: produitNom, produitImage: produitImage, produitReference: produitReference, ); } // NOUVEAU : Méthode pour convertir en cadeau DetailCommande convertirEnCadeau() { return DetailCommande( id: id, commandeId: commandeId, produitId: produitId, quantite: quantite, prixUnitaire: prixUnitaire, sousTotal: sousTotal, remiseType: null, // Supprimer les remises lors de la conversion en cadeau remiseValeur: 0.0, montantRemise: 0.0, prixFinal: 0.0, estCadeau: true, produitNom: produitNom, produitImage: produitImage, produitReference: produitReference, ); } // NOUVEAU : Méthode pour convertir en article normal DetailCommande convertirEnArticleNormal() { return DetailCommande( id: id, commandeId: commandeId, produitId: produitId, quantite: quantite, prixUnitaire: prixUnitaire, sousTotal: sousTotal, remiseType: remiseType, remiseValeur: remiseValeur, montantRemise: montantRemise, prixFinal: estCadeau ? sousTotal - montantRemise : prixFinal, estCadeau: false, produitNom: produitNom, produitImage: produitImage, produitReference: produitReference, ); } // Getters utiles bool get aRemise => remiseType != null && montantRemise > 0 && !estCadeau; double get pourcentageRemise { if (!aRemise) return 0.0; return (montantRemise / sousTotal) * 100; } String get remiseDescription { if (estCadeau) return 'CADEAU'; if (!aRemise) return ''; if (remiseType == RemiseType.pourcentage) { return '-${remiseValeur.toStringAsFixed(0)}%'; } else { return '-${montantRemise.toStringAsFixed(2)} MGA'; } } // NOUVEAU : Description du statut de l'article String get statutDescription { if (estCadeau) return 'CADEAU OFFERT'; if (aRemise) return 'AVEC REMISE'; return 'PRIX NORMAL'; } Map toMap() { return { 'id': id, 'commandeId': commandeId, 'produitId': produitId, 'quantite': quantite, 'prixUnitaire': prixUnitaire, 'sousTotal': sousTotal, 'remise_type': remiseType?.name, 'remise_valeur': remiseValeur, 'montant_remise': montantRemise, 'prix_final': prixFinal, 'est_cadeau': estCadeau ? 1 : 0, }; } factory DetailCommande.fromMap(Map map) { RemiseType? type; if (map['remise_type'] != null) { if (map['remise_type'] == 'pourcentage') { type = RemiseType.pourcentage; } else if (map['remise_type'] == 'montant') { type = RemiseType.montant; } } return DetailCommande( id: map['id'] as int?, commandeId: map['commandeId'] as int, produitId: map['produitId'] as int, quantite: map['quantite'] as int, prixUnitaire: (map['prixUnitaire'] as num).toDouble(), sousTotal: (map['sousTotal'] as num).toDouble(), 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(), estCadeau: (map['est_cadeau'] as int?) == 1, produitNom: map['produitNom'] as String?, produitImage: map['produitImage'] as String?, produitReference: map['produitReference'] as String?, ); } }