浏览代码

Forzar sincronización y sincronizacion de clave usuario

OscarGil03 6 月之前
父节点
当前提交
e9fde029c1

+ 7 - 0
lib/models/usuario_model.dart

@@ -22,6 +22,7 @@ class Usuario extends Basico {
   int? idEstado;
   int? idSucursal;
   String? turno;
+  String? clave;
   List<String>? permisos;
   static const VER_CATEGORIAS = 'VER_CATEGORIAS';
   static const VER_DESC_APP = 'VER_DESC_APP';
@@ -29,6 +30,7 @@ class Usuario extends Basico {
   static const VER_ADMIN = 'VER_ADMIN';
   static const CANCELAR_PEDIDO = 'CANCELAR_PEDIDO';
   static const VER_REPORTE = 'VER_REPORTE';
+  static const FORZAR_SINCRONIZACION = 'FORZAR_SINCRONIZACION';
 
   Usuario({
     super.id,
@@ -51,6 +53,7 @@ class Usuario extends Basico {
     this.idEstado,
     this.idSucursal,
     this.turno,
+    this.clave,
     this.permisos,
   });
 
@@ -77,6 +80,7 @@ class Usuario extends Basico {
       'idEstado': idEstado,
       'idSucursal': idSucursal,
       'turno': turno,
+      'clave': clave,
       'creado': creado?.toIso8601String(),
       'modificado': modificado?.toIso8601String(),
       'eliminado': eliminado?.toIso8601String(),
@@ -105,6 +109,7 @@ class Usuario extends Basico {
       'idEstado': idEstado,
       'idSucursal': idSucursal,
       'turno': turno,
+      'clave': clave,
       'creado': creado != null ? creado!.toIso8601String() : null,
       'modificado': modificado != null ? modificado!.toIso8601String() : null,
       'eliminado': eliminado != null ? eliminado!.toIso8601String() : null,
@@ -132,6 +137,7 @@ class Usuario extends Basico {
     idEstado = Basico.parseInt(json['idEstado']);
     idSucursal = Basico.parseInt(json['idSucursal']);
     turno = Basico.parseString(json['turno']);
+    clave = Basico.parseString(json['clave']);
   }
 
   Usuario.fromApi(Map<String, dynamic> json) {
@@ -155,6 +161,7 @@ class Usuario extends Basico {
     idEstado = Basico.parseInt(json['idEstado']);
     idSucursal = Basico.parseInt(json['idSucursal']);
     turno = Basico.parseString(json['turno']);
+    clave = Basico.parseString(json['clave']);
     permisos = (json['permisos'] as List).map((e) => e.toString()).toList();
   }
 

+ 179 - 26
lib/services/repo_service.dart

@@ -3,10 +3,12 @@ import 'package:intl/intl.dart';
 import 'package:path/path.dart';
 import 'package:path_provider/path_provider.dart';
 import 'package:sqflite/sqflite.dart';
+import '../data/api_response.dart';
 import '../models/models.dart';
+import 'services.dart';
 
 class RepoService<T> {
-  static int dbVersion = 18;
+  static int dbVersion = 19;
   static String dbName = 'joshipos026.db';
   static const String id = Basico.identificadorWeb;
   static const String idLocal = Basico.identificadorLocal;
@@ -575,6 +577,13 @@ class RepoService<T> {
           ''');
 
           break;
+
+        case 18:
+          await db.execute('''
+            ALTER TABLE Usuario ADD COLUMN clave TEXT;
+          ''');
+
+          break;
       }
       oldVersion++;
     }
@@ -952,8 +961,8 @@ class RepoService<T> {
     return result.map((map) => Pedido.fromJson(map)).toList();
   }
 
-  Future<void> sincronizarCategorias(
-      List<CategoriaProducto> categoriasApi) async {
+  Future<void> sincronizarCategorias(List<CategoriaProducto> categoriasApi,
+      {bool forzar = false}) async {
     var db = await RepoService().db;
 
     var categoriasLocalesQuery = await db!.query('CategoriaProducto');
@@ -967,10 +976,12 @@ class RepoService<T> {
         orElse: () => CategoriaProducto(),
       );
 
-      if (categoriaLocal.id != 0 &&
-          categoriaApi.modificado != null &&
-          (categoriaLocal.modificado == null ||
-              categoriaApi.modificado!.isAfter(categoriaLocal.modificado!))) {
+      if (forzar ||
+          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);
@@ -978,7 +989,8 @@ class RepoService<T> {
     }
   }
 
-  Future<void> sincronizarProductos(List<Producto> productosApi) async {
+  Future<void> sincronizarProductos(List<Producto> productosApi,
+      {bool forzar = false}) async {
     var db = await RepoService().db;
 
     // // Print del JSON recibido
@@ -1006,9 +1018,10 @@ class RepoService<T> {
       if (productoLocal.id == 0) {
         print("Insertando nuevo producto: ${productoApi.nombre}");
         await RepoService().guardar(productoApi);
-      } else if (productoApi.modificado != null &&
-          (productoLocal.modificado == null ||
-              productoApi.modificado!.isAfter(productoLocal.modificado!))) {
+      } else if (forzar ||
+          productoApi.modificado != null &&
+              (productoLocal.modificado == null ||
+                  productoApi.modificado!.isAfter(productoLocal.modificado!))) {
         print("Actualizando producto: ${productoApi.nombre}");
         await RepoService().guardar(productoApi);
       } else {
@@ -1032,9 +1045,12 @@ class RepoService<T> {
     print("Todos los registros de ProductoTopping han sido sincronizados.");
   }
 
-  Future<void> sincronizarSucursales(List<Sucursal> sucursalesApi) async {
+  Future<void> sincronizarSucursales(List<Sucursal> sucursalesApi,
+      {bool forzar = false}) async {
     var db = await RepoService().db;
 
+    int? idSucursalSeleccionada = await obtenerIdSucursalSeleccionada();
+
     var sucursalesLocalesQuery = await db!.query('Sucursal');
     List<Sucursal> sucursalesLocales =
         sucursalesLocalesQuery.map((e) => Sucursal.fromJson(e)).toList();
@@ -1045,10 +1061,13 @@ class RepoService<T> {
         orElse: () => Sucursal(),
       );
 
-      if (sucursalLocal.id != 0 &&
-          sucursalApi.modificado != null &&
-          (sucursalLocal.modificado == null ||
-              sucursalApi.modificado!.isAfter(sucursalLocal.modificado!))) {
+      sucursalApi.seleccionado =
+          (sucursalApi.id == idSucursalSeleccionada) ? 1 : 0;
+      if (forzar ||
+          sucursalLocal.id != 0 &&
+              sucursalApi.modificado != null &&
+              (sucursalLocal.modificado == null ||
+                  sucursalApi.modificado!.isAfter(sucursalLocal.modificado!))) {
         await RepoService().guardar(sucursalApi);
       } else if (sucursalLocal.id == 0) {
         await RepoService().guardar(sucursalApi);
@@ -1056,7 +1075,8 @@ class RepoService<T> {
     }
   }
 
-  Future<void> sincronizarPermisos(List<Permiso> permisosApi) async {
+  Future<void> sincronizarPermisos(List<Permiso> permisosApi,
+      {bool forzar = false}) async {
     var db = await RepoService().db;
     var permisosLocalesQuery = await db!.query('Permiso');
     List<Permiso> permisosLocales =
@@ -1068,10 +1088,11 @@ class RepoService<T> {
         orElse: () => Permiso(),
       );
 
-      if (permisoLocal.id != null &&
-          permisoApi.modificado != null &&
-          (permisoLocal.modificado == null ||
-              permisoApi.modificado!.isAfter(permisoLocal.modificado!))) {
+      if (forzar ||
+          permisoLocal.id != null &&
+              permisoApi.modificado != null &&
+              (permisoLocal.modificado == null ||
+                  permisoApi.modificado!.isAfter(permisoLocal.modificado!))) {
         print('Actualizando permiso con ID: ${permisoApi.id}');
         await RepoService().guardar(permisoApi);
       } else if (permisoLocal.id == null) {
@@ -1083,7 +1104,8 @@ class RepoService<T> {
     }
   }
 
-  Future<void> sincronizarUsuarios(List<Usuario> usuariosApi) async {
+  Future<void> sincronizarUsuarios(List<Usuario> usuariosApi,
+      {bool forzar = false}) async {
     var db = await RepoService().db;
 
     var usuariosLocalesQuery = await db!.query('Usuario');
@@ -1097,10 +1119,11 @@ class RepoService<T> {
       );
 
       // Comprobar si realmente se necesita actualizar el usuario basado en la fecha de modificado
-      if (usuarioLocal.id != 0 &&
-          usuarioApi.modificado != null &&
-          (usuarioLocal.modificado == null ||
-              usuarioApi.modificado!.isAfter(usuarioLocal.modificado!))) {
+      if (forzar ||
+          usuarioLocal.id != 0 &&
+              usuarioApi.modificado != null &&
+              (usuarioLocal.modificado == null ||
+                  usuarioApi.modificado!.isAfter(usuarioLocal.modificado!))) {
         print('Actualizando usuario con ID: ${usuarioApi.id}');
         await RepoService().guardar(usuarioApi);
 
@@ -1184,4 +1207,134 @@ class RepoService<T> {
 
     return null;
   }
+
+  Future<int?> obtenerIdSucursalSeleccionada() async {
+    var db = await this.db;
+    List<Map<String, dynamic>> queryResult = await db!.query(
+      'Sucursal',
+      where: 'seleccionado = ? AND eliminado IS NULL',
+      whereArgs: [1],
+      limit: 1,
+    );
+
+    if (queryResult.isNotEmpty) {
+      return Sucursal.fromJson(queryResult.first).id;
+    }
+
+    return null;
+  }
+
+  Future<void> forzarSincronizacion() async {
+    String? claveSucursal = await obtenerClaveSucursalSeleccionada();
+
+    try {
+      // Sincronizar categorías
+      await sincronizarCategorias(
+          await fetchCategoriasApi(claveSucursal: claveSucursal),
+          forzar: true);
+
+      // Sincronizar productos
+      await sincronizarProductos(
+          await fetchProductosApi(claveSucursal: claveSucursal),
+          forzar: true);
+
+      // Sincronizar toppings de producto
+      await sincronizarProductoTopping(
+          await fetchProductoToppingApi(claveSucursal: claveSucursal));
+
+      // Sincronizar sucursales
+      await sincronizarSucursales(await fetchSucursalesApi(), forzar: true);
+
+      // Sincronizar permisos
+      await sincronizarPermisos(await fetchPermisosApi(), forzar: true);
+
+      // Sincronizar usuarios
+      await sincronizarUsuarios(await fetchUsuariosApi(), forzar: true);
+
+      print('Sincronización forzosa completada.');
+    } catch (e) {
+      print('Error en la sincronización forzosa: $e');
+    }
+  }
+
+  Future<List<CategoriaProducto>> fetchCategoriasApi(
+      {String? claveSucursal}) async {
+    Map<String, String> parametros = {
+      "claveSucursal": claveSucursal!,
+      "limite": "-1"
+    };
+    final response = ApiResponse(
+        await BaseService().get('/pos/categoria', queryParameters: parametros));
+    return response.isOk && response.resultados != null
+        ? response.resultados!
+            .map((json) => CategoriaProducto.fromApi(json))
+            .toList()
+        : [];
+  }
+
+  Future<List<Producto>> fetchProductosApi({String? claveSucursal}) async {
+    Map<String, String> parametros = {
+      "limite": "-1",
+      "claveSucursal": claveSucursal!,
+      "expand": "media"
+    };
+    final response = ApiResponse(
+        await BaseService().get('/pos/producto', queryParameters: parametros));
+    return response.isOk && response.resultados != null
+        ? response.resultados!.map((json) => Producto.fromApi(json)).toList()
+        : [];
+  }
+
+  Future<List<ProductoTopping>> fetchProductoToppingApi(
+      {String? claveSucursal}) async {
+    Map<String, String> parametros = {
+      "limite": "-1",
+      "claveSucursal": claveSucursal!,
+    };
+
+    final response = ApiResponse(await BaseService()
+        .get('/pos/producto-topping', queryParameters: parametros));
+    return response.isOk && response.resultados != null
+        ? response.resultados!
+            .map((json) => ProductoTopping.fromApi(json))
+            .toList()
+        : [];
+  }
+
+  Future<List<Sucursal>> fetchSucursalesApi() async {
+    Map<String, String> parametros = {
+      "limite": "-1",
+    };
+
+    final response = ApiResponse(
+        await BaseService().get('/pos/sucursal', queryParameters: parametros));
+    return response.isOk && response.resultados != null
+        ? response.resultados!.map((json) => Sucursal.fromApi(json)).toList()
+        : [];
+  }
+
+  Future<List<Permiso>> fetchPermisosApi() async {
+    Map<String, String> parametros = {
+      "limite": "-1",
+    };
+
+    final response = ApiResponse(
+        await BaseService().get('/pos/permiso', queryParameters: parametros));
+    return response.isOk && response.resultados != null
+        ? response.resultados!.map((json) => Permiso.fromJson(json)).toList()
+        : [];
+  }
+
+  Future<List<Usuario>> fetchUsuariosApi() async {
+    Map<String, String> parametros = {
+      "limite": "-1",
+      "expand": "permisos",
+    };
+
+    final response = ApiResponse(
+        await BaseService().get('/pos/usuario', queryParameters: parametros));
+    return response.isOk && response.resultados != null
+        ? response.resultados!.map((json) => Usuario.fromApi(json)).toList()
+        : [];
+  }
 }

+ 1 - 1
lib/viewmodels/producto_view_model.dart

@@ -191,7 +191,7 @@ class ProductoViewModel<T> extends ChangeNotifier {
       final response = ApiResponse(await BaseService()
           .get('/pos/categoria', queryParameters: parametros));
 
-      print(response.resultados);
+      //print(response.resultados);
 
       if (response.isOk && response.resultados != null) {
         List<CategoriaProducto> categoriasApi = response.resultados!

+ 1 - 1
lib/views/login/login_screen.dart

@@ -71,7 +71,7 @@ class _LoginScreenState extends State<LoginScreen> {
 
     // Mostrar la pantalla de carga `Cargando` mientras verifica la sesión
     if (loginViewModel.isLoading) {
-      return const Cargando(); // Usa tu widget Cargando aquí
+      return const Cargando();
     }
 
     final size = MediaQuery.sizeOf(context);

+ 117 - 0
lib/widgets/app_drawer.dart

@@ -182,6 +182,123 @@ class AppDrawer extends StatelessWidget {
                             ),
                           },
                         ),
+                      if (userPermisos.contains(Usuario.FORZAR_SINCRONIZACION))
+                        ListTile(
+                          leading: circulo(const Icon(Icons.sync)),
+                          title: const Text('Forzar Sincronización'),
+                          onTap: () async {
+                            bool confirmado = await showDialog(
+                                  context: context,
+                                  builder: (context) {
+                                    return AlertDialog(
+                                      title: const Text("Forzar Sincronización",
+                                          style: TextStyle(
+                                              fontWeight: FontWeight.w500,
+                                              fontSize: 22)),
+                                      content: const Text(
+                                          '¿Estás seguro de que deseas forzar la sincronización?',
+                                          style: TextStyle(fontSize: 18)),
+                                      actions: [
+                                        Row(
+                                          mainAxisAlignment:
+                                              MainAxisAlignment.spaceBetween,
+                                          children: [
+                                            TextButton(
+                                              onPressed: () =>
+                                                  Navigator.of(context)
+                                                      .pop(false),
+                                              child: const Text('No',
+                                                  style:
+                                                      TextStyle(fontSize: 18)),
+                                              style: ButtonStyle(
+                                                padding:
+                                                    MaterialStateProperty.all(
+                                                        EdgeInsets.fromLTRB(
+                                                            20, 10, 20, 10)),
+                                                backgroundColor:
+                                                    MaterialStateProperty.all(
+                                                        Colors.red),
+                                                foregroundColor:
+                                                    MaterialStateProperty.all(
+                                                        AppTheme.secondary),
+                                              ),
+                                            ),
+                                            TextButton(
+                                              onPressed: () =>
+                                                  Navigator.of(context)
+                                                      .pop(true),
+                                              child: const Text('Sí',
+                                                  style:
+                                                      TextStyle(fontSize: 18)),
+                                              style: ButtonStyle(
+                                                padding:
+                                                    MaterialStateProperty.all(
+                                                        EdgeInsets.fromLTRB(
+                                                            20, 10, 20, 10)),
+                                                backgroundColor:
+                                                    MaterialStateProperty.all(
+                                                        AppTheme.tertiary),
+                                                foregroundColor:
+                                                    MaterialStateProperty.all(
+                                                        AppTheme.quaternary),
+                                              ),
+                                            ),
+                                          ],
+                                        ),
+                                      ],
+                                    );
+                                  },
+                                ) ??
+                                false;
+
+                            if (confirmado) {
+                              showDialog(
+                                context: context,
+                                barrierDismissible: false,
+                                builder: (context) => AlertDialog(
+                                  title: const Text("Sincronizando",
+                                      style: TextStyle(
+                                          fontWeight: FontWeight.w500,
+                                          fontSize: 22)),
+                                  content: Padding(
+                                    padding:
+                                        const EdgeInsetsDirectional.symmetric(
+                                            horizontal: 120),
+                                    child: const CircularProgressIndicator(),
+                                  ),
+                                ),
+                              );
+
+                              try {
+                                await RepoService().forzarSincronizacion();
+
+                                Navigator.of(context, rootNavigator: true)
+                                    .pop();
+                                Navigator.of(context).pop();
+
+                                ScaffoldMessenger.of(context).showSnackBar(
+                                  SnackBar(
+                                    content: const Text(
+                                        'Sincronización completada con éxito'),
+                                    backgroundColor: Colors.green,
+                                  ),
+                                );
+                              } catch (e) {
+                                Navigator.of(context, rootNavigator: true)
+                                    .pop();
+                                Navigator.of(context).pop();
+
+                                ScaffoldMessenger.of(context).showSnackBar(
+                                  SnackBar(
+                                    content:
+                                        Text('Error en la sincronización: $e'),
+                                    backgroundColor: Colors.red,
+                                  ),
+                                );
+                              }
+                            }
+                          },
+                        ),
                     ],
                   ),
                 ListTile(