Browse Source

command

master
Stephane 4 months ago
parent
commit
e83f7af730
  1. 4
      lib/models/command_detail.dart
  2. 312
      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 updatedAt;
final List<CommandeItem> items;
final String? tablename;
CommandeDetail({
required this.id,
@ -36,6 +37,7 @@ class CommandeDetail {
required this.createdAt,
required this.updatedAt,
required this.items,
this.tablename,
});
factory CommandeDetail.fromJson(Map<String, dynamic> json) {
@ -76,6 +78,7 @@ class CommandeDetail {
?.map((item) => CommandeItem.fromJson(item))
.toList() ??
[],
tablename: data['tablename'] ?? 'Table inconnue',
);
}
@ -98,6 +101,7 @@ class CommandeDetail {
'created_at': createdAt.toIso8601String(),
'updated_at': updatedAt.toIso8601String(),
'items': items.map((item) => item.toJson()).toList(),
'tablename': tablename,
};
}

312
lib/pages/cart_page.dart

@ -1,9 +1,14 @@
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
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 'package:itrimobe/pages/tables.dart';
import 'package:itrimobe/services/pdf_service.dart';
import '../layouts/main_layout.dart';
@ -315,7 +320,12 @@ class _CartPageState extends State<CartPage> {
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>(
context: context,
builder:
@ -337,6 +347,49 @@ class _CartPageState extends State<CartPage> {
),
),
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 ?'),
],
),
@ -348,9 +401,12 @@ class _CartPageState extends State<CartPage> {
ElevatedButton(
onPressed: () => Navigator.of(context).pop(true),
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 {
// 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(
Uri.parse('https://restaurant.careeracademy.mg/api/commandes'),
headers: {'Content-Type': 'application/json'},
body: jsonEncode({
'table_id': widget.tableId,
'statut': 'payee', // Directement payée
'statut': 'payee',
'items':
_cartItems
.map(
@ -378,33 +434,47 @@ class _CartPageState extends State<CartPage> {
'quantite': item.quantity,
'prix_unitaire': item.prix,
'commentaire': item.commentaire,
// Pas de réservation
},
)
.toList(),
'methode_paiement': 'especes',
'methode_paiement':
selectedPaymentMethod.id, // mvola, carte, ou especes
'montant_paye': total,
'date_paiement': DateTime.now().toIso8601String(),
'client_id': 1, // Valeur par défaut
'client_id': 1,
'reservation_id': 1,
'serveur': 'Serveur par défaut', // Valeur par défaut
'serveur': 'Serveur par défaut',
}),
);
if (response.statusCode == 201) {
final commandeData = jsonDecode(response.body);
// 2. Libérer la table
// Libérer la table
await http.patch(
Uri.parse(
'https://restaurant.careeracademy.mg/api/tables/${widget.tableId}',
),
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);
// _showPaymentSuccessDialog(commandeData, total, selectedPaymentMethod);
} else {
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(
Map<String, dynamic> commandeData,
double total,
PaymentMethod paymentMethod,
) {
showDialog(
context: context,
@ -446,49 +611,130 @@ class _CartPageState extends State<CartPage> {
Text('Table ${widget.tablename} libérée'),
const SizedBox(height: 8),
Text(
'Montant: ${total.toStringAsFixed(2)}',
'Montant: ${total.toStringAsFixed(0)} MGA',
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 Text('Voulez-vous imprimer un reçu ?'),
],
),
actions: [
TextButton(
onPressed: () {
onPressed: () => _navigateToTables(),
child: const Text('Non merci'),
),
ElevatedButton(
onPressed:
() =>
_imprimerEtNaviguer(commandeData, total, paymentMethod),
style: ElevatedButton.styleFrom(backgroundColor: Colors.green),
child: const Text('Imprimer'),
),
],
),
);
}
void _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()),
builder: (context) => const MainLayout(child: TablesScreen()),
),
(route) => false,
);
},
child: const Text('Non merci'),
),
ElevatedButton(
onPressed: () async {
}
Future<void> _imprimerEtNaviguer(
Map<String, dynamic> commandeData,
double total,
PaymentMethod paymentMethod,
) async {
Navigator.of(context).pop(); // Fermer le dialog
await _imprimerTicketPaiement(commandeData, total);
// Naviguer vers la page des tables après l'impression
Navigator.of(context).pushAndRemoveUntil(
MaterialPageRoute(
// Afficher un indicateur de chargement pendant l'impression
showDialog(
context: context,
barrierDismissible: false,
builder:
(context) => const MainLayout(child: TablesScreen()),
(context) => const AlertDialog(
content: Row(
mainAxisSize: MainAxisSize.min,
children: [
CircularProgressIndicator(),
SizedBox(width: 16),
Text('Impression en cours...'),
],
),
),
(route) => false,
);
},
style: ElevatedButton.styleFrom(backgroundColor: Colors.green),
child: const Text('Imprimer'),
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(
Map<String, dynamic> commandeData,
double total,

22
lib/pages/facture_screen.dart

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

12
lib/services/pdf_service.dart

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

Loading…
Cancel
Save