From 9eafda610f953d5c054d7beae1f6b101426cbb34 Mon Sep 17 00:00:00 2001 From: "b.razafimandimbihery" Date: Sat, 31 May 2025 11:25:01 +0300 Subject: [PATCH] commit fonctionnalite impec --- assets/Orange_money.png | Bin 0 -> 1570 bytes assets/airtel_money.png | Bin 0 -> 2672 bytes assets/mvola.jpg | Bin 0 -> 4573 bytes lib/Components/paymentType.dart | 7 + lib/Views/commandManagement.dart | 263 ++++++++++++++++++-------- lib/Views/mobilepage.dart | 304 ++++++++++++++++--------------- pubspec.yaml | 3 + 7 files changed, 361 insertions(+), 216 deletions(-) create mode 100644 assets/Orange_money.png create mode 100644 assets/airtel_money.png create mode 100644 assets/mvola.jpg create mode 100644 lib/Components/paymentType.dart diff --git a/assets/Orange_money.png b/assets/Orange_money.png new file mode 100644 index 0000000000000000000000000000000000000000..927252638f0cd0ffd4cac788b593c6ea7007a667 GIT binary patch literal 1570 zcmZ{k`#TeQ9LLw~R5X{PBhzu~B9}}dM+%XV%*K*bTcZ)R$W}T{D>2t?E)6lZlw3L^ z4fx4V< z#H@MO+Wfs`!`cqFrPi)liaXL10Voi#CNc#0L%^R9Fg*c6Az)Dos*}Lu>mUdM*xn!n z0*1SRLZ>^@&Kh25VlVy|26=DY&z-U zXzzK8GnqxaonyV-$!(jxDo^$KIYgT)7gk`?2Y^JWQmb&(;Xni`n<5nImUj71{}WX3 zvagFX5JnE=+HA6k_xxDst%@48NEN+EW?Q!0GWhiZi!+e)Ie?#} zcr=@(=#XStq%^*Bz@~K6;lzqKU^wIzMT;#C8>6A291P<{&N3^teV2}rR3zh_i9KyM zfhFyJxX9QU6#l!h@cq&OlUfzAZ*>0G2@93kXKqm)g1YZ=^UMGjZwq>D>@-po_NC0L zIyriduAcPZklXNy7~$~*sV_NrTPraop$xxedCa=!vw_D4VdRaFg$Aky)`A}IYfZ$q zS4hU2GFBSAEEjiJez&a;#$B#;vT0TL)tMbg?bEnDB1HH-Dkn=k2**R=qrE{H(&lHh zqWOmJ28BGl+q8pau;Q^g1)N_4H(NVAMKhZGI;5LQX)zh}llHen?-xuDra9>9p?U5% zN~&kL?7>vutKvN=ciVHx5-h6fv;8!SS5DV7Q%0FJ#v*lJDD8nYQ}rUK6t~esPhDV> znb_puuEVrViHgT;U?UoXR!U{-ZQsApKMs{-D5vKTIr?r|uo0?MjM;dk#q3Q~*IVrx zN2`i~b!SxsTC=d*u;38Y4LY@DYQ1xl#Cw%YLoYh0tJ!|1xj>XqPcZJwbXB#kl?;&Qxc+6=Cek%sIe zeO&Kkg1zd8gf~`crB4wm=?TosObT805XZHJDgYy3@E4ene7VarV+`4!|63yYsA*bcs8Z;jbRH1Ca2Ovlpi^dQa?FM1!eL zcxa+kx7kWkr5b*z*oi-6)aO;igu>~tkQ5}5^f9@)=$E^GO;y-!&&zfw*SE)YPqfLk z=k}Bb=Bd-1!*=vOVTRqmZ0Mzlx`I%jOH_vSVv2^oj4;|S#MW$PSBV>X$L6#6waDtA zyrZq74#U_X|8oqPe){6et!LOiJ5RwyAEH~-;l*aYmaLBKT>|4!Rs1}>elC5&VE2^g z*Mh?Sfb}?4W5I?-H0S=_<_La|?*}Bv{hD*DNRKUiTPEp-r$~7^ID(Zn>-B77N1^CGK@v(;Cl+fjkdb?{%#J=6OMS zzJGgHD5u4S#;DWEeOT3fQaea7?$8WPFV^D?lBJmC@f%S<9w z*8v)d+0@N0LR0odT6vstP!1+ zIAYKze}ACw?VWO54e>5sopX}+E$Vz5HNVjIow~UjfBQ`10_%C*e%vL~7sBm!)#Kfn z%Xa2hS2b#RsH|}z;{8}Su78)d(Ur-<_*6E(W9(2@Fw2gshE&RPrP$W&?a##&d+40* zHyCFuH|+1Z-FZhNzr(M2RG|wO&dT-Gn#wPK99s`#jF$8cneMZ~31o6n++Qf>*&!HI z7P@r*x@jAl?ALzNQr>+yFB7ciu0lPP(FJHG{2FQ5RkV1fQ&2P)jYIL5xu=`bY#XRh z@A;$9XcgZB=fmav(wl+l-G5|F7_5jt%qkU-hF(Qj{;coT@_YO^uuf0^0!e&NC-TRg M3j*a>?BEmmKlJq<@Bjb+ literal 0 HcmV?d00001 diff --git a/assets/airtel_money.png b/assets/airtel_money.png new file mode 100644 index 0000000000000000000000000000000000000000..b69f3b1bcddc8cbc76e8106522f2246cccab8fc9 GIT binary patch literal 2672 zcmai$c{~#iAIEp(o^pniGa(!MF;VVoQF7#nP!m=}Ig%^pE}Jt;?jsouxpQT<ouH>LNey30?pIz;9u0di9XB!y$8n z4>v~tQTrk8+gRG0F;AXmv)OxNbmp-WOn|_;zUfa#mlXhjDK5JzbaEBIu|w+Ip-}&> zt^F=7WAE*4;j`B?b$&g5vXxix=S}n9#l?foO;&is8cbvRRptKF)VhiJ?uUM+0+baT z!t(apu(AK??)fV<{a19{c1<0j2qppm@VvD!HL?%QXBK#elwN=&@|>%qSvi5wHxJnN z-@cK(Y&X1w_xc(UYCy9vIk9i=l;ugeQ_jw4#OOt9ZebGKcWXrQ5?+Dl#LiaSc0Umt zRbsBe7t}MMCZaD5_gRj%b>K=SUO?H80ZWNT#T|ml6+|%7;S`+szoNey;>FyyPTM+c zAfy-0L$M(!$i>UFO=zw2zq3G;GUc+~Z+4k62oGRm7Maf7X3h#YD2qR8tfP`#1gaP3-B9JRo(b(G7OAKVf) zmuj$5KZ=liXVVh3B9vJ_M6E@;h!fpNgMq^%2~%Nq;@2NyD1q4Ls`)Ndzz^Z7(Dieq`*B2gS5!!Xm-0@nHYoSC#uQgpPabbG?^ntL z6{FD|zg81zmI1{jx^1x|pE)_3i@-VB2z2MOtT=5~s2a!GZHbQI?+jBs?(%RYz8d%_ zSL4R$+r9qMOO_HYE;mXiX?bq?wY3K+&ZgtSJmc4A+9a|E3Cg+K_r&^^M9Wk}2DNYi zfsWps0JEMN@N32(P;~2Pq<6A%lUs2TqUgNEtjMGY2@qQ!&tQtXCgE1=Ln-)gO>X)o z0iSNH5W40zY$tYYMI3t`yU*n->P@8+JNaZn-Xlc}!_GXHnxPjOxc4hJ@%GQ*bOh5@NPX1U#^eqPn^MVCl3E;Cz^ z2{e&TRNm+^9_kk6V-qordZ>9hHBHMMgK4s8Fd4Nl*>ei@B%+}oj{vJfGpec^)(jf? zXb+x=X37fKx0%xyKS1}$$kQVt3^mENx(=0>t4d|uSgC;d1Vv_P25{efz- zqHSN;&2{+q(Kf*#$9gvsar2eu5`ggK@B%dJzW2y+*jcQ_s_sRNhSr}m(`m=YVeX6L zB6j#1`BGiFr9sD@_SrS$4$nsd{F-y-l^b{W(-{o^&<0t)>vr4jzf0*2SKQ|ZR+Ph* z9FO>W{CL3oqaTAvnT`K3ZlHYh9&cva4y#{Z{m!k6+L>Sg{}GKfYJ4NK)cIEXWQ6!YN%vcuqe3I3jX$>QB4w=afCG~Z+_K$? zp2BohYa%DauP}oB;_1|`Xr!HZbw?A2wq8f14Y~ND$E9IG2UPloTj3Aw(qMI(`&S-L z0zF7AKX?<)03VW!ls%b{WX80LZ$%=a*C=aN4CHBz`x$LV@HKQu-WaJf^NtETr zd&5HauI$pfr(rh{JJzNMV0ey241^_Wh9&!Bjw6EIt_51f7oi9?)80czvXMs(!+$#K zbo-D2eUW+Hu6+!6nm=bBP9@2h!C%%%57I;dh{v>Gm|pc3se*ibytkz&s^@q@XF@CK z*LXu#1t1P7{qXEANsSbdlhYCUt?q?IR%MUZxzy+K8`(pD-*}zwm?h>e=2KTKo**p)}|6GEk8E_V) zk*alG7&|15N=Mu}9jI-0(0oKUpTa#;&kXZ*T$W!xc21wMiMDfl1&h1H7DB!w{ZS^( zD_HY)1%F?PltMTVdi6libqMNO>3#^jI5@D+GePRIWF!Ppwqt_%B4QgA7&Vr8B%ie$`89)27wp25hMvr^holsdq%mg2kQVz7a+; zai$PAoS@`v_YNiHL(sS2Y9%UYcp%Et#kuINU2633@7a(aTIHX)kS*7R^9V`Onx`{T z`oJSNuGCQQT4y(g-(X*i-IBzP+MXWwz51VLnxzE>p3!|t@q|PRN~p{M_Mbgy-*0Kb z@bzd~%t$VpUX501HhvkbGvt9L9QZE?6z8$3!WHX zXZ*B{-qf+Ehxvz6O#iSPKE0&4eRp8t_N2NF<@Dz(; z$C7SgoAeH#qxX!k^Fh~byj0E1&k=mpmqr$^iLBD~&cGc<1uN>5d2gSAG7Q z6>V`6WEaJ-hz58tYTuY+3kn&*l#N4a>x@I8Iskv>q6FqHo+&$2NPvZzmFa6^RP4V1 DuK5+U literal 0 HcmV?d00001 diff --git a/assets/mvola.jpg b/assets/mvola.jpg new file mode 100644 index 0000000000000000000000000000000000000000..ec35389432717cddbf765a05af5a5db17b8843cd GIT binary patch literal 4573 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 literal 0 HcmV?d00001 diff --git a/lib/Components/paymentType.dart b/lib/Components/paymentType.dart new file mode 100644 index 0000000..190d5f0 --- /dev/null +++ b/lib/Components/paymentType.dart @@ -0,0 +1,7 @@ +enum PaymentType { + cash, + card, + mvola, + orange, + airtel +} \ No newline at end of file diff --git a/lib/Views/commandManagement.dart b/lib/Views/commandManagement.dart index 47b7b48..8055196 100644 --- a/lib/Views/commandManagement.dart +++ b/lib/Views/commandManagement.dart @@ -1563,7 +1563,12 @@ class _CommandeActions extends StatelessWidget { } } -enum PaymentType { cash, card } +enum PaymentType { cash, card , + + mvola, + orange, + airtel +} class PaymentMethod { final PaymentType type; @@ -1584,7 +1589,28 @@ class PaymentMethodDialog extends StatefulWidget { class _PaymentMethodDialogState extends State { PaymentType _selectedPayment = PaymentType.cash; final _amountController = TextEditingController(); + void _validatePayment() { + if (_selectedPayment == PaymentType.cash) { + final amountGiven = double.tryParse(_amountController.text) ?? 0; + if (amountGiven < widget.commande.montantTotal) { + Get.snackbar( + 'Erreur', + 'Le montant donné est insuffisant', + snackPosition: SnackPosition.BOTTOM, + backgroundColor: Colors.red, + colorText: Colors.white, + ); + return; + } + } + Navigator.pop(context, PaymentMethod( + type: _selectedPayment, + amountGiven: _selectedPayment == PaymentType.cash + ? double.parse(_amountController.text) + : widget.commande.montantTotal, + )); +} @override void initState() { super.initState(); @@ -1598,94 +1624,189 @@ class _PaymentMethodDialogState extends State { super.dispose(); } + @override - Widget build(BuildContext context) { - final amount = double.tryParse(_amountController.text) ?? 0; - final change = amount - widget.commande.montantTotal; +Widget build(BuildContext context) { + final amount = double.tryParse(_amountController.text) ?? 0; + final change = amount - widget.commande.montantTotal; - return AlertDialog( - title: const Text('Méthode de paiement'), - content: Column( + return AlertDialog( + title: const Text('Méthode de paiement', style: TextStyle(fontWeight: FontWeight.bold)), + content: SingleChildScrollView( + child: Column( mainAxisSize: MainAxisSize.min, children: [ - RadioListTile( - title: const Text('Paiement en liquide'), + // Section Paiement mobile + const Align( + alignment: Alignment.centerLeft, + child: Text('Mobile Money', style: TextStyle(fontWeight: FontWeight.w500)), + ), + const SizedBox(height: 8), + Row( + children: [ + Expanded( + child: _buildMobileMoneyTile( + title: 'Mvola', + imagePath: 'assets/mvola.jpg', + value: PaymentType.mvola, + ), + ), + const SizedBox(width: 8), + Expanded( + child: _buildMobileMoneyTile( + title: 'Orange Money', + imagePath: 'assets/Orange_money.png', + value: PaymentType.orange, + ), + ), + const SizedBox(width: 8), + Expanded( + child: _buildMobileMoneyTile( + title: 'Airtel Money', + imagePath: 'assets/airtel_money.png', + value: PaymentType.airtel, + ), + ), + ], + ), + const SizedBox(height: 16), + + // Section Carte bancaire + const Align( + alignment: Alignment.centerLeft, + child: Text('Carte Bancaire', style: TextStyle(fontWeight: FontWeight.w500)), + ), + const SizedBox(height: 8), + _buildPaymentMethodTile( + title: 'Carte bancaire', + icon: Icons.credit_card, + value: PaymentType.card, + ), + const SizedBox(height: 16), + + // Section Paiement en liquide + const Align( + alignment: Alignment.centerLeft, + child: Text('Espèces', style: TextStyle(fontWeight: FontWeight.w500)), + ), + const SizedBox(height: 8), + _buildPaymentMethodTile( + title: 'Paiement en liquide', + icon: Icons.money, value: PaymentType.cash, - groupValue: _selectedPayment, - onChanged: (value) { - setState(() { - _selectedPayment = value!; - }); - }, ), - if (_selectedPayment == PaymentType.cash) + if (_selectedPayment == PaymentType.cash) ...[ + const SizedBox(height: 12), TextField( controller: _amountController, decoration: const InputDecoration( labelText: 'Montant donné', prefixText: 'MGA ', + border: OutlineInputBorder(), ), - keyboardType: TextInputType.number, - onChanged: (value) { - setState(() {}); // Rafraîchir l'UI pour calculer la monnaie - }, + keyboardType: TextInputType.numberWithOptions(decimal: true), + onChanged: (value) => setState(() {}), ), - RadioListTile( - title: const Text('Carte bancaire'), - value: PaymentType.card, - groupValue: _selectedPayment, - onChanged: (value) { - setState(() { - _selectedPayment = value!; - }); - }, - ), - if (_selectedPayment == PaymentType.cash) - Padding( - padding: const EdgeInsets.only(top: 8.0), - child: Text( - 'Monnaie à rendre: ${change.toStringAsFixed(2)} MGA', - style: TextStyle( - fontSize: 16, - fontWeight: FontWeight.bold, - color: change >= 0 ? Colors.green : Colors.red, - ), + const SizedBox(height: 8), + Text( + 'Monnaie à rendre: ${change.toStringAsFixed(2)} MGA', + style: TextStyle( + fontSize: 16, + fontWeight: FontWeight.bold, + color: change >= 0 ? Colors.green : Colors.red, ), ), + ], ], ), - actions: [ - TextButton( - onPressed: () => Navigator.pop(context), - child: const Text('Annuler'), + ), + actions: [ + TextButton( + onPressed: () => Navigator.pop(context), + child: const Text('Annuler', style: TextStyle(color: Colors.grey)), + ), + ElevatedButton( + style: ElevatedButton.styleFrom( + backgroundColor: Colors.blue.shade800, + foregroundColor: Colors.white, ), - ElevatedButton( - onPressed: () { - final amount = _selectedPayment == PaymentType.cash - ? double.tryParse(_amountController.text) ?? 0 - : widget.commande.montantTotal; - - if (_selectedPayment == PaymentType.cash && amount < widget.commande.montantTotal) { - ScaffoldMessenger.of(context).showSnackBar( - const SnackBar( - content: Text('Le montant donné est insuffisant'), - backgroundColor: Colors.red, - ), - ); - return; - } + onPressed: _validatePayment, + child: const Text('Confirmer'), + ), + ], + ); +} - Navigator.pop( - context, - PaymentMethod( - type: _selectedPayment, - amountGiven: amount, - ), - ); - }, - child: const Text('Confirmer'), +Widget _buildMobileMoneyTile({ + required String title, + required String imagePath, + required PaymentType value, +}) { + return Card( + elevation: 2, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(8), + side: BorderSide( + color: _selectedPayment == value ? Colors.blue : Colors.grey.withOpacity(0.2), + width: 2, + ), + ), + child: InkWell( + borderRadius: BorderRadius.circular(8), + onTap: () => setState(() => _selectedPayment = value), + child: Padding( + padding: const EdgeInsets.all(12), + child: Column( + children: [ + Image.asset( + imagePath, + height: 30, + width: 30, + fit: BoxFit.contain, + errorBuilder: (context, error, stackTrace) => + const Icon(Icons.mobile_friendly, size: 30), + ), + const SizedBox(height: 8), + Text( + title, + textAlign: TextAlign.center, + style: const TextStyle(fontSize: 12), + ), + ], ), - ], - ); - } + ), + ), + ); +} + +Widget _buildPaymentMethodTile({ + required String title, + required IconData icon, + required PaymentType value, +}) { + return Card( + elevation: 2, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(8), + side: BorderSide( + color: _selectedPayment == value ? Colors.blue : Colors.grey.withOpacity(0.2), + width: 2, + ), + ), + child: InkWell( + borderRadius: BorderRadius.circular(8), + onTap: () => setState(() => _selectedPayment = value), + child: Padding( + padding: const EdgeInsets.all(12), + child: Row( + children: [ + Icon(icon, size: 24), + const SizedBox(width: 12), + Text(title), + ], + ), + ), + ), + ); +} } diff --git a/lib/Views/mobilepage.dart b/lib/Views/mobilepage.dart index 420dd13..902720e 100644 --- a/lib/Views/mobilepage.dart +++ b/lib/Views/mobilepage.dart @@ -258,14 +258,31 @@ class _NouvelleCommandePageState extends State { } void _showClientFormDialog() { - Get.dialog( - AlertDialog( - title: const Text('Informations Client'), - content: SingleChildScrollView( + Get.dialog( + AlertDialog( + title: Row( + children: [ + Container( + padding: const EdgeInsets.all(8), + decoration: BoxDecoration( + color: Colors.blue.shade100, + borderRadius: BorderRadius.circular(8), + ), + child: Icon(Icons.person_add, color: Colors.blue.shade700), + ), + const SizedBox(width: 12), + const Text('Informations Client'), + ], + ), + content: Container( + width: 600, + constraints: const BoxConstraints(maxHeight: 600), + child: SingleChildScrollView( child: Form( key: _formKey, child: Column( mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, children: [ _buildTextFormField( controller: _nomController, @@ -307,38 +324,35 @@ class _NouvelleCommandePageState extends State { ), const SizedBox(height: 12), _buildCommercialDropdown(), - ], + ], ), ), ), - actions: [ - TextButton( - onPressed: () => Get.back(), - child: const Text('Annuler'), - ), - ElevatedButton( - style: ElevatedButton.styleFrom( - backgroundColor: Colors.blue.shade800, - foregroundColor: Colors.white, - ), - onPressed: () { - if (_formKey.currentState!.validate()) { - Get.back(); - Get.snackbar( - 'Succès', - 'Informations client enregistrées', - snackPosition: SnackPosition.BOTTOM, - backgroundColor: Colors.green, - colorText: Colors.white, - ); - } - }, - child: const Text('Enregistrer'), - ), - ], ), - ); - } + actions: [ + TextButton( + onPressed: () => Get.back(), + child: const Text('Annuler'), + ), + ElevatedButton( + style: ElevatedButton.styleFrom( + backgroundColor: Colors.blue.shade800, + foregroundColor: Colors.white, + padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 12), + ), + onPressed: () { + if (_formKey.currentState!.validate()) { + Get.back(); + // Au lieu d'afficher juste un message, on valide directement la commande + _submitOrder(); + } + }, + child: const Text('Valider la commande'), // Changement de texte ici + ), + ], + ), + ); +} Widget _buildTextFormField({ required TextEditingController controller, @@ -464,7 +478,7 @@ class _NouvelleCommandePageState extends State { children: [ const SizedBox(height: 4), Text( - '${product.price.toStringAsFixed(2)} DA', + '${product.price.toStringAsFixed(2)} MGA', style: TextStyle( color: Colors.green.shade700, fontWeight: FontWeight.w600, @@ -626,9 +640,9 @@ class _NouvelleCommandePageState extends State { child: const Icon(Icons.shopping_bag, size: 20), ), title: Text(product.name), - subtitle: Text('${entry.value} x ${product.price.toStringAsFixed(2)} DA'), + subtitle: Text('${entry.value} x ${product.price.toStringAsFixed(2)} MGA'), trailing: Text( - '${(entry.value * product.price).toStringAsFixed(2)} DA', + '${(entry.value * product.price).toStringAsFixed(2)} MGA', style: TextStyle( fontWeight: FontWeight.bold, color: Colors.blue.shade800, @@ -658,7 +672,7 @@ class _NouvelleCommandePageState extends State { style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold), ), Text( - '${total.toStringAsFixed(2)} DA', + '${total.toStringAsFixed(2)} MGA', style: const TextStyle( fontSize: 18, fontWeight: FontWeight.bold, @@ -708,122 +722,122 @@ class _NouvelleCommandePageState extends State { } Future _submitOrder() async { - if (_nomController.text.isEmpty || - _prenomController.text.isEmpty || - _emailController.text.isEmpty || - _telephoneController.text.isEmpty || - _adresseController.text.isEmpty) { - Get.back(); // Ferme le bottom sheet - Get.snackbar( - 'Informations manquantes', - 'Veuillez remplir les informations client', - snackPosition: SnackPosition.BOTTOM, - backgroundColor: Colors.red, - colorText: Colors.white, - ); - _showClientFormDialog(); - return; - } - - final itemsInCart = _quantites.entries.where((e) => e.value > 0).toList(); - if (itemsInCart.isEmpty) { - Get.snackbar( - 'Panier vide', - 'Veuillez ajouter des produits à votre commande', - snackPosition: SnackPosition.BOTTOM, - backgroundColor: Colors.red, - colorText: Colors.white, - ); - return; - } - - setState(() { - _isLoading = true; - }); + // Vérifier d'abord si le panier est vide + final itemsInCart = _quantites.entries.where((e) => e.value > 0).toList(); + if (itemsInCart.isEmpty) { + Get.snackbar( + 'Panier vide', + 'Veuillez ajouter des produits à votre commande', + snackPosition: SnackPosition.BOTTOM, + backgroundColor: Colors.red, + colorText: Colors.white, + ); + _showCartBottomSheet(); // Ouvrir le panier pour montrer qu'il est vide + return; + } - // Créer le client - final client = Client( - nom: _nomController.text, - prenom: _prenomController.text, - email: _emailController.text, - telephone: _telephoneController.text, - adresse: _adresseController.text, - dateCreation: DateTime.now(), + // Ensuite vérifier les informations client + if (_nomController.text.isEmpty || + _prenomController.text.isEmpty || + _emailController.text.isEmpty || + _telephoneController.text.isEmpty || + _adresseController.text.isEmpty) { + Get.snackbar( + 'Informations manquantes', + 'Veuillez remplir les informations client', + snackPosition: SnackPosition.BOTTOM, + backgroundColor: Colors.red, + colorText: Colors.white, ); + _showClientFormDialog(); + return; + } - // Calculer le total et préparer les détails - double total = 0; - final details = []; - - for (final entry in itemsInCart) { - final product = _products.firstWhere((p) => p.id == entry.key); - total += entry.value * product.price; - - details.add(DetailCommande( - commandeId: 0, - produitId: product.id!, - quantite: entry.value, - prixUnitaire: product.price, - sousTotal: entry.value * product.price, - )); - } + setState(() { + _isLoading = true; + }); + + // Créer le client + final client = Client( + nom: _nomController.text, + prenom: _prenomController.text, + email: _emailController.text, + telephone: _telephoneController.text, + adresse: _adresseController.text, + dateCreation: DateTime.now(), + ); + + // Calculer le total et préparer les détails + double total = 0; + final details = []; + + for (final entry in itemsInCart) { + final product = _products.firstWhere((p) => p.id == entry.key); + total += entry.value * product.price; + + details.add(DetailCommande( + commandeId: 0, + produitId: product.id!, + quantite: entry.value, + prixUnitaire: product.price, + sousTotal: entry.value * product.price, + )); + } - // Créer la commande - final commande = Commande( - clientId: 0, - dateCommande: DateTime.now(), - statut: StatutCommande.enAttente, - montantTotal: total, - notes: 'Commande passée via l\'application', - commandeurId: _selectedCommercialUser?.id, + // Créer la commande + final commande = Commande( + clientId: 0, + dateCommande: DateTime.now(), + statut: StatutCommande.enAttente, + montantTotal: total, + notes: 'Commande passée via l\'application', + commandeurId: _selectedCommercialUser?.id, + ); + + try { + await _appDatabase.createCommandeComplete(client, commande, details); + + // Afficher le dialogue de confirmation + await showDialog( + context: context, + builder: (context) => AlertDialog( + title: const Text('Commande Validée'), + content: const Text('Votre commande a été enregistrée et expédiée avec succès.'), + actions: [ + TextButton( + onPressed: () { + Navigator.pop(context); + // Réinitialiser le formulaire + _nomController.clear(); + _prenomController.clear(); + _emailController.clear(); + _telephoneController.clear(); + _adresseController.clear(); + setState(() { + _quantites.clear(); + _isLoading = false; + }); + }, + child: const Text('OK'), + ), + ], + ), ); - try { - await _appDatabase.createCommandeComplete(client, commande, details); - - Get.back(); // Ferme le bottom sheet - - // Afficher le dialogue de confirmation - await showDialog( - context: context, - builder: (context) => AlertDialog( - title: const Text('Commande Validée'), - content: const Text('Votre commande a été enregistrée avec succès.'), - actions: [ - TextButton( - onPressed: () { - Navigator.pop(context); - // Réinitialiser le formulaire - _nomController.clear(); - _prenomController.clear(); - _emailController.clear(); - _telephoneController.clear(); - _adresseController.clear(); - setState(() { - _quantites.clear(); - _isLoading = false; - }); - }, - child: const Text('OK'), - ), - ], - ), - ); - - } catch (e) { - setState(() { - _isLoading = false; - }); - - Get.snackbar( - 'Erreur', - 'Une erreur est survenue: ${e.toString()}', - snackPosition: SnackPosition.BOTTOM, - backgroundColor: Colors.red, - colorText: Colors.white, - ); - } + } catch (e) { + setState(() { + _isLoading = false; + }); + + Get.snackbar( + 'Erreur', + 'Une erreur est survenue: ${e.toString()}', + snackPosition: SnackPosition.BOTTOM, + backgroundColor: Colors.red, + colorText: Colors.white, + ); } +} @override void dispose() { diff --git a/pubspec.yaml b/pubspec.yaml index b62d35b..80c52e5 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -102,6 +102,9 @@ flutter: - assets/database/usersdb.db - assets/database/work.db - assets/database/roles.db + - assets/airtel_money.png + - assets/mvola.jpg + - assets/Orange_money.png # An image asset can refer to one or more resolution-specific "variants", see # https://flutter.dev/assets-and-images/#resolution-aware