push finale
This commit is contained in:
parent
4b4d9637fd
commit
c757bdb701
@ -3,6 +3,7 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:youmazgestion/Models/client.dart';
|
import 'package:youmazgestion/Models/client.dart';
|
||||||
import 'package:youmazgestion/Services/stock_managementDatabase.dart';
|
import 'package:youmazgestion/Services/stock_managementDatabase.dart';
|
||||||
|
import 'package:intl/intl.dart';
|
||||||
|
|
||||||
class CommandeDetails extends StatelessWidget {
|
class CommandeDetails extends StatelessWidget {
|
||||||
final Commande commande;
|
final Commande commande;
|
||||||
@ -54,7 +55,7 @@ class CommandeDetails extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
const SizedBox(height: 2),
|
const SizedBox(height: 2),
|
||||||
Text(
|
Text(
|
||||||
'${(detail.prixFinal / detail.quantite).toStringAsFixed(2)} MGA',
|
'${NumberFormat('#,##0', 'fr_FR').format(detail.prixFinal / detail.quantite)} MGA',
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
fontSize: 13,
|
fontSize: 13,
|
||||||
fontWeight: FontWeight.bold,
|
fontWeight: FontWeight.bold,
|
||||||
@ -65,7 +66,7 @@ class CommandeDetails extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
return _buildTableCell('${detail.prixUnitaire.toStringAsFixed(2)} MGA', isAmount: true);
|
return _buildTableCell('${NumberFormat('#,##0', 'fr_FR').format(detail.prixUnitaire)} MGA', isAmount: true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -85,7 +86,7 @@ class CommandeDetails extends StatelessWidget {
|
|||||||
textAlign: TextAlign.center,
|
textAlign: TextAlign.center,
|
||||||
),
|
),
|
||||||
Text(
|
Text(
|
||||||
'-${detail.montantRemise.toStringAsFixed(0)} MGA',
|
'-${NumberFormat('#,##0', 'fr_FR').format(detail.montantRemise)} MGA',
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
fontSize: 10,
|
fontSize: 10,
|
||||||
color: Colors.teal.shade700,
|
color: Colors.teal.shade700,
|
||||||
@ -119,7 +120,7 @@ class CommandeDetails extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
const SizedBox(height: 2),
|
const SizedBox(height: 2),
|
||||||
Text(
|
Text(
|
||||||
'${detail.prixFinal.toStringAsFixed(2)} MGA',
|
'${NumberFormat('#,##0', 'fr_FR').format(detail.prixFinal)} MGA',
|
||||||
style: const TextStyle(
|
style: const TextStyle(
|
||||||
fontSize: 13,
|
fontSize: 13,
|
||||||
fontWeight: FontWeight.bold,
|
fontWeight: FontWeight.bold,
|
||||||
@ -129,7 +130,7 @@ class CommandeDetails extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
return _buildTableCell('${detail.prixFinal.toStringAsFixed(2)} MGA', isAmount: true);
|
return _buildTableCell('${NumberFormat('#,##0', 'fr_FR').format(detail.prixFinal)} MGA', isAmount: true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -197,7 +198,7 @@ class CommandeDetails extends StatelessWidget {
|
|||||||
borderRadius: BorderRadius.circular(12),
|
borderRadius: BorderRadius.circular(12),
|
||||||
),
|
),
|
||||||
child: Text(
|
child: Text(
|
||||||
'Économies: ${totalRemises.toStringAsFixed(0)} MGA',
|
'Économies: ${NumberFormat('#,##0', 'fr_FR').format(totalRemises)} MGA',
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
fontSize: 12,
|
fontSize: 12,
|
||||||
fontWeight: FontWeight.bold,
|
fontWeight: FontWeight.bold,
|
||||||
@ -312,7 +313,7 @@ class CommandeDetails extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
Text(
|
Text(
|
||||||
'${sousTotal.toStringAsFixed(2)} MGA',
|
'${NumberFormat('#,##0', 'fr_FR').format(sousTotal)} MGA',
|
||||||
style: const TextStyle(
|
style: const TextStyle(
|
||||||
fontSize: 14,
|
fontSize: 14,
|
||||||
fontWeight: FontWeight.w500,
|
fontWeight: FontWeight.w500,
|
||||||
@ -343,7 +344,7 @@ class CommandeDetails extends StatelessWidget {
|
|||||||
],
|
],
|
||||||
),
|
),
|
||||||
Text(
|
Text(
|
||||||
'-${totalRemises.toStringAsFixed(2)} MGA',
|
'-${NumberFormat('#,##0', 'fr_FR').format(totalRemises)} MGA',
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
fontSize: 14,
|
fontSize: 14,
|
||||||
fontWeight: FontWeight.bold,
|
fontWeight: FontWeight.bold,
|
||||||
@ -367,7 +368,7 @@ class CommandeDetails extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
Text(
|
Text(
|
||||||
'${commande.montantTotal.toStringAsFixed(2)} MGA',
|
'${NumberFormat('#,##0', 'fr_FR').format(commande.montantTotal)} MGA',
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
fontWeight: FontWeight.bold,
|
fontWeight: FontWeight.bold,
|
||||||
fontSize: 18,
|
fontSize: 18,
|
||||||
|
|||||||
@ -7,13 +7,13 @@ import 'package:youmazgestion/Models/client.dart';
|
|||||||
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) onPaymentSelected;
|
final Function(Commande) onGenerateBonLivraison;
|
||||||
|
|
||||||
|
|
||||||
const CommandeActions({
|
const CommandeActions({
|
||||||
required this.commande,
|
required this.commande,
|
||||||
required this.onStatutChanged,
|
required this.onStatutChanged,
|
||||||
required this.onPaymentSelected,
|
required this.onGenerateBonLivraison,
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -30,7 +30,7 @@ class CommandeActions extends StatelessWidget {
|
|||||||
label: 'Confirmer',
|
label: 'Confirmer',
|
||||||
icon: Icons.check_circle,
|
icon: Icons.check_circle,
|
||||||
color: Colors.blue,
|
color: Colors.blue,
|
||||||
onPressed: () => onPaymentSelected(commande),
|
onPressed: () => onGenerateBonLivraison(commande),
|
||||||
),
|
),
|
||||||
_buildActionButton(
|
_buildActionButton(
|
||||||
label: 'Annuler',
|
label: 'Annuler',
|
||||||
|
|||||||
@ -1,4 +1,5 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:intl/intl.dart';
|
||||||
import 'package:youmazgestion/Models/client.dart';
|
import 'package:youmazgestion/Models/client.dart';
|
||||||
|
|
||||||
|
|
||||||
@ -48,7 +49,7 @@ class _DiscountDialogState extends State<DiscountDialog> {
|
|||||||
child: Column(
|
child: Column(
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
children: [
|
children: [
|
||||||
Text('Montant original: ${widget.commande.montantTotal.toStringAsFixed(2)} MGA'),
|
Text('Montant original: ${NumberFormat('#,##0', 'fr_FR').format(widget.commande.montantTotal)} MGA'),
|
||||||
const SizedBox(height: 16),
|
const SizedBox(height: 16),
|
||||||
|
|
||||||
// Choix du type de remise
|
// Choix du type de remise
|
||||||
@ -124,7 +125,7 @@ class _DiscountDialogState extends State<DiscountDialog> {
|
|||||||
children: [
|
children: [
|
||||||
const Text('Montant final:'),
|
const Text('Montant final:'),
|
||||||
Text(
|
Text(
|
||||||
'${_montantFinal.toStringAsFixed(2)} MGA',
|
'${NumberFormat('#,##0', 'fr_FR').format(_montantFinal)} MGA',
|
||||||
style: const TextStyle(
|
style: const TextStyle(
|
||||||
fontWeight: FontWeight.bold,
|
fontWeight: FontWeight.bold,
|
||||||
fontSize: 16,
|
fontSize: 16,
|
||||||
@ -138,7 +139,7 @@ class _DiscountDialogState extends State<DiscountDialog> {
|
|||||||
children: [
|
children: [
|
||||||
const Text('Économie:'),
|
const Text('Économie:'),
|
||||||
Text(
|
Text(
|
||||||
'${(widget.commande.montantTotal - _montantFinal).toStringAsFixed(2)} MGA',
|
'${NumberFormat('#,##0', 'fr_FR').format(widget.commande.montantTotal - _montantFinal)} MGA',
|
||||||
style: const TextStyle(
|
style: const TextStyle(
|
||||||
color: Colors.green,
|
color: Colors.green,
|
||||||
fontWeight: FontWeight.bold,
|
fontWeight: FontWeight.bold,
|
||||||
|
|||||||
@ -2,6 +2,7 @@ import 'package:flutter/material.dart';
|
|||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
import 'package:get/get_core/src/get_main.dart';
|
import 'package:get/get_core/src/get_main.dart';
|
||||||
import 'package:get/get_navigation/src/snackbar/snackbar.dart';
|
import 'package:get/get_navigation/src/snackbar/snackbar.dart';
|
||||||
|
import 'package:intl/intl.dart';
|
||||||
import 'package:youmazgestion/Components/commandManagementComponents/PaymentMethod.dart';
|
import 'package:youmazgestion/Components/commandManagementComponents/PaymentMethod.dart';
|
||||||
import 'package:youmazgestion/Components/paymentType.dart';
|
import 'package:youmazgestion/Components/paymentType.dart';
|
||||||
import 'package:youmazgestion/Models/client.dart';
|
import 'package:youmazgestion/Models/client.dart';
|
||||||
@ -81,7 +82,7 @@ class _PaymentMethodDialogState extends State<PaymentMethodDialog> {
|
|||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
children: [
|
children: [
|
||||||
const Text('Montant à payer:', style: TextStyle(fontWeight: FontWeight.bold)),
|
const Text('Montant à payer:', style: TextStyle(fontWeight: FontWeight.bold)),
|
||||||
Text('${montantFinal.toStringAsFixed(2)} MGA',
|
Text('${NumberFormat('#,##0', 'fr_FR').format(montantFinal)} MGA',
|
||||||
style: const TextStyle(fontWeight: FontWeight.bold, fontSize: 16)),
|
style: const TextStyle(fontWeight: FontWeight.bold, fontSize: 16)),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
@ -162,7 +163,7 @@ class _PaymentMethodDialogState extends State<PaymentMethodDialog> {
|
|||||||
),
|
),
|
||||||
const SizedBox(height: 8),
|
const SizedBox(height: 8),
|
||||||
Text(
|
Text(
|
||||||
'Monnaie à rendre: ${change.toStringAsFixed(2)} MGA',
|
'Monnaie à rendre: ${NumberFormat('#,##0', 'fr_FR').format(change)} MGA',
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
fontSize: 16,
|
fontSize: 16,
|
||||||
fontWeight: FontWeight.bold,
|
fontWeight: FontWeight.bold,
|
||||||
|
|||||||
@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
|
import 'package:intl/intl.dart';
|
||||||
import 'package:youmazgestion/Models/client.dart';
|
import 'package:youmazgestion/Models/client.dart';
|
||||||
import 'package:youmazgestion/Models/produit.dart';
|
import 'package:youmazgestion/Models/produit.dart';
|
||||||
import 'package:youmazgestion/Services/stock_managementDatabase.dart';
|
import 'package:youmazgestion/Services/stock_managementDatabase.dart';
|
||||||
@ -149,7 +150,7 @@ class _CadeauDialogState extends State<CadeauDialog> {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
Text(
|
Text(
|
||||||
'Prix: ${widget.product.price.toStringAsFixed(2)} MGA',
|
'Prix: ${NumberFormat('#,##0', 'fr_FR').format(widget.product.price)} MGA',
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
fontSize: 12,
|
fontSize: 12,
|
||||||
color: Colors.grey.shade600,
|
color: Colors.grey.shade600,
|
||||||
@ -255,7 +256,7 @@ class _CadeauDialogState extends State<CadeauDialog> {
|
|||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
Text(
|
Text(
|
||||||
'Prix normal: ${produit.price.toStringAsFixed(2)} MGA',
|
'Prix normal: ${NumberFormat('#,##0', 'fr_FR').format(produit.price)} MGA',
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
fontSize: 12,
|
fontSize: 12,
|
||||||
color: Colors.grey.shade600,
|
color: Colors.grey.shade600,
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
// Components/RemiseDialog.dart
|
// Components/RemiseDialog.dart
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter/services.dart';
|
import 'package:flutter/services.dart';
|
||||||
|
import 'package:intl/intl.dart';
|
||||||
import 'package:youmazgestion/Models/client.dart';
|
import 'package:youmazgestion/Models/client.dart';
|
||||||
import 'package:youmazgestion/Models/produit.dart';
|
import 'package:youmazgestion/Models/produit.dart';
|
||||||
|
|
||||||
@ -115,11 +116,11 @@ class _RemiseDialogState extends State<RemiseDialog> {
|
|||||||
style: const TextStyle(fontSize: 12),
|
style: const TextStyle(fontSize: 12),
|
||||||
),
|
),
|
||||||
Text(
|
Text(
|
||||||
'Prix unitaire: ${widget.prixUnitaire.toStringAsFixed(2)} MGA',
|
'Prix unitaire: ${NumberFormat('#,##0', 'fr_FR').format(widget.prixUnitaire)} MGA',
|
||||||
style: const TextStyle(fontSize: 12),
|
style: const TextStyle(fontSize: 12),
|
||||||
),
|
),
|
||||||
Text(
|
Text(
|
||||||
'Sous-total: ${_sousTotal.toStringAsFixed(2)} MGA',
|
'Sous-total: ${NumberFormat('#,##0', 'fr_FR').format(_sousTotal)} MGA',
|
||||||
style: const TextStyle(
|
style: const TextStyle(
|
||||||
fontSize: 12,
|
fontSize: 12,
|
||||||
fontWeight: FontWeight.bold,
|
fontWeight: FontWeight.bold,
|
||||||
@ -234,7 +235,7 @@ class _RemiseDialogState extends State<RemiseDialog> {
|
|||||||
children: [
|
children: [
|
||||||
const Text('Sous-total:', style: TextStyle(fontSize: 12)),
|
const Text('Sous-total:', style: TextStyle(fontSize: 12)),
|
||||||
Text(
|
Text(
|
||||||
'${_sousTotal.toStringAsFixed(2)} MGA',
|
'${NumberFormat('#,##0', 'fr_FR').format(_sousTotal)} MGA',
|
||||||
style: const TextStyle(fontSize: 12),
|
style: const TextStyle(fontSize: 12),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
@ -252,7 +253,7 @@ class _RemiseDialogState extends State<RemiseDialog> {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
Text(
|
Text(
|
||||||
'-${_montantRemise.toStringAsFixed(2)} MGA',
|
'-${NumberFormat('#,##0', 'fr_FR').format(_montantRemise)} MGA',
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
fontSize: 12,
|
fontSize: 12,
|
||||||
color: Colors.orange.shade700,
|
color: Colors.orange.shade700,
|
||||||
@ -274,7 +275,7 @@ class _RemiseDialogState extends State<RemiseDialog> {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
Text(
|
Text(
|
||||||
'${_prixFinal.toStringAsFixed(2)} MGA',
|
'${NumberFormat('#,##0', 'fr_FR').format(_prixFinal)} MGA',
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
fontSize: 14,
|
fontSize: 14,
|
||||||
fontWeight: FontWeight.bold,
|
fontWeight: FontWeight.bold,
|
||||||
|
|||||||
@ -1,5 +1,7 @@
|
|||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
|
import 'dart:convert';
|
||||||
import 'dart:math' as console;
|
import 'dart:math' as console;
|
||||||
|
import 'dart:typed_data';
|
||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
import 'package:mysql1/mysql1.dart';
|
import 'package:mysql1/mysql1.dart';
|
||||||
import 'package:youmazgestion/controller/userController.dart';
|
import 'package:youmazgestion/controller/userController.dart';
|
||||||
@ -596,6 +598,7 @@ String _formatDate(DateTime date) {
|
|||||||
await insertDefaultPointsDeVente();
|
await insertDefaultPointsDeVente();
|
||||||
final newResult =
|
final newResult =
|
||||||
await db.query('SELECT * FROM points_de_vente ORDER BY nom ASC');
|
await db.query('SELECT * FROM points_de_vente ORDER BY nom ASC');
|
||||||
|
print(newResult);
|
||||||
return newResult.map((row) => row.fields).toList();
|
return newResult.map((row) => row.fields).toList();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1023,7 +1026,7 @@ Future<List<Map<String, dynamic>>> getCommandesParPointDeVente(
|
|||||||
|
|
||||||
Future<List<Map<String, dynamic>>> getPointsDeVentes() async {
|
Future<List<Map<String, dynamic>>> getPointsDeVentes() async {
|
||||||
final db = await database;
|
final db = await database;
|
||||||
final result = await db.query('SELECT DISTINCT id, nom FROM pointsdevente ORDER BY nom ASC');
|
final result = await db.query('SELECT DISTINCT * FROM pointsdevente ORDER BY nom ASC');
|
||||||
return result.map((row) => row.fields).toList();
|
return result.map((row) => row.fields).toList();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1238,6 +1241,7 @@ Future<List<Map<String, dynamic>>> getCommandesParPointDeVente(
|
|||||||
return result.affectedRows!;
|
return result.affectedRows!;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Future<int> deletePointDeVente(int id) async {
|
// Future<int> deletePointDeVente(int id) async {
|
||||||
// final db = await database;
|
// final db = await database;
|
||||||
// final result = await db.query('DELETE FROM points_de_vente WHERE id = ?', [id]);
|
// final result = await db.query('DELETE FROM points_de_vente WHERE id = ?', [id]);
|
||||||
@ -1425,12 +1429,53 @@ Future<List<Map<String, dynamic>>> getCommandesParPointDeVente(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<Map<String, dynamic>?> getPointDeVenteById(int id) async {
|
Future<ResultRow?> getPointDeVenteById(int id) async {
|
||||||
final db = await database;
|
final db = await database;
|
||||||
final result =
|
final result =
|
||||||
await db.query('SELECT * FROM points_de_vente WHERE id = ?', [id]);
|
await db.query('SELECT * FROM points_de_vente WHERE id = ?', [id]);
|
||||||
return result.isNotEmpty ? result.first.fields : null;
|
return result.isNotEmpty ? result.first : null;
|
||||||
|
}
|
||||||
|
List<String> parseHeaderInfo(dynamic blobData) {
|
||||||
|
if (blobData == null) return [];
|
||||||
|
|
||||||
|
try {
|
||||||
|
String content = '';
|
||||||
|
|
||||||
|
print("=== TYPE DE DONNÉES BLOB === ${blobData.runtimeType}");
|
||||||
|
|
||||||
|
if (blobData is String) {
|
||||||
|
content = blobData;
|
||||||
|
} else if (blobData is Uint8List || blobData is List<int>) {
|
||||||
|
try {
|
||||||
|
content = utf8.decode(blobData);
|
||||||
|
} catch (eUtf8) {
|
||||||
|
print('❌ utf8.decode failed: $eUtf8');
|
||||||
|
try {
|
||||||
|
content = latin1.decode(blobData);
|
||||||
|
} catch (eLatin1) {
|
||||||
|
print('❌ latin1.decode failed: $eLatin1');
|
||||||
|
content = String.fromCharCodes(blobData);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
content = blobData.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
print('=== LIVRAISON BRUTE ===\n$content\n=== FIN ===');
|
||||||
|
|
||||||
|
return content
|
||||||
|
.split('\n')
|
||||||
|
.map((line) => line.trim())
|
||||||
|
.where((line) => line.isNotEmpty)
|
||||||
|
.toList();
|
||||||
|
} catch (e) {
|
||||||
|
print('❌ Erreur lors du parsing des données d\'en-tête: $e');
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Future<int?> getOrCreatePointDeVenteByNom(String nom) async {
|
Future<int?> getOrCreatePointDeVenteByNom(String nom) async {
|
||||||
final db = await database;
|
final db = await database;
|
||||||
@ -3378,6 +3423,47 @@ Future<List<Map<String, dynamic>>> getHistoriqueSortiesPersonnelles({
|
|||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Future<void> updatePointDeVentes(
|
||||||
|
int id,
|
||||||
|
String nom,
|
||||||
|
String code, {
|
||||||
|
String? content,
|
||||||
|
String? livraison,
|
||||||
|
String? facture,
|
||||||
|
Uint8List? imagePath,
|
||||||
|
}) async {
|
||||||
|
final db = await database;
|
||||||
|
|
||||||
|
try {
|
||||||
|
await db.query(
|
||||||
|
'''
|
||||||
|
UPDATE points_de_vente
|
||||||
|
SET nom = ?,
|
||||||
|
content = ?,
|
||||||
|
livraison = ?,
|
||||||
|
facture = ?,
|
||||||
|
logo = ?
|
||||||
|
WHERE id = ?
|
||||||
|
''',
|
||||||
|
[
|
||||||
|
nom,
|
||||||
|
(content?.isEmpty ?? true) ? null : content,
|
||||||
|
(livraison?.isEmpty ?? true) ? null : livraison,
|
||||||
|
(facture?.isEmpty ?? true) ? null : facture,
|
||||||
|
imagePath,
|
||||||
|
id,
|
||||||
|
],
|
||||||
|
);
|
||||||
|
} catch (e, stacktrace) {
|
||||||
|
print('Erreur lors de la mise à jour du point de vente : $e');
|
||||||
|
print('Stacktrace : $stacktrace');
|
||||||
|
rethrow; // si tu veux faire remonter l’erreur plus haut
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Future<Map<String, dynamic>> getStatistiquesSortiesPersonnelles() async {
|
Future<Map<String, dynamic>> getStatistiquesSortiesPersonnelles() async {
|
||||||
final db = await database;
|
final db = await database;
|
||||||
|
|||||||
@ -1422,12 +1422,12 @@ Widget _buildTableauVentesPointDeVente(List<Map<String, dynamic>> ventesData) {
|
|||||||
),
|
),
|
||||||
Expanded(
|
Expanded(
|
||||||
flex: 2,
|
flex: 2,
|
||||||
child: Text(
|
child:Text(
|
||||||
NumberFormat('#,##0.00', 'fr_FR').format(
|
NumberFormat('#,##0.00', 'fr_FR').format(
|
||||||
(data['chiffre_affaires'] as num?)?.toDouble() ?? 0.0,
|
(data['chiffre_affaires'] as num?)?.toDouble() ?? 0.0,
|
||||||
),
|
),
|
||||||
style: TextStyle(fontSize: 12, fontWeight: FontWeight.w500),
|
style: const TextStyle(fontSize: 12, fontWeight: FontWeight.w500),
|
||||||
),
|
)
|
||||||
),
|
),
|
||||||
Expanded(
|
Expanded(
|
||||||
flex: 1,
|
flex: 1,
|
||||||
|
|||||||
@ -4800,6 +4800,7 @@ Future<void> _showDemandeTransfertDialog(Product product) async {
|
|||||||
foregroundColor: Colors.white,
|
foregroundColor: Colors.white,
|
||||||
),
|
),
|
||||||
const SizedBox(height: 8),
|
const SizedBox(height: 8),
|
||||||
|
if (_userController.username == 'superadmin'|| _userController.username == 'admin') ...[
|
||||||
FloatingActionButton(
|
FloatingActionButton(
|
||||||
heroTag: 'importBtn',
|
heroTag: 'importBtn',
|
||||||
onPressed: _isImporting ? null : _importFromExcel,
|
onPressed: _isImporting ? null : _importFromExcel,
|
||||||
@ -4808,6 +4809,7 @@ Future<void> _showDemandeTransfertDialog(Product product) async {
|
|||||||
backgroundColor: Colors.blue,
|
backgroundColor: Colors.blue,
|
||||||
foregroundColor: Colors.white,
|
foregroundColor: Colors.white,
|
||||||
),
|
),
|
||||||
|
],
|
||||||
const SizedBox(height: 8),
|
const SizedBox(height: 8),
|
||||||
FloatingActionButton.extended(
|
FloatingActionButton.extended(
|
||||||
heroTag: 'addBtn',
|
heroTag: 'addBtn',
|
||||||
|
|||||||
@ -5,6 +5,7 @@ import 'package:flutter/material.dart';
|
|||||||
import 'package:flutter/services.dart';
|
import 'package:flutter/services.dart';
|
||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
import 'package:intl/intl.dart';
|
import 'package:intl/intl.dart';
|
||||||
|
import 'package:mysql1/mysql1.dart';
|
||||||
import 'package:numbers_to_letters/numbers_to_letters.dart';
|
import 'package:numbers_to_letters/numbers_to_letters.dart';
|
||||||
import 'package:pdf/pdf.dart';
|
import 'package:pdf/pdf.dart';
|
||||||
import 'package:pdf/widgets.dart' as pw;
|
import 'package:pdf/widgets.dart' as pw;
|
||||||
@ -276,10 +277,22 @@ class _GestionCommandesPageState extends State<GestionCommandesPage> {
|
|||||||
/// Le PDF est sauvegardé dans un fichier temporaire, qui est partagé
|
/// Le PDF est sauvegardé dans un fichier temporaire, qui est partagé
|
||||||
/// via le mécanisme de partage de fichiers du système.
|
/// via le mécanisme de partage de fichiers du système.
|
||||||
///
|
///
|
||||||
Future<void> _generateBonLivraison(Commande commande) async {
|
// Dans GestionCommandesPage - Remplacez la méthode _generateBonLivraison complète
|
||||||
|
|
||||||
|
Future<void> _generateBonLivraison(Commande commande) async {
|
||||||
final details = await _database.getDetailsCommande(commande.id!);
|
final details = await _database.getDetailsCommande(commande.id!);
|
||||||
final client = await _database.getClientById(commande.clientId);
|
final client = await _database.getClientById(commande.clientId);
|
||||||
final pointDeVente = await _database.getPointDeVenteById(1);
|
final pointDeVenteId = commande.pointDeVenteId;
|
||||||
|
ResultRow? pointDeVenteComplet;
|
||||||
|
|
||||||
|
// ✅ MODIFICATION: Récupération complète des données du point de vente
|
||||||
|
if (pointDeVenteId != null) {
|
||||||
|
pointDeVenteComplet = await _database.getPointDeVenteById(pointDeVenteId);
|
||||||
|
} else {
|
||||||
|
print("ce point de vente n'existe pas");
|
||||||
|
}
|
||||||
|
final pointDeVente = pointDeVenteComplet;
|
||||||
|
|
||||||
|
|
||||||
// Récupérer les informations des vendeurs
|
// Récupérer les informations des vendeurs
|
||||||
final commandeur = commande.commandeurId != null
|
final commandeur = commande.commandeurId != null
|
||||||
@ -289,6 +302,32 @@ class _GestionCommandesPageState extends State<GestionCommandesPage> {
|
|||||||
? await _database.getUserById(commande.validateurId!)
|
? await _database.getUserById(commande.validateurId!)
|
||||||
: null;
|
: null;
|
||||||
|
|
||||||
|
// ✅ NOUVELLE FONCTIONNALITÉ: Parser les informations d'en-tête pour livraison
|
||||||
|
List<String> infosLivraison = [];
|
||||||
|
final livraisonBrute = pointDeVenteComplet?['livraison'];
|
||||||
|
print('=== LIVRAISON BRUTE ===');
|
||||||
|
print(livraisonBrute);
|
||||||
|
print('=== FIN ===');
|
||||||
|
|
||||||
|
if (livraisonBrute != null) {
|
||||||
|
infosLivraison = _database.parseHeaderInfo(livraisonBrute);
|
||||||
|
print('=== INFOS LIVRAISON PARSÉES ===');
|
||||||
|
for (int i = 0; i < infosLivraison.length; i++) {
|
||||||
|
print('Ligne $i: ${infosLivraison[i]}');
|
||||||
|
}
|
||||||
|
print('===============================');
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Infos par défaut si aucune info personnalisée
|
||||||
|
final infosLivraisonDefaut = [
|
||||||
|
'REMAX Andravoangy',
|
||||||
|
'SUPREME CENTER Behoririka \n BOX 405 | 416 | 119',
|
||||||
|
'Tripolisa analankely BOX 7',
|
||||||
|
'033 37 808 18',
|
||||||
|
'www.guycom.mg',
|
||||||
|
];
|
||||||
|
|
||||||
// ✅ DEBUG: Vérifiez combien de détails vous avez
|
// ✅ DEBUG: Vérifiez combien de détails vous avez
|
||||||
print('=== DEBUG BON DE LIVRAISON ===');
|
print('=== DEBUG BON DE LIVRAISON ===');
|
||||||
print('Nombre de détails récupérés: ${details.length}');
|
print('Nombre de détails récupérés: ${details.length}');
|
||||||
@ -329,16 +368,14 @@ class _GestionCommandesPageState extends State<GestionCommandesPage> {
|
|||||||
});
|
});
|
||||||
print(' ✅ Produit trouvé: ${produit.name}');
|
print(' ✅ Produit trouvé: ${produit.name}');
|
||||||
} else {
|
} else {
|
||||||
// ✅ Même si le produit est null, on l'ajoute quand même avec les infos du détail
|
|
||||||
detailsAvecProduits.add({
|
detailsAvecProduits.add({
|
||||||
'detail': detail,
|
'detail': detail,
|
||||||
'produit': null, // On garde null mais on utilisera les infos du détail
|
'produit': null,
|
||||||
});
|
});
|
||||||
print(' ⚠️ Produit non trouvé, utilisation des données du détail');
|
print(' ⚠️ Produit non trouvé, utilisation des données du détail');
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
print(' ❌ Erreur lors de la récupération du produit: $e');
|
print(' ❌ Erreur lors de la récupération du produit: $e');
|
||||||
// En cas d'erreur, on ajoute quand même le détail
|
|
||||||
detailsAvecProduits.add({
|
detailsAvecProduits.add({
|
||||||
'detail': detail,
|
'detail': detail,
|
||||||
'produit': null,
|
'produit': null,
|
||||||
@ -361,10 +398,9 @@ class _GestionCommandesPageState extends State<GestionCommandesPage> {
|
|||||||
regularFont = pw.Font.ttf(await rootBundle.load('assets/fonts/Roboto-Regular.ttf'));
|
regularFont = pw.Font.ttf(await rootBundle.load('assets/fonts/Roboto-Regular.ttf'));
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
print('⚠️ Impossible de charger les polices personnalisées: $e');
|
print('⚠️ Impossible de charger les polices personnalisées: $e');
|
||||||
// Utiliser les polices par défaut
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ✅ DÉFINITION DES STYLES DE TEXTE - Variables globales dans la fonction
|
// ✅ DÉFINITION DES STYLES DE TEXTE
|
||||||
final tinyTextStyle = pw.TextStyle(fontSize: 9, font: regularFont);
|
final tinyTextStyle = pw.TextStyle(fontSize: 9, font: regularFont);
|
||||||
final smallTextStyle = pw.TextStyle(fontSize: 10, font: regularFont);
|
final smallTextStyle = pw.TextStyle(fontSize: 10, font: regularFont);
|
||||||
final normalTextStyle = pw.TextStyle(fontSize: 11, font: regularFont);
|
final normalTextStyle = pw.TextStyle(fontSize: 11, font: regularFont);
|
||||||
@ -373,11 +409,54 @@ class _GestionCommandesPageState extends State<GestionCommandesPage> {
|
|||||||
final frameTextStyle = pw.TextStyle(fontSize: 10, font: regularFont);
|
final frameTextStyle = pw.TextStyle(fontSize: 10, font: regularFont);
|
||||||
final italicTextStyle = pw.TextStyle(fontSize: 9, fontWeight: pw.FontWeight.bold, font: italicFont ?? regularFont);
|
final italicTextStyle = pw.TextStyle(fontSize: 9, fontWeight: pw.FontWeight.bold, font: italicFont ?? regularFont);
|
||||||
final italicLogoStyle = pw.TextStyle(fontSize: 8, fontWeight: pw.FontWeight.bold, font: italicFont ?? regularFont);
|
final italicLogoStyle = pw.TextStyle(fontSize: 8, fontWeight: pw.FontWeight.bold, font: italicFont ?? regularFont);
|
||||||
|
Future<pw.Widget> buildLogoWidget() async {
|
||||||
|
final logoRaw = pointDeVenteComplet?['logo'];
|
||||||
|
|
||||||
// ✅ Fonction pour créer un exemplaire - CORRIGÉE
|
if (logoRaw != null) {
|
||||||
|
try {
|
||||||
|
Uint8List bytes;
|
||||||
|
if (logoRaw is Uint8List) {
|
||||||
|
bytes = logoRaw;
|
||||||
|
} else if (logoRaw is List<int>) {
|
||||||
|
bytes = Uint8List.fromList(logoRaw);
|
||||||
|
} else if (logoRaw.runtimeType.toString() == 'Blob') {
|
||||||
|
// Cast dynamique pour appeler toBytes()
|
||||||
|
dynamic blobDynamic = logoRaw;
|
||||||
|
bytes = blobDynamic.toBytes();
|
||||||
|
} else {
|
||||||
|
throw Exception("Format de logo non supporté: ${logoRaw.runtimeType}");
|
||||||
|
}
|
||||||
|
|
||||||
|
final imageLogo = pw.MemoryImage(bytes);
|
||||||
|
return pw.Image(imageLogo, width: 100, height: 100);
|
||||||
|
} catch (e) {
|
||||||
|
print('Erreur chargement logo BDD: $e');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return pw.Image(image, width: 100, height: 100);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
final logoWidget = await buildLogoWidget();
|
||||||
|
|
||||||
|
// ✅ FONCTION POUR CONSTRUIRE L'EN-TÊTE DYNAMIQUE
|
||||||
|
pw.Widget buildEnteteInfos() {
|
||||||
|
final infosAUtiliser = infosLivraison.isNotEmpty ? infosLivraison : infosLivraisonDefaut;
|
||||||
|
|
||||||
|
return pw.Column(
|
||||||
|
crossAxisAlignment: pw.CrossAxisAlignment.start,
|
||||||
|
children: infosAUtiliser.map((info) {
|
||||||
|
return pw.Padding(
|
||||||
|
padding: const pw.EdgeInsets.only(bottom: 1),
|
||||||
|
child: pw.Text(info, style: tinyTextStyle),
|
||||||
|
);
|
||||||
|
}).toList(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ✅ Fonction pour créer un exemplaire - AVEC EN-TÊTE DYNAMIQUE
|
||||||
pw.Widget buildExemplaire(String typeExemplaire) {
|
pw.Widget buildExemplaire(String typeExemplaire) {
|
||||||
return pw.Container(
|
return pw.Container(
|
||||||
// ✅ PAS DE HAUTEUR FIXE - Elle s'adapte au contenu
|
|
||||||
width: double.infinity,
|
width: double.infinity,
|
||||||
decoration: pw.BoxDecoration(
|
decoration: pw.BoxDecoration(
|
||||||
border: pw.Border.all(color: PdfColors.black, width: 1.5),
|
border: pw.Border.all(color: PdfColors.black, width: 1.5),
|
||||||
@ -415,28 +494,15 @@ class _GestionCommandesPageState extends State<GestionCommandesPage> {
|
|||||||
crossAxisAlignment: pw.CrossAxisAlignment.start,
|
crossAxisAlignment: pw.CrossAxisAlignment.start,
|
||||||
mainAxisAlignment: pw.MainAxisAlignment.spaceBetween,
|
mainAxisAlignment: pw.MainAxisAlignment.spaceBetween,
|
||||||
children: [
|
children: [
|
||||||
// Logo et infos entreprise
|
// Logo et infos entreprise - ✅ AVEC INFOS DYNAMIQUES
|
||||||
pw.Column(
|
pw.Column(
|
||||||
crossAxisAlignment: pw.CrossAxisAlignment.start,
|
crossAxisAlignment: pw.CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
pw.Container(
|
logoWidget,
|
||||||
width: 100,
|
|
||||||
height: 100,
|
|
||||||
child: pw.Image(image),
|
|
||||||
),
|
|
||||||
pw.SizedBox(height: 3),
|
pw.SizedBox(height: 3),
|
||||||
pw.Text('NOTRE COMPETENCE, A VOTRE SERVICE', style: italicLogoStyle),
|
pw.Text('NOTRE COMPETENCE, A VOTRE SERVICE', style: italicLogoStyle),
|
||||||
pw.SizedBox(height: 4),
|
pw.SizedBox(height: 4),
|
||||||
pw.Column(
|
buildEnteteInfos(), // ✅ EN-TÊTE DYNAMIQUE ICI
|
||||||
crossAxisAlignment: pw.CrossAxisAlignment.start,
|
|
||||||
children: [
|
|
||||||
pw.Text('REMAX Andravoangy', style: tinyTextStyle),
|
|
||||||
pw.Text('SUPREME CENTER Behoririka \n BOX 405 | 416 | 119', style: tinyTextStyle),
|
|
||||||
pw.Text('Tripolisa analankely BOX 7', style: tinyTextStyle),
|
|
||||||
pw.Text('033 37 808 18', style: tinyTextStyle),
|
|
||||||
pw.Text('www.guycom.mg', style: tinyTextStyle),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
|
||||||
@ -964,14 +1030,22 @@ class _GestionCommandesPageState extends State<GestionCommandesPage> {
|
|||||||
|
|
||||||
// Partager ou ouvrir le fichier
|
// Partager ou ouvrir le fichier
|
||||||
await OpenFile.open(file.path);
|
await OpenFile.open(file.path);
|
||||||
}
|
}
|
||||||
//==============================================================
|
//==============================================================
|
||||||
|
|
||||||
// Modifiez la méthode _generateInvoice dans GestionCommandesPage
|
// Modifiez la méthode _generateInvoice dans GestionCommandesPage
|
||||||
Future<void> _generateInvoice(Commande commande) async {
|
Future<void> _generateInvoice(Commande commande) async {
|
||||||
final details = await _database.getDetailsCommande(commande.id!);
|
final details = await _database.getDetailsCommande(commande.id!);
|
||||||
final client = await _database.getClientById(commande.clientId);
|
final client = await _database.getClientById(commande.clientId);
|
||||||
final pointDeVente = await _database.getPointDeVenteById(1);
|
final pointDeVenteId = commande.pointDeVenteId;
|
||||||
|
ResultRow? pointDeVenteComplet;
|
||||||
|
|
||||||
|
if (pointDeVenteId != null) {
|
||||||
|
pointDeVenteComplet = await _database.getPointDeVenteById(pointDeVenteId);
|
||||||
|
} else {
|
||||||
|
print("ce point de vente n'existe pas");
|
||||||
|
}
|
||||||
|
final pointDeVente = pointDeVenteComplet;
|
||||||
|
|
||||||
// Récupérer les informations des vendeurs
|
// Récupérer les informations des vendeurs
|
||||||
final commandeur = commande.commandeurId != null
|
final commandeur = commande.commandeurId != null
|
||||||
@ -981,6 +1055,34 @@ class _GestionCommandesPageState extends State<GestionCommandesPage> {
|
|||||||
? await _database.getUserById(commande.validateurId!)
|
? await _database.getUserById(commande.validateurId!)
|
||||||
: null;
|
: null;
|
||||||
|
|
||||||
|
List<String> infosFacture = [];
|
||||||
|
final factureBrute = pointDeVenteComplet?['facture'];
|
||||||
|
print('=== FACTURE BRUTE ===');
|
||||||
|
print(factureBrute);
|
||||||
|
print('=== FIN ===');
|
||||||
|
|
||||||
|
if (factureBrute != null) {
|
||||||
|
infosFacture = _database.parseHeaderInfo(factureBrute);
|
||||||
|
print('=== INFOS FACTURE PARSÉES ===');
|
||||||
|
for (int i = 0; i < infosFacture.length; i++) {
|
||||||
|
print('Ligne $i: ${infosFacture[i]}');
|
||||||
|
}
|
||||||
|
print('===============================');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Infos par défaut si aucune info personnalisée
|
||||||
|
final infosFactureDefaut = [
|
||||||
|
'REMAX by GUYCOM Andravoangy',
|
||||||
|
'SUPREME CENTER Behoririka box 405',
|
||||||
|
'SUPREME CENTER Behoririka box 416',
|
||||||
|
'SUPREME CENTER Behoririka box 119',
|
||||||
|
'TRIPOLITSA Analakely BOX 7',
|
||||||
|
'033 37 808 18',
|
||||||
|
'www.guycom.mg',
|
||||||
|
'NIF: 4000106673 - STAT 95210 11 2017 1 003651',
|
||||||
|
'Facebook: GuyCom',
|
||||||
|
];
|
||||||
|
|
||||||
final iconPhone = await buildIconPhoneText();
|
final iconPhone = await buildIconPhoneText();
|
||||||
final iconChecked = await buildIconCheckedText();
|
final iconChecked = await buildIconCheckedText();
|
||||||
final iconGlobe = await buildIconGlobeText();
|
final iconGlobe = await buildIconGlobeText();
|
||||||
@ -1032,6 +1134,55 @@ class _GestionCommandesPageState extends State<GestionCommandesPage> {
|
|||||||
final emojifont = pw.TextStyle(
|
final emojifont = pw.TextStyle(
|
||||||
fontSize: 8, fontWeight: pw.FontWeight.bold, font: emojiSuportFont);
|
fontSize: 8, fontWeight: pw.FontWeight.bold, font: emojiSuportFont);
|
||||||
|
|
||||||
|
Future<pw.Widget> buildLogoWidget() async {
|
||||||
|
final logoRaw = pointDeVenteComplet?['logo'];
|
||||||
|
|
||||||
|
if (logoRaw != null) {
|
||||||
|
try {
|
||||||
|
Uint8List bytes;
|
||||||
|
if (logoRaw is Uint8List) {
|
||||||
|
bytes = logoRaw;
|
||||||
|
} else if (logoRaw is List<int>) {
|
||||||
|
bytes = Uint8List.fromList(logoRaw);
|
||||||
|
} else if (logoRaw.runtimeType.toString() == 'Blob') {
|
||||||
|
// Cast dynamique pour appeler toBytes()
|
||||||
|
dynamic blobDynamic = logoRaw;
|
||||||
|
bytes = blobDynamic.toBytes();
|
||||||
|
} else {
|
||||||
|
throw Exception("Format de logo non supporté: ${logoRaw.runtimeType}");
|
||||||
|
}
|
||||||
|
|
||||||
|
final imageLogo = pw.MemoryImage(bytes);
|
||||||
|
return pw.Container(width: 200, height: 120, child: pw.Image(imageLogo));
|
||||||
|
} catch (e) {
|
||||||
|
print('Erreur chargement logo BDD: $e');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return pw.Container(width: 200, height: 100, child: pw.Image(image));
|
||||||
|
}
|
||||||
|
|
||||||
|
final logoWidget = await buildLogoWidget();
|
||||||
|
pw.Widget buildEnteteFactureInfos() {
|
||||||
|
final infosAUtiliser = infosFacture.isNotEmpty ? infosFacture : infosFactureDefaut;
|
||||||
|
|
||||||
|
return pw.Column(
|
||||||
|
crossAxisAlignment: pw.CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
...infosAUtiliser.map((info) {
|
||||||
|
return pw.Row(
|
||||||
|
children: [
|
||||||
|
iconChecked,
|
||||||
|
pw.SizedBox(width: 4),
|
||||||
|
pw.Text(info, style: smallTextStyle),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}),
|
||||||
|
pw.SizedBox(height: 2), // ajouté en fin de liste
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
pdf.addPage(
|
pdf.addPage(
|
||||||
pw.Page(
|
pw.Page(
|
||||||
pageFormat: PdfPageFormat.a4, // Mode portrait
|
pageFormat: PdfPageFormat.a4, // Mode portrait
|
||||||
@ -1049,71 +1200,11 @@ class _GestionCommandesPageState extends State<GestionCommandesPage> {
|
|||||||
pw.Column(
|
pw.Column(
|
||||||
crossAxisAlignment: pw.CrossAxisAlignment.start,
|
crossAxisAlignment: pw.CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
pw.Container(
|
logoWidget,
|
||||||
width: 200,
|
|
||||||
height: 120,
|
|
||||||
child: pw.Image(image),
|
|
||||||
),
|
|
||||||
pw.Text(' NOTRE COMPETENCE, A VOTRE SERVICE',
|
pw.Text(' NOTRE COMPETENCE, A VOTRE SERVICE',
|
||||||
style: italicTextStyleLogo),
|
style: italicTextStyleLogo),
|
||||||
pw.SizedBox(height: 10),
|
pw.SizedBox(height: 10),
|
||||||
pw.Column(
|
buildEnteteFactureInfos(),
|
||||||
crossAxisAlignment: pw.CrossAxisAlignment.start,
|
|
||||||
children: [
|
|
||||||
pw.Row(children: [
|
|
||||||
iconChecked,
|
|
||||||
pw.SizedBox(width: 4),
|
|
||||||
pw.Text('REMAX by GUYCOM Andravoangy',
|
|
||||||
style: smallTextStyle)
|
|
||||||
]),
|
|
||||||
pw.SizedBox(height: 2),
|
|
||||||
pw.Row(children: [
|
|
||||||
iconChecked,
|
|
||||||
pw.SizedBox(width: 4),
|
|
||||||
pw.Text('SUPREME CENTER Behoririka box 405',
|
|
||||||
style: smallTextStyle)
|
|
||||||
]),
|
|
||||||
pw.SizedBox(height: 2),
|
|
||||||
pw.Row(children: [
|
|
||||||
iconChecked,
|
|
||||||
pw.SizedBox(width: 4),
|
|
||||||
pw.Text('SUPREME CENTER Behoririka box 416',
|
|
||||||
style: smallTextStyle)
|
|
||||||
]),
|
|
||||||
pw.SizedBox(height: 2),
|
|
||||||
pw.Row(children: [
|
|
||||||
iconChecked,
|
|
||||||
pw.SizedBox(width: 4),
|
|
||||||
pw.Text('SUPREME CENTER Behoririka box 119',
|
|
||||||
style: smallTextStyle)
|
|
||||||
]),
|
|
||||||
pw.SizedBox(height: 2),
|
|
||||||
pw.Row(children: [
|
|
||||||
iconChecked,
|
|
||||||
pw.SizedBox(width: 4),
|
|
||||||
pw.Text('TRIPOLITSA Analakely BOX 7',
|
|
||||||
style: smallTextStyle)
|
|
||||||
]),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
pw.SizedBox(height: 8),
|
|
||||||
pw.Row(children: [
|
|
||||||
iconPhone,
|
|
||||||
pw.SizedBox(width: 4),
|
|
||||||
pw.Text('033 37 808 18', style: smallTextStyle)
|
|
||||||
]),
|
|
||||||
pw.Row(children: [
|
|
||||||
iconGlobe,
|
|
||||||
pw.SizedBox(width: 4),
|
|
||||||
pw.Text('www.guycom.mg', style: smallTextStyle)
|
|
||||||
]),
|
|
||||||
pw.Row(children: [
|
|
||||||
iconGlobe,
|
|
||||||
pw.SizedBox(width: 4),
|
|
||||||
pw.Text('NIF: 4000106673 - STAT 95210 11 2017 1 003651',
|
|
||||||
style: smallTextStyle)
|
|
||||||
]),
|
|
||||||
pw.Text('Facebook: GuyCom', style: smallTextStyle),
|
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
|
||||||
@ -1906,9 +1997,11 @@ class _GestionCommandesPageState extends State<GestionCommandesPage> {
|
|||||||
return 'MÉTHODE INCONNUE (${payment.type.toString()})'; // Debug info
|
return 'MÉTHODE INCONNUE (${payment.type.toString()})'; // Debug info
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// Dans GestionCommandesPage - Remplacez la méthode _generateReceipt complète
|
||||||
|
|
||||||
Future<void> _generateReceipt(
|
Future<void> _generateReceipt(
|
||||||
Commande commande, PaymentMethod payment) async {
|
Commande commande, PaymentMethod payment) async {
|
||||||
|
|
||||||
final details = await _database.getDetailsCommande(commande.id!);
|
final details = await _database.getDetailsCommande(commande.id!);
|
||||||
final client = await _database.getClientById(commande.clientId);
|
final client = await _database.getClientById(commande.clientId);
|
||||||
final commandeur = commande.commandeurId != null
|
final commandeur = commande.commandeurId != null
|
||||||
@ -2875,6 +2968,30 @@ class _GestionCommandesPageState extends State<GestionCommandesPage> {
|
|||||||
trailing: Row(
|
trailing: Row(
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
children: [
|
children: [
|
||||||
|
Container(
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: Colors.white,
|
||||||
|
borderRadius: BorderRadius.circular(8),
|
||||||
|
boxShadow: [
|
||||||
|
BoxShadow(
|
||||||
|
color: Colors.black.withOpacity(0.1),
|
||||||
|
blurRadius: 2,
|
||||||
|
offset: const Offset(0, 1),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
child: IconButton(
|
||||||
|
icon: Icon(
|
||||||
|
Icons.payment,
|
||||||
|
color: Colors.green.shade600,
|
||||||
|
),
|
||||||
|
onPressed: () => _showPaymentOptions(commande),
|
||||||
|
tooltip: 'Générer le ticket de la commande',
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(
|
||||||
|
width: 10,
|
||||||
|
),
|
||||||
Container(
|
Container(
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: Colors.white,
|
color: Colors.white,
|
||||||
@ -2950,8 +3067,7 @@ class _GestionCommandesPageState extends State<GestionCommandesPage> {
|
|||||||
CommandeActions(
|
CommandeActions(
|
||||||
commande: commande,
|
commande: commande,
|
||||||
onStatutChanged: _updateStatut,
|
onStatutChanged: _updateStatut,
|
||||||
onPaymentSelected:
|
onGenerateBonLivraison:_generateBon_lifraisonWithPasswordVerification
|
||||||
_showPaymentOptions,
|
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
|||||||
@ -1,5 +1,9 @@
|
|||||||
|
import 'dart:io';
|
||||||
|
import 'dart:typed_data';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
|
import 'package:image_picker/image_picker.dart';
|
||||||
|
import 'package:path_provider/path_provider.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';
|
||||||
@ -22,8 +26,10 @@ class _AjoutPointDeVentePageState extends State<AjoutPointDeVentePage> {
|
|||||||
|
|
||||||
// Liste des points de vente
|
// Liste des points de vente
|
||||||
List<Map<String, dynamic>> _pointsDeVente = [];
|
List<Map<String, dynamic>> _pointsDeVente = [];
|
||||||
|
List<Map<String, dynamic>> _filteredPointsDeVente = [];
|
||||||
final TextEditingController _searchController = TextEditingController();
|
final TextEditingController _searchController = TextEditingController();
|
||||||
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
@ -31,10 +37,12 @@ class _AjoutPointDeVentePageState extends State<AjoutPointDeVentePage> {
|
|||||||
_searchController.addListener(_filterPointsDeVente);
|
_searchController.addListener(_filterPointsDeVente);
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> _loadPointsDeVente() async {
|
Future<void> _loadPointsDeVente() async {
|
||||||
|
if (mounted) {
|
||||||
setState(() {
|
setState(() {
|
||||||
_isLoading = true;
|
_isLoading = true;
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
final points = await _appDatabase.getPointsDeVente();
|
final points = await _appDatabase.getPointsDeVente();
|
||||||
@ -46,11 +54,15 @@ Future<void> _loadPointsDeVente() async {
|
|||||||
point['constraintCount'] = (verification['reasons'] as List).length;
|
point['constraintCount'] = (verification['reasons'] as List).length;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (mounted) {
|
||||||
setState(() {
|
setState(() {
|
||||||
_pointsDeVente = points;
|
_pointsDeVente = points;
|
||||||
|
_filteredPointsDeVente = List.from(points);
|
||||||
_isLoading = false;
|
_isLoading = false;
|
||||||
});
|
});
|
||||||
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
if (mounted) {
|
||||||
setState(() {
|
setState(() {
|
||||||
_isLoading = false;
|
_isLoading = false;
|
||||||
});
|
});
|
||||||
@ -62,22 +74,25 @@ Future<void> _loadPointsDeVente() async {
|
|||||||
colorText: Colors.white,
|
colorText: Colors.white,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void _filterPointsDeVente() {
|
|
||||||
final query = _searchController.text.toLowerCase();
|
|
||||||
if (query.isEmpty) {
|
|
||||||
_loadPointsDeVente();
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void _filterPointsDeVente() {
|
||||||
|
final query = _searchController.text.toLowerCase().trim();
|
||||||
|
if (mounted) {
|
||||||
setState(() {
|
setState(() {
|
||||||
_pointsDeVente = _pointsDeVente.where((point) {
|
if (query.isEmpty) {
|
||||||
|
_filteredPointsDeVente = List.from(_pointsDeVente);
|
||||||
|
} else {
|
||||||
|
_filteredPointsDeVente = _pointsDeVente.where((point) {
|
||||||
final nom = point['nom']?.toString().toLowerCase() ?? '';
|
final nom = point['nom']?.toString().toLowerCase() ?? '';
|
||||||
return nom.contains(query);
|
final code = point['code']?.toString().toLowerCase() ?? '';
|
||||||
|
return nom.contains(query) || code.contains(query);
|
||||||
}).toList();
|
}).toList();
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Future<void> _submitForm() async {
|
Future<void> _submitForm() async {
|
||||||
if (_formKey.currentState!.validate()) {
|
if (_formKey.currentState!.validate()) {
|
||||||
@ -114,26 +129,261 @@ Future<void> _loadPointsDeVente() async {
|
|||||||
colorText: Colors.white,
|
colorText: Colors.white,
|
||||||
);
|
);
|
||||||
} finally {
|
} finally {
|
||||||
|
if (mounted) {
|
||||||
setState(() {
|
setState(() {
|
||||||
_isLoading = false;
|
_isLoading = false;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Future<void> _showConstraintDialog(int id, Map<String, dynamic> verificationResult) async {
|
}
|
||||||
|
Future<void> editPointDeVente(BuildContext context, Map<String, dynamic> pointDeVente) async {
|
||||||
|
print('=== DIAGNOSTIC DES DONNÉES ===');
|
||||||
|
print('ID: ${pointDeVente['id']}');
|
||||||
|
print('Nom: ${pointDeVente['nom']}');
|
||||||
|
print('Code: ${pointDeVente['code']}');
|
||||||
|
print('Content (ticket): "${pointDeVente['content']}" (Type: ${pointDeVente['content'].runtimeType})');
|
||||||
|
print('Livraison: "${pointDeVente['livraison']}" (Type: ${pointDeVente['livraison'].runtimeType})');
|
||||||
|
print('Facture: "${pointDeVente['facture']}" (Type: ${pointDeVente['facture'].runtimeType})');
|
||||||
|
print('Logo: ${pointDeVente['logo']?.runtimeType}');
|
||||||
|
print('Toutes les clés: ${pointDeVente.keys.toList()}');
|
||||||
|
print('===============================');
|
||||||
|
final _editFormKey = GlobalKey<FormState>();
|
||||||
|
final ImagePicker _picker = ImagePicker();
|
||||||
|
|
||||||
|
// Fonction helper pour convertir les valeurs en String de manière sûre
|
||||||
|
String safeStringConversion(dynamic value) {
|
||||||
|
if (value == null) return '';
|
||||||
|
if (value is String) return value;
|
||||||
|
// Vérifier si c'est un type binaire (Uint8List, List<int>, etc.)
|
||||||
|
if (value is Uint8List || value is List<int> || value is List) {
|
||||||
|
// Si c'est des données binaires, on retourne une chaîne vide
|
||||||
|
// car on ne peut pas les convertir en texte lisible
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
// Pour tous les autres types, essayer la conversion toString()
|
||||||
|
try {
|
||||||
|
return value.toString();
|
||||||
|
} catch (e) {
|
||||||
|
print('Erreur conversion vers String: $e, type: ${value.runtimeType}');
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initialisation dynamique des contrôleurs avec conversion sécurisée
|
||||||
|
final _editNomController = TextEditingController(
|
||||||
|
text: safeStringConversion(pointDeVente['nom'])
|
||||||
|
);
|
||||||
|
final _editCodeController = TextEditingController(
|
||||||
|
text: safeStringConversion(pointDeVente['code'])
|
||||||
|
);
|
||||||
|
final _editTicketController = TextEditingController(
|
||||||
|
text: safeStringConversion(pointDeVente['content'])
|
||||||
|
);
|
||||||
|
final _editLivraisonController = TextEditingController(
|
||||||
|
text: safeStringConversion(pointDeVente['livraison'])
|
||||||
|
);
|
||||||
|
final _editFactureController = TextEditingController(
|
||||||
|
text: safeStringConversion(pointDeVente['facture'])
|
||||||
|
);
|
||||||
|
|
||||||
|
File? _selectedImage;
|
||||||
|
Uint8List? _currentImageBlob;
|
||||||
|
|
||||||
|
// Gérer la conversion du logo de manière sécurisée
|
||||||
|
final logoData = pointDeVente['logo'];
|
||||||
|
if (logoData != null) {
|
||||||
|
try {
|
||||||
|
if (logoData is Uint8List) {
|
||||||
|
_currentImageBlob = logoData;
|
||||||
|
} else if (logoData is List<int>) {
|
||||||
|
_currentImageBlob = Uint8List.fromList(logoData);
|
||||||
|
} else if (logoData is List) {
|
||||||
|
// Cas où c'est une List<dynamic> contenant des int
|
||||||
|
_currentImageBlob = Uint8List.fromList(logoData.cast<int>());
|
||||||
|
} else {
|
||||||
|
// Type non supporté (comme Blob), laisser null et logger
|
||||||
|
print('Type de logo non supporté: ${logoData.runtimeType}');
|
||||||
|
_currentImageBlob = null;
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
print('Erreur lors de la conversion du logo: $e');
|
||||||
|
_currentImageBlob = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool _isEditLoading = false;
|
||||||
|
|
||||||
|
Future<void> pickImage(ImageSource source) async {
|
||||||
|
try {
|
||||||
|
final XFile? image = await _picker.pickImage(source: source);
|
||||||
|
if (image != null) {
|
||||||
|
_selectedImage = File(image.path);
|
||||||
|
_currentImageBlob = await _selectedImage!.readAsBytes();
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
print('Erreur lors de la sélection de l\'image : $e');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> saveChanges() async {
|
||||||
|
if (_editFormKey.currentState?.validate() ?? false) {
|
||||||
|
_isEditLoading = true;
|
||||||
|
|
||||||
|
try {
|
||||||
|
await _appDatabase.updatePointDeVentes(
|
||||||
|
pointDeVente['id'],
|
||||||
|
_editNomController.text.trim(),
|
||||||
|
_editCodeController.text.trim(),
|
||||||
|
content: _editTicketController.text.trim(),
|
||||||
|
livraison: _editLivraisonController.text.trim(),
|
||||||
|
facture: _editFactureController.text.trim(),
|
||||||
|
imagePath: _currentImageBlob,
|
||||||
|
);
|
||||||
|
|
||||||
|
Get.back(); // Fermer le dialog
|
||||||
|
ScaffoldMessenger.of(context).showSnackBar(
|
||||||
|
const SnackBar(content: Text('Modification enregistrée avec succès')),
|
||||||
|
);
|
||||||
|
|
||||||
|
await _loadPointsDeVente(); // Rafraîchir la liste
|
||||||
|
} catch (e) {
|
||||||
|
print('Erreur lors de la sauvegarde : $e');
|
||||||
|
ScaffoldMessenger.of(context).showSnackBar(
|
||||||
|
const SnackBar(content: Text('Erreur lors de la modification')),
|
||||||
|
);
|
||||||
|
} finally {
|
||||||
|
_isEditLoading = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Affichage de la boîte de dialogue
|
||||||
|
await Get.dialog(
|
||||||
|
Dialog(
|
||||||
|
child: ConstrainedBox(
|
||||||
|
constraints: const BoxConstraints(maxWidth: 500, maxHeight: 700),
|
||||||
|
child: Padding(
|
||||||
|
padding: const EdgeInsets.all(16.0),
|
||||||
|
child: Form(
|
||||||
|
key: _editFormKey,
|
||||||
|
child: SingleChildScrollView(
|
||||||
|
child: Column(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
children: [
|
||||||
|
const Text('Modifier le point de vente', style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold)),
|
||||||
|
const SizedBox(height: 16),
|
||||||
|
TextFormField(
|
||||||
|
controller: _editNomController,
|
||||||
|
decoration: const InputDecoration(labelText: 'Nom du point de vente'),
|
||||||
|
validator: (value) => value == null || value.isEmpty ? 'Le nom est requis' : null,
|
||||||
|
),
|
||||||
|
const SizedBox(height: 8),
|
||||||
|
TextFormField(
|
||||||
|
controller: _editCodeController,
|
||||||
|
decoration: const InputDecoration(labelText: 'Code du point de vente'),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 8),
|
||||||
|
TextFormField(
|
||||||
|
controller: _editTicketController,
|
||||||
|
decoration: const InputDecoration(labelText: 'Info ticket'),
|
||||||
|
maxLines: 3,
|
||||||
|
),
|
||||||
|
const SizedBox(height: 8),
|
||||||
|
TextFormField(
|
||||||
|
controller: _editLivraisonController,
|
||||||
|
decoration: const InputDecoration(labelText: 'Info bon de livraison'),
|
||||||
|
maxLines: 3,
|
||||||
|
),
|
||||||
|
const SizedBox(height: 8),
|
||||||
|
TextFormField(
|
||||||
|
controller: _editFactureController,
|
||||||
|
decoration: const InputDecoration(labelText: 'Info facture'),
|
||||||
|
maxLines: 3,
|
||||||
|
),
|
||||||
|
const SizedBox(height: 16),
|
||||||
|
Row(
|
||||||
|
children: [
|
||||||
|
Expanded(
|
||||||
|
child: ElevatedButton.icon(
|
||||||
|
onPressed: () => pickImage(ImageSource.gallery),
|
||||||
|
icon: const Icon(Icons.image),
|
||||||
|
label: const Text('Galerie'),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(width: 10),
|
||||||
|
Expanded(
|
||||||
|
child: ElevatedButton.icon(
|
||||||
|
onPressed: () => pickImage(ImageSource.camera),
|
||||||
|
icon: const Icon(Icons.camera_alt),
|
||||||
|
label: const Text('Caméra'),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
const SizedBox(height: 10),
|
||||||
|
if (_currentImageBlob != null)
|
||||||
|
Container(
|
||||||
|
height: 100,
|
||||||
|
width: double.infinity,
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
borderRadius: BorderRadius.circular(8),
|
||||||
|
border: Border.all(color: Colors.grey.shade300),
|
||||||
|
),
|
||||||
|
child: ClipRRect(
|
||||||
|
borderRadius: BorderRadius.circular(8),
|
||||||
|
child: Image.memory(
|
||||||
|
_currentImageBlob!,
|
||||||
|
fit: BoxFit.cover,
|
||||||
|
errorBuilder: (context, error, stackTrace) =>
|
||||||
|
const Center(child: Text('Erreur image')),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 16),
|
||||||
|
SizedBox(
|
||||||
|
width: double.infinity,
|
||||||
|
child: ElevatedButton.icon(
|
||||||
|
onPressed: _isEditLoading ? null : saveChanges,
|
||||||
|
icon: const Icon(Icons.save),
|
||||||
|
label: Text(_isEditLoading ? 'Sauvegarde...' : 'Enregistrer'),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
// Nettoyage
|
||||||
|
await Future.delayed(const Duration(milliseconds: 200));
|
||||||
|
_editNomController.dispose();
|
||||||
|
_editCodeController.dispose();
|
||||||
|
_editTicketController.dispose();
|
||||||
|
_editLivraisonController.dispose();
|
||||||
|
_editFactureController.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Future<void> _showConstraintDialog(int id, Map<String, dynamic> verificationResult) async {
|
||||||
final reasons = verificationResult['reasons'] as List<String>;
|
final reasons = verificationResult['reasons'] as List<String>;
|
||||||
final suggestions = verificationResult['suggestions'] as List<String>;
|
final suggestions = verificationResult['suggestions'] as List<String>;
|
||||||
|
|
||||||
await Get.dialog(
|
await Get.dialog(
|
||||||
AlertDialog(
|
AlertDialog(
|
||||||
title: Row(
|
title: const Row(
|
||||||
children: const [
|
children: [
|
||||||
Icon(Icons.warning, color: Colors.orange),
|
Icon(Icons.warning, color: Colors.orange),
|
||||||
SizedBox(width: 8),
|
SizedBox(width: 8),
|
||||||
Text('Suppression impossible'),
|
Expanded(child: Text('Suppression impossible')),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
content: SingleChildScrollView(
|
content: ConstrainedBox(
|
||||||
|
constraints: const BoxConstraints(maxHeight: 400),
|
||||||
|
child: SingleChildScrollView(
|
||||||
child: Column(
|
child: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
@ -174,6 +424,7 @@ Future<void> _showConstraintDialog(int id, Map<String, dynamic> verificationResu
|
|||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
),
|
||||||
actions: [
|
actions: [
|
||||||
TextButton(
|
TextButton(
|
||||||
onPressed: () => Get.back(),
|
onPressed: () => Get.back(),
|
||||||
@ -189,13 +440,14 @@ Future<void> _showConstraintDialog(int id, Map<String, dynamic> verificationResu
|
|||||||
backgroundColor: Colors.blue,
|
backgroundColor: Colors.blue,
|
||||||
foregroundColor: Colors.white,
|
foregroundColor: Colors.white,
|
||||||
),
|
),
|
||||||
child: const Text('Transférer les produits'),
|
child: const Text('Transférer'),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
Future<void> _showTransferDialog(int sourcePointDeVenteId) async {
|
|
||||||
|
Future<void> _showTransferDialog(int sourcePointDeVenteId) async {
|
||||||
final pointsDeVente = await _appDatabase.getPointsDeVenteForTransfer(sourcePointDeVenteId);
|
final pointsDeVente = await _appDatabase.getPointsDeVenteForTransfer(sourcePointDeVenteId);
|
||||||
|
|
||||||
if (pointsDeVente.isEmpty) {
|
if (pointsDeVente.isEmpty) {
|
||||||
@ -214,31 +466,35 @@ Future<void> _showTransferDialog(int sourcePointDeVenteId) async {
|
|||||||
await Get.dialog(
|
await Get.dialog(
|
||||||
AlertDialog(
|
AlertDialog(
|
||||||
title: const Text('Transférer les produits'),
|
title: const Text('Transférer les produits'),
|
||||||
content: Column(
|
content: ConstrainedBox(
|
||||||
|
constraints: const BoxConstraints(minWidth: 300),
|
||||||
|
child: Column(
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
const Text('Sélectionnez le point de vente de destination pour les produits :'),
|
const Text('Sélectionnez le point de vente de destination pour les produits :'),
|
||||||
const SizedBox(height: 16),
|
const SizedBox(height: 16),
|
||||||
SizedBox(
|
DropdownButtonFormField<int>(
|
||||||
width: double.maxFinite,
|
|
||||||
child: DropdownButtonFormField<int>(
|
|
||||||
value: selectedPointDeVenteId,
|
value: selectedPointDeVenteId,
|
||||||
|
isExpanded: true,
|
||||||
decoration: const InputDecoration(
|
decoration: const InputDecoration(
|
||||||
labelText: 'Point de vente de destination',
|
labelText: 'Point de vente de destination',
|
||||||
border: OutlineInputBorder(),
|
border: OutlineInputBorder(),
|
||||||
),
|
),
|
||||||
items: pointsDeVente.map((pv) => DropdownMenuItem<int>(
|
items: pointsDeVente.map((pv) => DropdownMenuItem<int>(
|
||||||
value: pv['id'] as int,
|
value: pv['id'] as int,
|
||||||
child: Text(pv['nom'] as String),
|
child: Text(
|
||||||
|
pv['nom'] as String,
|
||||||
|
overflow: TextOverflow.ellipsis,
|
||||||
|
),
|
||||||
)).toList(),
|
)).toList(),
|
||||||
onChanged: (value) {
|
onChanged: (value) {
|
||||||
selectedPointDeVenteId = value;
|
selectedPointDeVenteId = value;
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
),
|
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
),
|
||||||
actions: [
|
actions: [
|
||||||
TextButton(
|
TextButton(
|
||||||
onPressed: () => Get.back(),
|
onPressed: () => Get.back(),
|
||||||
@ -263,20 +519,21 @@ Future<void> _showTransferDialog(int sourcePointDeVenteId) async {
|
|||||||
backgroundColor: Colors.green,
|
backgroundColor: Colors.green,
|
||||||
foregroundColor: Colors.white,
|
foregroundColor: Colors.white,
|
||||||
),
|
),
|
||||||
child: const Text('Transférer et supprimer'),
|
child: const Text('Transférer'),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
// Nouvelle méthode pour effectuer le transfert et la suppression
|
|
||||||
Future<void> _performTransferAndDelete(int sourceId, int targetId) async {
|
Future<void> _performTransferAndDelete(int sourceId, int targetId) async {
|
||||||
|
if (mounted) {
|
||||||
setState(() {
|
setState(() {
|
||||||
_isLoading = true;
|
_isLoading = true;
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// Afficher un dialog de confirmation final
|
|
||||||
final confirmed = await Get.dialog<bool>(
|
final confirmed = await Get.dialog<bool>(
|
||||||
AlertDialog(
|
AlertDialog(
|
||||||
title: const Text('Confirmation finale'),
|
title: const Text('Confirmation finale'),
|
||||||
@ -324,38 +581,97 @@ Future<void> _performTransferAndDelete(int sourceId, int targetId) async {
|
|||||||
colorText: Colors.white,
|
colorText: Colors.white,
|
||||||
);
|
);
|
||||||
} finally {
|
} finally {
|
||||||
|
if (mounted) {
|
||||||
setState(() {
|
setState(() {
|
||||||
_isLoading = false;
|
_isLoading = false;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Vous pouvez aussi ajouter une méthode pour voir les détails d'un point de vente
|
}
|
||||||
Future<void> _showPointDeVenteDetails(Map<String, dynamic> pointDeVente) async {
|
|
||||||
|
Future<void> _showPointDeVenteDetails(Map<String, dynamic> pointDeVente) async {
|
||||||
final id = pointDeVente['id'] as int;
|
final id = pointDeVente['id'] as int;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// Récupérer les statistiques
|
|
||||||
final stats = await _getPointDeVenteStats(id);
|
final stats = await _getPointDeVenteStats(id);
|
||||||
|
|
||||||
await Get.dialog(
|
await Get.dialog(
|
||||||
AlertDialog(
|
AlertDialog(
|
||||||
title: Text('Détails: ${pointDeVente['nom']}'),
|
title: Text(
|
||||||
content: SingleChildScrollView(
|
'Détails: ${pointDeVente['nom']}',
|
||||||
|
style: const TextStyle(fontSize: 16),
|
||||||
|
),
|
||||||
|
content: ConstrainedBox(
|
||||||
|
constraints: const BoxConstraints(
|
||||||
|
maxWidth: 400,
|
||||||
|
maxHeight: 500,
|
||||||
|
),
|
||||||
|
child: SingleChildScrollView(
|
||||||
child: Column(
|
child: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
children: [
|
children: [
|
||||||
|
if (pointDeVente['logo'] != null)
|
||||||
|
Container(
|
||||||
|
height: 150,
|
||||||
|
width: double.infinity,
|
||||||
|
margin: const EdgeInsets.only(bottom: 16),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
borderRadius: BorderRadius.circular(8),
|
||||||
|
border: Border.all(color: Colors.grey.shade300),
|
||||||
|
),
|
||||||
|
child: ClipRRect(
|
||||||
|
borderRadius: BorderRadius.circular(8),
|
||||||
|
child: Image.memory(
|
||||||
|
pointDeVente['logo'] as Uint8List,
|
||||||
|
fit: BoxFit.cover,
|
||||||
|
errorBuilder: (context, error, stackTrace) {
|
||||||
|
return Container(
|
||||||
|
color: Colors.grey.shade200,
|
||||||
|
child: const Center(
|
||||||
|
child: Text('Image non disponible'),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
|
||||||
_buildStatRow('Produits associés', '${stats['produits']}'),
|
_buildStatRow('Produits associés', '${stats['produits']}'),
|
||||||
_buildStatRow('Utilisateurs associés', '${stats['utilisateurs']}'),
|
_buildStatRow('Utilisateurs associés', '${stats['utilisateurs']}'),
|
||||||
_buildStatRow('Demandes de transfert', '${stats['transferts']}'),
|
_buildStatRow('Demandes de transfert', '${stats['transferts']}'),
|
||||||
const SizedBox(height: 8),
|
const SizedBox(height: 8),
|
||||||
Text(
|
if (pointDeVente['content'] != null &&
|
||||||
'Code: ${pointDeVente['code'] ?? 'N/A'}',
|
pointDeVente['content'].toString().isNotEmpty)
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.only(top: 4),
|
||||||
|
child: Text(
|
||||||
|
'Ticket: ${pointDeVente['content']}',
|
||||||
style: const TextStyle(fontSize: 12, color: Colors.grey),
|
style: const TextStyle(fontSize: 12, color: Colors.grey),
|
||||||
),
|
),
|
||||||
|
),
|
||||||
|
if (pointDeVente['livraison'] != null &&
|
||||||
|
pointDeVente['livraison'].toString().isNotEmpty)
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.only(top: 4),
|
||||||
|
child: Text(
|
||||||
|
'Bon de livraison: ${pointDeVente['livraison']}',
|
||||||
|
style: const TextStyle(fontSize: 12, color: Colors.grey),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
if (pointDeVente['facture'] != null &&
|
||||||
|
pointDeVente['facture'].toString().isNotEmpty)
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.only(top: 4),
|
||||||
|
child: Text(
|
||||||
|
'Facture: ${pointDeVente['facture']}',
|
||||||
|
style: const TextStyle(fontSize: 12, color: Colors.grey),
|
||||||
|
),
|
||||||
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
),
|
||||||
actions: [
|
actions: [
|
||||||
TextButton(
|
TextButton(
|
||||||
onPressed: () => Get.back(),
|
onPressed: () => Get.back(),
|
||||||
@ -373,23 +689,30 @@ Future<void> _showPointDeVenteDetails(Map<String, dynamic> pointDeVente) async {
|
|||||||
colorText: Colors.white,
|
colorText: Colors.white,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildStatRow(String label, String value) {
|
Widget _buildStatRow(String label, String value) {
|
||||||
return Padding(
|
return Padding(
|
||||||
padding: const EdgeInsets.symmetric(vertical: 2),
|
padding: const EdgeInsets.symmetric(vertical: 2),
|
||||||
child: Row(
|
child: Row(
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
children: [
|
children: [
|
||||||
Text(label),
|
Expanded(
|
||||||
Text(value, style: const TextStyle(fontWeight: FontWeight.bold)),
|
child: Text(
|
||||||
|
label,
|
||||||
|
style: const TextStyle(fontSize: 13),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Text(
|
||||||
|
value,
|
||||||
|
style: const TextStyle(fontWeight: FontWeight.bold, fontSize: 13),
|
||||||
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Méthode helper pour récupérer les stats
|
Future<Map<String, int>> _getPointDeVenteStats(int id) async {
|
||||||
Future<Map<String, int>> _getPointDeVenteStats(int id) async {
|
|
||||||
final verification = await _appDatabase.checkCanDeletePointDeVente(id);
|
final verification = await _appDatabase.checkCanDeletePointDeVente(id);
|
||||||
|
|
||||||
// Parser les raisons pour extraire les nombres
|
// Parser les raisons pour extraire les nombres
|
||||||
@ -410,19 +733,16 @@ Future<Map<String, int>> _getPointDeVenteStats(int id) async {
|
|||||||
'utilisateurs': utilisateurs,
|
'utilisateurs': utilisateurs,
|
||||||
'transferts': transferts,
|
'transferts': transferts,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> _deletePointDeVente(int id) async {
|
Future<void> _deletePointDeVente(int id) async {
|
||||||
// 1. D'abord vérifier si la suppression est possible
|
|
||||||
final verificationResult = await _appDatabase.checkCanDeletePointDeVente(id);
|
final verificationResult = await _appDatabase.checkCanDeletePointDeVente(id);
|
||||||
|
|
||||||
if (!verificationResult['canDelete']) {
|
if (!verificationResult['canDelete']) {
|
||||||
// Afficher un dialog avec les détails des contraintes
|
|
||||||
await _showConstraintDialog(id, verificationResult);
|
await _showConstraintDialog(id, verificationResult);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 2. Si pas de contraintes, procéder normalement
|
|
||||||
final confirmed = await Get.dialog<bool>(
|
final confirmed = await Get.dialog<bool>(
|
||||||
AlertDialog(
|
AlertDialog(
|
||||||
title: const Text('Confirmer la suppression'),
|
title: const Text('Confirmer la suppression'),
|
||||||
@ -441,9 +761,11 @@ Future<Map<String, int>> _getPointDeVenteStats(int id) async {
|
|||||||
);
|
);
|
||||||
|
|
||||||
if (confirmed == true) {
|
if (confirmed == true) {
|
||||||
|
if (mounted) {
|
||||||
setState(() {
|
setState(() {
|
||||||
_isLoading = true;
|
_isLoading = true;
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await _appDatabase.deletePointDeVente(id);
|
await _appDatabase.deletePointDeVente(id);
|
||||||
@ -465,12 +787,14 @@ Future<Map<String, int>> _getPointDeVenteStats(int id) async {
|
|||||||
colorText: Colors.white,
|
colorText: Colors.white,
|
||||||
);
|
);
|
||||||
} finally {
|
} finally {
|
||||||
|
if (mounted) {
|
||||||
setState(() {
|
setState(() {
|
||||||
_isLoading = false;
|
_isLoading = false;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
@ -503,7 +827,6 @@ Future<Map<String, int>> _getPointDeVenteStats(int id) async {
|
|||||||
),
|
),
|
||||||
const SizedBox(height: 16),
|
const SizedBox(height: 16),
|
||||||
|
|
||||||
// Champ Nom
|
|
||||||
TextFormField(
|
TextFormField(
|
||||||
controller: _nomController,
|
controller: _nomController,
|
||||||
decoration: InputDecoration(
|
decoration: InputDecoration(
|
||||||
@ -524,7 +847,6 @@ Future<Map<String, int>> _getPointDeVenteStats(int id) async {
|
|||||||
),
|
),
|
||||||
const SizedBox(height: 12),
|
const SizedBox(height: 12),
|
||||||
|
|
||||||
// Champ Code
|
|
||||||
TextFormField(
|
TextFormField(
|
||||||
controller: _codeController,
|
controller: _codeController,
|
||||||
decoration: InputDecoration(
|
decoration: InputDecoration(
|
||||||
@ -539,10 +861,8 @@ Future<Map<String, int>> _getPointDeVenteStats(int id) async {
|
|||||||
),
|
),
|
||||||
const SizedBox(height: 16),
|
const SizedBox(height: 16),
|
||||||
|
|
||||||
// Bouton de soumission
|
|
||||||
ElevatedButton(
|
ElevatedButton(
|
||||||
onPressed: _isLoading ? null : _submitForm,
|
onPressed: _isLoading ? null : _submitForm,
|
||||||
|
|
||||||
style: ElevatedButton.styleFrom(
|
style: ElevatedButton.styleFrom(
|
||||||
padding: const EdgeInsets.symmetric(vertical: 16),
|
padding: const EdgeInsets.symmetric(vertical: 16),
|
||||||
backgroundColor: Colors.blue.shade800,
|
backgroundColor: Colors.blue.shade800,
|
||||||
@ -579,7 +899,7 @@ Future<Map<String, int>> _getPointDeVenteStats(int id) async {
|
|||||||
children: [
|
children: [
|
||||||
// Barre de recherche
|
// Barre de recherche
|
||||||
Padding(
|
Padding(
|
||||||
padding: const EdgeInsets.only(bottom: 8),
|
padding: const EdgeInsets.only(bottom: 16),
|
||||||
child: TextField(
|
child: TextField(
|
||||||
controller: _searchController,
|
controller: _searchController,
|
||||||
decoration: InputDecoration(
|
decoration: InputDecoration(
|
||||||
@ -595,7 +915,7 @@ Future<Map<String, int>> _getPointDeVenteStats(int id) async {
|
|||||||
icon: const Icon(Icons.clear),
|
icon: const Icon(Icons.clear),
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
_searchController.clear();
|
_searchController.clear();
|
||||||
_loadPointsDeVente();
|
_filterPointsDeVente();
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
: null,
|
: null,
|
||||||
@ -603,49 +923,11 @@ Future<Map<String, int>> _getPointDeVenteStats(int id) async {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
||||||
// En-tête de liste
|
|
||||||
Padding(
|
|
||||||
padding: const EdgeInsets.symmetric(vertical: 8),
|
|
||||||
child: Row(
|
|
||||||
children: [
|
|
||||||
const Expanded(
|
|
||||||
flex: 2,
|
|
||||||
child: Text(
|
|
||||||
'Nom',
|
|
||||||
style: TextStyle(
|
|
||||||
fontWeight: FontWeight.bold,
|
|
||||||
color: Color.fromARGB(255, 9, 56, 95),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
const Expanded(
|
|
||||||
child: Text(
|
|
||||||
'Code',
|
|
||||||
style: TextStyle(
|
|
||||||
fontWeight: FontWeight.bold,
|
|
||||||
color: Color.fromARGB(255, 9, 56, 95),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
SizedBox(
|
|
||||||
width: 40,
|
|
||||||
child: Text(
|
|
||||||
'Actions',
|
|
||||||
style: TextStyle(
|
|
||||||
fontWeight: FontWeight.bold,
|
|
||||||
color: Color.fromARGB(255, 9, 56, 95),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
|
|
||||||
// Liste
|
// Liste
|
||||||
Expanded(
|
Expanded(
|
||||||
child: _isLoading && _pointsDeVente.isEmpty
|
child: _isLoading && _filteredPointsDeVente.isEmpty
|
||||||
? const Center(child: CircularProgressIndicator())
|
? const Center(child: CircularProgressIndicator())
|
||||||
: _pointsDeVente.isEmpty
|
: _filteredPointsDeVente.isEmpty
|
||||||
? const Center(
|
? const Center(
|
||||||
child: Column(
|
child: Column(
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
@ -663,12 +945,11 @@ Future<Map<String, int>> _getPointDeVenteStats(int id) async {
|
|||||||
),
|
),
|
||||||
)
|
)
|
||||||
: ListView.builder(
|
: ListView.builder(
|
||||||
itemCount: _pointsDeVente.length,
|
itemCount: _filteredPointsDeVente.length,
|
||||||
itemBuilder: (context, index) {
|
itemBuilder: (context, index) {
|
||||||
final point = _pointsDeVente[index];
|
final point = _filteredPointsDeVente[index];
|
||||||
final canDelete = point['canDelete'] ?? true;
|
final canDelete = point['canDelete'] ?? true;
|
||||||
final constraintCount = point['constraintCount'] ?? 0;
|
final constraintCount = point['constraintCount'] ?? 0;
|
||||||
|
|
||||||
return Card(
|
return Card(
|
||||||
margin: const EdgeInsets.only(bottom: 8),
|
margin: const EdgeInsets.only(bottom: 8),
|
||||||
elevation: 2,
|
elevation: 2,
|
||||||
@ -791,7 +1072,16 @@ Future<Map<String, int>> _getPointDeVenteStats(int id) async {
|
|||||||
onPressed: () => _showPointDeVenteDetails(point),
|
onPressed: () => _showPointDeVenteDetails(point),
|
||||||
tooltip: 'Voir les détails',
|
tooltip: 'Voir les détails',
|
||||||
),
|
),
|
||||||
|
// Bouton modifier
|
||||||
|
IconButton(
|
||||||
|
icon: Icon(
|
||||||
|
Icons.edit,
|
||||||
|
size: 20,
|
||||||
|
color: Colors.blue.shade600,
|
||||||
|
),
|
||||||
|
onPressed: () => editPointDeVente(context, point),
|
||||||
|
tooltip: 'Modifier point de vente',
|
||||||
|
),
|
||||||
// Bouton suppression avec indication visuelle
|
// Bouton suppression avec indication visuelle
|
||||||
IconButton(
|
IconButton(
|
||||||
icon: Icon(
|
icon: Icon(
|
||||||
@ -810,7 +1100,7 @@ Future<Map<String, int>> _getPointDeVenteStats(int id) async {
|
|||||||
),
|
),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
)
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
|||||||
@ -11,8 +11,7 @@ class DatabaseConfig {
|
|||||||
static const String localDatabase = 'guycom';
|
static const String localDatabase = 'guycom';
|
||||||
|
|
||||||
// Production (public) MySQL settings
|
// Production (public) MySQL settings
|
||||||
static const String prodHost = '185.70.105.157';
|
static const String prodHost = '102.17.52.31';
|
||||||
// static const String prodHost = '102.17.52.31';
|
|
||||||
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';
|
||||||
|
|||||||
@ -70,11 +70,6 @@ dependencies:
|
|||||||
window_manager: ^0.3.7
|
window_manager: ^0.3.7
|
||||||
camera: ^0.10.5+9
|
camera: ^0.10.5+9
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
flutter_test:
|
flutter_test:
|
||||||
sdk: flutter
|
sdk: flutter
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user