push 09112025
This commit is contained in:
parent
c757bdb701
commit
13554ee49c
@ -147,7 +147,7 @@ class _ScanQRPageState extends State<ScanQRPage> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
errorBuilder: (context, error, child) {
|
errorBuilder: (context, error) {
|
||||||
return Center(
|
return Center(
|
||||||
child: Column(
|
child: Column(
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
@ -155,7 +155,8 @@ class _ScanQRPageState extends State<ScanQRPage> {
|
|||||||
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(
|
Text(
|
||||||
'Erreur: ${error.errorDetails?.message ?? 'Erreur inconnue'}'),
|
'Erreur: ${error.errorDetails?.message ?? 'Erreur inconnue'}',
|
||||||
|
),
|
||||||
const SizedBox(height: 16),
|
const SizedBox(height: 16),
|
||||||
ElevatedButton(
|
ElevatedButton(
|
||||||
onPressed: () => _initializeController(),
|
onPressed: () => _initializeController(),
|
||||||
|
|||||||
@ -1,37 +1,43 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:youmazgestion/Models/client.dart';
|
import 'package:youmazgestion/Models/client.dart';
|
||||||
|
|
||||||
|
// Classe supplémentaire
|
||||||
//Classe suplementaire
|
|
||||||
|
|
||||||
class CommandeActions extends StatelessWidget {
|
class CommandeActions extends StatelessWidget {
|
||||||
final Commande commande;
|
final Commande commande;
|
||||||
final Function(int, StatutCommande) onStatutChanged;
|
final Function(int, StatutCommande) onStatutChanged;
|
||||||
final Function(Commande) onGenerateBonLivraison;
|
final Function(Commande) onGenerateBonLivraison;
|
||||||
|
|
||||||
|
|
||||||
const CommandeActions({
|
const CommandeActions({
|
||||||
required this.commande,
|
required this.commande,
|
||||||
required this.onStatutChanged,
|
required this.onStatutChanged,
|
||||||
required this.onGenerateBonLivraison,
|
required this.onGenerateBonLivraison,
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
List<Widget> _buildActionButtons(BuildContext context) {
|
List<Widget> _buildActionButtons(BuildContext context) {
|
||||||
List<Widget> buttons = [];
|
List<Widget> buttons = [];
|
||||||
|
|
||||||
switch (commande.statut) {
|
switch (commande.statut) {
|
||||||
case StatutCommande.enAttente:
|
case StatutCommande.enAttente:
|
||||||
buttons.addAll([
|
buttons.addAll([
|
||||||
|
// Bouton confirmer
|
||||||
_buildActionButton(
|
_buildActionButton(
|
||||||
label: 'Confirmer',
|
label: 'Confirmer',
|
||||||
icon: Icons.check_circle,
|
icon: Icons.check_circle,
|
||||||
color: Colors.blue,
|
color: Colors.blue,
|
||||||
onPressed: () => onGenerateBonLivraison(commande),
|
onPressed: () => _showConfirmDialog(
|
||||||
|
context,
|
||||||
|
'Confirmer la commande',
|
||||||
|
'Êtes-vous sûr de vouloir confirmer cette commande ?',
|
||||||
|
() {
|
||||||
|
// Change le statut à "confirmée"
|
||||||
|
onStatutChanged(commande.id!, StatutCommande.confirmee);
|
||||||
|
// Et génère le bon de livraison après confirmation
|
||||||
|
onGenerateBonLivraison(commande);
|
||||||
|
},
|
||||||
),
|
),
|
||||||
|
),
|
||||||
|
|
||||||
|
// Bouton annuler
|
||||||
_buildActionButton(
|
_buildActionButton(
|
||||||
label: 'Annuler',
|
label: 'Annuler',
|
||||||
icon: Icons.cancel,
|
icon: Icons.cancel,
|
||||||
@ -181,6 +187,7 @@ class CommandeActions extends StatelessWidget {
|
|||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Container(
|
return Container(
|
||||||
|
|||||||
831
lib/Components/windows_qr_scanner.dart
Normal file
831
lib/Components/windows_qr_scanner.dart
Normal file
@ -0,0 +1,831 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:get/get.dart';
|
||||||
|
import 'package:youmazgestion/Components/app_bar.dart';
|
||||||
|
import 'package:youmazgestion/Components/appDrawer.dart';
|
||||||
|
import 'package:youmazgestion/Services/stock_managementDatabase.dart';
|
||||||
|
import 'package:youmazgestion/controller/userController.dart';
|
||||||
|
import '../Models/produit.dart';
|
||||||
|
import 'package:mobile_scanner/mobile_scanner.dart';
|
||||||
|
|
||||||
|
class DemandeSortiePersonnellePage extends StatefulWidget {
|
||||||
|
const DemandeSortiePersonnellePage({super.key});
|
||||||
|
|
||||||
|
@override
|
||||||
|
_DemandeSortiePersonnellePageState createState() =>
|
||||||
|
_DemandeSortiePersonnellePageState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _DemandeSortiePersonnellePageState
|
||||||
|
extends State<DemandeSortiePersonnellePage> with TickerProviderStateMixin {
|
||||||
|
final AppDatabase _database = AppDatabase.instance;
|
||||||
|
final UserController _userController = Get.find<UserController>();
|
||||||
|
|
||||||
|
final _formKey = GlobalKey<FormState>();
|
||||||
|
final _quantiteController = TextEditingController(text: '1');
|
||||||
|
final _motifController = TextEditingController();
|
||||||
|
final _notesController = TextEditingController();
|
||||||
|
final _searchController = TextEditingController();
|
||||||
|
|
||||||
|
Product? _selectedProduct;
|
||||||
|
List<Product> _products = [];
|
||||||
|
List<Product> _filteredProducts = [];
|
||||||
|
bool _isLoading = false;
|
||||||
|
bool _isSearching = false;
|
||||||
|
|
||||||
|
late AnimationController _animationController;
|
||||||
|
late Animation<double> _fadeAnimation;
|
||||||
|
late Animation<Offset> _slideAnimation;
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
_animationController = AnimationController(
|
||||||
|
duration: const Duration(milliseconds: 800),
|
||||||
|
vsync: this,
|
||||||
|
);
|
||||||
|
_fadeAnimation = Tween<double>(begin: 0.0, end: 1.0).animate(
|
||||||
|
CurvedAnimation(parent: _animationController, curve: Curves.easeInOut),
|
||||||
|
);
|
||||||
|
_slideAnimation =
|
||||||
|
Tween<Offset>(begin: const Offset(0, 0.3), end: Offset.zero).animate(
|
||||||
|
CurvedAnimation(parent: _animationController, curve: Curves.easeOutCubic),
|
||||||
|
);
|
||||||
|
|
||||||
|
_loadProducts();
|
||||||
|
_searchController.addListener(_filterProducts);
|
||||||
|
}
|
||||||
|
|
||||||
|
void _scanQrOrBarcode() async {
|
||||||
|
await showDialog(
|
||||||
|
context: context,
|
||||||
|
builder: (context) {
|
||||||
|
return AlertDialog(
|
||||||
|
content: Container(
|
||||||
|
width: double.maxFinite,
|
||||||
|
height: 400,
|
||||||
|
child: MobileScanner(
|
||||||
|
onDetect: (BarcodeCapture barcodeCap) {
|
||||||
|
print("BarcodeCapture: $barcodeCap");
|
||||||
|
// Now accessing the barcodes attribute
|
||||||
|
final List<Barcode> barcodes = barcodeCap.barcodes;
|
||||||
|
|
||||||
|
if (barcodes.isNotEmpty) {
|
||||||
|
// Get the first detected barcode value
|
||||||
|
String? scanResult = barcodes.first.rawValue;
|
||||||
|
|
||||||
|
print("Scanned Result: $scanResult");
|
||||||
|
|
||||||
|
if (scanResult != null && scanResult.isNotEmpty) {
|
||||||
|
setState(() {
|
||||||
|
_searchController.text = scanResult;
|
||||||
|
print(
|
||||||
|
"Updated Search Controller: ${_searchController.text}");
|
||||||
|
});
|
||||||
|
|
||||||
|
// Close dialog after scanning
|
||||||
|
Navigator.of(context).pop();
|
||||||
|
|
||||||
|
// Refresh product list based on new search input
|
||||||
|
_filterProducts();
|
||||||
|
} else {
|
||||||
|
print("Scan result was empty or null.");
|
||||||
|
Navigator.of(context).pop();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
print("No barcodes detected.");
|
||||||
|
Navigator.of(context).pop();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
void _filterProducts() {
|
||||||
|
final query = _searchController.text.toLowerCase();
|
||||||
|
setState(() {
|
||||||
|
if (query.isEmpty) {
|
||||||
|
_filteredProducts = _products;
|
||||||
|
_isSearching = false;
|
||||||
|
} else {
|
||||||
|
_isSearching = true;
|
||||||
|
_filteredProducts = _products.where((product) {
|
||||||
|
return product.name.toLowerCase().contains(query) ||
|
||||||
|
(product.reference?.toLowerCase().contains(query) ?? false);
|
||||||
|
}).toList();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> _loadProducts() async {
|
||||||
|
setState(() => _isLoading = true);
|
||||||
|
try {
|
||||||
|
final products = await _database.getProducts();
|
||||||
|
setState(() {
|
||||||
|
_products = products.where((p) {
|
||||||
|
// Check stock availability
|
||||||
|
print("point de vente id: ${_userController.pointDeVenteId}");
|
||||||
|
bool hasStock = _userController.pointDeVenteId == 0
|
||||||
|
? (p.stock ?? 0) > 0
|
||||||
|
: (p.stock ?? 0) > 0 &&
|
||||||
|
p.pointDeVenteId == _userController.pointDeVenteId;
|
||||||
|
return hasStock;
|
||||||
|
}).toList();
|
||||||
|
|
||||||
|
// Setting filtered products
|
||||||
|
_filteredProducts = _products;
|
||||||
|
|
||||||
|
// End loading state
|
||||||
|
_isLoading = false;
|
||||||
|
});
|
||||||
|
|
||||||
|
// Start the animation
|
||||||
|
_animationController.forward();
|
||||||
|
} catch (e) {
|
||||||
|
// Handle any errors
|
||||||
|
setState(() {
|
||||||
|
_isLoading = false;
|
||||||
|
});
|
||||||
|
_showErrorSnackbar('Impossible de charger les produits: $e');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> _soumettreDemandePersonnelle() async {
|
||||||
|
if (!_formKey.currentState!.validate() || _selectedProduct == null) {
|
||||||
|
_showErrorSnackbar('Veuillez remplir tous les champs obligatoires');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
final quantite = int.tryParse(_quantiteController.text) ?? 0;
|
||||||
|
|
||||||
|
if (quantite <= 0) {
|
||||||
|
_showErrorSnackbar('La quantité doit être supérieure à 0');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((_selectedProduct!.stock ?? 0) < quantite) {
|
||||||
|
_showErrorSnackbar(
|
||||||
|
'Stock insuffisant (disponible: ${_selectedProduct!.stock})');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Confirmation dialog
|
||||||
|
final confirmed = await _showConfirmationDialog();
|
||||||
|
if (!confirmed) return;
|
||||||
|
|
||||||
|
setState(() => _isLoading = true);
|
||||||
|
|
||||||
|
try {
|
||||||
|
await _database.createSortieStockPersonnelle(
|
||||||
|
produitId: _selectedProduct!.id!,
|
||||||
|
adminId: _userController.userId,
|
||||||
|
quantite: quantite,
|
||||||
|
motif: _motifController.text.trim(),
|
||||||
|
pointDeVenteId: _userController.pointDeVenteId > 0
|
||||||
|
? _userController.pointDeVenteId
|
||||||
|
: null,
|
||||||
|
notes: _notesController.text.trim().isNotEmpty
|
||||||
|
? _notesController.text.trim()
|
||||||
|
: null,
|
||||||
|
);
|
||||||
|
|
||||||
|
_showSuccessSnackbar(
|
||||||
|
'Votre demande de sortie personnelle a été soumise pour approbation');
|
||||||
|
|
||||||
|
// Réinitialiser le formulaire avec animation
|
||||||
|
_resetForm();
|
||||||
|
_loadProducts();
|
||||||
|
} catch (e) {
|
||||||
|
_showErrorSnackbar('Impossible de soumettre la demande: $e');
|
||||||
|
} finally {
|
||||||
|
setState(() => _isLoading = false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void _resetForm() {
|
||||||
|
_formKey.currentState!.reset();
|
||||||
|
_quantiteController.text = '1';
|
||||||
|
_motifController.clear();
|
||||||
|
_notesController.clear();
|
||||||
|
_searchController.clear();
|
||||||
|
setState(() {
|
||||||
|
_selectedProduct = null;
|
||||||
|
_isSearching = false;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<bool> _showConfirmationDialog() async {
|
||||||
|
return await showDialog<bool>(
|
||||||
|
context: context,
|
||||||
|
builder: (context) => AlertDialog(
|
||||||
|
shape:
|
||||||
|
RoundedRectangleBorder(borderRadius: BorderRadius.circular(16)),
|
||||||
|
title: Row(
|
||||||
|
children: [
|
||||||
|
Icon(Icons.help_outline, color: Colors.orange.shade700),
|
||||||
|
const SizedBox(width: 8),
|
||||||
|
const Text('Confirmer la demande'),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
content: Column(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
const Text(
|
||||||
|
'Êtes-vous sûr de vouloir soumettre cette demande ?'),
|
||||||
|
const SizedBox(height: 16),
|
||||||
|
Container(
|
||||||
|
padding: const EdgeInsets.all(12),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: Colors.grey.shade50,
|
||||||
|
borderRadius: BorderRadius.circular(8),
|
||||||
|
),
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Text('Produit: ${_selectedProduct?.name}'),
|
||||||
|
Text('Quantité: ${_quantiteController.text}'),
|
||||||
|
Text('Motif: ${_motifController.text}'),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
actions: [
|
||||||
|
TextButton(
|
||||||
|
onPressed: () => Navigator.of(context).pop(false),
|
||||||
|
child: const Text('Annuler'),
|
||||||
|
),
|
||||||
|
ElevatedButton(
|
||||||
|
onPressed: () => Navigator.of(context).pop(true),
|
||||||
|
style: ElevatedButton.styleFrom(
|
||||||
|
backgroundColor: Colors.orange.shade700,
|
||||||
|
foregroundColor: Colors.white,
|
||||||
|
),
|
||||||
|
child: const Text('Confirmer'),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
) ??
|
||||||
|
false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void _showSuccessSnackbar(String message) {
|
||||||
|
Get.snackbar(
|
||||||
|
'',
|
||||||
|
'',
|
||||||
|
titleText: Row(
|
||||||
|
children: [
|
||||||
|
Icon(Icons.check_circle, color: Colors.white),
|
||||||
|
const SizedBox(width: 8),
|
||||||
|
const Text('Succès',
|
||||||
|
style:
|
||||||
|
TextStyle(color: Colors.white, fontWeight: FontWeight.bold)),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
messageText: Text(message, style: const TextStyle(color: Colors.white)),
|
||||||
|
backgroundColor: Colors.green.shade600,
|
||||||
|
colorText: Colors.white,
|
||||||
|
duration: const Duration(seconds: 4),
|
||||||
|
margin: const EdgeInsets.all(16),
|
||||||
|
borderRadius: 12,
|
||||||
|
icon: Icon(Icons.check_circle_outline, color: Colors.white),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
void _showErrorSnackbar(String message) {
|
||||||
|
Get.snackbar(
|
||||||
|
'',
|
||||||
|
'',
|
||||||
|
titleText: Row(
|
||||||
|
children: [
|
||||||
|
Icon(Icons.error, color: Colors.white),
|
||||||
|
const SizedBox(width: 8),
|
||||||
|
const Text('Erreur',
|
||||||
|
style:
|
||||||
|
TextStyle(color: Colors.white, fontWeight: FontWeight.bold)),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
messageText: Text(message, style: const TextStyle(color: Colors.white)),
|
||||||
|
backgroundColor: Colors.red.shade600,
|
||||||
|
colorText: Colors.white,
|
||||||
|
duration: const Duration(seconds: 4),
|
||||||
|
margin: const EdgeInsets.all(16),
|
||||||
|
borderRadius: 12,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildHeaderCard() {
|
||||||
|
return Container(
|
||||||
|
padding: const EdgeInsets.all(20),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
gradient: LinearGradient(
|
||||||
|
colors: [Colors.blue.shade600, Colors.blue.shade400],
|
||||||
|
begin: Alignment.topLeft,
|
||||||
|
end: Alignment.bottomRight,
|
||||||
|
),
|
||||||
|
borderRadius: BorderRadius.circular(16),
|
||||||
|
boxShadow: [
|
||||||
|
BoxShadow(
|
||||||
|
color: Colors.blue.shade200,
|
||||||
|
blurRadius: 12,
|
||||||
|
offset: const Offset(0, 4),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Row(
|
||||||
|
children: [
|
||||||
|
Container(
|
||||||
|
padding: const EdgeInsets.all(12),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: Colors.white.withOpacity(0.2),
|
||||||
|
borderRadius: BorderRadius.circular(12),
|
||||||
|
),
|
||||||
|
child: Icon(Icons.inventory_2, color: Colors.white, size: 28),
|
||||||
|
),
|
||||||
|
const SizedBox(width: 16),
|
||||||
|
Expanded(
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
const Text(
|
||||||
|
'Sortie personnelle de stock',
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 20,
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
color: Colors.white,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 4),
|
||||||
|
Text(
|
||||||
|
'Demande d\'approbation requise',
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 14,
|
||||||
|
color: Colors.white.withOpacity(0.8),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
const SizedBox(height: 16),
|
||||||
|
Container(
|
||||||
|
padding: const EdgeInsets.all(12),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: Colors.white.withOpacity(0.1),
|
||||||
|
borderRadius: BorderRadius.circular(8),
|
||||||
|
),
|
||||||
|
child: const Text(
|
||||||
|
'Cette fonctionnalité permet aux administrateurs de demander '
|
||||||
|
'la sortie d\'un produit du stock pour usage personnel. '
|
||||||
|
'Toute demande nécessite une approbation avant traitement.',
|
||||||
|
style: TextStyle(fontSize: 14, color: Colors.white),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildProductSelector() {
|
||||||
|
return Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
'Sélection du produit *',
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 18,
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
color: Colors.grey.shade800,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 12),
|
||||||
|
|
||||||
|
// Barre de recherche
|
||||||
|
Row(
|
||||||
|
children: [
|
||||||
|
Expanded(
|
||||||
|
child: TextField(
|
||||||
|
controller: _searchController,
|
||||||
|
decoration: InputDecoration(
|
||||||
|
hintText: 'Rechercher un produit...',
|
||||||
|
prefixIcon: Icon(Icons.search, color: Colors.grey.shade600),
|
||||||
|
),
|
||||||
|
onChanged: (value) {
|
||||||
|
_filterProducts(); // Call to filter products
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
IconButton(
|
||||||
|
icon: Icon(Icons.qr_code_scanner, color: Colors.blue),
|
||||||
|
onPressed: _scanQrOrBarcode,
|
||||||
|
tooltip: 'Scanner QR ou code-barres',
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
|
||||||
|
const SizedBox(height: 12),
|
||||||
|
|
||||||
|
// Liste des produits
|
||||||
|
Container(
|
||||||
|
height: 200,
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
borderRadius: BorderRadius.circular(12),
|
||||||
|
border: Border.all(color: Colors.grey.shade300),
|
||||||
|
),
|
||||||
|
child: _filteredProducts.isEmpty
|
||||||
|
? Center(
|
||||||
|
child: Column(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
children: [
|
||||||
|
Icon(Icons.search_off,
|
||||||
|
size: 48, color: Colors.grey.shade400),
|
||||||
|
const SizedBox(height: 8),
|
||||||
|
Text(
|
||||||
|
_isSearching
|
||||||
|
? 'Aucun produit trouvé'
|
||||||
|
: 'Aucun produit disponible',
|
||||||
|
style: TextStyle(color: Colors.grey.shade600),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
)
|
||||||
|
: ListView.builder(
|
||||||
|
itemCount: _filteredProducts.length,
|
||||||
|
itemBuilder: (context, index) {
|
||||||
|
final product = _filteredProducts[index];
|
||||||
|
final isSelected = _selectedProduct?.id == product.id;
|
||||||
|
|
||||||
|
return AnimatedContainer(
|
||||||
|
duration: const Duration(milliseconds: 200),
|
||||||
|
margin: const EdgeInsets.symmetric(
|
||||||
|
horizontal: 8, vertical: 4),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: isSelected
|
||||||
|
? Colors.orange.shade50
|
||||||
|
: Colors.transparent,
|
||||||
|
borderRadius: BorderRadius.circular(8),
|
||||||
|
border: Border.all(
|
||||||
|
color: isSelected
|
||||||
|
? Colors.orange.shade300
|
||||||
|
: Colors.transparent,
|
||||||
|
width: 2,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
child: ListTile(
|
||||||
|
leading: Container(
|
||||||
|
width: 48,
|
||||||
|
height: 48,
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: isSelected
|
||||||
|
? Colors.orange.shade100
|
||||||
|
: Colors.grey.shade100,
|
||||||
|
borderRadius: BorderRadius.circular(8),
|
||||||
|
),
|
||||||
|
child: Icon(
|
||||||
|
Icons.inventory,
|
||||||
|
color: isSelected
|
||||||
|
? Colors.orange.shade700
|
||||||
|
: Colors.grey.shade600,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
title: Text(
|
||||||
|
product.name,
|
||||||
|
style: TextStyle(
|
||||||
|
fontWeight:
|
||||||
|
isSelected ? FontWeight.bold : FontWeight.w500,
|
||||||
|
color: isSelected
|
||||||
|
? Colors.orange.shade800
|
||||||
|
: Colors.grey.shade800,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
subtitle: Text(
|
||||||
|
'Stock: ${product.stock} • Réf: ${product.reference ?? 'N/A'}',
|
||||||
|
style: TextStyle(
|
||||||
|
color: isSelected
|
||||||
|
? Colors.orange.shade600
|
||||||
|
: Colors.grey.shade600,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
trailing: isSelected
|
||||||
|
? Icon(Icons.check_circle,
|
||||||
|
color: Colors.orange.shade700)
|
||||||
|
: Icon(Icons.radio_button_unchecked,
|
||||||
|
color: Colors.grey.shade400),
|
||||||
|
onTap: () {
|
||||||
|
setState(() {
|
||||||
|
_selectedProduct = product;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildFormSection() {
|
||||||
|
return Column(
|
||||||
|
children: [
|
||||||
|
// Quantité
|
||||||
|
_buildInputField(
|
||||||
|
label: 'Quantité *',
|
||||||
|
controller: _quantiteController,
|
||||||
|
keyboardType: TextInputType.number,
|
||||||
|
icon: Icons.format_list_numbered,
|
||||||
|
suffix: _selectedProduct != null
|
||||||
|
? Text('max: ${_selectedProduct!.stock}',
|
||||||
|
style: TextStyle(color: Colors.grey.shade600))
|
||||||
|
: null,
|
||||||
|
validator: (value) {
|
||||||
|
if (value == null || value.isEmpty) {
|
||||||
|
return 'Veuillez entrer une quantité';
|
||||||
|
}
|
||||||
|
final quantite = int.tryParse(value);
|
||||||
|
if (quantite == null || quantite <= 0) {
|
||||||
|
return 'Quantité invalide';
|
||||||
|
}
|
||||||
|
if (_selectedProduct != null &&
|
||||||
|
quantite > (_selectedProduct!.stock ?? 0)) {
|
||||||
|
return 'Quantité supérieure au stock disponible';
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
},
|
||||||
|
),
|
||||||
|
const SizedBox(height: 20),
|
||||||
|
|
||||||
|
// Motif
|
||||||
|
_buildInputField(
|
||||||
|
label: 'Motif *',
|
||||||
|
controller: _motifController,
|
||||||
|
icon: Icons.description,
|
||||||
|
hintText: 'Raison de cette sortie personnelle',
|
||||||
|
validator: (value) {
|
||||||
|
if (value == null || value.trim().isEmpty) {
|
||||||
|
return 'Veuillez indiquer le motif';
|
||||||
|
}
|
||||||
|
if (value.trim().length < 5) {
|
||||||
|
return 'Le motif doit contenir au moins 5 caractères';
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
},
|
||||||
|
),
|
||||||
|
const SizedBox(height: 20),
|
||||||
|
|
||||||
|
// Notes
|
||||||
|
_buildInputField(
|
||||||
|
label: 'Notes complémentaires',
|
||||||
|
controller: _notesController,
|
||||||
|
icon: Icons.note_add,
|
||||||
|
hintText: 'Informations complémentaires (optionnel)',
|
||||||
|
maxLines: 3,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildInputField({
|
||||||
|
required String label,
|
||||||
|
required TextEditingController controller,
|
||||||
|
required IconData icon,
|
||||||
|
String? hintText,
|
||||||
|
TextInputType? keyboardType,
|
||||||
|
int maxLines = 1,
|
||||||
|
Widget? suffix,
|
||||||
|
String? Function(String?)? validator,
|
||||||
|
}) {
|
||||||
|
return Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
label,
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 16,
|
||||||
|
fontWeight: FontWeight.w600,
|
||||||
|
color: Colors.grey.shade800,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 8),
|
||||||
|
TextFormField(
|
||||||
|
controller: controller,
|
||||||
|
keyboardType: keyboardType,
|
||||||
|
maxLines: maxLines,
|
||||||
|
validator: validator,
|
||||||
|
decoration: InputDecoration(
|
||||||
|
hintText: hintText,
|
||||||
|
prefixIcon: Icon(icon, color: Colors.grey.shade600),
|
||||||
|
suffix: suffix,
|
||||||
|
border: OutlineInputBorder(
|
||||||
|
borderRadius: BorderRadius.circular(12),
|
||||||
|
borderSide: BorderSide(color: Colors.grey.shade300),
|
||||||
|
),
|
||||||
|
enabledBorder: OutlineInputBorder(
|
||||||
|
borderRadius: BorderRadius.circular(12),
|
||||||
|
borderSide: BorderSide(color: Colors.grey.shade300),
|
||||||
|
),
|
||||||
|
focusedBorder: OutlineInputBorder(
|
||||||
|
borderRadius: BorderRadius.circular(12),
|
||||||
|
borderSide: BorderSide(color: Colors.orange.shade400, width: 2),
|
||||||
|
),
|
||||||
|
filled: true,
|
||||||
|
fillColor: Colors.grey.shade50,
|
||||||
|
contentPadding:
|
||||||
|
const EdgeInsets.symmetric(horizontal: 16, vertical: 12),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildUserInfoCard() {
|
||||||
|
return Container(
|
||||||
|
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: [
|
||||||
|
Icon(Icons.person, color: Colors.grey.shade700),
|
||||||
|
const SizedBox(width: 8),
|
||||||
|
Text(
|
||||||
|
'Informations de la demande',
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 16,
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
color: Colors.grey.shade800,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
const SizedBox(height: 12),
|
||||||
|
_buildInfoRow(
|
||||||
|
Icons.account_circle, 'Demandeur', _userController.name),
|
||||||
|
if (_userController.pointDeVenteId > 0)
|
||||||
|
_buildInfoRow(Icons.store, 'Point de vente',
|
||||||
|
_userController.pointDeVenteDesignation),
|
||||||
|
_buildInfoRow(Icons.calendar_today, 'Date',
|
||||||
|
DateTime.now().toLocal().toString().split(' ')[0]),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildInfoRow(IconData icon, String label, String value) {
|
||||||
|
return Padding(
|
||||||
|
padding: const EdgeInsets.symmetric(vertical: 4),
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
Icon(icon, size: 16, color: Colors.grey.shade600),
|
||||||
|
const SizedBox(width: 8),
|
||||||
|
Text(
|
||||||
|
'$label: ',
|
||||||
|
style: TextStyle(
|
||||||
|
fontWeight: FontWeight.w500,
|
||||||
|
color: Colors.grey.shade700,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Expanded(
|
||||||
|
child: Text(
|
||||||
|
value,
|
||||||
|
style: TextStyle(color: Colors.grey.shade800),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildSubmitButton() {
|
||||||
|
return Container(
|
||||||
|
width: double.infinity,
|
||||||
|
height: 56,
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
borderRadius: BorderRadius.circular(16),
|
||||||
|
gradient: LinearGradient(
|
||||||
|
colors: [Colors.orange.shade700, Colors.orange.shade500],
|
||||||
|
begin: Alignment.topLeft,
|
||||||
|
end: Alignment.bottomRight,
|
||||||
|
),
|
||||||
|
boxShadow: [
|
||||||
|
BoxShadow(
|
||||||
|
color: Colors.orange.shade300,
|
||||||
|
blurRadius: 12,
|
||||||
|
offset: const Offset(0, 4),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
child: ElevatedButton(
|
||||||
|
onPressed: _isLoading ? null : _soumettreDemandePersonnelle,
|
||||||
|
style: ElevatedButton.styleFrom(
|
||||||
|
backgroundColor: Colors.transparent,
|
||||||
|
shadowColor: Colors.transparent,
|
||||||
|
shape: RoundedRectangleBorder(
|
||||||
|
borderRadius: BorderRadius.circular(16),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
child: _isLoading
|
||||||
|
? const Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
children: [
|
||||||
|
SizedBox(
|
||||||
|
width: 24,
|
||||||
|
height: 24,
|
||||||
|
child: CircularProgressIndicator(
|
||||||
|
strokeWidth: 2,
|
||||||
|
color: Colors.white,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
SizedBox(width: 12),
|
||||||
|
Text(
|
||||||
|
'Traitement...',
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 16,
|
||||||
|
fontWeight: FontWeight.w600,
|
||||||
|
color: Colors.white,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
: const Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
children: [
|
||||||
|
Icon(Icons.send, color: Colors.white),
|
||||||
|
SizedBox(width: 8),
|
||||||
|
Text(
|
||||||
|
'Soumettre la demande',
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 16,
|
||||||
|
fontWeight: FontWeight.w600,
|
||||||
|
color: Colors.white,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Scaffold(
|
||||||
|
appBar: CustomAppBar(title: 'Demande sortie personnelle'),
|
||||||
|
drawer: CustomDrawer(),
|
||||||
|
body: _isLoading && _products.isEmpty
|
||||||
|
? const Center(child: CircularProgressIndicator())
|
||||||
|
: FadeTransition(
|
||||||
|
opacity: _fadeAnimation,
|
||||||
|
child: SlideTransition(
|
||||||
|
position: _slideAnimation,
|
||||||
|
child: SingleChildScrollView(
|
||||||
|
padding: const EdgeInsets.all(16),
|
||||||
|
child: Form(
|
||||||
|
key: _formKey,
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
_buildHeaderCard(),
|
||||||
|
const SizedBox(height: 24),
|
||||||
|
_buildProductSelector(),
|
||||||
|
const SizedBox(height: 24),
|
||||||
|
_buildFormSection(),
|
||||||
|
const SizedBox(height: 24),
|
||||||
|
_buildUserInfoCard(),
|
||||||
|
const SizedBox(height: 32),
|
||||||
|
_buildSubmitButton(),
|
||||||
|
const SizedBox(height: 16),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
_animationController.dispose();
|
||||||
|
_quantiteController.dispose();
|
||||||
|
_motifController.dispose();
|
||||||
|
_notesController.dispose();
|
||||||
|
_searchController.dispose();
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extension on BarcodeCapture {
|
||||||
|
get rawValue => null;
|
||||||
|
}
|
||||||
1090
lib/Services/qrService.dart
Normal file
1090
lib/Services/qrService.dart
Normal file
File diff suppressed because it is too large
Load Diff
@ -1979,11 +1979,45 @@ List<String> parseHeaderInfo(dynamic blobData) {
|
|||||||
Future<int> updateStatutCommande(
|
Future<int> updateStatutCommande(
|
||||||
int commandeId, StatutCommande statut) async {
|
int commandeId, StatutCommande statut) async {
|
||||||
final db = await database;
|
final db = await database;
|
||||||
|
|
||||||
|
try {
|
||||||
|
await db.query('START TRANSACTION');
|
||||||
|
|
||||||
|
// 🔹 Si le statut devient "annulée"
|
||||||
|
if (statut == StatutCommande.annulee) {
|
||||||
|
// 1. Récupérer les détails de la commande
|
||||||
|
final details = await db.query(
|
||||||
|
'SELECT produitId, quantite FROM details_commandes WHERE commandeId = ?',
|
||||||
|
[commandeId],
|
||||||
|
);
|
||||||
|
|
||||||
|
// 2. Remettre le stock pour chaque produit
|
||||||
|
for (final row in details) {
|
||||||
|
final produitId = row['produitId'];
|
||||||
|
final quantite = row['quantite'];
|
||||||
|
|
||||||
|
await db.query(
|
||||||
|
'UPDATE products SET stock = stock + ? WHERE id = ?',
|
||||||
|
[quantite, produitId],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. Mettre à jour le statut de la commande
|
||||||
final result = await db.query(
|
final result = await db.query(
|
||||||
'UPDATE commandes SET statut = ? WHERE id = ?',
|
'UPDATE commandes SET statut = ? WHERE id = ?',
|
||||||
[statut.index, commandeId]);
|
[statut.index, commandeId],
|
||||||
|
);
|
||||||
|
|
||||||
|
await db.query('COMMIT');
|
||||||
return result.affectedRows!;
|
return result.affectedRows!;
|
||||||
|
} catch (e) {
|
||||||
|
await db.query('ROLLBACK');
|
||||||
|
print("Erreur lors de la mise à jour du statut de la commande: $e");
|
||||||
|
rethrow;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
Future<List<Commande>> getCommandesByClient(int clientId) async {
|
Future<List<Commande>> getCommandesByClient(int clientId) async {
|
||||||
final db = await database;
|
final db = await database;
|
||||||
@ -2532,7 +2566,8 @@ Future<List<Map<String, dynamic>>> getVentesParPointDeVente({
|
|||||||
final db = await database;
|
final db = await database;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
String whereClause = 'WHERE c.statut != 5';
|
// 🔹 On ne garde que les commandes confirmées (statut = 1)
|
||||||
|
String whereClause = "WHERE c.statut = 1";
|
||||||
List<dynamic> whereArgs = [];
|
List<dynamic> whereArgs = [];
|
||||||
|
|
||||||
if (aujourdHuiSeulement == true) {
|
if (aujourdHuiSeulement == true) {
|
||||||
@ -2546,7 +2581,8 @@ Future<List<Map<String, dynamic>>> getVentesParPointDeVente({
|
|||||||
_formatDate(endOfDay),
|
_formatDate(endOfDay),
|
||||||
]);
|
]);
|
||||||
} else if (dateDebut != null && dateFin != null) {
|
} else if (dateDebut != null && dateFin != null) {
|
||||||
final adjustedEndDate = DateTime(dateFin.year, dateFin.month, dateFin.day, 23, 59, 59);
|
final adjustedEndDate =
|
||||||
|
DateTime(dateFin.year, dateFin.month, dateFin.day, 23, 59, 59);
|
||||||
whereClause += ' AND c.dateCommande >= ? AND c.dateCommande <= ?';
|
whereClause += ' AND c.dateCommande >= ? AND c.dateCommande <= ?';
|
||||||
whereArgs.addAll([
|
whereArgs.addAll([
|
||||||
_formatDate(dateDebut),
|
_formatDate(dateDebut),
|
||||||
@ -2580,6 +2616,7 @@ Future<List<Map<String, dynamic>>> getVentesParPointDeVente({
|
|||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<List<Map<String, dynamic>>> getTopProduitsParPointDeVente(
|
Future<List<Map<String, dynamic>>> getTopProduitsParPointDeVente(
|
||||||
int pointDeVenteId, {
|
int pointDeVenteId, {
|
||||||
int limit = 5,
|
int limit = 5,
|
||||||
|
|||||||
@ -11,6 +11,7 @@ import 'package:qr_code_scanner_plus/qr_code_scanner_plus.dart';
|
|||||||
import 'package:intl/intl.dart';
|
import 'package:intl/intl.dart';
|
||||||
import 'package:path_provider/path_provider.dart';
|
import 'package:path_provider/path_provider.dart';
|
||||||
import 'package:excel/excel.dart' hide Border;
|
import 'package:excel/excel.dart' hide Border;
|
||||||
|
import 'package:youmazgestion/Services/qrService.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 '../Components/appDrawer.dart';
|
import '../Components/appDrawer.dart';
|
||||||
@ -28,6 +29,7 @@ class _ProductManagementPageState extends State<ProductManagementPage> {
|
|||||||
final AppDatabase _productDatabase = AppDatabase.instance;
|
final AppDatabase _productDatabase = AppDatabase.instance;
|
||||||
final AppDatabase _appDatabase = AppDatabase.instance;
|
final AppDatabase _appDatabase = AppDatabase.instance;
|
||||||
final UserController _userController = Get.find<UserController>();
|
final UserController _userController = Get.find<UserController>();
|
||||||
|
final pdfService = PdfPrintService();
|
||||||
|
|
||||||
List<Product> _products = [];
|
List<Product> _products = [];
|
||||||
List<Product> _filteredProducts = [];
|
List<Product> _filteredProducts = [];
|
||||||
@ -44,16 +46,20 @@ class _ProductManagementPageState extends State<ProductManagementPage> {
|
|||||||
bool _isAssigning = false;
|
bool _isAssigning = false;
|
||||||
final GlobalKey _qrKey = GlobalKey(debugLabel: 'QR');
|
final GlobalKey _qrKey = GlobalKey(debugLabel: 'QR');
|
||||||
|
|
||||||
|
Future<void> _loadAvailableCategories() async {
|
||||||
|
try {
|
||||||
|
final categories = await _productDatabase.getCategories();
|
||||||
|
setState(() {
|
||||||
|
_availableCategories = ['Non catégorisé', ...categories];
|
||||||
|
});
|
||||||
|
} catch (e) {
|
||||||
|
debugPrint('Erreur lors du chargement des catégories: $e');
|
||||||
|
// Garder la catégorie par défaut en cas d'erreur
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Catégories prédéfinies pour l'ajout de produits
|
// Catégories prédéfinies pour l'ajout de produits
|
||||||
final List<String> _predefinedCategories = [
|
List<String> _availableCategories = ['Non catégorisé'];
|
||||||
'Smartphone',
|
|
||||||
'Tablette',
|
|
||||||
'Accessoires',
|
|
||||||
'Multimedia',
|
|
||||||
'Informatique',
|
|
||||||
'Laptop',
|
|
||||||
'Non catégorisé'
|
|
||||||
];
|
|
||||||
bool _isUserSuperAdmin() {
|
bool _isUserSuperAdmin() {
|
||||||
return _userController.role == 'Super Admin';
|
return _userController.role == 'Super Admin';
|
||||||
}
|
}
|
||||||
@ -67,6 +73,7 @@ bool _isUserSuperAdmin() {
|
|||||||
super.initState();
|
super.initState();
|
||||||
_loadProducts();
|
_loadProducts();
|
||||||
_loadPointsDeVente();
|
_loadPointsDeVente();
|
||||||
|
_loadAvailableCategories();
|
||||||
_searchController.addListener(_filterProducts);
|
_searchController.addListener(_filterProducts);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -94,7 +101,7 @@ bool _isUserSuperAdmin() {
|
|||||||
List<Map<String, dynamic>> pointsDeVente = [];
|
List<Map<String, dynamic>> pointsDeVente = [];
|
||||||
bool isLoadingPoints = true;
|
bool isLoadingPoints = true;
|
||||||
String selectedCategory =
|
String selectedCategory =
|
||||||
_predefinedCategories.last; // 'Non catégorisé' par défaut
|
_availableCategories.last; // 'Non catégorisé' par défaut
|
||||||
File? pickedImage;
|
File? pickedImage;
|
||||||
String? qrPreviewData;
|
String? qrPreviewData;
|
||||||
bool autoGenerateReference = true;
|
bool autoGenerateReference = true;
|
||||||
@ -923,7 +930,7 @@ Future<void> _showDemandeTransfertDialog(Product product) async {
|
|||||||
// Catégorie
|
// Catégorie
|
||||||
DropdownButtonFormField<String>(
|
DropdownButtonFormField<String>(
|
||||||
value: selectedCategory,
|
value: selectedCategory,
|
||||||
items: _predefinedCategories
|
items: _availableCategories
|
||||||
.map((category) => DropdownMenuItem(
|
.map((category) => DropdownMenuItem(
|
||||||
value: category, child: Text(category)))
|
value: category, child: Text(category)))
|
||||||
.toList(),
|
.toList(),
|
||||||
@ -1360,7 +1367,43 @@ Future<void> _showDemandeTransfertDialog(Product product) async {
|
|||||||
if (columnIndex >= row.length || row[columnIndex]?.value == null)
|
if (columnIndex >= row.length || row[columnIndex]?.value == null)
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
return row[columnIndex]!.value.toString().trim();
|
var cellValue = row[columnIndex]!.value;
|
||||||
|
|
||||||
|
// 🔥 TRAITEMENT SPÉCIAL POUR IMEI
|
||||||
|
if (field == 'imei') {
|
||||||
|
print('🔍 IMEI brut depuis Excel: $cellValue (${cellValue.runtimeType})');
|
||||||
|
|
||||||
|
// Si c'est un nombre (notation scientifique)
|
||||||
|
if (cellValue is num) {
|
||||||
|
// Convertir directement en entier pour éviter les décimales
|
||||||
|
String imeiStr = cellValue.toInt().toString();
|
||||||
|
print('🔄 IMEI converti depuis num: $imeiStr');
|
||||||
|
return imeiStr;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Si c'est déjà un String
|
||||||
|
String strValue = cellValue.toString().trim();
|
||||||
|
|
||||||
|
// Gérer la notation scientifique dans les strings
|
||||||
|
if (strValue.contains('E') || strValue.contains('e')) {
|
||||||
|
try {
|
||||||
|
// Remplacer virgule par point et parser
|
||||||
|
String normalized = strValue.replaceAll(',', '.');
|
||||||
|
double numValue = double.parse(normalized);
|
||||||
|
String imeiStr = numValue.toInt().toString();
|
||||||
|
print('🔄 IMEI converti depuis notation scientifique: $strValue → $imeiStr');
|
||||||
|
return imeiStr;
|
||||||
|
} catch (e) {
|
||||||
|
print('❌ Erreur conversion IMEI: $e');
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return strValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Pour les autres champs
|
||||||
|
return cellValue.toString().trim();
|
||||||
}
|
}
|
||||||
|
|
||||||
void _startPointDeVenteAssignmentScanning() {
|
void _startPointDeVenteAssignmentScanning() {
|
||||||
@ -1390,7 +1433,6 @@ Future<void> _showDemandeTransfertDialog(Product product) async {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
Map<String, dynamic> _normalizeRowData(
|
Map<String, dynamic> _normalizeRowData(
|
||||||
List<Data?> row, Map<String, int> mapping, int rowIndex) {
|
List<Data?> row, Map<String, int> mapping, int rowIndex) {
|
||||||
final normalizedData = <String, dynamic>{};
|
final normalizedData = <String, dynamic>{};
|
||||||
@ -1401,16 +1443,11 @@ Future<void> _showDemandeTransfertDialog(Product product) async {
|
|||||||
return value.toString().trim();
|
return value.toString().trim();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fonction simple pour les nombres (maintenant ils sont corrects)
|
// Fonction simple pour les nombres
|
||||||
double? _normalizeNumber(String? value) {
|
double? _normalizeNumber(String? value) {
|
||||||
if (value == null || value.isEmpty) return null;
|
if (value == null || value.isEmpty) return null;
|
||||||
|
|
||||||
// Remplacer les virgules par des points et supprimer les espaces
|
|
||||||
final cleaned = value.replaceAll(',', '.').replaceAll(RegExp(r'\s+'), '');
|
final cleaned = value.replaceAll(',', '.').replaceAll(RegExp(r'\s+'), '');
|
||||||
|
|
||||||
// Supprimer les caractères non numériques sauf le point
|
|
||||||
final numericString = cleaned.replaceAll(RegExp(r'[^0-9.]'), '');
|
final numericString = cleaned.replaceAll(RegExp(r'[^0-9.]'), '');
|
||||||
|
|
||||||
return double.tryParse(numericString);
|
return double.tryParse(numericString);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1422,7 +1459,7 @@ Future<void> _showDemandeTransfertDialog(Product product) async {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Normalisation du prix (maintenant simple car corrigé en amont)
|
// Normalisation du prix
|
||||||
if (mapping.containsKey('price')) {
|
if (mapping.containsKey('price')) {
|
||||||
final priceValue = _cleanValue(_getColumnValue(row, mapping, 'price'));
|
final priceValue = _cleanValue(_getColumnValue(row, mapping, 'price'));
|
||||||
final price = _normalizeNumber(priceValue);
|
final price = _normalizeNumber(priceValue);
|
||||||
@ -1438,7 +1475,6 @@ Future<void> _showDemandeTransfertDialog(Product product) async {
|
|||||||
if (reference != null && reference.isNotEmpty) {
|
if (reference != null && reference.isNotEmpty) {
|
||||||
normalizedData['reference'] = reference;
|
normalizedData['reference'] = reference;
|
||||||
} else {
|
} else {
|
||||||
// Génération automatique si non fournie
|
|
||||||
normalizedData['reference'] = _generateUniqueReference();
|
normalizedData['reference'] = _generateUniqueReference();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1463,7 +1499,6 @@ Future<void> _showDemandeTransfertDialog(Product product) async {
|
|||||||
if (mapping.containsKey('ram')) {
|
if (mapping.containsKey('ram')) {
|
||||||
final ram = _cleanValue(_getColumnValue(row, mapping, 'ram'));
|
final ram = _cleanValue(_getColumnValue(row, mapping, 'ram'));
|
||||||
if (ram != null && ram.isNotEmpty) {
|
if (ram != null && ram.isNotEmpty) {
|
||||||
// Standardisation du format (ex: "8 Go", "16GB" -> "8 Go", "16 Go")
|
|
||||||
final ramValue = ram.replaceAll('GB', 'Go').replaceAll('go', 'Go');
|
final ramValue = ram.replaceAll('GB', 'Go').replaceAll('go', 'Go');
|
||||||
normalizedData['ram'] = ramValue;
|
normalizedData['ram'] = ramValue;
|
||||||
}
|
}
|
||||||
@ -1471,48 +1506,88 @@ Future<void> _showDemandeTransfertDialog(Product product) async {
|
|||||||
|
|
||||||
// Normalisation de la mémoire interne
|
// Normalisation de la mémoire interne
|
||||||
if (mapping.containsKey('memoire_interne')) {
|
if (mapping.containsKey('memoire_interne')) {
|
||||||
final memoire =
|
final memoire = _cleanValue(_getColumnValue(row, mapping, 'memoire_interne'));
|
||||||
_cleanValue(_getColumnValue(row, mapping, 'memoire_interne'));
|
|
||||||
if (memoire != null && memoire.isNotEmpty) {
|
if (memoire != null && memoire.isNotEmpty) {
|
||||||
// Standardisation du format (ex: "256GB" -> "256 Go")
|
final memoireValue = memoire.replaceAll('GB', 'Go').replaceAll('go', 'Go');
|
||||||
final memoireValue =
|
|
||||||
memoire.replaceAll('GB', 'Go').replaceAll('go', 'Go');
|
|
||||||
normalizedData['memoire_interne'] = memoireValue;
|
normalizedData['memoire_interne'] = memoireValue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Normalisation de l'IMEI
|
// 🔥 IMPORTANT: Normaliser l'IMEI EN PREMIER avant de gérer le stock
|
||||||
|
// 🔥 Normalisation de l'IMEI (simplifié car _getColumnValue le gère maintenant)
|
||||||
|
String? imeiValue;
|
||||||
if (mapping.containsKey('imei')) {
|
if (mapping.containsKey('imei')) {
|
||||||
final imei = _cleanValue(_getColumnValue(row, mapping, 'imei'));
|
final imei = _cleanValue(_getColumnValue(row, mapping, 'imei'));
|
||||||
|
|
||||||
if (imei != null && imei.isNotEmpty) {
|
if (imei != null && imei.isNotEmpty) {
|
||||||
// Suppression des espaces et tirets dans l'IMEI
|
// Nettoyer les espaces et tirets
|
||||||
final imeiValue = imei.replaceAll(RegExp(r'[\s-]'), '');
|
String cleanedImei = imei.replaceAll(RegExp(r'[\s-]'), '');
|
||||||
if (imeiValue.length >= 15) {
|
|
||||||
normalizedData['imei'] = imeiValue.substring(0, 15);
|
// Vérifier que c'est bien un IMEI valide (10-15 chiffres)
|
||||||
} else {
|
if (cleanedImei.length >= 10 &&
|
||||||
|
cleanedImei.length <= 15 &&
|
||||||
|
RegExp(r'^\d+$').hasMatch(cleanedImei)) {
|
||||||
|
|
||||||
|
imeiValue = cleanedImei.length > 15
|
||||||
|
? cleanedImei.substring(0, 15)
|
||||||
|
: cleanedImei;
|
||||||
|
|
||||||
normalizedData['imei'] = imeiValue;
|
normalizedData['imei'] = imeiValue;
|
||||||
|
print('✅ IMEI valide enregistré: $imeiValue');
|
||||||
|
} else {
|
||||||
|
print('⚠️ IMEI invalide ignoré: "$cleanedImei" (longueur: ${cleanedImei.length})');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Le reste du code reste identique...
|
||||||
|
|
||||||
// Normalisation du point de vente
|
// Normalisation du point de vente
|
||||||
if (mapping.containsKey('point_de_vente')) {
|
if (mapping.containsKey('point_de_vente')) {
|
||||||
final pv = _cleanValue(_getColumnValue(row, mapping, 'point_de_vente'));
|
final pv = _cleanValue(_getColumnValue(row, mapping, 'point_de_vente'));
|
||||||
if (pv != null && pv.isNotEmpty) {
|
if (pv != null && pv.isNotEmpty) {
|
||||||
// Suppression des espaces superflus
|
normalizedData['point_de_vente'] = pv.replaceAll(RegExp(r'\s+'), ' ').trim();
|
||||||
normalizedData['point_de_vente'] =
|
|
||||||
pv.replaceAll(RegExp(r'\s+'), ' ').trim();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Valeurs par défaut
|
// Valeurs par défaut
|
||||||
normalizedData['description'] = ''; // Description toujours vide
|
normalizedData['description'] = '';
|
||||||
|
|
||||||
|
// 🎯 LOGIQUE CRITIQUE: Gestion du stock selon la présence d'IMEI
|
||||||
|
// On vérifie si normalizedData['imei'] existe ET n'est pas vide
|
||||||
|
if (normalizedData.containsKey('imei') &&
|
||||||
|
normalizedData['imei'] != null &&
|
||||||
|
normalizedData['imei'].toString().isNotEmpty) {
|
||||||
|
// Produit avec IMEI → stock forcé à 1
|
||||||
|
normalizedData['stock'] = 1;
|
||||||
|
print('🔒 Stock forcé à 1 car IMEI présent: ${normalizedData['imei']}');
|
||||||
|
} else {
|
||||||
|
// Produit sans IMEI → utiliser le stock du fichier
|
||||||
if (mapping.containsKey('stock')) {
|
if (mapping.containsKey('stock')) {
|
||||||
final stockValue = _cleanValue(_getColumnValue(row, mapping, 'stock'));
|
final stockValue = _cleanValue(_getColumnValue(row, mapping, 'stock'));
|
||||||
final stock = int.tryParse(stockValue ?? '0') ?? 1;
|
|
||||||
|
// Try parsing as int first
|
||||||
|
int? stock = int.tryParse(stockValue ?? '');
|
||||||
|
|
||||||
|
// If parsing as int fails, try parsing as double and convert to int
|
||||||
|
if (stock == null && stockValue != null && stockValue.isNotEmpty) {
|
||||||
|
final doubleValue = double.tryParse(stockValue);
|
||||||
|
if (doubleValue != null) {
|
||||||
|
stock = doubleValue.toInt();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Final fallback: ensure at least 1
|
||||||
|
stock ??= 1;
|
||||||
|
|
||||||
|
// Never allow 0 or negative values
|
||||||
normalizedData['stock'] = stock > 0 ? stock : 1;
|
normalizedData['stock'] = stock > 0 ? stock : 1;
|
||||||
|
|
||||||
|
print('📦 Stock depuis Excel: $stock (pas d\'IMEI)');
|
||||||
} else {
|
} else {
|
||||||
normalizedData['stock'] = 1; // Valeur par défaut
|
normalizedData['stock'] = 1;
|
||||||
|
print('📦 Stock par défaut: 1 (pas d\'IMEI, pas de colonne stock)');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Validation des données obligatoires
|
// Validation des données obligatoires
|
||||||
@ -1523,8 +1598,6 @@ Future<void> _showDemandeTransfertDialog(Product product) async {
|
|||||||
|
|
||||||
return normalizedData;
|
return normalizedData;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Méthode pour mapper les en-têtes aux colonnes (CORRIGÉE)
|
|
||||||
Map<String, int> _mapHeaders(List<Data?> headerRow) {
|
Map<String, int> _mapHeaders(List<Data?> headerRow) {
|
||||||
Map<String, int> columnMapping = {};
|
Map<String, int> columnMapping = {};
|
||||||
|
|
||||||
@ -1533,62 +1606,64 @@ Future<void> _showDemandeTransfertDialog(Product product) async {
|
|||||||
|
|
||||||
String header = headerRow[i]!.value.toString().trim().toUpperCase();
|
String header = headerRow[i]!.value.toString().trim().toUpperCase();
|
||||||
|
|
||||||
// Debug : afficher chaque en-tête trouvé
|
print('📋 En-tête colonne $i: "$header"');
|
||||||
print('En-tête trouvé: "$header" à la colonne $i');
|
|
||||||
|
|
||||||
// Mapping amélioré pour gérer les variations
|
// Nom du produit
|
||||||
if ((header.contains('NOM') &&
|
if ((header.contains('NOM') && header.contains('PRODUIT')) || header == 'NOM') {
|
||||||
(header.contains('PRODUIT') || header.contains('DU'))) ||
|
|
||||||
header == 'NOM DU PRODUITS' ||
|
|
||||||
header == 'NOM') {
|
|
||||||
columnMapping['name'] = i;
|
columnMapping['name'] = i;
|
||||||
print('→ Mappé vers name');
|
print(' ✅ Mappé vers name');
|
||||||
} else if ((header.contains('REFERENCE') &&
|
}
|
||||||
(header.contains('PRODUIT') || header.contains('PRODUITS'))) ||
|
// Référence
|
||||||
header == 'REFERENCE PRODUITS' ||
|
else if (header.contains('REFERENCE')) {
|
||||||
header == 'REFERENCE') {
|
|
||||||
columnMapping['reference'] = i;
|
columnMapping['reference'] = i;
|
||||||
print('→ Mappé vers reference');
|
print(' ✅ Mappé vers reference');
|
||||||
} else if ((header.contains('CATEGORIES') &&
|
}
|
||||||
(header.contains('PRODUIT') || header.contains('PRODUITS'))) ||
|
// Catégorie - VERSION TOLÉRANTE ⭐
|
||||||
header == 'CATEGORIES PRODUITS' ||
|
else if (header.contains('CATEG') && header.contains('PRODUIT')) {
|
||||||
header == 'CATEGORIE' ||
|
|
||||||
header == 'CATEGORY') {
|
|
||||||
columnMapping['category'] = i;
|
columnMapping['category'] = i;
|
||||||
print('→ Mappé vers category');
|
print(' ✅ Mappé vers category');
|
||||||
} else if (header == 'MARQUE' || header == 'BRAND') {
|
}
|
||||||
|
// Marque
|
||||||
|
else if (header == 'MARQUE' || header == 'BRAND') {
|
||||||
columnMapping['marque'] = i;
|
columnMapping['marque'] = i;
|
||||||
print('→ Mappé vers marque');
|
print(' ✅ Mappé vers marque');
|
||||||
} else if (header == 'RAM' || header.contains('MEMOIRE RAM')) {
|
}
|
||||||
|
// RAM
|
||||||
|
else if (header == 'RAM' || header.contains('MEMOIRE RAM')) {
|
||||||
columnMapping['ram'] = i;
|
columnMapping['ram'] = i;
|
||||||
print('→ Mappé vers ram');
|
print(' ✅ Mappé vers ram');
|
||||||
} else if (header == 'INTERNE' ||
|
}
|
||||||
header.contains('MEMOIRE INTERNE') ||
|
// Mémoire interne
|
||||||
header.contains('STOCKAGE')) {
|
else if (header == 'INTERNE' || header.contains('MEMOIRE INTERNE') || header.contains('STOCKAGE')) {
|
||||||
columnMapping['memoire_interne'] = i;
|
columnMapping['memoire_interne'] = i;
|
||||||
print('→ Mappé vers memoire_interne');
|
print(' ✅ Mappé vers memoire_interne');
|
||||||
} else if (header == 'IMEI' || header.contains('NUMERO IMEI')) {
|
}
|
||||||
|
// IMEI
|
||||||
|
else if (header == 'IMEI' || header.contains('NUMERO IMEI')) {
|
||||||
columnMapping['imei'] = i;
|
columnMapping['imei'] = i;
|
||||||
print('→ Mappé vers imei');
|
print(' ✅ Mappé vers imei');
|
||||||
} else if (header == 'PRIX' || header == 'PRICE') {
|
}
|
||||||
|
// Prix
|
||||||
|
else if (header == 'PRIX' || header == 'PRICE') {
|
||||||
columnMapping['price'] = i;
|
columnMapping['price'] = i;
|
||||||
print('→ Mappé vers price');
|
print(' ✅ Mappé vers price');
|
||||||
} else if (header == 'STOCK' || header == 'QUANTITY' || header == 'QTE') {
|
}
|
||||||
|
// Stock
|
||||||
|
else if (header == 'STOCK' || header == 'QUANTITY' || header == 'QTE') {
|
||||||
columnMapping['stock'] = i;
|
columnMapping['stock'] = i;
|
||||||
print('→ Mappé vers stock');
|
print(' ✅ Mappé vers stock');
|
||||||
} else if (header == 'BOUTIQUE' ||
|
}
|
||||||
header.contains('POINT DE VENTE') ||
|
// Point de vente
|
||||||
header == 'MAGASIN') {
|
else if (header.contains('BOUTIQUE') || header.contains('POINT') || header == 'MAGASIN') {
|
||||||
columnMapping['point_de_vente'] = i;
|
columnMapping['point_de_vente'] = i;
|
||||||
print('→ Mappé vers point_de_vente');
|
print(' ✅ Mappé vers point_de_vente');
|
||||||
} else {
|
}
|
||||||
print('→ Non reconnu');
|
else {
|
||||||
|
print(' ⚠️ Non reconnu');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Debug : afficher le mapping final
|
print('\n🎯 MAPPING FINAL: $columnMapping\n');
|
||||||
print('Mapping final: $columnMapping');
|
|
||||||
|
|
||||||
return columnMapping;
|
return columnMapping;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2184,7 +2259,7 @@ Future<void> _showDemandeTransfertDialog(Product product) async {
|
|||||||
'RAM', // ram
|
'RAM', // ram
|
||||||
'INTERNE', // memoire_interne
|
'INTERNE', // memoire_interne
|
||||||
'IMEI', // imei
|
'IMEI', // imei
|
||||||
'STOCK'
|
'STOCK',
|
||||||
'PRIX', // price
|
'PRIX', // price
|
||||||
'BOUTIQUE', // point_de_vente
|
'BOUTIQUE', // point_de_vente
|
||||||
];
|
];
|
||||||
@ -3255,15 +3330,12 @@ Future<void> _showDemandeTransfertDialog(Product product) async {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Méthodes placeholder pour les fonctions manquantes
|
|
||||||
void _showQRCode(Product product) {
|
void _showQRCode(Product product) {
|
||||||
// État pour contrôler le type d'affichage (true = URL complète, false = référence seulement)
|
RxBool showFullUrl = false.obs;
|
||||||
RxBool showFullUrl = true.obs;
|
RxInt nombreAImprimer = (product.stock ?? 1).obs; // 🔹 Valeur modifiable
|
||||||
|
|
||||||
Get.dialog(
|
Get.dialog(
|
||||||
Obx(() {
|
Obx(() {
|
||||||
// Données du QR code selon l'état
|
|
||||||
final qrData = showFullUrl.value
|
final qrData = showFullUrl.value
|
||||||
? 'https://stock.guycom.mg/${product.reference}'
|
? 'https://stock.guycom.mg/${product.reference}'
|
||||||
: product.reference!;
|
: product.reference!;
|
||||||
@ -3282,11 +3354,11 @@ Future<void> _showDemandeTransfertDialog(Product product) async {
|
|||||||
],
|
],
|
||||||
),
|
),
|
||||||
content: Container(
|
content: Container(
|
||||||
width: 300,
|
width: 350,
|
||||||
child: Column(
|
child: Column(
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
children: [
|
children: [
|
||||||
// Bouton pour basculer entre URL et référence
|
// Bouton bascule URL / Référence
|
||||||
ElevatedButton.icon(
|
ElevatedButton.icon(
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
showFullUrl.value = !showFullUrl.value;
|
showFullUrl.value = !showFullUrl.value;
|
||||||
@ -3296,19 +3368,18 @@ Future<void> _showDemandeTransfertDialog(Product product) async {
|
|||||||
size: 16,
|
size: 16,
|
||||||
),
|
),
|
||||||
label: Text(
|
label: Text(
|
||||||
showFullUrl.value ? 'URL/Référence' : 'Référence',
|
showFullUrl.value ? 'URL Complète' : 'Référence Seulement',
|
||||||
style: const TextStyle(fontSize: 14),
|
style: const TextStyle(fontSize: 14),
|
||||||
),
|
),
|
||||||
style: ElevatedButton.styleFrom(
|
style: ElevatedButton.styleFrom(
|
||||||
backgroundColor:
|
backgroundColor: showFullUrl.value ? Colors.blue : Colors.green,
|
||||||
showFullUrl.value ? Colors.blue : Colors.green,
|
|
||||||
foregroundColor: Colors.white,
|
foregroundColor: Colors.white,
|
||||||
minimumSize: const Size(double.infinity, 36),
|
minimumSize: const Size(double.infinity, 40),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(height: 16),
|
const SizedBox(height: 16),
|
||||||
|
|
||||||
// Container du QR Code
|
// QR Code affiché
|
||||||
Container(
|
Container(
|
||||||
padding: const EdgeInsets.all(16),
|
padding: const EdgeInsets.all(16),
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
@ -3321,11 +3392,12 @@ Future<void> _showDemandeTransfertDialog(Product product) async {
|
|||||||
version: QrVersions.auto,
|
version: QrVersions.auto,
|
||||||
size: 200,
|
size: 200,
|
||||||
backgroundColor: Colors.white,
|
backgroundColor: Colors.white,
|
||||||
|
errorCorrectionLevel: QrErrorCorrectLevel.M,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(height: 16),
|
const SizedBox(height: 16),
|
||||||
|
|
||||||
// Affichage des données actuelles
|
// Informations du produit
|
||||||
Container(
|
Container(
|
||||||
padding: const EdgeInsets.all(12),
|
padding: const EdgeInsets.all(12),
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
@ -3334,18 +3406,60 @@ Future<void> _showDemandeTransfertDialog(Product product) async {
|
|||||||
),
|
),
|
||||||
child: Column(
|
child: Column(
|
||||||
children: [
|
children: [
|
||||||
Text(
|
Row(
|
||||||
showFullUrl.value
|
children: [
|
||||||
? 'URL Complète'
|
Icon(Icons.inventory_2, size: 16, color: Colors.grey.shade600),
|
||||||
: 'Référence Seulement',
|
const SizedBox(width: 8),
|
||||||
style: const TextStyle(fontWeight: FontWeight.bold),
|
Expanded(
|
||||||
|
child: Text(
|
||||||
|
product.name,
|
||||||
|
style: const TextStyle(fontWeight: FontWeight.bold, fontSize: 14),
|
||||||
),
|
),
|
||||||
const SizedBox(height: 4),
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
const SizedBox(height: 8),
|
||||||
|
|
||||||
|
// 🔹 Nouveau champ : nombre à imprimer
|
||||||
|
Row(
|
||||||
|
children: [
|
||||||
|
const Icon(Icons.format_list_numbered, size: 16, color: Colors.deepPurple),
|
||||||
|
const SizedBox(width: 8),
|
||||||
|
Expanded(
|
||||||
|
child: TextField(
|
||||||
|
keyboardType: TextInputType.number,
|
||||||
|
decoration: InputDecoration(
|
||||||
|
labelText: 'Nombre d\'étiquettes à imprimer',
|
||||||
|
border: const OutlineInputBorder(),
|
||||||
|
isDense: true,
|
||||||
|
contentPadding: const EdgeInsets.symmetric(horizontal: 8, vertical: 8),
|
||||||
|
),
|
||||||
|
controller: TextEditingController(text: nombreAImprimer.value.toString()),
|
||||||
|
onChanged: (val) {
|
||||||
|
final parsed = int.tryParse(val);
|
||||||
|
if (parsed != null && parsed > 0) {
|
||||||
|
nombreAImprimer.value = parsed;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
const SizedBox(height: 8),
|
||||||
|
|
||||||
|
Row(
|
||||||
|
children: [
|
||||||
|
Icon(Icons.label, size: 16, color: Colors.orange.shade600),
|
||||||
|
const SizedBox(width: 8),
|
||||||
Text(
|
Text(
|
||||||
qrData,
|
'Format: Étiquette Niimbot B1 (50x15mm)',
|
||||||
style:
|
style: TextStyle(
|
||||||
const TextStyle(fontSize: 12, color: Colors.grey),
|
fontSize: 12,
|
||||||
textAlign: TextAlign.center,
|
color: Colors.orange.shade700,
|
||||||
|
fontWeight: FontWeight.w600,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
@ -3354,23 +3468,72 @@ Future<void> _showDemandeTransfertDialog(Product product) async {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
actions: [
|
actions: [
|
||||||
TextButton(
|
// Copier
|
||||||
|
TextButton.icon(
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
Clipboard.setData(ClipboardData(text: qrData));
|
Clipboard.setData(ClipboardData(text: qrData));
|
||||||
Get.back();
|
|
||||||
Get.snackbar(
|
Get.snackbar(
|
||||||
'Copié',
|
'Copié',
|
||||||
'${showFullUrl.value ? "URL" : "Référence"} copiée dans le presse-papiers',
|
'${showFullUrl.value ? "URL" : "Référence"} copiée',
|
||||||
backgroundColor: Colors.green,
|
backgroundColor: Colors.green,
|
||||||
colorText: Colors.white,
|
colorText: Colors.white,
|
||||||
|
duration: const Duration(seconds: 2),
|
||||||
|
icon: const Icon(Icons.check_circle, color: Colors.white),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
child: Text('Copier ${showFullUrl.value ? "URL" : "Référence"}'),
|
icon: const Icon(Icons.copy, size: 18),
|
||||||
|
label: const Text('Copier'),
|
||||||
),
|
),
|
||||||
TextButton(
|
|
||||||
onPressed: () => _generatePDF(product, qrData),
|
// Paramètres
|
||||||
child: const Text('Imprimer en PDF'),
|
TextButton.icon(
|
||||||
|
onPressed: () async {
|
||||||
|
Get.back();
|
||||||
|
pdfService.showNiimbotSettingsDialog();
|
||||||
|
},
|
||||||
|
icon: const Icon(Icons.settings, size: 18),
|
||||||
|
label: const Text('Paramètres'),
|
||||||
|
style: TextButton.styleFrom(foregroundColor: Colors.blue.shade700),
|
||||||
),
|
),
|
||||||
|
|
||||||
|
// 🔹 Imprimer selon le nombre choisi
|
||||||
|
ElevatedButton.icon(
|
||||||
|
onPressed: () async {
|
||||||
|
Get.back();
|
||||||
|
|
||||||
|
final int n = nombreAImprimer.value;
|
||||||
|
for (int i = 0; i < n; i++) {
|
||||||
|
await pdfService.printQrNiimbotOptimized(
|
||||||
|
qrData,
|
||||||
|
productName: null,
|
||||||
|
reference: product.reference ?? '',
|
||||||
|
leftPadding: 1.0,
|
||||||
|
topPadding: 0.5,
|
||||||
|
qrSize: 12.0,
|
||||||
|
fontSize: 5.0,
|
||||||
|
labelSize: NiimbotLabelSize.small,
|
||||||
|
);
|
||||||
|
await Future.delayed(const Duration(milliseconds: 100));
|
||||||
|
}
|
||||||
|
|
||||||
|
Get.snackbar(
|
||||||
|
'Impression terminée',
|
||||||
|
'$n étiquette${n > 1 ? "s" : ""} imprimée${n > 1 ? "s" : ""}',
|
||||||
|
backgroundColor: Colors.green,
|
||||||
|
colorText: Colors.white,
|
||||||
|
duration: const Duration(seconds: 3),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
icon: const Icon(Icons.print, size: 18),
|
||||||
|
label: const Text('Imprimer'),
|
||||||
|
style: ElevatedButton.styleFrom(
|
||||||
|
backgroundColor: Colors.orange.shade600,
|
||||||
|
foregroundColor: Colors.white,
|
||||||
|
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
|
||||||
|
// Fermer
|
||||||
TextButton(
|
TextButton(
|
||||||
onPressed: () => Get.back(),
|
onPressed: () => Get.back(),
|
||||||
child: const Text('Fermer'),
|
child: const Text('Fermer'),
|
||||||
@ -3380,7 +3543,6 @@ Future<void> _showDemandeTransfertDialog(Product product) async {
|
|||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> _generatePDF(Product product, String qrUrl) async {
|
Future<void> _generatePDF(Product product, String qrUrl) async {
|
||||||
final pdf = pw.Document();
|
final pdf = pw.Document();
|
||||||
|
|
||||||
@ -3439,9 +3601,9 @@ Future<void> _showDemandeTransfertDialog(Product product) async {
|
|||||||
List<Map<String, dynamic>> pointsDeVente = [];
|
List<Map<String, dynamic>> pointsDeVente = [];
|
||||||
bool isLoadingPoints = true;
|
bool isLoadingPoints = true;
|
||||||
// Initialiser la catégorie sélectionnée de manière sécurisée
|
// Initialiser la catégorie sélectionnée de manière sécurisée
|
||||||
String selectedCategory = _predefinedCategories.contains(product.category)
|
String selectedCategory = _availableCategories.contains(product.category)
|
||||||
? product.category
|
? product.category
|
||||||
: _predefinedCategories.last; // 'Non catégorisé' par défaut
|
: _availableCategories.last; // 'Non catégorisé' par défaut
|
||||||
File? pickedImage;
|
File? pickedImage;
|
||||||
String? qrPreviewData;
|
String? qrPreviewData;
|
||||||
bool showAddNewPoint = false;
|
bool showAddNewPoint = false;
|
||||||
@ -3809,7 +3971,7 @@ Future<void> _showDemandeTransfertDialog(Product product) async {
|
|||||||
// Catégorie avec gestion des valeurs non présentes
|
// Catégorie avec gestion des valeurs non présentes
|
||||||
DropdownButtonFormField<String>(
|
DropdownButtonFormField<String>(
|
||||||
value: selectedCategory,
|
value: selectedCategory,
|
||||||
items: _predefinedCategories
|
items: _availableCategories
|
||||||
.map((category) => DropdownMenuItem(
|
.map((category) => DropdownMenuItem(
|
||||||
value: category, child: Text(category)))
|
value: category, child: Text(category)))
|
||||||
.toList(),
|
.toList(),
|
||||||
|
|||||||
@ -163,11 +163,11 @@ class _GestionCommandesPageState extends State<GestionCommandesPage> {
|
|||||||
await _showCashPaymentDialog(commande, selectedPayment.amountGiven);
|
await _showCashPaymentDialog(commande, selectedPayment.amountGiven);
|
||||||
}
|
}
|
||||||
|
|
||||||
await _updateStatut(
|
// await _updateStatut(
|
||||||
commande.id!,
|
// commande.id!,
|
||||||
StatutCommande.confirmee,
|
// StatutCommande.confirmee,
|
||||||
validateurId: userController.userId,
|
// validateurId: userController.userId,
|
||||||
);
|
// );
|
||||||
|
|
||||||
await _generateReceipt(commande, selectedPayment);
|
await _generateReceipt(commande, selectedPayment);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,11 +1,18 @@
|
|||||||
|
// Importations nécessaires
|
||||||
|
import 'dart:typed_data';
|
||||||
|
import 'package:flutter/foundation.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
|
import 'package:image/image.dart' as img;
|
||||||
|
import 'package:zxing2/qrcode.dart';
|
||||||
|
import 'package:camera/camera.dart';
|
||||||
|
import 'package:mobile_scanner/mobile_scanner.dart';
|
||||||
|
|
||||||
import 'package:youmazgestion/Components/app_bar.dart';
|
import 'package:youmazgestion/Components/app_bar.dart';
|
||||||
import 'package:youmazgestion/Components/appDrawer.dart';
|
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});
|
||||||
@ -54,55 +61,200 @@ class _DemandeSortiePersonnellePageState
|
|||||||
_loadProducts();
|
_loadProducts();
|
||||||
_searchController.addListener(_filterProducts);
|
_searchController.addListener(_filterProducts);
|
||||||
}
|
}
|
||||||
|
/// ----------------- SCAN QR CODE -----------------
|
||||||
|
CameraController? _cameraController;
|
||||||
|
bool _isScanning = false;
|
||||||
|
|
||||||
|
Future<void> _scanQrOrBarcode() async {
|
||||||
|
if (defaultTargetPlatform == TargetPlatform.windows) {
|
||||||
|
final cameras = await availableCameras();
|
||||||
|
if (cameras.isEmpty) {
|
||||||
|
_showErrorSnackbar("Aucune caméra détectée");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Disposer l'ancien contrôleur
|
||||||
|
await _cameraController?.dispose();
|
||||||
|
|
||||||
|
_cameraController = CameraController(
|
||||||
|
cameras.first,
|
||||||
|
ResolutionPreset.high, // ✅ Meilleure résolution pour QR
|
||||||
|
enableAudio: false,
|
||||||
|
);
|
||||||
|
|
||||||
|
try {
|
||||||
|
await _cameraController!.initialize();
|
||||||
|
} catch (e) {
|
||||||
|
_showErrorSnackbar("Erreur initialisation caméra: $e");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_isScanning = true;
|
||||||
|
|
||||||
|
Future<void> scanLoop() async {
|
||||||
|
// ✅ Attendre que le dialog soit affiché
|
||||||
|
await Future.delayed(const Duration(milliseconds: 300));
|
||||||
|
|
||||||
|
while (_isScanning && _cameraController != null) {
|
||||||
|
try {
|
||||||
|
final XFile file = await _cameraController!.takePicture();
|
||||||
|
final bytes = await file.readAsBytes();
|
||||||
|
|
||||||
|
// ✅ Décoder l'image
|
||||||
|
final imageDecoded = img.decodeImage(bytes);
|
||||||
|
if (imageDecoded == null) {
|
||||||
|
print("❌ Image non décodée");
|
||||||
|
await Future.delayed(const Duration(milliseconds: 800));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
print("✅ Image décodée: ${imageDecoded.width}x${imageDecoded.height}");
|
||||||
|
|
||||||
|
// ✅ CORRECTION CRITIQUE: Convertir en format RGB correct
|
||||||
|
final rgbBytes = <int>[];
|
||||||
|
for (int y = 0; y < imageDecoded.height; y++) {
|
||||||
|
for (int x = 0; x < imageDecoded.width; x++) {
|
||||||
|
final pixel = imageDecoded.getPixel(x, y);
|
||||||
|
rgbBytes.add(pixel.r.toInt());
|
||||||
|
rgbBytes.add(pixel.g.toInt());
|
||||||
|
rgbBytes.add(pixel.b.toInt());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Créer Int32List pour ZXing
|
||||||
|
final int32Data = Int32List(imageDecoded.width * imageDecoded.height);
|
||||||
|
for (int i = 0; i < imageDecoded.height; i++) {
|
||||||
|
for (int j = 0; j < imageDecoded.width; j++) {
|
||||||
|
final idx = i * imageDecoded.width + j;
|
||||||
|
final rgbIdx = idx * 3;
|
||||||
|
final r = rgbBytes[rgbIdx];
|
||||||
|
final g = rgbBytes[rgbIdx + 1];
|
||||||
|
final b = rgbBytes[rgbIdx + 2];
|
||||||
|
// Format ARGB
|
||||||
|
int32Data[idx] = (0xFF << 24) | (r << 16) | (g << 8) | b;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
final luminanceSource = RGBLuminanceSource(
|
||||||
|
imageDecoded.width,
|
||||||
|
imageDecoded.height,
|
||||||
|
int32Data,
|
||||||
|
);
|
||||||
|
|
||||||
|
final bitmap = BinaryBitmap(HybridBinarizer(luminanceSource));
|
||||||
|
final reader = QRCodeReader();
|
||||||
|
|
||||||
|
try {
|
||||||
|
final result = reader.decode(bitmap);
|
||||||
|
if (result.text.isNotEmpty) {
|
||||||
|
print("✅ QR Code détecté: ${result.text}");
|
||||||
|
_isScanning = false;
|
||||||
|
|
||||||
|
if (mounted && Navigator.of(context).canPop()) {
|
||||||
|
Navigator.of(context).pop();
|
||||||
|
}
|
||||||
|
|
||||||
|
setState(() {
|
||||||
|
_searchController.text = result.text;
|
||||||
|
});
|
||||||
|
_filterProducts();
|
||||||
|
_showSuccessSnackbar("QR Code détecté : ${result.text}");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} on NotFoundException catch (_) {
|
||||||
|
// Pas de QR trouvé dans cette frame
|
||||||
|
print("⚠️ Pas de QR trouvé");
|
||||||
|
} catch (e) {
|
||||||
|
print("❌ Erreur décodage QR: $e");
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (e) {
|
||||||
|
print("❌ Erreur capture: $e");
|
||||||
|
}
|
||||||
|
|
||||||
|
// ✅ Délai plus long pour éviter la surcharge
|
||||||
|
await Future.delayed(const Duration(milliseconds: 800));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ✅ Lancer la boucle AVANT d'afficher le dialog
|
||||||
|
final scanFuture = scanLoop();
|
||||||
|
|
||||||
void _scanQrOrBarcode() async {
|
|
||||||
await showDialog(
|
await showDialog(
|
||||||
context: context,
|
context: context,
|
||||||
|
barrierDismissible: true,
|
||||||
builder: (context) {
|
builder: (context) {
|
||||||
return AlertDialog(
|
return WillPopScope(
|
||||||
content: Container(
|
onWillPop: () async {
|
||||||
|
_isScanning = false;
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
child: AlertDialog(
|
||||||
|
title: const Text('Scanner le QR Code'),
|
||||||
|
content: SizedBox(
|
||||||
|
width: 640,
|
||||||
|
height: 480,
|
||||||
|
child: _cameraController != null &&
|
||||||
|
_cameraController!.value.isInitialized
|
||||||
|
? CameraPreview(_cameraController!)
|
||||||
|
: const Center(child: CircularProgressIndicator()),
|
||||||
|
),
|
||||||
|
actions: [
|
||||||
|
TextButton(
|
||||||
|
onPressed: () {
|
||||||
|
_isScanning = false;
|
||||||
|
Navigator.of(context).pop();
|
||||||
|
},
|
||||||
|
child: const Text('Annuler'),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
_isScanning = false;
|
||||||
|
await scanFuture; // Attendre la fin de la boucle
|
||||||
|
await _cameraController?.dispose();
|
||||||
|
_cameraController = null;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 📱 Mobile et macOS → mobile_scanner
|
||||||
|
await showDialog(
|
||||||
|
context: context,
|
||||||
|
builder: (context) => AlertDialog(
|
||||||
|
title: const Text('Scanner le QR Code'),
|
||||||
|
content: SizedBox(
|
||||||
width: double.maxFinite,
|
width: double.maxFinite,
|
||||||
height: 400,
|
height: 400,
|
||||||
child: MobileScanner(
|
child: MobileScanner(
|
||||||
onDetect: (BarcodeCapture barcodeCap) {
|
onDetect: (capture) {
|
||||||
print("BarcodeCapture: $barcodeCap");
|
final List<Barcode> barcodes = capture.barcodes;
|
||||||
// Now accessing the barcodes attribute
|
|
||||||
final List<Barcode> barcodes = barcodeCap.barcodes;
|
|
||||||
|
|
||||||
if (barcodes.isNotEmpty) {
|
if (barcodes.isNotEmpty) {
|
||||||
// Get the first detected barcode value
|
final scanResult = barcodes.first.rawValue;
|
||||||
String? scanResult = barcodes.first.rawValue;
|
|
||||||
|
|
||||||
print("Scanned Result: $scanResult");
|
|
||||||
|
|
||||||
if (scanResult != null && scanResult.isNotEmpty) {
|
if (scanResult != null && scanResult.isNotEmpty) {
|
||||||
setState(() {
|
|
||||||
_searchController.text = scanResult;
|
|
||||||
print(
|
|
||||||
"Updated Search Controller: ${_searchController.text}");
|
|
||||||
});
|
|
||||||
|
|
||||||
// Close dialog after scanning
|
|
||||||
Navigator.of(context).pop();
|
Navigator.of(context).pop();
|
||||||
|
setState(() => _searchController.text = scanResult);
|
||||||
// Refresh product list based on new search input
|
|
||||||
_filterProducts();
|
_filterProducts();
|
||||||
} else {
|
_showSuccessSnackbar("QR Code détecté : $scanResult");
|
||||||
print("Scan result was empty or null.");
|
|
||||||
Navigator.of(context).pop();
|
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
print("No barcodes detected.");
|
|
||||||
Navigator.of(context).pop();
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
actions: [
|
||||||
},
|
TextButton(
|
||||||
|
onPressed: () => Navigator.of(context).pop(),
|
||||||
|
child: const Text('Annuler'),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// ----------------- FILTRAGE PRODUITS -----------------
|
||||||
void _filterProducts() {
|
void _filterProducts() {
|
||||||
final query = _searchController.text.toLowerCase();
|
final query = _searchController.text.toLowerCase();
|
||||||
setState(() {
|
setState(() {
|
||||||
@ -825,7 +977,3 @@ class _DemandeSortiePersonnellePageState
|
|||||||
super.dispose();
|
super.dispose();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
extension on BarcodeCapture {
|
|
||||||
get rawValue => null;
|
|
||||||
}
|
|
||||||
|
|||||||
@ -5,13 +5,18 @@ import 'dart:async';
|
|||||||
|
|
||||||
class DatabaseConfig {
|
class DatabaseConfig {
|
||||||
// Local MySQL settings
|
// Local MySQL settings
|
||||||
static const String localHost = '192.168.88.73';
|
static const String localHost = '192.168.88.3';
|
||||||
static const String localUsername = 'guycom';
|
static const String localUsername = 'guycom';
|
||||||
static const String? localPassword = '3iV59wjRdbuXAPR';
|
static const String? localPassword = '3iV59wjRdbuXAPR';
|
||||||
static const String localDatabase = 'guycom';
|
static const String localDatabase = 'guycom';
|
||||||
|
// static const String localHost = 'localhost';
|
||||||
|
// static const String localUsername = 'root';
|
||||||
|
// static const String? localPassword = null;
|
||||||
|
// static const String localDatabase = 'guycom';
|
||||||
|
|
||||||
// Production (public) MySQL settings
|
// Production (public) MySQL settings
|
||||||
static const String prodHost = '102.17.52.31';
|
static const String prodHost = '102.16.56.177';
|
||||||
|
// static const String prodHost = '185.70.105.157';
|
||||||
static const String prodUsername = 'guycom';
|
static const String prodUsername = 'guycom';
|
||||||
static const String prodPassword = '3iV59wjRdbuXAPR';
|
static const String prodPassword = '3iV59wjRdbuXAPR';
|
||||||
static const String prodDatabase = 'guycom';
|
static const String prodDatabase = 'guycom';
|
||||||
@ -23,7 +28,7 @@ class DatabaseConfig {
|
|||||||
static const int maxConnections = 10;
|
static const int maxConnections = 10;
|
||||||
static const int minConnections = 2;
|
static const int minConnections = 2;
|
||||||
|
|
||||||
static bool get isDevelopment => false;
|
static bool get isDevelopment => true;
|
||||||
|
|
||||||
/// Build config map for connection
|
/// Build config map for connection
|
||||||
static Map<String, dynamic> _buildConfig({
|
static Map<String, dynamic> _buildConfig({
|
||||||
@ -80,7 +85,7 @@ class DatabaseConfig {
|
|||||||
config['database']?.toString().isNotEmpty == true &&
|
config['database']?.toString().isNotEmpty == true &&
|
||||||
config['user'] != null;
|
config['user'] != null;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
// print("Erreur de validation de la configuration: $e");
|
print("Erreur de validation de la configuration: $e");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -6,23 +6,23 @@
|
|||||||
|
|
||||||
#include "generated_plugin_registrant.h"
|
#include "generated_plugin_registrant.h"
|
||||||
|
|
||||||
#include <charset_converter/charset_converter_plugin.h>
|
|
||||||
#include <file_selector_linux/file_selector_plugin.h>
|
#include <file_selector_linux/file_selector_plugin.h>
|
||||||
#include <open_file_linux/open_file_linux_plugin.h>
|
#include <open_file_linux/open_file_linux_plugin.h>
|
||||||
|
#include <printing/printing_plugin.h>
|
||||||
#include <screen_retriever/screen_retriever_plugin.h>
|
#include <screen_retriever/screen_retriever_plugin.h>
|
||||||
#include <url_launcher_linux/url_launcher_plugin.h>
|
#include <url_launcher_linux/url_launcher_plugin.h>
|
||||||
#include <window_manager/window_manager_plugin.h>
|
#include <window_manager/window_manager_plugin.h>
|
||||||
|
|
||||||
void fl_register_plugins(FlPluginRegistry* registry) {
|
void fl_register_plugins(FlPluginRegistry* registry) {
|
||||||
g_autoptr(FlPluginRegistrar) charset_converter_registrar =
|
|
||||||
fl_plugin_registry_get_registrar_for_plugin(registry, "CharsetConverterPlugin");
|
|
||||||
charset_converter_plugin_register_with_registrar(charset_converter_registrar);
|
|
||||||
g_autoptr(FlPluginRegistrar) file_selector_linux_registrar =
|
g_autoptr(FlPluginRegistrar) file_selector_linux_registrar =
|
||||||
fl_plugin_registry_get_registrar_for_plugin(registry, "FileSelectorPlugin");
|
fl_plugin_registry_get_registrar_for_plugin(registry, "FileSelectorPlugin");
|
||||||
file_selector_plugin_register_with_registrar(file_selector_linux_registrar);
|
file_selector_plugin_register_with_registrar(file_selector_linux_registrar);
|
||||||
g_autoptr(FlPluginRegistrar) open_file_linux_registrar =
|
g_autoptr(FlPluginRegistrar) open_file_linux_registrar =
|
||||||
fl_plugin_registry_get_registrar_for_plugin(registry, "OpenFileLinuxPlugin");
|
fl_plugin_registry_get_registrar_for_plugin(registry, "OpenFileLinuxPlugin");
|
||||||
open_file_linux_plugin_register_with_registrar(open_file_linux_registrar);
|
open_file_linux_plugin_register_with_registrar(open_file_linux_registrar);
|
||||||
|
g_autoptr(FlPluginRegistrar) printing_registrar =
|
||||||
|
fl_plugin_registry_get_registrar_for_plugin(registry, "PrintingPlugin");
|
||||||
|
printing_plugin_register_with_registrar(printing_registrar);
|
||||||
g_autoptr(FlPluginRegistrar) screen_retriever_registrar =
|
g_autoptr(FlPluginRegistrar) screen_retriever_registrar =
|
||||||
fl_plugin_registry_get_registrar_for_plugin(registry, "ScreenRetrieverPlugin");
|
fl_plugin_registry_get_registrar_for_plugin(registry, "ScreenRetrieverPlugin");
|
||||||
screen_retriever_plugin_register_with_registrar(screen_retriever_registrar);
|
screen_retriever_plugin_register_with_registrar(screen_retriever_registrar);
|
||||||
|
|||||||
@ -3,9 +3,9 @@
|
|||||||
#
|
#
|
||||||
|
|
||||||
list(APPEND FLUTTER_PLUGIN_LIST
|
list(APPEND FLUTTER_PLUGIN_LIST
|
||||||
charset_converter
|
|
||||||
file_selector_linux
|
file_selector_linux
|
||||||
open_file_linux
|
open_file_linux
|
||||||
|
printing
|
||||||
screen_retriever
|
screen_retriever
|
||||||
url_launcher_linux
|
url_launcher_linux
|
||||||
window_manager
|
window_manager
|
||||||
|
|||||||
@ -10,6 +10,7 @@ import file_selector_macos
|
|||||||
import mobile_scanner
|
import mobile_scanner
|
||||||
import open_file_mac
|
import open_file_mac
|
||||||
import path_provider_foundation
|
import path_provider_foundation
|
||||||
|
import printing
|
||||||
import screen_retriever
|
import screen_retriever
|
||||||
import shared_preferences_foundation
|
import shared_preferences_foundation
|
||||||
import url_launcher_macos
|
import url_launcher_macos
|
||||||
@ -21,6 +22,7 @@ func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
|
|||||||
MobileScannerPlugin.register(with: registry.registrar(forPlugin: "MobileScannerPlugin"))
|
MobileScannerPlugin.register(with: registry.registrar(forPlugin: "MobileScannerPlugin"))
|
||||||
OpenFilePlugin.register(with: registry.registrar(forPlugin: "OpenFilePlugin"))
|
OpenFilePlugin.register(with: registry.registrar(forPlugin: "OpenFilePlugin"))
|
||||||
PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin"))
|
PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin"))
|
||||||
|
PrintingPlugin.register(with: registry.registrar(forPlugin: "PrintingPlugin"))
|
||||||
ScreenRetrieverPlugin.register(with: registry.registrar(forPlugin: "ScreenRetrieverPlugin"))
|
ScreenRetrieverPlugin.register(with: registry.registrar(forPlugin: "ScreenRetrieverPlugin"))
|
||||||
SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin"))
|
SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin"))
|
||||||
UrlLauncherPlugin.register(with: registry.registrar(forPlugin: "UrlLauncherPlugin"))
|
UrlLauncherPlugin.register(with: registry.registrar(forPlugin: "UrlLauncherPlugin"))
|
||||||
|
|||||||
132
pubspec.lock
132
pubspec.lock
@ -49,6 +49,14 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.2.9"
|
version: "2.2.9"
|
||||||
|
bidi:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: bidi
|
||||||
|
sha256: "77f475165e94b261745cf1032c751e2032b8ed92ccb2bf5716036db79320637d"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.0.13"
|
||||||
boolean_selector:
|
boolean_selector:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -97,6 +105,14 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.3.5"
|
version: "0.3.5"
|
||||||
|
camera_windows:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
name: camera_windows
|
||||||
|
sha256: c4339d71bc4256993f5c8ae2f3355463d830a5cb52851409ab1c627401c69811
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "0.2.6+2"
|
||||||
characters:
|
characters:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -105,22 +121,22 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.4.0"
|
version: "1.4.0"
|
||||||
charset_converter:
|
charcode:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: charset_converter
|
name: charcode
|
||||||
sha256: a601f27b78ca86c3d88899d53059786d9c3f3c485b64974e9105c06c2569aef5
|
sha256: fb0f1107cac15a5ea6ef0a6ef71a807b9e4267c713bb93e00e92d737cc8dbd8a
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.3.0"
|
version: "1.4.0"
|
||||||
cli_util:
|
cli_util:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: cli_util
|
name: cli_util
|
||||||
sha256: "66f86e916d285c1a93d3b79587d94bd71984a66aac4ff74e524cfa7877f1395c"
|
sha256: ff6785f7e9e3c38ac98b2fb035701789de90154024a75b6cb926445e83197d1c
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.3.5"
|
version: "0.4.2"
|
||||||
clock:
|
clock:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -161,14 +177,6 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "3.0.6"
|
version: "3.0.6"
|
||||||
csslib:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: csslib
|
|
||||||
sha256: "09bad715f418841f976c77db72d5398dc1253c21fb9c0c7f0b0b985860b2d58e"
|
|
||||||
url: "https://pub.dev"
|
|
||||||
source: hosted
|
|
||||||
version: "1.0.2"
|
|
||||||
cupertino_icons:
|
cupertino_icons:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
@ -193,22 +201,6 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.0.7"
|
version: "2.0.7"
|
||||||
esc_pos_printer:
|
|
||||||
dependency: "direct main"
|
|
||||||
description:
|
|
||||||
name: esc_pos_printer
|
|
||||||
sha256: "312b05f909f3f7dd1e6a3332cf384dcee2c3a635138823654cd9c0133d8b5c45"
|
|
||||||
url: "https://pub.dev"
|
|
||||||
source: hosted
|
|
||||||
version: "4.1.0"
|
|
||||||
esc_pos_utils:
|
|
||||||
dependency: "direct main"
|
|
||||||
description:
|
|
||||||
name: esc_pos_utils
|
|
||||||
sha256: "8ec0013d7a7f1e790ced6b09b95ce3bf2c6f9468a3e2bc49ece000761d86c6f8"
|
|
||||||
url: "https://pub.dev"
|
|
||||||
source: hosted
|
|
||||||
version: "1.1.0"
|
|
||||||
excel:
|
excel:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
@ -281,6 +273,14 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.9.3+4"
|
version: "0.9.3+4"
|
||||||
|
fixnum:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: fixnum
|
||||||
|
sha256: b6dc7065e46c974bc7c5f143080a6764ec7a4be6da1285ececdc37be96de53be
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.1.1"
|
||||||
fl_chart:
|
fl_chart:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
@ -376,14 +376,6 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "10.8.0"
|
version: "10.8.0"
|
||||||
gbk_codec:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: gbk_codec
|
|
||||||
sha256: "3af5311fc9393115e3650ae6023862adf998051a804a08fb804f042724999f61"
|
|
||||||
url: "https://pub.dev"
|
|
||||||
source: hosted
|
|
||||||
version: "0.4.0"
|
|
||||||
get:
|
get:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
@ -396,10 +388,10 @@ packages:
|
|||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: get_it
|
name: get_it
|
||||||
sha256: d85128a5dae4ea777324730dc65edd9c9f43155c109d5cc0a69cab74139fbac1
|
sha256: a4292e7cf67193f8e7c1258203104eb2a51ec8b3a04baa14695f4064c144297b
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "7.7.0"
|
version: "8.2.0"
|
||||||
google_fonts:
|
google_fonts:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -416,22 +408,6 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "5.1.2"
|
version: "5.1.2"
|
||||||
hex:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: hex
|
|
||||||
sha256: "4e7cd54e4b59ba026432a6be2dd9d96e4c5205725194997193bf871703b82c4a"
|
|
||||||
url: "https://pub.dev"
|
|
||||||
source: hosted
|
|
||||||
version: "0.2.0"
|
|
||||||
html:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: html
|
|
||||||
sha256: "9475be233c437f0e3637af55e7702cbbe5c23a68bd56e8a5fa2d426297b7c6c8"
|
|
||||||
url: "https://pub.dev"
|
|
||||||
source: hosted
|
|
||||||
version: "0.15.5+1"
|
|
||||||
http:
|
http:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -460,10 +436,10 @@ packages:
|
|||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: image
|
name: image
|
||||||
sha256: "8e9d133755c3e84c73288363e6343157c383a0c6c56fc51afcc5d4d7180306d6"
|
sha256: f31d52537dc417fdcde36088fdf11d191026fd5e4fae742491ebd40e5a8bea7d
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "3.3.0"
|
version: "4.3.0"
|
||||||
image_picker:
|
image_picker:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
@ -660,18 +636,18 @@ packages:
|
|||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: mobile_scanner
|
name: mobile_scanner
|
||||||
sha256: d234581c090526676fd8fab4ada92f35c6746e3fb4f05a399665d75a399fb760
|
sha256: "54005bdea7052d792d35b4fef0f84ec5ddc3a844b250ecd48dc192fb9b4ebc95"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "5.2.3"
|
version: "7.0.1"
|
||||||
msix:
|
msix:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: msix
|
name: msix
|
||||||
sha256: e3de4d9f52543ad6e4b0f534991e1303cbd379d24be28dd241ac60bd9439a201
|
sha256: f88033fcb9e0dd8de5b18897cbebbd28ea30596810f4a7c86b12b0c03ace87e5
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "3.7.0"
|
version: "3.16.12"
|
||||||
mysql1:
|
mysql1:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
@ -844,10 +820,18 @@ packages:
|
|||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: pdf
|
name: pdf
|
||||||
sha256: "10659b915e65832b106f6d1d213e09b789cc1f24bf282ee911e49db35b96be4d"
|
sha256: "28eacad99bffcce2e05bba24e50153890ad0255294f4dd78a17075a2ba5c8416"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "3.8.4"
|
version: "3.11.3"
|
||||||
|
pdf_widget_wrapper:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: pdf_widget_wrapper
|
||||||
|
sha256: c930860d987213a3d58c7ec3b7ecf8085c3897f773e8dc23da9cae60a5d6d0f5
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.0.4"
|
||||||
petitparser:
|
petitparser:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -888,6 +872,14 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.5.1"
|
version: "1.5.1"
|
||||||
|
printing:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
name: printing
|
||||||
|
sha256: "482cd5a5196008f984bb43ed0e47cbfdca7373490b62f3b27b3299275bf22a93"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "5.14.2"
|
||||||
provider:
|
provider:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -1254,7 +1246,7 @@ packages:
|
|||||||
source: hosted
|
source: hosted
|
||||||
version: "1.1.1"
|
version: "1.1.1"
|
||||||
win32:
|
win32:
|
||||||
dependency: transitive
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: win32
|
name: win32
|
||||||
sha256: dc6ecaa00a7c708e5b4d10ee7bec8c270e9276dfcab1783f57e9962d7884305f
|
sha256: dc6ecaa00a7c708e5b4d10ee7bec8c270e9276dfcab1783f57e9962d7884305f
|
||||||
@ -1293,6 +1285,14 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "3.1.3"
|
version: "3.1.3"
|
||||||
|
zxing2:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
name: zxing2
|
||||||
|
sha256: "2677c49a3b9ca9457cb1d294fd4bd5041cac6aab8cdb07b216ba4e98945c684f"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "0.2.4"
|
||||||
sdks:
|
sdks:
|
||||||
dart: ">=3.7.0 <4.0.0"
|
dart: ">=3.7.0 <4.0.0"
|
||||||
flutter: ">=3.27.0"
|
flutter: ">=3.29.0"
|
||||||
|
|||||||
13
pubspec.yaml
13
pubspec.yaml
@ -44,10 +44,12 @@ dependencies:
|
|||||||
sqflite_common_ffi: ^2.2.5
|
sqflite_common_ffi: ^2.2.5
|
||||||
quantity_input: ^1.0.2
|
quantity_input: ^1.0.2
|
||||||
grouped_list: ^5.1.2
|
grouped_list: ^5.1.2
|
||||||
esc_pos_printer: ^4.0.1
|
# esc_pos_printer: ^4.0.1
|
||||||
esc_pos_utils: ^1.1.0
|
win32: ^5.12.0
|
||||||
|
# esc_pos_utils: ^1.1.0
|
||||||
|
printing: ^5.10.0
|
||||||
flutter_login: ^4.1.1
|
flutter_login: ^4.1.1
|
||||||
image: ^3.0.2
|
image: ^4.3.0
|
||||||
logging: ^1.2.0
|
logging: ^1.2.0
|
||||||
msix: ^3.7.0
|
msix: ^3.7.0
|
||||||
flutter_charts: ^0.5.1
|
flutter_charts: ^0.5.1
|
||||||
@ -63,13 +65,14 @@ dependencies:
|
|||||||
path_provider: ^2.0.15
|
path_provider: ^2.0.15
|
||||||
shared_preferences: ^2.2.2
|
shared_preferences: ^2.2.2
|
||||||
excel: ^2.0.1
|
excel: ^2.0.1
|
||||||
mobile_scanner: ^5.0.0
|
mobile_scanner: ^7.0.1
|
||||||
fl_chart: ^0.65.0
|
fl_chart: ^0.65.0
|
||||||
numbers_to_letters: ^1.0.0
|
numbers_to_letters: ^1.0.0
|
||||||
qr_code_scanner_plus: ^2.0.10+1
|
qr_code_scanner_plus: ^2.0.10+1
|
||||||
window_manager: ^0.3.7
|
window_manager: ^0.3.7
|
||||||
camera: ^0.10.5+9
|
camera: ^0.10.5+9
|
||||||
|
zxing2: ^0.2.1
|
||||||
|
camera_windows: ^0.2.6+2
|
||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
flutter_test:
|
flutter_test:
|
||||||
sdk: flutter
|
sdk: flutter
|
||||||
|
|||||||
@ -6,17 +6,20 @@
|
|||||||
|
|
||||||
#include "generated_plugin_registrant.h"
|
#include "generated_plugin_registrant.h"
|
||||||
|
|
||||||
#include <charset_converter/charset_converter_plugin.h>
|
#include <camera_windows/camera_windows.h>
|
||||||
#include <file_selector_windows/file_selector_windows.h>
|
#include <file_selector_windows/file_selector_windows.h>
|
||||||
|
#include <printing/printing_plugin.h>
|
||||||
#include <screen_retriever/screen_retriever_plugin.h>
|
#include <screen_retriever/screen_retriever_plugin.h>
|
||||||
#include <url_launcher_windows/url_launcher_windows.h>
|
#include <url_launcher_windows/url_launcher_windows.h>
|
||||||
#include <window_manager/window_manager_plugin.h>
|
#include <window_manager/window_manager_plugin.h>
|
||||||
|
|
||||||
void RegisterPlugins(flutter::PluginRegistry* registry) {
|
void RegisterPlugins(flutter::PluginRegistry* registry) {
|
||||||
CharsetConverterPluginRegisterWithRegistrar(
|
CameraWindowsRegisterWithRegistrar(
|
||||||
registry->GetRegistrarForPlugin("CharsetConverterPlugin"));
|
registry->GetRegistrarForPlugin("CameraWindows"));
|
||||||
FileSelectorWindowsRegisterWithRegistrar(
|
FileSelectorWindowsRegisterWithRegistrar(
|
||||||
registry->GetRegistrarForPlugin("FileSelectorWindows"));
|
registry->GetRegistrarForPlugin("FileSelectorWindows"));
|
||||||
|
PrintingPluginRegisterWithRegistrar(
|
||||||
|
registry->GetRegistrarForPlugin("PrintingPlugin"));
|
||||||
ScreenRetrieverPluginRegisterWithRegistrar(
|
ScreenRetrieverPluginRegisterWithRegistrar(
|
||||||
registry->GetRegistrarForPlugin("ScreenRetrieverPlugin"));
|
registry->GetRegistrarForPlugin("ScreenRetrieverPlugin"));
|
||||||
UrlLauncherWindowsRegisterWithRegistrar(
|
UrlLauncherWindowsRegisterWithRegistrar(
|
||||||
|
|||||||
@ -3,8 +3,9 @@
|
|||||||
#
|
#
|
||||||
|
|
||||||
list(APPEND FLUTTER_PLUGIN_LIST
|
list(APPEND FLUTTER_PLUGIN_LIST
|
||||||
charset_converter
|
camera_windows
|
||||||
file_selector_windows
|
file_selector_windows
|
||||||
|
printing
|
||||||
screen_retriever
|
screen_retriever
|
||||||
url_launcher_windows
|
url_launcher_windows
|
||||||
window_manager
|
window_manager
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user