Browse Source

commit 24/07/2025-2

28062025_02
andrymodeste 5 months ago
parent
commit
a52749c415
  1. 3
      android/app/build.gradle.kts
  2. 1
      ios/build/ios/XCBuildData/PIFCache/workspace/WORKSPACE@v11_hash=(null)_subobjects=b2e12b7ce127ba825ffc2656889f5368-json
  3. 37
      lib/Components/AddClient.dart
  4. 31
      lib/Components/QrScan.dart
  5. 186
      lib/Views/demande_sortie_personnelle_page.dart

3
android/app/build.gradle.kts

@ -8,7 +8,8 @@ plugins {
android { android {
namespace = "com.example.my_app" namespace = "com.example.my_app"
compileSdk = flutter.compileSdkVersion compileSdk = flutter.compileSdkVersion
ndkVersion = flutter.ndkVersion ndkVersion = "26.3.11579264"
compileOptions { compileOptions {
sourceCompatibility = JavaVersion.VERSION_11 sourceCompatibility = JavaVersion.VERSION_11

1
ios/build/ios/XCBuildData/PIFCache/workspace/WORKSPACE@v11_hash=(null)_subobjects=b2e12b7ce127ba825ffc2656889f5368-json

@ -0,0 +1 @@
{"guid":"dc4b70c03e8043e50e38f2068887b1d4","name":"Pods","path":"/Users/rabarisonmimistephanethannio/Documents/DEV/guycom_finale/ios/Pods/Pods.xcodeproj/project.xcworkspace","projects":["PROJECT@v11_mod=7e9f9a517e1b730b3eb5b9aa5a52f2df_hash=bfdfe7dc352907fc980b868725387e98plugins=1OJSG6M1FOV3XYQCBH7Z29RZ0FPR9XDE1"]}

37
lib/Components/AddClient.dart

@ -4,7 +4,6 @@ import 'package:youmazgestion/Models/client.dart';
import '../Services/stock_managementDatabase.dart'; import '../Services/stock_managementDatabase.dart';
class ClientFormController extends GetxController { class ClientFormController extends GetxController {
final _formKey = GlobalKey<FormState>(); final _formKey = GlobalKey<FormState>();
@ -88,7 +87,9 @@ class ClientFormController extends GetxController {
prenom: _prenomController.text.trim(), prenom: _prenomController.text.trim(),
email: _emailController.text.trim(), email: _emailController.text.trim(),
telephone: _telephoneController.text.trim(), telephone: _telephoneController.text.trim(),
adresse: _adresseController.text.trim().isEmpty ? null : _adresseController.text.trim(), adresse: _adresseController.text.trim().isEmpty
? null
: _adresseController.text.trim(),
dateCreation: DateTime.now(), dateCreation: DateTime.now(),
); );
@ -98,7 +99,6 @@ class ClientFormController extends GetxController {
// Procéder avec la commande // Procéder avec la commande
Get.back(); Get.back();
_submitOrderWithClient(clientToUse); _submitOrderWithClient(clientToUse);
} catch (e) { } catch (e) {
Get.snackbar( Get.snackbar(
'Erreur', 'Erreur',
@ -116,6 +116,7 @@ class ClientFormController extends GetxController {
} }
// Widget pour le formulaire avec auto-completion // Widget pour le formulaire avec auto-completion
// ignore: unused_element
void _showClientFormDialog() { void _showClientFormDialog() {
final controller = Get.put(ClientFormController()); final controller = Get.put(ClientFormController());
@ -168,7 +169,8 @@ void _showClientFormDialog() {
), ),
child: Row( child: Row(
children: [ children: [
Icon(Icons.check_circle, color: Colors.green.shade600), Icon(Icons.check_circle,
color: Colors.green.shade600),
const SizedBox(width: 8), const SizedBox(width: 8),
Expanded( Expanded(
child: Text( child: Text(
@ -191,7 +193,8 @@ void _showClientFormDialog() {
_buildTextFormField( _buildTextFormField(
controller: controller._nomController, controller: controller._nomController,
label: 'Nom', label: 'Nom',
validator: (value) => value?.isEmpty ?? true ? 'Veuillez entrer un nom' : null, validator: (value) =>
value?.isEmpty ?? true ? 'Veuillez entrer un nom' : null,
onChanged: (value) { onChanged: (value) {
if (controller.selectedClient.value != null) { if (controller.selectedClient.value != null) {
controller.selectedClient.value = null; controller.selectedClient.value = null;
@ -203,7 +206,9 @@ void _showClientFormDialog() {
_buildTextFormField( _buildTextFormField(
controller: controller._prenomController, controller: controller._prenomController,
label: 'Prénom', label: 'Prénom',
validator: (value) => value?.isEmpty ?? true ? 'Veuillez entrer un prénom' : null, validator: (value) => value?.isEmpty ?? true
? 'Veuillez entrer un prénom'
: null,
onChanged: (value) { onChanged: (value) {
if (controller.selectedClient.value != null) { if (controller.selectedClient.value != null) {
controller.selectedClient.value = null; controller.selectedClient.value = null;
@ -217,8 +222,10 @@ void _showClientFormDialog() {
label: 'Email', label: 'Email',
keyboardType: TextInputType.emailAddress, keyboardType: TextInputType.emailAddress,
validator: (value) { validator: (value) {
if (value?.isEmpty ?? true) return 'Veuillez entrer un email'; // if (value?.isEmpty ?? true) return 'Veuillez entrer un email';
if (!RegExp(r'^[\w-\.]+@([\w-]+\.)+[\w-]{2,4}$').hasMatch(value!)) { if (value?.isEmpty ?? true) return null;
if (!RegExp(r'^[\w-\.]+@([\w-]+\.)+[\w-]{2,4}$')
.hasMatch(value!)) {
return 'Email invalide'; return 'Email invalide';
} }
return null; return null;
@ -237,7 +244,9 @@ void _showClientFormDialog() {
controller: controller._telephoneController, controller: controller._telephoneController,
label: 'Téléphone', label: 'Téléphone',
keyboardType: TextInputType.phone, keyboardType: TextInputType.phone,
validator: (value) => value?.isEmpty ?? true ? 'Veuillez entrer un téléphone' : null, validator: (value) => value?.isEmpty ?? true
? 'Veuillez entrer un téléphone'
: null,
onChanged: (value) { onChanged: (value) {
if (controller.selectedClient.value != null) { if (controller.selectedClient.value != null) {
controller.selectedClient.value = null; controller.selectedClient.value = null;
@ -252,7 +261,9 @@ void _showClientFormDialog() {
controller: controller._adresseController, controller: controller._adresseController,
label: 'Adresse', label: 'Adresse',
maxLines: 2, maxLines: 2,
validator: (value) => value?.isEmpty ?? true ? 'Veuillez entrer une adresse' : null, validator: (value) => value?.isEmpty ?? true
? 'Veuillez entrer une adresse'
: null,
onChanged: (value) { onChanged: (value) {
if (controller.selectedClient.value != null) { if (controller.selectedClient.value != null) {
controller.selectedClient.value = null; controller.selectedClient.value = null;
@ -288,7 +299,8 @@ void _showClientFormDialog() {
), ),
), ),
const SizedBox(height: 8), const SizedBox(height: 8),
...controller.suggestedClients.map((client) => ...controller.suggestedClients.map(
(client) =>
_buildClientSuggestionTile(client, controller), _buildClientSuggestionTile(client, controller),
), ),
], ],
@ -349,7 +361,8 @@ Widget _buildSearchSection(ClientFormController controller) {
} }
// Widget pour afficher une suggestion de client // Widget pour afficher une suggestion de client
Widget _buildClientSuggestionTile(Client client, ClientFormController controller) { Widget _buildClientSuggestionTile(
Client client, ClientFormController controller) {
return Card( return Card(
margin: const EdgeInsets.only(bottom: 8), margin: const EdgeInsets.only(bottom: 8),
child: ListTile( child: ListTile(

31
lib/Components/QrScan.dart

@ -28,7 +28,8 @@ class _ScanQRPageState extends State<ScanQRPage> {
if (!isMobile) { if (!isMobile) {
setState(() { setState(() {
_hasError = true; _hasError = true;
_errorMessage = "Le scanner QR n'est pas disponible sur cette plateforme."; _errorMessage =
"Le scanner QR n'est pas disponible sur cette plateforme.";
}); });
return; return;
} }
@ -61,7 +62,9 @@ class _ScanQRPageState extends State<ScanQRPage> {
return Scaffold( return Scaffold(
appBar: AppBar( appBar: AppBar(
title: const Text('Scanner QR Code'), title: const Text('Scanner QR Code'),
actions: _hasError ? [] : [ actions: _hasError
? []
: [
if (cameraController != null) ...[ if (cameraController != null) ...[
IconButton( IconButton(
color: Colors.white, color: Colors.white,
@ -71,7 +74,8 @@ class _ScanQRPageState extends State<ScanQRPage> {
), ),
IconButton( IconButton(
color: Colors.white, color: Colors.white,
icon: const Icon(Icons.flip_camera_ios, color: Colors.white), icon:
const Icon(Icons.flip_camera_ios, color: Colors.white),
iconSize: 32.0, iconSize: 32.0,
onPressed: () => cameraController!.switchCamera(), onPressed: () => cameraController!.switchCamera(),
), ),
@ -150,7 +154,8 @@ class _ScanQRPageState extends State<ScanQRPage> {
children: [ children: [
const Icon(Icons.error, size: 64, color: Colors.red), const Icon(Icons.error, size: 64, color: Colors.red),
const SizedBox(height: 16), const SizedBox(height: 16),
Text('Erreur: ${error.errorDetails?.message ?? 'Erreur inconnue'}'), Text(
'Erreur: ${error.errorDetails?.message ?? 'Erreur inconnue'}'),
const SizedBox(height: 16), const SizedBox(height: 16),
ElevatedButton( ElevatedButton(
onPressed: () => _initializeController(), onPressed: () => _initializeController(),
@ -236,19 +241,25 @@ class QrScannerOverlay extends CustomPainter {
..style = PaintingStyle.stroke; ..style = PaintingStyle.stroke;
// Coins du scanner // Coins du scanner
_drawCorner(canvas, borderPaint, areaLeft, areaTop, borderLength, true, true); _drawCorner(
_drawCorner(canvas, borderPaint, areaLeft + areaSize, areaTop, borderLength, false, true); canvas, borderPaint, areaLeft, areaTop, borderLength, true, true);
_drawCorner(canvas, borderPaint, areaLeft, areaTop + areaSize, borderLength, true, false); _drawCorner(canvas, borderPaint, areaLeft + areaSize, areaTop, borderLength,
_drawCorner(canvas, borderPaint, areaLeft + areaSize, areaTop + areaSize, borderLength, false, false); false, true);
_drawCorner(canvas, borderPaint, areaLeft, areaTop + areaSize, borderLength,
true, false);
_drawCorner(canvas, borderPaint, areaLeft + areaSize, areaTop + areaSize,
borderLength, false, false);
} }
void _drawCorner(Canvas canvas, Paint paint, double x, double y, double length, bool isLeft, bool isTop) { void _drawCorner(Canvas canvas, Paint paint, double x, double y,
double length, bool isLeft, bool isTop) {
final double horizontalStart = isLeft ? x : x - length; final double horizontalStart = isLeft ? x : x - length;
final double horizontalEnd = isLeft ? x + length : x; final double horizontalEnd = isLeft ? x + length : x;
final double verticalStart = isTop ? y : y - length; final double verticalStart = isTop ? y : y - length;
final double verticalEnd = isTop ? y + length : y; final double verticalEnd = isTop ? y + length : y;
canvas.drawLine(Offset(horizontalStart, y), Offset(horizontalEnd, y), paint); canvas.drawLine(
Offset(horizontalStart, y), Offset(horizontalEnd, y), paint);
canvas.drawLine(Offset(x, verticalStart), Offset(x, verticalEnd), paint); canvas.drawLine(Offset(x, verticalStart), Offset(x, verticalEnd), paint);
} }

186
lib/Views/demande_sortie_personnelle_page.dart

@ -5,16 +5,18 @@ import 'package:youmazgestion/Components/appDrawer.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';
import '../Models/produit.dart'; import '../Models/produit.dart';
import 'package:mobile_scanner/mobile_scanner.dart';
class DemandeSortiePersonnellePage extends StatefulWidget { class DemandeSortiePersonnellePage extends StatefulWidget {
const DemandeSortiePersonnellePage({super.key}); const DemandeSortiePersonnellePage({super.key});
@override @override
_DemandeSortiePersonnellePageState createState() => _DemandeSortiePersonnellePageState(); _DemandeSortiePersonnellePageState createState() =>
_DemandeSortiePersonnellePageState();
} }
class _DemandeSortiePersonnellePageState extends State<DemandeSortiePersonnellePage> class _DemandeSortiePersonnellePageState
with TickerProviderStateMixin { extends State<DemandeSortiePersonnellePage> with TickerProviderStateMixin {
final AppDatabase _database = AppDatabase.instance; final AppDatabase _database = AppDatabase.instance;
final UserController _userController = Get.find<UserController>(); final UserController _userController = Get.find<UserController>();
@ -44,7 +46,8 @@ class _DemandeSortiePersonnellePageState extends State<DemandeSortiePersonnelleP
_fadeAnimation = Tween<double>(begin: 0.0, end: 1.0).animate( _fadeAnimation = Tween<double>(begin: 0.0, end: 1.0).animate(
CurvedAnimation(parent: _animationController, curve: Curves.easeInOut), CurvedAnimation(parent: _animationController, curve: Curves.easeInOut),
); );
_slideAnimation = Tween<Offset>(begin: const Offset(0, 0.3), end: Offset.zero).animate( _slideAnimation =
Tween<Offset>(begin: const Offset(0, 0.3), end: Offset.zero).animate(
CurvedAnimation(parent: _animationController, curve: Curves.easeOutCubic), CurvedAnimation(parent: _animationController, curve: Curves.easeOutCubic),
); );
@ -52,6 +55,57 @@ class _DemandeSortiePersonnellePageState extends State<DemandeSortiePersonnelleP
_searchController.addListener(_filterProducts); _searchController.addListener(_filterProducts);
} }
void _scanQrOrBarcode() async {
// Open the mobile scanner as a modal widget
await showDialog(
context: context,
builder: (context) {
return AlertDialog(
content: Container(
width: double.maxFinite,
height: 400, // Adjust according to your needs
child: MobileScanner(
onDetect: (barcodeCapture) {
String scanResult = barcodeCapture.rawValue ?? '';
Navigator.of(context).pop(); // Close dialog after scanning
if (scanResult.isEmpty) return;
setState(() {
_searchController.text = scanResult; // Show scanned text
});
// Assume IMEI is always 15 digits, adjust if necessary
Product? found;
if (scanResult.length == 15 &&
int.tryParse(scanResult) != null) {
found =
_products.firstWhereOrNull((p) => p.imei == scanResult);
} else {
found = _products
.firstWhereOrNull((p) => p.reference == scanResult);
}
if (found != null) {
setState(() {
_selectedProduct = found;
_filteredProducts = [found!];
});
} else {
_showErrorSnackbar('Aucun produit trouvé avec ce code.');
setState(() {
_filteredProducts = [];
_selectedProduct = null;
});
}
},
),
),
);
},
);
}
void _filterProducts() { void _filterProducts() {
final query = _searchController.text.toLowerCase(); final query = _searchController.text.toLowerCase();
setState(() { setState(() {
@ -98,7 +152,8 @@ class _DemandeSortiePersonnellePageState extends State<DemandeSortiePersonnelleP
} }
if ((_selectedProduct!.stock ?? 0) < quantite) { if ((_selectedProduct!.stock ?? 0) < quantite) {
_showErrorSnackbar('Stock insuffisant (disponible: ${_selectedProduct!.stock})'); _showErrorSnackbar(
'Stock insuffisant (disponible: ${_selectedProduct!.stock})');
return; return;
} }
@ -114,11 +169,16 @@ class _DemandeSortiePersonnellePageState extends State<DemandeSortiePersonnelleP
adminId: _userController.userId, adminId: _userController.userId,
quantite: quantite, quantite: quantite,
motif: _motifController.text.trim(), motif: _motifController.text.trim(),
pointDeVenteId: _userController.pointDeVenteId > 0 ? _userController.pointDeVenteId : null, pointDeVenteId: _userController.pointDeVenteId > 0
notes: _notesController.text.trim().isNotEmpty ? _notesController.text.trim() : null, ? _userController.pointDeVenteId
: null,
notes: _notesController.text.trim().isNotEmpty
? _notesController.text.trim()
: null,
); );
_showSuccessSnackbar('Votre demande de sortie personnelle a été soumise pour approbation'); _showSuccessSnackbar(
'Votre demande de sortie personnelle a été soumise pour approbation');
// Réinitialiser le formulaire avec animation // Réinitialiser le formulaire avec animation
_resetForm(); _resetForm();
@ -146,7 +206,8 @@ class _DemandeSortiePersonnellePageState extends State<DemandeSortiePersonnelleP
return await showDialog<bool>( return await showDialog<bool>(
context: context, context: context,
builder: (context) => AlertDialog( builder: (context) => AlertDialog(
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(16)), shape:
RoundedRectangleBorder(borderRadius: BorderRadius.circular(16)),
title: Row( title: Row(
children: [ children: [
Icon(Icons.help_outline, color: Colors.orange.shade700), Icon(Icons.help_outline, color: Colors.orange.shade700),
@ -158,7 +219,8 @@ class _DemandeSortiePersonnellePageState extends State<DemandeSortiePersonnelleP
mainAxisSize: MainAxisSize.min, mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
const Text('Êtes-vous sûr de vouloir soumettre cette demande ?'), const Text(
'Êtes-vous sûr de vouloir soumettre cette demande ?'),
const SizedBox(height: 16), const SizedBox(height: 16),
Container( Container(
padding: const EdgeInsets.all(12), padding: const EdgeInsets.all(12),
@ -192,7 +254,8 @@ class _DemandeSortiePersonnellePageState extends State<DemandeSortiePersonnelleP
), ),
], ],
), ),
) ?? false; ) ??
false;
} }
void _showSuccessSnackbar(String message) { void _showSuccessSnackbar(String message) {
@ -203,7 +266,9 @@ class _DemandeSortiePersonnellePageState extends State<DemandeSortiePersonnelleP
children: [ children: [
Icon(Icons.check_circle, color: Colors.white), Icon(Icons.check_circle, color: Colors.white),
const SizedBox(width: 8), const SizedBox(width: 8),
const Text('Succès', style: TextStyle(color: Colors.white, fontWeight: FontWeight.bold)), const Text('Succès',
style:
TextStyle(color: Colors.white, fontWeight: FontWeight.bold)),
], ],
), ),
messageText: Text(message, style: const TextStyle(color: Colors.white)), messageText: Text(message, style: const TextStyle(color: Colors.white)),
@ -224,7 +289,9 @@ class _DemandeSortiePersonnellePageState extends State<DemandeSortiePersonnelleP
children: [ children: [
Icon(Icons.error, color: Colors.white), Icon(Icons.error, color: Colors.white),
const SizedBox(width: 8), const SizedBox(width: 8),
const Text('Erreur', style: TextStyle(color: Colors.white, fontWeight: FontWeight.bold)), const Text('Erreur',
style:
TextStyle(color: Colors.white, fontWeight: FontWeight.bold)),
], ],
), ),
messageText: Text(message, style: const TextStyle(color: Colors.white)), messageText: Text(message, style: const TextStyle(color: Colors.white)),
@ -327,31 +394,25 @@ class _DemandeSortiePersonnellePageState extends State<DemandeSortiePersonnelleP
const SizedBox(height: 12), const SizedBox(height: 12),
// Barre de recherche // Barre de recherche
Container( Row(
decoration: BoxDecoration( children: [
color: Colors.grey.shade50, Expanded(
borderRadius: BorderRadius.circular(12),
border: Border.all(color: Colors.grey.shade300),
),
child: TextField( child: TextField(
controller: _searchController, controller: _searchController,
decoration: InputDecoration( decoration: InputDecoration(
hintText: 'Rechercher un produit...', hintText: 'Rechercher un produit...',
prefixIcon: Icon(Icons.search, color: Colors.grey.shade600), prefixIcon: Icon(Icons.search, color: Colors.grey.shade600),
suffixIcon: _isSearching
? IconButton(
icon: Icon(Icons.clear, color: Colors.grey.shade600),
onPressed: () {
_searchController.clear();
FocusScope.of(context).unfocus();
},
)
: null,
border: InputBorder.none,
contentPadding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12),
), ),
), ),
), ),
IconButton(
icon: Icon(Icons.qr_code_scanner, color: Colors.blue),
onPressed: _scanQrOrBarcode,
tooltip: 'Scanner QR ou code-barres',
),
],
),
const SizedBox(height: 12), const SizedBox(height: 12),
// Liste des produits // Liste des produits
@ -366,10 +427,13 @@ class _DemandeSortiePersonnellePageState extends State<DemandeSortiePersonnelleP
child: Column( child: Column(
mainAxisAlignment: MainAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.center,
children: [ children: [
Icon(Icons.search_off, size: 48, color: Colors.grey.shade400), Icon(Icons.search_off,
size: 48, color: Colors.grey.shade400),
const SizedBox(height: 8), const SizedBox(height: 8),
Text( Text(
_isSearching ? 'Aucun produit trouvé' : 'Aucun produit disponible', _isSearching
? 'Aucun produit trouvé'
: 'Aucun produit disponible',
style: TextStyle(color: Colors.grey.shade600), style: TextStyle(color: Colors.grey.shade600),
), ),
], ],
@ -383,12 +447,17 @@ class _DemandeSortiePersonnellePageState extends State<DemandeSortiePersonnelleP
return AnimatedContainer( return AnimatedContainer(
duration: const Duration(milliseconds: 200), duration: const Duration(milliseconds: 200),
margin: const EdgeInsets.symmetric(horizontal: 8, vertical: 4), margin: const EdgeInsets.symmetric(
horizontal: 8, vertical: 4),
decoration: BoxDecoration( decoration: BoxDecoration(
color: isSelected ? Colors.orange.shade50 : Colors.transparent, color: isSelected
? Colors.orange.shade50
: Colors.transparent,
borderRadius: BorderRadius.circular(8), borderRadius: BorderRadius.circular(8),
border: Border.all( border: Border.all(
color: isSelected ? Colors.orange.shade300 : Colors.transparent, color: isSelected
? Colors.orange.shade300
: Colors.transparent,
width: 2, width: 2,
), ),
), ),
@ -397,30 +466,41 @@ class _DemandeSortiePersonnellePageState extends State<DemandeSortiePersonnelleP
width: 48, width: 48,
height: 48, height: 48,
decoration: BoxDecoration( decoration: BoxDecoration(
color: isSelected ? Colors.orange.shade100 : Colors.grey.shade100, color: isSelected
? Colors.orange.shade100
: Colors.grey.shade100,
borderRadius: BorderRadius.circular(8), borderRadius: BorderRadius.circular(8),
), ),
child: Icon( child: Icon(
Icons.inventory, Icons.inventory,
color: isSelected ? Colors.orange.shade700 : Colors.grey.shade600, color: isSelected
? Colors.orange.shade700
: Colors.grey.shade600,
), ),
), ),
title: Text( title: Text(
product.name, product.name,
style: TextStyle( style: TextStyle(
fontWeight: isSelected ? FontWeight.bold : FontWeight.w500, fontWeight:
color: isSelected ? Colors.orange.shade800 : Colors.grey.shade800, isSelected ? FontWeight.bold : FontWeight.w500,
color: isSelected
? Colors.orange.shade800
: Colors.grey.shade800,
), ),
), ),
subtitle: Text( subtitle: Text(
'Stock: ${product.stock} • Réf: ${product.reference ?? 'N/A'}', 'Stock: ${product.stock} • Réf: ${product.reference ?? 'N/A'}',
style: TextStyle( style: TextStyle(
color: isSelected ? Colors.orange.shade600 : Colors.grey.shade600, color: isSelected
? Colors.orange.shade600
: Colors.grey.shade600,
), ),
), ),
trailing: isSelected trailing: isSelected
? Icon(Icons.check_circle, color: Colors.orange.shade700) ? Icon(Icons.check_circle,
: Icon(Icons.radio_button_unchecked, color: Colors.grey.shade400), color: Colors.orange.shade700)
: Icon(Icons.radio_button_unchecked,
color: Colors.grey.shade400),
onTap: () { onTap: () {
setState(() { setState(() {
_selectedProduct = product; _selectedProduct = product;
@ -445,7 +525,8 @@ class _DemandeSortiePersonnellePageState extends State<DemandeSortiePersonnelleP
keyboardType: TextInputType.number, keyboardType: TextInputType.number,
icon: Icons.format_list_numbered, icon: Icons.format_list_numbered,
suffix: _selectedProduct != null suffix: _selectedProduct != null
? Text('max: ${_selectedProduct!.stock}', style: TextStyle(color: Colors.grey.shade600)) ? Text('max: ${_selectedProduct!.stock}',
style: TextStyle(color: Colors.grey.shade600))
: null, : null,
validator: (value) { validator: (value) {
if (value == null || value.isEmpty) { if (value == null || value.isEmpty) {
@ -455,7 +536,8 @@ class _DemandeSortiePersonnellePageState extends State<DemandeSortiePersonnelleP
if (quantite == null || quantite <= 0) { if (quantite == null || quantite <= 0) {
return 'Quantité invalide'; return 'Quantité invalide';
} }
if (_selectedProduct != null && quantite > (_selectedProduct!.stock ?? 0)) { if (_selectedProduct != null &&
quantite > (_selectedProduct!.stock ?? 0)) {
return 'Quantité supérieure au stock disponible'; return 'Quantité supérieure au stock disponible';
} }
return null; return null;
@ -538,7 +620,8 @@ class _DemandeSortiePersonnellePageState extends State<DemandeSortiePersonnelleP
), ),
filled: true, filled: true,
fillColor: Colors.grey.shade50, fillColor: Colors.grey.shade50,
contentPadding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12), contentPadding:
const EdgeInsets.symmetric(horizontal: 16, vertical: 12),
), ),
), ),
], ],
@ -571,10 +654,13 @@ class _DemandeSortiePersonnellePageState extends State<DemandeSortiePersonnelleP
], ],
), ),
const SizedBox(height: 12), const SizedBox(height: 12),
_buildInfoRow(Icons.account_circle, 'Demandeur', _userController.name), _buildInfoRow(
Icons.account_circle, 'Demandeur', _userController.name),
if (_userController.pointDeVenteId > 0) if (_userController.pointDeVenteId > 0)
_buildInfoRow(Icons.store, 'Point de vente', _userController.pointDeVenteDesignation), _buildInfoRow(Icons.store, 'Point de vente',
_buildInfoRow(Icons.calendar_today, 'Date', DateTime.now().toLocal().toString().split(' ')[0]), _userController.pointDeVenteDesignation),
_buildInfoRow(Icons.calendar_today, 'Date',
DateTime.now().toLocal().toString().split(' ')[0]),
], ],
), ),
); );
@ -722,3 +808,7 @@ class _DemandeSortiePersonnellePageState extends State<DemandeSortiePersonnelleP
super.dispose(); super.dispose();
} }
} }
extension on BarcodeCapture {
get rawValue => null;
}

Loading…
Cancel
Save