Explorar el Código

Campo minimo para validar un minimo obligatorio al generar un pedido

OscarGil03 hace 6 meses
padre
commit
a79399eb4d

+ 5 - 0
lib/models/categoria_producto_model.dart

@@ -6,6 +6,7 @@ class CategoriaProducto extends Basico {
   String? descripcion;
   int? esToping;
   int? maximo;
+  int? minimo;
 
   CategoriaProducto({
     super.id,
@@ -13,6 +14,7 @@ class CategoriaProducto extends Basico {
     this.descripcion,
     this.esToping,
     this.maximo,
+    this.minimo,
   });
 
   @override
@@ -27,6 +29,7 @@ class CategoriaProducto extends Basico {
       'descripcion': descripcion ?? '',
       'esToping': esToping ?? 0,
       'maximo': maximo ?? 0,
+      'minimo': minimo ?? 0,
       'creado': creado?.toIso8601String(),
       'modificado': modificado?.toIso8601String(),
       'eliminado': eliminado?.toIso8601String(),
@@ -39,6 +42,7 @@ class CategoriaProducto extends Basico {
     descripcion = Basico.parseString(json['descripcion']);
     esToping = Basico.parseInt(json['esToping']);
     maximo = Basico.parseInt(json['maximo']);
+    minimo = Basico.parseInt(json['minimo']);
   }
 
   CategoriaProducto.fromApi(Map<String, dynamic> json) {
@@ -47,6 +51,7 @@ class CategoriaProducto extends Basico {
     descripcion = Basico.parseString(json['descripcion']);
     esToping = Basico.parseInt(json['esToping']);
     maximo = Basico.parseInt(json['maximo']);
+    minimo = Basico.parseInt(json['minimo']);
     creado = Basico.parseDate(json['creado']);
     modificado = Basico.parseDate(json['modificado']);
     eliminado = Basico.parseDate(json['eliminado']);

+ 8 - 1
lib/services/repo_service.dart

@@ -6,7 +6,7 @@ import 'package:sqflite/sqflite.dart';
 import '../models/models.dart';
 
 class RepoService<T> {
-  static int dbVersion = 17;
+  static int dbVersion = 18;
   static String dbName = 'joshipos026.db';
   static const String id = Basico.identificadorWeb;
   static const String idLocal = Basico.identificadorLocal;
@@ -568,6 +568,13 @@ class RepoService<T> {
           ''');
 
           break;
+
+        case 17:
+          await db.execute('''
+            ALTER TABLE CategoriaProducto ADD COLUMN minimo INTEGER;
+          ''');
+
+          break;
       }
       oldVersion++;
     }

+ 28 - 6
lib/views/categoria_producto/categoria_producto_form.dart

@@ -20,6 +20,7 @@ class Formulario extends State<CategoriaProductoForm> {
   final _nombre = TextEditingController();
   final _descripcion = TextEditingController();
   final _maximo = TextEditingController();
+  final _minimo = TextEditingController();
   late int esToping;
 
   @override
@@ -28,6 +29,7 @@ class Formulario extends State<CategoriaProductoForm> {
     _nombre.text = widget.categoriaProducto.nombre ?? "";
     _descripcion.text = widget.categoriaProducto.descripcion ?? "";
     _maximo.text = widget.categoriaProducto.maximo?.toString() ?? "";
+    _minimo.text = widget.categoriaProducto.minimo?.toString() ?? "";
     esToping = widget.categoriaProducto.esToping ?? 0;
     WidgetsBinding.instance.addPostFrameCallback((_) {
       _loadData();
@@ -96,12 +98,28 @@ class Formulario extends State<CategoriaProductoForm> {
                     ),
                     SizedBox(height: 10),
                     if (esToping == 1)
-                      AppTextField(
-                        maxLength: 2,
-                        etiqueta: 'Máximo',
-                        controller: _maximo,
-                        hintText: 'Máximo permitido en un pedido (opcional)',
-                        keyboardType: TextInputType.number,
+                      Row(
+                        children: [
+                          Expanded(
+                            child: AppTextField(
+                              maxLength: 2,
+                              etiqueta: 'Máximo',
+                              controller: _maximo,
+                              hintText:
+                                  'Máximo permitido en un pedido (opcional)',
+                              keyboardType: TextInputType.number,
+                            ),
+                          ),
+                          Expanded(
+                            child: AppTextField(
+                              maxLength: 2,
+                              etiqueta: 'Mínimo',
+                              controller: _minimo,
+                              hintText: 'Mínimo a seleccionar en un pedido',
+                              keyboardType: TextInputType.number,
+                            ),
+                          )
+                        ],
                       ),
                   ],
                 ),
@@ -115,6 +133,9 @@ class Formulario extends State<CategoriaProductoForm> {
               int? maximoValue =
                   _maximo.text.isNotEmpty ? int.tryParse(_maximo.text) : null;
 
+              int? minimoValue =
+                  _minimo.text.isNotEmpty ? int.tryParse(_minimo.text) : null;
+
               await categoriaProductoVM.updateCategoriaProducto(
                 CategoriaProducto(
                   id: widget.categoriaProducto.id,
@@ -122,6 +143,7 @@ class Formulario extends State<CategoriaProductoForm> {
                   descripcion: _descripcion.text,
                   esToping: esToping,
                   maximo: maximoValue,
+                  minimo: minimoValue,
                 ),
               );
 

+ 49 - 2
lib/views/pedido/pedido_form.dart

@@ -129,6 +129,46 @@ class _PedidoFormState extends State<PedidoForm> {
     // print('Total con descuento aplicado: $totalPedido');
   }
 
+  bool validarMinimosSeleccionados() {
+    for (var item in carrito) {
+      for (var categoriaId in item.selectableToppings.keys) {
+        final categoria = categorias.firstWhere((c) => c.id == categoriaId);
+        final minimoRequerido = categoria.minimo ?? 0;
+        final seleccionados = item.selectedToppings[categoriaId]?.length ?? 0;
+
+        if (minimoRequerido > 0 && seleccionados < minimoRequerido) {
+          showDialog(
+            context: context,
+            builder: (BuildContext context) {
+              return AlertDialog(
+                title: const Text('Faltan Toppings',
+                    style:
+                        TextStyle(fontWeight: FontWeight.w500, fontSize: 22)),
+                content: Text(
+                    'El producto ${item.producto.nombre} requiere que seleccione al menos $minimoRequerido topping en la categoría ${categoria.nombre}.',
+                    style: TextStyle(fontSize: 18)),
+                actions: <Widget>[
+                  TextButton(
+                      onPressed: () => Navigator.of(context).pop(),
+                      child: const Text('Aceptar'),
+                      style: ButtonStyle(
+                          padding: MaterialStatePropertyAll(
+                              EdgeInsets.fromLTRB(20, 10, 20, 10)),
+                          backgroundColor:
+                              MaterialStatePropertyAll(AppTheme.tertiary),
+                          foregroundColor:
+                              MaterialStatePropertyAll(AppTheme.quaternary))),
+                ],
+              );
+            },
+          );
+          return false;
+        }
+      }
+    }
+    return true;
+  }
+
   void _finalizeOrder() async {
     if (carrito.isEmpty) {
       showDialog(
@@ -159,6 +199,9 @@ class _PedidoFormState extends State<PedidoForm> {
       );
       return;
     }
+    if (!validarMinimosSeleccionados()) {
+      return;
+    }
 
     await _promptForCustomerName();
   }
@@ -1122,9 +1165,13 @@ class _PedidoFormState extends State<PedidoForm> {
                           return ExpansionTile(
                             initiallyExpanded: true,
                             title: Text(
-                              '${categoria.nombre} (Hasta ${categoria.maximo ?? 0})',
+                              '${categoria.nombre}'
+                              ' (Hasta ${categoria.maximo ?? 0})'
+                              ' ${(categoria.minimo ?? 0) > 0 ? " (Mínimo ${categoria.minimo})" : ""}',
                               style: const TextStyle(
-                                  fontWeight: FontWeight.bold, fontSize: 16),
+                                fontWeight: FontWeight.bold,
+                                fontSize: 16,
+                              ),
                             ),
                             children: availableToppings.map((topping) {
                               bool isSelected = item