Procházet zdrojové kódy

Merge remote-tracking branch 'origin/master' into luisdev

ElPoteito před 6 měsíci
rodič
revize
344e50621b

+ 1 - 1
lib/models/basico_model.dart

@@ -63,7 +63,7 @@ class Basico {
     return {
       'id': id,
       'idLocal': idLocal,
-      'eliminado': eliminado,
+      //'eliminado': eliminado,
     };
   }
 

+ 21 - 4
lib/models/categoria_producto_model.dart

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

+ 36 - 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>))

+ 46 - 21
lib/models/producto_model.dart

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

+ 2 - 2
lib/services/base_service.dart

@@ -8,8 +8,8 @@ class BaseService {
   int limit = 20;
   int total = 0;
 
-  String base_url = /*'https://pos.papas.edesarrollos.info'*/ '';
-  String baseUrl = /*'pos.papas.edesarrollos.info'*/ '';
+  String base_url = 'https://pos.conalep.turquessacoffee.com';
+  String baseUrl = 'pos.conalep.turquessacoffee.com';
   Future<Map<String, String>> getDefaultHeaders({withAuth = true}) async {
     Map<String, String> defaultHeaders = {'Content-Type': 'application/json'};
 

+ 146 - 146
lib/services/productos_service.dart

@@ -17,156 +17,156 @@ class ProductosService {
   }
 
   Future<void> fillCategoriaBD() async {
-    final List<CategoriaProducto> categorias = [
-      CategoriaProducto(id: 1, nombre: 'SNACKS'),
-      CategoriaProducto(id: 2, nombre: 'BEBIDAS'),
-      CategoriaProducto(id: 3, nombre: 'DULCERÍA'),
-      CategoriaProducto(id: 4, nombre: 'PRODUCTOS DE COCINA'),
-      CategoriaProducto(id: 5, nombre: 'PAPELERÍA'),
-    ];
+    // final List<CategoriaProducto> categorias = [
+    //   CategoriaProducto(id: 1, nombre: 'SNACKS'),
+    //   CategoriaProducto(id: 2, nombre: 'BEBIDAS'),
+    //   CategoriaProducto(id: 3, nombre: 'DULCERÍA'),
+    //   CategoriaProducto(id: 4, nombre: 'PRODUCTOS DE COCINA'),
+    //   CategoriaProducto(id: 5, nombre: 'PAPELERÍA'),
+    // ];
 
-    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: 'SABRITAS', precio: '22'),
-      Producto(
-          id: 2, idCategoria: 1, nombre: 'TOSTITOS CON QUESO', precio: '30'),
-      Producto(
-          id: 3, idCategoria: 1, nombre: 'TOSTITOS CON CARNE', precio: '40'),
-      Producto(id: 4, idCategoria: 1, nombre: 'PEPIHUATES', precio: '15'),
-      Producto(id: 5, idCategoria: 1, nombre: 'FRITURAS', precio: '15'),
-      Producto(id: 6, idCategoria: 1, nombre: 'PALOMITAS', precio: '10'),
-      Producto(
-          id: 7,
-          idCategoria: 1,
-          nombre: 'GALLETAS GAMESA CHICAS',
-          precio: '22'),
-      Producto(
-          id: 8,
-          idCategoria: 1,
-          nombre: 'GALLETAS GAMESA CHICAS',
-          precio: '25'),
-      Producto(id: 9, idCategoria: 1, nombre: 'PANES BIMBO', precio: '0'),
-      Producto(id: 10, idCategoria: 1, nombre: 'PANES GRINGOS', precio: '0'),
-      Producto(
-          id: 11,
-          idCategoria: 1,
-          nombre: 'PALETAS Y HELADOS RICCO',
-          precio: '0'),
-      //Bebidas
-      Producto(id: 12, idCategoria: 2, nombre: 'COCA COLA 600ML', precio: '25'),
-      Producto(id: 13, idCategoria: 2, nombre: 'COCA LATA', precio: '0'),
-      Producto(id: 14, idCategoria: 2, nombre: 'FUZE TEA', precio: '23'),
-      Producto(id: 15, idCategoria: 2, nombre: 'AGUA 1/2 LITRO', precio: '8'),
-      Producto(id: 16, idCategoria: 2, nombre: 'SPORT', precio: '23'),
-      Producto(id: 17, idCategoria: 2, nombre: 'JUGO BIDA', precio: '17'),
-      Producto(id: 18, idCategoria: 2, nombre: 'JUGO VALLE', precio: '23'),
-      Producto(
-          id: 19, idCategoria: 2, nombre: 'YOGURT DE BOTELLITA', precio: '15'),
-      Producto(id: 20, idCategoria: 2, nombre: 'AGUA FRESCA', precio: '12'),
-      Producto(id: 21, idCategoria: 2, nombre: 'LICUADO', precio: '28'),
-      Producto(id: 22, idCategoria: 2, nombre: 'CAPRISUN', precio: '0'),
-      Producto(id: 23, idCategoria: 2, nombre: 'KOOL AID', precio: '0'),
-      Producto(id: 24, idCategoria: 2, nombre: 'SODA GRINGA', precio: '0'),
-      Producto(id: 25, idCategoria: 2, nombre: 'CAFÉ AMERICANO', precio: '12'),
-      Producto(id: 26, idCategoria: 2, nombre: 'CAFÉ CON LECHE', precio: '15'),
-      //Dulcería
-      Producto(id: 27, idCategoria: 3, nombre: 'TUTSI POP', precio: '5'),
-      Producto(id: 28, idCategoria: 3, nombre: 'CHICLES', precio: '2'),
-      Producto(id: 29, idCategoria: 3, nombre: 'HALLS', precio: '12'),
-      Producto(id: 30, idCategoria: 3, nombre: 'MENTOS', precio: '1'),
-      Producto(id: 31, idCategoria: 3, nombre: 'ROCKALETA', precio: '10'),
-      Producto(
-          id: 32, idCategoria: 3, nombre: 'PALETA SURTIDA AZUL', precio: '5'),
-      Producto(id: 33, idCategoria: 3, nombre: 'TAMBORINES', precio: '1'),
-      Producto(id: 34, idCategoria: 3, nombre: 'PICAFRESA', precio: '2'),
-      Producto(id: 35, idCategoria: 3, nombre: 'SNICKERS', precio: '20'),
-      Producto(id: 36, idCategoria: 3, nombre: 'MILKY WAY', precio: '20'),
-      Producto(id: 37, idCategoria: 3, nombre: 'M&M', precio: '20'),
-      Producto(id: 38, idCategoria: 3, nombre: 'MUIBON', precio: '8'),
-      Producto(id: 39, idCategoria: 3, nombre: 'BARRA BRAN FRUT', precio: '12'),
-      Producto(
-          id: 40, idCategoria: 3, nombre: 'BARRA RICE KRISPIES', precio: '12'),
-      Producto(id: 41, idCategoria: 3, nombre: 'BARRA STILA', precio: '12'),
-      //Pdtos Cocina
-      Producto(id: 42, idCategoria: 4, nombre: 'HAMBURGUESA', precio: '45'),
-      Producto(
-          id: 43,
-          idCategoria: 4,
-          nombre: 'HAMBURGUESA CON PAPAS',
-          precio: '60'),
-      Producto(
-          id: 44,
-          idCategoria: 4,
-          nombre: 'PAPAS FRITAS CON QUESO',
-          precio: '30'),
-      Producto(
-          id: 45, idCategoria: 4, nombre: 'PAPAS CON BONELESS', precio: '55'),
-      Producto(id: 46, idCategoria: 4, nombre: 'EXTRA DE PAPAS', precio: '20'),
-      Producto(id: 47, idCategoria: 4, nombre: 'TORTA DE JAMÓN', precio: '25'),
-      Producto(id: 48, idCategoria: 4, nombre: 'TORTA DE PIERNA', precio: '45'),
-      Producto(
-          id: 49, idCategoria: 4, nombre: 'TORTA DE BARBACOA', precio: '45'),
-      Producto(
-          id: 50, idCategoria: 4, nombre: 'QUESADILLA CON JAMÓN', precio: '25'),
-      Producto(
-          id: 51, idCategoria: 4, nombre: 'QUESADILLA CON CARNE', precio: '30'),
-      Producto(
-          id: 52,
-          idCategoria: 4,
-          nombre: 'QUESADILLA CON BONELESS',
-          precio: '35'),
-      Producto(id: 53, idCategoria: 4, nombre: 'PIZZA PAN', precio: '20'),
-      Producto(id: 54, idCategoria: 4, nombre: 'PIZZA DOG', precio: '25'),
-      Producto(id: 55, idCategoria: 4, nombre: 'BURRITOS 3PZAS', precio: '30'),
-      Producto(id: 56, idCategoria: 4, nombre: 'MOLLETE', precio: '18'),
-      Producto(id: 57, idCategoria: 4, nombre: 'SANDWICH', precio: '20'),
-      Producto(
-          id: 58, idCategoria: 4, nombre: 'YOGURT CON FRUTA', precio: '30'),
-      Producto(
-          id: 59, idCategoria: 4, nombre: 'TAMAL ELOTE O CARNE', precio: '22'),
-      Producto(
-          id: 60, idCategoria: 4, nombre: 'TAMAL CON FRIJOL', precio: '30'),
-      Producto(id: 61, idCategoria: 4, nombre: 'MARUCHAN', precio: '25'),
-      Producto(
-          id: 62, idCategoria: 4, nombre: 'ENSALADA BONELESS', precio: '55'),
-      Producto(
-          id: 63, idCategoria: 4, nombre: 'ENSALADA DE JAMÓN', precio: '35'),
-      Producto(
-          id: 64, idCategoria: 4, nombre: 'ENSALADA DE POLLO', precio: '45'),
-      Producto(
-          id: 65,
-          idCategoria: 4,
-          nombre: 'COMIDA DEL DIA 1/2 ORDEN',
-          precio: '45'),
-      Producto(
-          id: 66,
-          idCategoria: 4,
-          nombre: 'COMIDA DEL DIA 1 ORDEN',
-          precio: '80'),
-      //Papeleria
-      Producto(
-          id: 67, idCategoria: 5, nombre: 'HOJAS BLANCAS (2)', precio: '1'),
-      Producto(id: 68, idCategoria: 5, nombre: 'CARTULINA', precio: '10'),
-      Producto(id: 69, idCategoria: 5, nombre: 'FOLDERS', precio: '5'),
-      Producto(id: 70, idCategoria: 5, nombre: 'BORRADOR', precio: '10'),
-      Producto(id: 71, idCategoria: 5, nombre: 'SACAPUNTAS', precio: '10'),
-      Producto(id: 72, idCategoria: 5, nombre: 'MARCATEXTOS', precio: '15'),
-      Producto(id: 73, idCategoria: 5, nombre: 'PLUMA', precio: '10'),
-      Producto(id: 74, idCategoria: 5, nombre: 'LAPIZ', precio: '10'),
-      Producto(id: 75, idCategoria: 5, nombre: 'TOALLA FEMENINA', precio: '5'),
-      Producto(id: 76, idCategoria: 5, nombre: 'PAPEL DE BAÑO', precio: '5'),
-    ];
+    // List<Producto> productos = [
+    //   Producto(id: 1, idCategoria: 1, nombre: 'SABRITAS', precio: '22'),
+    //   Producto(
+    //       id: 2, idCategoria: 1, nombre: 'TOSTITOS CON QUESO', precio: '30'),
+    //   Producto(
+    //       id: 3, idCategoria: 1, nombre: 'TOSTITOS CON CARNE', precio: '40'),
+    //   Producto(id: 4, idCategoria: 1, nombre: 'PEPIHUATES', precio: '15'),
+    //   Producto(id: 5, idCategoria: 1, nombre: 'FRITURAS', precio: '15'),
+    //   Producto(id: 6, idCategoria: 1, nombre: 'PALOMITAS', precio: '10'),
+    //   Producto(
+    //       id: 7,
+    //       idCategoria: 1,
+    //       nombre: 'GALLETAS GAMESA CHICAS',
+    //       precio: '22'),
+    //   Producto(
+    //       id: 8,
+    //       idCategoria: 1,
+    //       nombre: 'GALLETAS GAMESA CHICAS',
+    //       precio: '25'),
+    //   Producto(id: 9, idCategoria: 1, nombre: 'PANES BIMBO', precio: '0'),
+    //   Producto(id: 10, idCategoria: 1, nombre: 'PANES GRINGOS', precio: '0'),
+    //   Producto(
+    //       id: 11,
+    //       idCategoria: 1,
+    //       nombre: 'PALETAS Y HELADOS RICCO',
+    //       precio: '0'),
+    //   //Bebidas
+    //   Producto(id: 12, idCategoria: 2, nombre: 'COCA COLA 600ML', precio: '25'),
+    //   Producto(id: 13, idCategoria: 2, nombre: 'COCA LATA', precio: '0'),
+    //   Producto(id: 14, idCategoria: 2, nombre: 'FUZE TEA', precio: '23'),
+    //   Producto(id: 15, idCategoria: 2, nombre: 'AGUA 1/2 LITRO', precio: '8'),
+    //   Producto(id: 16, idCategoria: 2, nombre: 'SPORT', precio: '23'),
+    //   Producto(id: 17, idCategoria: 2, nombre: 'JUGO BIDA', precio: '17'),
+    //   Producto(id: 18, idCategoria: 2, nombre: 'JUGO VALLE', precio: '23'),
+    //   Producto(
+    //       id: 19, idCategoria: 2, nombre: 'YOGURT DE BOTELLITA', precio: '15'),
+    //   Producto(id: 20, idCategoria: 2, nombre: 'AGUA FRESCA', precio: '12'),
+    //   Producto(id: 21, idCategoria: 2, nombre: 'LICUADO', precio: '28'),
+    //   Producto(id: 22, idCategoria: 2, nombre: 'CAPRISUN', precio: '0'),
+    //   Producto(id: 23, idCategoria: 2, nombre: 'KOOL AID', precio: '0'),
+    //   Producto(id: 24, idCategoria: 2, nombre: 'SODA GRINGA', precio: '0'),
+    //   Producto(id: 25, idCategoria: 2, nombre: 'CAFÉ AMERICANO', precio: '12'),
+    //   Producto(id: 26, idCategoria: 2, nombre: 'CAFÉ CON LECHE', precio: '15'),
+    //   //Dulcería
+    //   Producto(id: 27, idCategoria: 3, nombre: 'TUTSI POP', precio: '5'),
+    //   Producto(id: 28, idCategoria: 3, nombre: 'CHICLES', precio: '2'),
+    //   Producto(id: 29, idCategoria: 3, nombre: 'HALLS', precio: '12'),
+    //   Producto(id: 30, idCategoria: 3, nombre: 'MENTOS', precio: '1'),
+    //   Producto(id: 31, idCategoria: 3, nombre: 'ROCKALETA', precio: '10'),
+    //   Producto(
+    //       id: 32, idCategoria: 3, nombre: 'PALETA SURTIDA AZUL', precio: '5'),
+    //   Producto(id: 33, idCategoria: 3, nombre: 'TAMBORINES', precio: '1'),
+    //   Producto(id: 34, idCategoria: 3, nombre: 'PICAFRESA', precio: '2'),
+    //   Producto(id: 35, idCategoria: 3, nombre: 'SNICKERS', precio: '20'),
+    //   Producto(id: 36, idCategoria: 3, nombre: 'MILKY WAY', precio: '20'),
+    //   Producto(id: 37, idCategoria: 3, nombre: 'M&M', precio: '20'),
+    //   Producto(id: 38, idCategoria: 3, nombre: 'MUIBON', precio: '8'),
+    //   Producto(id: 39, idCategoria: 3, nombre: 'BARRA BRAN FRUT', precio: '12'),
+    //   Producto(
+    //       id: 40, idCategoria: 3, nombre: 'BARRA RICE KRISPIES', precio: '12'),
+    //   Producto(id: 41, idCategoria: 3, nombre: 'BARRA STILA', precio: '12'),
+    //   //Pdtos Cocina
+    //   Producto(id: 42, idCategoria: 4, nombre: 'HAMBURGUESA', precio: '45'),
+    //   Producto(
+    //       id: 43,
+    //       idCategoria: 4,
+    //       nombre: 'HAMBURGUESA CON PAPAS',
+    //       precio: '60'),
+    //   Producto(
+    //       id: 44,
+    //       idCategoria: 4,
+    //       nombre: 'PAPAS FRITAS CON QUESO',
+    //       precio: '30'),
+    //   Producto(
+    //       id: 45, idCategoria: 4, nombre: 'PAPAS CON BONELESS', precio: '55'),
+    //   Producto(id: 46, idCategoria: 4, nombre: 'EXTRA DE PAPAS', precio: '20'),
+    //   Producto(id: 47, idCategoria: 4, nombre: 'TORTA DE JAMÓN', precio: '25'),
+    //   Producto(id: 48, idCategoria: 4, nombre: 'TORTA DE PIERNA', precio: '45'),
+    //   Producto(
+    //       id: 49, idCategoria: 4, nombre: 'TORTA DE BARBACOA', precio: '45'),
+    //   Producto(
+    //       id: 50, idCategoria: 4, nombre: 'QUESADILLA CON JAMÓN', precio: '25'),
+    //   Producto(
+    //       id: 51, idCategoria: 4, nombre: 'QUESADILLA CON CARNE', precio: '30'),
+    //   Producto(
+    //       id: 52,
+    //       idCategoria: 4,
+    //       nombre: 'QUESADILLA CON BONELESS',
+    //       precio: '35'),
+    //   Producto(id: 53, idCategoria: 4, nombre: 'PIZZA PAN', precio: '20'),
+    //   Producto(id: 54, idCategoria: 4, nombre: 'PIZZA DOG', precio: '25'),
+    //   Producto(id: 55, idCategoria: 4, nombre: 'BURRITOS 3PZAS', precio: '30'),
+    //   Producto(id: 56, idCategoria: 4, nombre: 'MOLLETE', precio: '18'),
+    //   Producto(id: 57, idCategoria: 4, nombre: 'SANDWICH', precio: '20'),
+    //   Producto(
+    //       id: 58, idCategoria: 4, nombre: 'YOGURT CON FRUTA', precio: '30'),
+    //   Producto(
+    //       id: 59, idCategoria: 4, nombre: 'TAMAL ELOTE O CARNE', precio: '22'),
+    //   Producto(
+    //       id: 60, idCategoria: 4, nombre: 'TAMAL CON FRIJOL', precio: '30'),
+    //   Producto(id: 61, idCategoria: 4, nombre: 'MARUCHAN', precio: '25'),
+    //   Producto(
+    //       id: 62, idCategoria: 4, nombre: 'ENSALADA BONELESS', precio: '55'),
+    //   Producto(
+    //       id: 63, idCategoria: 4, nombre: 'ENSALADA DE JAMÓN', precio: '35'),
+    //   Producto(
+    //       id: 64, idCategoria: 4, nombre: 'ENSALADA DE POLLO', precio: '45'),
+    //   Producto(
+    //       id: 65,
+    //       idCategoria: 4,
+    //       nombre: 'COMIDA DEL DIA 1/2 ORDEN',
+    //       precio: '45'),
+    //   Producto(
+    //       id: 66,
+    //       idCategoria: 4,
+    //       nombre: 'COMIDA DEL DIA 1 ORDEN',
+    //       precio: '80'),
+    //   //Papeleria
+    //   Producto(
+    //       id: 67, idCategoria: 5, nombre: 'HOJAS BLANCAS (2)', precio: '1'),
+    //   Producto(id: 68, idCategoria: 5, nombre: 'CARTULINA', precio: '10'),
+    //   Producto(id: 69, idCategoria: 5, nombre: 'FOLDERS', precio: '5'),
+    //   Producto(id: 70, idCategoria: 5, nombre: 'BORRADOR', precio: '10'),
+    //   Producto(id: 71, idCategoria: 5, nombre: 'SACAPUNTAS', precio: '10'),
+    //   Producto(id: 72, idCategoria: 5, nombre: 'MARCATEXTOS', precio: '15'),
+    //   Producto(id: 73, idCategoria: 5, nombre: 'PLUMA', precio: '10'),
+    //   Producto(id: 74, idCategoria: 5, nombre: 'LAPIZ', precio: '10'),
+    //   Producto(id: 75, idCategoria: 5, nombre: 'TOALLA FEMENINA', precio: '5'),
+    //   Producto(id: 76, idCategoria: 5, nombre: 'PAPEL DE BAÑO', precio: '5'),
+    // ];
 
-    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);
+    // }
   }
 }

+ 242 - 33
lib/services/repo_service.dart

@@ -6,7 +6,7 @@ import 'package:sqflite/sqflite.dart';
 import '../models/models.dart';
 
 class RepoService<T> {
-  static int dbVersion = 9;
+  static int dbVersion = 14;
   static String dbName = 'conalepPos7.db';
   static const String id = Basico.identificadorWeb;
   static const String idLocal = Basico.identificadorLocal;
@@ -77,7 +77,7 @@ class RepoService<T> {
 
       if (modelo.runtimeType.toString() == 'Pedido') {
         await db.execute(
-            'ALTER TABLE Pedido ADD COLUMN descuento INTEGER DEFAULT 0');
+            'ALTER TABLE Pedido ADD COLUMN IF NOT EXISTS descuento INTEGER DEFAULT 0');
       }
 
       await db.execute('''
@@ -125,13 +125,13 @@ class RepoService<T> {
       switch (oldVersion) {
         case 1:
           await db.execute(
-              "ALTER TABLE CategoriaProducto ADD COLUMN esToping INTEGER DEFAULT 0");
+              "ALTER TABLE CategoriaProducto ADD COLUMN IF NOT EXISTS esToping INTEGER DEFAULT 0");
 
           await db.execute(
-              "ALTER TABLE CategoriaProducto ADD COLUMN descripcion TEXT DEFAULT ''");
+              "ALTER TABLE CategoriaProducto ADD COLUMN IF NOT EXISTS descripcion TEXT DEFAULT ''");
 
           await db.execute(
-              "ALTER TABLE CategoriaProducto ADD COLUMN maximo INTEGER");
+              "ALTER TABLE CategoriaProducto ADD COLUMN IF NOT EXISTS maximo INTEGER");
 
           break;
 
@@ -161,7 +161,7 @@ class RepoService<T> {
 
         case 4:
           await db.execute('''
-          ALTER TABLE Pedido ADD COLUMN descuento INTEGER DEFAULT 0
+          ALTER TABLE Pedido ADD COLUMN IF NOT EXISTS descuento INTEGER DEFAULT 0
         ''');
           break;
 
@@ -184,16 +184,16 @@ class RepoService<T> {
 
         case 6:
           await db.execute('''
-          ALTER TABLE Pedido ADD COLUMN tipoPago TEXT DEFAULT '';
+          ALTER TABLE Pedido ADD COLUMN IF NOT EXISTS tipoPago TEXT DEFAULT '';
         ''');
           await db.execute('''
-          ALTER TABLE Pedido ADD COLUMN cantEfectivo REAL DEFAULT 0;
+          ALTER TABLE Pedido ADD COLUMN IF NOT EXISTS cantEfectivo REAL DEFAULT 0;
         ''');
           await db.execute('''
-          ALTER TABLE Pedido ADD COLUMN cantTarjeta REAL DEFAULT 0;
+          ALTER TABLE Pedido ADD COLUMN IF NOT EXISTS cantTarjeta REAL DEFAULT 0;
         ''');
           await db.execute('''
-          ALTER TABLE Pedido ADD COLUMN cantTransferencia REAL DEFAULT 0;
+          ALTER TABLE Pedido ADD COLUMN IF NOT EXISTS cantTransferencia REAL DEFAULT 0;
         ''');
           break;
 
@@ -213,9 +213,103 @@ class RepoService<T> {
 
         case 8:
           await db.execute('''
-          ALTER TABLE Producto ADD COLUMN toping INTEGER DEFAULT 0;
+          ALTER TABLE Producto ADD COLUMN IF NOT EXISTS toping INTEGER DEFAULT 0;
         ''');
           break;
+
+        case 9:
+          await db.execute('''
+          ALTER TABLE Pedido ADD COLUMN idWeb INTEGER;
+        ''');
+          await db.execute('''
+          ALTER TABLE Pedido ADD COLUMN sincronizado TEXT;
+        ''');
+          await db.execute('''
+          ALTER TABLE PedidoProducto ADD COLUMN idWeb INTEGER;
+        ''');
+          await db.execute('''
+          ALTER TABLE PedidoProducto ADD COLUMN sincronizado TEXT;
+        ''');
+          break;
+
+        // case 10:
+        //   await db.execute('''
+        //   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 11:
+        //   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 12:
+          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 13:
+          await db.execute('DROP TABLE IF EXISTS Producto');
+
+          //Se tiene que crear nuevamente para que precio sea Double
+          await db.execute('''
+          CREATE TABLE Producto (
+            id INTEGER PRIMARY KEY AUTOINCREMENT,
+            idCategoria INTEGER,
+            idLocal INTEGER,
+            nombre TEXT,
+            descripcion TEXT,
+            imagen TEXT,
+            venta INTEGER,
+            existencia INTEGER,
+            precio REAL,
+            verMenu INTEGER,
+            codigo TEXT,
+            descuento TEXT,
+            toping INTEGER,
+            creado TEXT,
+            modificado TEXT,
+            eliminado TEXT
+          )
+        ''');
+          await db.execute('DELETE FROM CategoriaProducto');
+          await db.execute('''
+            ALTER TABLE CategoriaProducto ADD COLUMN creado TEXT;
+          ''');
+          await db.execute('''
+            ALTER TABLE CategoriaProducto ADD COLUMN modificado TEXT;
+          ''');
+
+          break;
       }
       oldVersion++;
     }
@@ -223,32 +317,43 @@ 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}");
+
       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();
+
+      // Verificar si el modelo tiene ID asignado
+      int? id = modelMap['id'];
 
-      int id = 0;
-      if (modelMap['id'] != null && modelMap['id'] != 0) {
+      // Eliminar el campo 'id' si es 0 o null para permitir autoincremento
+      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;
     }
   }
@@ -322,8 +427,10 @@ class RepoService<T> {
     } else if (item is PedidoProductoTopping) {
       return item.toJson();
     } else if (item is Producto) {
+      print("Convirtiendo Producto a JSON para guardar");
       return item.toJson();
     } else if (item is CategoriaProducto) {
+      print("Convirtiendo CategoriaProducto a JSON para guardar");
       return item.toJson();
     } else if (item is Variable) {
       return item.toJson();
@@ -335,7 +442,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();
   }
 
@@ -369,6 +477,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');
@@ -455,17 +583,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();
   }
@@ -515,4 +644,84 @@ class RepoService<T> {
     return await dbClient!
         .delete('Descuento', where: 'id = ?', whereArgs: [id]);
   }
+
+  Future<Variable?> obtenerPorNombre(String nombre) async {
+    var dbClient = await db;
+    List<Map<String, dynamic>> maps = await dbClient!.query(
+      'Variable',
+      where: 'nombre = ?',
+      whereArgs: [nombre],
+    );
+
+    if (maps.isNotEmpty) {
+      return Variable.fromJson(Map<String, dynamic>.from(maps.first));
+    }
+
+    return null;
+  }
+
+  Future<List<Pedido>> obtenerPedidosOrdenadosPorFecha() async {
+    var dbClient = await db;
+
+    String orderBy =
+        "datetime(substr(peticion, 7, 4) || '-' || substr(peticion, 4, 2) || '-' || substr(peticion, 1, 2) || ' ' || substr(peticion, 12)) ASC";
+
+    List<Map<String, dynamic>> result = await dbClient!.query(
+      'Pedido',
+      where: 'sincronizado IS NULL',
+      orderBy: orderBy,
+    );
+
+    return result.map((map) => Pedido.fromJson(map)).toList();
+  }
+
+  Future<void> sincronizarCategorias(
+      List<CategoriaProducto> categoriasApi) async {
+    var db = await RepoService().db;
+
+    var categoriasLocalesQuery = await db!.query('CategoriaProducto');
+    List<CategoriaProducto> categoriasLocales = categoriasLocalesQuery
+        .map((e) => CategoriaProducto.fromJson(e))
+        .toList();
+
+    for (var categoriaApi in categoriasApi) {
+      var categoriaLocal = categoriasLocales.firstWhere(
+        (categoria) => categoria.id == categoriaApi.id,
+        orElse: () => CategoriaProducto(),
+      );
+
+      if (categoriaLocal.id != 0 &&
+          categoriaApi.modificado != null &&
+          categoriaLocal.modificado != null &&
+          categoriaApi.modificado!.isAfter(categoriaLocal.modificado!)) {
+        await RepoService().guardar(categoriaApi);
+      } else if (categoriaLocal.id == 0) {
+        await RepoService().guardar(categoriaApi);
+      }
+    }
+  }
+
+  Future<void> sincronizarProductos(List<Producto> productosApi) async {
+    var db = await RepoService().db;
+
+    var productosLocalesQuery = await db!.query('Producto');
+    List<Producto> productosLocales =
+        productosLocalesQuery.map((e) => Producto.fromJson(e)).toList();
+
+    for (var productoApi in productosApi) {
+      var productoLocal = productosLocales.firstWhere(
+        (producto) => producto.id == productoApi.id,
+        orElse: () => Producto(),
+      );
+
+      if (productoLocal.id != 0 &&
+          productoApi.modificado != null &&
+          productoLocal.modificado != null &&
+          productoApi.modificado!.isAfter(productoLocal.modificado!)) {
+        await RepoService().guardar(productoApi);
+      } else if (productoLocal.id == 0) {
+        await RepoService().guardar(productoApi);
+      }
+    }
+  }
 }

+ 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();

+ 63 - 4
lib/viewmodels/corte_caja_view_model.dart

@@ -15,6 +15,12 @@ class CorteCajaViewModel extends ChangeNotifier {
   int _totalCortes = 0;
   int _limit = 10;
 
+  double corteFinal = 0.0;
+  double totalDepositos = 0.0;
+  double totalGastos = 0.0;
+  List<Deposito> depositos = [];
+  List<Gasto> gastos = [];
+
   int get currentPage => _currentPage;
   int get totalCortes => _totalCortes;
   int get totalPages => (_totalCortes / _limit).ceil();
@@ -50,12 +56,65 @@ class CorteCajaViewModel extends ChangeNotifier {
   Future<void> buscarPorFecha(DateTime startDate, DateTime endDate) async {
     setIsLoading(true);
     RepoService<CorteCaja> repoCorte = RepoService<CorteCaja>();
+    _cortes = await repoCorte.buscarPorFechaCorte(startDate, endDate);
+    setIsLoading(false);
+    notifyListeners();
+  }
+
+  Future<void> fetchDepositosAndGastos(int corteCajaId) async {
+    RepoService<Deposito> depositoRepo = RepoService<Deposito>();
+    RepoService<Gasto> gastoRepo = RepoService<Gasto>();
+    depositos = await depositoRepo.obtenerDepositosPorIdCorteCaja(corteCajaId);
+    gastos = await gastoRepo.obtenerGastosPorIdCorteCaja(corteCajaId);
+    calcularTotalDeposito();
+    calcularTotalGasto();
+    notifyListeners();
+  }
 
-    List<CorteCaja> localCorte =
-        await repoCorte.buscarPorFechaCorte(startDate, endDate);
-    _cortes = localCorte;
+  Future<void> addDeposito(
+      double monto, String descripcion, int corteCajaId) async {
+    Deposito nuevoDeposito = Deposito(
+        monto: monto, descripcion: descripcion, idCorteCaja: corteCajaId);
+    await nuevoDeposito.guardar();
+    depositos.add(nuevoDeposito);
+    calcularTotalDeposito();
+    notifyListeners();
+  }
 
-    setIsLoading(false);
+  Future<void> addGasto(
+      double monto, String descripcion, int corteCajaId) async {
+    Gasto nuevoGasto =
+        Gasto(monto: monto, descripcion: descripcion, idCorteCaja: corteCajaId);
+    await nuevoGasto.guardar();
+    gastos.add(nuevoGasto);
+    calcularTotalGasto();
+    notifyListeners();
+  }
+
+  void calcularTotalDeposito() {
+    totalDepositos = depositos.fold(0, (sum, item) => sum + (item.monto ?? 0));
+  }
+
+  void calcularTotalGasto() {
+    totalGastos = gastos.fold(0, (sum, item) => sum + (item.monto ?? 0));
+  }
+
+  void quitarDeposito(int index) {
+    depositos.removeAt(index);
+    calcularTotalDeposito();
+    notifyListeners();
+  }
+
+  void quitarGasto(int index) {
+    gastos.removeAt(index);
+    calcularTotalGasto();
+    notifyListeners();
+  }
+
+  void calcularCorteFinal(
+      double fondo, double fondoSiguiente, double ventasMostrador) {
+    corteFinal = (fondo + totalDepositos + ventasMostrador) -
+        (fondoSiguiente + totalGastos);
     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';
 
@@ -244,7 +245,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;
@@ -256,4 +265,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;
 }

+ 81 - 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,75 @@ class ProductoViewModel<T> extends ChangeNotifier {
     return null;
   }
 
+  Future<bool> sincronizarCategorias() async {
+    try {
+      final response = ApiResponse(await BaseService().get('/pos/categoria'));
+
+      if (response.isOk && response.resultados != null) {
+        List<CategoriaProducto> categoriasApi = response.resultados!
+            .map((json) => CategoriaProducto.fromApi(json))
+            .toList();
+
+        if (categoriasApi.isNotEmpty) {
+          await RepoService().sincronizarCategorias(categoriasApi);
+          notifyListeners();
+          return true;
+        }
+      }
+      return false;
+    } catch (e) {
+      print('Error al sincronizar categorías: $e');
+      return false;
+    }
+  }
+
+  Future<bool> sincronizarProductos() async {
+    try {
+      Map<String, String> parametros = {"limite": "-1"};
+      final response = ApiResponse(await BaseService()
+          .get('/pos/producto', queryParameters: parametros));
+
+      if (response.isOk && response.resultados != null) {
+        List<Producto> productosApi =
+            response.resultados!.map((json) => Producto.fromApi(json)).toList();
+
+        if (productosApi.isNotEmpty) {
+          await RepoService().sincronizarProductos(productosApi);
+          notifyListeners();
+          return true;
+        }
+      }
+      return false;
+    } catch (e) {
+      print('Error al sincronizar productos: $e');
+      return false;
+    }
+  }
+
+  Future<void> sincronizarProductosYCategorias() async {
+    print('Sincronizando productos');
+    setIsLoading(true);
+    try {
+      bool categoriasSincronizadas = await sincronizarCategorias();
+      print('Categorias sincronizadas: $categoriasSincronizadas');
+
+      if (categoriasSincronizadas) {
+        bool productosSincronizados = await sincronizarProductos();
+        if (productosSincronizados) {
+          await fetchLocalAll();
+        }
+        print('Productos sincronizados: $productosSincronizados');
+      }
+      notifyListeners();
+    } catch (e, stackTrace) {
+      // Capturar el mensaje detallado del error
+      throw Exception(
+          "Error al sincronizar productos y categorías: $e\n$stackTrace");
+    } finally {
+      setIsLoading(false);
+    }
+  }
+
   void setIsLoading(bool loading) {
     _isLoading = loading;
     notifyListeners();

+ 15 - 0
lib/viewmodels/variable_view_model.dart

@@ -118,4 +118,19 @@ class VariableViewModel extends ChangeNotifier {
 
     return false;
   }
+
+  Future<Variable?> getVariableByClave(String nombre) async {
+    var db = await RepoService().db;
+    var result = await db!.query(
+      'Variable',
+      where: 'nombre = ?',
+      whereArgs: [nombre],
+    );
+
+    if (result.isNotEmpty) {
+      return Variable.fromJson(result.first);
+    }
+
+    return null;
+  }
 }

+ 46 - 4
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,29 @@ class _CategoriaProductoScreenState extends State<CategoriaProductoScreen> {
     });
   }
 
+  void _mostrarResultado(BuildContext context, String mensaje, bool exito) {
+    showDialog(
+      context: context,
+      builder: (BuildContext context) {
+        return AlertDialog(
+          title: Text(
+              exito ? 'Sincronización exitosa' : 'Error de sincronización'),
+          content: SingleChildScrollView(
+            child: Text(mensaje), // Mostrar el mensaje completo de error aquí
+          ),
+          actions: [
+            TextButton(
+              child: Text('OK'),
+              onPressed: () {
+                Navigator.of(context).pop();
+              },
+            ),
+          ],
+        );
+      },
+    );
+  }
+
   @override
   Widget build(BuildContext context) {
     final model = Provider.of<CategoriaProductoViewModel>(context);
@@ -159,10 +183,28 @@ class _CategoriaProductoScreenState extends State<CategoriaProductoScreen> {
 
     return Scaffold(
       appBar: AppBar(
-        title: Text(
-          'Categoría Producto',
-          style:
-              TextStyle(color: AppTheme.secondary, fontWeight: FontWeight.w500),
+        title: GestureDetector(
+          onTap: () async {
+            _versionTapCount++;
+            if (_versionTapCount == 5) {
+              final productoViewModel =
+                  Provider.of<ProductoViewModel>(context, listen: false);
+
+              try {
+                await productoViewModel.sincronizarProductosYCategorias();
+                _mostrarResultado(context,
+                    'La sincronización se completó exitosamente.', true);
+              } catch (e) {
+                _mostrarResultado(context, e.toString(), false);
+              }
+              _versionTapCount = 0;
+            }
+          },
+          child: Text(
+            'Categoría Producto',
+            style: TextStyle(
+                color: AppTheme.secondary, fontWeight: FontWeight.w500),
+          ),
         ),
         iconTheme: IconThemeData(color: AppTheme.secondary),
       ),

+ 346 - 111
lib/views/corte_caja/corte_caja_form.dart

@@ -1,117 +1,130 @@
-import 'package:conalep_pos/themes/themes.dart';
 import 'package:flutter/material.dart';
+import 'package:provider/provider.dart';
+import 'package:conalep_pos/themes/themes.dart';
 import 'package:intl/intl.dart';
+import '../../viewmodels/corte_caja_view_model.dart';
+import '../../widgets/widgets.dart';
 
 class CorteCajaForm extends StatefulWidget {
+  final int? corteCajaId;
+
+  const CorteCajaForm({this.corteCajaId, Key? key}) : super(key: key);
+
   @override
   _CorteCajaFormState createState() => _CorteCajaFormState();
 }
 
 class _CorteCajaFormState extends State<CorteCajaForm> {
   final TextEditingController _fondoController = TextEditingController();
-  final TextEditingController _depositosController = TextEditingController();
   final TextEditingController _fondoSiguienteController =
       TextEditingController();
   final TextEditingController _ventasMostradorController =
       TextEditingController();
-  final TextEditingController _gastosDiaController = TextEditingController();
-  double corteFinal = 0.0;
+  final TextEditingController _montoDepositoController =
+      TextEditingController();
+  final TextEditingController _montoGastoController = TextEditingController();
+  final TextEditingController _descripcionDepositoController =
+      TextEditingController();
+  final TextEditingController _descripcionGastoController =
+      TextEditingController();
+
+  bool showDepositoPanel = false;
+  bool showGastoPanel = false;
+
+  @override
+  void initState() {
+    super.initState();
+    if (widget.corteCajaId != null) {
+      Provider.of<CorteCajaViewModel>(context, listen: false)
+          .fetchDepositosAndGastos(widget.corteCajaId!);
+    }
+  }
 
   @override
   Widget build(BuildContext context) {
+    final viewModel = Provider.of<CorteCajaViewModel>(context);
+
     return Scaffold(
       appBar: AppBar(
-          title: Text(
-            'Corte de Caja Conalep',
-            style: TextStyle(color: AppTheme.quaternary),
-          ),
-          iconTheme: IconThemeData(color: AppTheme.quaternary)),
-      body: Align(
-        alignment: Alignment.topLeft,
-        child: Container(
-          padding: EdgeInsets.all(16),
-          width: MediaQuery.of(context).size.width * 0.5,
-          child: Column(
-            crossAxisAlignment: CrossAxisAlignment.start,
+        title: Text('Corte de Caja Conalep',
+            style: TextStyle(color: AppTheme.quaternary)),
+        iconTheme: IconThemeData(color: AppTheme.quaternary),
+      ),
+      body: Column(children: [
+        Container(
+          padding: EdgeInsets.symmetric(horizontal: 50, vertical: 20),
+          width: MediaQuery.of(context).size.width,
+          child: Row(
+            crossAxisAlignment: CrossAxisAlignment.center,
+            mainAxisAlignment: MainAxisAlignment.center,
             children: [
-              Text("Fecha: ${DateFormat('dd/MM/yyyy').format(DateTime.now())}",
-                  style: TextStyle(fontSize: 20, fontWeight: FontWeight.bold)),
-              SizedBox(height: 20),
-              Table(
-                columnWidths: {
-                  0: FlexColumnWidth(3),
-                  1: FlexColumnWidth(2),
-                },
-                children: [
-                  _buildTableRow("Fondo", _fondoController),
-                  _buildTableRow("Depósitos", _depositosController),
-                  _buildTableRow(
-                      "Fondo día siguiente", _fondoSiguienteController),
-                  _buildTableRow(
-                      "Ventas mostrador efectivo", _ventasMostradorController),
-                  _buildTableRow("Gastos del día", _gastosDiaController),
-                  TableRow(
-                    children: [
-                      Padding(
-                        padding: EdgeInsets.all(8.0),
-                        child: Text("Corte final",
-                            style: TextStyle(fontWeight: FontWeight.bold)),
-                      ),
-                      Padding(
-                        padding:
-                            EdgeInsets.symmetric(vertical: 8, horizontal: 12),
-                        child: Text("\$${corteFinal.toStringAsFixed(2)}",
-                            style: TextStyle(fontWeight: FontWeight.bold)),
-                      ),
-                    ],
-                  ),
-                ],
+              Expanded(
+                flex: 4,
+                child: _buildFormContent(context, viewModel),
+              ),
+              SizedBox(width: 80),
+              Expanded(
+                flex: 3,
+                child: Column(
+                  children: [
+                    if (showDepositoPanel) _buildDepositoPanel(viewModel),
+                    if (showGastoPanel) _buildGastoPanel(viewModel),
+                  ],
+                ),
               ),
-              SizedBox(height: 20),
-              Row(
-                mainAxisAlignment: MainAxisAlignment.spaceEvenly,
-                children: [
-                  ElevatedButton(
-                    onPressed: calcularCorteFinal,
-                    child: Text(
-                      'Finalizar',
-                      style: TextStyle(color: AppTheme.quaternary),
-                    ),
-                    style: ElevatedButton.styleFrom(backgroundColor: AppTheme.primary),
-                  ),
-                  ElevatedButton.icon(
-                    icon: Image.asset(
-                      'assets/csv_white.png',
-                      scale: 3,
-                    ),
-                    label: Text('Generar CSV',
-                        style: TextStyle(color: AppTheme.quaternary)),
-                    onPressed: () {
-                      // Lógica para generar CSV
-                    },
-                    style: ElevatedButton.styleFrom(
-                        backgroundColor: AppTheme.primary,
-                        padding: EdgeInsets.all(20),
-                        shape: RoundedRectangleBorder(
-                            borderRadius:
-                                BorderRadius.all(Radius.circular(20)))),
-                  ),
-                  ElevatedButton.icon(
-                    icon: Icon(Icons.picture_as_pdf,
-                        color: AppTheme.quaternary), // Icono para PDF
-                    label: Text('Generar PDF',
-                        style: TextStyle(color: AppTheme.quaternary)),
-                    onPressed: () {
-                      // Lógica para generar PDF
-                    },
-                    style: ElevatedButton.styleFrom(backgroundColor: AppTheme.primary),
-                  ),
-                ],
-              )
             ],
           ),
+        )
+      ]),
+    );
+  }
+
+  Widget _buildFormContent(BuildContext context, CorteCajaViewModel viewModel) {
+    return Column(
+      crossAxisAlignment: CrossAxisAlignment.start,
+      children: [
+        Text(
+          "Fecha: ${DateFormat('dd/MM/yyyy').format(DateTime.now())}",
+          style: TextStyle(fontSize: 20, fontWeight: FontWeight.bold),
         ),
-      ),
+        SizedBox(height: 20),
+        tarjeta(
+          Table(
+            defaultVerticalAlignment: TableCellVerticalAlignment.middle,
+            columnWidths: {0: FlexColumnWidth(4), 1: FlexColumnWidth(2)},
+            children: [
+              _buildTableRow("Fondo", _fondoController),
+              _buildDepositoRow(viewModel),
+              _buildTableRow("Fondo día siguiente", _fondoSiguienteController),
+              _buildTableRow(
+                  "Ventas mostrador efectivo", _ventasMostradorController),
+              _buildGastoRow(viewModel),
+              _buildCorteFinalRow(viewModel),
+            ],
+          ),
+        ),
+        SizedBox(height: 20),
+        Center(
+          child: ElevatedButton(
+            onPressed: () {
+              viewModel.calcularCorteFinal(
+                double.tryParse(_fondoController.text) ?? 0,
+                double.tryParse(_fondoSiguienteController.text) ?? 0,
+                double.tryParse(_ventasMostradorController.text) ?? 0,
+              );
+            },
+            child:
+                Text('Finalizar', style: TextStyle(color: AppTheme.quaternary)),
+            style: ElevatedButton.styleFrom(
+              backgroundColor: AppTheme.primary,
+              padding: EdgeInsets.symmetric(horizontal: 50, vertical: 15),
+              shape: RoundedRectangleBorder(
+                borderRadius: BorderRadius.circular(10),
+              ),
+            ),
+          ),
+        ),
+      ],
     );
   }
 
@@ -119,34 +132,256 @@ class _CorteCajaFormState extends State<CorteCajaForm> {
     return TableRow(
       children: [
         Padding(
-          padding: EdgeInsets.all(8.0),
+          padding: const EdgeInsets.all(8.0),
           child: Text(label, style: TextStyle(fontWeight: FontWeight.bold)),
         ),
-        TextField(
-          controller: controller,
-          keyboardType: TextInputType.number,
-          decoration: InputDecoration(
-            hintText: "\$",
-            border: OutlineInputBorder(),
-            contentPadding: EdgeInsets.symmetric(vertical: 8, horizontal: 12),
+        Padding(
+          padding: const EdgeInsets.all(8.0),
+          child: TextField(
+            controller: controller,
+            keyboardType: TextInputType.number,
+            decoration: InputDecoration(
+              hintText: "\$",
+              border: OutlineInputBorder(),
+              contentPadding: EdgeInsets.symmetric(vertical: 8, horizontal: 12),
+            ),
           ),
         ),
       ],
     );
   }
 
-  void calcularCorteFinal() {
-    double fondo = double.tryParse(_fondoController.text) ?? 0;
-    double depositos = double.tryParse(_depositosController.text) ?? 0;
-    double fondoSiguiente =
-        double.tryParse(_fondoSiguienteController.text) ?? 0;
-    double ventasMostrador =
-        double.tryParse(_ventasMostradorController.text) ?? 0;
-    double gastosDia = double.tryParse(_gastosDiaController.text) ?? 0;
-
-    setState(() {
-      corteFinal =
-          (fondo + depositos + ventasMostrador) - (fondoSiguiente + gastosDia);
-    });
+  TableRow _buildDepositoRow(CorteCajaViewModel viewModel) {
+    return TableRow(
+      children: [
+        Padding(
+          padding: const EdgeInsets.all(8.0),
+          child:
+              Text("Depósitos", style: TextStyle(fontWeight: FontWeight.bold)),
+        ),
+        Row(
+          children: [
+            Expanded(
+              child: Container(
+                height: 45,
+                decoration: BoxDecoration(
+                  border: Border.all(color: Colors.grey),
+                  borderRadius: BorderRadius.circular(5),
+                ),
+                alignment: Alignment.center,
+                padding: EdgeInsets.symmetric(horizontal: 12),
+                child: Text("\$${viewModel.totalDepositos.toStringAsFixed(2)}"),
+              ),
+            ),
+            IconButton(
+              icon: Icon(Icons.add, color: AppTheme.primary),
+              onPressed: () {
+                setState(() {
+                  showDepositoPanel = true;
+                  showGastoPanel = false;
+                });
+              },
+            ),
+          ],
+        ),
+      ],
+    );
+  }
+
+  TableRow _buildGastoRow(CorteCajaViewModel viewModel) {
+    return TableRow(
+      children: [
+        Padding(
+          padding: const EdgeInsets.all(8.0),
+          child: Text("Gastos del Día",
+              style: TextStyle(fontWeight: FontWeight.bold)),
+        ),
+        Row(
+          children: [
+            Expanded(
+              child: Container(
+                height: 45,
+                decoration: BoxDecoration(
+                  border: Border.all(color: Colors.grey),
+                  borderRadius: BorderRadius.circular(5),
+                ),
+                alignment: Alignment.center,
+                padding: EdgeInsets.symmetric(horizontal: 12),
+                child: Text("\$${viewModel.totalGastos.toStringAsFixed(2)}"),
+              ),
+            ),
+            IconButton(
+              icon: Icon(Icons.add, color: AppTheme.primary),
+              onPressed: () {
+                setState(() {
+                  showGastoPanel = true;
+                  showDepositoPanel = false;
+                });
+              },
+            ),
+          ],
+        ),
+      ],
+    );
+  }
+
+  Widget _buildDepositoPanel(CorteCajaViewModel viewModel) {
+    return tarjeta(
+      Column(
+        crossAxisAlignment: CrossAxisAlignment.start,
+        children: [
+          Text(
+            "Depósitos",
+            style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
+          ),
+          SizedBox(height: 10),
+          TextField(
+            controller: _montoDepositoController,
+            decoration: InputDecoration(
+              labelText: "Monto del depósito",
+              border: OutlineInputBorder(),
+            ),
+            keyboardType: TextInputType.number,
+          ),
+          SizedBox(height: 10),
+          TextField(
+            controller: _descripcionDepositoController,
+            decoration: InputDecoration(
+              labelText: "Descripción del depósito",
+              border: OutlineInputBorder(),
+            ),
+          ),
+          SizedBox(height: 10),
+          ElevatedButton(
+            onPressed: () {
+              double monto =
+                  double.tryParse(_montoDepositoController.text) ?? 0;
+              String descripcion = _descripcionDepositoController.text;
+
+              viewModel.addDeposito(monto, descripcion, widget.corteCajaId!);
+              _montoDepositoController.clear();
+              _descripcionDepositoController.clear();
+              viewModel.calcularTotalDeposito();
+
+              setState(() {
+                showDepositoPanel = false;
+              });
+            },
+            child: Text("Agregar"),
+          ),
+          SizedBox(height: 20),
+          _buildDepositoList(viewModel),
+        ],
+      ),
+    );
+  }
+
+  Widget _buildDepositoList(CorteCajaViewModel viewModel) {
+    return ListView.builder(
+      shrinkWrap: true,
+      itemCount: viewModel.depositos.length,
+      itemBuilder: (context, index) {
+        var deposito = viewModel.depositos[index];
+        return ListTile(
+          title: Text("\$${deposito.monto?.toStringAsFixed(2)}"),
+          subtitle: Text(deposito.descripcion!),
+          trailing: IconButton(
+            icon: Icon(Icons.delete, color: Colors.red),
+            onPressed: () {
+              viewModel.quitarDeposito(index);
+              viewModel.calcularTotalDeposito();
+            },
+          ),
+        );
+      },
+    );
+  }
+
+  Widget _buildGastoPanel(CorteCajaViewModel viewModel) {
+    return tarjeta(
+      Column(
+        crossAxisAlignment: CrossAxisAlignment.start,
+        children: [
+          Text(
+            "Gastos",
+            style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
+          ),
+          SizedBox(height: 10),
+          TextField(
+            controller: _montoGastoController,
+            decoration: InputDecoration(
+              labelText: "Monto del gasto",
+              border: OutlineInputBorder(),
+            ),
+            keyboardType: TextInputType.number,
+          ),
+          SizedBox(height: 10),
+          TextField(
+            controller: _descripcionGastoController,
+            decoration: InputDecoration(
+              labelText: "Descripción del gasto",
+              border: OutlineInputBorder(),
+            ),
+          ),
+          SizedBox(height: 10),
+          ElevatedButton(
+            onPressed: () {
+              double monto = double.tryParse(_montoGastoController.text) ?? 0;
+              String descripcion = _descripcionGastoController.text;
+
+              viewModel.addGasto(monto, descripcion, widget.corteCajaId!);
+              _montoGastoController.clear();
+              _descripcionGastoController.clear();
+              viewModel.calcularTotalGasto();
+
+              setState(() {
+                showGastoPanel = false;
+              });
+            },
+            child: Text("Agregar"),
+          ),
+          SizedBox(height: 20),
+          _buildGastoList(viewModel),
+        ],
+      ),
+    );
+  }
+
+  Widget _buildGastoList(CorteCajaViewModel viewModel) {
+    return ListView.builder(
+      shrinkWrap: true,
+      itemCount: viewModel.gastos.length,
+      itemBuilder: (context, index) {
+        var gasto = viewModel.gastos[index];
+        return ListTile(
+          title: Text("\$${gasto.monto?.toStringAsFixed(2)}"),
+          subtitle: Text(gasto.descripcion!),
+          trailing: IconButton(
+            icon: Icon(Icons.delete, color: Colors.red),
+            onPressed: () {
+              viewModel.quitarGasto(index);
+              viewModel.calcularTotalGasto();
+            },
+          ),
+        );
+      },
+    );
+  }
+
+  TableRow _buildCorteFinalRow(CorteCajaViewModel viewModel) {
+    return TableRow(
+      children: [
+        Padding(
+          padding: EdgeInsets.all(8.0),
+          child: Text("Corte Final",
+              style: TextStyle(fontWeight: FontWeight.bold)),
+        ),
+        Padding(
+          padding: const EdgeInsets.all(8.0),
+          child: Text("\$${viewModel.corteFinal.toStringAsFixed(2)}",
+              style: TextStyle(fontWeight: FontWeight.bold)),
+        ),
+      ],
+    );
   }
 }

+ 11 - 2
lib/views/home/home_screen.dart

@@ -4,6 +4,8 @@ import 'package:flutter/material.dart';
 import 'package:provider/provider.dart';
 
 import '../../viewmodels/login_view_model.dart';
+import '../../viewmodels/viewmodels.dart';
+import '../pedido/pedido_sync.dart';
 
 class HomeScreen extends StatefulWidget {
   const HomeScreen({super.key});
@@ -16,8 +18,15 @@ class Formulario extends State<HomeScreen> {
   @override
   void initState() {
     super.initState();
-    Future(() async {
-      await Provider.of<LoginViewModel>(context, listen: false).setValores();
+    // Future(() async {
+    //   await Provider.of<LoginViewModel>(context, listen: false).setValores();
+    // });
+    PedidoSync()
+        .startSync(Provider.of<PedidoViewModel>(context, listen: false));
+
+    WidgetsBinding.instance.addPostFrameCallback((_) {
+      Provider.of<ProductoViewModel>(context, listen: false)
+          .sincronizarProductosYCategorias();
     });
   }
 

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

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

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

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

Rozdílová data souboru nebyla zobrazena, protože soubor je příliš velký
+ 599 - 359
lib/views/pedido/pedido_form.dart


+ 289 - 172
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;
@@ -47,7 +49,7 @@ class _PedidoScreenState extends State<PedidoScreen> {
     }
 
     if (pedidosConProductos.isNotEmpty) {
-      String fileName = 'Pedidos_OlivaMia_POS';
+      String fileName = 'Pedidos_Conalep_POS';
       if (fechaInicio != null && fechaFin != null) {
         String startDateStr = DateFormat('dd-MM-yyyy').format(fechaInicio!);
         String endDateStr = DateFormat('dd-MM-yyyy').format(fechaFin!);
@@ -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,
+          //   ),
+          // ),
         ],
       ),
     );
@@ -499,4 +560,60 @@ class _PedidoScreenState extends State<PedidoScreen> {
           ],
         ));
   }
+
+  void alertaSync(BuildContext context) {
+    showDialog(
+      context: context,
+      builder: (BuildContext context) {
+        return AlertDialog(
+          title: const Text('Confirmación',
+              style: TextStyle(fontWeight: FontWeight.w500, fontSize: 22)),
+          content: const Text('¿Deseas sincronizar todos los pedidos de nuevo?',
+              style: TextStyle(fontSize: 18)),
+          actions: [
+            Row(
+              mainAxisAlignment: MainAxisAlignment.spaceBetween,
+              children: [
+                TextButton(
+                  child: const Text('No', style: TextStyle(fontSize: 18)),
+                  style: ButtonStyle(
+                      padding: MaterialStatePropertyAll(
+                          EdgeInsets.fromLTRB(20, 10, 20, 10)),
+                      backgroundColor: MaterialStatePropertyAll(Colors.red),
+                      foregroundColor:
+                          MaterialStatePropertyAll(AppTheme.secondary)),
+                  onPressed: () {
+                    Navigator.of(context).pop();
+                  },
+                ),
+                TextButton(
+                  child: const Text('Sí', style: TextStyle(fontSize: 18)),
+                  style: ButtonStyle(
+                      padding: MaterialStatePropertyAll(
+                          EdgeInsets.fromLTRB(20, 10, 20, 10)),
+                      backgroundColor:
+                          MaterialStatePropertyAll(AppTheme.tertiary),
+                      foregroundColor:
+                          MaterialStatePropertyAll(AppTheme.quaternary)),
+                  onPressed: () {
+                    // No hace nada por el momento
+                    Navigator.of(context).pop();
+                  },
+                ),
+              ],
+            )
+          ],
+        );
+      },
+    );
+  }
+
+  String _formatDateTime(String? dateTimeString) {
+    if (dateTimeString == null) return "Sin fecha";
+
+    DateTime parsedDate = DateTime.parse(dateTimeString);
+
+    var formatter = DateFormat('dd-MM-yyyy HH:mm:ss');
+    return formatter.format(parsedDate.toLocal());
+  }
 }

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

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

+ 257 - 43
lib/views/pedido/pedido_ticket.dart

@@ -28,9 +28,9 @@ Future<void> imprimirTicketsJuntos(BuildContext context, Pedido pedido) async {
   }
 
   if (ticketCocinaActivo) {
-    pdf.addPage(
-      generarPaginaSegundoTicket(pedido),
-    );
+    final paginaSegundoTicket =
+        await generarPaginaSegundoTicket(context, pedido);
+    pdf.addPage(paginaSegundoTicket);
   }
 
   await printPdf(Uint8List.fromList(await pdf.save()));
@@ -47,16 +47,16 @@ pw.Page generarPaginaPrimerTicket(Pedido pedido, pw.MemoryImage image) {
   final productList = pedido.productos
       .map(
         (producto) {
-          final productPrice = double.parse(producto.producto?.precio ?? '0');
+          final productPrice = producto.producto?.precio ?? 0.0;
           final productTotal = productPrice * (producto.cantidad ?? 1);
 
           totalSinDescuento += productTotal;
 
           final toppingsList = producto.toppings.where((topping) {
-            final toppingPrice = double.parse(topping.topping?.precio ?? '0');
+            final toppingPrice = topping.topping?.precio ?? 0.0;
             return toppingPrice > 0;
           }).map((topping) {
-            final toppingPrice = double.parse(topping.topping?.precio ?? '0');
+            final toppingPrice = topping.topping?.precio ?? 0.0;
             totalSinDescuento += toppingPrice * (producto.cantidad ?? 1);
 
             return pw.Row(
@@ -196,41 +196,247 @@ pw.Page generarPaginaPrimerTicket(Pedido pedido, pw.MemoryImage image) {
       });
 }
 
-pw.Page generarPaginaSegundoTicket(Pedido pedido) {
+//Ticket PC
+
+// Future<pw.Page> generarPaginaSegundoTicket(
+//     BuildContext context, Pedido pedido) async {
+//   final numberFormat = NumberFormat('#,##0.00', 'es_MX');
+//   double subtotal = 0;
+//   double totalConDescuento = 0;
+//   double descuento = pedido.descuento?.toDouble() ?? 0.0;
+//   double precioDescuento = 0;
+
+//   final sucursalVariable =
+//       await Provider.of<VariableViewModel>(context, listen: false)
+//           .getVariableByClave('Sucursal');
+//   final sucursalDescripcion = sucursalVariable?.descripcion ?? '';
+
+//   List<pw.Widget> content = [
+//     pw.Padding(
+//       padding: pw.EdgeInsets.only(right: 10),
+//       child: pw.Row(
+//         mainAxisAlignment: pw.MainAxisAlignment.spaceAround,
+//         children: [
+//           pw.Text('${pedido.folio}/ ',
+//               style:
+//                   pw.TextStyle(fontSize: 10.5, fontWeight: pw.FontWeight.bold)),
+//           if (sucursalDescripcion.isNotEmpty)
+//             pw.Text('$sucursalDescripcion/ ',
+//                 style: pw.TextStyle(
+//                     fontWeight: pw.FontWeight.bold, fontSize: 10.5)),
+//         ],
+//       ),
+//     ),
+//     pw.SizedBox(height: 2),
+//     pw.Row(
+//       mainAxisAlignment: pw.MainAxisAlignment.spaceAround,
+//       children: [
+//         pw.Text('${_formatDateTime(pedido.peticion)}',
+//             style:
+//                 pw.TextStyle(fontSize: 10.5, fontWeight: pw.FontWeight.bold)),
+//       ],
+//     ),
+//     pw.SizedBox(height: 2),
+//     if (pedido.nombreCliente != null && pedido.nombreCliente!.isNotEmpty)
+//       pw.Text('Cliente: ${pedido.nombreCliente}',
+//           style: pw.TextStyle(fontWeight: pw.FontWeight.bold, fontSize: 10.5)),
+//     pw.SizedBox(height: 2),
+//   ];
+
+//   // Mostrar los productos con la cantidad, el precio total y el precio de los toppings
+//   content.addAll(pedido.productos
+//       .map((producto) {
+//         final productPrice = producto.producto?.precio ?? 0.0;
+//         final productTotal = productPrice * (producto.cantidad ?? 1);
+//         subtotal += productTotal;
+
+//         final toppingsList = producto.toppings.map((topping) {
+//           final toppingPrice = topping.topping?.precio ?? 0.0;
+//           final toppingTotal = toppingPrice * (producto.cantidad ?? 1);
+//           subtotal += toppingTotal;
+
+//           return pw.Row(
+//             mainAxisAlignment: pw.MainAxisAlignment.start,
+//             children: [
+//               pw.Expanded(
+//                 flex: 3,
+//                 child: pw.Text(
+//                     '-${topping.topping?.nombre ?? "Topping no especificado"}',
+//                     style: const pw.TextStyle(fontSize: 8.5)),
+//               ),
+//               if (toppingPrice > 0)
+//                 pw.Expanded(
+//                   flex: 1,
+//                   child: pw.Text(
+//                     '\$${numberFormat.format(toppingTotal)}',
+//                     style: const pw.TextStyle(fontSize: 8.5),
+//                     textAlign: pw.TextAlign.right,
+//                   ),
+//                 ),
+//             ],
+//           );
+//         }).toList();
+
+//         return [
+//           pw.Row(
+//             mainAxisAlignment: pw.MainAxisAlignment.spaceBetween,
+//             children: [
+//               pw.Expanded(
+//                 flex: 1,
+//                 child: pw.Text('${producto.cantidad}',
+//                     style: const pw.TextStyle(fontSize: 10.5)),
+//               ),
+//               pw.Expanded(
+//                 flex: 6,
+//                 child: pw.Text(
+//                     producto.producto?.nombre ?? "Producto no especificado",
+//                     style: const pw.TextStyle(fontSize: 10.5)),
+//               ),
+//               pw.Expanded(
+//                   flex: 4,
+//                   child: pw.Align(
+//                     alignment: pw.Alignment.centerLeft,
+//                     child: pw.Text(
+//                       '\$${numberFormat.format(productTotal)}',
+//                       style: const pw.TextStyle(fontSize: 10.5),
+//                     ),
+//                   )),
+//             ],
+//           ),
+//           ...toppingsList,
+//         ];
+//       })
+//       .expand((e) => e)
+//       .toList());
+
+//   // Calcular el descuento y el total final
+//   precioDescuento = subtotal * (descuento / 100);
+//   totalConDescuento = subtotal - precioDescuento;
+
+//   content.add(pw.Divider());
+
+//   if (descuento > 0) {
+//     content.addAll([
+//       pw.Row(
+//         mainAxisAlignment: pw.MainAxisAlignment.spaceBetween,
+//         children: [
+//           pw.Text('Subtotal:',
+//               style:
+//                   pw.TextStyle(fontWeight: pw.FontWeight.bold, fontSize: 10.5)),
+//           pw.Padding(
+//               padding: pw.EdgeInsets.only(right: 15),
+//               child: pw.Text('\$${numberFormat.format(subtotal)}',
+//                   style: pw.TextStyle(
+//                       fontWeight: pw.FontWeight.bold, fontSize: 10.5))),
+//         ],
+//       ),
+//       pw.Row(
+//         mainAxisAlignment: pw.MainAxisAlignment.spaceBetween,
+//         children: [
+//           pw.Text('Descuento:',
+//               style:
+//                   pw.TextStyle(fontWeight: pw.FontWeight.bold, fontSize: 10.5)),
+//           pw.Padding(
+//               padding: pw.EdgeInsets.only(right: 15),
+//               child: pw.Text('-\$${numberFormat.format(precioDescuento)}',
+//                   style: pw.TextStyle(
+//                       fontWeight: pw.FontWeight.bold, fontSize: 10.5))),
+//         ],
+//       ),
+//     ]);
+//   }
+
+//   content.add(
+//     pw.Row(
+//       mainAxisAlignment: pw.MainAxisAlignment.spaceBetween,
+//       children: [
+//         pw.Text('Total:',
+//             style:
+//                 pw.TextStyle(fontWeight: pw.FontWeight.bold, fontSize: 10.5)),
+//         pw.Padding(
+//             padding: pw.EdgeInsets.only(right: 20),
+//             child: pw.Text(
+//                 '\$${numberFormat.format(descuento > 0 ? totalConDescuento : subtotal)}',
+//                 style: pw.TextStyle(
+//                     fontWeight: pw.FontWeight.bold, fontSize: 10.5))),
+//       ],
+//     ),
+//   );
+
+//   if (pedido.comentarios != null && pedido.comentarios!.isNotEmpty) {
+//     content.add(pw.SizedBox(height: 1));
+//     content.add(pw.Text('Comentarios:',
+//         style: pw.TextStyle(fontWeight: pw.FontWeight.bold, fontSize: 10.5)));
+//     content.add(pw.Padding(
+//       padding: const pw.EdgeInsets.only(right: 15),
+//       child: pw.Text(pedido.comentarios!,
+//           style: const pw.TextStyle(fontSize: 10.5)),
+//     ));
+//   }
+
+//   content.add(pw.SizedBox(height: 15));
+//   content.add(pw.Text('.', style: pw.TextStyle(fontSize: 1)));
+
+//   return pw.Page(
+//     pageFormat: PdfPageFormat.roll57,
+//     margin: pw.EdgeInsets.all(5),
+//     build: (pw.Context context) {
+//       return pw.Column(
+//           crossAxisAlignment: pw.CrossAxisAlignment.center, children: content);
+//     },
+//   );
+// }
+
+//Ticket Tablet
+
+Future<pw.Page> generarPaginaSegundoTicket(
+    BuildContext context, Pedido pedido) async {
   final numberFormat = NumberFormat('#,##0.00', 'es_MX');
   double subtotal = 0;
   double totalConDescuento = 0;
   double descuento = pedido.descuento?.toDouble() ?? 0.0;
   double precioDescuento = 0;
 
+  final sucursalVariable =
+      await Provider.of<VariableViewModel>(context, listen: false)
+          .getVariableByClave('Sucursal');
+  final sucursalDescripcion = sucursalVariable?.descripcion ?? '';
+
   List<pw.Widget> content = [
-    pw.SizedBox(height: 20),
-    pw.Text('.', style: pw.TextStyle(fontSize: 1)),
     pw.Row(
       mainAxisAlignment: pw.MainAxisAlignment.spaceAround,
       children: [
-        pw.Text('${pedido.folio}',
-            style: pw.TextStyle(fontSize: 9, fontWeight: pw.FontWeight.bold)),
-        pw.Text('${pedido.peticion}',
-            style: pw.TextStyle(fontSize: 9, fontWeight: pw.FontWeight.bold)),
+        pw.Text('${pedido.folio}/ ',
+            style: pw.TextStyle(fontSize: 7, fontWeight: pw.FontWeight.bold)),
+        if (sucursalDescripcion.isNotEmpty)
+          pw.Text('$sucursalDescripcion/ ',
+              style: pw.TextStyle(fontWeight: pw.FontWeight.bold, fontSize: 7)),
       ],
     ),
-    pw.SizedBox(height: 5),
+    pw.SizedBox(height: 2),
+    pw.Row(
+      mainAxisAlignment: pw.MainAxisAlignment.spaceAround,
+      children: [
+        pw.Text('${_formatDateTime(pedido.peticion)}',
+            style: pw.TextStyle(fontSize: 7, fontWeight: pw.FontWeight.bold)),
+      ],
+    ),
+    pw.SizedBox(height: 2),
     if (pedido.nombreCliente != null && pedido.nombreCliente!.isNotEmpty)
       pw.Text('Cliente: ${pedido.nombreCliente}',
-          style: pw.TextStyle(fontWeight: pw.FontWeight.bold, fontSize: 9)),
-    pw.SizedBox(height: 10),
+          style: pw.TextStyle(fontWeight: pw.FontWeight.bold, fontSize: 7)),
+    pw.SizedBox(height: 2),
   ];
 
   // Mostrar los productos con la cantidad, el precio total y el precio de los toppings
   content.addAll(pedido.productos
       .map((producto) {
-        final productPrice = double.parse(producto.producto?.precio ?? '0');
+        final productPrice = producto.producto?.precio ?? 0.0;
         final productTotal = productPrice * (producto.cantidad ?? 1);
         subtotal += productTotal;
 
         final toppingsList = producto.toppings.map((topping) {
-          final toppingPrice = double.parse(topping.topping?.precio ?? '0');
+          final toppingPrice = topping.topping?.precio ?? 0.0;
           final toppingTotal = toppingPrice * (producto.cantidad ?? 1);
           subtotal += toppingTotal;
 
@@ -241,14 +447,14 @@ pw.Page generarPaginaSegundoTicket(Pedido pedido) {
                 flex: 3,
                 child: pw.Text(
                     '-${topping.topping?.nombre ?? "Topping no especificado"}',
-                    style: const pw.TextStyle(fontSize: 7)),
+                    style: const pw.TextStyle(fontSize: 6)),
               ),
               if (toppingPrice > 0)
                 pw.Expanded(
                   flex: 1,
                   child: pw.Text(
                     '\$${numberFormat.format(toppingTotal)}',
-                    style: const pw.TextStyle(fontSize: 7),
+                    style: const pw.TextStyle(fontSize: 6),
                     textAlign: pw.TextAlign.right,
                   ),
                 ),
@@ -263,22 +469,23 @@ pw.Page generarPaginaSegundoTicket(Pedido pedido) {
               pw.Expanded(
                 flex: 1,
                 child: pw.Text('${producto.cantidad}',
-                    style: const pw.TextStyle(fontSize: 9)),
+                    style: const pw.TextStyle(fontSize: 7)),
               ),
               pw.Expanded(
-                flex: 3,
+                flex: 5,
                 child: pw.Text(
                     producto.producto?.nombre ?? "Producto no especificado",
-                    style: const pw.TextStyle(fontSize: 9)),
+                    style: const pw.TextStyle(fontSize: 7)),
               ),
               pw.Expanded(
-                flex: 2,
-                child: pw.Text(
-                  '\$${numberFormat.format(productTotal)}',
-                  style: const pw.TextStyle(fontSize: 9),
-                  textAlign: pw.TextAlign.right,
-                ),
-              ),
+                  flex: 2,
+                  child: pw.Align(
+                    alignment: pw.Alignment.centerRight,
+                    child: pw.Text(
+                      '\$${numberFormat.format(productTotal)}',
+                      style: const pw.TextStyle(fontSize: 7),
+                    ),
+                  )),
             ],
           ),
           ...toppingsList,
@@ -299,18 +506,18 @@ pw.Page generarPaginaSegundoTicket(Pedido pedido) {
         mainAxisAlignment: pw.MainAxisAlignment.spaceBetween,
         children: [
           pw.Text('Subtotal:',
-              style: pw.TextStyle(fontWeight: pw.FontWeight.bold, fontSize: 9)),
+              style: pw.TextStyle(fontWeight: pw.FontWeight.bold, fontSize: 7)),
           pw.Text('\$${numberFormat.format(subtotal)}',
-              style: pw.TextStyle(fontWeight: pw.FontWeight.bold, fontSize: 9)),
+              style: pw.TextStyle(fontWeight: pw.FontWeight.bold, fontSize: 7)),
         ],
       ),
       pw.Row(
         mainAxisAlignment: pw.MainAxisAlignment.spaceBetween,
         children: [
           pw.Text('Descuento:',
-              style: pw.TextStyle(fontWeight: pw.FontWeight.bold, fontSize: 9)),
+              style: pw.TextStyle(fontWeight: pw.FontWeight.bold, fontSize: 7)),
           pw.Text('-\$${numberFormat.format(precioDescuento)}',
-              style: pw.TextStyle(fontWeight: pw.FontWeight.bold, fontSize: 9)),
+              style: pw.TextStyle(fontWeight: pw.FontWeight.bold, fontSize: 7)),
         ],
       ),
     ]);
@@ -321,32 +528,30 @@ pw.Page generarPaginaSegundoTicket(Pedido pedido) {
       mainAxisAlignment: pw.MainAxisAlignment.spaceBetween,
       children: [
         pw.Text('Total:',
-            style: pw.TextStyle(fontWeight: pw.FontWeight.bold, fontSize: 9)),
+            style: pw.TextStyle(fontWeight: pw.FontWeight.bold, fontSize: 7)),
         pw.Text(
             '\$${numberFormat.format(descuento > 0 ? totalConDescuento : subtotal)}',
-            style: pw.TextStyle(fontWeight: pw.FontWeight.bold, fontSize: 9)),
+            style: pw.TextStyle(fontWeight: pw.FontWeight.bold, fontSize: 7)),
       ],
     ),
   );
 
   if (pedido.comentarios != null && pedido.comentarios!.isNotEmpty) {
-    content.add(pw.SizedBox(height: 10));
+    content.add(pw.SizedBox(height: 1));
     content.add(pw.Text('Comentarios:',
-        style: pw.TextStyle(fontWeight: pw.FontWeight.bold, fontSize: 9)));
+        style: pw.TextStyle(fontWeight: pw.FontWeight.bold, fontSize: 7)));
     content.add(pw.Padding(
       padding: const pw.EdgeInsets.only(right: 15),
       child:
-          pw.Text(pedido.comentarios!, style: const pw.TextStyle(fontSize: 9)),
+          pw.Text(pedido.comentarios!, style: const pw.TextStyle(fontSize: 7)),
     ));
-    content.add(pw.Text('.', style: pw.TextStyle(fontSize: 1)));
-    content.add(pw.Text('.', style: pw.TextStyle(fontSize: 1)));
   }
 
-  content.add(pw.SizedBox(height: 20));
-  content.add(pw.Text('.', style: pw.TextStyle(fontSize: 1)));
+  content.add(pw.SizedBox(height: 1));
 
   return pw.Page(
     pageFormat: PdfPageFormat.roll57,
+    margin: pw.EdgeInsets.all(5),
     build: (pw.Context context) {
       return pw.Column(
           crossAxisAlignment: pw.CrossAxisAlignment.center, children: content);
@@ -359,3 +564,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());
+}

+ 9 - 5
lib/views/producto/producto_form.dart

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

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

@@ -161,7 +161,7 @@ class _ProductoScreenState extends State<ProductoScreen> {
           },
         ),
         DataCell(
-          Text(item.precio ?? "Sin Precio"),
+          Text(item.precio.toString()),
           onTap: () {
             Provider.of<ProductoViewModel>(context, listen: false)
                 .selectProducto(item);

+ 65 - 29
lib/views/venta/venta_screen.dart

@@ -25,6 +25,8 @@ class _VentaScreenState extends State<VentaScreen> {
   double totalEfectivoDelDia = 0.0;
   double totalTarjetaDelDia = 0.0;
   double totalTransferenciaDelDia = 0.0;
+  double cambio = 0.0;
+  double totalSinCambio = 0.0;
 
   String formatCurrency(double amount) {
     final format = NumberFormat("#,##0.00", "es_MX");
@@ -200,33 +202,46 @@ class _VentaScreenState extends State<VentaScreen> {
                             fontSize: 20, fontWeight: FontWeight.bold),
                       ),
                     ),
-                    if (totalEfectivoDelDia > 0)
-                      Padding(
-                        padding: const EdgeInsets.all(16.0),
-                        child: Text(
-                          "Total en Efectivo: \$${formatCurrency(totalEfectivoDelDia)}",
-                          style: TextStyle(
-                              fontSize: 20, fontWeight: FontWeight.bold),
-                        ),
-                      ),
-                    if (totalTarjetaDelDia > 0)
-                      Padding(
-                        padding: const EdgeInsets.all(16.0),
-                        child: Text(
-                          "Total en Tarjeta: \$${formatCurrency(totalTarjetaDelDia)}",
-                          style: TextStyle(
-                              fontSize: 20, fontWeight: FontWeight.bold),
-                        ),
-                      ),
-                    if (totalTransferenciaDelDia > 0)
-                      Padding(
-                        padding: const EdgeInsets.all(16.0),
-                        child: Text(
-                          "Total en Transferencia: \$${formatCurrency(totalTransferenciaDelDia)}",
-                          style: TextStyle(
-                              fontSize: 20, fontWeight: FontWeight.bold),
-                        ),
-                      ),
+                    Row(
+                      children: [
+                        if (totalTarjetaDelDia > 0)
+                          Padding(
+                            padding: const EdgeInsets.all(16.0),
+                            child: Text(
+                              "Total en Tarjeta: \$${formatCurrency(totalTarjetaDelDia)}",
+                              style: TextStyle(
+                                  fontSize: 20, fontWeight: FontWeight.bold),
+                            ),
+                          ),
+                        if (totalTransferenciaDelDia > 0)
+                          Padding(
+                            padding: const EdgeInsets.all(16.0),
+                            child: Text(
+                              "Total en Transferencia: \$${formatCurrency(totalTransferenciaDelDia)}",
+                              style: TextStyle(
+                                  fontSize: 20, fontWeight: FontWeight.bold),
+                            ),
+                          ),
+                        if (totalEfectivoDelDia > 0)
+                          Padding(
+                            padding: const EdgeInsets.all(16.0),
+                            child: Text(
+                              "Total en Efectivo: \$${formatCurrency(totalEfectivoDelDia)}",
+                              style: TextStyle(
+                                  fontSize: 20, fontWeight: FontWeight.bold),
+                            ),
+                          ),
+                        if (cambio > 0)
+                          Padding(
+                            padding: const EdgeInsets.all(16.0),
+                            child: Text(
+                              "Cambio Entregado: \$${formatCurrency(cambio)}",
+                              style: TextStyle(
+                                  fontSize: 20, fontWeight: FontWeight.bold),
+                            ),
+                          ),
+                      ],
+                    ),
                     if (totalCancelados > 0)
                       Padding(
                         padding: const EdgeInsets.all(16.0),
@@ -249,11 +264,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 =
@@ -264,14 +291,23 @@ class _VentaScreenState extends State<VentaScreen> {
     totalEfectivoDelDia = 0.0;
     totalTarjetaDelDia = 0.0;
     totalTransferenciaDelDia = 0.0;
+    cambio = 0.0;
+    totalSinCambio = 0.0;
 
     for (var pedido in pedidosNoCancelados) {
       totalDelDia += pedido.totalPedido ?? 0.0;
       totalEfectivoDelDia += pedido.cantEfectivo ?? 0.0;
       totalTarjetaDelDia += pedido.cantTarjeta ?? 0.0;
       totalTransferenciaDelDia += pedido.cantTransferencia ?? 0.0;
+      totalSinCambio =
+          totalEfectivoDelDia + totalTarjetaDelDia + totalTransferenciaDelDia;
+
+      cambio = totalSinCambio - totalDelDia;
     }
 
+    print("Total del dia sin cambios $totalSinCambio");
+    print("Cambio $cambio");
+
     totalCancelados = pedidosCancelados.fold(
         0.0, (sum, current) => sum + (current.totalPedido ?? 0.0));
 

+ 23 - 14
lib/views/venta/venta_ticket.dart

@@ -38,6 +38,9 @@ class VentaTicket {
     double totalTransferencia = pedidosNoCancelados.fold(
         0.0, (sum, p) => sum + (p.cantTransferencia ?? 0.0));
 
+    double totalSinCambio = totalEfectivo + totalTarjeta + totalTransferencia;
+    double cambio = totalSinCambio - totalNoCancelados;
+
     final spelling = SpellingNumber(lang: 'es');
     String totalEnLetras = toTitleCase(spelling.convert(totalNoCancelados));
 
@@ -48,14 +51,12 @@ class VentaTicket {
     String formattedTotalTarjeta = numberFormat.format(totalTarjeta);
     String formattedTotalTransferencia =
         numberFormat.format(totalTransferencia);
+    String formattedCambio = numberFormat.format(cambio);
 
     int centavos =
         ((totalNoCancelados - totalNoCancelados.floor()) * 100).round();
     String centavosEnLetras = centavos.toString().padLeft(2, '0') + "/100 M.N.";
 
-    print("Total en letras: $totalEnLetras $centavosEnLetras");
-    print("Total formateado: $formattedTotalNoCancelados");
-
     pdf.addPage(pw.Page(
         pageFormat: PdfPageFormat.roll57,
         build: (pw.Context context) {
@@ -94,19 +95,27 @@ class VentaTicket {
                 child: pw.Column(
                   crossAxisAlignment: pw.CrossAxisAlignment.start,
                   children: [
-                    pw.Text("- Total en Efectivo: \$${formattedTotalEfectivo}",
-                        style: pw.TextStyle(
-                            fontWeight: pw.FontWeight.bold, fontSize: 11)),
-                    pw.Text("- Total en Tarjeta: \$${formattedTotalTarjeta}",
-                        style: pw.TextStyle(
-                            fontWeight: pw.FontWeight.bold, fontSize: 11)),
-                    pw.Text(
-                        "- Total en Transferencia: \$${formattedTotalTransferencia}",
-                        style: pw.TextStyle(
-                            fontWeight: pw.FontWeight.bold, fontSize: 11)),
+                    if (totalTarjeta > 0)
+                      pw.Text("- Total en Tarjeta: \$${formattedTotalTarjeta}",
+                          style: pw.TextStyle(
+                              fontWeight: pw.FontWeight.bold, fontSize: 9.5)),
+                    if (totalTransferencia > 0)
+                      pw.Text(
+                          "- Total en Transferencia: \$${formattedTotalTransferencia}",
+                          style: pw.TextStyle(
+                              fontWeight: pw.FontWeight.bold, fontSize: 9.5)),
+                    if (totalEfectivo > 0)
+                      pw.Text(
+                          "- Total en Efectivo: \$${formattedTotalEfectivo}",
+                          style: pw.TextStyle(
+                              fontWeight: pw.FontWeight.bold, fontSize: 9.5)),
+                    if (cambio > 0)
+                      pw.Text("- Cambio Entregado: \$${formattedCambio}",
+                          style: pw.TextStyle(
+                              fontWeight: pw.FontWeight.bold, fontSize: 9.5)),
                     pw.Text("- Total General: \$${formattedTotalNoCancelados}",
                         style: pw.TextStyle(
-                            fontWeight: pw.FontWeight.bold, fontSize: 11)),
+                            fontWeight: pw.FontWeight.bold, fontSize: 9.5)),
                     pw.Text("Son: $totalEnLetras Pesos $centavosEnLetras",
                         style: pw.TextStyle(fontSize: 10))
                   ],

+ 33 - 17
lib/widgets/app_drawer.dart

@@ -16,9 +16,15 @@ import '../viewmodels/login_view_model.dart';
 import '../views/descuento/descuento_screen.dart';
 import '../views/variable/variable_screen.dart';
 import 'widgets_components.dart';
+import 'copia_db.dart';
 
-class AppDrawer extends StatelessWidget {
-  AppDrawer({super.key});
+class AppDrawer extends StatefulWidget {
+  @override
+  _AppDrawerState createState() => _AppDrawerState();
+}
+
+class _AppDrawerState extends State<AppDrawer> {
+  int _versionTapCount = 0;
 
   Future<bool> _showExitConfirmationDialog(BuildContext context) async {
     bool shouldPop = false;
@@ -161,18 +167,18 @@ class AppDrawer extends StatelessWidget {
                 ),
               },
             ),
-            ListTile(
-              leading: circulo(const Icon(Icons.point_of_sale_rounded)),
-              title: const Text('Corte De Caja'),
-              onTap: () => {
-                Navigator.pop(context),
-                Navigator.of(context).push(
-                  MaterialPageRoute(
-                    builder: (context) => CorteCajaScreen(),
-                  ),
-                ),
-              },
-            ),
+            // ListTile(
+            //   leading: circulo(const Icon(Icons.point_of_sale_rounded)),
+            //   title: const Text('Corte De Caja'),
+            //   onTap: () => {
+            //     Navigator.pop(context),
+            //     Navigator.of(context).push(
+            //       MaterialPageRoute(
+            //         builder: (context) => CorteCajaScreen(),
+            //       ),
+            //     ),
+            //   },
+            // ),
             ExpansionTile(
               leading: circulo(const Icon(Icons.admin_panel_settings)),
               title: const Text('Administración'),
@@ -223,15 +229,25 @@ class AppDrawer extends StatelessWidget {
             //   },
             // ),
           ])),
-          const Padding(
+          GestureDetector(
+            onTap: () {
+              _versionTapCount++;
+              if (_versionTapCount == 5) {
+                copyDatabase(context);
+                _versionTapCount = 0;
+              }
+            },
+            child: const Padding(
               padding: EdgeInsets.only(bottom: 10),
               child: Align(
                 alignment: Alignment.bottomCenter,
                 child: Text(
-                  'v1.24.08.22',
+                  'v1.24.10.03',
                   style: TextStyle(fontWeight: FontWeight.w300),
                 ),
-              ))
+              ),
+            ),
+          ),
         ],
       ),
     );

+ 53 - 0
lib/widgets/copia_db.dart

@@ -0,0 +1,53 @@
+import 'dart:io';
+import 'package:flutter/material.dart';
+import 'package:path_provider/path_provider.dart';
+
+Future<void> copyDatabase(BuildContext context) async {
+  try {
+    Directory appDocDir = await getApplicationDocumentsDirectory();
+
+    List<String> dbPaths = [
+      '${appDocDir.path}/databases/conalepPos7.db',
+      '${appDocDir.path}/conalepPos7.db',
+      '/data/data/com.example.conalep_pos/databases/conalepPos7.db',
+    ];
+
+    File? dbFile;
+    for (String path in dbPaths) {
+      if (await File(path).exists()) {
+        dbFile = File(path);
+        break;
+      }
+    }
+
+    if (dbFile != null) {
+      Directory? downloadsDir = await getExternalStorageDirectory();
+      if (downloadsDir == null) {
+        ScaffoldMessenger.of(context).showSnackBar(
+          SnackBar(
+              content: Text('No se pudo acceder al directorio de descargas.')),
+        );
+        return;
+      }
+
+      String newPath = '${downloadsDir.path}/conalepPos7_backup.db';
+
+      await dbFile.copy(newPath);
+
+      ScaffoldMessenger.of(context).showSnackBar(
+        SnackBar(
+            content: Text('Copia de la base de datos guardada en Descargas.')),
+      );
+    } else {
+      ScaffoldMessenger.of(context).showSnackBar(
+        SnackBar(
+            content: Text(
+                'El archivo de la base de datos no existe en ninguna de las rutas verificadas.')),
+      );
+    }
+  } catch (e) {
+    ScaffoldMessenger.of(context).showSnackBar(
+      SnackBar(content: Text('Error al copiar la base de datos: $e')),
+    );
+  }
+}

+ 1 - 0
lib/widgets/widgets.dart

@@ -6,3 +6,4 @@ export '../widgets/app_text_span.dart';
 export '../widgets/app_drawer.dart';
 export '../widgets/widgets_components.dart';
 export '../widgets/pagination_buttons.dart';
+export '../widgets/copia_db.dart';

+ 6 - 1
lib/widgets/widgets_components.dart

@@ -328,10 +328,15 @@ alerta(BuildContext context, {String? etiqueta = "Capturar búsqueda"}) {
             Row(children: [
               Expanded(
                   child: TextButton(
+                style: ButtonStyle(
+                    backgroundColor: MaterialStatePropertyAll(Colors.black)),
                 onPressed: () async {
                   Navigator.pop(context);
                 },
-                child: const Text('Continuar'),
+                child: const Text(
+                  'Continuar',
+                  style: TextStyle(color: Colors.white),
+                ),
               ))
             ])
           ],