|
@@ -5,8 +5,8 @@ import 'package:flutter/material.dart';
|
|
|
import 'package:flutter/services.dart';
|
|
|
import 'package:intl/intl.dart';
|
|
|
import 'package:provider/provider.dart';
|
|
|
-import 'package:yoshi_papas_app/data/session/session_storage.dart';
|
|
|
-import 'package:yoshi_papas_app/views/pedido/pedido_ticket.dart';
|
|
|
+import '/data/session/session_storage.dart';
|
|
|
+import '/views/pedido/pedido_ticket.dart';
|
|
|
import '../../themes/themes.dart';
|
|
|
import '../../models/models.dart';
|
|
|
import '../../viewmodels/viewmodels.dart';
|
|
@@ -14,6 +14,10 @@ import 'package:collection/collection.dart';
|
|
|
import '../../widgets/widgets.dart';
|
|
|
|
|
|
class PedidoForm extends StatefulWidget {
|
|
|
+ final Pedido? pedidoExistente;
|
|
|
+
|
|
|
+ const PedidoForm({Key? key, this.pedidoExistente}) : super(key: key);
|
|
|
+
|
|
|
@override
|
|
|
_PedidoFormState createState() => _PedidoFormState();
|
|
|
}
|
|
@@ -50,6 +54,7 @@ class _PedidoFormState extends State<PedidoForm> {
|
|
|
double cambio = 0.0;
|
|
|
double faltante = 0.0;
|
|
|
bool totalCompletado = false;
|
|
|
+ bool _isMesasActive = false;
|
|
|
|
|
|
double calcularTotalPedido() {
|
|
|
double total = 0;
|
|
@@ -85,6 +90,21 @@ class _PedidoFormState extends State<PedidoForm> {
|
|
|
@override
|
|
|
void initState() {
|
|
|
super.initState();
|
|
|
+
|
|
|
+ pedidoActual = widget.pedidoExistente ?? Pedido(id: 0);
|
|
|
+ Future.microtask(() async {
|
|
|
+ bool isMesasActive =
|
|
|
+ await Provider.of<VariableViewModel>(context, listen: false)
|
|
|
+ .isVariableActive('MESAS');
|
|
|
+ setState(() {
|
|
|
+ _isMesasActive = isMesasActive;
|
|
|
+ });
|
|
|
+
|
|
|
+ if (pedidoActual != null && pedidoActual!.id! > 0) {
|
|
|
+ await _cargarPedidoExistente(pedidoActual!.id!);
|
|
|
+ }
|
|
|
+ });
|
|
|
+
|
|
|
cargarCategoriasIniciales().then((_) {
|
|
|
if (categorias.isNotEmpty) {
|
|
|
categoriaSeleccionada = categorias.first;
|
|
@@ -95,6 +115,34 @@ class _PedidoFormState extends State<PedidoForm> {
|
|
|
Provider.of<DescuentoViewModel>(context, listen: false).cargarDescuentos();
|
|
|
}
|
|
|
|
|
|
+ Future<void> _cargarPedidoExistente(int pedidoId) async {
|
|
|
+ Pedido? pedidoCompleto =
|
|
|
+ await Provider.of<PedidoViewModel>(context, listen: false)
|
|
|
+ .fetchPedidoConProductos(pedidoId);
|
|
|
+
|
|
|
+ if (pedidoCompleto != null) {
|
|
|
+ setState(() {
|
|
|
+ pedidoActual = pedidoCompleto;
|
|
|
+ carrito = pedidoCompleto.productos.map((producto) {
|
|
|
+ return ItemCarrito(
|
|
|
+ producto: producto.producto!,
|
|
|
+ cantidad: producto.cantidad!,
|
|
|
+ selectedToppings: producto.toppings.fold<Map<int, Set<int>>>(
|
|
|
+ {},
|
|
|
+ (acc, topping) {
|
|
|
+ acc[topping.idCategoria!] ??= {};
|
|
|
+ acc[topping.idCategoria]!.add(topping.idTopping!);
|
|
|
+ return acc;
|
|
|
+ },
|
|
|
+ ),
|
|
|
+ );
|
|
|
+ }).toList();
|
|
|
+ });
|
|
|
+
|
|
|
+ _recalcularTotal();
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
void _onSearchChanged(String value) async {
|
|
|
if (value.isEmpty) {
|
|
|
cargarProductosPorCategoria(
|
|
@@ -209,6 +257,276 @@ class _PedidoFormState extends State<PedidoForm> {
|
|
|
await _promptForCustomerName();
|
|
|
}
|
|
|
|
|
|
+ void _crearPedido() async {
|
|
|
+ if (carrito.isEmpty) {
|
|
|
+ _mostrarDialogoPedidoVacio();
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ Pedido nuevoPedido = Pedido(
|
|
|
+ peticion: DateTime.now().toUtc().toIso8601String(),
|
|
|
+ estatus: 'NUEVO',
|
|
|
+ totalPedido: totalPedido,
|
|
|
+ descuento: selectedDescuento,
|
|
|
+ idUsuario: await SessionStorage().getId(),
|
|
|
+ productos: carrito.map((item) {
|
|
|
+ return PedidoProducto(
|
|
|
+ idProducto: item.producto.id,
|
|
|
+ producto: item.producto,
|
|
|
+ costoUnitario: item.producto.precio.toString(),
|
|
|
+ cantidad: item.cantidad,
|
|
|
+ toppings: item.selectedToppings.entries.expand((entry) {
|
|
|
+ return entry.value.map((toppingId) {
|
|
|
+ return PedidoProductoTopping(
|
|
|
+ idCategoria: entry.key,
|
|
|
+ idTopping: toppingId,
|
|
|
+ );
|
|
|
+ });
|
|
|
+ }).toList(),
|
|
|
+ );
|
|
|
+ }).toList(),
|
|
|
+ );
|
|
|
+
|
|
|
+ bool result = await Provider.of<PedidoViewModel>(context, listen: false)
|
|
|
+ .guardarPedidoLocal(pedido: nuevoPedido);
|
|
|
+
|
|
|
+ if (result) {
|
|
|
+ Navigator.of(context).pop();
|
|
|
+ } else {
|
|
|
+ print('Error al crear el pedido');
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ void _mostrarDialogoPedidoVacio() {
|
|
|
+ showDialog(
|
|
|
+ context: context,
|
|
|
+ builder: (BuildContext context) {
|
|
|
+ return AlertDialog(
|
|
|
+ title: const Text('Pedido vacío',
|
|
|
+ style: TextStyle(fontWeight: FontWeight.w500, fontSize: 22)),
|
|
|
+ content: const Text(
|
|
|
+ 'No puedes crear un pedido sin productos. Por favor, agrega al menos un producto.',
|
|
|
+ style: TextStyle(fontSize: 18)),
|
|
|
+ shape:
|
|
|
+ RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)),
|
|
|
+ actions: <Widget>[
|
|
|
+ TextButton(
|
|
|
+ style: TextButton.styleFrom(
|
|
|
+ padding: const EdgeInsets.fromLTRB(30, 20, 30, 20),
|
|
|
+ foregroundColor: AppTheme.quaternary,
|
|
|
+ backgroundColor: AppTheme.tertiary),
|
|
|
+ onPressed: () {
|
|
|
+ Navigator.of(context).pop();
|
|
|
+ },
|
|
|
+ child: const Text('Aceptar', style: TextStyle(fontSize: 18)),
|
|
|
+ ),
|
|
|
+ ],
|
|
|
+ );
|
|
|
+ },
|
|
|
+ );
|
|
|
+ }
|
|
|
+
|
|
|
+ Future<void> _crearPedidoConModal() async {
|
|
|
+ TextEditingController nombreController = TextEditingController();
|
|
|
+ TextEditingController descripcionController = TextEditingController();
|
|
|
+
|
|
|
+ int? idMesaSeleccionada;
|
|
|
+
|
|
|
+ await Provider.of<MesaViewModel>(context, listen: false).fetchLocalAll();
|
|
|
+ List<Mesa> mesasDisponibles =
|
|
|
+ Provider.of<MesaViewModel>(context, listen: false).mesas;
|
|
|
+
|
|
|
+ bool? shouldSave = await showDialog<bool>(
|
|
|
+ context: context,
|
|
|
+ builder: (BuildContext context) {
|
|
|
+ return AlertDialog(
|
|
|
+ title: const Text(
|
|
|
+ 'Crear Pedido',
|
|
|
+ style: TextStyle(fontSize: 22, fontWeight: FontWeight.w500),
|
|
|
+ ),
|
|
|
+ content: Column(
|
|
|
+ mainAxisSize: MainAxisSize.min,
|
|
|
+ children: [
|
|
|
+ AppTextField(
|
|
|
+ controller: nombreController,
|
|
|
+ etiqueta: 'Nombre del Cliente',
|
|
|
+ hintText: 'Nombre del Cliente',
|
|
|
+ ),
|
|
|
+ const SizedBox(height: 10),
|
|
|
+ AppTextField(
|
|
|
+ controller: descripcionController,
|
|
|
+ etiqueta: 'Descripción',
|
|
|
+ hintText: 'Descripción del Pedido',
|
|
|
+ ),
|
|
|
+ const SizedBox(height: 10),
|
|
|
+ DropdownButtonFormField<int>(
|
|
|
+ hint: const Text('Seleccionar Mesa'),
|
|
|
+ items: mesasDisponibles
|
|
|
+ .map((mesa) => DropdownMenuItem<int>(
|
|
|
+ value: mesa.id,
|
|
|
+ child: Text(mesa.nombre ?? 'Sin Nombre'),
|
|
|
+ ))
|
|
|
+ .toList(),
|
|
|
+ onChanged: (value) {
|
|
|
+ idMesaSeleccionada = value;
|
|
|
+ },
|
|
|
+ decoration: const InputDecoration(
|
|
|
+ labelText: 'Mesa',
|
|
|
+ border: OutlineInputBorder(),
|
|
|
+ ),
|
|
|
+ ),
|
|
|
+ ],
|
|
|
+ ),
|
|
|
+ actions: [
|
|
|
+ TextButton(
|
|
|
+ onPressed: () => Navigator.of(context).pop(false),
|
|
|
+ child: const Text('Cancelar'),
|
|
|
+ ),
|
|
|
+ TextButton(
|
|
|
+ onPressed: () {
|
|
|
+ if (idMesaSeleccionada != null) {
|
|
|
+ Navigator.of(context).pop(true);
|
|
|
+ } else {
|
|
|
+ ScaffoldMessenger.of(context).showSnackBar(
|
|
|
+ const SnackBar(
|
|
|
+ content: Text('Seleccione una mesa por favor'),
|
|
|
+ ),
|
|
|
+ );
|
|
|
+ }
|
|
|
+ },
|
|
|
+ child: const Text('Guardar'),
|
|
|
+ ),
|
|
|
+ ],
|
|
|
+ );
|
|
|
+ },
|
|
|
+ );
|
|
|
+
|
|
|
+ if (shouldSave ?? false) {
|
|
|
+ Pedido nuevoPedido = Pedido(
|
|
|
+ peticion: DateTime.now().toUtc().toIso8601String(),
|
|
|
+ nombreCliente: nombreController.text,
|
|
|
+ comentarios: descripcionController.text,
|
|
|
+ estatus: 'NUEVO',
|
|
|
+ idMesa: idMesaSeleccionada,
|
|
|
+ totalPedido: totalPedido,
|
|
|
+ descuento: selectedDescuento,
|
|
|
+ idUsuario: await SessionStorage().getId(),
|
|
|
+ productos: carrito.map((item) {
|
|
|
+ return PedidoProducto(
|
|
|
+ idProducto: item.producto.id,
|
|
|
+ producto: item.producto,
|
|
|
+ costoUnitario: item.producto.precio.toString(),
|
|
|
+ cantidad: item.cantidad,
|
|
|
+ toppings: item.selectedToppings.entries.expand((entry) {
|
|
|
+ return entry.value.map((toppingId) {
|
|
|
+ return PedidoProductoTopping(
|
|
|
+ idCategoria: entry.key,
|
|
|
+ idTopping: toppingId,
|
|
|
+ );
|
|
|
+ });
|
|
|
+ }).toList(),
|
|
|
+ );
|
|
|
+ }).toList(),
|
|
|
+ );
|
|
|
+
|
|
|
+ bool result = await Provider.of<PedidoViewModel>(context, listen: false)
|
|
|
+ .guardarPedidoLocal(pedido: nuevoPedido);
|
|
|
+
|
|
|
+ if (result) {
|
|
|
+ Navigator.of(context).pop();
|
|
|
+ } else {
|
|
|
+ print('Error al guardar el pedido');
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ Future<void> _guardarPedidoExistente() async {
|
|
|
+ if (pedidoActual == null) return;
|
|
|
+
|
|
|
+ pedidoActual!.totalPedido = totalPedido;
|
|
|
+ pedidoActual!.descuento = selectedDescuento;
|
|
|
+ pedidoActual!.sincronizado = null;
|
|
|
+
|
|
|
+ final Map<int, PedidoProducto> productosExistentes = {
|
|
|
+ for (var producto in pedidoActual!.productos)
|
|
|
+ producto.idProducto!: producto
|
|
|
+ };
|
|
|
+
|
|
|
+ for (var item in carrito) {
|
|
|
+ PedidoProducto? productoExistente = productosExistentes[item.producto.id];
|
|
|
+
|
|
|
+ if (productoExistente != null) {
|
|
|
+ print(
|
|
|
+ "Actualizando producto existente: ID ${productoExistente.idProducto}, cantidad previa: ${productoExistente.cantidad}, cantidad nueva: ${item.cantidad}");
|
|
|
+
|
|
|
+ productoExistente.cantidad = item.cantidad;
|
|
|
+ productoExistente.descuento =
|
|
|
+ item.producto.descuento?.toString() ?? '0';
|
|
|
+ productoExistente.costoUnitario = item.producto.precio.toString();
|
|
|
+ productoExistente.sincronizado = null;
|
|
|
+
|
|
|
+ // Actualizar los toppings, si aplica
|
|
|
+ for (var entry in item.selectedToppings.entries) {
|
|
|
+ int categoriaId = entry.key;
|
|
|
+ for (int toppingId in entry.value) {
|
|
|
+ PedidoProductoTopping? toppingExistente =
|
|
|
+ productoExistente.toppings.firstWhereOrNull(
|
|
|
+ (t) => t.idTopping == toppingId && t.idCategoria == categoriaId,
|
|
|
+ );
|
|
|
+
|
|
|
+ if (toppingExistente == null) {
|
|
|
+ print(
|
|
|
+ "Añadiendo topping nuevo: Categoria ${categoriaId}, Topping ${toppingId}");
|
|
|
+ productoExistente.toppings.add(
|
|
|
+ PedidoProductoTopping(
|
|
|
+ idCategoria: categoriaId,
|
|
|
+ idTopping: toppingId,
|
|
|
+ ),
|
|
|
+ );
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ print(
|
|
|
+ "Añadiendo nuevo producto: ID ${item.producto.id}, cantidad: ${item.cantidad}");
|
|
|
+ PedidoProducto nuevoProducto = PedidoProducto(
|
|
|
+ idProducto: item.producto.id,
|
|
|
+ producto: item.producto,
|
|
|
+ costoUnitario: item.producto.precio.toString(),
|
|
|
+ descuento: item.producto.descuento?.toString() ?? '0',
|
|
|
+ cantidad: item.cantidad,
|
|
|
+ sincronizado: null,
|
|
|
+ toppings: item.selectedToppings.entries.expand((entry) {
|
|
|
+ return entry.value.map((toppingId) {
|
|
|
+ return PedidoProductoTopping(
|
|
|
+ idCategoria: entry.key,
|
|
|
+ idTopping: toppingId,
|
|
|
+ );
|
|
|
+ });
|
|
|
+ }).toList(),
|
|
|
+ );
|
|
|
+
|
|
|
+ pedidoActual!.productos.add(nuevoProducto);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ print("Productos finales antes de guardar:");
|
|
|
+ pedidoActual!.productos.forEach((producto) {
|
|
|
+ print(
|
|
|
+ "Producto ID: ${producto.idProducto}, Cantidad: ${producto.cantidad}, Costo: ${producto.costoUnitario}");
|
|
|
+ });
|
|
|
+
|
|
|
+ bool result = await Provider.of<PedidoViewModel>(context, listen: false)
|
|
|
+ .guardarPedidoLocal(pedido: pedidoActual!);
|
|
|
+
|
|
|
+ if (result) {
|
|
|
+ print("Pedido actualizado correctamente: ID ${pedidoActual!.id}");
|
|
|
+ Navigator.of(context).pop();
|
|
|
+ } else {
|
|
|
+ print('Error al actualizar el pedido');
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
Future<void> _promptForCustomerName() async {
|
|
|
TextEditingController nombreController = TextEditingController();
|
|
|
TextEditingController comentarioController = TextEditingController();
|
|
@@ -1047,15 +1365,48 @@ class _PedidoFormState extends State<PedidoForm> {
|
|
|
const SizedBox(height: 25),
|
|
|
Padding(
|
|
|
padding: const EdgeInsets.all(8.0),
|
|
|
- child: ElevatedButton(
|
|
|
- onPressed: _finalizeOrder,
|
|
|
- style: ElevatedButton.styleFrom(
|
|
|
- backgroundColor: AppTheme.tertiary,
|
|
|
- textStyle: const TextStyle(fontSize: 22),
|
|
|
- fixedSize: const Size(250, 50),
|
|
|
- ),
|
|
|
- child: Text('Finalizar Pedido',
|
|
|
- style: TextStyle(color: AppTheme.quaternary)),
|
|
|
+ child: Column(
|
|
|
+ children: [
|
|
|
+ if (pedidoActual != null && pedidoActual!.id! > 0) ...[
|
|
|
+ ElevatedButton(
|
|
|
+ onPressed: () => _guardarPedidoExistente(),
|
|
|
+ style: ElevatedButton.styleFrom(
|
|
|
+ backgroundColor: AppTheme.tertiary,
|
|
|
+ textStyle: const TextStyle(fontSize: 22),
|
|
|
+ fixedSize: const Size(250, 50),
|
|
|
+ ),
|
|
|
+ child: Text(
|
|
|
+ 'Actualizar Pedido',
|
|
|
+ style: TextStyle(color: AppTheme.quaternary),
|
|
|
+ ),
|
|
|
+ ),
|
|
|
+ const SizedBox(height: 10),
|
|
|
+ ElevatedButton(
|
|
|
+ onPressed: () => _promptForCustomerName(),
|
|
|
+ style: ElevatedButton.styleFrom(
|
|
|
+ backgroundColor: Colors.green,
|
|
|
+ textStyle: const TextStyle(fontSize: 22),
|
|
|
+ fixedSize: const Size(250, 50),
|
|
|
+ ),
|
|
|
+ child: Text(
|
|
|
+ 'Finalizar Pedido',
|
|
|
+ style: TextStyle(color: Colors.white),
|
|
|
+ ),
|
|
|
+ ),
|
|
|
+ ] else
|
|
|
+ ElevatedButton(
|
|
|
+ onPressed: () => _crearPedidoConModal(),
|
|
|
+ style: ElevatedButton.styleFrom(
|
|
|
+ backgroundColor: AppTheme.tertiary,
|
|
|
+ textStyle: const TextStyle(fontSize: 22),
|
|
|
+ fixedSize: const Size(250, 50),
|
|
|
+ ),
|
|
|
+ child: Text(
|
|
|
+ 'Crear Pedido',
|
|
|
+ style: TextStyle(color: AppTheme.quaternary),
|
|
|
+ ),
|
|
|
+ ),
|
|
|
+ ],
|
|
|
),
|
|
|
),
|
|
|
],
|