|
@@ -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);
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|