Browse Source

actualizacion productos, categorias y pedidos

OscarGil03 6 months ago
parent
commit
512e65e1b2

+ 59 - 0
lib/data/api_response.dart

@@ -0,0 +1,59 @@
+import 'dart:convert';
+
+import 'package:http/http.dart';
+
+class ApiResponse {
+  int? statusCode;
+  Paginacion? paginacion;
+  List<Map<String, dynamic>>? resultados;
+  Map<String, dynamic>? detalle;
+  Map<String, dynamic>? errores;
+  String mensaje = '';
+
+  bool get isOk => statusCode! >= 200 && statusCode! < 300;
+  bool get isError => statusCode! >= 400 && statusCode! < 500;
+  bool get isServerError => statusCode! >= 500;
+
+  ApiResponse(Response response) {
+    statusCode = response.statusCode;
+    var body = json.decode(response.body);
+    if (body.containsKey('paginacion')) {
+      var pag = body['paginacion'];
+      paginacion = Paginacion(
+        total: pag['total'],
+        pagina: pag['pagina'],
+        limite: pag['limite'],
+      );
+    }
+    if (body.containsKey('mensaje')) {
+      mensaje = body['mensaje'];
+    }
+    if (body.containsKey('resultado') && body["resultado"] != null) {
+      resultados = (body['resultado'] as List<dynamic>)
+        .cast<Map<String, dynamic>>()
+        .toList();
+    }
+    if (body.containsKey('detalle')) {
+      detalle = body['detalle'];
+    }
+    if (body.containsKey('errores')) {
+      errores = body['errores'];
+    }
+  }
+}
+
+class Paginacion {
+  final int total;
+  final int pagina;
+  final int limite;
+
+  Paginacion({required this.total, required this.pagina, required this.limite});
+
+  factory Paginacion.fromJson(Map<String, dynamic> json) {
+    return Paginacion(
+      total: json['total'],
+      pagina: json['pagina'],
+      limite: json['limite'],
+    );
+  }
+}

+ 62 - 0
lib/data/session/session_storage.dart

@@ -0,0 +1,62 @@
+import 'package:shared_preferences/shared_preferences.dart';
+
+class SessionStorage {
+  Future<SharedPreferences> _getPreferences() async {
+    return await SharedPreferences.getInstance();
+  }
+
+  Future<void> saveToken(String token) async {
+    final preferences = await _getPreferences();
+    await preferences.setString('token', token);
+  }
+
+  Future<void> saveId(int id) async {
+    final preferences = await _getPreferences();
+    await preferences.setInt('id', id);
+  }
+  
+  Future<void> saveCorreo(String value) async {
+    final preferences = await _getPreferences();
+    await preferences.setString('correo', value);
+  }
+
+  Future<void> saveNombre(String? value) async {
+    final preferences = await _getPreferences();
+    await preferences.setString('nombre', value.toString());
+  }
+
+  Future<String?> getNombre() async {
+    final preferences = await _getPreferences();
+    return preferences.getString('nombre');
+  }
+
+  Future<String?> getCorreo() async {
+    final preferences = await _getPreferences();
+    return preferences.getString('correo');
+  }
+
+  Future<String?> getEmpresa() async {
+    final preferences = await _getPreferences();
+    return preferences.getString('empresa');
+  }
+
+  Future<void> saveEmpresa(String? value) async {
+    final preferences = await _getPreferences();
+    await preferences.setString('empresa', value.toString());
+  }
+
+  Future<String?> getToken() async {
+    final preferences = await _getPreferences();
+    return preferences.getString('token');
+  }
+
+  Future<int?> getId() async {
+    final preferences = await _getPreferences();
+    return preferences.getInt('id');
+  }
+
+  Future<void> clearToken() async {
+    final preferences = await _getPreferences();
+    await preferences.remove('token');
+  }
+}

+ 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?.toIso8601String(),
+      'modificado': modificado?.toIso8601String(),
+      'eliminado': eliminado?.toIso8601String(),
     }..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['creado']);
+    modificado = Basico.parseDate(json['modificado']);
+    eliminado = Basico.parseDate(json['eliminado']);
+  }
+
   Future<void> guardar() async {
     idLocal = await RepoService().guardar(this);
   }

+ 36 - 0
lib/models/pedido_model.dart

@@ -1,5 +1,8 @@
+import 'package:intl/intl.dart';
+
 import 'basico_model.dart';
 import 'pedido_producto_model.dart';
+import '../services/services.dart';
 
 class Pedido extends Basico {
   int? folio;
@@ -21,6 +24,9 @@ class Pedido extends Basico {
   double? cantTarjeta;
   double? cantTransferencia;
   List<PedidoProducto> productos = [];
+  int? idWeb;
+
+  String? sincronizado;
 
   Pedido({
     super.id,
@@ -43,6 +49,8 @@ class Pedido extends Basico {
     this.cantTarjeta,
     this.cantTransferencia,
     this.productos = const [],
+    this.idWeb,
+    this.sincronizado,
   });
 
   @override
@@ -70,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?)!;
@@ -91,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>))

+ 50 - 19
lib/models/producto_model.dart

@@ -14,9 +14,9 @@ class Producto extends Basico {
   int? verMenu;
   String? codigo;
   String? descuento;
-  String? venceDescuento;
   int? toping;
   List<Producto>? topings;
+  int? activo;
 
   Producto({
     super.id,
@@ -30,27 +30,37 @@ class Producto extends Basico {
     this.verMenu,
     this.codigo,
     this.descuento,
-    this.venceDescuento,
     this.toping,
     this.topings,
+    this.activo,
   });
 
   @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,
+      'activo': activo ?? 0,
+      'creado': creado?.toIso8601String(),
+      'modificado': modificado?.toIso8601String(),
+      'eliminado': eliminado?.toIso8601String(),
     }..addAll(super.toJson());
   }
 
@@ -67,17 +77,20 @@ class Producto extends Basico {
       'verMenu': verMenu,
       'codigo': codigo,
       'descuento': descuento,
-      'venceDescuento': venceDescuento,
       'toping': toping,
+      'activo': activo,
+      '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']);
@@ -87,7 +100,7 @@ class Producto extends Basico {
     verMenu = Basico.parseInt(json['verMenu']);
     codigo = Basico.parseString(json['codigo']);
     descuento = Basico.parseString(json['descuento']);
-    venceDescuento = Basico.parseString(json['venceDescuento']);
+    activo = Basico.parseInt(json['activo']);
     if (json['toping'] is bool) {
       toping = json['toping'] ? 1 : 0;
     } else {
@@ -100,6 +113,24 @@ 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.parseString(json['precio']);
+    verMenu = Basico.parseInt(json['verEnMenu']);
+    codigo = Basico.parseString(json['codigo']);
+    descuento = Basico.parseString(json['descuento']);
+    activo = Basico.parseInt(json['activo']);
+    creado = Basico.parseDate(json['creado']);
+    modificado = Basico.parseDate(json['modificado']);
+    eliminado = Basico.parseDate(json['eliminado']);
+  }
+
   Future<void> guardar() async {
     idLocal = await RepoService().guardar(this);
   }

+ 58 - 0
lib/services/base_service.dart

@@ -0,0 +1,58 @@
+import 'dart:convert';
+
+import '../data/session/session_storage.dart';
+import 'package:http/http.dart' as http;
+
+class BaseService {
+  int currentPage = 0;
+  int limit = 20;
+  int total = 0;
+  //String baseUrl = 'hermogas.est.api.rdsistemas.app';
+  //produccion: hermogas.est.api.rdsistemas.app
+  //prueba: hermogas.est.test.rdsistemas.app
+
+  String base_url = '';
+  String baseUrl = '';
+  Future<Map<String, String>> getDefaultHeaders({withAuth = true}) async {
+    Map<String, String> defaultHeaders = {'Content-Type': 'application/json'};
+
+    if (withAuth) {
+      String? token = await SessionStorage().getToken();
+      defaultHeaders['Authorization'] = 'Bearer ${token ?? ''}';
+    }
+
+    return defaultHeaders;
+  }
+
+  Future<http.Response> get(String endpoint,
+      {bool withAuth = true,
+      Map<String, String>? queryParameters,
+      Map<String, String>? headers}) async {
+    final uri = Uri.https(baseUrl, endpoint, queryParameters);
+    var defaultHeaders = await getDefaultHeaders(withAuth: withAuth);
+    var head = {...?headers, ...defaultHeaders};
+    return await http.get(uri, headers: head);
+  }
+
+  Future<http.Response> post(String endpoint,
+      {bool withAuth = true,
+      Map<String, String>? queryParameters,
+      Map<String, dynamic>? body,
+      Map<String, String>? headers}) async {
+    final uri = Uri.https(baseUrl, endpoint, queryParameters);
+    var defaultHeaders = await getDefaultHeaders(withAuth: withAuth);
+    var head = {...?headers, ...defaultHeaders};
+    return await http.post(uri, body: json.encode(body), headers: head);
+  }
+
+  Future<http.Response> delete(String endpoint,
+      {bool withAuth = true,
+      Map<String, String>? queryParameters,
+      Map<String, dynamic>? body,
+      Map<String, String>? headers}) async {
+    final uri = Uri.https(baseUrl, endpoint, queryParameters);
+    var defaultHeaders = await getDefaultHeaders(withAuth: withAuth);
+    var head = {...?headers, ...defaultHeaders};
+    return await http.delete(uri, body: json.encode(body), headers: head);
+  }
+}

+ 23 - 23
lib/services/productos_service.dart

@@ -16,33 +16,33 @@ class ProductosService {
   }
 
   Future<void> fillCategoriaBD() async {
-    final List<CategoriaProducto> categorias = [
-      CategoriaProducto(id: 1, nombre: 'SANDWICHES'),
-      CategoriaProducto(id: 2, nombre: 'BEBIDAS'),
-    ];
+    // final List<CategoriaProducto> categorias = [
+    //   CategoriaProducto(id: 1, nombre: 'SANDWICHES'),
+    //   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: 'SANDWICH POLLO PARM', precio: '195'),
-      Producto(
-          id: 2, idCategoria: 1, nombre: 'SANDWICH MEATBALLS', precio: '195'),
-      Producto(
-          id: 3, idCategoria: 1, nombre: 'SANDWICH CAPRESE', precio: '180'),
-      Producto(id: 4, idCategoria: 2, nombre: 'AGUA', precio: '25'),
-      Producto(id: 5, idCategoria: 2, nombre: 'SODA BOTE', precio: '35'),
-    ];
+    // List<Producto> productos = [
+    //   Producto(
+    //       id: 1, idCategoria: 1, nombre: 'SANDWICH POLLO PARM', precio: '195'),
+    //   Producto(
+    //       id: 2, idCategoria: 1, nombre: 'SANDWICH MEATBALLS', precio: '195'),
+    //   Producto(
+    //       id: 3, idCategoria: 1, nombre: 'SANDWICH CAPRESE', precio: '180'),
+    //   Producto(id: 4, idCategoria: 2, nombre: 'AGUA', precio: '25'),
+    //   Producto(id: 5, idCategoria: 2, nombre: 'SODA BOTE', precio: '35'),
+    // ];
 
-    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 - 23
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 = 13;
   static String dbName = 'olivamiapos01.db';
   static const String id = Basico.identificadorWeb;
   static const String idLocal = Basico.identificadorLocal;
@@ -208,6 +208,55 @@ class RepoService<T> {
             )
           ''');
           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 12:
+          await db.execute('''
+            ALTER TABLE CategoriaProducto ADD COLUMN creado TEXT;
+          ''');
+          await db.execute('''
+            ALTER TABLE CategoriaProducto ADD COLUMN modificado TEXT;
+          ''');
+          await db.execute('''
+            ALTER TABLE Producto ADD COLUMN creado TEXT;
+          ''');
+          await db.execute('''
+            ALTER TABLE Producto ADD COLUMN modificado TEXT;
+          ''');
+          await db.execute('''
+            ALTER TABLE Producto ADD COLUMN activo INTEGER;
+          ''');
+
+          break;
       }
       oldVersion++;
     }
@@ -215,36 +264,63 @@ class RepoService<T> {
 
   Future<int> guardar(T model) async {
     try {
-      var dbClient = await db;
-      String nombreTabla = model.runtimeType.toString();
+      print("Guardando modelo en la base de datos: ${model.runtimeType}");
+
+      // Comprobar si es un modelo que obtiene las fechas del servidor (Producto o CategoriaProducto)
+      if (model is Producto || model is CategoriaProducto) {
+        // No hacer nada, las fechas vienen del servidor
+        print("Usando fechas del servidor para ${model.runtimeType}");
+      } else if (model is Basico) {
+        // Generar las fechas localmente para otros modelos
+        asignarFechasLocalmente(model);
+      }
+
+      // Convertir el modelo a JSON para la base de datos
       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();
+
+      int? id = modelMap['id'];
 
-      int id = 0;
-      if (modelMap['id'] != null && modelMap['id'] != 0) {
+      if (id == null || id == 0) {
+        modelMap.remove('id');
+      }
+
+      List<Map> existing = id != null && id > 0
+          ? await dbClient!.query(nombreTabla, where: 'id = ?', whereArgs: [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 {
-        modelMap.remove('id');
+        print("Insertando nuevo registro en la tabla $nombreTabla");
         id = await dbClient!.insert(nombreTabla, modelMap);
       }
 
-      if (model is Producto) {
-        await _guardarToppings(dbClient, id, model.topings);
-      }
-
-      return id;
+      return id!;
     } catch (e) {
-      print('Error al guardar en $T: $e');
+      print('Error al guardar en dynamic: $e');
       return 0;
     }
   }
 
+  void asignarFechasLocalmente(Basico model) {
+    DateTime ahora = DateTime.now();
+    if (model.creado == null) {
+      model.creado = ahora.toUtc();
+    }
+    model.modificado = ahora.toUtc();
+  }
+
   Future<void> _guardarToppings(
       Database db, int idProducto, List<Producto>? topings) async {
     await db.delete('ProductoTopping',
@@ -307,6 +383,7 @@ class RepoService<T> {
   }
 
   dynamic toEncodable(dynamic item) {
+    print("Serializando objeto: $item");
     if (item is Pedido) {
       return item.toJson();
     } else if (item is PedidoProducto) {
@@ -327,7 +404,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();
   }
 
@@ -361,6 +439,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');
@@ -433,17 +531,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();
   }
@@ -493,4 +592,83 @@ 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 &&
+          productoApi.modificado!.isAfter(productoLocal.modificado!)) {
+        await RepoService().guardar(productoApi);
+      } else if (productoLocal.id == 0) {
+        await RepoService().guardar(productoApi);
+      }
+    }
+  }
 }

+ 1 - 0
lib/services/services.dart

@@ -1 +1,2 @@
 export '../services/repo_service.dart';
+export '../services/base_service.dart';

+ 5 - 5
lib/viewmodels/categoria_producto_view_model.dart

@@ -39,20 +39,20 @@ class CategoriaProductoViewModel extends ChangeNotifier {
   }
 
   Future<void> fetchLocalAll({int page = 1}) async {
-    _currentPage = page; // Ajusta la página actual
+    _currentPage = page;
     var db = await RepoService().db;
 
-    // Primero, obtén el total de productos para calcular el total de páginas
     int? count = Sqflite.firstIntValue(
         await db!.rawQuery('SELECT COUNT(*) FROM CategoriaProducto'));
     _totalProducts = count ?? 0;
 
-    // Calcular el offset basado en la página actual y el límite
     int offset = (_limit * (page - 1));
 
-    // Consulta con paginación
     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 - 0
lib/viewmodels/pedido_view_model.dart

@@ -2,6 +2,7 @@ import 'package:flutter/material.dart';
 import 'package:intl/intl.dart';
 import 'package:sqflite/sqflite.dart';
 
+import '../data/api_response.dart';
 import '../models/models.dart';
 import '../services/services.dart';
 
@@ -208,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;
@@ -220,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;
 }

+ 83 - 9
lib/viewmodels/producto_view_model.dart

@@ -1,7 +1,11 @@
 import 'package:flutter/material.dart';
 import 'package:sqflite/sqflite.dart';
+
+import '../data/api_response.dart';
+import '../services/base_service.dart';
 import '../models/models.dart';
 import '../services/services.dart';
+import '../services/repo_service.dart';
 
 class ProductoViewModel<T> extends ChangeNotifier {
   String _busqueda = "";
@@ -45,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();
@@ -69,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();
@@ -80,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();
   }
 
@@ -175,6 +178,77 @@ class ProductoViewModel<T> extends ChangeNotifier {
     return null;
   }
 
+  Future<bool> sincronizarCategorias() async {
+    try {
+      final response = ApiResponse(await BaseService().get('/pos/categoria'));
+
+      print(response.resultados);
+
+      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();

+ 52 - 1
lib/views/categoria_producto/categoria_producto_screen.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,42 @@ 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),
+          ),
+          actions: [
+            TextButton(
+              child: Text('OK'),
+              onPressed: () {
+                Navigator.of(context).pop();
+              },
+            ),
+          ],
+        );
+      },
+    );
+  }
+
+  Future<void> _sincronizarProductos(BuildContext context) async {
+    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);
+    }
+  }
+
   @override
   Widget build(BuildContext context) {
     final model = Provider.of<CategoriaProductoViewModel>(context);
@@ -67,7 +104,6 @@ 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,
@@ -165,6 +201,21 @@ class _CategoriaProductoScreenState extends State<CategoriaProductoScreen> {
               TextStyle(color: AppTheme.secondary, fontWeight: FontWeight.w500),
         ),
         iconTheme: IconThemeData(color: AppTheme.secondary),
+        actions: [
+          TextButton.icon(
+            icon: Icon(Icons.sync, color: AppTheme.secondary),
+            label: Text(
+              "Sincronizar",
+              style: TextStyle(
+                  color: AppTheme.secondary,
+                  fontWeight: FontWeight.w500,
+                  fontSize: 18),
+            ),
+            onPressed: () async {
+              await _sincronizarProductos(context);
+            },
+          )
+        ],
       ),
       body: Column(
         children: [

+ 16 - 4
lib/views/pedido/pedido_csv.dart

@@ -1,5 +1,4 @@
 import 'dart:convert';
-
 import 'package:csv/csv.dart';
 import 'package:intl/intl.dart';
 import 'package:path_provider/path_provider.dart';
@@ -18,12 +17,12 @@ Future<void> exportarPedidosACSV(List<Pedido> pedidos, String fileName) async {
       "Toppings",
       "Descuento (%)",
       "Estado",
-      "Fecha",
       "Total con Descuento",
       "Tipo de Pago",
       "Cantidad Efectivo",
       "Cantidad Tarjeta",
-      "Cantidad Transferencia"
+      "Cantidad Transferencia",
+      "Fecha",
     ]
   ];
 
@@ -56,6 +55,9 @@ Future<void> exportarPedidosACSV(List<Pedido> pedidos, String fileName) async {
       double precioDescuento = subtotal * (descuento / 100);
       double totalConDescuento = subtotal - precioDescuento;
 
+      // Aquí es donde formateamos la fecha a la zona horaria local
+      String formattedFecha = _formatDateTime(pedido.peticion);
+
       List<dynamic> row = [
         pedido.folio,
         pedido.nombreCliente,
@@ -65,12 +67,12 @@ Future<void> exportarPedidosACSV(List<Pedido> pedidos, String fileName) async {
         toppingsText,
         descuento,
         pedido.estatus,
-        pedido.peticion ?? '',
         formatCurrency(totalConDescuento),
         pedido.tipoPago ?? 'No especificado',
         formatCurrency(pedido.cantEfectivo ?? 0.0),
         formatCurrency(pedido.cantTarjeta ?? 0.0),
         formatCurrency(pedido.cantTransferencia ?? 0.0),
+        formattedFecha,
       ];
       rows.add(row);
     }
@@ -88,6 +90,16 @@ Future<void> exportarPedidosACSV(List<Pedido> pedidos, String fileName) async {
   print('Archivo CSV guardado en $path');
 }
 
+// Formato de la fecha a hora local, similar a lo que ya hiciste en tu pantalla
+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()); // Convertimos a hora local
+}
+
 String formatCurrency(double amount) {
   final format = NumberFormat("#,##0.00", "es_MX");
   return format.format(amount);

+ 27 - 5
lib/views/pedido/pedido_detalle_screen.dart

@@ -50,12 +50,25 @@ class PedidoDetalleScreen extends StatelessWidget {
               color: Colors.white,
               child: Column(
                 children: [
+                  // Colocamos la fecha y el cliente en una misma fila
                   ListTile(
-                    title: Text(
-                      'Cliente: ${pedido.nombreCliente}',
-                      style:
-                          TextStyle(fontWeight: FontWeight.bold, fontSize: 22),
+                    title: Row(
+                      mainAxisAlignment: MainAxisAlignment.spaceBetween,
+                      children: [
+                        Text(
+                          'Cliente: ${pedido.nombreCliente}',
+                          style: TextStyle(
+                              fontWeight: FontWeight.bold, fontSize: 22),
+                        ),
+                        Text(
+                          'Fecha: ${_formatDateTime(pedido.peticion)}',
+                          style: TextStyle(
+                              fontWeight: FontWeight.bold, fontSize: 22),
+                        ),
+                      ],
                     ),
+                  ),
+                  ListTile(
                     subtitle: Text(
                       'Comentarios: ${pedido.comentarios}',
                       style:
@@ -70,7 +83,7 @@ class PedidoDetalleScreen extends StatelessWidget {
                         fontWeight: FontWeight.bold,
                       ),
                     ),
-                  )
+                  ),
                 ],
               ),
             ),
@@ -313,4 +326,13 @@ class PedidoDetalleScreen extends StatelessWidget {
       ],
     );
   }
+
+  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());
+  }
 }

+ 577 - 268
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 '../pedido/pedido_ticket.dart';
@@ -32,6 +33,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;
@@ -91,12 +93,22 @@ class _PedidoFormState extends State<PedidoForm> {
     Provider.of<DescuentoViewModel>(context, listen: false).cargarDescuentos();
   }
 
-  _onSearchChanged(String value) {
+  void _onSearchChanged(String value) async {
     if (value.isEmpty) {
-      cargarProductosIniciales();
+      cargarProductosPorCategoria(
+          categoriaSeleccionada?.id ?? categorias.first.id);
     } else {
-      Provider.of<ProductoViewModel>(context, listen: false)
+      setState(() {
+        _estadoBusqueda = true;
+      });
+
+      await Provider.of<ProductoViewModel>(context, listen: false)
           .fetchLocalByName(nombre: value);
+
+      setState(() {
+        productos =
+            Provider.of<ProductoViewModel>(context, listen: false).productos;
+      });
     }
   }
 
@@ -158,6 +170,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) +
@@ -174,6 +189,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;
+        }
       });
     }
 
@@ -188,254 +210,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)),
+                      ],
                     ),
-                  ],
-                )
-              ],
-            );
+                  ),
+                ));
           },
         );
       },
@@ -447,11 +731,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",
@@ -538,6 +821,7 @@ class _PedidoFormState extends State<PedidoForm> {
   void dispose() {
     _gridViewController.dispose();
     _searchController.dispose();
+    _categoryScrollController.dispose();
     super.dispose();
   }
 
@@ -970,6 +1254,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),
@@ -986,7 +1272,9 @@ class _PedidoFormState extends State<PedidoForm> {
               itemCount: productos.length,
               itemBuilder: (context, index) {
                 final producto = productos[index];
-                if (producto.idCategoria != categoriaSeleccionada?.id) {
+                // Si no se está buscando, aplicar el filtro de categoría
+                if (!_estadoBusqueda &&
+                    producto.idCategoria != categoriaSeleccionada?.id) {
                   return Container();
                 }
                 return Card(
@@ -1043,31 +1331,38 @@ class _PedidoFormState extends State<PedidoForm> {
 
     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,
+      child: Scrollbar(
+        thumbVisibility: true,
+        trackVisibility: true,
+        interactive: true,
+        controller: _categoryScrollController,
+        child: 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(
+                  primary: isSelected ? AppTheme.tertiary : Colors.grey,
+                  foregroundColor:
+                      isSelected ? AppTheme.quaternary : AppTheme.secondary,
+                  onPrimary: AppTheme.secondary,
+                ),
+                child: Text(categoria.nombre!),
               ),
-              child: Text(categoria.nombre!),
-            ),
-          );
-        },
+            );
+          },
+        ),
       ),
     );
   }
@@ -1080,9 +1375,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,
       ),

+ 341 - 216
lib/views/pedido/pedido_screen.dart

@@ -10,6 +10,7 @@ import '../../models/models.dart';
 import '../../viewmodels/viewmodels.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;
@@ -99,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: [
@@ -195,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),
         ),
       ]));
@@ -203,10 +214,19 @@ class _PedidoScreenState extends State<PedidoScreen> {
 
     return Scaffold(
       appBar: AppBar(
-          title: Text(
-            'Pedidos',
-            style: TextStyle(
-                color: AppTheme.secondary, fontWeight: FontWeight.w500),
+          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(
@@ -216,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(
-                            crossAxisAlignment: CrossAxisAlignment.end,
-                            children: [
-                              Expanded(
-                                flex: 7,
-                                child: _buildDateRangePicker(),
-                              ),
-                              const SizedBox(width: 5),
-                              botonBuscar()
-                            ],
-                          );
-                        } else {
-                          return Column(
-                            children: [
-                              Row(
-                                children: [_buildDateRangePicker()],
-                              ),
-                              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,
+          //   ),
+          // ),
         ],
       ),
     );
@@ -444,59 +505,123 @@ class _PedidoScreenState extends State<PedidoScreen> {
 
   Widget botonBuscar() {
     return Expanded(
-        flex: 2,
-        child: Row(
-          children: [
-            Expanded(
-              flex: 2,
-              child: Padding(
-                padding: const EdgeInsets.only(bottom: 5),
-                child: ElevatedButton(
-                  onPressed: clearSearchAndReset,
-                  style: ElevatedButton.styleFrom(
-                    shape: RoundedRectangleBorder(
-                      borderRadius: BorderRadius.circular(20.0),
-                    ),
-                    primary: AppTheme.tertiary,
-                    padding: const EdgeInsets.symmetric(vertical: 25),
+      flex: 2,
+      child: Row(
+        children: [
+          Expanded(
+            flex: 2,
+            child: Padding(
+              padding: const EdgeInsets.only(bottom: 5),
+              child: ElevatedButton(
+                onPressed: clearSearchAndReset,
+                style: ElevatedButton.styleFrom(
+                  shape: RoundedRectangleBorder(
+                    borderRadius: BorderRadius.circular(20.0),
                   ),
-                  child: Text('Limpiar',
-                      style: TextStyle(color: AppTheme.quaternary)),
+                  primary: AppTheme.tertiary,
+                  padding: const EdgeInsets.symmetric(vertical: 25),
                 ),
+                child: Text('Limpiar',
+                    style: TextStyle(color: AppTheme.quaternary)),
               ),
             ),
-            const SizedBox(width: 8),
-            Expanded(
-              flex: 2,
-              child: Padding(
-                padding: const EdgeInsets.only(bottom: 5),
-                child: ElevatedButton(
-                  onPressed: () async {
-                    if (_busqueda.text.isNotEmpty) {
-                      await Provider.of<PedidoViewModel>(context, listen: false)
-                          .buscarPedidosPorFolio(_busqueda.text.trim());
-                    } else if (fechaInicio != null && fechaFin != null) {
-                      await Provider.of<PedidoViewModel>(context, listen: false)
-                          .buscarPedidosPorFecha(fechaInicio!, fechaFin!);
-                    } else {
-                      ScaffoldMessenger.of(context).showSnackBar(const SnackBar(
-                          content: Text(
-                              'Introduce un folio o selecciona un rango de fechas para buscar.')));
-                    }
-                  },
-                  style: ElevatedButton.styleFrom(
-                    shape: RoundedRectangleBorder(
-                      borderRadius: BorderRadius.circular(20.0),
-                    ),
-                    primary: AppTheme.tertiary,
-                    padding: const EdgeInsets.symmetric(vertical: 25),
+          ),
+          const SizedBox(width: 8),
+          Expanded(
+            flex: 2,
+            child: Padding(
+              padding: const EdgeInsets.only(bottom: 5),
+              child: ElevatedButton(
+                onPressed: () async {
+                  if (_busqueda.text.isNotEmpty) {
+                    await Provider.of<PedidoViewModel>(context, listen: false)
+                        .buscarPedidosPorFolio(_busqueda.text.trim());
+                  } else if (fechaInicio != null && fechaFin != null) {
+                    DateTime fechaInicioUTC = DateTime(fechaInicio!.year,
+                            fechaInicio!.month, fechaInicio!.day)
+                        .toUtc();
+                    DateTime fechaFinUTC = DateTime(fechaFin!.year,
+                            fechaFin!.month, fechaFin!.day, 23, 59, 59)
+                        .toUtc();
+
+                    await Provider.of<PedidoViewModel>(context, listen: false)
+                        .buscarPedidosPorFecha(fechaInicioUTC, fechaFinUTC);
+                  } else {
+                    ScaffoldMessenger.of(context).showSnackBar(const SnackBar(
+                        content: Text(
+                            'Introduce un folio o selecciona un rango de fechas para buscar.')));
+                  }
+                },
+                style: ElevatedButton.styleFrom(
+                  shape: RoundedRectangleBorder(
+                    borderRadius: BorderRadius.circular(20.0),
                   ),
-                  child: Text('Buscar',
-                      style: TextStyle(color: AppTheme.quaternary)),
+                  primary: AppTheme.tertiary,
+                  padding: const EdgeInsets.symmetric(vertical: 25),
                 ),
+                child: Text('Buscar',
+                    style: TextStyle(color: AppTheme.quaternary)),
               ),
             ),
+          ),
+        ],
+      ),
+    );
+  }
+
+  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());
   }
 }

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

@@ -0,0 +1,37 @@
+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();
+      // 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;
+  }
+}

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

@@ -13,14 +13,19 @@ Future<void> imprimirTicketsJuntos(BuildContext context, Pedido pedido) async {
   bool ticketCocinaActivo =
       await Provider.of<VariableViewModel>(context, listen: false)
           .isVariableActive('ticket_cocina');
+  bool ticketVentaActivo =
+      await Provider.of<VariableViewModel>(context, listen: false)
+          .isVariableActive('ticket_venta');
   final pdf = pw.Document();
   final image = pw.MemoryImage(
     (await rootBundle.load('assets/OlivaLogo-BN.png')).buffer.asUint8List(),
   );
 
-  pdf.addPage(
-    generarPaginaPrimerTicket(pedido, image),
-  );
+  if (ticketVentaActivo) {
+    pdf.addPage(
+      generarPaginaPrimerTicket(pedido, image),
+    );
+  }
 
   if (ticketCocinaActivo) {
     pdf.addPage(
@@ -116,7 +121,7 @@ pw.Page generarPaginaPrimerTicket(Pedido pedido, pw.MemoryImage image) {
                   padding: const pw.EdgeInsets.only(right: 15),
                   child: pw.Column(children: [
                     pw.SizedBox(height: 5),
-                    pw.Text('Fecha: ${pedido.peticion}',
+                    pw.Text('Fecha: ${_formatDateTime(pedido.peticion)}',
                         style: const pw.TextStyle(fontSize: 9)),
                     pw.Text('Oliva Mía',
                         style: const pw.TextStyle(fontSize: 9)),
@@ -201,7 +206,7 @@ pw.Page generarPaginaSegundoTicket(Pedido pedido) {
           pw.Text('.', style: pw.TextStyle(fontSize: 1)),
           pw.Padding(
               padding: const pw.EdgeInsets.only(right: 15),
-              child: pw.Text('Fecha: ${pedido.peticion}',
+              child: pw.Text('Fecha: ${_formatDateTime(pedido.peticion)}',
                   style: pw.TextStyle(
                       fontSize: 9, fontWeight: pw.FontWeight.bold))),
           pw.SizedBox(height: 5),
@@ -280,3 +285,12 @@ Future<void> printPdf(Uint8List pdfBytes) async {
     onLayout: (PdfPageFormat format) => pdfBytes,
   );
 }
+
+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());
+}

+ 52 - 1
lib/views/producto/producto_screen.dart

@@ -46,6 +46,42 @@ class _ProductoScreenState extends State<ProductoScreen> {
     });
   }
 
+  Future<void> _sincronizarProductos(BuildContext context) async {
+    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);
+    }
+  }
+
+  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),
+          ),
+          actions: [
+            TextButton(
+              child: Text('OK'),
+              onPressed: () {
+                Navigator.of(context).pop();
+              },
+            ),
+          ],
+        );
+      },
+    );
+  }
+
   @override
   Widget build(BuildContext context) {
     final model = Provider.of<ProductoViewModel>(context);
@@ -161,7 +197,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);
@@ -179,6 +215,21 @@ class _ProductoScreenState extends State<ProductoScreen> {
               TextStyle(color: AppTheme.secondary, fontWeight: FontWeight.w500),
         ),
         iconTheme: IconThemeData(color: AppTheme.secondary),
+        actions: [
+          TextButton.icon(
+            icon: Icon(Icons.sync, color: AppTheme.secondary),
+            label: Text(
+              "Sincronizar",
+              style: TextStyle(
+                  color: AppTheme.secondary,
+                  fontWeight: FontWeight.w500,
+                  fontSize: 18),
+            ),
+            onPressed: () async {
+              await _sincronizarProductos(context);
+            },
+          )
+        ],
       ),
       body: Column(
         children: [

+ 17 - 4
lib/views/variable/variable_screen.dart

@@ -128,7 +128,7 @@ class _VariablesScreenState extends State<VariablesScreen> {
           ),
         ])),
         DataCell(
-          Text(item.id.toString()),
+          Text(item.nombre.toString()),
           onTap: () {
             Provider.of<VariableViewModel>(context, listen: false)
                 .selectVariable(item);
@@ -136,7 +136,18 @@ class _VariablesScreenState extends State<VariablesScreen> {
           },
         ),
         DataCell(
-          Text(item.nombre.toString()),
+          Text(item.clave.toString()),
+          onTap: () {
+            Provider.of<VariableViewModel>(context, listen: false)
+                .selectVariable(item);
+            go(item);
+          },
+        ),
+        DataCell(
+          Icon(
+            item.activo == true ? Icons.check_circle : Icons.cancel,
+            color: item.activo == true ? Colors.green : Colors.red,
+          ),
           onTap: () {
             Provider.of<VariableViewModel>(context, listen: false)
                 .selectVariable(item);
@@ -224,9 +235,11 @@ class _VariablesScreenState extends State<VariablesScreen> {
                                   columns: [
                                     DataColumn(label: Text(" ", style: estilo)),
                                     DataColumn(
-                                        label: Text("ID", style: estilo)),
-                                    DataColumn(
                                         label: Text("NOMBRE", style: estilo)),
+                                    DataColumn(
+                                        label: Text("CLAVE", style: estilo)),
+                                    DataColumn(
+                                        label: Text("ACTIVO", style: estilo)),
                                   ],
                                   rows: registros,
                                 ),

+ 14 - 2
lib/views/venta/venta_screen.dart

@@ -249,11 +249,23 @@ 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);
+    // Convertir el inicio y fin del día local a UTC
+    final inicioDelDia = DateTime(fecha.year, fecha.month, fecha.day)
+        .toUtc(); // Convierte la fecha local de inicio del día a UTC
+
+    final finDelDia = DateTime(fecha.year, fecha.month, fecha.day, 23, 59, 59)
+        .toUtc(); // Convierte la fecha local de fin del día a UTC
+
+    print('Buscando pedidos desde: $inicioDelDia hasta: $finDelDia (en UTC)');
+
+    // 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 - 1
lib/widgets/app_drawer.dart

@@ -193,7 +193,7 @@ class AppDrawer extends StatelessWidget {
               child: Align(
                 alignment: Alignment.bottomCenter,
                 child: Text(
-                  'v1.24.08.16',
+                  'v1.24.10.10',
                   style: TextStyle(fontWeight: FontWeight.w300),
                 ),
               ))