Преглед на файлове

Sincronizacion de categorias, productos y mesas

OscarGil03 преди 2 месеца
родител
ревизия
8251b26aa0

+ 3 - 0
lib/main.dart

@@ -29,6 +29,9 @@ void main() async {
       ChangeNotifierProvider(create: (_) => SucursalViewModel()),
       ChangeNotifierProvider(create: (_) => PermisoViewModel()),
       ChangeNotifierProvider(create: (_) => UsuarioViewModel()),
+      ChangeNotifierProvider(create: (_) => ProductoViewModel()),
+      ChangeNotifierProvider(create: (_) => MesaViewModel()),
+      ChangeNotifierProvider(create: (_) => CategoriaProductoViewModel()),
       // Agrega aquí cualquier otro provider que necesites
     ], child: const MyApp()));
   });

+ 139 - 0
lib/mvvm/viewmodels/categoria_producto_view_model.dart

@@ -0,0 +1,139 @@
+import 'package:flutter/material.dart';
+import 'package:sqflite/sqflite.dart';
+import '../../core/services/services.dart';
+import '../../core/models/models.dart';
+
+class CategoriaProductoViewModel extends ChangeNotifier {
+  String _busqueda = "";
+  String get busqueda => _busqueda;
+
+  List<CategoriaProducto> _categoriaProductos = [];
+  bool _isLoading = false;
+  CategoriaProducto? _selectedCategoriaProducto;
+  Map<int, String> _categoriaMap = {};
+
+  List<CategoriaProducto> get categoriaProductos => _categoriaProductos;
+  CategoriaProducto? get selectedCategoriaProducto =>
+      _selectedCategoriaProducto;
+  bool get isLoading => _isLoading;
+
+  Map<int, String> get categoriaMap => _categoriaMap;
+
+  int _currentPage = 1;
+  int _totalProducts = 0;
+  int _limit = 20;
+
+  int get currentPage => _currentPage;
+  int get totalProducts => _totalProducts;
+  int get totalPages => (_totalProducts / _limit).ceil();
+
+  setBusqueda(String value) {
+    _busqueda = value;
+    notifyListeners();
+  }
+
+  void selectCategoriaProducto(CategoriaProducto categoriaProducto) {
+    _selectedCategoriaProducto = categoriaProducto;
+    notifyListeners();
+  }
+
+  Future<void> fetchLocalAll({int page = 1}) async {
+    _currentPage = page;
+    var db = await DatabaseService().db;
+
+    int? count = Sqflite.firstIntValue(
+        await db!.rawQuery('SELECT COUNT(*) FROM CategoriaProducto'));
+    _totalProducts = count ?? 0;
+
+    int offset = (_limit * (page - 1));
+
+    var query = await db.query('CategoriaProducto',
+        where: 'eliminado IS NULL',
+        orderBy: 'id asc',
+        limit: _limit,
+        offset: offset);
+    _categoriaProductos =
+        query.map((element) => CategoriaProducto.fromJson(element)).toList();
+    notifyListeners();
+  }
+
+  Future<void> fetchLocalCategoria() async {
+    var db = await DatabaseService().db;
+    var query = await db!.query('CategoriaProducto', orderBy: 'idLocal asc');
+    List<CategoriaProducto> aux = [];
+    _categoriaMap = {};
+    for (var element in query) {
+      CategoriaProducto categoria = CategoriaProducto.fromJson(element);
+      aux.add(categoria);
+      _categoriaMap[categoria.id!] = categoria.nombre!;
+    }
+    _categoriaProductos = aux;
+    notifyListeners();
+  }
+
+  Future<List<CategoriaProducto>> getCategoriaProducto({String q = ''}) async {
+    var db = await DatabaseService().db;
+    List<Map> results = await db!.query('CategoriaProducto',
+        where: 'nombre LIKE ?', whereArgs: ['%$q%'], orderBy: 'nombre ASC');
+    return results
+        .map(
+            (map) => CategoriaProducto.fromJson(Map<String, dynamic>.from(map)))
+        .toList();
+  }
+
+  Future<void> addCategoriaProducto(CategoriaProducto categoriaProducto) async {
+    await DatabaseService().guardar(categoriaProducto);
+    fetchLocalAll();
+  }
+
+  Future<void> fetchLocalByName({required String nombre}) async {
+    var db = await DatabaseService().db;
+    var query = await db!.query(
+      'CategoriaProducto',
+      where: 'nombre LIKE "%$nombre%"',
+      orderBy: 'idLocal asc',
+    );
+    List<CategoriaProducto> aux = [];
+    for (var element in query) {
+      CategoriaProducto categoriaProducto = CategoriaProducto.fromJson(element);
+      aux.add(categoriaProducto);
+    }
+    _categoriaProductos = aux;
+    notifyListeners();
+  }
+
+  Future<void> updateCategoriaProducto(
+      CategoriaProducto categoriaProducto) async {
+    setIsLoading(true);
+    try {
+      int result = await DatabaseService().guardar(categoriaProducto);
+      print("Update result: $result");
+      fetchLocalAll();
+    } catch (e) {
+      print('Error updating product: $e');
+    }
+    setIsLoading(false);
+  }
+
+  Future<void> deleteCategoriaProducto(int id) async {
+    await DatabaseService().eliminar<CategoriaProducto>(id);
+    fetchLocalAll();
+  }
+
+  void setIsLoading(bool loading) {
+    _isLoading = loading;
+    notifyListeners();
+  }
+
+  void nextPage() {
+    if (_currentPage < totalPages) {
+      fetchLocalAll(page: _currentPage + 1);
+    }
+  }
+
+  void previousPage() {
+    if (_currentPage > 1) {
+      fetchLocalAll(page: _currentPage - 1);
+    }
+  }
+}

+ 177 - 0
lib/mvvm/viewmodels/mesa_view_model.dart

@@ -0,0 +1,177 @@
+import 'package:flutter/material.dart';
+import 'package:sqflite/sqflite.dart';
+import '../../core/services/services.dart';
+import '../../core/models/models.dart';
+
+class MesaViewModel extends ChangeNotifier {
+  String _busqueda = "";
+  String get busqueda => _busqueda;
+
+  List<Mesa> _mesas = [];
+  bool _isLoading = false;
+  Mesa? _selectedMesa;
+
+  List<Mesa> get mesas => _mesas;
+  bool get isLoading => _isLoading;
+  Mesa? get selectedMesa => _selectedMesa;
+
+  int _currentPage = 1;
+  int _totalMesas = 0;
+  int _limit = 10;
+
+  int get currentPage => _currentPage;
+  int get totalMesas => _totalMesas;
+  int get totalPages => (_totalMesas / _limit).ceil();
+
+  setBusqueda(String value) {
+    _busqueda = value;
+    notifyListeners();
+  }
+
+  void selectMesa(Mesa mesa) {
+    _selectedMesa = mesa;
+    notifyListeners();
+  }
+
+  Future<void> fetchLocalAll({
+    int page = 1,
+    bool sinLimite = false,
+    String orderBy = 'id ASC',
+  }) async {
+    _currentPage = page;
+    var db = await DatabaseService().db;
+
+    if (!sinLimite) {
+      int? count = Sqflite.firstIntValue(
+          await db!.rawQuery('SELECT COUNT(*) FROM Mesa'));
+      _totalMesas = count ?? 0;
+    }
+
+    String? limitOffsetClause;
+    if (!sinLimite) {
+      int offset = (_limit * (page - 1));
+      limitOffsetClause = 'LIMIT $_limit OFFSET $offset';
+    } else {
+      limitOffsetClause = '';
+    }
+
+    var query = await db!
+        .rawQuery('SELECT * FROM Mesa ORDER BY $orderBy $limitOffsetClause');
+
+    _mesas = query.map((element) => Mesa.fromJson(element)).toList();
+
+    notifyListeners();
+  }
+
+  Future<void> fetchLocalByName({required String nombre}) async {
+    var db = await DatabaseService().db;
+    var query = await db!.query(
+      'Mesa',
+      where: 'nombre LIKE "%$nombre%"',
+      orderBy: 'id asc',
+    );
+    List<Mesa> aux = [];
+    for (var element in query) {
+      Mesa mesa = Mesa.fromJson(element);
+      aux.add(mesa);
+    }
+    _mesas = aux;
+    notifyListeners();
+  }
+
+  Mesa fetchLocalById({required int? idMesa}) {
+    final mesa = mesas.firstWhere((mesa) => mesa.id == idMesa,
+        orElse: () => Mesa(id: 0, nombre: 'Mesa desconocida'));
+    return mesa;
+  }
+
+  Future<void> addMesa(Mesa mesa) async {
+    mesa.creado = DateTime.now().toUtc();
+    await DatabaseService().guardar(mesa);
+    await fetchLocalAll();
+  }
+
+  Future<void> updateMesa(Mesa mesa) async {
+    setIsLoading(true);
+    try {
+      mesa.modificado = DateTime.now().toUtc();
+      await DatabaseService().guardar(mesa);
+      await fetchLocalAll();
+    } catch (e) {
+      debugPrint('Error updating mesa: $e');
+    }
+    setIsLoading(false);
+  }
+
+  Future<void> deleteMesa(int id) async {
+    await DatabaseService().eliminar<Mesa>(id);
+    fetchLocalAll();
+  }
+
+  void setIsLoading(bool loading) {
+    _isLoading = loading;
+    notifyListeners();
+  }
+
+  void nextPage() {
+    if (_currentPage < totalPages) {
+      fetchLocalAll(page: _currentPage + 1);
+    }
+  }
+
+  void previousPage() {
+    if (_currentPage > 1) {
+      fetchLocalAll(page: _currentPage - 1);
+    }
+  }
+
+  Future<bool> isMesaActive(String clave) async {
+    var db = await DatabaseService().db;
+    var result = await db!.query(
+      'Mesa',
+      where: 'clave = ?',
+      whereArgs: [clave],
+    );
+
+    if (result.isNotEmpty) {
+      var mesa = Mesa.fromJson(result.first);
+      return mesa.activa == true;
+    }
+
+    return false;
+  }
+
+  Future<bool> sincronizarMesas() async {
+    String? claveSucursal =
+        await DatabaseService().obtenerClaveSucursalSeleccionada();
+
+    try {
+      Map<String, String> parametros = {
+        "claveSucursal": claveSucursal!,
+        "limite": "-1"
+      };
+
+      final response = ApiResponse(await BaseService()
+          .get('/pos/mesa', queryParameters: parametros, withAuth: true));
+
+      if (response.isOk && response.resultados != null) {
+        List<Mesa> mesasApi =
+            response.resultados!.map((json) => Mesa.fromApi(json)).toList();
+
+        if (mesasApi.isNotEmpty) {
+          debugPrint("Mesas API obtenidas: ${mesasApi.length}");
+          await DatabaseService().sincronizarMesas(mesasApi);
+          return true;
+        } else {
+          debugPrint("No se encontraron mesas en la API.");
+        }
+      } else {
+        debugPrint("Error en la respuesta de la API o resultados nulos MESAS.");
+      }
+      return false;
+    } catch (e) {
+      debugPrint('Error al sincronizar mesas: $e');
+      return false;
+    }
+  }
+}

+ 437 - 0
lib/mvvm/viewmodels/producto_view_model.dart

@@ -0,0 +1,437 @@
+import 'package:flutter/material.dart';
+import 'package:sqflite/sqflite.dart';
+
+import '../../core/services/services.dart';
+import '../../core/models/models.dart';
+
+class ProductoViewModel<T> extends ChangeNotifier {
+  String _busqueda = "";
+  String get busqueda => _busqueda;
+
+  List<Producto> _productos = [];
+  List<CategoriaProducto> _toppingCategories = [];
+  bool _isLoading = false;
+  int? _selectedCategoriaId = 0;
+  Producto? _selectedProducto;
+
+  List<Producto> get productos => _productos;
+  Producto? get selectedProducto => _selectedProducto;
+  bool get isLoading => _isLoading;
+
+  int _currentPage = 1;
+  int _totalProducts = 0;
+  int _limit = 10;
+
+  int get currentPage => _currentPage;
+  int get totalProducts => _totalProducts;
+  int get totalPages => (_totalProducts / _limit).ceil();
+
+  setBusqueda(String value) {
+    _busqueda = value;
+    notifyListeners();
+  }
+
+  void selectProducto(Producto producto) {
+    _selectedProducto = producto;
+    notifyListeners();
+  }
+
+  Future<void> fetchLocalAll({int page = 1}) async {
+    _currentPage = page;
+    var db = await DatabaseService().db;
+    int? count = Sqflite.firstIntValue(
+        await db!.rawQuery('SELECT COUNT(*) FROM Producto'));
+    _totalProducts = count ?? 0;
+
+    int offset = (_limit * (page - 1));
+
+    var query = await db.query('Producto',
+        where: 'eliminado IS NULL',
+        orderBy: 'id asc',
+        limit: _limit,
+        offset: offset);
+    _productos = query.map((element) => Producto.fromJson(element)).toList();
+
+    notifyListeners();
+  }
+
+  Future<void> fetchLocalByID({required int idCategoria}) async {
+    var db = await DatabaseService().db;
+    var query = await db!.query('Producto',
+        where: 'idCategoria = ?',
+        whereArgs: [idCategoria],
+        orderBy: 'idLocal asc');
+    List<Producto> aux = [];
+    for (var element in query) {
+      Producto producto = Producto.fromJson(element);
+      aux.add(producto);
+    }
+    _productos = aux;
+    notifyListeners();
+  }
+
+  Future<void> fetchAllByCategory(int idCategoria) async {
+    var db = await DatabaseService().db;
+    var query = await db!.query('Producto',
+        where: 'idCategoria = ? and eliminado IS NULL',
+        whereArgs: [idCategoria],
+        orderBy: 'idLocal asc');
+    _productos = query.map((e) => Producto.fromJson(e)).toList();
+    notifyListeners();
+  }
+
+  Future<void> fetchLocalByName({required String nombre}) async {
+    var db = await DatabaseService().db;
+    var query = await db!.query(
+      'Producto',
+      where: 'nombre LIKE ?',
+      whereArgs: ['%$nombre%'],
+      orderBy: 'idLocal asc',
+    );
+    _productos = query.map((e) => Producto.fromJson(e)).toList();
+    notifyListeners();
+  }
+
+  Future<int> addProducto(Producto producto) async {
+    setIsLoading(true);
+    try {
+      int id = await DatabaseService().guardar(producto);
+      fetchLocalAll();
+      return id;
+    } catch (e) {
+      print('Error al agregar producto: $e');
+      return -1;
+    } finally {
+      setIsLoading(false);
+    }
+  }
+
+  Future<void> updateProducto(Producto producto) async {
+    setIsLoading(true);
+    try {
+      int result = await DatabaseService().guardar(producto);
+      print("Resultado: $result");
+      fetchLocalAll();
+    } catch (e) {
+      print('Error al actualizar: $e');
+    }
+    setIsLoading(false);
+  }
+
+  Future<void> deleteProducto(int id) async {
+    await DatabaseService().eliminar<Producto>(id);
+    fetchLocalAll();
+  }
+
+  Future<bool> updateProductImagePath(int productId, String imagePath) async {
+    setIsLoading(true);
+    try {
+      Producto? producto =
+          await DatabaseService().obtenerProductoPorId(productId);
+      producto!.imagen = imagePath;
+      await DatabaseService().guardar(producto);
+      notifyListeners();
+      return true;
+    } catch (e) {
+      print('Error al actualizar la imagen del producto: $e');
+      return false;
+    } finally {
+      setIsLoading(false);
+    }
+  }
+
+  Future<List<CategoriaProducto>> fetchToppingCategories() async {
+    var db = await DatabaseService().db;
+    var query = await db!.query('CategoriaProducto',
+        where: 'esToping = ?', whereArgs: [1], orderBy: 'id asc');
+    _toppingCategories =
+        query.map((element) => CategoriaProducto.fromJson(element)).toList();
+    return _toppingCategories;
+  }
+
+  Future<List<Producto>> fetchProductsByCategory(int categoryId) async {
+    var db = await DatabaseService().db;
+    var query = await db!.query('Producto',
+        where: 'idCategoria = ?', whereArgs: [categoryId], orderBy: 'id asc');
+    return query.map((e) => Producto.fromJson(e)).toList();
+  }
+
+  Future<List<int>> obtenerToppingsPorProducto(int idProducto) async {
+    var dbClient = await DatabaseService().db;
+    var result = await dbClient!.query(
+      'ProductoTopping',
+      where: 'idProducto = ?',
+      whereArgs: [idProducto],
+    );
+    return result.map((map) => map['idTopping'] as int).toList();
+  }
+
+  Future<Producto?> obtenerProductoPorId(int idProducto) async {
+    Database? dbClient = await DatabaseService().db;
+    List<Map> maps = await dbClient!
+        .query('Producto', where: 'id = ?', whereArgs: [idProducto]);
+    if (maps.isNotEmpty) {
+      return Producto.fromJson(Map<String, dynamic>.from(maps.first));
+    }
+    return null;
+  }
+
+  Future<bool> sincronizarCategorias() async {
+    String? claveSucursal =
+        await DatabaseService().obtenerClaveSucursalSeleccionada();
+
+    try {
+      Map<String, String> parametros = {
+        "claveSucursal": claveSucursal!,
+        "limite": "-1"
+      };
+      final response = ApiResponse(await BaseService()
+          .get('/pos/categoria', queryParameters: parametros));
+
+      //print(response.resultados);
+
+      if (response.isOk && response.resultados != null) {
+        List<CategoriaProducto> categoriasApi = response.resultados!
+            .map((json) => CategoriaProducto.fromApi(json))
+            .toList();
+
+        if (categoriasApi.isNotEmpty) {
+          await DatabaseService().sincronizarCategorias(categoriasApi);
+          notifyListeners();
+          return true;
+        }
+      }
+      return false;
+    } catch (e) {
+      print('Error al sincronizar categorías: $e');
+      return false;
+    }
+  }
+
+  Future<bool> sincronizarProductos() async {
+    String? claveSucursal =
+        await DatabaseService().obtenerClaveSucursalSeleccionada();
+
+    try {
+      Map<String, String> parametros = {
+        "limite": "-1",
+        "claveSucursal": claveSucursal!,
+        "expand": "media"
+      };
+      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) {
+          print("Productos API obtenidos: ${productosApi.length}");
+
+          // // Aquí mantengo tu lógica de descarga de imágenes.
+          // for (var productoApi in productosApi) {
+          //   if (productoApi.media != null && productoApi.media!.isNotEmpty) {
+          //     for (var media in productoApi.media!) {
+          //       // Descargar y guardar la imagen localmente
+          //       String? localImagePath = await downloadAndStoreImage(
+          //           media.ruta!, productoApi.id!, media.nombre!);
+
+          //       if (localImagePath != null) {
+          //         productoApi.imagen = localImagePath;
+          //       }
+          //     }
+          //   }
+          // }
+
+          // Delegamos la sincronización de los productos al DatabaseService
+          await DatabaseService().sincronizarProductos(productosApi);
+          notifyListeners();
+          return true;
+        } else {
+          print("No se encontraron productos en la API.");
+        }
+      } else {
+        print("Error en la respuesta de la API o resultados nulos.");
+      }
+      return false;
+    } catch (e) {
+      print('Error al sincronizar productos: $e');
+      return false;
+    }
+  }
+
+  Future<bool> sincronizarProductoTopping() async {
+    String? claveSucursal =
+        await DatabaseService().obtenerClaveSucursalSeleccionada();
+
+    try {
+      Map<String, String> parametros = {
+        "limite": "-1",
+        "claveSucursal": claveSucursal!,
+      };
+
+      final response = ApiResponse(await BaseService()
+          .get('/pos/producto-topping', queryParameters: parametros));
+
+      if (response.isOk && response.resultados != null) {
+        List<ProductoTopping> toppingApi = response.resultados!
+            .map((json) => ProductoTopping.fromApi(json))
+            .toList();
+
+        if (toppingApi.isNotEmpty) {
+          print("Producto Toppings API obtenidos: ${toppingApi.length}");
+
+          await DatabaseService().sincronizarProductoTopping(toppingApi);
+          notifyListeners();
+          return true;
+        } else {
+          print("No se encontraron toppings de producto en la API.");
+        }
+      } else {
+        print("Error en la respuesta de la API o resultados nulos.");
+      }
+      return false;
+    } catch (e) {
+      print('Error al sincronizar producto toppings: $e');
+      return false;
+    }
+  }
+
+  Future<void> sincronizarProductosYCategorias() async {
+    setIsLoading(true);
+    try {
+      bool categoriasSincronizadas = await sincronizarCategorias();
+
+      if (categoriasSincronizadas) {
+        bool productosSincronizados = await sincronizarProductos();
+        if (productosSincronizados) {
+          bool toppingsSincronizados = await sincronizarProductoTopping();
+          if (toppingsSincronizados) {
+            await fetchLocalAll();
+          }
+        }
+      }
+      notifyListeners();
+    } catch (e, stackTrace) {
+      throw Exception(
+          "Error al sincronizar productos, categorías y toppings: $e\n$stackTrace");
+    } finally {
+      setIsLoading(false);
+    }
+  }
+
+  Future<bool> sincronizarProductosLocales() async {
+    List<Producto> productosNoSincronizados = await DatabaseService()
+        .obtenerProductosNoSincronizadosOrdenadosPorFecha();
+
+    if (productosNoSincronizados.isNotEmpty) {
+      Producto productoNoSincronizado = productosNoSincronizados.first;
+
+      Map<String, dynamic> productoJson =
+          await prepararProductoParaApi(productoNoSincronizado);
+
+      print('JSON Producto enviado: $productoJson');
+
+      // Llamada a la API
+      var response = ApiResponse(await BaseService()
+          .post('/pos/producto/sincronizar', body: productoJson));
+
+      if (response.isOk && response.detalle != null) {
+        int idWeb = response.detalle!['idWeb'];
+        String sincronizado = response.detalle!['sincronizado'];
+        await DatabaseService().actualizarProductoSincronizado(
+            productoNoSincronizado.id!, idWeb, sincronizado);
+        return true;
+      } else {
+        print('Error en la sincronización del producto: ${response.mensaje}');
+        return true;
+      }
+    } else {
+      print('No se encontraron productos no sincronizados.');
+      return false;
+    }
+  }
+
+  Future<Map<String, dynamic>> prepararProductoParaApi(
+      Producto producto) async {
+    String? claveSucursal =
+        await DatabaseService().obtenerClaveSucursalSeleccionada();
+    Map<String, dynamic> apiMap = producto.toJson();
+
+    apiMap['claveSucursal'] = claveSucursal;
+
+    if (producto.idWeb != null && producto.idWeb! > 0) {
+      apiMap['idWeb'] = producto.idWeb;
+    }
+
+    return apiMap;
+  }
+
+  Future<Map<String, dynamic>> prepararCategoriaParaApi(
+      CategoriaProducto categoria) async {
+    String? claveSucursal =
+        await DatabaseService().obtenerClaveSucursalSeleccionada();
+    Map<String, dynamic> apiMap = categoria.toJson();
+    // Asegúrate que CategoriaProducto tenga un toJson y si necesitas toApi, créalo similar a Pedido.
+
+    apiMap['claveSucursal'] = claveSucursal;
+
+    if (categoria.idWeb != null && categoria.idWeb! > 0) {
+      apiMap['idWeb'] = categoria.idWeb;
+    }
+
+    return apiMap;
+  }
+
+  Future<bool> sincronizarCategoriasLocales() async {
+    // Obtener categorias no sincronizadas
+    List<CategoriaProducto> categoriasNoSincronizadas = await DatabaseService()
+        .obtenerCategoriasNoSincronizadasOrdenadasPorFecha();
+
+    if (categoriasNoSincronizadas.isNotEmpty) {
+      CategoriaProducto categoriaNoSincronizada =
+          categoriasNoSincronizadas.first;
+
+      Map<String, dynamic> categoriaJson =
+          await prepararCategoriaParaApi(categoriaNoSincronizada);
+
+      print('JSON Categoria enviado: $categoriaJson');
+
+      // Llamada a la API
+      var response = ApiResponse(await BaseService()
+          .post('/pos/categoria/sincronizar', body: categoriaJson));
+
+      if (response.isOk && response.detalle != null) {
+        int idWeb = response.detalle!['idWeb'];
+        String sincronizado = response.detalle!['sincronizado'];
+        await DatabaseService().actualizarCategoriaSincronizada(
+            categoriaNoSincronizada.id!, idWeb, sincronizado);
+        return true;
+      } else {
+        print(
+            'Error en la sincronización de la categoría: ${response.mensaje}');
+        return true;
+      }
+    } else {
+      print('No se encontraron categorias no sincronizadas.');
+      return false;
+    }
+  }
+
+  void setIsLoading(bool loading) {
+    _isLoading = loading;
+    notifyListeners();
+  }
+
+  void nextPage() {
+    if (_currentPage < totalPages) {
+      fetchLocalAll(page: _currentPage + 1);
+    }
+  }
+
+  void previousPage() {
+    if (_currentPage > 1) {
+      fetchLocalAll(page: _currentPage - 1);
+    }
+  }
+}

+ 3 - 0
lib/mvvm/viewmodels/viewmodels.dart

@@ -3,3 +3,6 @@ export '../viewmodels/sucursal_view_model.dart';
 export '../viewmodels/permiso_view_model.dart';
 export '../viewmodels/home_view_model.dart';
 export '../viewmodels/usuarios_view_model.dart';
+export '../viewmodels/mesa_view_model.dart';
+export '../viewmodels/producto_view_model.dart';
+export '../viewmodels/categoria_producto_view_model.dart';

+ 39 - 17
lib/mvvm/views/home/home_screen.dart

@@ -1,7 +1,10 @@
 import 'package:flutter/material.dart';
+import 'package:provider/provider.dart';
 import 'package:turquessa_mesas_hoster/utils/widgets/custom_appbar.dart';
 import 'package:turquessa_mesas_hoster/core/models/mesas_model.dart';
 
+import '../../viewmodels/viewmodels.dart';
+
 class HomeScreen extends StatefulWidget {
   const HomeScreen({super.key});
 
@@ -13,10 +16,20 @@ class Formulario extends State<HomeScreen> {
   @override
   void initState() {
     super.initState();
+    final mesaViewModel = Provider.of<MesaViewModel>(context, listen: false);
+
+    WidgetsBinding.instance.addPostFrameCallback((_) async {
+      Provider.of<ProductoViewModel>(context, listen: false)
+          .sincronizarProductosYCategorias();
+
+      await mesaViewModel.sincronizarMesas();
+      await mesaViewModel.fetchLocalAll(sinLimite: true, orderBy: 'nombre ASC');
+    });
   }
 
   @override
   Widget build(BuildContext context) {
+    final mesaViewModel = Provider.of<MesaViewModel>(context);
     var _selectedIndex;
     return Scaffold(
       backgroundColor: Colors.grey.shade200,
@@ -57,24 +70,33 @@ class Formulario extends State<HomeScreen> {
           ),
           Expanded(
             child: Center(
-                child: GridView.builder(
-                    gridDelegate:
-                        const SliverGridDelegateWithFixedCrossAxisCount(
-                            crossAxisCount: 4,
-                            childAspectRatio: 1.0,
-                            crossAxisSpacing: 10.0,
-                            mainAxisSpacing: 10.0),
-                    padding: const EdgeInsets.all(10),
-                    itemCount: 8,
-                    itemBuilder: (context, index) {
-                      return TableCard(
-                        icon: Icons.table_chart,
-                        color: Colors.blue,
-                        title: 'Mesa ${index + 1}',
-                      );
-                    })),
+              child: GridView.builder(
+                gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
+                  crossAxisCount: 4,
+                  childAspectRatio: 1.0,
+                  crossAxisSpacing: 10.0,
+                  mainAxisSpacing: 10.0,
+                ),
+                padding: const EdgeInsets.all(10),
+                itemCount: mesaViewModel.mesas.length,
+                itemBuilder: (context, index) {
+                  final mesa = mesaViewModel.mesas[index];
+                  return GestureDetector(
+                    onTap: () {
+                      mesaViewModel.selectMesa(mesa);
+                    },
+                    child: TableCard(
+                      icon: Icons.table_chart,
+                      //TODO: Agregar campo de estatus de la mesa para definir los colores
+                      color: (mesa.activa == true) ? Colors.blue : Colors.grey,
+                      title: mesa.nombre ?? 'Mesa sin nombre',
+                    ),
+                  );
+                },
+              ),
+            ),
           ),
-          // if (selectedTable != null)
+          //if (mesaViewModel.selectedMesa != null)
           Expanded(
               flex: 1,
               child: Container(