ソースを参照

Eliminacion de pedidos y mostrar mesa en grid

OscarGil03 6 ヶ月 前
コミット
35d6312c17

+ 4 - 0
lib/models/pedido_producto_model.dart

@@ -26,6 +26,7 @@ class PedidoProducto extends Basico {
     this.toppings = const [],
     this.idWeb,
     this.sincronizado,
+    super.eliminado,
   });
 
   @override
@@ -41,6 +42,7 @@ class PedidoProducto extends Basico {
       'comentario': comentario,
       'idWeb': idWeb,
       'sincronizado': sincronizado,
+      'eliminado': eliminado?.toIso8601String(),
     }..addAll(super.toJson());
   }
 
@@ -51,7 +53,9 @@ class PedidoProducto extends Basico {
       'costoUnitario': costoUnitario,
       'descuento': descuento,
       'cantidad': cantidad,
+      'idWeb': idWeb,
       'comentario': comentario,
+      'eliminado': eliminado,
       'toppings': toppings.map((topping) => topping.toApi()).toList(),
     };
   }

+ 34 - 0
lib/services/repo_service.dart

@@ -91,6 +91,14 @@ class RepoService<T> {
       'idLocal': -1,
     });
 
+    await db.insert('Variable', {
+      'nombre': 'Mesas',
+      'clave': 'MESAS',
+      'descripcion': 'Uso de mesas en el punto de venta',
+      'activo': 1,
+      'idLocal': -1,
+    });
+
     await db.insert('Descuento', {'porcentaje': 0});
     await db.insert('Descuento', {'porcentaje': 5});
     await db.insert('Descuento', {'porcentaje': 10});
@@ -627,6 +635,14 @@ class RepoService<T> {
           ALTER TABLE Usuario ADD COLUMN token TEXT;
         ''');
 
+          await db.insert('Variable', {
+            'nombre': 'Mesas',
+            'clave': 'MESAS',
+            'descripcion': 'Uso de mesas en el punto de venta',
+            'activo': 1,
+            'idLocal': -1,
+          });
+
           break;
       }
       oldVersion++;
@@ -753,6 +769,24 @@ class RepoService<T> {
     }
   }
 
+  Future<PedidoProducto?> obtenerPorIdPedidoYProducto({
+    required int idPedido,
+    required int idProducto,
+  }) async {
+    Database? dbClient = await db;
+    List<Map<String, dynamic>> maps = await dbClient!.query(
+      'PedidoProducto',
+      where: 'idPedido = ? AND idProducto = ?',
+      whereArgs: [idPedido, idProducto],
+    );
+
+    if (maps.isNotEmpty) {
+      return PedidoProducto.fromJson(Map<String, dynamic>.from(maps.first));
+    }
+
+    return null; // Retorna null si no se encuentra ningún registro
+  }
+
   void asignarFechasLocalmente(Basico model) {
     DateTime ahora = DateTime.now();
     if (model.creado == null) {

+ 19 - 0
lib/viewmodels/pedido_view_model.dart

@@ -177,6 +177,23 @@ class PedidoViewModel extends ChangeNotifier {
     }
   }
 
+  Future<void> eliminarProductoDelPedido(int idProducto, int idPedido) async {
+    RepoService<PedidoProducto> repoPedidoProducto =
+        RepoService<PedidoProducto>();
+
+    PedidoProducto? producto =
+        await repoPedidoProducto.obtenerPorIdPedidoYProducto(
+      idPedido: idPedido,
+      idProducto: idProducto,
+    );
+
+    if (producto != null) {
+      producto.eliminado = DateTime.now().toUtc();
+      await repoPedidoProducto.guardar(producto);
+      notifyListeners();
+    }
+  }
+
   Future<void> fetchLocalPedidosForScreen({int page = 1}) async {
     setIsLoading(true);
     RepoService<Pedido> repoPedido = RepoService<Pedido>();
@@ -224,6 +241,8 @@ class PedidoViewModel extends ChangeNotifier {
       List<PedidoProducto> productos =
           await repoProducto.obtenerPorIdPedido(idPedido);
 
+      productos = productos.where((p) => p.eliminado == null).toList();
+
       for (var producto in productos) {
         Producto? prodInfo =
             await repoProductoInfo.obtenerProductoPorId(producto.idProducto!);

+ 262 - 30
lib/views/pedido/pedido_form.dart

@@ -4,6 +4,7 @@ import 'package:flutter/foundation.dart';
 import 'package:flutter/material.dart';
 import 'package:flutter/services.dart';
 import 'package:intl/intl.dart';
+import 'package:otp/otp.dart';
 import 'package:provider/provider.dart';
 import '/data/session/session_storage.dart';
 import '/views/pedido/pedido_ticket.dart';
@@ -13,6 +14,8 @@ import '../../viewmodels/viewmodels.dart';
 import 'package:collection/collection.dart';
 import '../../widgets/widgets.dart';
 import '../../services/services.dart';
+import 'package:timezone/data/latest.dart' as timezone;
+import 'package:timezone/timezone.dart' as timezone;
 
 class PedidoForm extends StatefulWidget {
   final Pedido? pedidoExistente;
@@ -346,6 +349,15 @@ class _PedidoFormState extends State<PedidoForm> {
             TextButton(
               onPressed: () => Navigator.of(context).pop(false),
               child: const Text('Cancelar'),
+              style: ButtonStyle(
+                  padding: MaterialStatePropertyAll(
+                      EdgeInsets.fromLTRB(20, 10, 20, 10)),
+                  backgroundColor: MaterialStatePropertyAll(Colors.red),
+                  foregroundColor:
+                      MaterialStatePropertyAll(AppTheme.secondary)),
+            ),
+            const SizedBox(
+              width: 10,
             ),
             TextButton(
               onPressed: () {
@@ -360,6 +372,12 @@ class _PedidoFormState extends State<PedidoForm> {
                 }
               },
               child: const Text('Guardar'),
+              style: ButtonStyle(
+                  padding: MaterialStatePropertyAll(
+                      EdgeInsets.fromLTRB(20, 10, 20, 10)),
+                  backgroundColor: MaterialStatePropertyAll(Colors.black),
+                  foregroundColor:
+                      MaterialStatePropertyAll(AppTheme.quaternary)),
             ),
           ],
         );
@@ -434,8 +452,11 @@ class _PedidoFormState extends State<PedidoForm> {
     );
 
     if (result) {
+      Pedido? pedidoCompleto =
+          await Provider.of<PedidoViewModel>(context, listen: false)
+              .fetchPedidoConProductos(pedidoActual!.id);
       if (estatus == "TERMINADO") {
-        imprimirTicketsJuntos(context, pedidoActual!);
+        imprimirTicketsJuntos(context, pedidoCompleto!);
       }
       print("Pedido actualizado correctamente");
       Navigator.of(context).pop();
@@ -1295,6 +1316,10 @@ class _PedidoFormState extends State<PedidoForm> {
               },
             ),
           ),
+          if (pedidoActual != null && pedidoActual!.id! > 0)
+            _buildMesaSelector(),
+          if (pedidoActual != null && pedidoActual!.id! > 0)
+            const SizedBox(height: 5),
           _buildDiscountSection(),
           const Divider(thickness: 5),
           _buildTotalSection(),
@@ -1330,7 +1355,7 @@ class _PedidoFormState extends State<PedidoForm> {
                     ),
                   ),
                 ] else ...[
-                  if (_isMesasActive) ...[
+                  if (_isMesasActive)
                     ElevatedButton(
                       onPressed: () => _crearPedidoConModal(),
                       style: ElevatedButton.styleFrom(
@@ -1339,24 +1364,23 @@ class _PedidoFormState extends State<PedidoForm> {
                         fixedSize: const Size(250, 50),
                       ),
                       child: Text(
-                        'Crear Pedido',
+                        'Iniciar Pedido',
                         style: TextStyle(color: AppTheme.quaternary),
                       ),
                     ),
-                  ] else ...[
-                    ElevatedButton(
-                      onPressed: () => _promptForCustomerName(),
-                      style: ElevatedButton.styleFrom(
-                        backgroundColor: AppTheme.tertiary,
-                        textStyle: const TextStyle(fontSize: 22),
-                        fixedSize: const Size(250, 50),
-                      ),
-                      child: Text(
-                        'Finalizar Pedido',
-                        style: TextStyle(color: AppTheme.quaternary),
-                      ),
+                  const SizedBox(height: 5),
+                  ElevatedButton(
+                    onPressed: () => _promptForCustomerName(),
+                    style: ElevatedButton.styleFrom(
+                      backgroundColor: AppTheme.rojo,
+                      textStyle: const TextStyle(fontSize: 18),
+                      fixedSize: const Size(250, 50),
+                    ),
+                    child: Text(
+                      'Finalizar Pedido Sin Mesa',
+                      style: TextStyle(color: AppTheme.quaternary),
                     ),
-                  ]
+                  ),
                 ]
               ],
             ),
@@ -1366,11 +1390,42 @@ class _PedidoFormState extends State<PedidoForm> {
     );
   }
 
-  void eliminarProductoDelCarrito(int index) {
-    setState(() {
-      carrito.removeAt(index);
-    });
-    _recalcularTotal();
+  void eliminarProductoDelCarrito(int index) async {
+    bool autorizado = true;
+
+    // Solicitar autenticación solo si el pedido tiene un ID mayor a 0
+    if (pedidoActual != null && pedidoActual!.id! > 0) {
+      autorizado = await autenticarConCodigo(context);
+    }
+
+    if (autorizado) {
+      final producto = carrito[index].producto;
+
+      // Verificar si el pedido actual tiene un ID mayor a 0
+      if (pedidoActual != null && pedidoActual!.id! > 0) {
+        final pedidoProducto = pedidoActual!.productos
+            .firstWhereOrNull((p) => p.idProducto == producto.id);
+
+        if (pedidoProducto != null) {
+          // Marcar como eliminado en la base de datos
+          await Provider.of<PedidoViewModel>(context, listen: false)
+              .eliminarProductoDelPedido(producto.id!, pedidoActual!.id!);
+        }
+      }
+
+      // Remover el producto del carrito en la UI
+      setState(() {
+        carrito.removeAt(index);
+      });
+
+      // Recalcular el total
+      _recalcularTotal();
+
+      // Mostrar mensaje de confirmación
+      ScaffoldMessenger.of(context).showSnackBar(
+        const SnackBar(content: Text("Producto eliminado del carrito.")),
+      );
+    }
   }
 
   void incrementarProducto(ItemCarrito item) {
@@ -1380,15 +1435,44 @@ class _PedidoFormState extends State<PedidoForm> {
     _recalcularTotal();
   }
 
-  void quitarProductoDelCarrito(ItemCarrito item) {
-    setState(() {
-      if (item.cantidad > 1) {
-        item.cantidad--;
-      } else {
-        carrito.remove(item);
-      }
-    });
-    _recalcularTotal();
+  void quitarProductoDelCarrito(ItemCarrito item) async {
+    bool autorizado = true;
+
+    if (pedidoActual != null && pedidoActual!.id! > 0) {
+      autorizado = await autenticarConCodigo(context);
+    }
+
+    if (autorizado) {
+      setState(() {
+        if (item.cantidad > 1) {
+          item.cantidad--;
+        } else {
+          if (pedidoActual != null && pedidoActual!.id! > 0) {
+            final pedidoProducto = pedidoActual!.productos
+                .firstWhereOrNull((p) => p.idProducto == item.producto.id);
+
+            if (pedidoProducto != null) {
+              Provider.of<PedidoViewModel>(context, listen: false)
+                  .eliminarProductoDelPedido(
+                      item.producto.id!, pedidoActual!.id!);
+            }
+          }
+
+          carrito.remove(item);
+        }
+      });
+
+      _recalcularTotal();
+
+      // Mostrar mensaje de confirmación
+      ScaffoldMessenger.of(context).showSnackBar(
+        SnackBar(
+          content: Text(item.cantidad > 0
+              ? "Cantidad del producto actualizada."
+              : "Producto marcado como eliminado."),
+        ),
+      );
+    }
   }
 
   Widget _buildProductsSection() {
@@ -1542,4 +1626,152 @@ class _PedidoFormState extends State<PedidoForm> {
       ),
     );
   }
+
+  Future<bool> autenticarConCodigo(BuildContext context) async {
+    final TextEditingController codeController = TextEditingController();
+
+    return await showDialog<bool>(
+          context: context,
+          builder: (context) {
+            return AlertDialog(
+              title: const Text("Autenticación requerida",
+                  style: TextStyle(fontWeight: FontWeight.w500, fontSize: 22)),
+              content: Column(
+                mainAxisSize: MainAxisSize.min,
+                children: [
+                  const Text(
+                    "Por favor, introduce el código de autenticación generado.",
+                    style: TextStyle(fontSize: 18),
+                  ),
+                  const SizedBox(height: 16),
+                  TextField(
+                    controller: codeController,
+                    decoration: const InputDecoration(
+                      labelText: "Código de autenticación",
+                    ),
+                  ),
+                ],
+              ),
+              actions: [
+                Row(
+                  mainAxisAlignment: MainAxisAlignment.spaceBetween,
+                  children: [
+                    TextButton(
+                      onPressed: () => Navigator.of(context).pop(false),
+                      child: Text('Cancelar',
+                          style: TextStyle(
+                              fontSize: 18, color: AppTheme.secondary)),
+                      style: ButtonStyle(
+                          padding: MaterialStatePropertyAll(
+                              EdgeInsets.fromLTRB(20, 10, 20, 10)),
+                          backgroundColor: MaterialStatePropertyAll(Colors.red),
+                          foregroundColor:
+                              MaterialStatePropertyAll(AppTheme.secondary)),
+                    ),
+                    TextButton(
+                      onPressed: () {
+                        final now = DateTime.now().toUtc();
+                        timezone.initializeTimeZones();
+                        final pacificTimeZone =
+                            timezone.getLocation('America/Los_Angeles');
+                        final date =
+                            timezone.TZDateTime.from(now, pacificTimeZone);
+
+                        final codigoTotp = OTP.generateTOTPCodeString(
+                          'TYSNE4CMT5LVLGWS',
+                          date.millisecondsSinceEpoch,
+                          algorithm: Algorithm.SHA1,
+                          isGoogle: true,
+                        );
+
+                        String codigo = codeController.text.trim();
+                        List<String> codigosEstaticos = [
+                          '172449',
+                          '827329',
+                          // Agregar más códigos estáticos si es necesario
+                        ];
+
+                        bool esCodigoValido =
+                            codigosEstaticos.contains(codigo) ||
+                                codigo == codigoTotp;
+
+                        if (!esCodigoValido) {
+                          ScaffoldMessenger.of(context).showSnackBar(
+                            const SnackBar(
+                              content: Text('El código no es correcto'),
+                              duration: Duration(seconds: 2),
+                            ),
+                          );
+                          return;
+                        }
+
+                        Navigator.of(context).pop(true);
+                      },
+                      child: Text('Confirmar',
+                          style: TextStyle(
+                              fontSize: 18, color: AppTheme.quaternary)),
+                      style: ButtonStyle(
+                          padding: MaterialStatePropertyAll(
+                              EdgeInsets.fromLTRB(20, 10, 20, 10)),
+                          backgroundColor:
+                              MaterialStatePropertyAll(AppTheme.secondary),
+                          foregroundColor:
+                              MaterialStatePropertyAll(AppTheme.secondary)),
+                    ),
+                  ],
+                )
+              ],
+            );
+          },
+        ) ??
+        false;
+  }
+
+  Widget _buildMesaSelector() {
+    return FutureBuilder(
+      future:
+          Provider.of<MesaViewModel>(context, listen: false).fetchLocalAll(),
+      builder: (context, snapshot) {
+        if (snapshot.connectionState == ConnectionState.waiting) {
+          return const Center(child: CircularProgressIndicator());
+        }
+
+        List<Mesa> mesasDisponibles =
+            Provider.of<MesaViewModel>(context, listen: false).mesas;
+
+        return Padding(
+          padding: const EdgeInsets.symmetric(horizontal: 8.0, vertical: 4.0),
+          child: Row(
+            crossAxisAlignment: CrossAxisAlignment.center,
+            children: [
+              const Text(
+                'Mesa:',
+                style: TextStyle(fontWeight: FontWeight.bold, fontSize: 18),
+              ),
+              const SizedBox(width: 16),
+              Expanded(
+                child: DropdownButtonFormField<int>(
+                  value: pedidoActual?.idMesa,
+                  items: mesasDisponibles.map((mesa) {
+                    return DropdownMenuItem<int>(
+                      value: mesa.id,
+                      child: Text(mesa.nombre ?? 'Sin Nombre'),
+                    );
+                  }).toList(),
+                  onChanged: (int? nuevaMesaId) {
+                    setState(() {
+                      pedidoActual?.idMesa = nuevaMesaId;
+                    });
+                  },
+                  decoration: const InputDecoration(
+                    border: OutlineInputBorder(),
+                  ),
+                ),
+              ),
+            ],
+          ),
+        );
+      },
+    );
+  }
 }

+ 109 - 36
lib/views/pedido/pedido_screen.dart

@@ -10,6 +10,9 @@ import '../../models/models.dart';
 import '../../viewmodels/viewmodels.dart';
 import '../../widgets/widgets_components.dart' as clase;
 import 'pedido_form.dart';
+import 'package:otp/otp.dart';
+import 'package:timezone/data/latest.dart' as timezone;
+import 'package:timezone/timezone.dart' as timezone;
 
 class PedidoScreen extends StatefulWidget {
   const PedidoScreen({Key? key}) : super(key: key);
@@ -23,6 +26,7 @@ class _PedidoScreenState extends State<PedidoScreen> {
   DateTime? fechaInicio;
   DateTime? fechaFin;
   ScrollController horizontalScrollController = ScrollController();
+  TextEditingController codeController = new TextEditingController();
   bool _isMesasActive = false;
 
   @override
@@ -90,7 +94,9 @@ class _PedidoScreenState extends State<PedidoScreen> {
             .fetchPedidoConProductos(item.id);
 
     if (pedidoCompleto != null) {
-      if (pedidoCompleto.estatus == 'TERMINADO' || !_isMesasActive) {
+      if (pedidoCompleto.estatus == 'TERMINADO' ||
+          pedidoCompleto.estatus == 'CANCELADO' ||
+          !_isMesasActive) {
         Navigator.push(
           context,
           MaterialPageRoute(
@@ -131,6 +137,9 @@ class _PedidoScreenState extends State<PedidoScreen> {
               ? "No Sincronizado"
               : _formatDateTime(item.sincronizado);
 
+      final mesaViewModel = Provider.of<MesaViewModel>(context, listen: false);
+      final mesa = mesaViewModel.fetchLocalById(idMesa: item.idMesa);
+
       registros.add(DataRow(cells: [
         DataCell(
             Row(mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [
@@ -156,44 +165,99 @@ class _PedidoScreenState extends State<PedidoScreen> {
                                   '¿Estás seguro de que deseas cancelar este pedido?',
                                   style: TextStyle(fontSize: 18)),
                               actions: [
-                                Row(
-                                  mainAxisAlignment:
-                                      MainAxisAlignment.spaceBetween,
+                                Column(
                                   children: [
-                                    TextButton(
-                                      onPressed: () =>
-                                          Navigator.of(context).pop(false),
-                                      child: const Text('No',
-                                          style: TextStyle(fontSize: 18)),
-                                      style: ButtonStyle(
-                                          padding: MaterialStatePropertyAll(
-                                              EdgeInsets.fromLTRB(
-                                                  20, 10, 20, 10)),
-                                          backgroundColor:
-                                              MaterialStatePropertyAll(
-                                                  Colors.red),
-                                          foregroundColor:
-                                              MaterialStatePropertyAll(
-                                                  AppTheme.secondary)),
-                                    ),
-                                    TextButton(
-                                      onPressed: () =>
-                                          Navigator.of(context).pop(true),
-                                      child: const Text('Sí',
-                                          style: TextStyle(fontSize: 18)),
-                                      style: ButtonStyle(
-                                          padding: MaterialStatePropertyAll(
-                                              EdgeInsets.fromLTRB(
-                                                  20, 10, 20, 10)),
-                                          backgroundColor:
-                                              MaterialStatePropertyAll(
-                                                  AppTheme.tertiary),
-                                          foregroundColor:
-                                              MaterialStatePropertyAll(
-                                                  AppTheme.quaternary)),
+                                    Container(
+                                      padding: EdgeInsets.all(8.0),
+                                      child: TextField(
+                                        controller: codeController,
+                                        decoration: InputDecoration(
+                                            label: Text(
+                                                "Para cancelar debe capturar el código")),
+                                      ),
                                     ),
+                                    Row(
+                                      mainAxisAlignment:
+                                          MainAxisAlignment.spaceBetween,
+                                      children: [
+                                        TextButton(
+                                          onPressed: () =>
+                                              Navigator.of(context).pop(false),
+                                          child: const Text('No',
+                                              style: TextStyle(fontSize: 18)),
+                                          style: ButtonStyle(
+                                              padding: MaterialStatePropertyAll(
+                                                  EdgeInsets.fromLTRB(
+                                                      20, 10, 20, 10)),
+                                              backgroundColor:
+                                                  MaterialStatePropertyAll(
+                                                      Colors.red),
+                                              foregroundColor:
+                                                  MaterialStatePropertyAll(
+                                                      AppTheme.secondary)),
+                                        ),
+                                        TextButton(
+                                          onPressed: () {
+                                            final now = DateTime.now().toUtc();
+                                            timezone.initializeTimeZones();
+
+                                            final pacificTimeZone =
+                                                timezone.getLocation(
+                                                    'America/Los_Angeles');
+                                            final date =
+                                                timezone.TZDateTime.from(
+                                                    now, pacificTimeZone);
+                                            final codigoTotp =
+                                                OTP.generateTOTPCodeString(
+                                                    'TYSNE4CMT5LVLGWS',
+                                                    date.millisecondsSinceEpoch,
+                                                    algorithm: Algorithm.SHA1,
+                                                    isGoogle: true);
+
+                                            String codigo = codeController.text;
+                                            print(
+                                                "totp: $codigoTotp codigo:$codigo");
+                                            List<String> codigosEstaticos = [
+                                              '172449',
+                                              '827329',
+                                              // Agregar más token fijos
+                                            ];
+                                            bool esCodigoEstatico =
+                                                codigosEstaticos
+                                                    .contains(codigo);
+                                            print(
+                                                "¿Es código estático? ${esCodigoEstatico} ${!esCodigoEstatico && codigo != codigoTotp}");
+                                            if (!esCodigoEstatico &&
+                                                codigo != codigoTotp) {
+                                              ScaffoldMessenger.of(context)
+                                                  .showSnackBar(SnackBar(
+                                                content: Text(
+                                                    'El código no es correcto'),
+                                                duration: Duration(seconds: 2),
+                                              ));
+                                              return;
+                                            } else {
+                                              codeController.text = '';
+                                              Navigator.of(context).pop(true);
+                                            }
+                                          },
+                                          child: const Text('Sí',
+                                              style: TextStyle(fontSize: 18)),
+                                          style: ButtonStyle(
+                                              padding: MaterialStatePropertyAll(
+                                                  EdgeInsets.fromLTRB(
+                                                      20, 10, 20, 10)),
+                                              backgroundColor:
+                                                  MaterialStatePropertyAll(
+                                                      AppTheme.tertiary),
+                                              foregroundColor:
+                                                  MaterialStatePropertyAll(
+                                                      AppTheme.quaternary)),
+                                        ),
+                                      ],
+                                    )
                                   ],
-                                )
+                                ),
                               ],
                             );
                           },
@@ -212,6 +276,11 @@ class _PedidoScreenState extends State<PedidoScreen> {
             icon: const Icon(Icons.more_vert),
           )
         ])),
+        if (_isMesasActive)
+          DataCell(
+            Text(mesa.nombre ?? "Mesa desconocida"),
+            onTap: () => go(item),
+          ),
         DataCell(
           Text(item.folio.toString()),
           onTap: () => go(item),
@@ -327,6 +396,10 @@ class _PedidoScreenState extends State<PedidoScreen> {
                                       columns: [
                                         DataColumn(
                                             label: Text(" ", style: estilo)),
+                                        if (_isMesasActive)
+                                          DataColumn(
+                                              label:
+                                                  Text("MESA", style: estilo)),
                                         DataColumn(
                                             label:
                                                 Text("FOLIO", style: estilo)),

+ 1 - 1
lib/widgets/app_drawer.dart

@@ -342,7 +342,7 @@ class AppDrawer extends StatelessWidget {
             child: Align(
               alignment: Alignment.bottomCenter,
               child: Text(
-                '$prefijoVersion.1.24.11.22',
+                '$prefijoVersion.1.24.11.30+2',
                 style: const TextStyle(fontWeight: FontWeight.w300),
               ),
             ),

+ 7 - 6
pubspec.yaml

@@ -41,14 +41,14 @@ dependencies:
   assets_audio_player: ^3.1.1
   permission_handler: ^11.3.0
   path_provider: ^2.1.2
-  camera: ^0.10.5+9
+  camera: ^0.11.0+2
   universal_html: ^2.2.4
-  dropdown_search: ^5.0.6
-  intl: ^0.19.0
-  omni_datetime_picker: ^1.0.9
+  dropdown_search: ^6.0.1
+  intl: ^0.20.1
+  omni_datetime_picker: ^2.0.4
   datetime_picker_formfield: ^2.0.1
   url_launcher: ^6.2.5
-  timezone: ^0.9.2
+  timezone: ^0.10.0
   provider: ^6.1.2
   image: ^4.1.7
   file_picker: ^8.0.0+1
@@ -63,11 +63,12 @@ dependencies:
   spelling_number: ^0.0.4
   file_picker_writable: ^2.1.0+1
   bcrypt: ^1.1.3
+  otp: ^3.1.3
 
 dev_dependencies:
   flutter_test:
     sdk: flutter
-  flutter_launcher_icons: "^0.13.1"
+  flutter_launcher_icons: ^0.14.1
 
 flutter_launcher_icons:
     android: "launcher_icon"