Browse Source

command

master
Stephane 4 months ago
parent
commit
e83f7af730
  1. 4
      lib/models/command_detail.dart
  2. 320
      lib/pages/cart_page.dart
  3. 22
      lib/pages/facture_screen.dart
  4. 12
      lib/services/pdf_service.dart

4
lib/models/command_detail.dart

@ -17,6 +17,7 @@ class CommandeDetail {
final DateTime createdAt; final DateTime createdAt;
final DateTime updatedAt; final DateTime updatedAt;
final List<CommandeItem> items; final List<CommandeItem> items;
final String? tablename;
CommandeDetail({ CommandeDetail({
required this.id, required this.id,
@ -36,6 +37,7 @@ class CommandeDetail {
required this.createdAt, required this.createdAt,
required this.updatedAt, required this.updatedAt,
required this.items, required this.items,
this.tablename,
}); });
factory CommandeDetail.fromJson(Map<String, dynamic> json) { factory CommandeDetail.fromJson(Map<String, dynamic> json) {
@ -76,6 +78,7 @@ class CommandeDetail {
?.map((item) => CommandeItem.fromJson(item)) ?.map((item) => CommandeItem.fromJson(item))
.toList() ?? .toList() ??
[], [],
tablename: data['tablename'] ?? 'Table inconnue',
); );
} }
@ -98,6 +101,7 @@ class CommandeDetail {
'created_at': createdAt.toIso8601String(), 'created_at': createdAt.toIso8601String(),
'updated_at': updatedAt.toIso8601String(), 'updated_at': updatedAt.toIso8601String(),
'items': items.map((item) => item.toJson()).toList(), 'items': items.map((item) => item.toJson()).toList(),
'tablename': tablename,
}; };
} }

320
lib/pages/cart_page.dart

@ -1,9 +1,14 @@
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:http/http.dart' as http; import 'package:http/http.dart' as http;
import 'package:itrimobe/models/command_detail.dart';
import 'package:itrimobe/models/payment_method.dart';
import 'package:itrimobe/pages/caisse_screen.dart';
import 'package:itrimobe/pages/facture_screen.dart';
import 'dart:convert'; import 'dart:convert';
import 'package:itrimobe/pages/tables.dart'; import 'package:itrimobe/pages/tables.dart';
import 'package:itrimobe/services/pdf_service.dart';
import '../layouts/main_layout.dart'; import '../layouts/main_layout.dart';
@ -315,7 +320,12 @@ class _CartPageState extends State<CartPage> {
return sum + (item.prix * item.quantity); return sum + (item.prix * item.quantity);
}); });
// Confirmer le paiement // Sélectionner le moyen de paiement
PaymentMethod? selectedPaymentMethod = await _showPaymentMethodDialog();
if (selectedPaymentMethod == null) return;
// Confirmer le paiement avec le moyen sélectionné
final bool? confirm = await showDialog<bool>( final bool? confirm = await showDialog<bool>(
context: context, context: context,
builder: builder:
@ -337,6 +347,49 @@ class _CartPageState extends State<CartPage> {
), ),
), ),
const SizedBox(height: 16), const SizedBox(height: 16),
Container(
padding: const EdgeInsets.all(12),
decoration: BoxDecoration(
color: selectedPaymentMethod.color.withOpacity(0.1),
borderRadius: BorderRadius.circular(8),
border: Border.all(
color: selectedPaymentMethod.color.withOpacity(0.3),
),
),
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
Icon(
selectedPaymentMethod.icon,
color: selectedPaymentMethod.color,
size: 24,
),
const SizedBox(width: 8),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'Paiement: ${selectedPaymentMethod.name}',
style: TextStyle(
fontWeight: FontWeight.w600,
color: selectedPaymentMethod.color,
),
),
Text(
selectedPaymentMethod.description,
style: TextStyle(
fontSize: 12,
color: selectedPaymentMethod.color.withOpacity(
0.7,
),
),
),
],
),
],
),
),
const SizedBox(height: 16),
const Text('Valider et payer cette commande ?'), const Text('Valider et payer cette commande ?'),
], ],
), ),
@ -348,9 +401,12 @@ class _CartPageState extends State<CartPage> {
ElevatedButton( ElevatedButton(
onPressed: () => Navigator.of(context).pop(true), onPressed: () => Navigator.of(context).pop(true),
style: ElevatedButton.styleFrom( style: ElevatedButton.styleFrom(
backgroundColor: Colors.orange[600], backgroundColor: selectedPaymentMethod.color,
),
child: const Text(
'Confirmer le paiement',
style: TextStyle(color: Colors.white),
), ),
child: const Text('Confirmer le paiement'),
), ),
], ],
), ),
@ -363,13 +419,13 @@ class _CartPageState extends State<CartPage> {
}); });
try { try {
// 1. Créer la commande avec les items du panier // Créer la commande avec le moyen de paiement sélectionné
final response = await http.post( final response = await http.post(
Uri.parse('https://restaurant.careeracademy.mg/api/commandes'), Uri.parse('https://restaurant.careeracademy.mg/api/commandes'),
headers: {'Content-Type': 'application/json'}, headers: {'Content-Type': 'application/json'},
body: jsonEncode({ body: jsonEncode({
'table_id': widget.tableId, 'table_id': widget.tableId,
'statut': 'payee', // Directement payée 'statut': 'payee',
'items': 'items':
_cartItems _cartItems
.map( .map(
@ -378,33 +434,47 @@ class _CartPageState extends State<CartPage> {
'quantite': item.quantity, 'quantite': item.quantity,
'prix_unitaire': item.prix, 'prix_unitaire': item.prix,
'commentaire': item.commentaire, 'commentaire': item.commentaire,
// Pas de réservation
}, },
) )
.toList(), .toList(),
'methode_paiement': 'especes', 'methode_paiement':
selectedPaymentMethod.id, // mvola, carte, ou especes
'montant_paye': total, 'montant_paye': total,
'date_paiement': DateTime.now().toIso8601String(), 'date_paiement': DateTime.now().toIso8601String(),
'client_id': 1, // Valeur par défaut 'client_id': 1,
'reservation_id': 1, 'reservation_id': 1,
'serveur': 'Serveur par défaut', // Valeur par défaut 'serveur': 'Serveur par défaut',
}), }),
); );
if (response.statusCode == 201) { if (response.statusCode == 201) {
final commandeData = jsonDecode(response.body); final commandeData = jsonDecode(response.body);
// 2. Libérer la table // Libérer la table
await http.patch( await http.patch(
Uri.parse( Uri.parse(
'https://restaurant.careeracademy.mg/api/tables/${widget.tableId}', 'https://restaurant.careeracademy.mg/api/tables/${widget.tableId}',
), ),
headers: {'Content-Type': 'application/json'}, headers: {'Content-Type': 'application/json'},
body: jsonEncode({'statut': 'libre'}), body: jsonEncode({'status': 'available'}),
);
// Convertir le Map en objet CommandeDetail
final commandeDetail = CommandeDetail.fromJson(commandeData['data']);
// Navigation avec l'objet converti
Navigator.of(context).pushReplacement(
MaterialPageRoute(
builder:
(context) => FactureScreen(
commande:
commandeDetail, // Maintenant c'est un objet CommandeDetail
paymentMethod: selectedPaymentMethod!.id,
),
),
); );
// 3. Succès // _showPaymentSuccessDialog(commandeData, total, selectedPaymentMethod);
_showPaymentSuccessDialog(commandeData, total);
} else { } else {
throw Exception('Erreur lors du paiement'); throw Exception('Erreur lors du paiement');
} }
@ -422,9 +492,104 @@ class _CartPageState extends State<CartPage> {
} }
} }
// Dialog de sélection du moyen de paiement
Future<PaymentMethod?> _showPaymentMethodDialog() async {
return await showDialog<PaymentMethod>(
context: context,
builder:
(context) => AlertDialog(
title: const Row(
children: [
Icon(Icons.payment, color: Colors.blue),
SizedBox(width: 8),
Text('Choisir le moyen de paiement'),
],
),
content: SizedBox(
width: double.maxFinite,
child: ListView.separated(
shrinkWrap: true,
itemCount: paymentMethods.length,
separatorBuilder: (context, index) => const SizedBox(height: 8),
itemBuilder: (context, index) {
final method = paymentMethods[index];
return InkWell(
onTap: () => Navigator.of(context).pop(method),
borderRadius: BorderRadius.circular(8),
child: Container(
padding: const EdgeInsets.all(16),
decoration: BoxDecoration(
border: Border.all(
color: method.color.withOpacity(0.3),
),
borderRadius: BorderRadius.circular(8),
color: method.color.withOpacity(0.05),
),
child: Row(
children: [
Container(
padding: const EdgeInsets.all(8),
decoration: BoxDecoration(
color: method.color.withOpacity(0.1),
borderRadius: BorderRadius.circular(8),
),
child: Icon(
method.icon,
color: method.color,
size: 24,
),
),
const SizedBox(width: 16),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
method.name,
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.w600,
color: method.color,
),
),
const SizedBox(height: 2),
Text(
method.description,
style: TextStyle(
fontSize: 12,
color: method.color.withOpacity(0.7),
),
),
],
),
),
Icon(
Icons.arrow_forward_ios,
color: method.color,
size: 16,
),
],
),
),
);
},
),
),
actions: [
TextButton(
onPressed: () => Navigator.of(context).pop(),
child: const Text('Annuler'),
),
],
),
);
}
// Dialog de succès mis à jour
void _showPaymentSuccessDialog( void _showPaymentSuccessDialog(
Map<String, dynamic> commandeData, Map<String, dynamic> commandeData,
double total, double total,
PaymentMethod paymentMethod,
) { ) {
showDialog( showDialog(
context: context, context: context,
@ -446,41 +611,55 @@ class _CartPageState extends State<CartPage> {
Text('Table ${widget.tablename} libérée'), Text('Table ${widget.tablename} libérée'),
const SizedBox(height: 8), const SizedBox(height: 8),
Text( Text(
'Montant: ${total.toStringAsFixed(2)}', 'Montant: ${total.toStringAsFixed(0)} MGA',
style: const TextStyle(fontWeight: FontWeight.bold), style: const TextStyle(fontWeight: FontWeight.bold),
), ),
const SizedBox(height: 12),
Container(
padding: const EdgeInsets.symmetric(
horizontal: 12,
vertical: 8,
),
decoration: BoxDecoration(
color: paymentMethod.color.withOpacity(0.1),
borderRadius: BorderRadius.circular(8),
border: Border.all(
color: paymentMethod.color.withOpacity(0.3),
),
),
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
Icon(
paymentMethod.icon,
color: paymentMethod.color,
size: 16,
),
const SizedBox(width: 6),
Text(
paymentMethod.name,
style: TextStyle(
color: paymentMethod.color,
fontWeight: FontWeight.w600,
fontSize: 14,
),
),
],
),
),
const SizedBox(height: 16), const SizedBox(height: 16),
const Text('Voulez-vous imprimer un reçu ?'), const Text('Voulez-vous imprimer un reçu ?'),
], ],
), ),
actions: [ actions: [
TextButton( TextButton(
onPressed: () { onPressed: () => _navigateToTables(),
Navigator.of(context).pop(); // Fermer le dialog
// Naviguer vers la page des tables
Navigator.of(context).pushAndRemoveUntil(
MaterialPageRoute(
builder:
(context) => const MainLayout(child: TablesScreen()),
),
(route) => false,
);
},
child: const Text('Non merci'), child: const Text('Non merci'),
), ),
ElevatedButton( ElevatedButton(
onPressed: () async { onPressed:
Navigator.of(context).pop(); // Fermer le dialog () =>
await _imprimerTicketPaiement(commandeData, total); _imprimerEtNaviguer(commandeData, total, paymentMethod),
// Naviguer vers la page des tables après l'impression
Navigator.of(context).pushAndRemoveUntil(
MaterialPageRoute(
builder:
(context) => const MainLayout(child: TablesScreen()),
),
(route) => false,
);
},
style: ElevatedButton.styleFrom(backgroundColor: Colors.green), style: ElevatedButton.styleFrom(backgroundColor: Colors.green),
child: const Text('Imprimer'), child: const Text('Imprimer'),
), ),
@ -489,6 +668,73 @@ class _CartPageState extends State<CartPage> {
); );
} }
void _navigateToTables() {
Navigator.of(context).pop(); // Fermer le dialog
Navigator.of(context).pushAndRemoveUntil(
MaterialPageRoute(
builder: (context) => const MainLayout(child: TablesScreen()),
),
(route) => false,
);
}
Future<void> _imprimerEtNaviguer(
Map<String, dynamic> commandeData,
double total,
PaymentMethod paymentMethod,
) async {
Navigator.of(context).pop(); // Fermer le dialog
// Afficher un indicateur de chargement pendant l'impression
showDialog(
context: context,
barrierDismissible: false,
builder:
(context) => const AlertDialog(
content: Row(
mainAxisSize: MainAxisSize.min,
children: [
CircularProgressIndicator(),
SizedBox(width: 16),
Text('Impression en cours...'),
],
),
),
);
try {
await _imprimerTicketPaiement(commandeData, total);
// Fermer le dialog d'impression
Navigator.of(context).pop();
// Afficher un message de succès
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(
content: Text('Ticket imprimé avec succès'),
backgroundColor: Colors.green,
duration: Duration(seconds: 2),
),
);
} catch (e) {
// Fermer le dialog d'impression
Navigator.of(context).pop();
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text('Erreur d\'impression: ${e.toString()}'),
backgroundColor: Colors.orange,
duration: const Duration(seconds: 3),
),
);
}
// Attendre un peu puis naviguer
Future.delayed(const Duration(milliseconds: 500), () {
_navigateToTables();
});
}
Future<void> _imprimerTicketPaiement( Future<void> _imprimerTicketPaiement(
Map<String, dynamic> commandeData, Map<String, dynamic> commandeData,
double total, double total,

22
lib/pages/facture_screen.dart

@ -11,10 +11,10 @@ class FactureScreen extends StatefulWidget {
final String paymentMethod; final String paymentMethod;
const FactureScreen({ const FactureScreen({
Key? key, super.key,
required this.commande, required this.commande,
required this.paymentMethod, required this.paymentMethod,
}) : super(key: key); });
@override @override
_FactureScreenState createState() => _FactureScreenState(); _FactureScreenState createState() => _FactureScreenState();
@ -114,23 +114,23 @@ class _FactureScreenState extends State<FactureScreen> {
} }
Widget _buildHeader() { Widget _buildHeader() {
return Column( return const Column(
children: [ children: [
const Text( Text(
'RESTAURANT', 'RESTAURANT ITRIMOBE',
style: TextStyle( style: TextStyle(
fontSize: 20, fontSize: 20,
fontWeight: FontWeight.bold, fontWeight: FontWeight.bold,
letterSpacing: 1.2, letterSpacing: 1.2,
), ),
), ),
const SizedBox(height: 12), SizedBox(height: 12),
const Text( Text(
'Adresse: 123 Rue de la Paix', 'Adresse: Moramanga, Madagascar',
style: TextStyle(fontSize: 12, color: Colors.black87), style: TextStyle(fontSize: 12, color: Colors.black87),
), ),
const Text( Text(
'Contact: +33 1 23 45 67 89', 'Contact: +261 34 12 34 56',
style: TextStyle(fontSize: 12, color: Colors.black87), style: TextStyle(fontSize: 12, color: Colors.black87),
), ),
], ],
@ -157,7 +157,7 @@ class _FactureScreenState extends State<FactureScreen> {
), ),
const SizedBox(height: 4), const SizedBox(height: 4),
Text( Text(
'Table: ${widget.commande.tableId}', 'Via: ${widget.commande.tablename}',
style: const TextStyle(fontSize: 12, color: Colors.black87), style: const TextStyle(fontSize: 12, color: Colors.black87),
), ),
const SizedBox(height: 4), const SizedBox(height: 4),

12
lib/services/pdf_service.dart

@ -55,11 +55,11 @@ class PlatformPrintService {
const double lineHeight = 1.2; const double lineHeight = 1.2;
final restaurantInfo = { final restaurantInfo = {
'nom': 'RESTAURANT', 'nom': 'RESTAURANT ITRIMOBE',
'adresse': '123 Rue de la Paix', 'adresse': 'Moramanga, Antananarivo',
'ville': '75000 PARIS', 'ville': 'Madagascar',
'contact': '01.23.45.67.89', 'contact': '261348415301',
'email': 'contact@restaurant.fr', 'email': 'contact@careeragency.mg',
}; };
final factureNumber = final factureNumber =
@ -389,7 +389,7 @@ class PlatformPrintService {
return await printTicket(commande: commande, paymentMethod: paymentMethod); return await printTicket(commande: commande, paymentMethod: paymentMethod);
} }
// Utilitaires de formatage // Utilitaires de formatageπ
static String _formatDate(DateTime dateTime) { static String _formatDate(DateTime dateTime) {
return '${dateTime.day.toString().padLeft(2, '0')}/${dateTime.month.toString().padLeft(2, '0')}/${dateTime.year}'; return '${dateTime.day.toString().padLeft(2, '0')}/${dateTime.month.toString().padLeft(2, '0')}/${dateTime.year}';
} }

Loading…
Cancel
Save