ソースを参照

Ajustes variables, pedidos, productos y categorias

OscarGil03 7 ヶ月 前
コミット
b62a3ab36c

+ 14 - 8
lib/models/basico_model.dart

@@ -18,11 +18,15 @@ class Basico {
   });
 
   static parseDate(origen) {
-    if (origen == "null") return null;
-    if (origen == null) return null;
-    if (origen == '') return null;
-    if (origen == "null") return null;
-    return DateTime.parse(origen);
+    if (origen == null || origen == "null" || origen == '') {
+      return null;
+    }
+    try {
+      return DateTime.parse(origen);
+    } catch (e) {
+      print("Error al parsear fecha: $e");
+      return null;
+    }
   }
 
   static double parseDouble(origen) {
@@ -70,8 +74,10 @@ class Basico {
   parseJson(Map<String, dynamic> json) {
     id = json['id'] != null ? int.parse(json['id'].toString()) : 0;
     idLocal = json['idLocal'] ?? -1;
-    eliminado = parseDate(json['eliminado']);
-    creado = parseDate(json['creado']);
-    modificado = parseDate(json['modificado']);
+    creado = json['creado'] != null ? parseDate(json['creado']) : creado;
+    modificado =
+        json['modificado'] != null ? parseDate(json['modificado']) : modificado;
+    eliminado =
+        json['eliminado'] != null ? parseDate(json['eliminado']) : eliminado;
   }
 }

+ 2 - 2
lib/models/categoria_producto_model.dart

@@ -46,8 +46,8 @@ class CategoriaProducto extends Basico {
     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']);
+    creado = Basico.parseDate(json['creado']);
+    modificado = Basico.parseDate(json['modificado']);
     eliminado = Basico.parseDate(json['eliminado']);
   }
 

+ 2 - 2
lib/models/producto_model.dart

@@ -121,8 +121,8 @@ class Producto extends Basico {
     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']);
+    creado = Basico.parseDate(json['creado']);
+    modificado = Basico.parseDate(json['modificado']);
     eliminado = Basico.parseDate(json['eliminado']);
   }
 

+ 60 - 4
lib/services/repo_service.dart

@@ -6,7 +6,7 @@ import 'package:sqflite/sqflite.dart';
 import '../models/models.dart';
 
 class RepoService<T> {
-  static int dbVersion = 12;
+  static int dbVersion = 13;
   static String dbName = 'joshipos026.db';
   static const String id = Basico.identificadorWeb;
   static const String idLocal = Basico.identificadorLocal;
@@ -116,6 +116,45 @@ class RepoService<T> {
         eliminado TEXT
       )
     ''');
+
+    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;
+        ''');
+
+    await db.insert('Variable', {
+      'nombre': 'Imprimir Ticket Cocina',
+      'clave': 'ticket_cocina',
+      'descripcion':
+          'Variable para imprimir ticket de cocina automaticamente al momento de generar un pedido',
+      'activo': 1,
+      'idLocal': -1,
+    });
+
+    await db.insert('Variable', {
+      'nombre': 'Imprimir Ticket Venta',
+      'clave': 'ticket_venta',
+      'descripcion':
+          'Variable para imprimir ticket de venta automaticamente al momento de generar un pedido',
+      'activo': 1,
+      'idLocal': -1,
+    });
+
+    await db.insert('Variable', {
+      'nombre': 'Sucursal',
+      'clave': 'PRUEBA',
+      'descripcion': 'Sucursal Prueba',
+      'activo': 1,
+      'idLocal': -1,
+    });
   }
 
   void _onUpgrade(Database db, int oldVersion, int newVersion) async {
@@ -420,6 +459,16 @@ class RepoService<T> {
     try {
       print("Guardando modelo en la base de datos: ${model.runtimeType}");
 
+      // Comprobar si es un modelo que obtiene las fechas del servidor (Producto o CategoriaProducto)
+      if (model is Producto || model is CategoriaProducto) {
+        // No hacer nada, las fechas vienen del servidor
+        print("Usando fechas del servidor para ${model.runtimeType}");
+      } else if (model is Basico) {
+        // Generar las fechas localmente para otros modelos
+        asignarFechasLocalmente(model);
+      }
+
+      // Convertir el modelo a JSON para la base de datos
       String modelo = json.encode(model, toEncodable: toEncodable);
       print("Modelo convertido a JSON: $modelo");
 
@@ -457,6 +506,14 @@ class RepoService<T> {
     }
   }
 
+  void asignarFechasLocalmente(Basico model) {
+    DateTime ahora = DateTime.now();
+    if (model.creado == null) {
+      model.creado = ahora.toUtc();
+    }
+    model.modificado = ahora.toUtc();
+  }
+
   Future<void> _guardarToppings(
       Database db, int idProducto, List<Producto>? topings) async {
     await db.delete('ProductoTopping',
@@ -776,8 +833,8 @@ class RepoService<T> {
 
       if (categoriaLocal.id != 0 &&
           categoriaApi.modificado != null &&
-          categoriaLocal.modificado != null &&
-          categoriaApi.modificado!.isAfter(categoriaLocal.modificado!)) {
+          (categoriaLocal.modificado == null ||
+              categoriaApi.modificado!.isAfter(categoriaLocal.modificado!))) {
         await RepoService().guardar(categoriaApi);
       } else if (categoriaLocal.id == 0) {
         await RepoService().guardar(categoriaApi);
@@ -800,7 +857,6 @@ class RepoService<T> {
 
       if (productoLocal.id != 0 &&
           productoApi.modificado != null &&
-          productoLocal.modificado != null &&
           productoApi.modificado!.isAfter(productoLocal.modificado!)) {
         await RepoService().guardar(productoApi);
       } else if (productoLocal.id == 0) {

+ 2 - 0
lib/viewmodels/producto_view_model.dart

@@ -182,6 +182,8 @@ class ProductoViewModel<T> extends ChangeNotifier {
     try {
       final response = ApiResponse(await BaseService().get('/pos/categoria'));
 
+      print(response.resultados);
+
       if (response.isOk && response.resultados != null) {
         List<CategoriaProducto> categoriasApi = response.resultados!
             .map((json) => CategoriaProducto.fromApi(json))

+ 33 - 24
lib/views/categoria_producto/categoria_producto_screen.dart

@@ -54,7 +54,7 @@ class _CategoriaProductoScreenState extends State<CategoriaProductoScreen> {
           title: Text(
               exito ? 'Sincronización exitosa' : 'Error de sincronización'),
           content: SingleChildScrollView(
-            child: Text(mensaje), // Mostrar el mensaje completo de error aquí
+            child: Text(mensaje),
           ),
           actions: [
             TextButton(
@@ -69,6 +69,19 @@ class _CategoriaProductoScreenState extends State<CategoriaProductoScreen> {
     );
   }
 
+  Future<void> _sincronizarProductos(BuildContext context) async {
+    final productoViewModel =
+        Provider.of<ProductoViewModel>(context, listen: false);
+
+    try {
+      await productoViewModel.sincronizarProductosYCategorias();
+      _mostrarResultado(
+          context, 'La sincronización se completó exitosamente.', true);
+    } catch (e) {
+      _mostrarResultado(context, e.toString(), false);
+    }
+  }
+
   @override
   Widget build(BuildContext context) {
     final model = Provider.of<CategoriaProductoViewModel>(context);
@@ -91,7 +104,6 @@ class _CategoriaProductoScreenState extends State<CategoriaProductoScreen> {
               PopupMenuItem(
                 child: const Text('Eliminar'),
                 onTap: () async {
-                  // Retrasa la ejecución para permitir que se cierre el menú popup.
                   await Future.delayed(Duration(milliseconds: 100));
                   bool confirmado = await showDialog<bool>(
                         context: context,
@@ -183,30 +195,27 @@ class _CategoriaProductoScreenState extends State<CategoriaProductoScreen> {
 
     return Scaffold(
       appBar: AppBar(
-        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),
-          ),
+        title: Text(
+          'Categoría Producto',
+          style:
+              TextStyle(color: AppTheme.secondary, fontWeight: FontWeight.w500),
         ),
         iconTheme: IconThemeData(color: AppTheme.secondary),
+        actions: [
+          TextButton.icon(
+            icon: Icon(Icons.sync, color: AppTheme.secondary),
+            label: Text(
+              "Sincronizar",
+              style: TextStyle(
+                  color: AppTheme.secondary,
+                  fontWeight: FontWeight.w500,
+                  fontSize: 18),
+            ),
+            onPressed: () async {
+              await _sincronizarProductos(context);
+            },
+          )
+        ],
       ),
       body: Column(
         children: [

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

@@ -49,12 +49,25 @@ class PedidoDetalleScreen extends StatelessWidget {
               color: Colors.white,
               child: Column(
                 children: [
+                  // Colocamos la fecha y el cliente en una misma fila
                   ListTile(
-                    title: Text(
-                      'Cliente: ${pedido.nombreCliente}',
-                      style:
-                          TextStyle(fontWeight: FontWeight.bold, fontSize: 22),
+                    title: Row(
+                      mainAxisAlignment: MainAxisAlignment.spaceBetween,
+                      children: [
+                        Text(
+                          'Cliente: ${pedido.nombreCliente}',
+                          style: TextStyle(
+                              fontWeight: FontWeight.bold, fontSize: 22),
+                        ),
+                        Text(
+                          'Fecha: ${_formatDateTime(pedido.peticion)}',
+                          style: TextStyle(
+                              fontWeight: FontWeight.bold, fontSize: 22),
+                        ),
+                      ],
                     ),
+                  ),
+                  ListTile(
                     subtitle: Text(
                       'Comentarios: ${pedido.comentarios}',
                       style:
@@ -69,7 +82,7 @@ class PedidoDetalleScreen extends StatelessWidget {
                         fontWeight: FontWeight.bold,
                       ),
                     ),
-                  )
+                  ),
                 ],
               ),
             ),
@@ -312,4 +325,13 @@ class PedidoDetalleScreen extends StatelessWidget {
       ],
     );
   }
+
+  String _formatDateTime(String? dateTimeString) {
+    if (dateTimeString == null) return "Sin fecha";
+
+    DateTime parsedDate = DateTime.parse(dateTimeString);
+
+    var formatter = DateFormat('dd-MM-yyyy HH:mm:ss');
+    return formatter.format(parsedDate.toLocal());
+  }
 }

+ 32 - 35
lib/views/pedido/pedido_form.dart

@@ -1319,44 +1319,41 @@ class _PedidoFormState extends State<PedidoForm> {
     List<CategoriaProducto> categoriasFiltradas =
         categorias.where((categoria) => categoria.esToping == 0).toList();
 
-    return Column(
-      children: [
-        Container(
-          height: 50,
-          child: Stack(
-            children: [
-              ListView.builder(
-                controller: _categoryScrollController,
-                scrollDirection: Axis.horizontal,
-                itemCount: categoriasFiltradas.length,
-                itemBuilder: (context, index) {
-                  final categoria = categoriasFiltradas[index];
-                  bool isSelected = categoriaSeleccionada?.id == categoria.id;
-                  return Padding(
-                    padding: const EdgeInsets.symmetric(horizontal: 4.0),
-                    child: ElevatedButton(
-                      onPressed: () {
-                        cargarProductosPorCategoria(categoria.id);
-                        setState(() {
-                          categoriaSeleccionada = categoria;
-                        });
-                      },
-                      style: ElevatedButton.styleFrom(
-                        backgroundColor:
-                            isSelected ? AppTheme.tertiary : Colors.grey,
-                        foregroundColor: isSelected
-                            ? AppTheme.quaternary
-                            : AppTheme.secondary,
-                      ),
-                      child: Text(categoria.nombre!),
-                    ),
-                  );
+    return Container(
+      height: 50,
+      child: Scrollbar(
+        thumbVisibility: true,
+        trackVisibility: true,
+        interactive: true,
+        controller: _categoryScrollController,
+        child: ListView.builder(
+          controller: _categoryScrollController,
+          scrollDirection: Axis.horizontal,
+          itemCount: categoriasFiltradas.length,
+          itemBuilder: (context, index) {
+            final categoria = categoriasFiltradas[index];
+            bool isSelected = categoriaSeleccionada?.id == categoria.id;
+            return Padding(
+              padding: const EdgeInsets.symmetric(horizontal: 4.0),
+              child: ElevatedButton(
+                onPressed: () {
+                  cargarProductosPorCategoria(categoria.id);
+                  setState(() {
+                    categoriaSeleccionada = categoria;
+                  });
                 },
+                style: ElevatedButton.styleFrom(
+                  primary: isSelected ? AppTheme.tertiary : Colors.grey,
+                  foregroundColor:
+                      isSelected ? AppTheme.quaternary : AppTheme.secondary,
+                  onPrimary: AppTheme.secondary,
+                ),
+                child: Text(categoria.nombre!),
               ),
-            ],
-          ),
+            );
+          },
         ),
-      ],
+      ),
     );
   }
 

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

@@ -18,7 +18,6 @@ class PedidoSync {
 
     _syncTimer = Timer.periodic(Duration(seconds: 5), (timer) async {
       bool hasMoreToSync = await pedidoViewModel.sincronizarPedidos();
-      print('Pedido Sincronizado');
       // if (!hasMoreToSync) {
       //   timer.cancel();
       //   _syncTimer = null;

+ 51 - 0
lib/views/producto/producto_screen.dart

@@ -46,6 +46,42 @@ class _ProductoScreenState extends State<ProductoScreen> {
     });
   }
 
+  Future<void> _sincronizarProductos(BuildContext context) async {
+    final productoViewModel =
+        Provider.of<ProductoViewModel>(context, listen: false);
+
+    try {
+      await productoViewModel.sincronizarProductosYCategorias();
+      _mostrarResultado(
+          context, 'La sincronización se completó exitosamente.', true);
+    } catch (e) {
+      _mostrarResultado(context, e.toString(), false);
+    }
+  }
+
+  void _mostrarResultado(BuildContext context, String mensaje, bool exito) {
+    showDialog(
+      context: context,
+      builder: (BuildContext context) {
+        return AlertDialog(
+          title: Text(
+              exito ? 'Sincronización exitosa' : 'Error de sincronización'),
+          content: SingleChildScrollView(
+            child: Text(mensaje),
+          ),
+          actions: [
+            TextButton(
+              child: Text('OK'),
+              onPressed: () {
+                Navigator.of(context).pop();
+              },
+            ),
+          ],
+        );
+      },
+    );
+  }
+
   @override
   Widget build(BuildContext context) {
     final model = Provider.of<ProductoViewModel>(context);
@@ -179,6 +215,21 @@ class _ProductoScreenState extends State<ProductoScreen> {
               TextStyle(color: AppTheme.secondary, fontWeight: FontWeight.w500),
         ),
         iconTheme: IconThemeData(color: AppTheme.secondary),
+        actions: [
+          TextButton.icon(
+            icon: Icon(Icons.sync, color: AppTheme.secondary),
+            label: Text(
+              "Sincronizar",
+              style: TextStyle(
+                  color: AppTheme.secondary,
+                  fontWeight: FontWeight.w500,
+                  fontSize: 18),
+            ),
+            onPressed: () async {
+              await _sincronizarProductos(context);
+            },
+          )
+        ],
       ),
       body: Column(
         children: [

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

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

+ 1 - 1
lib/widgets/app_drawer.dart

@@ -168,7 +168,7 @@ class AppDrawer extends StatelessWidget {
             child: Align(
               alignment: Alignment.bottomCenter,
               child: Text(
-                'v1.24.09.18',
+                'v1.24.09.23',
                 style: const TextStyle(fontWeight: FontWeight.w300),
               ),
             ),