Kaynağa Gözat

Sincronizacion de pedidos, productos y categorías

OscarGil03 7 ay önce
ebeveyn
işleme
549cf23782

+ 21 - 4
lib/models/categoria_producto_model.dart

@@ -17,12 +17,19 @@ class CategoriaProducto extends Basico {
 
   @override
   Map<String, dynamic> toJson() {
+    print("Convirtiendo Producto a JSON");
+    print("ID: $id, descripcion: $descripcion, Nombre: $nombre");
+    print("creado: $creado, modificado: $modificado, eliminado: $eliminado");
+
     return {
       'id': id,
-      'nombre': nombre,
-      'descripcion': descripcion,
-      'esToping': esToping,
-      'maximo': maximo,
+      'nombre': nombre ?? '',
+      'descripcion': descripcion ?? '',
+      'esToping': esToping ?? 0,
+      'maximo': maximo ?? 0,
+      'creado': creado != null ? creado!.toIso8601String() : null,
+      'modificado': modificado != null ? modificado!.toIso8601String() : null,
+      'eliminado': eliminado != null ? eliminado!.toIso8601String() : null,
     }..addAll(super.toJson());
   }
 
@@ -34,6 +41,16 @@ class CategoriaProducto extends Basico {
     maximo = Basico.parseInt(json['maximo']);
   }
 
+  CategoriaProducto.fromApi(Map<String, dynamic> json) {
+    super.parseJson(json);
+    nombre = Basico.parseString(json['nombre']);
+    descripcion = Basico.parseString(json['descripcion']);
+    maximo = Basico.parseInt(json['maximo']);
+    creado = Basico.parseDate(json['fechaCreado']);
+    modificado = Basico.parseDate(json['fechaModificado']);
+    eliminado = Basico.parseDate(json['eliminado']);
+  }
+
   Future<void> guardar() async {
     idLocal = await RepoService().guardar(this);
   }

+ 36 - 1
lib/models/pedido_model.dart

@@ -1,6 +1,8 @@
-import 'package:yoshi_papas_app/models/pedido_producto_model.dart';
+import 'package:intl/intl.dart';
 
 import 'basico_model.dart';
+import 'pedido_producto_model.dart';
+import '../services/services.dart';
 
 class Pedido extends Basico {
   int? folio;
@@ -22,6 +24,9 @@ class Pedido extends Basico {
   double? cantTarjeta;
   double? cantTransferencia;
   List<PedidoProducto> productos = [];
+  int? idWeb;
+
+  String? sincronizado;
 
   Pedido({
     super.id,
@@ -44,6 +49,8 @@ class Pedido extends Basico {
     this.cantTarjeta,
     this.cantTransferencia,
     this.productos = const [],
+    this.idWeb,
+    this.sincronizado,
   });
 
   @override
@@ -71,6 +78,32 @@ class Pedido extends Basico {
     }..addAll(super.toJson());
   }
 
+  Map<String, dynamic> toApi() {
+    idLocal = id;
+
+    Map<String, dynamic> apiMap = {
+      'idLocal': idLocal,
+      'folio': folio,
+      'estatus': estatus,
+      'comentarios': comentarios,
+      'nombreCliente': nombreCliente,
+      'creado': peticion,
+      'tipoPago': tipoPago,
+      'totalPedido': totalPedido,
+      'descuento': descuento,
+      'cantEfectivo': cantEfectivo,
+      'cantTarjeta': cantTarjeta,
+      'cantTransferencia': cantTransferencia,
+      'productos': productos.map((producto) => producto.toApi()).toList(),
+    };
+    Map<String, dynamic> basicoMap = super.toJson();
+    basicoMap.remove('id');
+    basicoMap.remove('eliminado');
+    apiMap.addAll(basicoMap);
+
+    return apiMap;
+  }
+
   Pedido.fromJson(Map<String, dynamic> json) {
     super.parseJson(json);
     id = (json['id'] as int?)!;
@@ -92,6 +125,8 @@ class Pedido extends Basico {
     cantEfectivo = Basico.parseDouble(json['cantEfectivo']);
     cantTarjeta = Basico.parseDouble(json['cantTarjeta']);
     cantTransferencia = Basico.parseDouble(json['cantTransferencia']);
+    idWeb = Basico.parseInt(json['idWeb']);
+    sincronizado = Basico.parseString(json['sincronizado']);
 
     List<PedidoProducto> _productos = [];
     if (json["productos"] != null && (json["productos"] as List).isNotEmpty) {

+ 17 - 0
lib/models/pedido_producto_model.dart

@@ -10,6 +10,8 @@ class PedidoProducto extends Basico {
   int? terminar;
   String? comentario;
   List<PedidoProductoTopping> toppings = [];
+  int? idWeb;
+  String? sincronizado;
 
   PedidoProducto({
     super.id,
@@ -22,6 +24,8 @@ class PedidoProducto extends Basico {
     this.terminar,
     this.comentario,
     this.toppings = const [],
+    this.idWeb,
+    this.sincronizado,
   });
 
   @override
@@ -38,6 +42,17 @@ class PedidoProducto extends Basico {
     }..addAll(super.toJson());
   }
 
+  Map<String, dynamic> toApi() {
+    return {
+      'idLocal': idProducto,
+      'nombre': producto?.nombre ?? '',
+      'costoUnitario': costoUnitario,
+      'descuento': descuento,
+      'cantidad': cantidad,
+      'comentario': comentario,
+    };
+  }
+
   PedidoProducto.fromJson(Map<String, dynamic> json) {
     super.parseJson(json);
     idPedido = Basico.parseInt(json['idPedido']);
@@ -49,6 +64,8 @@ class PedidoProducto extends Basico {
     cantidad = Basico.parseInt(json['cantidad']);
     terminar = Basico.parseInt(json['terminar']);
     comentario = Basico.parseString(json['comentario']);
+    idWeb = Basico.parseInt(json['idWeb']);
+    sincronizado = Basico.parseString(json['sincronizado']);
     toppings = (json['toppings'] as List<dynamic>?)
             ?.map((topingJson) => PedidoProductoTopping.fromJson(
                 topingJson as Map<String, dynamic>))

+ 46 - 21
lib/models/producto_model.dart

@@ -11,11 +11,10 @@ class Producto extends Basico {
   String? imagen;
   int? venta;
   int? existencia;
-  String? precio;
+  double? precio;
   int? verMenu;
   String? codigo;
   String? descuento;
-  String? venceDescuento;
   int? toping;
   List<Producto>? topings;
 
@@ -31,27 +30,35 @@ class Producto extends Basico {
     this.verMenu,
     this.codigo,
     this.descuento,
-    this.venceDescuento,
     this.toping,
     this.topings,
   });
 
   @override
   Map<String, dynamic> toJson() {
+    print("Convirtiendo Producto a JSON");
+    print("ID: $id, Categoria: $idCategoria, Nombre: $nombre");
+    print("Descripcion: $descripcion, imagen: $imagen, venta: $venta");
+    print("existencia: $existencia, precio: $precio, verMenu: $verMenu");
+    print("codigo: $codigo, descuento: $descuento, creado: $creado");
+    print("eliminado: $eliminado, modificado: $modificado");
+
     return {
       'id': id,
-      'idCategoria': idCategoria,
-      'nombre': nombre,
-      'descripcion': descripcion,
-      'imagen': imagen,
-      'venta': venta,
-      'existencia': existencia,
-      'precio': precio,
-      'verMenu': verMenu,
-      'codigo': codigo,
-      'descuento': descuento,
-      'venceDescuento': venceDescuento,
-      'toping': toping,
+      'idCategoria': idCategoria ?? 0,
+      'nombre': nombre ?? '',
+      'descripcion': descripcion ?? '',
+      'imagen': imagen ?? '',
+      'venta': venta ?? 0,
+      'existencia': existencia ?? 0,
+      'precio': precio ?? 0.0,
+      'verMenu': verMenu ?? 0,
+      'codigo': codigo ?? '',
+      'descuento': descuento ?? '',
+      'toping': toping ?? 0,
+      'creado': creado != null ? creado!.toIso8601String() : null,
+      'modificado': modificado != null ? modificado!.toIso8601String() : null,
+      'eliminado': eliminado != null ? eliminado!.toIso8601String() : null,
     }..addAll(super.toJson());
   }
 
@@ -68,27 +75,28 @@ class Producto extends Basico {
       'verMenu': verMenu,
       'codigo': codigo,
       'descuento': descuento,
-      'venceDescuento': venceDescuento,
       'toping': toping,
+      'creado': creado != null ? creado!.toIso8601String() : null,
+      'modificado': modificado != null ? modificado!.toIso8601String() : null,
+      'eliminado': eliminado != null ? eliminado!.toIso8601String() : null,
     };
   }
 
   Producto.fromJson(Map<String, dynamic> json) {
     super.parseJson(json);
     idCategoria = Basico.parseInt(json['idCategoria']);
-    categoria = json["categoria"] != null
-        ? CategoriaProducto.fromJson(json["categoria"])
-        : null;
+    // categoria = json["categoria"] != null
+    //     ? CategoriaProducto.fromJson(json["categoria"])
+    //     : null;
     nombre = Basico.parseString(json['nombre']);
     descripcion = Basico.parseString(json['descripcion']);
     imagen = Basico.parseString(json['imagen']);
     venta = Basico.parseInt(json['venta']);
     existencia = Basico.parseInt(json['existencia']);
-    precio = Basico.parseString(json['precio']);
+    precio = Basico.parseDouble(json['precio']);
     verMenu = Basico.parseInt(json['verMenu']);
     codigo = Basico.parseString(json['codigo']);
     descuento = Basico.parseString(json['descuento']);
-    venceDescuento = Basico.parseString(json['venceDescuento']);
     if (json['toping'] is bool) {
       toping = json['toping'] ? 1 : 0;
     } else {
@@ -101,6 +109,23 @@ class Producto extends Basico {
     }
   }
 
+  Producto.fromApi(Map<String, dynamic> json) {
+    super.parseJson(json);
+    idCategoria = Basico.parseInt(json['idCategoria']);
+    nombre = Basico.parseString(json['nombre']);
+    descripcion = Basico.parseString(json['descripcion']);
+    imagen = Basico.parseString(json['imagen']);
+    venta = Basico.parseInt(json['venta']);
+    existencia = Basico.parseInt(json['existencia']);
+    precio = Basico.parseDouble(json['precio']);
+    verMenu = Basico.parseInt(json['verEnMenu']);
+    codigo = Basico.parseString(json['codigo']);
+    descuento = Basico.parseString(json['descuento']);
+    creado = Basico.parseDate(json['fechaCreado']);
+    modificado = Basico.parseDate(json['fechaModificado']);
+    eliminado = Basico.parseDate(json['eliminado']);
+  }
+
   Future<void> guardar() async {
     idLocal = await RepoService().guardar(this);
   }

+ 2 - 2
lib/services/base_service.dart

@@ -11,8 +11,8 @@ class BaseService {
   //produccion: hermogas.est.api.rdsistemas.app
   //prueba: hermogas.est.test.rdsistemas.app
 
-  String base_url = 'https://pos.papas.edesarrollos.info';
-  String baseUrl = 'pos.papas.edesarrollos.info';
+  String base_url = 'https://joshipapas.api.edesarrollos.info/';
+  String baseUrl = 'joshipapas.api.edesarrollos.info';
   Future<Map<String, String>> getDefaultHeaders({withAuth = true}) async {
     Map<String, String> defaultHeaders = {'Content-Type': 'application/json'};
 

+ 37 - 37
lib/services/productos_service.dart

@@ -17,47 +17,47 @@ class ProductosService {
   }
 
   Future<void> fillCategoriaBD() async {
-    final List<CategoriaProducto> categorias = [
-      CategoriaProducto(id: 1, nombre: 'COMIDAS'),
-      CategoriaProducto(id: 2, nombre: 'BEBIDAS'),
-    ];
+    // final List<CategoriaProducto> categorias = [
+    //   CategoriaProducto(id: 1, nombre: 'COMIDAS'),
+    //   CategoriaProducto(id: 2, nombre: 'BEBIDAS'),
+    // ];
 
-    RepoService<CategoriaProducto> repoService =
-        RepoService<CategoriaProducto>();
-    for (var categoria in categorias) {
-      await repoService.guardarLocal(categoria);
-    }
+    // RepoService<CategoriaProducto> repoService =
+    //     RepoService<CategoriaProducto>();
+    // for (var categoria in categorias) {
+    //   await repoService.guardarLocal(categoria);
+    // }
   }
 
   Future<void> fillProductoBD() async {
-    List<Producto> productos = [
-      Producto(id: 1, idCategoria: 1, nombre: 'CONO DE PAPAS', precio: '100'),
-      Producto(
-          id: 2,
-          idCategoria: 1,
-          nombre: 'CONO DE BONELESS (250 GR)',
-          precio: '150'),
-      Producto(
-          id: 3,
-          idCategoria: 1,
-          nombre: 'CONO DE PAPAS + BONELESS (CURLY, GAJO O FRANCESA)',
-          precio: '180'),
-      Producto(id: 4, idCategoria: 1, nombre: 'PIZZARINO', precio: '50'),
-      Producto(
-          id: 5,
-          idCategoria: 1,
-          nombre: 'CASCO DE BASEBALL CON PAPAS FRANCESAS',
-          precio: '120'),
-      Producto(
-          id: 6,
-          idCategoria: 1,
-          nombre: 'EXTRA 2 DEDOS DE QUESOS Y 2 AROS DE CEBOLLA',
-          precio: '50'),
-    ];
+    // List<Producto> productos = [
+    //   Producto(id: 1, idCategoria: 1, nombre: 'CONO DE PAPAS', precio: '100'),
+    //   Producto(
+    //       id: 2,
+    //       idCategoria: 1,
+    //       nombre: 'CONO DE BONELESS (250 GR)',
+    //       precio: '150'),
+    //   Producto(
+    //       id: 3,
+    //       idCategoria: 1,
+    //       nombre: 'CONO DE PAPAS + BONELESS (CURLY, GAJO O FRANCESA)',
+    //       precio: '180'),
+    //   Producto(id: 4, idCategoria: 1, nombre: 'PIZZARINO', precio: '50'),
+    //   Producto(
+    //       id: 5,
+    //       idCategoria: 1,
+    //       nombre: 'CASCO DE BASEBALL CON PAPAS FRANCESAS',
+    //       precio: '120'),
+    //   Producto(
+    //       id: 6,
+    //       idCategoria: 1,
+    //       nombre: 'EXTRA 2 DEDOS DE QUESOS Y 2 AROS DE CEBOLLA',
+    //       precio: '50'),
+    // ];
 
-    RepoService<Producto> repoService = RepoService<Producto>();
-    for (var producto in productos) {
-      await repoService.guardarLocal(producto);
-    }
+    // RepoService<Producto> repoService = RepoService<Producto>();
+    // for (var producto in productos) {
+    //   await repoService.guardarLocal(producto);
+    // }
   }
 }

+ 201 - 22
lib/services/repo_service.dart

@@ -6,7 +6,7 @@ import 'package:sqflite/sqflite.dart';
 import '../models/models.dart';
 
 class RepoService<T> {
-  static int dbVersion = 8;
+  static int dbVersion = 12;
   static String dbName = 'joshipos026.db';
   static const String id = Basico.identificadorWeb;
   static const String idLocal = Basico.identificadorLocal;
@@ -341,6 +341,76 @@ class RepoService<T> {
             )
           ''');
           break;
+
+        // case 8:
+        //   await db.execute('''
+        //   ALTER TABLE Producto ADD COLUMN toping INTEGER DEFAULT 0;
+        // ''');
+        //   break;
+
+        case 9:
+          await db.execute('''
+          ALTER TABLE Pedido ADD COLUMN idWeb INTEGER;
+        ''');
+          await db.execute('''
+          ALTER TABLE Pedido ADD COLUMN sincronizado TEXT;
+        ''');
+          await db.execute('''
+          ALTER TABLE PedidoProducto ADD COLUMN idWeb INTEGER;
+        ''');
+          await db.execute('''
+          ALTER TABLE PedidoProducto ADD COLUMN sincronizado TEXT;
+        ''');
+          break;
+
+        case 10:
+          await db.execute('''
+          update Pedido set sincronizado = null, peticion = strftime('%Y-%m-%dT%H:%M:%S',
+                datetime(substr(peticion, 7, 4) || '-' ||
+                substr(peticion, 4, 2) || '-' ||
+                substr(peticion, 1, 2) || ' ' ||
+                substr(peticion, 12, 8) || ' -07:00', 'localtime', '+07:00'))
+          WHERE strftime('%Y-%m-%dT%H:%M:%S',
+                datetime(substr(peticion, 7, 4) || '-' ||
+                substr(peticion, 4, 2) || '-' ||
+                substr(peticion, 1, 2) || ' ' ||
+                substr(peticion, 12, 8) || ' -07:00', 'localtime', '+07:00')) is not null
+        ''');
+          break;
+
+        case 11:
+          await db.execute('DROP TABLE IF EXISTS Producto');
+
+          //Se tiene que crear nuevamente para que precio sea Double
+          await db.execute('''
+          CREATE TABLE Producto (
+            id INTEGER PRIMARY KEY AUTOINCREMENT,
+            idCategoria INTEGER,
+            idLocal INTEGER,
+            nombre TEXT,
+            descripcion TEXT,
+            imagen TEXT,
+            venta INTEGER,
+            existencia INTEGER,
+            precio REAL,
+            verMenu INTEGER,
+            codigo TEXT,
+            descuento TEXT,
+            toping INTEGER,
+            creado TEXT,
+            modificado TEXT,
+            eliminado TEXT
+          )
+        ''');
+          await db.execute('DELETE FROM CategoriaProducto');
+          await db.execute('''
+            ALTER TABLE CategoriaProducto ADD COLUMN creado TEXT;
+          ''');
+          await db.execute('''
+            ALTER TABLE CategoriaProducto ADD COLUMN modificado TEXT;
+          ''');
+
+          break;
       }
       oldVersion++;
     }
@@ -348,32 +418,39 @@ class RepoService<T> {
 
   Future<int> guardar(T model) async {
     try {
+      print("Guardando modelo en la base de datos: ${model.runtimeType}");
+
+      String modelo = json.encode(model, toEncodable: toEncodable);
+      print("Modelo convertido a JSON: $modelo");
+
+      Map<String, dynamic> modelMap = json.decode(modelo);
       var dbClient = await db;
       String nombreTabla = model.runtimeType.toString();
-      String modelo = json.encode(model, toEncodable: toEncodable);
 
-      print("Guardando en tabla: $nombreTabla");
-      print("Modelo serializado: $modelo");
+      int? id = modelMap['id'];
 
-      Map<String, dynamic> modelMap = json.decode(modelo);
+      if (id == null || id == 0) {
+        modelMap.remove('id');
+      }
+
+      List<Map> existing = id != null && id > 0
+          ? await dbClient!.query(nombreTabla, where: 'id = ?', whereArgs: [id])
+          : [];
 
-      int id = 0;
-      if (modelMap['id'] != null && modelMap['id'] != 0) {
-        print("Actualizando el registro con ID: ${modelMap['id']}");
+      if (existing.isNotEmpty) {
+        print("Actualizando registro existente con ID: $id");
         await dbClient!.update(
           nombreTabla,
           modelMap,
           where: 'id = ?',
-          whereArgs: [modelMap['id']],
+          whereArgs: [id],
         );
-        id = modelMap['id'];
       } else {
-        print("Insertando nuevo registro");
-        modelMap.remove('id');
+        print("Insertando nuevo registro en la tabla $nombreTabla");
         id = await dbClient!.insert(nombreTabla, modelMap);
       }
 
-      return id;
+      return id!;
     } catch (e) {
       print('Error al guardar en dynamic: $e');
       return 0;
@@ -463,7 +540,8 @@ class RepoService<T> {
   Future<List<T>> obtenerTodos({String orderBy = 'id DESC'}) async {
     var db = await this.db;
     String tableName = T.toString();
-    var result = await db!.query(tableName, orderBy: orderBy);
+    var result = await db!
+        .query(tableName, where: 'eliminado IS NULL', orderBy: orderBy);
     return result.map((map) => fromMap<T>(map)).toList();
   }
 
@@ -497,6 +575,26 @@ class RepoService<T> {
         .toList();
   }
 
+  Future<List<Deposito>> obtenerDepositosPorIdCorteCaja(int idCorteCaja) async {
+    var dbClient = await db;
+    List<Map<String, dynamic>> maps = await dbClient!.query(
+      'Deposito',
+      where: 'idCorteCaja = ?',
+      whereArgs: [idCorteCaja],
+    );
+    return maps.map((map) => Deposito.fromJson(map)).toList();
+  }
+
+  Future<List<Gasto>> obtenerGastosPorIdCorteCaja(int idCorteCaja) async {
+    var dbClient = await db;
+    List<Map<String, dynamic>> maps = await dbClient!.query(
+      'Gasto',
+      where: 'idCorteCaja = ?',
+      whereArgs: [idCorteCaja],
+    );
+    return maps.map((map) => Gasto.fromJson(map)).toList();
+  }
+
   Future<int> contarPedidos() async {
     Database? dbClient = await db;
     var result = await dbClient!.rawQuery('SELECT COUNT(*) FROM Pedido');
@@ -569,17 +667,18 @@ class RepoService<T> {
       DateTime startDate, DateTime endDate) async {
     var dbClient = await db;
 
-    String startDateString =
-        DateFormat('yyyy-MM-dd 00:00:00').format(startDate);
-    String endDateString = DateFormat('yyyy-MM-dd 23:59:59').format(endDate);
+    String startDateString = startDate.toIso8601String();
+    String endDateString = endDate.toIso8601String();
+
+    print(
+        'Ejecutando consulta: SELECT * FROM Pedido WHERE peticion BETWEEN $startDateString AND $endDateString');
 
     List<Map<String, dynamic>> maps = await dbClient!.rawQuery('''
     SELECT * FROM Pedido 
-    WHERE 
-      (datetime(substr(peticion, 7, 4) || '-' || substr(peticion, 4, 2) || '-' || substr(peticion, 1, 2) || ' ' || substr(peticion, 12)) BETWEEN ? AND ?)
-    OR 
-      (datetime(substr(peticion, 7, 4) || '-' || substr(peticion, 1, 2) || '-' || substr(peticion, 4, 2) || ' ' || substr(peticion, 12)) BETWEEN ? AND ?)
-  ''', [startDateString, endDateString, startDateString, endDateString]);
+    WHERE peticion BETWEEN ? AND ?
+  ''', [startDateString, endDateString]);
+
+    print('Resultado de la consulta: ${maps.length} pedidos encontrados.');
 
     return maps.map((map) => Pedido.fromJson(map)).toList();
   }
@@ -629,4 +728,84 @@ class RepoService<T> {
     return await dbClient!
         .delete('Descuento', where: 'id = ?', whereArgs: [id]);
   }
+
+  Future<Variable?> obtenerPorNombre(String nombre) async {
+    var dbClient = await db;
+    List<Map<String, dynamic>> maps = await dbClient!.query(
+      'Variable',
+      where: 'nombre = ?',
+      whereArgs: [nombre],
+    );
+
+    if (maps.isNotEmpty) {
+      return Variable.fromJson(Map<String, dynamic>.from(maps.first));
+    }
+
+    return null;
+  }
+
+  Future<List<Pedido>> obtenerPedidosOrdenadosPorFecha() async {
+    var dbClient = await db;
+
+    String orderBy =
+        "datetime(substr(peticion, 7, 4) || '-' || substr(peticion, 4, 2) || '-' || substr(peticion, 1, 2) || ' ' || substr(peticion, 12)) ASC";
+
+    List<Map<String, dynamic>> result = await dbClient!.query(
+      'Pedido',
+      where: 'sincronizado IS NULL',
+      orderBy: orderBy,
+    );
+
+    return result.map((map) => Pedido.fromJson(map)).toList();
+  }
+
+  Future<void> sincronizarCategorias(
+      List<CategoriaProducto> categoriasApi) async {
+    var db = await RepoService().db;
+
+    var categoriasLocalesQuery = await db!.query('CategoriaProducto');
+    List<CategoriaProducto> categoriasLocales = categoriasLocalesQuery
+        .map((e) => CategoriaProducto.fromJson(e))
+        .toList();
+
+    for (var categoriaApi in categoriasApi) {
+      var categoriaLocal = categoriasLocales.firstWhere(
+        (categoria) => categoria.id == categoriaApi.id,
+        orElse: () => CategoriaProducto(),
+      );
+
+      if (categoriaLocal.id != 0 &&
+          categoriaApi.modificado != null &&
+          categoriaLocal.modificado != null &&
+          categoriaApi.modificado!.isAfter(categoriaLocal.modificado!)) {
+        await RepoService().guardar(categoriaApi);
+      } else if (categoriaLocal.id == 0) {
+        await RepoService().guardar(categoriaApi);
+      }
+    }
+  }
+
+  Future<void> sincronizarProductos(List<Producto> productosApi) async {
+    var db = await RepoService().db;
+
+    var productosLocalesQuery = await db!.query('Producto');
+    List<Producto> productosLocales =
+        productosLocalesQuery.map((e) => Producto.fromJson(e)).toList();
+
+    for (var productoApi in productosApi) {
+      var productoLocal = productosLocales.firstWhere(
+        (producto) => producto.id == productoApi.id,
+        orElse: () => Producto(),
+      );
+
+      if (productoLocal.id != 0 &&
+          productoApi.modificado != null &&
+          productoLocal.modificado != null &&
+          productoApi.modificado!.isAfter(productoLocal.modificado!)) {
+        await RepoService().guardar(productoApi);
+      } else if (productoLocal.id == 0) {
+        await RepoService().guardar(productoApi);
+      }
+    }
+  }
 }

+ 4 - 1
lib/viewmodels/categoria_producto_view_model.dart

@@ -49,7 +49,10 @@ class CategoriaProductoViewModel extends ChangeNotifier {
     int offset = (_limit * (page - 1));
 
     var query = await db.query('CategoriaProducto',
-        orderBy: 'id asc', limit: _limit, offset: offset);
+        where: 'eliminado IS NULL',
+        orderBy: 'id asc',
+        limit: _limit,
+        offset: offset);
     _categoriaProductos =
         query.map((element) => CategoriaProducto.fromJson(element)).toList();
     notifyListeners();

+ 94 - 2
lib/viewmodels/pedido_view_model.dart

@@ -1,11 +1,10 @@
 import 'package:flutter/material.dart';
 import 'package:intl/intl.dart';
 import 'package:sqflite/sqflite.dart';
-import 'package:yoshi_papas_app/services/repo_service.dart';
 
 import '../data/api_response.dart';
-import '../services/base_service.dart';
 import '../models/models.dart';
+import '../services/services.dart';
 
 class PedidoViewModel extends ChangeNotifier {
   String _busqueda = "";
@@ -210,7 +209,15 @@ class PedidoViewModel extends ChangeNotifier {
       DateTime startDate, DateTime endDate) async {
     setIsLoading(true);
     RepoService<Pedido> repoPedido = RepoService<Pedido>();
+
+    print('Consulta SQL de pedidos desde: $startDate hasta: $endDate');
+
     List<Pedido> pedidos = await repoPedido.buscarPorFecha(startDate, endDate);
+
+    print('Pedidos obtenidos desde la base de datos: ${pedidos.length}');
+    pedidos.forEach((pedido) => print(
+        'Pedido Folio: ${pedido.folio}, Estatus: ${pedido.estatus}, Total: ${pedido.totalPedido}'));
+
     setIsLoading(false);
     notifyListeners();
     return pedidos;
@@ -222,4 +229,89 @@ class PedidoViewModel extends ChangeNotifier {
         where: 'id = ?', whereArgs: [idPedido]);
     fetchLocalPedidosForScreen();
   }
+
+  Future<bool> sincronizarPedidos() async {
+    List<Pedido> pedidosNoSincronizados =
+        await fetchAllLocalPedidosOrdenadosPorFecha();
+
+    if (pedidosNoSincronizados.isNotEmpty) {
+      Pedido pedidoNoSincronizado = pedidosNoSincronizados.first;
+
+      if (pedidoNoSincronizado.productos.isEmpty) {
+        pedidoNoSincronizado =
+            await fetchPedidoConProductos(pedidoNoSincronizado.id) ??
+                pedidoNoSincronizado;
+      }
+
+      Map<String, dynamic> pedidoJson =
+          await prepararPedidoParaApi(pedidoNoSincronizado);
+
+      print('JSON enviado: $pedidoJson');
+
+      var response = ApiResponse(await BaseService()
+          .post('/pos/pedido/sincronizar', body: pedidoJson));
+
+      if (response.isOk && response.detalle != null) {
+        int idWeb = response.detalle!['id'];
+        String sincronizado = response.detalle!['sincronizado'];
+
+        await actualizarPedidoSincronizado(
+            pedidoNoSincronizado.id!, idWeb, sincronizado);
+
+        return true;
+      } else {
+        print('Error en la sincronización del pedido: ${response.mensaje}');
+        return true;
+      }
+    } else {
+      print('No se encontraron pedidos no sincronizados.');
+      return false;
+    }
+  }
+
+  Future<void> actualizarPedidoSincronizado(
+      int idPedido, int idWeb, String sincronizado) async {
+    var db = await RepoService().db;
+
+    await db!.update(
+      'Pedido',
+      {
+        'idWeb': idWeb,
+        'sincronizado': sincronizado,
+      },
+      where: 'id = ?',
+      whereArgs: [idPedido],
+    );
+  }
+
+  Future<List<Pedido>> fetchAllLocalPedidosOrdenadosPorFecha() async {
+    setIsLoading(true);
+    RepoService<Pedido> repoPedido = RepoService<Pedido>();
+
+    List<Pedido> allPedidos =
+        await repoPedido.obtenerPedidosOrdenadosPorFecha();
+
+    setIsLoading(false);
+    return allPedidos;
+  }
+}
+
+Future<Map<String, dynamic>> prepararPedidoParaApi(Pedido pedido) async {
+  String? claveSucursal = await obtenerClaveSucursal();
+
+  Map<String, dynamic> apiMap = pedido.toApi();
+
+  apiMap['claveSucursal'] = claveSucursal;
+
+  if (pedido.idWeb != null && pedido.idWeb! > 0) {
+    apiMap['idWeb'] = pedido.idWeb;
+  }
+
+  return apiMap;
+}
+
+Future<String?> obtenerClaveSucursal() async {
+  RepoService<Variable> repoVariable = RepoService<Variable>();
+  Variable? sucursalVariable = await repoVariable.obtenerPorNombre('Sucursal');
+  return sucursalVariable?.clave;
 }

+ 77 - 9
lib/viewmodels/producto_view_model.dart

@@ -49,7 +49,10 @@ class ProductoViewModel<T> extends ChangeNotifier {
     int offset = (_limit * (page - 1));
 
     var query = await db.query('Producto',
-        orderBy: 'idLocal asc', limit: _limit, offset: offset);
+        where: 'eliminado IS NULL',
+        orderBy: 'idLocal asc',
+        limit: _limit,
+        offset: offset);
     _productos = query.map((element) => Producto.fromJson(element)).toList();
 
     notifyListeners();
@@ -73,7 +76,7 @@ class ProductoViewModel<T> extends ChangeNotifier {
   Future<void> fetchAllByCategory(int idCategoria) async {
     var db = await RepoService().db;
     var query = await db!.query('Producto',
-        where: 'idCategoria = ?',
+        where: 'idCategoria = ? and eliminado IS NULL',
         whereArgs: [idCategoria],
         orderBy: 'idLocal asc');
     _productos = query.map((e) => Producto.fromJson(e)).toList();
@@ -84,15 +87,11 @@ class ProductoViewModel<T> extends ChangeNotifier {
     var db = await RepoService().db;
     var query = await db!.query(
       'Producto',
-      where: 'nombre LIKE "%$nombre%"',
+      where: 'nombre LIKE ?',
+      whereArgs: ['%$nombre%'],
       orderBy: 'idLocal asc',
     );
-    List<Producto> aux = [];
-    for (var element in query) {
-      Producto producto = Producto.fromJson(element);
-      aux.add(producto);
-    }
-    _productos = aux;
+    _productos = query.map((e) => Producto.fromJson(e)).toList();
     notifyListeners();
   }
 
@@ -179,6 +178,75 @@ class ProductoViewModel<T> extends ChangeNotifier {
     return null;
   }
 
+  Future<bool> sincronizarCategorias() async {
+    try {
+      final response = ApiResponse(await BaseService().get('/pos/categoria'));
+
+      if (response.isOk && response.resultados != null) {
+        List<CategoriaProducto> categoriasApi = response.resultados!
+            .map((json) => CategoriaProducto.fromApi(json))
+            .toList();
+
+        if (categoriasApi.isNotEmpty) {
+          await RepoService().sincronizarCategorias(categoriasApi);
+          notifyListeners();
+          return true;
+        }
+      }
+      return false;
+    } catch (e) {
+      print('Error al sincronizar categorías: $e');
+      return false;
+    }
+  }
+
+  Future<bool> sincronizarProductos() async {
+    try {
+      Map<String, String> parametros = {"limite": "-1"};
+      final response = ApiResponse(await BaseService()
+          .get('/pos/producto', queryParameters: parametros));
+
+      if (response.isOk && response.resultados != null) {
+        List<Producto> productosApi =
+            response.resultados!.map((json) => Producto.fromApi(json)).toList();
+
+        if (productosApi.isNotEmpty) {
+          await RepoService().sincronizarProductos(productosApi);
+          notifyListeners();
+          return true;
+        }
+      }
+      return false;
+    } catch (e) {
+      print('Error al sincronizar productos: $e');
+      return false;
+    }
+  }
+
+  Future<void> sincronizarProductosYCategorias() async {
+    print('Sincronizando productos');
+    setIsLoading(true);
+    try {
+      bool categoriasSincronizadas = await sincronizarCategorias();
+      print('Categorias sincronizadas: $categoriasSincronizadas');
+
+      if (categoriasSincronizadas) {
+        bool productosSincronizados = await sincronizarProductos();
+        if (productosSincronizados) {
+          await fetchLocalAll();
+        }
+        print('Productos sincronizados: $productosSincronizados');
+      }
+      notifyListeners();
+    } catch (e, stackTrace) {
+      // Capturar el mensaje detallado del error
+      throw Exception(
+          "Error al sincronizar productos y categorías: $e\n$stackTrace");
+    } finally {
+      setIsLoading(false);
+    }
+  }
+
   void setIsLoading(bool loading) {
     _isLoading = loading;
     notifyListeners();

+ 51 - 7
lib/views/categoria_producto/categoria_producto_screen.dart

@@ -1,6 +1,6 @@
-import 'package:yoshi_papas_app/themes/themes.dart';
-import 'package:yoshi_papas_app/viewmodels/categoria_producto_view_model.dart';
-import 'package:yoshi_papas_app/widgets/app_textfield.dart';
+import '../../widgets/widgets.dart';
+import '../../themes/themes.dart';
+import '../../viewmodels/viewmodels.dart';
 import 'package:flutter/material.dart';
 import 'package:provider/provider.dart';
 import '../../models/models.dart';
@@ -16,6 +16,7 @@ class CategoriaProductoScreen extends StatefulWidget {
 class _CategoriaProductoScreenState extends State<CategoriaProductoScreen> {
   final _busqueda = TextEditingController(text: '');
   ScrollController horizontalScrollController = ScrollController();
+  int _versionTapCount = 0;
 
   @override
   void initState() {
@@ -45,6 +46,29 @@ class _CategoriaProductoScreenState extends State<CategoriaProductoScreen> {
     });
   }
 
+  void _mostrarResultado(BuildContext context, String mensaje, bool exito) {
+    showDialog(
+      context: context,
+      builder: (BuildContext context) {
+        return AlertDialog(
+          title: Text(
+              exito ? 'Sincronización exitosa' : 'Error de sincronización'),
+          content: SingleChildScrollView(
+            child: Text(mensaje), // Mostrar el mensaje completo de error aquí
+          ),
+          actions: [
+            TextButton(
+              child: Text('OK'),
+              onPressed: () {
+                Navigator.of(context).pop();
+              },
+            ),
+          ],
+        );
+      },
+    );
+  }
+
   @override
   Widget build(BuildContext context) {
     final model = Provider.of<CategoriaProductoViewModel>(context);
@@ -67,6 +91,7 @@ class _CategoriaProductoScreenState extends State<CategoriaProductoScreen> {
               PopupMenuItem(
                 child: const Text('Eliminar'),
                 onTap: () async {
+                  // Retrasa la ejecución para permitir que se cierre el menú popup.
                   await Future.delayed(Duration(milliseconds: 100));
                   bool confirmado = await showDialog<bool>(
                         context: context,
@@ -94,7 +119,7 @@ class _CategoriaProductoScreenState extends State<CategoriaProductoScreen> {
                                                 20, 10, 20, 10)),
                                         backgroundColor:
                                             MaterialStatePropertyAll(
-                                                AppTheme.primary),
+                                                Colors.red),
                                         foregroundColor:
                                             MaterialStatePropertyAll(
                                                 AppTheme.secondary)),
@@ -158,9 +183,28 @@ class _CategoriaProductoScreenState extends State<CategoriaProductoScreen> {
 
     return Scaffold(
       appBar: AppBar(
-        title: Text(
-          'Categoría Producto',
-          style: TextStyle(color: AppTheme.secondary),
+        title: GestureDetector(
+          onTap: () async {
+            _versionTapCount++;
+            if (_versionTapCount == 5) {
+              final productoViewModel =
+                  Provider.of<ProductoViewModel>(context, listen: false);
+
+              try {
+                await productoViewModel.sincronizarProductosYCategorias();
+                _mostrarResultado(context,
+                    'La sincronización se completó exitosamente.', true);
+              } catch (e) {
+                _mostrarResultado(context, e.toString(), false);
+              }
+              _versionTapCount = 0;
+            }
+          },
+          child: Text(
+            'Categoría Producto',
+            style: TextStyle(
+                color: AppTheme.secondary, fontWeight: FontWeight.w500),
+          ),
         ),
         iconTheme: IconThemeData(color: AppTheme.secondary),
       ),

+ 18 - 5
lib/views/home/home_screen.dart

@@ -1,8 +1,11 @@
 import 'package:flutter/material.dart';
 import 'package:yoshi_papas_app/widgets/widgets.dart';
+import 'package:flutter/material.dart';
 import 'package:provider/provider.dart';
 
-import '../../viewmodels/login_view_model.dart';
+import '../../themes/themes.dart';
+import '../../viewmodels/viewmodels.dart';
+import '../pedido/pedido_sync.dart';
 
 class HomeScreen extends StatefulWidget {
   const HomeScreen({super.key});
@@ -15,8 +18,15 @@ class Formulario extends State<HomeScreen> {
   @override
   void initState() {
     super.initState();
-    Future(() async {
-      await Provider.of<LoginViewModel>(context, listen: false).setValores();
+    // Future(() async {
+    //   await Provider.of<LoginViewModel>(context, listen: false).setValores();
+    // });
+    PedidoSync()
+        .startSync(Provider.of<PedidoViewModel>(context, listen: false));
+
+    WidgetsBinding.instance.addPostFrameCallback((_) {
+      Provider.of<ProductoViewModel>(context, listen: false)
+          .sincronizarProductosYCategorias();
     });
   }
 
@@ -26,8 +36,11 @@ class Formulario extends State<HomeScreen> {
       backgroundColor: Colors.grey.shade200,
       drawer: AppDrawer(),
       appBar: AppBar(
-        elevation: 0,
-        title: const Text('Inicio'),
+        title: Text(
+          'Inicio',
+          style: TextStyle(color: AppTheme.secondary),
+        ),
+        iconTheme: IconThemeData(color: AppTheme.secondary),
       ),
       body: const Center(
         child: Image(

+ 3 - 5
lib/views/pedido/pedido_csv.dart

@@ -4,7 +4,7 @@ import 'package:intl/intl.dart';
 import 'package:path_provider/path_provider.dart';
 import 'dart:io';
 import 'package:path/path.dart' as p;
-import 'package:yoshi_papas_app/models/pedido_model.dart';
+import '../../models/models.dart';
 
 Future<void> exportarPedidosACSV(List<Pedido> pedidos, String fileName) async {
   List<List<dynamic>> rows = [
@@ -34,8 +34,7 @@ Future<void> exportarPedidosACSV(List<Pedido> pedidos, String fileName) async {
           ? producto.toppings.map((t) {
               String toppingNombre =
                   t.topping?.nombre ?? 'Topping no especificado';
-              double toppingPrecio =
-                  double.tryParse(t.topping?.precio ?? '0') ?? 0.0;
+              double toppingPrecio = t.topping?.precio ?? 0.0;
 
               if (toppingPrecio > 0) {
                 toppingNombre += "(+\$${formatCurrency(toppingPrecio)})";
@@ -47,8 +46,7 @@ Future<void> exportarPedidosACSV(List<Pedido> pedidos, String fileName) async {
           : 'Sin toppings';
 
       // Calcular el total con descuento para este producto
-      double precioUnitario =
-          double.tryParse(producto.producto?.precio ?? '0') ?? 0.0;
+      double precioUnitario = producto.producto?.precio ?? 0.0;
       double subtotal =
           (precioUnitario + totalToppingsPrecio) * (producto.cantidad ?? 1);
       double descuento = pedido.descuento?.toDouble() ?? 0.0;

+ 7 - 8
lib/views/pedido/pedido_detalle_screen.dart

@@ -1,8 +1,8 @@
 import 'package:intl/intl.dart';
-import 'package:yoshi_papas_app/themes/themes.dart';
-import 'package:yoshi_papas_app/views/pedido/pedido_ticket.dart';
 import 'package:flutter/material.dart';
 import '../../models/models.dart';
+import '../../themes/themes.dart';
+import '../pedido/pedido_ticket.dart';
 
 class PedidoDetalleScreen extends StatelessWidget {
   final Pedido pedido;
@@ -18,13 +18,12 @@ class PedidoDetalleScreen extends StatelessWidget {
   Widget build(BuildContext context) {
     double totalSinDescuento =
         pedido.productos.fold(0, (previousValue, element) {
-      double productTotal = element.cantidad! *
-          (double.tryParse(element.producto?.precio ?? '') ?? 0.0);
+      double productTotal =
+          element.cantidad! * (element.producto?.precio ?? 0.0);
 
       double toppingsTotal = element.toppings.fold(0, (toppingTotal, topping) {
         return toppingTotal +
-            (double.tryParse(topping.topping?.precio ?? '') ?? 0.0) *
-                element.cantidad!;
+            (topping.topping?.precio ?? 0.0) * element.cantidad!;
       });
 
       return previousValue + productTotal + toppingsTotal;
@@ -124,7 +123,7 @@ class PedidoDetalleScreen extends StatelessWidget {
                                   Expanded(
                                     flex: 2,
                                     child: Text(
-                                      '\$${formatCurrency(double.tryParse(producto.producto?.precio ?? '0.0') ?? 0.0)}',
+                                      '\$${formatCurrency(producto.producto?.precio ?? 0.0)}',
                                       style: TextStyle(
                                           fontWeight: FontWeight.w500,
                                           fontSize: 17),
@@ -153,7 +152,7 @@ class PedidoDetalleScreen extends StatelessWidget {
                                             ),
                                             Spacer(),
                                             Text(
-                                              '\$${formatCurrency(double.tryParse(topping.topping?.precio ?? '0.0') ?? 0.0)}',
+                                              '\$${formatCurrency(topping.topping?.precio ?? 0.0)}',
                                               style: TextStyle(
                                                   fontSize: 15,
                                                   color: Colors.grey[600]),

+ 579 - 276
lib/views/pedido/pedido_form.dart

@@ -2,6 +2,7 @@ import 'dart:async';
 import 'dart:io';
 import 'package:flutter/foundation.dart';
 import 'package:flutter/material.dart';
+import 'package:flutter/services.dart';
 import 'package:intl/intl.dart';
 import 'package:provider/provider.dart';
 import 'package:yoshi_papas_app/views/pedido/pedido_ticket.dart';
@@ -31,6 +32,7 @@ class _PedidoFormState extends State<PedidoForm> {
   bool _estadoBusqueda = false;
   Pedido? pedidoActual;
   ScrollController _gridViewController = ScrollController();
+  ScrollController _categoryScrollController = ScrollController();
   final _searchController = TextEditingController();
   final NumberFormat _numberFormat = NumberFormat.decimalPattern('es_MX');
   int? selectedDescuento = 0;
@@ -50,14 +52,14 @@ class _PedidoFormState extends State<PedidoForm> {
     double total = 0;
 
     for (var item in carrito) {
-      total += double.parse(item.producto.precio!) * item.cantidad;
+      total += item.producto.precio! * item.cantidad;
       item.selectedToppings.forEach((categoryId, selectedToppingIds) {
         for (int toppingId in selectedToppingIds) {
           Producto? topping = item.selectableToppings[categoryId]?.firstWhere(
               (topping) => topping.id == toppingId,
-              orElse: () => Producto(precio: '0'));
+              orElse: () => Producto(precio: 0));
           if (topping != null) {
-            total += (double.tryParse(topping.precio!) ?? 0) * item.cantidad;
+            total += (topping.precio ?? 0.0) * item.cantidad;
           }
         }
       });
@@ -90,10 +92,15 @@ class _PedidoFormState extends State<PedidoForm> {
     Provider.of<DescuentoViewModel>(context, listen: false).cargarDescuentos();
   }
 
-  _onSearchChanged(String value) {
+  void _onSearchChanged(String value) {
     if (value.isEmpty) {
-      cargarProductosIniciales();
+      cargarProductosPorCategoria(
+          categoriaSeleccionada?.id ?? categorias.first.id);
     } else {
+      setState(() {
+        _estadoBusqueda = true;
+        categoriaSeleccionada = null;
+      });
       Provider.of<ProductoViewModel>(context, listen: false)
           .fetchLocalByName(nombre: value);
     }
@@ -157,6 +164,9 @@ class _PedidoFormState extends State<PedidoForm> {
     String errorMessage = '';
     double faltante = totalPedido;
     bool totalCompletado = false;
+    bool efectivoCompleto = false;
+    bool tarjetaCompleto = false;
+    bool transferenciaCompleto = false;
 
     void _calcularCambio(StateSetter setState) {
       double totalPagado = (double.tryParse(efectivoController.text) ?? 0) +
@@ -173,6 +183,13 @@ class _PedidoFormState extends State<PedidoForm> {
           faltante = 0;
           totalCompletado = true;
         }
+
+        // Si el total ha sido alcanzado o excedido, desactivar otros métodos de pago
+        if (totalPagado >= totalPedido) {
+          if (!efectivoSeleccionado) efectivoSeleccionado = false;
+          if (!tarjetaSeleccionada) tarjetaSeleccionada = false;
+          if (!transferenciaSeleccionada) transferenciaSeleccionada = false;
+        }
       });
     }
 
@@ -187,254 +204,516 @@ class _PedidoFormState extends State<PedidoForm> {
       _calcularCambio(setState);
     }
 
+    bool _isPaymentOptionEnabled(bool isSelected) {
+      return !totalCompletado || isSelected;
+    }
+
     bool? shouldSave = await showDialog<bool>(
       context: context,
       builder: (BuildContext context) {
         return StatefulBuilder(
           builder: (context, setState) {
-            return AlertDialog(
-              actionsPadding: EdgeInsets.fromLTRB(50, 10, 50, 30),
-              title: const Text(
-                'Finalizar Pedido',
-                style: TextStyle(fontSize: 22, fontWeight: FontWeight.w500),
-              ),
-              content: Column(
-                mainAxisSize: MainAxisSize.min,
-                children: [
-                  AppTextField(
-                    controller: nombreController,
-                    etiqueta: 'Nombre',
-                    hintText: "Nombre del Cliente",
-                    errorText: errorMessage.isEmpty ? null : errorMessage,
-                    onChanged: (value) {
-                      setState(() {
-                        errorMessage = value.trim().isEmpty
-                            ? "El nombre del cliente es obligatorio."
-                            : '';
-                      });
-                    },
-                  ),
-                  const SizedBox(height: 10),
-                  AppTextField(
-                    controller: comentarioController,
-                    etiqueta: 'Comentarios (opcional)',
-                    hintText: 'Comentarios',
-                    maxLines: 3,
-                  ),
-                  const SizedBox(height: 10),
-                  Align(
-                    alignment: Alignment.center,
-                    child: Text(
-                      'Métodos de pago',
-                      style:
-                          TextStyle(fontWeight: FontWeight.bold, fontSize: 20),
-                    ),
-                  ),
-                  const SizedBox(height: 10),
-                  // Efectivo
-                  Row(
-                    mainAxisAlignment: MainAxisAlignment.spaceBetween,
-                    children: [
-                      Row(
-                        children: [
-                          Checkbox(
-                            activeColor: AppTheme.primary,
-                            value: efectivoSeleccionado,
-                            onChanged:
-                                (totalCompletado && !efectivoSeleccionado)
-                                    ? null
-                                    : (bool? value) {
-                                        setState(() {
-                                          efectivoSeleccionado = value ?? false;
-                                          if (!efectivoSeleccionado) {
-                                            efectivoController.clear();
-                                            _calcularCambio(setState);
-                                          }
-                                        });
-                                      },
-                          ),
-                          const Text(
-                            "Efectivo",
-                            style: TextStyle(
-                                fontSize: 18, fontWeight: FontWeight.bold),
-                          ),
-                        ],
-                      ),
-                      if (efectivoSeleccionado)
-                        SizedBox(
-                          width: 150,
-                          child: AppTextField(
-                            controller: efectivoController,
-                            etiqueta: 'Cantidad',
-                            hintText: '0.00',
-                            keyboardType: TextInputType.number,
-                            onChanged: (value) => _calcularCambio(setState),
-                          ),
-                        ),
-                    ],
+            return RawKeyboardListener(
+                focusNode: FocusNode(),
+                onKey: (RawKeyEvent event) {
+                  if (event.isKeyPressed(LogicalKeyboardKey.enter) &&
+                      totalCompletado) {
+                    Navigator.of(context).pop(true);
+                  }
+                },
+                child: AlertDialog(
+                  actionsPadding: EdgeInsets.fromLTRB(50, 10, 50, 30),
+                  title: const Text(
+                    'Finalizar Pedido',
+                    style: TextStyle(fontSize: 22, fontWeight: FontWeight.w500),
                   ),
-                  const SizedBox(height: 10),
-                  // Tarjeta
-                  Row(
-                    mainAxisAlignment: MainAxisAlignment.spaceBetween,
-                    children: [
-                      Row(
-                        children: [
-                          Checkbox(
-                            activeColor: AppTheme.primary,
-                            value: tarjetaSeleccionada,
-                            onChanged: (totalCompletado && !tarjetaSeleccionada)
-                                ? null
-                                : (bool? value) {
-                                    setState(() {
-                                      tarjetaSeleccionada = value ?? false;
-                                      if (!tarjetaSeleccionada) {
-                                        tarjetaController.clear();
-                                        _calcularCambio(setState);
-                                      }
-                                    });
+                  content: Container(
+                    height: 600,
+                    child: Column(
+                      children: [
+                        Expanded(
+                          child: SingleChildScrollView(
+                            child: Column(
+                              children: [
+                                AppTextField(
+                                  controller: nombreController,
+                                  etiqueta: 'Nombre',
+                                  hintText: "Nombre del Cliente",
+                                ),
+                                const SizedBox(height: 10),
+                                AppTextField(
+                                  controller: comentarioController,
+                                  etiqueta: 'Comentarios (opcional)',
+                                  hintText: 'Comentarios',
+                                  maxLines: 2,
+                                ),
+                                const SizedBox(height: 10),
+                                Align(
+                                  alignment: Alignment.center,
+                                  child: Text(
+                                    'Métodos de pago',
+                                    style: TextStyle(
+                                        fontWeight: FontWeight.bold,
+                                        fontSize: 20),
+                                  ),
+                                ),
+                                const SizedBox(height: 10),
+                                // Efectivo
+                                GestureDetector(
+                                  onTap: () {
+                                    if (_isPaymentOptionEnabled(
+                                        efectivoSeleccionado)) {
+                                      setState(() {
+                                        efectivoSeleccionado =
+                                            !efectivoSeleccionado;
+                                        if (!efectivoSeleccionado) {
+                                          efectivoCompleto = false;
+                                          efectivoController.clear();
+                                          _calcularCambio(setState);
+                                        } else if (efectivoCompleto) {
+                                          efectivoController.text =
+                                              totalPedido.toStringAsFixed(2);
+                                          _calcularCambio(setState);
+                                        }
+                                      });
+                                    }
                                   },
-                          ),
-                          const Text(
-                            "Tarjeta",
-                            style: TextStyle(
-                                fontSize: 18, fontWeight: FontWeight.bold),
-                          ),
-                        ],
-                      ),
-                      if (tarjetaSeleccionada)
-                        SizedBox(
-                          width: 150,
-                          child: AppTextField(
-                            controller: tarjetaController,
-                            etiqueta: 'Cantidad',
-                            hintText: '0.00',
-                            keyboardType: TextInputType.number,
-                            onChanged: (value) {
-                              _validarCantidad(setState, tarjetaController);
-                            },
+                                  child: Row(
+                                    mainAxisAlignment:
+                                        MainAxisAlignment.spaceBetween,
+                                    crossAxisAlignment:
+                                        CrossAxisAlignment.center,
+                                    children: [
+                                      Row(
+                                        children: [
+                                          Checkbox(
+                                            activeColor: AppTheme.primary,
+                                            value: efectivoSeleccionado,
+                                            onChanged: _isPaymentOptionEnabled(
+                                                    efectivoSeleccionado)
+                                                ? (bool? value) {
+                                                    setState(() {
+                                                      efectivoSeleccionado =
+                                                          value ?? false;
+                                                      if (!efectivoSeleccionado) {
+                                                        efectivoCompleto =
+                                                            false;
+                                                        efectivoController
+                                                            .clear();
+                                                        _calcularCambio(
+                                                            setState);
+                                                      } else if (efectivoCompleto) {
+                                                        efectivoController
+                                                                .text =
+                                                            totalPedido
+                                                                .toStringAsFixed(
+                                                                    2);
+                                                        _calcularCambio(
+                                                            setState);
+                                                      }
+                                                    });
+                                                  }
+                                                : null,
+                                          ),
+                                          const Text(
+                                            "Efectivo",
+                                            style: TextStyle(
+                                                fontSize: 18,
+                                                fontWeight: FontWeight.bold),
+                                          ),
+                                        ],
+                                      ),
+                                      if (efectivoSeleccionado)
+                                        SizedBox(
+                                          width: 180,
+                                          child: Row(
+                                            crossAxisAlignment:
+                                                CrossAxisAlignment.start,
+                                            children: [
+                                              Column(
+                                                children: [
+                                                  const Text('Exacto',
+                                                      style: TextStyle(
+                                                          fontSize: 18,
+                                                          fontWeight:
+                                                              FontWeight.bold,
+                                                          color: Colors.black)),
+                                                  const SizedBox(
+                                                    height: 17,
+                                                  ),
+                                                  Checkbox(
+                                                    activeColor:
+                                                        AppTheme.primary,
+                                                    value: efectivoCompleto,
+                                                    onChanged:
+                                                        efectivoSeleccionado
+                                                            ? (bool? value) {
+                                                                setState(() {
+                                                                  efectivoCompleto =
+                                                                      value ??
+                                                                          false;
+                                                                  if (efectivoCompleto) {
+                                                                    efectivoController
+                                                                            .text =
+                                                                        totalPedido
+                                                                            .toStringAsFixed(2);
+                                                                    _calcularCambio(
+                                                                        setState);
+                                                                  } else {
+                                                                    efectivoController
+                                                                        .clear();
+                                                                    _calcularCambio(
+                                                                        setState);
+                                                                  }
+                                                                });
+                                                              }
+                                                            : null,
+                                                  ),
+                                                ],
+                                              ),
+                                              const SizedBox(
+                                                width: 5,
+                                              ),
+                                              Expanded(
+                                                child: AppTextField(
+                                                  controller:
+                                                      efectivoController,
+                                                  etiqueta: 'Cantidad',
+                                                  hintText: '0.00',
+                                                  keyboardType:
+                                                      TextInputType.number,
+                                                  onChanged: (value) =>
+                                                      _calcularCambio(setState),
+                                                ),
+                                              ),
+                                            ],
+                                          ),
+                                        ),
+                                    ],
+                                  ),
+                                ),
+                                const SizedBox(height: 10),
+                                // Tarjeta
+                                GestureDetector(
+                                  onTap: () {
+                                    if (_isPaymentOptionEnabled(
+                                        tarjetaSeleccionada)) {
+                                      setState(() {
+                                        tarjetaSeleccionada =
+                                            !tarjetaSeleccionada;
+                                        if (!tarjetaSeleccionada) {
+                                          tarjetaController.clear();
+                                          _calcularCambio(setState);
+                                        }
+                                      });
+                                    }
+                                  },
+                                  child: Row(
+                                    mainAxisAlignment:
+                                        MainAxisAlignment.spaceBetween,
+                                    crossAxisAlignment:
+                                        CrossAxisAlignment.center,
+                                    children: [
+                                      Row(
+                                        children: [
+                                          Checkbox(
+                                            activeColor: AppTheme.primary,
+                                            value: tarjetaSeleccionada,
+                                            onChanged: _isPaymentOptionEnabled(
+                                                    tarjetaSeleccionada)
+                                                ? (bool? value) {
+                                                    setState(() {
+                                                      tarjetaSeleccionada =
+                                                          value ?? false;
+                                                      if (!tarjetaSeleccionada) {
+                                                        tarjetaController
+                                                            .clear();
+                                                        _calcularCambio(
+                                                            setState);
+                                                      }
+                                                    });
+                                                  }
+                                                : null,
+                                          ),
+                                          const Text(
+                                            "Tarjeta",
+                                            style: TextStyle(
+                                                fontSize: 18,
+                                                fontWeight: FontWeight.bold),
+                                          ),
+                                        ],
+                                      ),
+                                      if (tarjetaSeleccionada)
+                                        SizedBox(
+                                          width: 180,
+                                          child: Row(
+                                            crossAxisAlignment:
+                                                CrossAxisAlignment.start,
+                                            children: [
+                                              Column(
+                                                children: [
+                                                  const Text('Exacto',
+                                                      style: TextStyle(
+                                                          fontSize: 18,
+                                                          fontWeight:
+                                                              FontWeight.bold,
+                                                          color: Colors.black)),
+                                                  const SizedBox(
+                                                    height: 17,
+                                                  ),
+                                                  Checkbox(
+                                                    activeColor:
+                                                        AppTheme.primary,
+                                                    value: tarjetaCompleto,
+                                                    onChanged:
+                                                        tarjetaSeleccionada
+                                                            ? (bool? value) {
+                                                                setState(() {
+                                                                  tarjetaCompleto =
+                                                                      value ??
+                                                                          false;
+                                                                  if (tarjetaCompleto) {
+                                                                    tarjetaController
+                                                                            .text =
+                                                                        totalPedido
+                                                                            .toStringAsFixed(2);
+                                                                    _calcularCambio(
+                                                                        setState);
+                                                                  } else {
+                                                                    tarjetaController
+                                                                        .clear();
+                                                                    _calcularCambio(
+                                                                        setState);
+                                                                  }
+                                                                });
+                                                              }
+                                                            : null,
+                                                  ),
+                                                ],
+                                              ),
+                                              const SizedBox(
+                                                width: 5,
+                                              ),
+                                              Expanded(
+                                                child: AppTextField(
+                                                  controller: tarjetaController,
+                                                  etiqueta: 'Cantidad',
+                                                  hintText: '0.00',
+                                                  keyboardType:
+                                                      TextInputType.number,
+                                                  onChanged: (value) {
+                                                    _validarCantidad(setState,
+                                                        tarjetaController);
+                                                  },
+                                                ),
+                                              ),
+                                            ],
+                                          ),
+                                        ),
+                                    ],
+                                  ),
+                                ),
+                                const SizedBox(height: 10),
+                                // Transferencia
+                                GestureDetector(
+                                  onTap: () {
+                                    if (_isPaymentOptionEnabled(
+                                        transferenciaSeleccionada)) {
+                                      setState(() {
+                                        transferenciaSeleccionada =
+                                            !transferenciaSeleccionada;
+                                        if (!transferenciaSeleccionada) {
+                                          transferenciaController.clear();
+                                          _calcularCambio(setState);
+                                        }
+                                      });
+                                    }
+                                  },
+                                  child: Row(
+                                    mainAxisAlignment:
+                                        MainAxisAlignment.spaceBetween,
+                                    crossAxisAlignment:
+                                        CrossAxisAlignment.center,
+                                    children: [
+                                      Row(
+                                        children: [
+                                          Checkbox(
+                                            activeColor: AppTheme.primary,
+                                            value: transferenciaSeleccionada,
+                                            onChanged: _isPaymentOptionEnabled(
+                                                    transferenciaSeleccionada)
+                                                ? (bool? value) {
+                                                    setState(() {
+                                                      transferenciaSeleccionada =
+                                                          value ?? false;
+                                                      if (!transferenciaSeleccionada) {
+                                                        transferenciaController
+                                                            .clear();
+                                                        _calcularCambio(
+                                                            setState);
+                                                      }
+                                                    });
+                                                  }
+                                                : null,
+                                          ),
+                                          const Text(
+                                            "Transferencia",
+                                            style: TextStyle(
+                                                fontSize: 18,
+                                                fontWeight: FontWeight.bold),
+                                          ),
+                                        ],
+                                      ),
+                                      if (transferenciaSeleccionada)
+                                        SizedBox(
+                                          width: 180,
+                                          child: Row(
+                                            crossAxisAlignment:
+                                                CrossAxisAlignment.start,
+                                            children: [
+                                              Column(
+                                                children: [
+                                                  const Text('Exacto',
+                                                      style: TextStyle(
+                                                          fontSize: 18,
+                                                          fontWeight:
+                                                              FontWeight.bold,
+                                                          color: Colors.black)),
+                                                  const SizedBox(
+                                                    height: 17,
+                                                  ),
+                                                  Checkbox(
+                                                    activeColor:
+                                                        AppTheme.primary,
+                                                    value:
+                                                        transferenciaCompleto,
+                                                    onChanged:
+                                                        transferenciaSeleccionada
+                                                            ? (bool? value) {
+                                                                setState(() {
+                                                                  transferenciaCompleto =
+                                                                      value ??
+                                                                          false;
+                                                                  if (transferenciaCompleto) {
+                                                                    transferenciaController
+                                                                            .text =
+                                                                        totalPedido
+                                                                            .toStringAsFixed(2);
+                                                                    _calcularCambio(
+                                                                        setState);
+                                                                  } else {
+                                                                    transferenciaController
+                                                                        .clear();
+                                                                    _calcularCambio(
+                                                                        setState);
+                                                                  }
+                                                                });
+                                                              }
+                                                            : null,
+                                                  ),
+                                                ],
+                                              ),
+                                              const SizedBox(
+                                                width: 5,
+                                              ),
+                                              Expanded(
+                                                child: AppTextField(
+                                                  controller:
+                                                      transferenciaController,
+                                                  etiqueta: 'Cantidad',
+                                                  hintText: '0.00',
+                                                  keyboardType:
+                                                      TextInputType.number,
+                                                  onChanged: (value) {
+                                                    _validarCantidad(setState,
+                                                        transferenciaController);
+                                                  },
+                                                ),
+                                              ),
+                                            ],
+                                          ),
+                                        ),
+                                    ],
+                                  ),
+                                ),
+                                const SizedBox(height: 10),
+                                // Mostrar el total del pedido y la cantidad faltante
+                                Align(
+                                  alignment: Alignment.centerRight,
+                                  child: Column(
+                                      crossAxisAlignment:
+                                          CrossAxisAlignment.end,
+                                      children: [
+                                        Text(
+                                          'Total del pedido: \$${totalPedido.toStringAsFixed(2)}',
+                                          style: const TextStyle(
+                                              fontWeight: FontWeight.bold,
+                                              fontSize: 18),
+                                        ),
+                                        if (faltante > 0)
+                                          Text(
+                                            'Faltante: \$${faltante.toStringAsFixed(2)}',
+                                            style: const TextStyle(
+                                                color: Colors.red,
+                                                fontSize: 18,
+                                                fontWeight: FontWeight.bold),
+                                          )
+                                        else if (cambio > 0)
+                                          Text(
+                                              'Cambio: \$${cambio.toStringAsFixed(2)}',
+                                              style: const TextStyle(
+                                                  color: Colors.green,
+                                                  fontSize: 18,
+                                                  fontWeight: FontWeight.bold)),
+                                      ]),
+                                ),
+                              ],
+                            ),
                           ),
                         ),
-                    ],
-                  ),
-                  const SizedBox(height: 10),
-                  // Transferencia
-                  Row(
-                    mainAxisAlignment: MainAxisAlignment.spaceBetween,
-                    children: [
-                      Row(
-                        children: [
-                          Checkbox(
-                            activeColor: AppTheme.primary,
-                            value: transferenciaSeleccionada,
-                            onChanged:
-                                (totalCompletado && !transferenciaSeleccionada)
-                                    ? null
-                                    : (bool? value) {
-                                        setState(() {
-                                          transferenciaSeleccionada =
-                                              value ?? false;
-                                          if (!transferenciaSeleccionada) {
-                                            transferenciaController.clear();
-                                            _calcularCambio(setState);
-                                          }
-                                        });
-                                      },
-                          ),
-                          const Text(
-                            "Transferencia",
-                            style: TextStyle(
-                                fontSize: 18, fontWeight: FontWeight.bold),
-                          ),
-                        ],
-                      ),
-                      if (transferenciaSeleccionada)
-                        SizedBox(
-                          width: 150,
-                          child: AppTextField(
-                            controller: transferenciaController,
-                            etiqueta: 'Cantidad',
-                            hintText: '0.00',
-                            keyboardType: TextInputType.number,
-                            onChanged: (value) {
-                              _validarCantidad(
-                                  setState, transferenciaController);
-                            },
-                          ),
+                        // Aquí mantenemos los botones fijos
+                        Row(
+                          mainAxisAlignment: MainAxisAlignment.spaceBetween,
+                          children: [
+                            TextButton(
+                              child: const Text('Cancelar',
+                                  style: TextStyle(fontSize: 18)),
+                              onPressed: () {
+                                Navigator.of(context).pop(false);
+                              },
+                              style: ButtonStyle(
+                                  padding: MaterialStatePropertyAll(
+                                      EdgeInsets.fromLTRB(30, 20, 30, 20)),
+                                  backgroundColor:
+                                      MaterialStatePropertyAll(Colors.red),
+                                  foregroundColor: MaterialStatePropertyAll(
+                                      AppTheme.secondary)),
+                            ),
+                            const SizedBox(width: 100),
+                            TextButton(
+                              child: const Text('Guardar',
+                                  style: TextStyle(fontSize: 18)),
+                              onPressed: totalCompletado
+                                  ? () {
+                                      Navigator.of(context).pop(true);
+                                    }
+                                  : null,
+                              style: ButtonStyle(
+                                  padding: MaterialStatePropertyAll(
+                                      EdgeInsets.fromLTRB(30, 20, 30, 20)),
+                                  backgroundColor: MaterialStatePropertyAll(
+                                      totalCompletado
+                                          ? AppTheme.tertiary
+                                          : Colors.grey),
+                                  foregroundColor: MaterialStatePropertyAll(
+                                      AppTheme.quaternary)),
+                            ),
+                          ],
                         ),
-                    ],
-                  ),
-                  const SizedBox(height: 10),
-                  // Mostrar el total del pedido y la cantidad faltante
-                  Align(
-                    alignment: Alignment.centerRight,
-                    child: Column(
-                        crossAxisAlignment: CrossAxisAlignment.end,
-                        children: [
-                          Text(
-                            'Total del pedido: \$${totalPedido.toStringAsFixed(2)}',
-                            style: const TextStyle(
-                                fontWeight: FontWeight.bold, fontSize: 18),
-                          ),
-                          if (faltante > 0)
-                            Text(
-                              'Faltante: \$${faltante.toStringAsFixed(2)}',
-                              style: const TextStyle(
-                                  color: Colors.red,
-                                  fontSize: 18,
-                                  fontWeight: FontWeight.bold),
-                            )
-                          else if (cambio > 0)
-                            Text('Cambio: \$${cambio.toStringAsFixed(2)}',
-                                style: const TextStyle(
-                                    color: Colors.green,
-                                    fontSize: 18,
-                                    fontWeight: FontWeight.bold)),
-                        ]),
-                  ),
-                ],
-              ),
-              actions: <Widget>[
-                Row(
-                  mainAxisAlignment: MainAxisAlignment.spaceBetween,
-                  children: [
-                    TextButton(
-                      child: const Text('Cancelar',
-                          style: TextStyle(fontSize: 18)),
-                      onPressed: () {
-                        Navigator.of(context).pop(false);
-                      },
-                      style: ButtonStyle(
-                          padding: MaterialStatePropertyAll(
-                              EdgeInsets.fromLTRB(30, 20, 30, 20)),
-                          backgroundColor: MaterialStatePropertyAll(Colors.red),
-                          foregroundColor:
-                              MaterialStatePropertyAll(AppTheme.secondary)),
-                    ),
-                    const SizedBox(width: 100),
-                    TextButton(
-                      child:
-                          const Text('Guardar', style: TextStyle(fontSize: 18)),
-                      onPressed: () {
-                        if (nombreController.text.trim().isEmpty) {
-                          setState(() => errorMessage =
-                              "El nombre del cliente es obligatorio.");
-                          return;
-                        }
-                        Navigator.of(context).pop(true);
-                      },
-                      style: ButtonStyle(
-                          padding: MaterialStatePropertyAll(
-                              EdgeInsets.fromLTRB(30, 20, 30, 20)),
-                          backgroundColor:
-                              MaterialStatePropertyAll(AppTheme.tertiary),
-                          foregroundColor:
-                              MaterialStatePropertyAll(AppTheme.quaternary)),
+                      ],
                     ),
-                  ],
-                )
-              ],
-            );
+                  ),
+                ));
           },
         );
       },
@@ -446,11 +725,10 @@ class _PedidoFormState extends State<PedidoForm> {
   }
 
   void prepararPedidoActual(String nombreCliente, String comentarios) async {
-    DateTime now = DateTime.now();
-    String formattedDate = DateFormat('dd-MM-yyyy kk:mm:ss').format(now);
+    String now = DateTime.now().toUtc().toIso8601String();
 
     Pedido nuevoPedido = Pedido(
-      peticion: formattedDate,
+      peticion: now,
       nombreCliente: nombreCliente,
       comentarios: comentarios,
       estatus: "NUEVO",
@@ -480,7 +758,7 @@ class _PedidoFormState extends State<PedidoForm> {
       return PedidoProducto(
         idProducto: item.producto.id,
         producto: item.producto,
-        costoUnitario: item.producto.precio,
+        costoUnitario: item.producto.precio.toString(),
         cantidad: item.cantidad,
         comentario: comentarios,
         toppings: selectedToppings,
@@ -537,6 +815,7 @@ class _PedidoFormState extends State<PedidoForm> {
   void dispose() {
     _gridViewController.dispose();
     _searchController.dispose();
+    _categoryScrollController.dispose();
     super.dispose();
   }
 
@@ -860,9 +1139,7 @@ class _PedidoFormState extends State<PedidoForm> {
                                             MainAxisAlignment.spaceBetween,
                                         children: [
                                           Text(topping.nombre!),
-                                          if (double.tryParse(
-                                                  topping.precio!) !=
-                                              0.0)
+                                          if (topping.precio != 0.0)
                                             Text(
                                               '+\$${topping.precio}',
                                               style: TextStyle(
@@ -969,6 +1246,8 @@ class _PedidoFormState extends State<PedidoForm> {
   Widget _buildProductsSection() {
     return Column(
       children: [
+        const SizedBox(height: 5),
+        _buildSearchBar(),
         const SizedBox(height: 10),
         _buildCategoryButtons(),
         const SizedBox(height: 15),
@@ -1040,34 +1319,44 @@ class _PedidoFormState extends State<PedidoForm> {
     List<CategoriaProducto> categoriasFiltradas =
         categorias.where((categoria) => categoria.esToping == 0).toList();
 
-    return Container(
-      height: 50,
-      child: ListView.builder(
-        scrollDirection: Axis.horizontal,
-        itemCount: categoriasFiltradas.length,
-        itemBuilder: (context, index) {
-          final categoria = categoriasFiltradas[index];
-          bool isSelected = categoriaSeleccionada?.id == categoria.id;
-          return Padding(
-            padding: const EdgeInsets.symmetric(horizontal: 4.0),
-            child: ElevatedButton(
-              onPressed: () {
-                cargarProductosPorCategoria(categoria.id);
-                setState(() {
-                  categoriaSeleccionada = categoria;
-                });
-              },
-              style: ElevatedButton.styleFrom(
-                primary: isSelected ? AppTheme.tertiary : Colors.grey,
-                foregroundColor:
-                    isSelected ? AppTheme.quaternary : AppTheme.secondary,
-                onPrimary: AppTheme.secondary,
+    return Column(
+      children: [
+        Container(
+          height: 50,
+          child: Stack(
+            children: [
+              ListView.builder(
+                controller: _categoryScrollController,
+                scrollDirection: Axis.horizontal,
+                itemCount: categoriasFiltradas.length,
+                itemBuilder: (context, index) {
+                  final categoria = categoriasFiltradas[index];
+                  bool isSelected = categoriaSeleccionada?.id == categoria.id;
+                  return Padding(
+                    padding: const EdgeInsets.symmetric(horizontal: 4.0),
+                    child: ElevatedButton(
+                      onPressed: () {
+                        cargarProductosPorCategoria(categoria.id);
+                        setState(() {
+                          categoriaSeleccionada = categoria;
+                        });
+                      },
+                      style: ElevatedButton.styleFrom(
+                        backgroundColor:
+                            isSelected ? AppTheme.tertiary : Colors.grey,
+                        foregroundColor: isSelected
+                            ? AppTheme.quaternary
+                            : AppTheme.secondary,
+                      ),
+                      child: Text(categoria.nombre!),
+                    ),
+                  );
+                },
               ),
-              child: Text(categoria.nombre!),
-            ),
-          );
-        },
-      ),
+            ],
+          ),
+        ),
+      ],
     );
   }
 
@@ -1079,9 +1368,23 @@ class _PedidoFormState extends State<PedidoForm> {
         decoration: InputDecoration(
           hintText: 'Buscar producto...',
           prefixIcon: const Icon(Icons.search),
+          suffixIcon: IconButton(
+            icon: Icon(Icons.clear),
+            onPressed: () {
+              _searchController.clear();
+              _onSearchChanged('');
+            },
+          ),
           border: OutlineInputBorder(
             borderRadius: BorderRadius.circular(12.0),
           ),
+          enabledBorder: OutlineInputBorder(
+              borderRadius: BorderRadius.circular(12.0),
+              borderSide: BorderSide(width: 1.5)),
+          focusedBorder: OutlineInputBorder(
+            borderSide: BorderSide(color: Colors.black),
+            borderRadius: BorderRadius.circular(12.0),
+          ),
         ),
         onChanged: _onSearchChanged,
       ),

+ 325 - 255
lib/views/pedido/pedido_screen.dart

@@ -2,14 +2,15 @@ import 'package:flutter/material.dart';
 import 'package:intl/intl.dart';
 import 'package:omni_datetime_picker/omni_datetime_picker.dart';
 import 'package:provider/provider.dart';
-import 'package:yoshi_papas_app/themes/themes.dart';
-import 'package:yoshi_papas_app/views/pedido/pedido_csv.dart';
-import 'package:yoshi_papas_app/views/pedido/pedido_detalle_screen.dart';
+import '../pedido/pedido_csv.dart';
+import '../pedido/pedido_detalle_screen.dart';
+import '../../widgets/widgets.dart';
+import '../../themes/themes.dart';
 import '../../models/models.dart';
 import '../../viewmodels/viewmodels.dart';
-import '../../widgets/app_textfield.dart';
 import '../../widgets/widgets_components.dart' as clase;
 import 'pedido_form.dart';
+import 'pedido_sync.dart';
 
 class PedidoScreen extends StatefulWidget {
   const PedidoScreen({Key? key}) : super(key: key);
@@ -19,6 +20,7 @@ class PedidoScreen extends StatefulWidget {
 }
 
 class _PedidoScreenState extends State<PedidoScreen> {
+  int _syncAgainTapCount = 0;
   final _busqueda = TextEditingController(text: '');
   DateTime? fechaInicio;
   DateTime? fechaFin;
@@ -36,30 +38,18 @@ class _PedidoScreenState extends State<PedidoScreen> {
   void exportCSV() async {
     final pedidosViewModel =
         Provider.of<PedidoViewModel>(context, listen: false);
+    List<Pedido> pedidosConProductos = [];
 
-    List<Pedido> pedidosConProductos;
-
-    // Si hay un rango de fechas seleccionado, obtenemos los pedidos de ese rango
-    if (fechaInicio != null && fechaFin != null) {
-      pedidosConProductos = await pedidosViewModel.fetchPedidosPorFechaSinLimit(
-          fechaInicio!, fechaFin!);
-    } else {
-      // De lo contrario, obtenemos todos los pedidos
-      pedidosConProductos = await pedidosViewModel.fetchAllLocalPedidos();
-    }
-
-    List<Pedido> pedidosConDetalles = [];
-
-    for (Pedido pedido in pedidosConProductos) {
+    for (Pedido pedido in pedidosViewModel.pedidos) {
       Pedido? pedidoConProductos =
           await pedidosViewModel.fetchPedidoConProductos(pedido.id);
       if (pedidoConProductos != null) {
-        pedidosConDetalles.add(pedidoConProductos);
+        pedidosConProductos.add(pedidoConProductos);
       }
     }
 
-    if (pedidosConDetalles.isNotEmpty) {
-      String fileName = 'Pedidos_JoshiPapas_POS';
+    if (pedidosConProductos.isNotEmpty) {
+      String fileName = 'Pedidos_Conalep_POS';
       if (fechaInicio != null && fechaFin != null) {
         String startDateStr = DateFormat('dd-MM-yyyy').format(fechaInicio!);
         String endDateStr = DateFormat('dd-MM-yyyy').format(fechaFin!);
@@ -67,7 +57,7 @@ class _PedidoScreenState extends State<PedidoScreen> {
       }
       fileName += '.csv';
 
-      await exportarPedidosACSV(pedidosConDetalles, fileName);
+      await exportarPedidosACSV(pedidosConProductos, fileName);
       ScaffoldMessenger.of(context).showSnackBar(SnackBar(
           content: Text('Archivo CSV descargado! Archivo: $fileName')));
     } else {
@@ -111,6 +101,11 @@ class _PedidoScreenState extends State<PedidoScreen> {
     TextStyle estilo = const TextStyle(fontWeight: FontWeight.bold);
     List<DataRow> registros = [];
     for (Pedido item in pvm.pedidos) {
+      final sincronizadoStatus =
+          item.sincronizado == null || item.sincronizado!.isEmpty
+              ? "No Sincronizado"
+              : _formatDateTime(item.sincronizado);
+
       registros.add(DataRow(cells: [
         DataCell(
             Row(mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [
@@ -149,7 +144,7 @@ class _PedidoScreenState extends State<PedidoScreen> {
                                                 20, 10, 20, 10)),
                                         backgroundColor:
                                             MaterialStatePropertyAll(
-                                                AppTheme.primary),
+                                                Colors.red),
                                         foregroundColor:
                                             MaterialStatePropertyAll(
                                                 AppTheme.secondary)),
@@ -207,7 +202,11 @@ class _PedidoScreenState extends State<PedidoScreen> {
           onTap: () => go(item),
         ),
         DataCell(
-          Text(item.peticion ?? "Sin fecha"),
+          Text(_formatDateTime(item.peticion)),
+          onTap: () => go(item),
+        ),
+        DataCell(
+          Text(sincronizadoStatus),
           onTap: () => go(item),
         ),
       ]));
@@ -215,9 +214,19 @@ class _PedidoScreenState extends State<PedidoScreen> {
 
     return Scaffold(
       appBar: AppBar(
-          title: Text(
-            'Pedidos',
-            style: TextStyle(color: AppTheme.secondary),
+          title: GestureDetector(
+            onTap: () {
+              _syncAgainTapCount++;
+              if (_syncAgainTapCount == 5) {
+                alertaSync(context);
+                _syncAgainTapCount = 0;
+              }
+            },
+            child: Text(
+              'Pedidos',
+              style: TextStyle(
+                  color: AppTheme.secondary, fontWeight: FontWeight.w500),
+            ),
           ),
           actions: <Widget>[
             IconButton(
@@ -227,183 +236,224 @@ class _PedidoScreenState extends State<PedidoScreen> {
             ),
           ],
           iconTheme: IconThemeData(color: AppTheme.secondary)),
-      floatingActionButton: FloatingActionButton.extended(
-        onPressed: () async {
-          await Navigator.push(
-            context,
-            MaterialPageRoute(
-              builder: (context) => PedidoForm(),
-            ),
-          ).then((_) => Provider.of<PedidoViewModel>(context, listen: false)
-              .fetchLocalPedidosForScreen());
-        },
-        icon: Icon(Icons.add, size: 30, color: AppTheme.quaternary),
-        label: Text(
-          "Agregar Pedido",
-          style: TextStyle(fontSize: 20, color: AppTheme.quaternary),
-        ),
-        shape: RoundedRectangleBorder(
-          borderRadius: BorderRadius.circular(8),
-        ),
-        materialTapTargetSize: MaterialTapTargetSize.shrinkWrap,
-        backgroundColor: AppTheme.tertiary,
-      ),
-      body: Column(
+      body: Stack(
         children: [
-          Expanded(
-            child: ListView(
-              padding: const EdgeInsets.fromLTRB(8, 0, 8, 0),
-              children: [
-                const SizedBox(height: 8),
-                clase.tarjeta(
-                  Padding(
-                    padding: const EdgeInsets.all(8.0),
-                    child: LayoutBuilder(
-                      builder: (context, constraints) {
-                        if (screenWidth > 1000) {
-                          return Row(
-                            children: [
-                              Expanded(
-                                flex: 7,
-                                child: _buildDateRangePicker(),
-                              ),
-                              SizedBox(width: 5),
-                              botonBuscar()
-                            ],
-                          );
-                        } else {
-                          return Column(
-                            children: [
-                              Row(
-                                children: [_buildDateRangePicker()],
-                              ),
-                              SizedBox(height: 15),
-                              Row(
-                                children: [botonBuscar()],
-                              ),
-                            ],
-                          );
-                        }
-                      },
+          Column(
+            children: [
+              Expanded(
+                child: ListView(
+                  padding: const EdgeInsets.fromLTRB(8, 0, 8, 0),
+                  children: [
+                    const SizedBox(height: 8),
+                    clase.tarjeta(
+                      Padding(
+                        padding: const EdgeInsets.all(8.0),
+                        child: LayoutBuilder(
+                          builder: (context, constraints) {
+                            if (screenWidth > 1000) {
+                              return Row(
+                                crossAxisAlignment: CrossAxisAlignment.end,
+                                children: [
+                                  Expanded(
+                                    flex: 7,
+                                    child: _buildDateRangePicker(),
+                                  ),
+                                  const SizedBox(width: 5),
+                                  botonBuscar()
+                                ],
+                              );
+                            } else {
+                              return Column(
+                                children: [
+                                  Row(
+                                    children: [_buildDateRangePicker()],
+                                  ),
+                                  Row(
+                                    children: [botonBuscar()],
+                                  ),
+                                ],
+                              );
+                            }
+                          },
+                        ),
+                      ),
                     ),
-                  ),
-                ),
-                const SizedBox(height: 8),
-                pvm.isLoading
-                    ? const Center(child: CircularProgressIndicator())
-                    : Container(),
-                clase.tarjeta(
-                  Column(
-                    children: [
-                      LayoutBuilder(builder: (context, constraints) {
-                        return SingleChildScrollView(
-                          scrollDirection: Axis.vertical,
-                          child: Scrollbar(
-                            controller: horizontalScrollController,
-                            interactive: true,
-                            thumbVisibility: true,
-                            thickness: 10.0,
-                            child: SingleChildScrollView(
-                              controller: horizontalScrollController,
-                              scrollDirection: Axis.horizontal,
-                              child: ConstrainedBox(
-                                constraints: BoxConstraints(
-                                    minWidth: isMobile
-                                        ? constraints.maxWidth
-                                        : screenWidth),
-                                child: DataTable(
-                                  columnSpacing: columnSpacing,
-                                  sortAscending: true,
-                                  sortColumnIndex: 1,
-                                  columns: [
-                                    DataColumn(label: Text(" ", style: estilo)),
-                                    DataColumn(
-                                        label: Text("FOLIO", style: estilo)),
-                                    DataColumn(
-                                        label: Text("NOMBRE", style: estilo)),
-                                    DataColumn(
-                                        label:
-                                            Text("COMENTARIOS", style: estilo)),
-                                    DataColumn(
-                                        label: Text("ESTATUS", style: estilo)),
-                                    DataColumn(
-                                        label: Text("FECHA", style: estilo)),
-                                  ],
-                                  rows: registros,
+                    const SizedBox(height: 8),
+                    pvm.isLoading
+                        ? const Center(child: CircularProgressIndicator())
+                        : Container(),
+                    clase.tarjeta(
+                      Column(
+                        children: [
+                          LayoutBuilder(builder: (context, constraints) {
+                            return SingleChildScrollView(
+                              scrollDirection: Axis.vertical,
+                              child: Scrollbar(
+                                controller: horizontalScrollController,
+                                interactive: true,
+                                thumbVisibility: true,
+                                thickness: 10.0,
+                                child: SingleChildScrollView(
+                                  controller: horizontalScrollController,
+                                  scrollDirection: Axis.horizontal,
+                                  child: ConstrainedBox(
+                                    constraints: BoxConstraints(
+                                        minWidth: isMobile
+                                            ? constraints.maxWidth
+                                            : screenWidth),
+                                    child: DataTable(
+                                      columnSpacing: columnSpacing,
+                                      sortAscending: true,
+                                      sortColumnIndex: 1,
+                                      columns: [
+                                        DataColumn(
+                                            label: Text(" ", style: estilo)),
+                                        DataColumn(
+                                            label:
+                                                Text("FOLIO", style: estilo)),
+                                        DataColumn(
+                                            label:
+                                                Text("NOMBRE", style: estilo)),
+                                        DataColumn(
+                                            label: Text("COMENTARIOS",
+                                                style: estilo)),
+                                        DataColumn(
+                                            label:
+                                                Text("ESTATUS", style: estilo)),
+                                        DataColumn(
+                                            label:
+                                                Text("FECHA", style: estilo)),
+                                        DataColumn(
+                                            label: Text("SINCRONIZADO",
+                                                style: estilo)),
+                                      ],
+                                      rows: registros,
+                                    ),
+                                  ),
                                 ),
                               ),
-                            ),
-                          ),
-                        );
-                      }),
-                    ],
-                  ),
-                ),
-                const SizedBox(height: 15),
-                if (!pvm.isLoading)
-                  Row(
-                    mainAxisAlignment: MainAxisAlignment.center,
-                    children: [
-                      TextButton(
-                        onPressed:
-                            pvm.currentPage > 1 ? pvm.previousPage : null,
-                        child: Text('Anterior'),
-                        style: ButtonStyle(
-                          backgroundColor:
-                              MaterialStateProperty.resolveWith<Color?>(
-                            (Set<MaterialState> states) {
-                              if (states.contains(MaterialState.disabled)) {
-                                return Colors.grey;
-                              }
-                              return AppTheme.tertiary;
-                            },
-                          ),
-                          foregroundColor:
-                              MaterialStateProperty.resolveWith<Color?>(
-                            (Set<MaterialState> states) {
-                              if (states.contains(MaterialState.disabled)) {
-                                return Colors.black;
-                              }
-                              return Colors.white;
-                            },
-                          ),
-                        ),
+                            );
+                          }),
+                        ],
                       ),
-                      SizedBox(width: 15),
-                      Text('Página ${pvm.currentPage} de ${pvm.totalPages}'),
-                      SizedBox(width: 15),
-                      TextButton(
-                        onPressed: pvm.currentPage < pvm.totalPages
-                            ? pvm.nextPage
-                            : null,
-                        child: Text('Siguiente'),
-                        style: ButtonStyle(
-                          backgroundColor:
-                              MaterialStateProperty.resolveWith<Color?>(
-                            (Set<MaterialState> states) {
-                              if (states.contains(MaterialState.disabled)) {
-                                return Colors.grey;
-                              }
-                              return AppTheme.tertiary;
-                            },
+                    ),
+                    const SizedBox(height: 15),
+                    if (!pvm.isLoading)
+                      Row(
+                        mainAxisAlignment: MainAxisAlignment.center,
+                        children: [
+                          TextButton(
+                            onPressed:
+                                pvm.currentPage > 1 ? pvm.previousPage : null,
+                            child: Text('Anterior'),
+                            style: ButtonStyle(
+                              backgroundColor:
+                                  MaterialStateProperty.resolveWith<Color?>(
+                                (Set<MaterialState> states) {
+                                  if (states.contains(MaterialState.disabled)) {
+                                    return Colors.grey;
+                                  }
+                                  return AppTheme.tertiary;
+                                },
+                              ),
+                              foregroundColor:
+                                  MaterialStateProperty.resolveWith<Color?>(
+                                (Set<MaterialState> states) {
+                                  if (states.contains(MaterialState.disabled)) {
+                                    return Colors.black;
+                                  }
+                                  return Colors.white;
+                                },
+                              ),
+                            ),
                           ),
-                          foregroundColor:
-                              MaterialStateProperty.resolveWith<Color?>(
-                            (Set<MaterialState> states) {
-                              if (states.contains(MaterialState.disabled)) {
-                                return Colors.black;
-                              }
-                              return Colors.white;
-                            },
+                          SizedBox(width: 15),
+                          Text(
+                              'Página ${pvm.currentPage} de ${pvm.totalPages}'),
+                          SizedBox(width: 15),
+                          TextButton(
+                            onPressed: pvm.currentPage < pvm.totalPages
+                                ? pvm.nextPage
+                                : null,
+                            child: Text('Siguiente'),
+                            style: ButtonStyle(
+                              backgroundColor:
+                                  MaterialStateProperty.resolveWith<Color?>(
+                                (Set<MaterialState> states) {
+                                  if (states.contains(MaterialState.disabled)) {
+                                    return Colors.grey;
+                                  }
+                                  return AppTheme.tertiary;
+                                },
+                              ),
+                              foregroundColor:
+                                  MaterialStateProperty.resolveWith<Color?>(
+                                (Set<MaterialState> states) {
+                                  if (states.contains(MaterialState.disabled)) {
+                                    return Colors.black;
+                                  }
+                                  return Colors.white;
+                                },
+                              ),
+                            ),
                           ),
-                        ),
+                        ],
                       ),
-                    ],
+                    const SizedBox(height: 15),
+                  ],
+                ),
+              ),
+            ],
+          ),
+          Positioned(
+            bottom: 16,
+            right: 16,
+            child: FloatingActionButton.extended(
+              heroTag: 'addPedido',
+              onPressed: () async {
+                await Navigator.push(
+                  context,
+                  MaterialPageRoute(
+                    builder: (context) => PedidoForm(),
                   ),
-                const SizedBox(height: 15),
-              ],
+                ).then((_) =>
+                    Provider.of<PedidoViewModel>(context, listen: false)
+                        .fetchLocalPedidosForScreen());
+              },
+              icon: Icon(Icons.add, size: 30, color: AppTheme.quaternary),
+              label: Text(
+                "Agregar Pedido",
+                style: TextStyle(fontSize: 20, color: AppTheme.quaternary),
+              ),
+              shape: RoundedRectangleBorder(
+                borderRadius: BorderRadius.circular(8),
+              ),
+              materialTapTargetSize: MaterialTapTargetSize.shrinkWrap,
+              backgroundColor: AppTheme.tertiary,
             ),
           ),
+          // Positioned(
+          //   bottom: 16,
+          //   left: 16,
+          //   child: FloatingActionButton.extended(
+          //     heroTag: 'sincronizacion',
+          //     onPressed: () {
+          //       alerta(context, etiqueta: "Sincronización Empezada");
+          //       PedidoSync().startSync(
+          //           Provider.of<PedidoViewModel>(context, listen: false));
+          //     },
+          //     icon: Icon(Icons.sync, size: 30, color: AppTheme.quaternary),
+          //     label: Text(
+          //       "Sincronización",
+          //       style: TextStyle(fontSize: 20, color: AppTheme.quaternary),
+          //     ),
+          //     shape: RoundedRectangleBorder(
+          //       borderRadius: BorderRadius.circular(8),
+          //     ),
+          //     materialTapTargetSize: MaterialTapTargetSize.shrinkWrap,
+          //     backgroundColor: AppTheme.tertiary,
+          //   ),
+          // ),
         ],
       ),
     );
@@ -423,68 +473,32 @@ class _PedidoScreenState extends State<PedidoScreen> {
         ),
         const SizedBox(width: 5),
         Expanded(
-            flex: 2,
-            child: clase.tarjeta(
-                ListTile(
-                    title: Text(
-                      "Fecha Inicial",
-                      style: TextStyle(
-                          color: AppTheme.quaternary,
-                          fontWeight: FontWeight.bold),
-                    ),
-                    subtitle: Text(
-                      fechaInicio == null
-                          ? ""
-                          : DateFormat("dd/MM/yyyy").format(fechaInicio!),
-                      style: TextStyle(
-                          color: AppTheme.quaternary,
-                          fontWeight: FontWeight.bold),
-                    ),
-                    trailing:
-                        Icon(Icons.calendar_month, color: AppTheme.quaternary),
-                    onTap: () async {
-                      DateTime? d = await clase.showDatetimePicker(
-                          context, fechaInicio,
-                          tipo: OmniDateTimePickerType.date, solofecha: true);
-                      if (d == null) return;
-                      setState(() {
-                        fechaInicio = d;
-                      });
-                    }),
-                color: AppTheme.tertiary)),
+          flex: 3,
+          child: clase.FechaSelectWidget(
+            fecha: fechaInicio,
+            onFechaChanged: (d) {
+              setState(() {
+                fechaInicio = d;
+              });
+            },
+            etiqueta: "Fecha Inicial",
+            context: context,
+          ),
+        ),
         const SizedBox(width: 5),
         Expanded(
-            flex: 2,
-            child: clase.tarjeta(
-                ListTile(
-                    title: Text(
-                      "Fecha Final",
-                      style: TextStyle(
-                          color: AppTheme.quaternary,
-                          fontWeight: FontWeight.bold),
-                    ),
-                    subtitle: Text(
-                      fechaFin == null
-                          ? ""
-                          : DateFormat("dd/MM/yyyy").format(fechaFin!),
-                      style: TextStyle(
-                          color: AppTheme.quaternary,
-                          fontWeight: FontWeight.bold),
-                    ),
-                    trailing:
-                        Icon(Icons.calendar_month, color: AppTheme.quaternary),
-                    onTap: () async {
-                      DateTime? d = await clase.showDatetimePicker(
-                          context, fechaInicio,
-                          inicia: fechaInicio,
-                          tipo: OmniDateTimePickerType.date,
-                          solofecha: true);
-                      if (d == null) return;
-                      setState(() {
-                        fechaFin = d;
-                      });
-                    }),
-                color: AppTheme.tertiary)),
+          flex: 3,
+          child: clase.FechaSelectWidget(
+            fecha: fechaFin,
+            onFechaChanged: (d) {
+              setState(() {
+                fechaFin = d;
+              });
+            },
+            etiqueta: "Fecha Final",
+            context: context,
+          ),
+        ),
       ],
     );
   }
@@ -497,7 +511,7 @@ class _PedidoScreenState extends State<PedidoScreen> {
             Expanded(
               flex: 2,
               child: Padding(
-                padding: const EdgeInsets.only(top: 0),
+                padding: const EdgeInsets.only(bottom: 5),
                 child: ElevatedButton(
                   onPressed: clearSearchAndReset,
                   style: ElevatedButton.styleFrom(
@@ -516,7 +530,7 @@ class _PedidoScreenState extends State<PedidoScreen> {
             Expanded(
               flex: 2,
               child: Padding(
-                padding: const EdgeInsets.only(top: 0),
+                padding: const EdgeInsets.only(bottom: 5),
                 child: ElevatedButton(
                   onPressed: () async {
                     if (_busqueda.text.isNotEmpty) {
@@ -546,4 +560,60 @@ class _PedidoScreenState extends State<PedidoScreen> {
           ],
         ));
   }
+
+  void alertaSync(BuildContext context) {
+    showDialog(
+      context: context,
+      builder: (BuildContext context) {
+        return AlertDialog(
+          title: const Text('Confirmación',
+              style: TextStyle(fontWeight: FontWeight.w500, fontSize: 22)),
+          content: const Text('¿Deseas sincronizar todos los pedidos de nuevo?',
+              style: TextStyle(fontSize: 18)),
+          actions: [
+            Row(
+              mainAxisAlignment: MainAxisAlignment.spaceBetween,
+              children: [
+                TextButton(
+                  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)),
+                  onPressed: () {
+                    Navigator.of(context).pop();
+                  },
+                ),
+                TextButton(
+                  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)),
+                  onPressed: () {
+                    // No hace nada por el momento
+                    Navigator.of(context).pop();
+                  },
+                ),
+              ],
+            )
+          ],
+        );
+      },
+    );
+  }
+
+  String _formatDateTime(String? dateTimeString) {
+    if (dateTimeString == null) return "Sin fecha";
+
+    DateTime parsedDate = DateTime.parse(dateTimeString);
+
+    var formatter = DateFormat('dd-MM-yyyy HH:mm:ss');
+    return formatter.format(parsedDate.toLocal());
+  }
 }

+ 38 - 0
lib/views/pedido/pedido_sync.dart

@@ -0,0 +1,38 @@
+import 'dart:async';
+
+import 'package:flutter/material.dart';
+import '../../viewmodels/viewmodels.dart';
+
+class PedidoSync {
+  static final PedidoSync _instance = PedidoSync._internal();
+  Timer? _syncTimer;
+
+  factory PedidoSync() {
+    return _instance;
+  }
+
+  PedidoSync._internal();
+
+  void startSync(PedidoViewModel pedidoViewModel) {
+    if (_syncTimer != null && _syncTimer!.isActive) return;
+
+    _syncTimer = Timer.periodic(Duration(seconds: 5), (timer) async {
+      bool hasMoreToSync = await pedidoViewModel.sincronizarPedidos();
+      print('Pedido Sincronizado');
+      // if (!hasMoreToSync) {
+      //   timer.cancel();
+      //   _syncTimer = null;
+      //   print('Sincronización completa, no hay más pedidos por sincronizar.');
+      // }
+    });
+  }
+
+  void stopSync() {
+    _syncTimer?.cancel();
+    _syncTimer = null;
+  }
+
+  bool isSyncing() {
+    return _syncTimer != null && _syncTimer!.isActive;
+  }
+}

+ 4 - 5
lib/views/pedido/pedido_ticket.dart

@@ -43,16 +43,16 @@ pw.Page generarPaginaPrimerTicket(Pedido pedido, pw.MemoryImage image) {
   final productList = pedido.productos
       .map(
         (producto) {
-          final productPrice = double.parse(producto.producto?.precio ?? '0');
+          final productPrice = producto.producto?.precio ?? 0.0;
           final productTotal = productPrice * (producto.cantidad ?? 1);
 
           totalSinDescuento += productTotal;
 
           final toppingsList = producto.toppings.where((topping) {
-            final toppingPrice = double.parse(topping.topping?.precio ?? '0');
+            final toppingPrice = topping.topping?.precio ?? 0.0;
             return toppingPrice > 0;
           }).map((topping) {
-            final toppingPrice = double.parse(topping.topping?.precio ?? '0');
+            final toppingPrice = topping.topping?.precio ?? 0.0;
             totalSinDescuento += toppingPrice * (producto.cantidad ?? 1);
 
             return pw.Row(
@@ -221,8 +221,7 @@ pw.Page generarPaginaSegundoTicket(Pedido pedido) {
 
         content.addAll(pedido.productos
             .map((producto) {
-              final productPrice =
-                  double.parse(producto.producto?.precio ?? '0');
+              final productPrice = producto.producto?.precio ?? 0.0;
               final productTotal = productPrice * (producto.cantidad ?? 1);
 
               final toppingsList = producto.toppings.map((topping) {

+ 13 - 8
lib/views/producto/producto_form.dart

@@ -1,13 +1,14 @@
 import 'dart:io';
 
-import 'package:yoshi_papas_app/themes/themes.dart';
 import 'package:flutter/material.dart';
-import 'package:yoshi_papas_app/views/producto/producto_imagen.dart';
-import 'package:yoshi_papas_app/widgets/widgets.dart';
 import 'package:provider/provider.dart';
 import 'package:file_picker/file_picker.dart';
 import '../../models/models.dart';
+import '../../services/repo_service.dart';
 import '../../viewmodels/viewmodels.dart';
+import '../../widgets/widgets.dart';
+import '../../themes/themes.dart';
+import '../producto/producto_imagen.dart';
 
 class ProductoForm extends StatefulWidget {
   final Producto producto;
@@ -21,7 +22,8 @@ class ProductoForm extends StatefulWidget {
 class Formulario extends State<ProductoForm> {
   final _nombre = TextEditingController();
   final _descripcion = TextEditingController();
-  final _precio = TextEditingController();
+  final _precio =
+      TextEditingController(); // Mantén esto como String pero conviértelo a `double` más adelante
   final _existencia = TextEditingController();
   final _descuento = TextEditingController();
   final _busquedaCategoria = TextEditingController();
@@ -41,7 +43,8 @@ class Formulario extends State<ProductoForm> {
     super.initState();
     _nombre.text = widget.producto.nombre ?? "";
     _descripcion.text = widget.producto.descripcion ?? "";
-    _precio.text = widget.producto.precio?.toString() ?? "";
+    _precio.text = widget.producto.precio?.toString() ??
+        "0"; // Conversión a String para el campo de texto
     _existencia.text = widget.producto.existencia?.toString() ?? "";
     _descuento.text = widget.producto.descuento?.toString() ?? "";
     _selectedFilePath = widget.producto.imagen;
@@ -64,7 +67,6 @@ class Formulario extends State<ProductoForm> {
         categoriaProducto = categories.first;
       }
 
-      // Usa ProductoViewModel para obtener los toppings guardados
       final productoVM = Provider.of<ProductoViewModel>(context, listen: false);
       await cargarToppingsGuardados(widget.producto.id, productoVM);
 
@@ -354,11 +356,14 @@ class Formulario extends State<ProductoForm> {
 
     if (_precio.text.isEmpty) _precio.text = '0';
 
+    // Conversion del precio a double
+    double precio = double.tryParse(_precio.text) ?? 0.0;
+
     Producto productToUpdate = Producto(
       id: widget.producto.id,
       nombre: _nombre.text,
       descripcion: _descripcion.text,
-      precio: _precio.text,
+      precio: precio, // Guardando como String para la base de datos
       idCategoria: categoriaProducto!.id,
       imagen: _selectedFilePath,
       topings: selectedToppings.isNotEmpty ? selectedToppings : null,
@@ -384,7 +389,7 @@ class Formulario extends State<ProductoForm> {
       Producto nuevoProducto = Producto(
         nombre: _nombre.text,
         descripcion: _descripcion.text,
-        precio: _precio.text,
+        precio: precio, // Guardando como String para la base de datos
         idCategoria: categoriaProducto!.id,
         imagen: _selectedFilePath,
         topings: selectedToppings.isNotEmpty ? selectedToppings : null,

+ 7 - 7
lib/views/producto/producto_screen.dart

@@ -1,12 +1,11 @@
-import 'package:yoshi_papas_app/themes/themes.dart';
-import 'package:yoshi_papas_app/viewmodels/categoria_producto_view_model.dart';
-import 'package:yoshi_papas_app/widgets/app_textfield.dart';
 import 'package:flutter/material.dart';
 import 'package:provider/provider.dart';
 import '../../models/models.dart';
-import '../../viewmodels/producto_view_model.dart';
 import 'producto_form.dart';
 import '../../widgets/widgets_components.dart' as clase;
+import '../../widgets/widgets.dart';
+import '../../themes/themes.dart';
+import '../../viewmodels/viewmodels.dart';
 
 class ProductoScreen extends StatefulWidget {
   @override
@@ -95,7 +94,7 @@ class _ProductoScreenState extends State<ProductoScreen> {
                                                 20, 10, 20, 10)),
                                         backgroundColor:
                                             MaterialStatePropertyAll(
-                                                AppTheme.primary),
+                                                Colors.red),
                                         foregroundColor:
                                             MaterialStatePropertyAll(
                                                 AppTheme.secondary)),
@@ -162,7 +161,7 @@ class _ProductoScreenState extends State<ProductoScreen> {
           },
         ),
         DataCell(
-          Text(item.precio ?? "Sin Precio"),
+          Text(item.precio.toString()),
           onTap: () {
             Provider.of<ProductoViewModel>(context, listen: false)
                 .selectProducto(item);
@@ -176,7 +175,8 @@ class _ProductoScreenState extends State<ProductoScreen> {
       appBar: AppBar(
         title: Text(
           'Productos',
-          style: TextStyle(color: AppTheme.secondary),
+          style:
+              TextStyle(color: AppTheme.secondary, fontWeight: FontWeight.w500),
         ),
         iconTheme: IconThemeData(color: AppTheme.secondary),
       ),

+ 6 - 1
lib/views/variable/variable_form.dart

@@ -67,7 +67,12 @@ class _VariableFormState extends State<VariableForm> {
                       hintText: 'Descripción de la variable',
                     ),
                     SwitchListTile(
-                      title: Text("Activo"),
+                      activeColor: AppTheme.primary,
+                      title: const Text(
+                        "Activo",
+                        style: TextStyle(
+                            fontWeight: FontWeight.bold, fontSize: 18),
+                      ),
                       value: _activo,
                       onChanged: (bool value) {
                         setState(() {

+ 13 - 3
lib/views/venta/venta_screen.dart

@@ -1,4 +1,3 @@
-import 'package:yoshi_papas_app/themes/themes.dart';
 import 'package:flutter/material.dart';
 import 'package:intl/intl.dart';
 import 'package:omni_datetime_picker/omni_datetime_picker.dart';
@@ -7,6 +6,8 @@ import '../../widgets/widgets_components.dart';
 import '../../models/models.dart';
 import '../../viewmodels/viewmodels.dart';
 import 'venta_ticket.dart';
+import '../../widgets/widgets.dart';
+import '../../themes/themes.dart';
 
 class VentaScreen extends StatefulWidget {
   @override
@@ -248,11 +249,20 @@ class _VentaScreenState extends State<VentaScreen> {
   }
 
   void cargarPedidos(DateTime fecha) async {
-    final inicioDelDia = DateTime(fecha.year, fecha.month, fecha.day);
-    final finDelDia = DateTime(fecha.year, fecha.month, fecha.day, 23, 59, 59);
+    final inicioDelDia = DateTime.utc(fecha.year, fecha.month, fecha.day);
+    final finDelDia =
+        DateTime.utc(fecha.year, fecha.month, fecha.day, 23, 59, 59);
+
+    print('Buscando pedidos desde: $inicioDelDia hasta: $finDelDia');
+
+    // Realizar la búsqueda en UTC
     final pedidos = await Provider.of<PedidoViewModel>(context, listen: false)
         .buscarPorFecha(inicioDelDia, finDelDia);
 
+    print('Pedidos obtenidos: ${pedidos.length}');
+    pedidos.forEach((pedido) => print(
+        'Pedido: ${pedido.folio}, Total: ${pedido.totalPedido}, Estatus: ${pedido.estatus}'));
+
     final pedidosNoCancelados =
         pedidos.where((p) => p.estatus != "CANCELADO").toList();
     final pedidosCancelados =

+ 1 - 9
lib/widgets/app_drawer.dart

@@ -1,17 +1,11 @@
 // ignore_for_file: must_be_immutable
 
 import 'package:flutter/material.dart';
-import 'package:yoshi_papas_app/models/models.dart';
 import 'package:yoshi_papas_app/views/categoria_producto/categoria_producto_screen.dart';
-import 'package:yoshi_papas_app/views/home/home_screen.dart';
-import 'package:yoshi_papas_app/views/perfil/perfil_screen.dart';
 import 'package:yoshi_papas_app/views/pedido/pedido_screen.dart';
 import 'package:yoshi_papas_app/views/producto/producto_screen.dart';
-import 'package:yoshi_papas_app/views/toping/toping_screen.dart';
-import 'package:yoshi_papas_app/views/toping_categoria/toping_categoria_screen.dart';
 import 'package:yoshi_papas_app/views/variable/variable_screen.dart';
 import 'package:yoshi_papas_app/views/venta/venta_screen.dart';
-import '../models/usuario_model.dart';
 import 'package:provider/provider.dart';
 import '../themes/themes.dart';
 import '../viewmodels/login_view_model.dart';
@@ -52,8 +46,6 @@ class AppDrawer extends StatelessWidget {
 
   @override
   Widget build(BuildContext context) {
-    String? nombre = Provider.of<LoginViewModel>(context).nombre.toString();
-    String? correo = Provider.of<LoginViewModel>(context).correo.toString();
     return Drawer(
       surfaceTintColor: Colors.white,
       backgroundColor: Colors.white,
@@ -176,7 +168,7 @@ class AppDrawer extends StatelessWidget {
             child: Align(
               alignment: Alignment.bottomCenter,
               child: Text(
-                'v1.24.08.13',
+                'v1.24.09.18',
                 style: const TextStyle(fontWeight: FontWeight.w300),
               ),
             ),

+ 65 - 0
lib/widgets/widgets_components.dart

@@ -238,6 +238,71 @@ Future<DateTime?> showDatetimePicker(BuildContext context, DateTime? fecha,
   return d2.DateTimeField.combine(f, tiempo);
 }
 
+class FechaSelectWidget extends StatelessWidget {
+  final DateTime? fecha;
+  final Function(DateTime) onFechaChanged;
+  final String etiqueta;
+  final BuildContext context;
+
+  const FechaSelectWidget({
+    Key? key,
+    required this.fecha,
+    required this.onFechaChanged,
+    required this.etiqueta,
+    required this.context,
+  }) : super(key: key);
+
+  @override
+  Widget build(BuildContext context) {
+    return Column(
+      crossAxisAlignment: CrossAxisAlignment.start,
+      children: [
+        Text(
+          etiqueta,
+          style: const TextStyle(
+            fontWeight: FontWeight.bold,
+            fontSize: 16,
+          ),
+        ),
+        const SizedBox(height: 5),
+        InkWell(
+          onTap: () async {
+            DateTime? d = await showDatetimePicker(
+              context,
+              fecha,
+              tipo: OmniDateTimePickerType.date,
+              solofecha: true,
+            );
+            if (d == null) return;
+            onFechaChanged(d);
+          },
+          child: Container(
+            decoration: BoxDecoration(
+              border: Border.all(color: Colors.grey[400]!),
+              borderRadius: BorderRadius.circular(10),
+            ),
+            padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 15),
+            child: Row(
+              mainAxisAlignment: MainAxisAlignment.spaceBetween,
+              children: [
+                Text(
+                  fecha == null
+                      ? "Seleccionar"
+                      : DateFormat("dd/MM/yyyy").format(fecha!),
+                  style: TextStyle(
+                    color: fecha == null ? Colors.grey : Colors.black,
+                  ),
+                ),
+                const Icon(Icons.calendar_month),
+              ],
+            ),
+          ),
+        ),
+      ],
+    );
+  }
+}
+
 Widget circulo(Widget item, {Function()? accion, Color? color}) {
   color ??= Colors.black;
   return InkWell(