|
|
@ -6,6 +6,7 @@ |
|
|
import 'package:youmazgestion/Models/client.dart'; |
|
|
import 'package:youmazgestion/Models/client.dart'; |
|
|
import 'package:youmazgestion/Services/stock_managementDatabase.dart'; |
|
|
import 'package:youmazgestion/Services/stock_managementDatabase.dart'; |
|
|
import 'package:youmazgestion/controller/userController.dart'; |
|
|
import 'package:youmazgestion/controller/userController.dart'; |
|
|
|
|
|
|
|
|
class HistoriquePage extends StatefulWidget { |
|
|
class HistoriquePage extends StatefulWidget { |
|
|
const HistoriquePage({super.key}); |
|
|
const HistoriquePage({super.key}); |
|
|
|
|
|
|
|
|
@ -30,7 +31,8 @@ |
|
|
// Contrôleurs pour les filtres |
|
|
// Contrôleurs pour les filtres |
|
|
final TextEditingController _searchController = TextEditingController(); |
|
|
final TextEditingController _searchController = TextEditingController(); |
|
|
final TextEditingController _searchClientController = TextEditingController(); |
|
|
final TextEditingController _searchClientController = TextEditingController(); |
|
|
final TextEditingController _searchCommandeIdController = TextEditingController(); |
|
|
final TextEditingController _searchCommandeIdController = |
|
|
|
|
|
TextEditingController(); |
|
|
|
|
|
|
|
|
// Variables de filtre |
|
|
// Variables de filtre |
|
|
StatutCommande? _selectedStatut; |
|
|
StatutCommande? _selectedStatut; |
|
|
@ -50,7 +52,6 @@ |
|
|
_searchCommandeIdController.addListener(_filterCommandes); |
|
|
_searchCommandeIdController.addListener(_filterCommandes); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Future<void> _loadPointsDeVenteWithDefault() async { |
|
|
Future<void> _loadPointsDeVenteWithDefault() async { |
|
|
try { |
|
|
try { |
|
|
print(_userController.userId); |
|
|
print(_userController.userId); |
|
|
@ -115,7 +116,8 @@ Future<void> _loadPointsDeVenteWithDefault() async { |
|
|
context: context, |
|
|
context: context, |
|
|
firstDate: DateTime(2020), |
|
|
firstDate: DateTime(2020), |
|
|
lastDate: DateTime.now().add(const Duration(days: 365)), |
|
|
lastDate: DateTime.now().add(const Duration(days: 365)), |
|
|
initialDateRange: _dateRange ?? DateTimeRange( |
|
|
initialDateRange: _dateRange ?? |
|
|
|
|
|
DateTimeRange( |
|
|
start: DateTime.now().subtract(const Duration(days: 30)), |
|
|
start: DateTime.now().subtract(const Duration(days: 30)), |
|
|
end: DateTime.now(), |
|
|
end: DateTime.now(), |
|
|
), |
|
|
), |
|
|
@ -151,8 +153,8 @@ Future<void> _loadPointsDeVenteWithDefault() async { |
|
|
bool matchesCommandeId = commandeIdQuery.isEmpty || |
|
|
bool matchesCommandeId = commandeIdQuery.isEmpty || |
|
|
commande.id.toString().contains(commandeIdQuery); |
|
|
commande.id.toString().contains(commandeIdQuery); |
|
|
|
|
|
|
|
|
bool matchesStatut = _selectedStatut == null || |
|
|
bool matchesStatut = |
|
|
commande.statut == _selectedStatut; |
|
|
_selectedStatut == null || commande.statut == _selectedStatut; |
|
|
|
|
|
|
|
|
bool matchesDate = true; |
|
|
bool matchesDate = true; |
|
|
if (_dateRange != null) { |
|
|
if (_dateRange != null) { |
|
|
@ -161,8 +163,7 @@ Future<void> _loadPointsDeVenteWithDefault() async { |
|
|
date.isBefore(_dateRange!.end.add(const Duration(days: 1))); |
|
|
date.isBefore(_dateRange!.end.add(const Duration(days: 1))); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
bool matchesToday = !_showOnlyToday || |
|
|
bool matchesToday = !_showOnlyToday || _isToday(commande.dateCommande); |
|
|
_isToday(commande.dateCommande); |
|
|
|
|
|
|
|
|
|
|
|
bool matchesAmount = true; |
|
|
bool matchesAmount = true; |
|
|
if (_minAmount != null && commande.montantTotal < _minAmount!) { |
|
|
if (_minAmount != null && commande.montantTotal < _minAmount!) { |
|
|
@ -172,8 +173,13 @@ Future<void> _loadPointsDeVenteWithDefault() async { |
|
|
matchesAmount = false; |
|
|
matchesAmount = false; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
if (matchesSearch && matchesClient && matchesCommandeId && |
|
|
if (matchesSearch && |
|
|
matchesStatut && matchesDate && matchesToday && matchesAmount) { |
|
|
matchesClient && |
|
|
|
|
|
matchesCommandeId && |
|
|
|
|
|
matchesStatut && |
|
|
|
|
|
matchesDate && |
|
|
|
|
|
matchesToday && |
|
|
|
|
|
matchesAmount) { |
|
|
_filteredCommandes.add(commande); |
|
|
_filteredCommandes.add(commande); |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
@ -241,7 +247,8 @@ Future<void> _loadPointsDeVenteWithDefault() async { |
|
|
TextButton.icon( |
|
|
TextButton.icon( |
|
|
onPressed: _clearFilters, |
|
|
onPressed: _clearFilters, |
|
|
icon: const Icon(Icons.clear, size: 18), |
|
|
icon: const Icon(Icons.clear, size: 18), |
|
|
label: isMobile ? const SizedBox() : const Text('Réinitialiser'), |
|
|
label: |
|
|
|
|
|
isMobile ? const SizedBox() : const Text('Réinitialiser'), |
|
|
style: TextButton.styleFrom( |
|
|
style: TextButton.styleFrom( |
|
|
foregroundColor: Colors.grey.shade600, |
|
|
foregroundColor: Colors.grey.shade600, |
|
|
), |
|
|
), |
|
|
@ -281,7 +288,8 @@ Future<void> _loadPointsDeVenteWithDefault() async { |
|
|
filled: true, |
|
|
filled: true, |
|
|
fillColor: Colors.grey.shade50, |
|
|
fillColor: Colors.grey.shade50, |
|
|
), |
|
|
), |
|
|
),), |
|
|
), |
|
|
|
|
|
), |
|
|
const SizedBox(width: 12), |
|
|
const SizedBox(width: 12), |
|
|
Expanded( |
|
|
Expanded( |
|
|
child: TextField( |
|
|
child: TextField( |
|
|
@ -376,34 +384,38 @@ Future<void> _loadPointsDeVenteWithDefault() async { |
|
|
size: 20, |
|
|
size: 20, |
|
|
), |
|
|
), |
|
|
label: Text(_showOnlyToday |
|
|
label: Text(_showOnlyToday |
|
|
? isMobile ? 'Toutes dates' : 'Toutes les dates' |
|
|
? isMobile |
|
|
: isMobile ? 'Aujourd\'hui' : 'Aujourd\'hui seulement'), |
|
|
? 'Toutes dates' |
|
|
|
|
|
: 'Toutes les dates' |
|
|
|
|
|
: isMobile |
|
|
|
|
|
? 'Aujourd\'hui' |
|
|
|
|
|
: 'Aujourd\'hui seulement'), |
|
|
style: ElevatedButton.styleFrom( |
|
|
style: ElevatedButton.styleFrom( |
|
|
backgroundColor: _showOnlyToday |
|
|
backgroundColor: _showOnlyToday |
|
|
? Colors.green.shade600 |
|
|
? Colors.green.shade600 |
|
|
: Colors.blue.shade600, |
|
|
: Colors.blue.shade600, |
|
|
foregroundColor: Colors.white, |
|
|
foregroundColor: Colors.white, |
|
|
padding: EdgeInsets.symmetric( |
|
|
padding: EdgeInsets.symmetric( |
|
|
horizontal: isMobile ? 12 : 16, |
|
|
horizontal: isMobile ? 12 : 16, vertical: 8), |
|
|
vertical: 8 |
|
|
|
|
|
), |
|
|
|
|
|
), |
|
|
), |
|
|
), |
|
|
), |
|
|
ElevatedButton.icon( |
|
|
ElevatedButton.icon( |
|
|
onPressed: () => _selectDateRange(context), |
|
|
onPressed: () => _selectDateRange(context), |
|
|
icon: const Icon(Icons.date_range, size: 20), |
|
|
icon: const Icon(Icons.date_range, size: 20), |
|
|
label: Text(_dateRange != null |
|
|
label: Text(_dateRange != null |
|
|
? isMobile ? 'Période' : 'Période sélectionnée' |
|
|
? isMobile |
|
|
: isMobile ? 'Période' : 'Choisir période'), |
|
|
? 'Période' |
|
|
|
|
|
: 'Période sélectionnée' |
|
|
|
|
|
: isMobile |
|
|
|
|
|
? 'Période' |
|
|
|
|
|
: 'Choisir période'), |
|
|
style: ElevatedButton.styleFrom( |
|
|
style: ElevatedButton.styleFrom( |
|
|
backgroundColor: _dateRange != null |
|
|
backgroundColor: _dateRange != null |
|
|
? Colors.orange.shade600 |
|
|
? Colors.orange.shade600 |
|
|
: Colors.grey.shade600, |
|
|
: Colors.grey.shade600, |
|
|
foregroundColor: Colors.white, |
|
|
foregroundColor: Colors.white, |
|
|
padding: EdgeInsets.symmetric( |
|
|
padding: EdgeInsets.symmetric( |
|
|
horizontal: isMobile ? 12 : 16, |
|
|
horizontal: isMobile ? 12 : 16, vertical: 8), |
|
|
vertical: 8 |
|
|
|
|
|
), |
|
|
|
|
|
), |
|
|
), |
|
|
), |
|
|
), |
|
|
], |
|
|
], |
|
|
@ -422,8 +434,7 @@ Future<void> _loadPointsDeVenteWithDefault() async { |
|
|
mainAxisSize: MainAxisSize.min, |
|
|
mainAxisSize: MainAxisSize.min, |
|
|
children: [ |
|
|
children: [ |
|
|
Icon(Icons.date_range, |
|
|
Icon(Icons.date_range, |
|
|
size: 16, |
|
|
size: 16, color: Colors.orange.shade700), |
|
|
color: Colors.orange.shade700), |
|
|
|
|
|
const SizedBox(width: 4), |
|
|
const SizedBox(width: 4), |
|
|
Text( |
|
|
Text( |
|
|
'${DateFormat('dd/MM/yyyy').format(_dateRange!.start)} - ${DateFormat('dd/MM/yyyy').format(_dateRange!.end)}', |
|
|
'${DateFormat('dd/MM/yyyy').format(_dateRange!.start)} - ${DateFormat('dd/MM/yyyy').format(_dateRange!.end)}', |
|
|
@ -442,8 +453,7 @@ Future<void> _loadPointsDeVenteWithDefault() async { |
|
|
_filterCommandes(); |
|
|
_filterCommandes(); |
|
|
}, |
|
|
}, |
|
|
child: Icon(Icons.close, |
|
|
child: Icon(Icons.close, |
|
|
size: 16, |
|
|
size: 16, color: Colors.orange.shade700), |
|
|
color: Colors.orange.shade700), |
|
|
|
|
|
), |
|
|
), |
|
|
], |
|
|
], |
|
|
), |
|
|
), |
|
|
@ -454,10 +464,7 @@ Future<void> _loadPointsDeVenteWithDefault() async { |
|
|
|
|
|
|
|
|
// Compteur de résultats |
|
|
// Compteur de résultats |
|
|
Container( |
|
|
Container( |
|
|
padding: const EdgeInsets.symmetric( |
|
|
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8), |
|
|
horizontal: 12, |
|
|
|
|
|
vertical: 8 |
|
|
|
|
|
), |
|
|
|
|
|
decoration: BoxDecoration( |
|
|
decoration: BoxDecoration( |
|
|
color: Colors.blue.shade50, |
|
|
color: Colors.blue.shade50, |
|
|
borderRadius: BorderRadius.circular(20), |
|
|
borderRadius: BorderRadius.circular(20), |
|
|
@ -484,7 +491,8 @@ Future<void> _loadPointsDeVenteWithDefault() async { |
|
|
Get.bottomSheet( |
|
|
Get.bottomSheet( |
|
|
Container( |
|
|
Container( |
|
|
padding: const EdgeInsets.all(16), |
|
|
padding: const EdgeInsets.all(16), |
|
|
height: MediaQuery.of(context).size.height * 0.85, // Plus grand sur mobile |
|
|
height: |
|
|
|
|
|
MediaQuery.of(context).size.height * 0.85, // Plus grand sur mobile |
|
|
decoration: const BoxDecoration( |
|
|
decoration: const BoxDecoration( |
|
|
color: Colors.white, |
|
|
color: Colors.white, |
|
|
borderRadius: BorderRadius.vertical(top: Radius.circular(20)), |
|
|
borderRadius: BorderRadius.vertical(top: Radius.circular(20)), |
|
|
@ -503,7 +511,8 @@ Future<void> _loadPointsDeVenteWithDefault() async { |
|
|
color: Colors.blue.shade100, |
|
|
color: Colors.blue.shade100, |
|
|
borderRadius: BorderRadius.circular(8), |
|
|
borderRadius: BorderRadius.circular(8), |
|
|
), |
|
|
), |
|
|
child: Icon(Icons.receipt_long, color: Colors.blue.shade700), |
|
|
child: |
|
|
|
|
|
Icon(Icons.receipt_long, color: Colors.blue.shade700), |
|
|
), |
|
|
), |
|
|
const SizedBox(width: 12), |
|
|
const SizedBox(width: 12), |
|
|
Text( |
|
|
Text( |
|
|
@ -533,18 +542,28 @@ Future<void> _loadPointsDeVenteWithDefault() async { |
|
|
child: Column( |
|
|
child: Column( |
|
|
crossAxisAlignment: CrossAxisAlignment.start, |
|
|
crossAxisAlignment: CrossAxisAlignment.start, |
|
|
children: [ |
|
|
children: [ |
|
|
_buildDetailRow('Client', '${client?.nom} ${client?.prenom}', Icons.person), |
|
|
_buildDetailRow('Client', '${client?.nom} ${client?.prenom}', |
|
|
_buildDetailRow('Date', DateFormat('dd/MM/yyyy à HH:mm').format(commande.dateCommande), Icons.calendar_today), |
|
|
Icons.person), |
|
|
_buildDetailRow('Client', '${_selectedPointDeVente} ', Icons.person), |
|
|
_buildDetailRow( |
|
|
|
|
|
'Date', |
|
|
|
|
|
DateFormat('dd/MM/yyyy à HH:mm') |
|
|
|
|
|
.format(commande.dateCommande), |
|
|
|
|
|
Icons.calendar_today), |
|
|
|
|
|
_buildDetailRow( |
|
|
|
|
|
'PV', '${_selectedPointDeVente} ', Icons.person), |
|
|
Row( |
|
|
Row( |
|
|
children: [ |
|
|
children: [ |
|
|
Icon(Icons.assignment, size: 16, color: Colors.grey.shade600), |
|
|
Icon(Icons.assignment, |
|
|
|
|
|
size: 16, color: Colors.grey.shade600), |
|
|
const SizedBox(width: 8), |
|
|
const SizedBox(width: 8), |
|
|
const Text('Statut: ', style: TextStyle(fontWeight: FontWeight.w500)), |
|
|
const Text('Statut: ', |
|
|
|
|
|
style: TextStyle(fontWeight: FontWeight.w500)), |
|
|
Container( |
|
|
Container( |
|
|
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4), |
|
|
padding: const EdgeInsets.symmetric( |
|
|
|
|
|
horizontal: 8, vertical: 4), |
|
|
decoration: BoxDecoration( |
|
|
decoration: BoxDecoration( |
|
|
color: _getStatutColor(commande.statut).withOpacity(0.2), |
|
|
color: |
|
|
|
|
|
_getStatutColor(commande.statut).withOpacity(0.2), |
|
|
borderRadius: BorderRadius.circular(12), |
|
|
borderRadius: BorderRadius.circular(12), |
|
|
), |
|
|
), |
|
|
child: Text( |
|
|
child: Text( |
|
|
@ -618,7 +637,8 @@ Future<void> _loadPointsDeVenteWithDefault() async { |
|
|
), |
|
|
), |
|
|
title: Text( |
|
|
title: Text( |
|
|
detail.produitNom ?? 'Produit inconnu', |
|
|
detail.produitNom ?? 'Produit inconnu', |
|
|
style: const TextStyle(fontWeight: FontWeight.w500), |
|
|
style: |
|
|
|
|
|
const TextStyle(fontWeight: FontWeight.w500), |
|
|
), |
|
|
), |
|
|
subtitle: Text( |
|
|
subtitle: Text( |
|
|
'${detail.quantite} x ${detail.prixUnitaire.toStringAsFixed(2)} MGA', |
|
|
'${detail.quantite} x ${detail.prixUnitaire.toStringAsFixed(2)} MGA', |
|
|
@ -643,7 +663,8 @@ Future<void> _loadPointsDeVenteWithDefault() async { |
|
|
style: ElevatedButton.styleFrom( |
|
|
style: ElevatedButton.styleFrom( |
|
|
backgroundColor: Colors.blue.shade800, |
|
|
backgroundColor: Colors.blue.shade800, |
|
|
foregroundColor: Colors.white, |
|
|
foregroundColor: Colors.white, |
|
|
padding: const EdgeInsets.symmetric(vertical: 14), // Plus compact |
|
|
padding: const EdgeInsets.symmetric( |
|
|
|
|
|
vertical: 14), // Plus compact |
|
|
shape: RoundedRectangleBorder( |
|
|
shape: RoundedRectangleBorder( |
|
|
borderRadius: BorderRadius.circular(12), |
|
|
borderRadius: BorderRadius.circular(12), |
|
|
), |
|
|
), |
|
|
@ -817,7 +838,7 @@ Future<void> _loadPointsDeVenteWithDefault() async { |
|
|
), |
|
|
), |
|
|
), |
|
|
), |
|
|
Text( |
|
|
Text( |
|
|
'${_selectedPointDeVente}', |
|
|
'PV: ${_selectedPointDeVente}', |
|
|
style: const TextStyle( |
|
|
style: const TextStyle( |
|
|
fontWeight: FontWeight.w500, |
|
|
fontWeight: FontWeight.w500, |
|
|
fontSize: 14, |
|
|
fontSize: 14, |
|
|
@ -846,7 +867,8 @@ Future<void> _loadPointsDeVenteWithDefault() async { |
|
|
), |
|
|
), |
|
|
const SizedBox(height: 4), |
|
|
const SizedBox(height: 4), |
|
|
Container( |
|
|
Container( |
|
|
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4), |
|
|
padding: |
|
|
|
|
|
const EdgeInsets.symmetric(horizontal: 8, vertical: 4), |
|
|
decoration: BoxDecoration( |
|
|
decoration: BoxDecoration( |
|
|
color: _getStatutColor(commande.statut).withOpacity(0.2), |
|
|
color: _getStatutColor(commande.statut).withOpacity(0.2), |
|
|
borderRadius: BorderRadius.circular(12), |
|
|
borderRadius: BorderRadius.circular(12), |
|
|
@ -892,7 +914,8 @@ Future<void> _loadPointsDeVenteWithDefault() async { |
|
|
// Sur mobile, on ajoute un bouton pour afficher les filtres dans un modal |
|
|
// Sur mobile, on ajoute un bouton pour afficher les filtres dans un modal |
|
|
if (isMobile) ...[ |
|
|
if (isMobile) ...[ |
|
|
Padding( |
|
|
Padding( |
|
|
padding: const EdgeInsets.symmetric(horizontal: 16.0, vertical: 8.0), |
|
|
padding: |
|
|
|
|
|
const EdgeInsets.symmetric(horizontal: 16.0, vertical: 8.0), |
|
|
child: ElevatedButton.icon( |
|
|
child: ElevatedButton.icon( |
|
|
icon: const Icon(Icons.filter_alt), |
|
|
icon: const Icon(Icons.filter_alt), |
|
|
label: const Text('Filtres'), |
|
|
label: const Text('Filtres'), |
|
|
@ -921,7 +944,8 @@ Future<void> _loadPointsDeVenteWithDefault() async { |
|
|
Padding( |
|
|
Padding( |
|
|
padding: const EdgeInsets.symmetric(horizontal: 16.0), |
|
|
padding: const EdgeInsets.symmetric(horizontal: 16.0), |
|
|
child: Container( |
|
|
child: Container( |
|
|
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8), |
|
|
padding: |
|
|
|
|
|
const EdgeInsets.symmetric(horizontal: 16, vertical: 8), |
|
|
decoration: BoxDecoration( |
|
|
decoration: BoxDecoration( |
|
|
color: Colors.blue.shade50, |
|
|
color: Colors.blue.shade50, |
|
|
borderRadius: BorderRadius.circular(20), |
|
|
borderRadius: BorderRadius.circular(20), |
|
|
|