Browse Source

changement au nivaue de scan

28062025_02
b.razafimandimbihery 5 months ago
parent
commit
55e896775d
  1. 18
      lib/Services/stock_managementDatabase.dart
  2. 6
      lib/Views/DemandeTransfert.dart
  3. 534
      lib/Views/HandleProduct.dart
  4. 283
      lib/Views/commandManagement.dart
  5. 274
      lib/Views/newCommand.dart
  6. 4
      lib/config/DatabaseConfig.dart

18
lib/Services/stock_managementDatabase.dart

@ -2579,6 +2579,22 @@ Future<int> validerTransfert(int demandeId, int validateurId) async {
final sourceId = fields['point_de_vente_source_id'] as int;
final destinationId = fields['point_de_vente_destination_id'] as int;
final getpointDeventeSource = await db.query(
'Select point_de_vente_source_id FROM demandes_transfert WHERE id=?',[demandeId]
);
final getpointDeventeDest = await db.query(
'Select point_de_vente_destination_id FROM demandes_transfert WHERE id=?',[demandeId]
);
final getpointDeventeSourceValue = getpointDeventeSource.first.fields['point_de_vente_source_id'];
final getpointDeventedestValue = getpointDeventeDest.first.fields['point_de_vente_destination_id'];
if(getpointDeventeSourceValue==getpointDeventedestValue){
await db.query('update products set point_de_vente_id=? where id = ?',[getpointDeventedestValue,produitId]);
}else{
// 2. Vérifier le stock source
final stockSource = await db.query(
'SELECT stock FROM products WHERE id = ? AND point_de_vente_id = ? FOR UPDATE',
@ -2646,7 +2662,7 @@ Future<int> validerTransfert(int demandeId, int validateurId) async {
null, // IMEI doit être unique donc on ne le copie pas
]);
}
}
// 5. Mettre à jour le statut de la demande
await db.query('''
UPDATE demandes_transfert

6
lib/Views/DemandeTransfert.dart

@ -205,7 +205,7 @@ class _GestionTransfertsPageState extends State<GestionTransfertsPage> with Tick
),
Text('Référence: ${demande['produit_reference']}'),
Text('Quantité: ${demande['quantite']}'),
Text('De: ${demande['point_vente_source']}'),
Text(demande['point_vente_source'] == demande['point_vente_destination']?'De: Non specifier' : 'De: ${demande['point_vente_source']}'),
Text('Vers: ${demande['point_vente_destination']}'),
Text(
'Stock disponible: $stockDisponible',
@ -602,7 +602,7 @@ class _GestionTransfertsPageState extends State<GestionTransfertsPage> with Tick
statutIcon = Icons.check_circle;
statutText = 'Validée';
break;
case 'rejetee':
case 'refusee':
statutColor = Colors.red;
statutIcon = Icons.cancel;
statutText = 'Rejetée';
@ -695,7 +695,7 @@ class _GestionTransfertsPageState extends State<GestionTransfertsPage> with Tick
const SizedBox(width: 8),
Expanded(
child: Text(
'${demande['point_vente_source'] ?? 'N/A'}',
demande['point_vente_source']==demande['point_vente_destination']?"Non specifier" : '${demande['point_vente_source'] ?? 'N/A'}',
style: const TextStyle(fontWeight: FontWeight.w500),
),
),

534
lib/Views/HandleProduct.dart

@ -26,6 +26,7 @@ class ProductManagementPage extends StatefulWidget {
class _ProductManagementPageState extends State<ProductManagementPage> {
final AppDatabase _productDatabase = AppDatabase.instance;
final AppDatabase _appDatabase = AppDatabase.instance;
final UserController _userController = Get.find<UserController>();
List<Product> _products = [];
@ -99,6 +100,494 @@ bool _isUserSuperAdmin() {
bool autoGenerateReference = true;
bool showAddNewPoint = false;
// 🎨 Widget pour les cartes d'information
Widget _buildInfoCard(String label, String value, IconData icon, Color color) {
return Container(
padding: const EdgeInsets.all(12),
decoration: BoxDecoration(
color: color.withOpacity(0.1),
borderRadius: BorderRadius.circular(8),
border: Border.all(color: color.withOpacity(0.3)),
),
child: Column(
children: [
Icon(icon, color: color, size: 20),
const SizedBox(height: 4),
Text(
label,
style: TextStyle(
fontSize: 10,
color: Colors.grey.shade600,
fontWeight: FontWeight.w500,
),
textAlign: TextAlign.center,
),
Text(
value,
style: TextStyle(
fontSize: 12,
fontWeight: FontWeight.bold,
color: color,
),
textAlign: TextAlign.center,
),
],
),
);
}
// 🎨 Widget pour les étapes de transfert
Widget _buildTransferStep(String label, String pointDeVente, IconData icon, Color color) {
return Container(
padding: const EdgeInsets.all(12),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(8),
border: Border.all(color: color.withOpacity(0.3)),
),
child: Column(
children: [
Container(
padding: const EdgeInsets.all(6),
decoration: BoxDecoration(
color: color.withOpacity(0.1),
borderRadius: BorderRadius.circular(6),
),
child: Icon(icon, color: color, size: 16),
),
const SizedBox(height: 6),
Text(
label,
style: TextStyle(
fontSize: 10,
color: Colors.grey.shade600,
fontWeight: FontWeight.bold,
),
),
Text(
pointDeVente,
style: TextStyle(
fontSize: 11,
fontWeight: FontWeight.w600,
color: color,
),
textAlign: TextAlign.center,
maxLines: 2,
overflow: TextOverflow.ellipsis,
),
],
),
);
}
// 🎨 INTERFACE AMÉLIORÉE: Dialog moderne pour demande de transfert
Future<void> _showDemandeTransfertDialog(Product product) async {
final quantiteController = TextEditingController(text: '1');
final notesController = TextEditingController();
final _formKey = GlobalKey<FormState>();
// Récupérer les infos du point de vente source
final pointDeVenteSource = await _appDatabase.getPointDeVenteNomById(product.pointDeVenteId ?? 0);
final pointDeVenteDestination = await _appDatabase.getPointDeVenteNomById(_userController.pointDeVenteId);
await showDialog(
context: context,
barrierDismissible: false,
builder: (context) => AlertDialog(
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(16)),
contentPadding: EdgeInsets.zero,
content: Container(
width: 400,
child: SingleChildScrollView(
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
// En-tête avec design moderne
Container(
width: double.infinity,
padding: const EdgeInsets.all(20),
decoration: BoxDecoration(
gradient: LinearGradient(
colors: [Colors.blue.shade600, Colors.blue.shade700],
begin: Alignment.topLeft,
end: Alignment.bottomRight,
),
borderRadius: const BorderRadius.only(
topLeft: Radius.circular(16),
topRight: Radius.circular(16),
),
),
child: Column(
children: [
Icon(
Icons.swap_horizontal_circle,
size: 48,
color: Colors.white,
),
const SizedBox(height: 8),
Text(
'Demande de transfert',
style: TextStyle(
fontSize: 20,
fontWeight: FontWeight.bold,
color: Colors.white,
),
),
Text(
'Transférer un produit entre points de vente',
style: TextStyle(
fontSize: 14,
color: Colors.white.withOpacity(0.9),
),
),
],
),
),
// Contenu principal
Padding(
padding: const EdgeInsets.all(20),
child: Form(
key: _formKey,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// Informations du produit
Container(
width: double.infinity,
padding: const EdgeInsets.all(16),
decoration: BoxDecoration(
color: Colors.grey.shade50,
borderRadius: BorderRadius.circular(12),
border: Border.all(color: Colors.grey.shade200),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
Container(
padding: const EdgeInsets.all(8),
decoration: BoxDecoration(
color: Colors.blue.shade100,
borderRadius: BorderRadius.circular(8),
),
child: Icon(
Icons.inventory_2,
color: Colors.blue.shade700,
size: 20,
),
),
const SizedBox(width: 12),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'Produit à transférer',
style: TextStyle(
fontSize: 12,
color: Colors.grey.shade600,
fontWeight: FontWeight.w500,
),
),
Text(
product.name,
style: const TextStyle(
fontSize: 16,
fontWeight: FontWeight.bold,
),
),
],
),
),
],
),
const SizedBox(height: 12),
Row(
children: [
Expanded(
child: _buildInfoCard(
'Prix unitaire',
'${product.price.toStringAsFixed(2)} MGA',
Icons.attach_money,
Colors.green,
),
),
const SizedBox(width: 8),
Expanded(
child: _buildInfoCard(
'Stock disponible',
'${product.stock ?? 0}',
Icons.inventory,
product.stock != null && product.stock! > 0
? Colors.green
: Colors.red,
),
),
],
),
if (product.reference != null && product.reference!.isNotEmpty) ...[
const SizedBox(height: 8),
Text(
'Référence: ${product.reference}',
style: TextStyle(
fontSize: 12,
color: Colors.grey.shade600,
fontFamily: 'monospace',
),
),
],
],
),
),
const SizedBox(height: 20),
// Informations de transfert
Container(
width: double.infinity,
padding: const EdgeInsets.all(16),
decoration: BoxDecoration(
color: Colors.orange.shade50,
borderRadius: BorderRadius.circular(12),
border: Border.all(color: Colors.orange.shade200),
),
child: Column(
children: [
Row(
children: [
Icon(Icons.arrow_forward, color: Colors.orange.shade700),
const SizedBox(width: 8),
Text(
'Informations de transfert',
style: TextStyle(
fontSize: 14,
fontWeight: FontWeight.bold,
color: Colors.orange.shade700,
),
),
],
),
const SizedBox(height: 12),
Row(
children: [
Expanded(
child: _buildTransferStep(
'DE',
pointDeVenteSource ?? 'Chargement...',
Icons.store_outlined,
Colors.red.shade600,
),
),
Container(
margin: const EdgeInsets.symmetric(horizontal: 8),
child: Icon(
Icons.arrow_forward,
color: Colors.orange.shade700,
size: 24,
),
),
Expanded(
child: _buildTransferStep(
'VERS',
pointDeVenteDestination ?? 'Chargement...',
Icons.store,
Colors.green.shade600,
),
),
],
),
],
),
),
const SizedBox(height: 20),
// Champ quantité avec design amélioré
Text(
'Quantité à transférer',
style: TextStyle(
fontSize: 14,
fontWeight: FontWeight.w600,
color: Colors.grey.shade700,
),
),
const SizedBox(height: 8),
Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(12),
border: Border.all(color: Colors.grey.shade300),
),
child: Row(
children: [
IconButton(
onPressed: () {
int currentQty = int.tryParse(quantiteController.text) ?? 1;
if (currentQty > 1) {
quantiteController.text = (currentQty - 1).toString();
}
},
icon: Icon(Icons.remove, color: Colors.grey.shade600),
),
Expanded(
child: TextFormField(
controller: quantiteController,
decoration: const InputDecoration(
border: InputBorder.none,
contentPadding: EdgeInsets.symmetric(horizontal: 16),
hintText: 'Quantité',
),
textAlign: TextAlign.center,
keyboardType: TextInputType.number,
style: const TextStyle(
fontSize: 16,
fontWeight: FontWeight.bold,
),
validator: (value) {
if (value == null || value.isEmpty) {
return 'Veuillez entrer une quantité';
}
final qty = int.tryParse(value) ?? 0;
if (qty <= 0) {
return 'Quantité invalide';
}
if (product.stock != null && qty > product.stock!) {
return 'Quantité supérieure au stock disponible';
}
return null;
},
),
),
IconButton(
onPressed: () {
int currentQty = int.tryParse(quantiteController.text) ?? 1;
int maxStock = product.stock ?? 999;
if (currentQty < maxStock) {
quantiteController.text = (currentQty + 1).toString();
}
},
icon: Icon(Icons.add, color: Colors.grey.shade600),
),
],
),
),
// Boutons d'action avec design moderne
Row(
children: [
Expanded(
child: TextButton(
onPressed: () => Navigator.pop(context),
style: TextButton.styleFrom(
padding: const EdgeInsets.symmetric(vertical: 16),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12),
side: BorderSide(color: Colors.grey.shade300),
),
),
child: Text(
'Annuler',
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.w600,
color: Colors.grey.shade700,
),
),
),
),
const SizedBox(width: 12),
Expanded(
flex: 2,
child: ElevatedButton.icon(
onPressed: () async {
if (!_formKey.currentState!.validate()) return;
final qty = int.tryParse(quantiteController.text) ?? 0;
if (qty <= 0) {
Get.snackbar(
'Erreur',
'Quantité invalide',
snackPosition: SnackPosition.BOTTOM,
backgroundColor: Colors.red,
colorText: Colors.white,
);
return;
}
try {
setState(() => _isLoading = true);
Navigator.pop(context);
await _appDatabase.createDemandeTransfert(
produitId: product.id!,
pointDeVenteSourceId: product.pointDeVenteId!,
pointDeVenteDestinationId: _userController.pointDeVenteId,
demandeurId: _userController.userId,
quantite: qty,
notes: notesController.text.isNotEmpty
? notesController.text
: 'Demande de transfert depuis l\'application mobile',
);
Get.snackbar(
'Demande envoyée ✅',
'Votre demande de transfert de $qty unité(s) a été enregistrée et sera traitée prochainement.',
snackPosition: SnackPosition.BOTTOM,
backgroundColor: Colors.green,
colorText: Colors.white,
duration: const Duration(seconds: 4),
icon: const Icon(Icons.check_circle, color: Colors.white),
);
} catch (e) {
Get.snackbar(
'Erreur',
'Impossible d\'envoyer la demande: ${e.toString()}',
snackPosition: SnackPosition.BOTTOM,
backgroundColor: Colors.red,
colorText: Colors.white,
duration: const Duration(seconds: 4),
);
} finally {
setState(() => _isLoading = false);
}
},
icon: const Icon(Icons.send, color: Colors.white),
label: const Text(
'Envoyer la demande',
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.bold,
color: Colors.white,
),
),
style: ElevatedButton.styleFrom(
backgroundColor: Colors.blue.shade600,
padding: const EdgeInsets.symmetric(vertical: 16),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12),
),
elevation: 2,
),
),
),
],
),
],
),
),
),
],
),
),
),
),
);
}
// Fonction pour mettre à jour le QR preview
void updateQrPreview() {
if (nameController.text.isNotEmpty) {
@ -1253,25 +1742,32 @@ bool _isUserSuperAdmin() {
}
// Assigner le point de vente de l'utilisateur au produit
final updatedProduct = Product(
id: foundProduct.id,
name: foundProduct.name,
price: foundProduct.price,
image: foundProduct.image,
category: foundProduct.category,
description: foundProduct.description,
stock: foundProduct.stock,
qrCode: foundProduct.qrCode,
reference: foundProduct.reference,
marque: foundProduct.marque,
ram: foundProduct.ram,
memoireInterne: foundProduct.memoireInterne,
imei: foundProduct.imei,
pointDeVenteId:
_userController.pointDeVenteId, // Nouveau point de vente
// final updatedProduct = Product(
// id: foundProduct.id,
// name: foundProduct.name,
// price: foundProduct.price,
// image: foundProduct.image,
// category: foundProduct.category,
// description: foundProduct.description,
// stock: foundProduct.stock,
// qrCode: foundProduct.qrCode,
// reference: foundProduct.reference,
// marque: foundProduct.marque,
// ram: foundProduct.ram,
// memoireInterne: foundProduct.memoireInterne,
// imei: foundProduct.imei,
// pointDeVenteId:
// _userController.pointDeVenteId, // Nouveau point de vente
// );
await _appDatabase.createDemandeTransfert(
produitId: foundProduct.id!,
pointDeVenteSourceId: _userController.pointDeVenteId,
pointDeVenteDestinationId: _userController.pointDeVenteId,
demandeurId: _userController.userId,
quantite: foundProduct.stock,
notes: 'produit non assigner',
);
await _productDatabase.updateProduct(updatedProduct);
// await _productDatabase.updateProduct(updatedProduct);
// Recharger les produits pour refléter les changements
_loadProducts();
@ -1311,7 +1807,7 @@ bool _isUserSuperAdmin() {
child: Icon(Icons.check_circle, color: Colors.green.shade700),
),
const SizedBox(width: 12),
const Expanded(child: Text('Attribution réussie !')),
const Expanded(child: Text( 'demande attribution réussie en attente de validation!')),
],
),
content: Column(

283
lib/Views/commandManagement.dart

@ -251,6 +251,7 @@ Future<pw.Widget> buildIconGift() async {
}
// Bon de livraison==============================================
Future<void> _generateBonLivraison(Commande commande) async {
final details = await _database.getDetailsCommande(commande.id!);
final client = await _database.getClientById(commande.clientId);
@ -297,23 +298,25 @@ Future<void> _generateBonLivraison(Commande commande) async {
final image = pw.MemoryImage(imageBytes);
final italicFont = pw.Font.ttf(await rootBundle.load('assets/fonts/Roboto-Italic.ttf'));
// Tailles de texte adaptées pour côte à côte
final tinyTextStyle = pw.TextStyle(fontSize: 5);
final smallTextStyle = pw.TextStyle(fontSize: 6);
final normalTextStyle = pw.TextStyle(fontSize: 7);
final boldTextStyle = pw.TextStyle(fontSize: 7, fontWeight: pw.FontWeight.bold);
final boldClientStyle = pw.TextStyle(fontSize: 8, fontWeight: pw.FontWeight.bold);
final frameTextStyle = pw.TextStyle(fontSize: 6);
final italicTextStyle = pw.TextStyle(fontSize: 5, fontWeight: pw.FontWeight.bold, font: italicFont);
final italicLogoStyle = pw.TextStyle(fontSize: 4, fontWeight: pw.FontWeight.bold, font: italicFont);
final titleStyle = pw.TextStyle(fontSize: 9, fontWeight: pw.FontWeight.bold);
// Fonction pour créer un exemplaire
pw.Widget buildExemplaire(String typeExemplaire, {bool isSecond = false}) {
// Tailles de texte agrandies pour une meilleure lisibilité
final tinyTextStyle = pw.TextStyle(fontSize: 9);
final smallTextStyle = pw.TextStyle(fontSize: 10);
final normalTextStyle = pw.TextStyle(fontSize: 11);
final boldTextStyle = pw.TextStyle(fontSize: 11, fontWeight: pw.FontWeight.bold);
final boldClientStyle = pw.TextStyle(fontSize: 12, fontWeight: pw.FontWeight.bold);
final frameTextStyle = pw.TextStyle(fontSize: 10);
final italicTextStyle = pw.TextStyle(fontSize: 9, fontWeight: pw.FontWeight.bold, font: italicFont);
final italicLogoStyle = pw.TextStyle(fontSize: 8, fontWeight: pw.FontWeight.bold, font: italicFont);
final titleStyle = pw.TextStyle(fontSize: 14, fontWeight: pw.FontWeight.bold);
final headerStyle = pw.TextStyle(fontSize: 12, fontWeight: pw.FontWeight.bold);
// Fonction pour créer un exemplaire en mode paysage
pw.Widget buildExemplaire(String typeExemplaire) {
return pw.Container(
height: 380, // Hauteur ajustée pour le mode paysage
width: double.infinity,
decoration: pw.BoxDecoration(
border: pw.Border.all(color: PdfColors.black, width: 1),
border: pw.Border.all(color: PdfColors.black, width: 1.5),
),
child: pw.Column(
crossAxisAlignment: pw.CrossAxisAlignment.start,
@ -321,7 +324,7 @@ Future<void> _generateBonLivraison(Commande commande) async {
// En-tête avec indication de l'exemplaire
pw.Container(
width: double.infinity,
padding: const pw.EdgeInsets.all(3),
padding: const pw.EdgeInsets.all(5),
decoration: pw.BoxDecoration(
color: typeExemplaire == "CLIENT" ? PdfColors.blue100 : PdfColors.green100,
),
@ -329,7 +332,7 @@ Future<void> _generateBonLivraison(Commande commande) async {
child: pw.Text(
'BON DE LIVRAISON - EXEMPLAIRE $typeExemplaire',
style: pw.TextStyle(
fontSize: 8,
fontSize: 14,
fontWeight: pw.FontWeight.bold,
color: typeExemplaire == "CLIENT" ? PdfColors.blue800 : PdfColors.green800,
),
@ -337,8 +340,9 @@ Future<void> _generateBonLivraison(Commande commande) async {
),
),
pw.Padding(
padding: const pw.EdgeInsets.all(6),
pw.Expanded(
child: pw.Padding(
padding: const pw.EdgeInsets.all(8),
child: pw.Column(
crossAxisAlignment: pw.CrossAxisAlignment.start,
children: [
@ -347,27 +351,29 @@ Future<void> _generateBonLivraison(Commande commande) async {
crossAxisAlignment: pw.CrossAxisAlignment.start,
mainAxisAlignment: pw.MainAxisAlignment.spaceBetween,
children: [
// Logo et infos entreprise - très compact
// Logo et infos entreprise
pw.Column(
crossAxisAlignment: pw.CrossAxisAlignment.start,
children: [
pw.Container(
width: 45,
height: 45,
width: 100,
height: 100,
child: pw.Image(image),
),
pw.Text('NOTRE COMPETENCE, A VOTRE SERVICE', style: italicLogoStyle),
pw.SizedBox(height: 3),
pw.Text('NOTRE COMPETENCE, A VOTRE SERVICE', style: italicLogoStyle),
pw.SizedBox(height: 4),
pw.Column(
crossAxisAlignment: pw.CrossAxisAlignment.start,
children: [
pw.Text('📍 REMAX Andravoangy', style: tinyTextStyle),
pw.Text('📍 SUPREME CENTER Behoririka', style: tinyTextStyle),
pw.Text('📍 SUPREME CENTER Behoririka \n BOX 405 | 416 | 119', style: tinyTextStyle),
pw.Text('📍 Tripolisa analankely BOX 7', style: tinyTextStyle),
pw.Text('📞 033 37 808 18', style: tinyTextStyle),
pw.Text('🌐 www.guycom.mg', style: tinyTextStyle),
pw.SizedBox(height: 1),
// Ajout du NIF
pw.Text('NIF: 1026/GC78-20-02-22', style: pw.TextStyle(fontSize: 5, fontWeight: pw.FontWeight.bold)),
pw.SizedBox(height: 2),
pw.Text('NIF: 1026/GC78-20-02-22',
style: pw.TextStyle(fontSize: 7, fontWeight: pw.FontWeight.bold)),
],
),
],
@ -378,11 +384,11 @@ Future<void> _generateBonLivraison(Commande commande) async {
crossAxisAlignment: pw.CrossAxisAlignment.center,
children: [
pw.Text('Date: ${DateFormat('dd/MM/yyyy').format(DateTime.now())}', style: boldClientStyle),
pw.SizedBox(height: 3),
pw.Container(width: 80, height: 1, color: PdfColors.black),
pw.SizedBox(height: 3),
pw.SizedBox(height: 4),
pw.Container(width: 100, height: 2, color: PdfColors.black),
pw.SizedBox(height: 4),
pw.Container(
padding: const pw.EdgeInsets.all(3),
padding: const pw.EdgeInsets.all(6),
decoration: pw.BoxDecoration(
border: pw.Border.all(color: PdfColors.black),
),
@ -390,7 +396,7 @@ Future<void> _generateBonLivraison(Commande commande) async {
children: [
pw.Text('Boutique:', style: frameTextStyle),
pw.Text('${pointDeVente?['nom'] ?? 'S405A'}', style: boldTextStyle),
pw.SizedBox(height: 1),
pw.SizedBox(height: 2),
pw.Text('Bon N°:', style: frameTextStyle),
pw.Text('${pointDeVente?['nom'] ?? 'S405A'}-P${commande.id}', style: boldTextStyle),
],
@ -399,22 +405,22 @@ Future<void> _generateBonLivraison(Commande commande) async {
],
),
// Informations client - compact
// Informations client
pw.Container(
width: 100,
width: 120,
decoration: pw.BoxDecoration(
border: pw.Border.all(color: PdfColors.black, width: 1),
),
padding: const pw.EdgeInsets.all(4),
padding: const pw.EdgeInsets.all(6),
child: pw.Column(
crossAxisAlignment: pw.CrossAxisAlignment.center,
children: [
pw.Text('CLIENT', style: frameTextStyle),
pw.SizedBox(height: 1),
pw.SizedBox(height: 2),
pw.Text('ID: ${pointDeVente?['nom'] ?? 'S405A'}-${client?.id ?? 'Non spécifié'}', style: smallTextStyle),
pw.Container(width: 80, height: 1, color: PdfColors.black, margin: const pw.EdgeInsets.symmetric(vertical: 1)),
pw.Text(client?.nom ?? 'Non spécifié', style: boldTextStyle),
pw.SizedBox(height: 1),
pw.Container(width: 100, height: 1, color: PdfColors.black, margin: const pw.EdgeInsets.symmetric(vertical: 2)),
pw.Text('${client?.nom} \n ${client?.prenom}', style: boldTextStyle),
pw.SizedBox(height: 2),
pw.Text(client?.telephone ?? 'Non spécifié', style: tinyTextStyle),
],
),
@ -422,27 +428,33 @@ Future<void> _generateBonLivraison(Commande commande) async {
],
),
pw.SizedBox(height: 4),
pw.SizedBox(height: 8),
// Tableau des produits - très compact
pw.Table(
border: pw.TableBorder.all(width: 0.5),
// Tableau des produits (ajusté pour le mode paysage)
pw.Expanded(
child: pw.Table(
border: pw.TableBorder.all(width: 1),
columnWidths: {
0: const pw.FlexColumnWidth(3.5),
1: const pw.FlexColumnWidth(0.8),
2: const pw.FlexColumnWidth(1.2),
0: const pw.FlexColumnWidth(5),
1: const pw.FlexColumnWidth(1.2),
2: const pw.FlexColumnWidth(1.5),
3: const pw.FlexColumnWidth(1.5),
4: const pw.FlexColumnWidth(1.2),
4: const pw.FlexColumnWidth(1.5),
},
children: [
pw.TableRow(
decoration: const pw.BoxDecoration(color: PdfColors.grey200),
children: [
pw.Padding(padding: const pw.EdgeInsets.all(1), child: pw.Text('Désignations', style: boldTextStyle)),
pw.Padding(padding: const pw.EdgeInsets.all(1), child: pw.Text('Qté', style: boldTextStyle, textAlign: pw.TextAlign.center)),
pw.Padding(padding: const pw.EdgeInsets.all(1), child: pw.Text('P.U.', style: boldTextStyle, textAlign: pw.TextAlign.right)),
pw.Padding(padding: const pw.EdgeInsets.all(1), child: pw.Text('Remise/Cadeau', style: boldTextStyle, textAlign: pw.TextAlign.center)),
pw.Padding(padding: const pw.EdgeInsets.all(1), child: pw.Text('Montant', style: boldTextStyle, textAlign: pw.TextAlign.right)),
pw.Padding(padding: const pw.EdgeInsets.all(3),
child: pw.Text('Désignations', style: boldTextStyle)),
pw.Padding(padding: const pw.EdgeInsets.all(3),
child: pw.Text('Qté', style: boldTextStyle, textAlign: pw.TextAlign.center)),
pw.Padding(padding: const pw.EdgeInsets.all(3),
child: pw.Text('P.U.', style: boldTextStyle, textAlign: pw.TextAlign.right)),
// pw.Padding(padding: const pw.EdgeInsets.all(3),
// child: pw.Text('Remise/Cadeau', style: boldTextStyle, textAlign: pw.TextAlign.center)),
pw.Padding(padding: const pw.EdgeInsets.all(3),
child: pw.Text('Montant', style: boldTextStyle, textAlign: pw.TextAlign.right)),
],
),
@ -458,7 +470,7 @@ Future<void> _generateBonLivraison(Commande commande) async {
: null,
children: [
pw.Padding(
padding: const pw.EdgeInsets.all(1),
padding: const pw.EdgeInsets.all(3),
child: pw.Column(
crossAxisAlignment: pw.CrossAxisAlignment.start,
children: [
@ -466,78 +478,79 @@ Future<void> _generateBonLivraison(Commande commande) async {
children: [
pw.Expanded(
child: pw.Text(detail.produitNom ?? 'Produit inconnu',
style: pw.TextStyle(fontSize: 6, fontWeight: pw.FontWeight.bold)),
style: pw.TextStyle(fontSize: 10, fontWeight: pw.FontWeight.bold)),
),
if (detail.estCadeau)
pw.Container(
padding: const pw.EdgeInsets.symmetric(horizontal: 1, vertical: 0.5),
padding: const pw.EdgeInsets.symmetric(horizontal: 2, vertical: 1),
decoration: pw.BoxDecoration(
color: PdfColors.green,
borderRadius: pw.BorderRadius.circular(2),
),
child: pw.Text('🎁', style: pw.TextStyle(fontSize: 4, color: PdfColors.white)),
child: pw.Text('🎁', style: pw.TextStyle(fontSize: 5, color: PdfColors.white)),
),
],
),
if (produit?.category != null && produit!.category.isNotEmpty)
pw.Text('${produit.category}${produit?.marque != null && produit!.marque.isNotEmpty ? ' - ${produit.marque}' : ''}', style: tinyTextStyle),
pw.Text('${produit.category}${produit?.marque != null && produit!.marque.isNotEmpty ? ' - ${produit.marque}' : ''}',
style: tinyTextStyle),
if (produit?.imei != null && produit!.imei!.isNotEmpty)
pw.Text('IMEI: ${produit.imei}', style: tinyTextStyle),
],
),
),
pw.Padding(
padding: const pw.EdgeInsets.all(1),
padding: const pw.EdgeInsets.all(3),
child: pw.Text('${detail.quantite}', style: normalTextStyle, textAlign: pw.TextAlign.center),
),
pw.Padding(
padding: const pw.EdgeInsets.all(1),
padding: const pw.EdgeInsets.all(3),
child: pw.Column(
crossAxisAlignment: pw.CrossAxisAlignment.end,
children: [
if (detail.estCadeau) ...[
pw.Text('${detail.prixUnitaire.toStringAsFixed(0)}',
style: pw.TextStyle(fontSize: 4, decoration: pw.TextDecoration.lineThrough, color: PdfColors.grey600)),
pw.Text('GRATUIT', style: pw.TextStyle(fontSize: 5, color: PdfColors.green700, fontWeight: pw.FontWeight.bold)),
style: pw.TextStyle(fontSize: 8, decoration: pw.TextDecoration.lineThrough, color: PdfColors.grey600)),
pw.Text('GRATUIT', style: pw.TextStyle(fontSize: 9, color: PdfColors.green700, fontWeight: pw.FontWeight.bold)),
] else if (detail.aRemise) ...[
pw.Text('${detail.prixUnitaire.toStringAsFixed(0)}',
style: pw.TextStyle(fontSize: 4, decoration: pw.TextDecoration.lineThrough, color: PdfColors.grey600)),
style: pw.TextStyle(fontSize: 8, decoration: pw.TextDecoration.lineThrough, color: PdfColors.grey600)),
pw.Text('${(detail.prixFinal / detail.quantite).toStringAsFixed(0)}',
style: pw.TextStyle(fontSize: 6, color: PdfColors.orange)),
style: pw.TextStyle(fontSize: 9, color: PdfColors.orange)),
] else
pw.Text('${detail.prixUnitaire.toStringAsFixed(0)}', style: smallTextStyle),
],
),
),
// pw.Padding(
// padding: const pw.EdgeInsets.all(3),
// child: pw.Text(
// detail.estCadeau
// ? 'CADEAU'
// : detail.aRemise
// ? 'REMISE'
// : '-',
// style: pw.TextStyle(
// fontSize: 9,
// color: detail.estCadeau ? PdfColors.green700 : detail.aRemise ? PdfColors.orange : PdfColors.grey600,
// fontWeight: detail.estCadeau ? pw.FontWeight.bold : pw.FontWeight.normal,
// ),
// textAlign: pw.TextAlign.center,
// ),
// ),
pw.Padding(
padding: const pw.EdgeInsets.all(1),
child: pw.Text(
detail.estCadeau
? 'CADEAU'
: detail.aRemise
? 'REMISE'
: '-',
style: pw.TextStyle(
fontSize: 5,
color: detail.estCadeau ? PdfColors.green700 : detail.aRemise ? PdfColors.orange : PdfColors.grey600,
fontWeight: detail.estCadeau ? pw.FontWeight.bold : pw.FontWeight.normal,
),
textAlign: pw.TextAlign.center,
),
),
pw.Padding(
padding: const pw.EdgeInsets.all(1),
padding: const pw.EdgeInsets.all(3),
child: pw.Column(
crossAxisAlignment: pw.CrossAxisAlignment.end,
children: [
if (detail.estCadeau) ...[
pw.Text('${detail.sousTotal.toStringAsFixed(0)}',
style: pw.TextStyle(fontSize: 4, decoration: pw.TextDecoration.lineThrough, color: PdfColors.grey600)),
pw.Text('GRATUIT', style: pw.TextStyle(fontSize: 5, fontWeight: pw.FontWeight.bold, color: PdfColors.green700)),
style: pw.TextStyle(fontSize: 8, decoration: pw.TextDecoration.lineThrough, color: PdfColors.grey600)),
pw.Text('GRATUIT', style: pw.TextStyle(fontSize: 9, fontWeight: pw.FontWeight.bold, color: PdfColors.green700)),
] else if (detail.aRemise) ...[
pw.Text('${detail.sousTotal.toStringAsFixed(0)}',
style: pw.TextStyle(fontSize: 4, decoration: pw.TextDecoration.lineThrough, color: PdfColors.grey600)),
pw.Text('${detail.prixFinal.toStringAsFixed(0)}', style: pw.TextStyle(fontSize: 6, fontWeight: pw.FontWeight.bold)),
style: pw.TextStyle(fontSize: 8, decoration: pw.TextDecoration.lineThrough, color: PdfColors.grey600)),
pw.Text('${detail.prixFinal.toStringAsFixed(0)}', style: pw.TextStyle(fontSize: 9, fontWeight: pw.FontWeight.bold)),
] else
pw.Text('${detail.prixFinal.toStringAsFixed(0)}', style: smallTextStyle),
],
@ -548,10 +561,11 @@ Future<void> _generateBonLivraison(Commande commande) async {
}).toList(),
],
),
),
pw.SizedBox(height: 4),
pw.SizedBox(height: 8),
// Section finale - très compacte
// Section finale (ajustée pour le mode paysage)
pw.Row(
crossAxisAlignment: pw.CrossAxisAlignment.start,
children: [
@ -566,44 +580,44 @@ Future<void> _generateBonLivraison(Commande commande) async {
mainAxisAlignment: pw.MainAxisAlignment.end,
children: [
pw.Text('SOUS-TOTAL:', style: smallTextStyle),
pw.SizedBox(width: 8),
pw.SizedBox(width: 10),
pw.Text('${sousTotal.toStringAsFixed(0)}', style: smallTextStyle),
],
),
pw.SizedBox(height: 1),
pw.SizedBox(height: 2),
],
if (totalRemises > 0) ...[
pw.Row(
mainAxisAlignment: pw.MainAxisAlignment.end,
children: [
pw.Text('REMISES:', style: pw.TextStyle(color: PdfColors.orange, fontSize: 6)),
pw.SizedBox(width: 8),
pw.Text('-${totalRemises.toStringAsFixed(0)}', style: pw.TextStyle(color: PdfColors.orange, fontSize: 6)),
pw.Text('REMISES:', style: pw.TextStyle(color: PdfColors.orange, fontSize: 10)),
pw.SizedBox(width: 10),
pw.Text('-${totalRemises.toStringAsFixed(0)}', style: pw.TextStyle(color: PdfColors.orange, fontSize: 10)),
],
),
pw.SizedBox(height: 1),
pw.SizedBox(height: 2),
],
if (totalCadeaux > 0) ...[
pw.Row(
mainAxisAlignment: pw.MainAxisAlignment.end,
children: [
pw.Text('CADEAUX ($nombreCadeaux):', style: pw.TextStyle(color: PdfColors.green700, fontSize: 6)),
pw.SizedBox(width: 8),
pw.Text('-${totalCadeaux.toStringAsFixed(0)}', style: pw.TextStyle(color: PdfColors.green700, fontSize: 6)),
pw.Text('CADEAUX ($nombreCadeaux):', style: pw.TextStyle(color: PdfColors.green700, fontSize: 10)),
pw.SizedBox(width: 10),
pw.Text('-${totalCadeaux.toStringAsFixed(0)}', style: pw.TextStyle(color: PdfColors.green700, fontSize: 10)),
],
),
pw.SizedBox(height: 1),
pw.SizedBox(height: 2),
],
pw.Container(width: 100, height: 1, color: PdfColors.black, margin: const pw.EdgeInsets.symmetric(vertical: 1)),
pw.Container(width: 120, height: 1.5, color: PdfColors.black, margin: const pw.EdgeInsets.symmetric(vertical: 2)),
pw.Row(
mainAxisAlignment: pw.MainAxisAlignment.end,
children: [
pw.Text('TOTAL:', style: boldTextStyle),
pw.SizedBox(width: 8),
pw.SizedBox(width: 10),
pw.Text('${commande.montantTotal.toStringAsFixed(0)} MGA', style: boldTextStyle),
],
),
@ -618,7 +632,7 @@ Future<void> _generateBonLivraison(Commande commande) async {
),
child: pw.Text(
'🎁 $nombreCadeaux cadeau(s) offert(s) (${totalCadeaux.toStringAsFixed(0)} MGA)',
style: pw.TextStyle(fontSize: 5, color: PdfColors.green700),
style: pw.TextStyle(fontSize: 9, color: PdfColors.green700),
),
),
],
@ -626,7 +640,7 @@ Future<void> _generateBonLivraison(Commande commande) async {
),
),
pw.SizedBox(width: 10),
pw.SizedBox(width: 15),
// Informations vendeurs et signatures
pw.Expanded(
@ -636,7 +650,7 @@ Future<void> _generateBonLivraison(Commande commande) async {
children: [
// Vendeurs
pw.Container(
padding: const pw.EdgeInsets.all(3),
padding: const pw.EdgeInsets.all(4),
decoration: pw.BoxDecoration(
color: PdfColors.grey100,
borderRadius: pw.BorderRadius.circular(3),
@ -644,8 +658,8 @@ Future<void> _generateBonLivraison(Commande commande) async {
child: pw.Column(
crossAxisAlignment: pw.CrossAxisAlignment.start,
children: [
pw.Text('VENDEURS', style: pw.TextStyle(fontSize: 6, fontWeight: pw.FontWeight.bold)),
pw.SizedBox(height: 1),
pw.Text('VENDEURS', style: pw.TextStyle(fontSize: 10, fontWeight: pw.FontWeight.bold)),
pw.SizedBox(height: 3),
pw.Row(
children: [
pw.Expanded(
@ -655,7 +669,7 @@ Future<void> _generateBonLivraison(Commande commande) async {
pw.Text('Initiateur:', style: tinyTextStyle),
pw.Text(
commandeur != null ? '${commandeur.name} ${commandeur.lastName ?? ''}'.trim() : 'N/A',
style: pw.TextStyle(fontSize: 5),
style: pw.TextStyle(fontSize: 9),
),
],
),
@ -667,7 +681,7 @@ Future<void> _generateBonLivraison(Commande commande) async {
pw.Text('Validateur:', style: tinyTextStyle),
pw.Text(
validateur != null ? '${validateur.name} ${validateur.lastName ?? ''}'.trim() : 'N/A',
style: pw.TextStyle(fontSize: 5),
style: pw.TextStyle(fontSize: 9),
),
],
),
@ -678,7 +692,7 @@ Future<void> _generateBonLivraison(Commande commande) async {
),
),
pw.SizedBox(height: 6),
pw.SizedBox(height: 8),
// Signatures
pw.Row(
@ -686,16 +700,16 @@ Future<void> _generateBonLivraison(Commande commande) async {
children: [
pw.Column(
children: [
pw.Text('Vendeur', style: pw.TextStyle(fontSize: 5, fontWeight: pw.FontWeight.bold)),
pw.SizedBox(height: 8),
pw.Container(width: 50, height: 1, color: PdfColors.black),
pw.Text('Vendeur', style: pw.TextStyle(fontSize: 9, fontWeight: pw.FontWeight.bold)),
pw.SizedBox(height: 15),
pw.Container(width: 70, height: 1, color: PdfColors.black),
],
),
pw.Column(
children: [
pw.Text('Client', style: pw.TextStyle(fontSize: 5, fontWeight: pw.FontWeight.bold)),
pw.SizedBox(height: 8),
pw.Container(width: 50, height: 1, color: PdfColors.black),
pw.Text('Client', style: pw.TextStyle(fontSize: 9, fontWeight: pw.FontWeight.bold)),
pw.SizedBox(height: 15),
pw.Container(width: 70, height: 1, color: PdfColors.black),
],
),
],
@ -706,7 +720,7 @@ Future<void> _generateBonLivraison(Commande commande) async {
],
),
pw.SizedBox(height: 3),
pw.SizedBox(height: 4),
// Note finale
pw.Text(
@ -716,47 +730,51 @@ Future<void> _generateBonLivraison(Commande commande) async {
],
),
),
),
],
),
);
}
// PAGE EN MODE PAYSAGE : Les deux exemplaires sur une seule page
pdf.addPage(
pw.Page(
pageFormat: PdfPageFormat.a4.landscape,
margin: const pw.EdgeInsets.all(10),
pageFormat: PdfPageFormat.a4.landscape, // Mode paysage
margin: const pw.EdgeInsets.all(12),
build: (pw.Context context) {
return pw.Row(
crossAxisAlignment: pw.CrossAxisAlignment.start,
return pw.Row( // Utilisation de Row au lieu de Column pour placer côte à côte
children: [
// Exemplaire CLIENT (à gauche)
// Premier exemplaire (CLIENT)
pw.Expanded(
child: buildExemplaire("CLIENT"),
),
pw.SizedBox(width: 10),
pw.SizedBox(width: 15),
// Ligne de séparation verticale avec ciseaux
pw.Column(
// Trait de séparation vertical
pw.Container(
width: 2,
height: double.infinity,
child: pw.Column(
mainAxisAlignment: pw.MainAxisAlignment.center,
children: [
pw.Text('✂️', style: pw.TextStyle(fontSize: 14)),
pw.SizedBox(height: 10),
pw.Transform.rotate(
angle: 3.14159 / 2, // 90 degrés en radians
child: pw.Text('✂️ DÉCOUPER ICI ✂️', style: pw.TextStyle(fontSize: 8, color: PdfColors.grey600)),
),
pw.Container(
width: 1,
height: 200,
color: PdfColors.grey400,
angle: 1.5708, // 90 degrés en radians (π/2)
child: pw.Text('DÉCOUPER ICI', style: pw.TextStyle(fontSize: 10, fontWeight: pw.FontWeight.bold)),
),
pw.SizedBox(height: 10),
pw.Text('✂️', style: pw.TextStyle(fontSize: 14)),
],
),
),
pw.SizedBox(width: 10),
pw.SizedBox(width: 15),
// Exemplaire MAGASIN (à droite)
// Deuxième exemplaire (MAGASIN)
pw.Expanded(
child: buildExemplaire("MAGASIN", isSecond: true),
child: buildExemplaire("MAGASIN"),
),
],
);
@ -851,7 +869,7 @@ Future<void> _generateInvoice(Commande commande) async {
crossAxisAlignment: pw.CrossAxisAlignment.start,
children: [
pw.Container(
width: 120,
width: 200,
height: 120,
child: pw.Image(image),
),
@ -874,6 +892,7 @@ Future<void> _generateInvoice(Commande commande) async {
pw.SizedBox(height: 8),
pw.Row(children: [iconPhone, pw.SizedBox(width: 4), pw.Text('033 37 808 18', style: smallTextStyle)]),
pw.Row(children: [iconGlobe, pw.SizedBox(width: 4), pw.Text('www.guycom.mg', style: smallTextStyle)]),
pw.Row(children: [iconGlobe, pw.SizedBox(width: 4), pw.Text('NIF: 1026/GC78-20-02-22', style: smallTextStyle)]),
pw.Text('Facebook: GuyCom', style: smallTextStyle),
],
),
@ -946,7 +965,7 @@ Future<void> _generateInvoice(Commande commande) async {
pw.SizedBox(height: 6),
pw.Container(width: 180, height: 1, color: PdfColors.black),
pw.SizedBox(height: 4),
pw.Text(client?.nom ?? 'Non spécifié', style: boldClientTextStyle),
pw.Text('${client?.nom} \n ${client?.prenom}', style: boldTextStyle),
pw.SizedBox(height: 4),
pw.Text(client?.telephone ?? 'Non spécifié', style: frameTextStyle),
],

274
lib/Views/newCommand.dart

@ -608,94 +608,286 @@ void _modifierQuantite(int productId, int nouvelleQuantite) {
}
void _showProductFoundAndAddedDialog(Product product, int newQuantity) {
final isProduitCommandable = _isProduitCommandable(product);
final canRequestTransfer = product.stock != null && product.stock! >= 1;
Get.dialog(
AlertDialog(
title: Row(
Dialog(
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(16)),
child: Container(
padding: const EdgeInsets.all(20),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
// Header avec icône de succès
Container(
width: double.infinity,
padding: const EdgeInsets.all(16),
decoration: BoxDecoration(
color: Colors.green.shade50,
borderRadius: BorderRadius.circular(12),
border: Border.all(color: Colors.green.shade200),
),
child: Row(
children: [
Container(
padding: const EdgeInsets.all(8),
decoration: BoxDecoration(
color: Colors.green.shade100,
borderRadius: BorderRadius.circular(8),
shape: BoxShape.circle,
),
child: Icon(
Icons.check_circle,
color: Colors.green.shade700,
size: 24,
),
child: Icon(Icons.check_circle, color: Colors.green.shade700),
),
const SizedBox(width: 12),
const Expanded(child: Text('Produit identifié et ajouté !')),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Text(
'Produit identifié !',
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.bold,
color: Colors.black87,
),
),
Text(
'Ajouté au panier avec succès',
style: TextStyle(
fontSize: 12,
color: Colors.green.shade700,
fontWeight: FontWeight.w500,
),
),
],
),
content: Column(
mainAxisSize: MainAxisSize.min,
),
],
),
),
const SizedBox(height: 20),
// Informations du produit
Container(
width: double.infinity,
padding: const EdgeInsets.all(16),
decoration: BoxDecoration(
color: Colors.grey.shade50,
borderRadius: BorderRadius.circular(12),
border: Border.all(color: Colors.grey.shade200),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
product.name,
style: const TextStyle(
fontSize: 16,
fontSize: 18,
fontWeight: FontWeight.bold,
color: Colors.black87,
),
),
const SizedBox(height: 8),
const SizedBox(height: 12),
// Détails du produit en grille
_buildProductDetailRow('Prix', '${product.price.toStringAsFixed(2)} MGA'),
_buildProductDetailRow('Quantité ajoutée', '$newQuantity'),
if (product.imei != null && product.imei!.isNotEmpty)
Text('IMEI: ${product.imei}'),
_buildProductDetailRow('IMEI', product.imei!),
if (product.reference != null && product.reference!.isNotEmpty)
Text('Référence: ${product.reference}'),
Text('Prix: ${product.price.toStringAsFixed(2)} MGA'),
Text('Quantité dans le panier: $newQuantity'),
_buildProductDetailRow('Référence', product.reference!),
if (product.stock != null)
Text('Stock restant: ${product.stock! - newQuantity}'),
const SizedBox(height: 12),
_buildProductDetailRow('Stock restant', '${product.stock! - newQuantity}'),
],
),
),
const SizedBox(height: 20),
// Badge identification automatique
Container(
padding: const EdgeInsets.all(8),
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8),
decoration: BoxDecoration(
color: Colors.green.shade50,
borderRadius: BorderRadius.circular(8),
color: Colors.blue.shade50,
borderRadius: BorderRadius.circular(20),
border: Border.all(color: Colors.blue.shade200),
),
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
Icon(Icons.auto_awesome,
color: Colors.green.shade700, size: 16),
const SizedBox(width: 8),
const Expanded(
child: Text(
'Produit identifié automatiquement',
color: Colors.blue.shade700, size: 16),
const SizedBox(width: 6),
Text(
'Identifié automatiquement',
style: TextStyle(
fontSize: 12,
fontWeight: FontWeight.w500,
),
fontWeight: FontWeight.w600,
color: Colors.blue.shade700,
),
),
],
),
),
],
const SizedBox(height: 24),
// Boutons d'action redessinés
Column(
children: [
// Bouton principal selon les permissions
SizedBox(
width: double.infinity,
height: 48,
child: (!isProduitCommandable && !_isUserSuperAdmin())
? ElevatedButton.icon(
onPressed: canRequestTransfer
? () {
Get.back();
_showDemandeTransfertDialog(product);
}
: () {
Get.snackbar(
'Stock insuffisant',
'Impossible de demander un transfert : produit en rupture de stock',
snackPosition: SnackPosition.BOTTOM,
backgroundColor: Colors.orange.shade600,
colorText: Colors.white,
margin: const EdgeInsets.all(16),
);
},
icon: const Icon(Icons.swap_horiz, size: 20),
label: const Text(
'Demander un transfert',
style: TextStyle(fontSize: 16, fontWeight: FontWeight.w600),
),
actions: [
TextButton(
onPressed: () => Get.back(),
child: const Text('Continuer'),
style: ElevatedButton.styleFrom(
backgroundColor: canRequestTransfer
? Colors.orange.shade600
: Colors.grey.shade400,
foregroundColor: Colors.white,
elevation: 2,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12),
),
ElevatedButton(
),
)
: ElevatedButton.icon(
onPressed: () {
_ajouterAuPanier(product, 1);
Get.back();
_showCartBottomSheet();
},
icon: const Icon(Icons.shopping_cart, size: 20),
label: const Text(
'Voir le panier',
style: TextStyle(fontSize: 16, fontWeight: FontWeight.w600),
),
style: ElevatedButton.styleFrom(
backgroundColor: Colors.green.shade700,
backgroundColor: Colors.green.shade600,
foregroundColor: Colors.white,
elevation: 2,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12),
),
child: const Text('Voir le panier'),
),
ElevatedButton(
),
),
const SizedBox(height: 12),
// Boutons secondaires
Row(
children: [
// Continuer
Expanded(
child: SizedBox(
height: 44,
child: OutlinedButton.icon(
onPressed: () => Get.back(),
icon: const Icon(Icons.close, size: 18),
label: const Text('Continuer'),
style: OutlinedButton.styleFrom(
foregroundColor: Colors.grey.shade700,
side: BorderSide(color: Colors.grey.shade300),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12),
),
),
),
),
),
const SizedBox(width: 12),
// Scanner encore
Expanded(
child: SizedBox(
height: 44,
child: ElevatedButton.icon(
onPressed: () {
Get.back();
_startAutomaticScanning(); // Scanner un autre produit
_startAutomaticScanning();
},
icon: const Icon(Icons.qr_code_scanner, size: 18),
label: const Text('Scanner'),
style: ElevatedButton.styleFrom(
backgroundColor: Colors.blue.shade700,
backgroundColor: Colors.blue.shade600,
foregroundColor: Colors.white,
elevation: 1,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12),
),
),
),
),
),
],
),
],
),
],
),
),
),
);
}
// Widget helper pour les détails du produit
Widget _buildProductDetailRow(String label, String value) {
return Padding(
padding: const EdgeInsets.only(bottom: 8),
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
SizedBox(
width: 100,
child: Text(
'$label:',
style: TextStyle(
fontSize: 14,
fontWeight: FontWeight.w500,
color: Colors.grey.shade600,
),
),
),
Expanded(
child: Text(
value,
style: const TextStyle(
fontSize: 14,
fontWeight: FontWeight.w600,
color: Colors.black87,
),
),
child: const Text('Scanner encore'),
),
],
),
@ -1899,9 +2091,9 @@ Widget _buildUserPointDeVenteInfo() {
// 6. Ajoutez cette méthode pour filtrer les produits par point de vente
// 🎯 MODIFIÉ: Dropdown avec gestion améliorée
Widget _buildPointDeVenteFilter() {
if (!_isUserSuperAdmin()) {
return const SizedBox.shrink(); // Cacher pour les non-admins
}
// if (!_isUserSuperAdmin()) {
// return const SizedBox.shrink(); // Cacher pour les non-admins
// }
return Card(
elevation: 2,
@ -2630,7 +2822,7 @@ Widget _buildProductListItem(Product product, int quantity, bool isMobile) {
const SizedBox(height: 4),
ElevatedButton.icon(
icon: const Icon(Icons.swap_horiz, size: 14),
label: const Text('Demander transfert'),
label:!isMobile ? const Text('Demander transfertt'):const SizedBox.shrink(),
style: ElevatedButton.styleFrom(
backgroundColor: (product.stock != null && product.stock! >= 1)
? Colors.blue.shade700

4
lib/config/DatabaseConfig.dart

@ -1,6 +1,8 @@
// Config/database_config.dart - Version améliorée
class DatabaseConfig {
static const String host = 'localhost';
static const String host = '10.0.2.2';
//static const String host = '172.20.10.5';
// static const String host = 'localhost';
static const int port = 3306;
static const String username = 'root';
static const String? password = null;

Loading…
Cancel
Save