import 'package:collection/collection.dart'; import 'package:intl/intl.dart'; import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; import '../../models/models.dart'; import '../../services/services.dart'; import '../../themes/themes.dart'; import '../../viewmodels/viewmodels.dart'; import '../../widgets/widgets.dart'; import '../pedido/pedido_ticket.dart'; class PedidoDetalleScreen extends StatefulWidget { final Pedido pedido; PedidoDetalleScreen({Key? key, required this.pedido}) : super(key: key); @override _PedidoDetalleScreenState createState() => _PedidoDetalleScreenState(); } class _PedidoDetalleScreenState extends State { late Pedido pedido; @override void initState() { super.initState(); pedido = widget.pedido; } String formatCurrency(double amount) { final format = NumberFormat("#,##0.00", "es_MX"); return format.format(amount); } final List tiposDePago = ['Efectivo', 'Tarjeta', 'Transferencia']; @override Widget build(BuildContext context) { double totalSinDescuento = pedido.productos.fold(0, (previousValue, element) { double productTotal = element.cantidad! * (element.producto?.precio ?? 0.0); double toppingsTotal = element.toppings.fold(0, (toppingTotal, topping) { return toppingTotal + (topping.topping?.precio ?? 0.0) * element.cantidad!; }); return previousValue + productTotal + toppingsTotal; }); double descuento = pedido.descuento?.toDouble() ?? 0.0; double precioDescuento = totalSinDescuento * (descuento / 100); double totalConDescuento = totalSinDescuento - precioDescuento; return Scaffold( appBar: AppBar( title: Text( 'Detalle del Pedido ${pedido.folio}', style: TextStyle(fontWeight: FontWeight.w500), ), ), body: SingleChildScrollView( padding: const EdgeInsets.all(12.0), child: Column( children: [ Card( elevation: 4, color: Colors.white, child: Column( children: [ // Colocamos la fecha y el cliente en una misma fila ListTile( 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: TextStyle(fontSize: 20, fontWeight: FontWeight.w500), ), ), ListTile( title: Text( 'Estado del Pedido: ${pedido.estatus}', style: TextStyle( fontSize: 22, fontWeight: FontWeight.bold, ), ), ), ], ), ), SizedBox(height: 10), Card( elevation: 4, color: Colors.white, child: Padding( padding: const EdgeInsets.all(8.0), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text('Productos', style: TextStyle( fontSize: 22, fontWeight: FontWeight.bold)), const SizedBox(height: 15), ListView.builder( shrinkWrap: true, physics: NeverScrollableScrollPhysics(), itemCount: pedido.productos.length, itemBuilder: (context, index) { final producto = pedido.productos[index]; return Padding( padding: const EdgeInsets.symmetric(vertical: 4.0), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( children: [ Expanded( flex: 6, child: Text( producto.producto?.nombre ?? "Producto no especificado", style: TextStyle( fontWeight: FontWeight.bold, fontSize: 17), overflow: TextOverflow.ellipsis, ), ), Expanded( flex: 1, child: Text( 'x${producto.cantidad}', style: TextStyle( fontWeight: FontWeight.w500, fontSize: 17), textAlign: TextAlign.center, ), ), Expanded( flex: 2, child: Text( '\$${formatCurrency(producto.producto?.precio ?? 0.0)}', style: TextStyle( fontWeight: FontWeight.w500, fontSize: 17), textAlign: TextAlign.right, ), ), ], ), if (producto.comentario!.isNotEmpty) Text( 'Comentarios: ${producto.comentario!}', style: TextStyle( fontSize: 15, color: AppTheme.tertiary), ), if (producto.toppings.isNotEmpty) Padding( padding: const EdgeInsets.only(top: 4.0), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: producto.toppings.map((topping) { return Padding( padding: const EdgeInsets.symmetric( vertical: 2.0), child: Row( children: [ Text( '- ${topping.topping?.nombre ?? "Topping no especificado"}', style: TextStyle( fontSize: 15, color: Colors.grey[600]), ), Spacer(), Text( '\$${formatCurrency(topping.topping?.precio ?? 0.0)}', style: TextStyle( fontSize: 15, color: Colors.grey[600]), ), ], ), ); }).toList(), ), ), ], ), ); }, ), Divider(), Padding( padding: const EdgeInsets.symmetric(vertical: 8.0), child: Column( crossAxisAlignment: CrossAxisAlignment.end, children: [ Row( mainAxisAlignment: MainAxisAlignment.end, children: [ const Text('Subtotal:', style: TextStyle( fontSize: 16, fontWeight: FontWeight.bold)), const SizedBox(width: 5), Text('\$${formatCurrency(totalSinDescuento)}', style: const TextStyle( fontSize: 16, fontWeight: FontWeight.bold)), ], ), if (descuento > 0) ...[ Row( mainAxisAlignment: MainAxisAlignment.end, children: [ Text( 'Descuento (${descuento.toStringAsFixed(0)}%):', style: const TextStyle( fontSize: 16, fontWeight: FontWeight.bold)), const SizedBox(width: 8), Text('-\$${formatCurrency(precioDescuento)}', style: const TextStyle( fontSize: 16, fontWeight: FontWeight.bold)), ], ), ], Row( mainAxisAlignment: MainAxisAlignment.end, children: [ const Text('Total:', style: TextStyle( fontSize: 16, fontWeight: FontWeight.bold)), const SizedBox(width: 5), Text('\$${formatCurrency(totalConDescuento)}', style: const TextStyle( fontSize: 16, fontWeight: FontWeight.bold)), ], ), ], ), ), ], ), ), ), const SizedBox(height: 10), Card( elevation: 4, color: Colors.white, child: Padding( padding: const EdgeInsets.all(8.0), child: Consumer( builder: (context, viewModel, _) { return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( children: [ Text('Pago', style: TextStyle( fontSize: 22, fontWeight: FontWeight.bold)), Spacer(), ElevatedButton( onPressed: () { showDialog( context: context, builder: (context) { return TotpCuadroConfirmacion( title: "Cambiar Método de Pago", content: "Por favor, ingresa el código de autenticación para continuar.", onSuccess: () { _mostrarModalCambiarMetodoPago(context); }, ); }, ); }, child: Text('Cambiar Método Pago', style: TextStyle( color: AppTheme.quaternary, fontWeight: FontWeight.w500, fontSize: 16)), style: ElevatedButton.styleFrom( backgroundColor: AppTheme.tertiary, padding: const EdgeInsets.fromLTRB(20, 10, 20, 10), ), ), ], ), const SizedBox(height: 10), if ((pedido.cantEfectivo ?? 0) > 0) _buildReadOnlyPaymentRow( "Efectivo", pedido.cantEfectivo ?? 0.0), if ((pedido.cantTarjeta ?? 0) > 0) _buildReadOnlyPaymentRow( "Tarjeta", pedido.cantTarjeta ?? 0.0), if ((pedido.cantTransferencia ?? 0) > 0) _buildReadOnlyPaymentRow( "Transferencia", pedido.cantTransferencia ?? 0.0), ], ); }, ), ), ), const SizedBox(height: 20), Align( alignment: Alignment.centerLeft, child: ElevatedButton.icon( icon: Icon( Icons.receipt_long_outlined, color: AppTheme.quaternary, size: 30, ), onPressed: () => imprimirTicketsJuntos(context, pedido), label: Text( 'Imprimir Ticket', style: TextStyle( fontWeight: FontWeight.w500, fontSize: 18, color: AppTheme.quaternary), ), style: ElevatedButton.styleFrom( padding: const EdgeInsets.fromLTRB(50, 20, 50, 20), backgroundColor: AppTheme.tertiary, ), ), ) ], ), ), ); } Widget _buildReadOnlyPaymentRow(String paymentType, double amount) { return Padding( padding: const EdgeInsets.symmetric(vertical: 6.0), child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text(paymentType, style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold)), Text('\$${formatCurrency(amount)}', style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold)), ], ), ); } Future _mostrarModalCambiarMetodoPago(BuildContext context) async { double totalPedido = pedido.totalPedido ?? 0.0; TextEditingController efectivoController = TextEditingController( text: (pedido.cantEfectivo ?? 0) > 0 ? pedido.cantEfectivo!.toStringAsFixed(2) : ''); TextEditingController tarjetaController = TextEditingController( text: (pedido.cantTarjeta ?? 0) > 0 ? pedido.cantTarjeta!.toStringAsFixed(2) : ''); TextEditingController transferenciaController = TextEditingController( text: (pedido.cantTransferencia ?? 0) > 0 ? pedido.cantTransferencia!.toStringAsFixed(2) : ''); bool efectivoSeleccionado = (pedido.cantEfectivo ?? 0) > 0; bool tarjetaSeleccionada = (pedido.cantTarjeta ?? 0) > 0; bool transferenciaSeleccionada = (pedido.cantTransferencia ?? 0) > 0; bool efectivoCompleto = false; bool tarjetaCompleto = false; bool transferenciaCompleto = false; double cambio = 0.0; double faltante = totalPedido; bool totalCompletado = false; void _calcularCambio(StateSetter setState) { double totalPagado = (double.tryParse(efectivoController.text) ?? 0) + (double.tryParse(tarjetaController.text) ?? 0) + (double.tryParse(transferenciaController.text) ?? 0); setState(() { cambio = totalPagado - totalPedido; faltante = cambio < 0 ? totalPedido - totalPagado : 0; totalCompletado = cambio >= 0; }); } bool? shouldSave = await showDialog( context: context, builder: (BuildContext context) { return StatefulBuilder( builder: (context, setState) { return AlertDialog( actionsPadding: EdgeInsets.fromLTRB(50, 10, 50, 30), title: const Text( 'Cambiar Método de Pago', style: TextStyle(fontSize: 22, fontWeight: FontWeight.w500), ), content: SingleChildScrollView( child: AnimatedSize( duration: const Duration(milliseconds: 300), curve: Curves.easeInOut, child: Column( crossAxisAlignment: CrossAxisAlignment.stretch, children: [ Align( alignment: Alignment.center, child: Text( 'Métodos de pago', style: TextStyle( fontWeight: FontWeight.bold, fontSize: 20), ), ), const SizedBox(height: 10), _buildPaymentMethodRow( setState, totalPedido, label: 'Efectivo', selected: efectivoSeleccionado, exactSelected: efectivoCompleto, controller: efectivoController, onSelected: (value) { setState(() { efectivoSeleccionado = value; if (!efectivoSeleccionado) { efectivoCompleto = false; efectivoController.clear(); } _calcularCambio(setState); }); }, onExactSelected: (value) { setState(() { efectivoCompleto = value; if (efectivoCompleto) { efectivoController.text = totalPedido.toStringAsFixed(2); efectivoSeleccionado = true; tarjetaSeleccionada = false; transferenciaSeleccionada = false; tarjetaController.clear(); transferenciaController.clear(); } else { efectivoController.clear(); } _calcularCambio(setState); }); }, disableOtherMethods: tarjetaCompleto || transferenciaCompleto, onChangedMonto: () => _calcularCambio(setState), ), _buildPaymentMethodRow( setState, totalPedido, label: 'Tarjeta', selected: tarjetaSeleccionada, exactSelected: tarjetaCompleto, controller: tarjetaController, sinCambio: true, onSelected: (value) { setState(() { tarjetaSeleccionada = value; if (!tarjetaSeleccionada) { tarjetaCompleto = false; tarjetaController.clear(); } _calcularCambio(setState); }); }, onExactSelected: (value) { setState(() { tarjetaCompleto = value; if (tarjetaCompleto) { tarjetaController.text = totalPedido.toStringAsFixed(2); tarjetaSeleccionada = true; efectivoSeleccionado = false; transferenciaSeleccionada = false; efectivoController.clear(); transferenciaController.clear(); } else { tarjetaController.clear(); } _calcularCambio(setState); }); }, disableOtherMethods: efectivoCompleto || transferenciaCompleto, onChangedMonto: () => _calcularCambio(setState), ), _buildPaymentMethodRow( setState, totalPedido, label: 'Transferencia', selected: transferenciaSeleccionada, exactSelected: transferenciaCompleto, controller: transferenciaController, sinCambio: true, onSelected: (value) { setState(() { transferenciaSeleccionada = value; if (!transferenciaSeleccionada) { transferenciaCompleto = false; transferenciaController.clear(); } _calcularCambio(setState); }); }, onExactSelected: (value) { setState(() { transferenciaCompleto = value; if (transferenciaCompleto) { transferenciaController.text = totalPedido.toStringAsFixed(2); transferenciaSeleccionada = true; efectivoSeleccionado = false; tarjetaSeleccionada = false; efectivoController.clear(); tarjetaController.clear(); } else { transferenciaController.clear(); } _calcularCambio(setState); }); }, disableOtherMethods: efectivoCompleto || tarjetaCompleto, onChangedMonto: () => _calcularCambio(setState), ), const SizedBox(height: 10), Align( alignment: Alignment.centerRight, child: Column( crossAxisAlignment: CrossAxisAlignment.end, children: [ Text( 'Total del pedido: \$${totalPedido.toStringAsFixed(2)}', style: const TextStyle( fontWeight: FontWeight.bold, fontSize: 18), ), if (faltante > 0) Text( 'Faltante: \$${faltante.toStringAsFixed(2)}', style: const TextStyle( color: Colors.red, fontSize: 18, fontWeight: FontWeight.bold), ) else if (cambio > 0) Text( 'Cambio: \$${cambio.toStringAsFixed(2)}', style: const TextStyle( color: Colors.green, fontSize: 18, fontWeight: FontWeight.bold), ), ], ), ), ], ), ), ), actions: [ TextButton( child: const Text('Cancelar', style: TextStyle(fontSize: 18)), onPressed: () => Navigator.of(context).pop(false), style: ButtonStyle( padding: MaterialStateProperty.all( EdgeInsets.fromLTRB(30, 20, 30, 20)), backgroundColor: MaterialStateProperty.all(Colors.red), foregroundColor: MaterialStateProperty.all(AppTheme.secondary), ), ), const SizedBox(width: 100), TextButton( child: const Text('Guardar', style: TextStyle(fontSize: 18)), onPressed: totalCompletado ? () => Navigator.of(context).pop(true) : null, style: ButtonStyle( padding: MaterialStateProperty.all( EdgeInsets.fromLTRB(30, 20, 30, 20)), backgroundColor: MaterialStateProperty.all( totalCompletado ? AppTheme.tertiary : Colors.grey), foregroundColor: MaterialStateProperty.all(AppTheme.quaternary), ), ), ], ); }, ); }, ); if (shouldSave ?? false) { double cantEfectivo = efectivoSeleccionado ? double.tryParse(efectivoController.text) ?? 0 : 0; double cantTarjeta = tarjetaSeleccionada ? double.tryParse(tarjetaController.text) ?? 0 : 0; double cantTransferencia = transferenciaSeleccionada ? double.tryParse(transferenciaController.text) ?? 0 : 0; pedido.cantEfectivo = cantEfectivo; pedido.cantTarjeta = cantTarjeta; pedido.cantTransferencia = cantTransferencia; List nuevosMetodos = []; if (cantEfectivo > 0) nuevosMetodos.add('Efectivo'); if (cantTarjeta > 0) nuevosMetodos.add('Tarjeta'); if (cantTransferencia > 0) nuevosMetodos.add('Transferencia'); pedido.tipoPago = nuevosMetodos.isNotEmpty ? nuevosMetodos.join(',') : 'No Definido'; pedido.sincronizado = null; await _guardarPedido(pedido, context); // Recargamos el pedido desde la BD para tener sus datos actualizados PedidoViewModel viewModel = Provider.of(context, listen: false); Pedido? pedidoActualizado = await viewModel.fetchPedidoConProductos(pedido.id!); if (pedidoActualizado != null) { setState(() { pedido = pedidoActualizado; }); } } } Widget _buildPaymentMethodRow( StateSetter setState, double totalPedido, { required String label, required bool selected, required bool exactSelected, required TextEditingController controller, required Function(bool) onSelected, required Function(bool) onExactSelected, required bool disableOtherMethods, required Function() onChangedMonto, bool sinCambio = false, }) { return Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, crossAxisAlignment: CrossAxisAlignment.center, children: [ Row( children: [ Checkbox( activeColor: AppTheme.primary, value: selected, onChanged: disableOtherMethods ? null : (value) { onSelected(value ?? false); }, ), GestureDetector( onTap: disableOtherMethods ? null : () { onSelected(!selected); }, child: Text( label, style: const TextStyle(fontSize: 18, fontWeight: FontWeight.bold), ), ), ], ), SizedBox( width: 180, child: Row( crossAxisAlignment: CrossAxisAlignment.start, children: [ Column( children: [ const Text( 'Exacto', style: TextStyle( fontSize: 18, fontWeight: FontWeight.bold, color: Colors.black), ), const SizedBox(height: 17), Checkbox( activeColor: AppTheme.primary, value: exactSelected, onChanged: !disableOtherMethods ? (value) { onExactSelected(value ?? false); if (value == true) { setState(() { // nada especial, ya manejamos esto arriba }); } } : null, ), ], ), const SizedBox(width: 5), Expanded( child: AppTextField( controller: controller, enabled: selected, etiqueta: 'Cantidad', hintText: '0.00', keyboardType: TextInputType.number, onChanged: (value) { if (sinCambio) { double? input = double.tryParse(value) ?? 0; if (input > totalPedido) { controller.text = totalPedido.toStringAsFixed(2); controller.selection = TextSelection.fromPosition( TextPosition(offset: controller.text.length), ); } } onChangedMonto(); }, ), ), ], ), ), ], ); } 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()); } Future _guardarPedido(Pedido pedido, BuildContext buildContext) async { RepoService repoPedido = RepoService(); RepoService repoPedidoProducto = RepoService(); RepoService repoPedidoProductoTopping = RepoService(); try { if (pedido.id != null && pedido.id! > 0) { await repoPedido.guardar(pedido); } else { pedido.id = await repoPedido.guardarLocal(pedido); } List productosExistentes = await repoPedidoProducto.obtenerPorIdPedido(pedido.id!); for (var productoExistente in productosExistentes) { bool sigueExistiendo = pedido.productos.any( (producto) => producto.idProducto == productoExistente.idProducto); if (!sigueExistiendo) { productoExistente.eliminado = DateTime.now().toUtc(); await repoPedidoProducto.guardar(productoExistente); } } for (var producto in pedido.productos) { PedidoProducto pedidoProducto = PedidoProducto( idPedido: pedido.id, idProducto: producto.idProducto, cantidad: producto.cantidad, costoUnitario: producto.costoUnitario, comentario: producto.comentario, ); PedidoProducto? productoExistente = productosExistentes .firstWhereOrNull((p) => p.idProducto == pedidoProducto.idProducto); if (productoExistente != null) { pedidoProducto.id = productoExistente.id; await repoPedidoProducto.guardar(pedidoProducto); } else { int idPedidoProducto = await repoPedidoProducto.guardarLocal(pedidoProducto); for (var topping in producto.toppings) { PedidoProductoTopping pedidoProductoTopping = PedidoProductoTopping( idPedidoProducto: idPedidoProducto, idTopping: topping.idTopping, idCategoria: topping.idCategoria, ); await repoPedidoProductoTopping.guardarLocal(pedidoProductoTopping); } } } ScaffoldMessenger.of(buildContext).showSnackBar( const SnackBar(content: Text("Pedido actualizado correctamente.")), ); } catch (e) { print("Error al guardar el pedido: $e"); ScaffoldMessenger.of(buildContext).showSnackBar( const SnackBar(content: Text("Error al actualizar el pedido.")), ); } } }