Browse Source

push de 22102025

master
andrymodeste 1 month ago
parent
commit
bb81798e03
  1. 176
      lib/pages/caisse_screen.dart
  2. 29
      lib/pages/commande_item_validation.dart
  3. 77
      lib/pages/historique_commande.dart

176
lib/pages/caisse_screen.dart

@ -35,28 +35,28 @@ class _CaisseScreenState extends State<CaisseScreen> {
name: 'MVola',
description: 'Paiement mobile MVola',
icon: Icons.phone,
color: Color(0xFF4285F4),
color: Color(0xFF28A745),
),
const PaymentMethod(
id: 'orange_money',
name: 'Orange Money',
description: 'Paiement mobile Orange Money',
icon: Icons.phone,
color: Color(0xFF4285F4),
color: Color(0xFFFF9500),
),
const PaymentMethod(
id: 'carte',
name: 'Carte Bancaire',
description: 'Paiement par carte',
icon: Icons.credit_card,
color: Color(0xFF28A745),
color: Color(0xFF4285F4),
),
const PaymentMethod(
id: 'especes',
name: 'Espèces',
description: 'Paiement en liquide',
icon: Icons.attach_money,
color: Color(0xFFFF9500),
color: Color(0xFFFFEB3B),
),
];
@ -332,28 +332,118 @@ class _CaisseScreenState extends State<CaisseScreen> {
);
}
Widget _buildPaymentMethods() {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Text(
'Méthode de paiement',
style: TextStyle(
fontSize: 18,
fontWeight: FontWeight.bold,
color: Colors.black87,
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(height: 16),
...paymentMethods.map((method) => _buildPaymentMethodCard(method)),
],
);
}
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?.totalTtc ?? 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?.totalTtc ?? 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),
),
),
],
),
),
],
);
},
);
}
Widget _buildPaymentMethodCard(PaymentMethod method) {
final isSelected = selectedPaymentMethod?.id == method.id;
// Modification de _buildPaymentMethodCard pour accepter setState de la StatefulBuilder
Widget _buildPaymentMethodCard(PaymentMethod method, bool isSelected) {
final amount = commande?.totalTtc ?? 0.0;
return Container(
@ -363,7 +453,7 @@ class _CaisseScreenState extends State<CaisseScreen> {
child: InkWell(
onTap: () {
setState(() {
selectedPaymentMethod = method;
selectedPaymentMethod = method; // 🔹 setState global
});
},
borderRadius: BorderRadius.circular(12),
@ -403,9 +493,7 @@ class _CaisseScreenState extends State<CaisseScreen> {
size: 24,
),
),
const SizedBox(width: 16),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
@ -431,24 +519,18 @@ class _CaisseScreenState extends State<CaisseScreen> {
],
),
),
const SizedBox(width: 16),
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,
),
color: isSelected ? Colors.black87 : Colors.white,
fontSize: 16,
fontWeight: FontWeight.bold),
),
],
),
@ -459,6 +541,9 @@ class _CaisseScreenState extends State<CaisseScreen> {
}
Widget _buildPaymentButton() {
final canPay = selectedPaymentMethod != null && !isProcessingPayment;
@ -592,22 +677,23 @@ class _CaisseScreenState extends State<CaisseScreen> {
),
)
: SingleChildScrollView(
padding: const EdgeInsets.all(20),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
_buildCommandeHeader(),
padding: const EdgeInsets.all(20),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
_buildCommandeHeader(),
const SizedBox(height: 32),
const SizedBox(height: 32),
_buildPaymentMethods(),
_buildPaymentSection(), // <-- ici on met le widget avec 2 colonnes
_buildPaymentButton(),
_buildPaymentButton(),
const SizedBox(height: 20),
],
),
),
const SizedBox(height: 20),
],
),
),
);
}
}

29
lib/pages/commande_item_validation.dart

@ -421,13 +421,28 @@ class _ValidateAddItemsPageState extends State<ValidateAddItemsPage> {
}
// Méthode pour naviguer avec succès
void _navigateBackWithSuccess() {
Navigator.of(context).pop({
'success': true,
'shouldReload': true,
'message': 'Articles ajoutés avec succès à la commande ${widget.numeroCommande}'
});
}
void _navigateBackWithSuccess() {
// Fermer toutes les pages précédentes et rediriger vers OrdersManagementScreen
Navigator.of(context).pushAndRemoveUntil(
MaterialPageRoute(
builder: (context) => OrdersManagementScreen(),
),
(Route<dynamic> route) => false, // Supprime toutes les pages précédentes
);
// Optionnel : petit délai pour laisser le temps à la navigation
Future.delayed(Duration(milliseconds: 300), () {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text(
'Articles ajoutés avec succès à la commande ${widget.numeroCommande}',
),
backgroundColor: Colors.green[700],
),
);
});
}
// Ancien dialog de succès (sans impression) - garde comme fallback
void _showSuccessDialog() {

77
lib/pages/historique_commande.dart

@ -12,6 +12,7 @@ class _OrderHistoryPageState extends State<OrderHistoryPage>
late AnimationController _animationController;
List<AnimationController> _cardAnimationControllers = [];
List<CommandeData> commandes = [];
List<CommandeData> allCommandes = []; // contiendra toutes les commandes
bool isLoading = true;
String? error;
DateTime? _startDate;
@ -53,9 +54,8 @@ Future<void> _loadCommandes({int page = 1}) async {
'limit': itemsPerPage.toString(),
};
// CORRECTION : Envoyer la date en format ISO avec fuseau horaire UTC
// Gestion des filtres de date
if (_startDate != null) {
// Début de journée en UTC (00:00:00)
final startUtc = DateTime.utc(
_startDate!.year,
_startDate!.month,
@ -65,9 +65,8 @@ Future<void> _loadCommandes({int page = 1}) async {
queryParams['date_start'] = startUtc.toIso8601String();
print('📅 Date début (UTC): ${startUtc.toIso8601String()}');
}
if (_endDate != null) {
// Fin de journée en UTC (23:59:59)
final endUtc = DateTime.utc(
_endDate!.year,
_endDate!.month,
@ -78,19 +77,18 @@ Future<void> _loadCommandes({int page = 1}) async {
print('📅 Date fin (UTC): ${endUtc.toIso8601String()}');
}
// Debug: Afficher l'URL complète
// URL complète
final uri = Uri.parse('$baseUrl/api/commandes').replace(queryParameters: queryParams);
print('🌐 URL appelée: $uri');
final response = await http.get(uri, headers: _headers);
print('📡 Status code: ${response.statusCode}');
if (response.statusCode == 200) {
final dynamic responseBody = json.decode(response.body);
List<dynamic> data = [];
// Gestion selon le format de réponse
// Gestion du format de réponse
if (responseBody is Map<String, dynamic>) {
final dataMap = responseBody['data'] as Map<String, dynamic>?;
@ -102,7 +100,6 @@ Future<void> _loadCommandes({int page = 1}) async {
data = [commandesValue];
}
// Pagination
final pagination = dataMap['pagination'] as Map<String, dynamic>?;
currentPage = pagination?['currentPage'] ?? page;
totalPages = pagination?['totalPages'] ?? (data.length / itemsPerPage).ceil();
@ -123,8 +120,7 @@ Future<void> _loadCommandes({int page = 1}) async {
try {
final item = data[i];
if (item is Map<String, dynamic>) {
final commandeData = CommandeData.fromJson(item);
parsedCommandes.add(commandeData);
parsedCommandes.add(CommandeData.fromJson(item));
} else {
print('Item $i invalide: ${item.runtimeType}');
}
@ -136,8 +132,10 @@ Future<void> _loadCommandes({int page = 1}) async {
print('${parsedCommandes.length} commandes chargées');
// Mise à jour du state avec toutes les commandes
setState(() {
commandes = parsedCommandes;
allCommandes = parsedCommandes; // toutes les commandes
commandes = parsedCommandes; // commandes affichées (filtrage frontend possible)
isLoading = false;
});
@ -160,7 +158,34 @@ Future<void> _loadCommandes({int page = 1}) async {
});
}
}
void _filterByDate() {
if (_startDate == null && _endDate == null) {
setState(() {
commandes = allCommandes;
currentPage = 1;
totalItems = commandes.length;
totalPages = (totalItems / itemsPerPage).ceil();
});
return;
}
setState(() {
commandes = allCommandes.where((c) {
final date = c.datePaiement ?? c.dateCommande;
if (date == null) return false;
final start = _startDate ?? DateTime(2000);
// Fin de journée pour inclure la date complète
final end = _endDate?.add(Duration(days: 1)).subtract(Duration(milliseconds: 1)) ?? DateTime(2100);
return !date.isBefore(start) && !date.isAfter(end);
}).toList();
currentPage = 1;
totalItems = commandes.length;
totalPages = (totalItems / itemsPerPage).ceil();
});
}
Future<void> _selectDate(BuildContext context, bool isStart) async {
final DateTime? picked = await showDatePicker(
@ -337,7 +362,7 @@ Widget _buildHeader() {
),
SizedBox(width: 5),
ElevatedButton(
onPressed: () => _loadCommandes(page: 1),
onPressed: () => _filterByDate(),
child: Text('Filtrer'),
style: ElevatedButton.styleFrom(
backgroundColor: Colors.orange,
@ -364,23 +389,17 @@ Widget _buildHeader() {
),
),
SizedBox(width: 5),
ElevatedButton(
onPressed: () {
final today = DateTime.now();
setState(() {
_startDate = DateTime(today.year, today.month, today.day);
_endDate = DateTime(today.year, today.month, today.day, 23, 59, 59);
});
_loadCommandes(page: 1);
},
child: Text('Aujourd’hui'),
style: ElevatedButton.styleFrom(
backgroundColor: Colors.blue,
foregroundColor: Colors.white,
padding: EdgeInsets.symmetric(horizontal: 8, vertical: 4),
textStyle: TextStyle(fontSize: 10),
),
),
ElevatedButton(
onPressed: () {
final today = DateTime.now();
setState(() {
_startDate = DateTime(today.year, today.month, today.day);
_endDate = DateTime(today.year, today.month, today.day, 23, 59, 59);
_filterByDate(); // filtre local sur toutes les commandes déjà chargées
});
},
child: Text('Aujourd’hui'),
),
],
),
SizedBox(height: 4),

Loading…
Cancel
Save