// pages/caisse_screen.dart import 'package:flutter/material.dart'; import 'package:itrimobe/pages/facture_screen.dart'; import '../models/command_detail.dart'; import '../models/payment_method.dart'; import '../services/restaurant_api_service.dart'; import 'package:intl/intl.dart'; import 'information.dart'; class CaisseScreen extends StatefulWidget { final String commandeId; final String tableName; const CaisseScreen({ super.key, required this.commandeId, required this.tableName, }); @override _CaisseScreenState createState() => _CaisseScreenState(); } class _CaisseScreenState extends State { CommandeDetail? commande; PrintTemplate? template; PaymentMethod? selectedPaymentMethod; bool isLoading = true; bool isProcessingPayment = false; final List paymentMethods = [ const PaymentMethod( id: 'mvola', name: 'MVola', description: 'Paiement mobile MVola', icon: Icons.phone, color: Color(0xFF28A745), ), const PaymentMethod( id: 'orange_money', name: 'Orange Money', description: 'Paiement mobile Orange Money', icon: Icons.phone, color: Color(0xFFFF9500), ), const PaymentMethod( id: 'carte', name: 'Carte Bancaire', description: 'Paiement par carte', icon: Icons.credit_card, color: Color(0xFF4285F4), ), const PaymentMethod( id: 'especes', name: 'Espèces', description: 'Paiement en liquide', icon: Icons.attach_money, color: Color(0xFFFFEB3B), ), ]; @override void initState() { super.initState(); _loadCommandeDetails(); } Future _loadCommandeDetails() async { setState(() => isLoading = true); try { final result = await RestaurantApiService.getCommandeDetails( widget.commandeId, ); final loadedTemplate = await RestaurantApiService.getPrintTemplate(); setState(() { commande = result; template = loadedTemplate; isLoading = false; }); } catch (e) { setState(() => isLoading = false); _showErrorDialog( 'Erreur lors du chargement des détails de la commande: $e', ); } } // Dans caisse_screen.dart, modifiez la méthode _processPayment Future _processPayment() async { if (selectedPaymentMethod == null || commande == null) return; setState(() => isProcessingPayment = true); try { final success = await RestaurantApiService.processPayment( commandeId: widget.commandeId, paymentMethodId: selectedPaymentMethod!.id, amount: commande!.totalHt, ); if (success) { final updateSuccess = await RestaurantApiService.updateCommandeStatus( commandeId: widget.commandeId, newStatus: 'payee', ); if (!updateSuccess) { _showErrorDialog("Paiement effectué, mais échec lors de la mise à jour du statut."); return; } // 🔄 Redirige vers la facture et attend le retour final result = await Navigator.of(context).push( MaterialPageRoute( builder: (context) => FactureScreen( commande: commande!, template: template!, paymentMethod: selectedPaymentMethod!.id, ), ), ); // Quand on revient de la facture, on retourne à la page précédente avec succès if (mounted) { Navigator.of(context).pop(true); } } else { _showErrorDialog('Le paiement a échoué. Veuillez réessayer.'); } } catch (e) { _showErrorDialog('Erreur lors du traitement du paiement: $e'); } finally { if (mounted) { setState(() => isProcessingPayment = false); } } } void _showErrorDialog(String message) { showDialog( context: context, builder: (context) => AlertDialog( title: const Text('Erreur'), content: Text(message), actions: [ TextButton( onPressed: () => Navigator.of(context).pop(), child: const Text('OK'), ), ], ), ); } void _showSuccessDialog() { showDialog( context: context, barrierDismissible: false, builder: (context) => AlertDialog( title: const Row( children: [ Icon(Icons.check_circle, color: Color(0xFF28A745), size: 28), SizedBox(width: 12), Text('Paiement réussi'), ], ), content: Text( 'Le paiement de ${NumberFormat("#,##0.00", "fr_FR").format(commande!.totalHt)} MGA a été traité avec succès via ${selectedPaymentMethod!.name}.', ), actions: [ TextButton( onPressed: () { Navigator.of(context).pop(); // Fermer le dialog Navigator.of( context, ).pop(true); // Retourner à la page précédente avec succès }, child: const Text('Fermer'), ), ], ), ); } Widget _buildCommandeHeader() { print(widget.tableName); if (commande == null) return const SizedBox.shrink(); return Container( padding: const EdgeInsets.all(20), decoration: BoxDecoration( color: Colors.grey[50], borderRadius: BorderRadius.circular(12), border: Border.all(color: Colors.grey[200]!), ), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text( 'Commande #${commande!.numeroCommande}', style: const TextStyle( fontSize: 18, fontWeight: FontWeight.bold, color: Colors.black87, ), ), Container( padding: const EdgeInsets.symmetric( horizontal: 12, vertical: 6, ), decoration: BoxDecoration( color: Colors.blue[50], borderRadius: BorderRadius.circular(20), border: Border.all(color: Colors.blue[200]!), ), child: Text( '${widget.tableName}', style: TextStyle( color: Colors.blue[700], fontSize: 12, fontWeight: FontWeight.w600, ), ), ), ], ), const SizedBox(height: 16), _buildCommandeItems(), const SizedBox(height: 16), const Divider(), const SizedBox(height: 8), Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ const Text( 'Total:', style: TextStyle( fontSize: 16, fontWeight: FontWeight.bold, color: Colors.black87, ), ), Text( '${NumberFormat("#,##0.00", "fr_FR").format(commande!.totalHt)} MGA', style: const TextStyle( fontSize: 18, fontWeight: FontWeight.bold, color: Color(0xFF28A745), ), ), ], ), ], ), ); } Widget _buildCommandeItems() { if (commande?.items.isEmpty ?? true) { return const Text('Aucun article'); } return Column( children: commande!.items.map((item) { return Padding( padding: const EdgeInsets.symmetric(vertical: 4), child: Row( crossAxisAlignment: CrossAxisAlignment.start, children: [ Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( '${item.quantite}× ${item.menuNom}', style: const TextStyle( fontSize: 14, fontWeight: FontWeight.w500, ), ), if (item.menuDescription != null && item.menuDescription!.isNotEmpty) Padding( padding: const EdgeInsets.only(top: 2), child: Text( item.menuDescription!, style: TextStyle( fontSize: 12, color: Colors.grey[600], ), maxLines: 2, overflow: TextOverflow.ellipsis, ), ), if (item.hasComments) Padding( padding: const EdgeInsets.only(top: 2), child: Text( 'Note: ${item.commentaires}', style: TextStyle( fontSize: 12, color: Colors.orange[700], fontStyle: FontStyle.italic, ), ), ), ], ), ), const SizedBox(width: 12), Text( '${NumberFormat("#,##0.00", "fr_FR").format(item.totalItem)} MGA', style: const TextStyle( fontSize: 14, fontWeight: FontWeight.w600, ), ), ], ), ); }).toList(), ); } Widget _buildPaymentSection() { double montantDonne = 0.0; double rendu = 0.0; TextEditingController montantController = TextEditingController(); return StatefulBuilder( builder: (context, setState) { return Row( crossAxisAlignment: CrossAxisAlignment.start, children: [ // 🔵 Colonne gauche : méthodes de paiement Expanded( flex: 1, child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ const Text( 'Méthode de paiement', style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold), ), const SizedBox(height: 16), ...paymentMethods.map((method) { final isSelected = selectedPaymentMethod?.id == method.id; return _buildPaymentMethodCard(method, isSelected); // 🔹 plus besoin de StatefulBuilder }).toList(), ], ), ), const SizedBox(width: 90), // 🟢 Colonne droite : saisie montant et rendu Expanded( flex: 1, child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( 'Montant à payer :', style: const TextStyle( fontWeight: FontWeight.bold, fontSize: 16), ), const SizedBox(height: 8), Text( '${NumberFormat("#,##0.00", "fr_FR").format(commande?.totalHt ?? 0)} MGA', style: const TextStyle( fontWeight: FontWeight.bold, fontSize: 20, color: Color(0xFF28A745)), ), const SizedBox(height: 24), TextField( controller: montantController, keyboardType: TextInputType.number, decoration: const InputDecoration( labelText: "Montant donné par le client", border: OutlineInputBorder(), ), onChanged: (value) { setState(() { montantDonne = double.tryParse(value) ?? 0.0; rendu = montantDonne - (commande?.totalHt ?? 0.0); }); }, ), const SizedBox(height: 24), Text( 'Rendu :', style: const TextStyle( fontWeight: FontWeight.bold, fontSize: 16, ), ), const SizedBox(height: 8), Container( padding: const EdgeInsets.symmetric( horizontal: 16, vertical: 12), decoration: BoxDecoration( color: rendu >= 0 ? Colors.green[50] : Colors.red[50], borderRadius: BorderRadius.circular(12), ), child: Text( '${rendu >= 0 ? NumberFormat("#,##0.00", "fr_FR").format(rendu) : "0"} MGA', style: TextStyle( fontWeight: FontWeight.bold, fontSize: 24, // rendu plus grand color: rendu >= 0 ? Colors.green : Colors.red, ), ), ), if (rendu < 0) const Padding( padding: EdgeInsets.only(top: 8.0), child: Text( "Montant insuffisant", style: TextStyle(color: Colors.red), ), ), ], ), ), ], ); }, ); } // Modification de _buildPaymentMethodCard pour accepter setState de la StatefulBuilder Widget _buildPaymentMethodCard(PaymentMethod method, bool isSelected) { final amount = commande?.totalHt ?? 0.0; return Container( margin: const EdgeInsets.only(bottom: 12), child: Material( color: Colors.transparent, child: InkWell( onTap: () { setState(() { selectedPaymentMethod = method; // 🔹 setState global }); }, borderRadius: BorderRadius.circular(12), child: AnimatedContainer( duration: const Duration(milliseconds: 250), curve: Curves.easeInOut, padding: const EdgeInsets.all(20), decoration: BoxDecoration( color: isSelected ? Colors.white : method.color, borderRadius: BorderRadius.circular(12), border: Border.all( color: isSelected ? method.color : Colors.transparent, width: 2, ), boxShadow: [ if (isSelected) BoxShadow( color: method.color.withOpacity(0.4), blurRadius: 8, offset: const Offset(0, 3), ), ], ), child: Row( children: [ Container( padding: const EdgeInsets.all(12), decoration: BoxDecoration( color: isSelected ? method.color.withOpacity(0.1) : Colors.white.withOpacity(0.2), borderRadius: BorderRadius.circular(8), ), child: Icon( method.icon, color: isSelected ? method.color : Colors.white, size: 24, ), ), const SizedBox(width: 16), Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( method.name, style: TextStyle( color: isSelected ? Colors.black87 : Colors.white, fontSize: 16, fontWeight: FontWeight.bold, ), ), const SizedBox(height: 4), Text( method.description, style: TextStyle( color: isSelected ? Colors.black54 : Colors.white.withOpacity(0.9), fontSize: 13, ), ), ], ), ), Icon( isSelected ? Icons.check_circle : Icons.radio_button_unchecked, color: isSelected ? method.color : Colors.white, size: 22, ), const SizedBox(width: 8), Text( '${NumberFormat("#,##0.00", "fr_FR").format(amount)} MGA', style: TextStyle( color: isSelected ? Colors.black87 : Colors.white, fontSize: 16, fontWeight: FontWeight.bold), ), ], ), ), ), ), ); } Widget _buildPaymentButton() { final canPay = selectedPaymentMethod != null && !isProcessingPayment; return Container( width: double.infinity, margin: const EdgeInsets.only(top: 20), child: ElevatedButton( onPressed: canPay ? _processPayment : null, style: ElevatedButton.styleFrom( backgroundColor: const Color(0xFF28A745), foregroundColor: Colors.white, padding: const EdgeInsets.symmetric(vertical: 16), shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(12), ), elevation: 2, ), child: isProcessingPayment ? const Row( mainAxisSize: MainAxisSize.min, children: [ SizedBox( width: 20, height: 20, child: CircularProgressIndicator( strokeWidth: 2, valueColor: AlwaysStoppedAnimation(Colors.white), ), ), SizedBox(width: 12), Text( 'Traitement en cours...', style: TextStyle( fontSize: 16, fontWeight: FontWeight.w600, ), ), ], ) : Row( mainAxisSize: MainAxisSize.min, children: [ const Icon(Icons.payment, size: 20), const SizedBox(width: 8), Text( selectedPaymentMethod != null ? 'Payer ${NumberFormat("#,##0.00", "fr_FR").format(commande?.totalHt)} MGA' : 'Sélectionnez une méthode de paiement', style: const TextStyle( fontSize: 16, fontWeight: FontWeight.w600, ), ), ], ), ), ); } @override Widget build(BuildContext context) { return Scaffold( backgroundColor: Colors.white, appBar: AppBar( backgroundColor: Colors.white, elevation: 0, leading: IconButton( icon: const Icon(Icons.arrow_back, color: Colors.black87), onPressed: () => Navigator.of(context).pop(), ), title: const Text( 'Caisse', style: TextStyle( color: Colors.black87, fontSize: 20, fontWeight: FontWeight.bold, ), ), centerTitle: true, ), body: isLoading ? const Center( child: Column( mainAxisSize: MainAxisSize.min, children: [ CircularProgressIndicator( valueColor: AlwaysStoppedAnimation( Color(0xFF28A745), ), ), SizedBox(height: 16), Text( 'Chargement des détails...', style: TextStyle(fontSize: 14, color: Colors.grey), ), ], ), ) : commande == null ? Center( child: Column( mainAxisSize: MainAxisSize.min, children: [ const Icon( Icons.error_outline, size: 64, color: Colors.red, ), const SizedBox(height: 16), const Text( 'Impossible de charger la commande', style: TextStyle( fontSize: 16, fontWeight: FontWeight.w600, ), ), const SizedBox(height: 8), Text( 'Commande #${widget.commandeId}', style: TextStyle(fontSize: 14, color: Colors.grey[600]), ), const SizedBox(height: 20), ElevatedButton( onPressed: _loadCommandeDetails, child: const Text('Réessayer'), ), ], ), ) : SingleChildScrollView( padding: const EdgeInsets.all(20), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ _buildCommandeHeader(), const SizedBox(height: 32), _buildPaymentSection(), // <-- ici on met le widget avec 2 colonnes _buildPaymentButton(), const SizedBox(height: 20), ], ), ), ); } }