|
@@ -2,6 +2,7 @@ import 'dart:async';
|
|
|
import 'dart:io';
|
|
|
import 'package:flutter/foundation.dart';
|
|
|
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/views/pedido/pedido_ticket.dart';
|
|
@@ -31,6 +32,7 @@ class _PedidoFormState extends State<PedidoForm> {
|
|
|
bool _estadoBusqueda = false;
|
|
|
Pedido? pedidoActual;
|
|
|
ScrollController _gridViewController = ScrollController();
|
|
|
+ ScrollController _categoryScrollController = ScrollController();
|
|
|
final _searchController = TextEditingController();
|
|
|
final NumberFormat _numberFormat = NumberFormat.decimalPattern('es_MX');
|
|
|
int? selectedDescuento = 0;
|
|
@@ -50,14 +52,14 @@ class _PedidoFormState extends State<PedidoForm> {
|
|
|
double total = 0;
|
|
|
|
|
|
for (var item in carrito) {
|
|
|
- total += double.parse(item.producto.precio!) * item.cantidad;
|
|
|
+ total += item.producto.precio! * item.cantidad;
|
|
|
item.selectedToppings.forEach((categoryId, selectedToppingIds) {
|
|
|
for (int toppingId in selectedToppingIds) {
|
|
|
Producto? topping = item.selectableToppings[categoryId]?.firstWhere(
|
|
|
(topping) => topping.id == toppingId,
|
|
|
- orElse: () => Producto(precio: '0'));
|
|
|
+ orElse: () => Producto(precio: 0));
|
|
|
if (topping != null) {
|
|
|
- total += (double.tryParse(topping.precio!) ?? 0) * item.cantidad;
|
|
|
+ total += (topping.precio ?? 0.0) * item.cantidad;
|
|
|
}
|
|
|
}
|
|
|
});
|
|
@@ -90,10 +92,15 @@ class _PedidoFormState extends State<PedidoForm> {
|
|
|
Provider.of<DescuentoViewModel>(context, listen: false).cargarDescuentos();
|
|
|
}
|
|
|
|
|
|
- _onSearchChanged(String value) {
|
|
|
+ void _onSearchChanged(String value) {
|
|
|
if (value.isEmpty) {
|
|
|
- cargarProductosIniciales();
|
|
|
+ cargarProductosPorCategoria(
|
|
|
+ categoriaSeleccionada?.id ?? categorias.first.id);
|
|
|
} else {
|
|
|
+ setState(() {
|
|
|
+ _estadoBusqueda = true;
|
|
|
+ categoriaSeleccionada = null;
|
|
|
+ });
|
|
|
Provider.of<ProductoViewModel>(context, listen: false)
|
|
|
.fetchLocalByName(nombre: value);
|
|
|
}
|
|
@@ -157,6 +164,9 @@ class _PedidoFormState extends State<PedidoForm> {
|
|
|
String errorMessage = '';
|
|
|
double faltante = totalPedido;
|
|
|
bool totalCompletado = false;
|
|
|
+ bool efectivoCompleto = false;
|
|
|
+ bool tarjetaCompleto = false;
|
|
|
+ bool transferenciaCompleto = false;
|
|
|
|
|
|
void _calcularCambio(StateSetter setState) {
|
|
|
double totalPagado = (double.tryParse(efectivoController.text) ?? 0) +
|
|
@@ -173,6 +183,13 @@ class _PedidoFormState extends State<PedidoForm> {
|
|
|
faltante = 0;
|
|
|
totalCompletado = true;
|
|
|
}
|
|
|
+
|
|
|
+ // Si el total ha sido alcanzado o excedido, desactivar otros métodos de pago
|
|
|
+ if (totalPagado >= totalPedido) {
|
|
|
+ if (!efectivoSeleccionado) efectivoSeleccionado = false;
|
|
|
+ if (!tarjetaSeleccionada) tarjetaSeleccionada = false;
|
|
|
+ if (!transferenciaSeleccionada) transferenciaSeleccionada = false;
|
|
|
+ }
|
|
|
});
|
|
|
}
|
|
|
|
|
@@ -187,254 +204,516 @@ class _PedidoFormState extends State<PedidoForm> {
|
|
|
_calcularCambio(setState);
|
|
|
}
|
|
|
|
|
|
+ bool _isPaymentOptionEnabled(bool isSelected) {
|
|
|
+ return !totalCompletado || isSelected;
|
|
|
+ }
|
|
|
+
|
|
|
bool? shouldSave = await showDialog<bool>(
|
|
|
context: context,
|
|
|
builder: (BuildContext context) {
|
|
|
return StatefulBuilder(
|
|
|
builder: (context, setState) {
|
|
|
- return AlertDialog(
|
|
|
- actionsPadding: EdgeInsets.fromLTRB(50, 10, 50, 30),
|
|
|
- title: const Text(
|
|
|
- 'Finalizar Pedido',
|
|
|
- style: TextStyle(fontSize: 22, fontWeight: FontWeight.w500),
|
|
|
- ),
|
|
|
- content: Column(
|
|
|
- mainAxisSize: MainAxisSize.min,
|
|
|
- children: [
|
|
|
- AppTextField(
|
|
|
- controller: nombreController,
|
|
|
- etiqueta: 'Nombre',
|
|
|
- hintText: "Nombre del Cliente",
|
|
|
- errorText: errorMessage.isEmpty ? null : errorMessage,
|
|
|
- onChanged: (value) {
|
|
|
- setState(() {
|
|
|
- errorMessage = value.trim().isEmpty
|
|
|
- ? "El nombre del cliente es obligatorio."
|
|
|
- : '';
|
|
|
- });
|
|
|
- },
|
|
|
- ),
|
|
|
- const SizedBox(height: 10),
|
|
|
- AppTextField(
|
|
|
- controller: comentarioController,
|
|
|
- etiqueta: 'Comentarios (opcional)',
|
|
|
- hintText: 'Comentarios',
|
|
|
- maxLines: 3,
|
|
|
- ),
|
|
|
- const SizedBox(height: 10),
|
|
|
- Align(
|
|
|
- alignment: Alignment.center,
|
|
|
- child: Text(
|
|
|
- 'Métodos de pago',
|
|
|
- style:
|
|
|
- TextStyle(fontWeight: FontWeight.bold, fontSize: 20),
|
|
|
- ),
|
|
|
- ),
|
|
|
- const SizedBox(height: 10),
|
|
|
- // Efectivo
|
|
|
- Row(
|
|
|
- mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
|
|
- children: [
|
|
|
- Row(
|
|
|
- children: [
|
|
|
- Checkbox(
|
|
|
- activeColor: AppTheme.primary,
|
|
|
- value: efectivoSeleccionado,
|
|
|
- onChanged:
|
|
|
- (totalCompletado && !efectivoSeleccionado)
|
|
|
- ? null
|
|
|
- : (bool? value) {
|
|
|
- setState(() {
|
|
|
- efectivoSeleccionado = value ?? false;
|
|
|
- if (!efectivoSeleccionado) {
|
|
|
- efectivoController.clear();
|
|
|
- _calcularCambio(setState);
|
|
|
- }
|
|
|
- });
|
|
|
- },
|
|
|
- ),
|
|
|
- const Text(
|
|
|
- "Efectivo",
|
|
|
- style: TextStyle(
|
|
|
- fontSize: 18, fontWeight: FontWeight.bold),
|
|
|
- ),
|
|
|
- ],
|
|
|
- ),
|
|
|
- if (efectivoSeleccionado)
|
|
|
- SizedBox(
|
|
|
- width: 150,
|
|
|
- child: AppTextField(
|
|
|
- controller: efectivoController,
|
|
|
- etiqueta: 'Cantidad',
|
|
|
- hintText: '0.00',
|
|
|
- keyboardType: TextInputType.number,
|
|
|
- onChanged: (value) => _calcularCambio(setState),
|
|
|
- ),
|
|
|
- ),
|
|
|
- ],
|
|
|
+ return RawKeyboardListener(
|
|
|
+ focusNode: FocusNode(),
|
|
|
+ onKey: (RawKeyEvent event) {
|
|
|
+ if (event.isKeyPressed(LogicalKeyboardKey.enter) &&
|
|
|
+ totalCompletado) {
|
|
|
+ Navigator.of(context).pop(true);
|
|
|
+ }
|
|
|
+ },
|
|
|
+ child: AlertDialog(
|
|
|
+ actionsPadding: EdgeInsets.fromLTRB(50, 10, 50, 30),
|
|
|
+ title: const Text(
|
|
|
+ 'Finalizar Pedido',
|
|
|
+ style: TextStyle(fontSize: 22, fontWeight: FontWeight.w500),
|
|
|
),
|
|
|
- const SizedBox(height: 10),
|
|
|
- // Tarjeta
|
|
|
- Row(
|
|
|
- mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
|
|
- children: [
|
|
|
- Row(
|
|
|
- children: [
|
|
|
- Checkbox(
|
|
|
- activeColor: AppTheme.primary,
|
|
|
- value: tarjetaSeleccionada,
|
|
|
- onChanged: (totalCompletado && !tarjetaSeleccionada)
|
|
|
- ? null
|
|
|
- : (bool? value) {
|
|
|
- setState(() {
|
|
|
- tarjetaSeleccionada = value ?? false;
|
|
|
- if (!tarjetaSeleccionada) {
|
|
|
- tarjetaController.clear();
|
|
|
- _calcularCambio(setState);
|
|
|
- }
|
|
|
- });
|
|
|
+ content: Container(
|
|
|
+ height: 600,
|
|
|
+ child: Column(
|
|
|
+ children: [
|
|
|
+ Expanded(
|
|
|
+ child: SingleChildScrollView(
|
|
|
+ child: Column(
|
|
|
+ children: [
|
|
|
+ AppTextField(
|
|
|
+ controller: nombreController,
|
|
|
+ etiqueta: 'Nombre',
|
|
|
+ hintText: "Nombre del Cliente",
|
|
|
+ ),
|
|
|
+ const SizedBox(height: 10),
|
|
|
+ AppTextField(
|
|
|
+ controller: comentarioController,
|
|
|
+ etiqueta: 'Comentarios (opcional)',
|
|
|
+ hintText: 'Comentarios',
|
|
|
+ maxLines: 2,
|
|
|
+ ),
|
|
|
+ const SizedBox(height: 10),
|
|
|
+ Align(
|
|
|
+ alignment: Alignment.center,
|
|
|
+ child: Text(
|
|
|
+ 'Métodos de pago',
|
|
|
+ style: TextStyle(
|
|
|
+ fontWeight: FontWeight.bold,
|
|
|
+ fontSize: 20),
|
|
|
+ ),
|
|
|
+ ),
|
|
|
+ const SizedBox(height: 10),
|
|
|
+ // Efectivo
|
|
|
+ GestureDetector(
|
|
|
+ onTap: () {
|
|
|
+ if (_isPaymentOptionEnabled(
|
|
|
+ efectivoSeleccionado)) {
|
|
|
+ setState(() {
|
|
|
+ efectivoSeleccionado =
|
|
|
+ !efectivoSeleccionado;
|
|
|
+ if (!efectivoSeleccionado) {
|
|
|
+ efectivoCompleto = false;
|
|
|
+ efectivoController.clear();
|
|
|
+ _calcularCambio(setState);
|
|
|
+ } else if (efectivoCompleto) {
|
|
|
+ efectivoController.text =
|
|
|
+ totalPedido.toStringAsFixed(2);
|
|
|
+ _calcularCambio(setState);
|
|
|
+ }
|
|
|
+ });
|
|
|
+ }
|
|
|
},
|
|
|
- ),
|
|
|
- const Text(
|
|
|
- "Tarjeta",
|
|
|
- style: TextStyle(
|
|
|
- fontSize: 18, fontWeight: FontWeight.bold),
|
|
|
- ),
|
|
|
- ],
|
|
|
- ),
|
|
|
- if (tarjetaSeleccionada)
|
|
|
- SizedBox(
|
|
|
- width: 150,
|
|
|
- child: AppTextField(
|
|
|
- controller: tarjetaController,
|
|
|
- etiqueta: 'Cantidad',
|
|
|
- hintText: '0.00',
|
|
|
- keyboardType: TextInputType.number,
|
|
|
- onChanged: (value) {
|
|
|
- _validarCantidad(setState, tarjetaController);
|
|
|
- },
|
|
|
+ child: Row(
|
|
|
+ mainAxisAlignment:
|
|
|
+ MainAxisAlignment.spaceBetween,
|
|
|
+ crossAxisAlignment:
|
|
|
+ CrossAxisAlignment.center,
|
|
|
+ children: [
|
|
|
+ Row(
|
|
|
+ children: [
|
|
|
+ Checkbox(
|
|
|
+ activeColor: AppTheme.primary,
|
|
|
+ value: efectivoSeleccionado,
|
|
|
+ onChanged: _isPaymentOptionEnabled(
|
|
|
+ efectivoSeleccionado)
|
|
|
+ ? (bool? value) {
|
|
|
+ setState(() {
|
|
|
+ efectivoSeleccionado =
|
|
|
+ value ?? false;
|
|
|
+ if (!efectivoSeleccionado) {
|
|
|
+ efectivoCompleto =
|
|
|
+ false;
|
|
|
+ efectivoController
|
|
|
+ .clear();
|
|
|
+ _calcularCambio(
|
|
|
+ setState);
|
|
|
+ } else if (efectivoCompleto) {
|
|
|
+ efectivoController
|
|
|
+ .text =
|
|
|
+ totalPedido
|
|
|
+ .toStringAsFixed(
|
|
|
+ 2);
|
|
|
+ _calcularCambio(
|
|
|
+ setState);
|
|
|
+ }
|
|
|
+ });
|
|
|
+ }
|
|
|
+ : null,
|
|
|
+ ),
|
|
|
+ const Text(
|
|
|
+ "Efectivo",
|
|
|
+ style: TextStyle(
|
|
|
+ fontSize: 18,
|
|
|
+ fontWeight: FontWeight.bold),
|
|
|
+ ),
|
|
|
+ ],
|
|
|
+ ),
|
|
|
+ if (efectivoSeleccionado)
|
|
|
+ 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: efectivoCompleto,
|
|
|
+ onChanged:
|
|
|
+ efectivoSeleccionado
|
|
|
+ ? (bool? value) {
|
|
|
+ setState(() {
|
|
|
+ efectivoCompleto =
|
|
|
+ value ??
|
|
|
+ false;
|
|
|
+ if (efectivoCompleto) {
|
|
|
+ efectivoController
|
|
|
+ .text =
|
|
|
+ totalPedido
|
|
|
+ .toStringAsFixed(2);
|
|
|
+ _calcularCambio(
|
|
|
+ setState);
|
|
|
+ } else {
|
|
|
+ efectivoController
|
|
|
+ .clear();
|
|
|
+ _calcularCambio(
|
|
|
+ setState);
|
|
|
+ }
|
|
|
+ });
|
|
|
+ }
|
|
|
+ : null,
|
|
|
+ ),
|
|
|
+ ],
|
|
|
+ ),
|
|
|
+ const SizedBox(
|
|
|
+ width: 5,
|
|
|
+ ),
|
|
|
+ Expanded(
|
|
|
+ child: AppTextField(
|
|
|
+ controller:
|
|
|
+ efectivoController,
|
|
|
+ etiqueta: 'Cantidad',
|
|
|
+ hintText: '0.00',
|
|
|
+ keyboardType:
|
|
|
+ TextInputType.number,
|
|
|
+ onChanged: (value) =>
|
|
|
+ _calcularCambio(setState),
|
|
|
+ ),
|
|
|
+ ),
|
|
|
+ ],
|
|
|
+ ),
|
|
|
+ ),
|
|
|
+ ],
|
|
|
+ ),
|
|
|
+ ),
|
|
|
+ const SizedBox(height: 10),
|
|
|
+ // Tarjeta
|
|
|
+ GestureDetector(
|
|
|
+ onTap: () {
|
|
|
+ if (_isPaymentOptionEnabled(
|
|
|
+ tarjetaSeleccionada)) {
|
|
|
+ setState(() {
|
|
|
+ tarjetaSeleccionada =
|
|
|
+ !tarjetaSeleccionada;
|
|
|
+ if (!tarjetaSeleccionada) {
|
|
|
+ tarjetaController.clear();
|
|
|
+ _calcularCambio(setState);
|
|
|
+ }
|
|
|
+ });
|
|
|
+ }
|
|
|
+ },
|
|
|
+ child: Row(
|
|
|
+ mainAxisAlignment:
|
|
|
+ MainAxisAlignment.spaceBetween,
|
|
|
+ crossAxisAlignment:
|
|
|
+ CrossAxisAlignment.center,
|
|
|
+ children: [
|
|
|
+ Row(
|
|
|
+ children: [
|
|
|
+ Checkbox(
|
|
|
+ activeColor: AppTheme.primary,
|
|
|
+ value: tarjetaSeleccionada,
|
|
|
+ onChanged: _isPaymentOptionEnabled(
|
|
|
+ tarjetaSeleccionada)
|
|
|
+ ? (bool? value) {
|
|
|
+ setState(() {
|
|
|
+ tarjetaSeleccionada =
|
|
|
+ value ?? false;
|
|
|
+ if (!tarjetaSeleccionada) {
|
|
|
+ tarjetaController
|
|
|
+ .clear();
|
|
|
+ _calcularCambio(
|
|
|
+ setState);
|
|
|
+ }
|
|
|
+ });
|
|
|
+ }
|
|
|
+ : null,
|
|
|
+ ),
|
|
|
+ const Text(
|
|
|
+ "Tarjeta",
|
|
|
+ style: TextStyle(
|
|
|
+ fontSize: 18,
|
|
|
+ fontWeight: FontWeight.bold),
|
|
|
+ ),
|
|
|
+ ],
|
|
|
+ ),
|
|
|
+ if (tarjetaSeleccionada)
|
|
|
+ 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: tarjetaCompleto,
|
|
|
+ onChanged:
|
|
|
+ tarjetaSeleccionada
|
|
|
+ ? (bool? value) {
|
|
|
+ setState(() {
|
|
|
+ tarjetaCompleto =
|
|
|
+ value ??
|
|
|
+ false;
|
|
|
+ if (tarjetaCompleto) {
|
|
|
+ tarjetaController
|
|
|
+ .text =
|
|
|
+ totalPedido
|
|
|
+ .toStringAsFixed(2);
|
|
|
+ _calcularCambio(
|
|
|
+ setState);
|
|
|
+ } else {
|
|
|
+ tarjetaController
|
|
|
+ .clear();
|
|
|
+ _calcularCambio(
|
|
|
+ setState);
|
|
|
+ }
|
|
|
+ });
|
|
|
+ }
|
|
|
+ : null,
|
|
|
+ ),
|
|
|
+ ],
|
|
|
+ ),
|
|
|
+ const SizedBox(
|
|
|
+ width: 5,
|
|
|
+ ),
|
|
|
+ Expanded(
|
|
|
+ child: AppTextField(
|
|
|
+ controller: tarjetaController,
|
|
|
+ etiqueta: 'Cantidad',
|
|
|
+ hintText: '0.00',
|
|
|
+ keyboardType:
|
|
|
+ TextInputType.number,
|
|
|
+ onChanged: (value) {
|
|
|
+ _validarCantidad(setState,
|
|
|
+ tarjetaController);
|
|
|
+ },
|
|
|
+ ),
|
|
|
+ ),
|
|
|
+ ],
|
|
|
+ ),
|
|
|
+ ),
|
|
|
+ ],
|
|
|
+ ),
|
|
|
+ ),
|
|
|
+ const SizedBox(height: 10),
|
|
|
+ // Transferencia
|
|
|
+ GestureDetector(
|
|
|
+ onTap: () {
|
|
|
+ if (_isPaymentOptionEnabled(
|
|
|
+ transferenciaSeleccionada)) {
|
|
|
+ setState(() {
|
|
|
+ transferenciaSeleccionada =
|
|
|
+ !transferenciaSeleccionada;
|
|
|
+ if (!transferenciaSeleccionada) {
|
|
|
+ transferenciaController.clear();
|
|
|
+ _calcularCambio(setState);
|
|
|
+ }
|
|
|
+ });
|
|
|
+ }
|
|
|
+ },
|
|
|
+ child: Row(
|
|
|
+ mainAxisAlignment:
|
|
|
+ MainAxisAlignment.spaceBetween,
|
|
|
+ crossAxisAlignment:
|
|
|
+ CrossAxisAlignment.center,
|
|
|
+ children: [
|
|
|
+ Row(
|
|
|
+ children: [
|
|
|
+ Checkbox(
|
|
|
+ activeColor: AppTheme.primary,
|
|
|
+ value: transferenciaSeleccionada,
|
|
|
+ onChanged: _isPaymentOptionEnabled(
|
|
|
+ transferenciaSeleccionada)
|
|
|
+ ? (bool? value) {
|
|
|
+ setState(() {
|
|
|
+ transferenciaSeleccionada =
|
|
|
+ value ?? false;
|
|
|
+ if (!transferenciaSeleccionada) {
|
|
|
+ transferenciaController
|
|
|
+ .clear();
|
|
|
+ _calcularCambio(
|
|
|
+ setState);
|
|
|
+ }
|
|
|
+ });
|
|
|
+ }
|
|
|
+ : null,
|
|
|
+ ),
|
|
|
+ const Text(
|
|
|
+ "Transferencia",
|
|
|
+ style: TextStyle(
|
|
|
+ fontSize: 18,
|
|
|
+ fontWeight: FontWeight.bold),
|
|
|
+ ),
|
|
|
+ ],
|
|
|
+ ),
|
|
|
+ if (transferenciaSeleccionada)
|
|
|
+ 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:
|
|
|
+ transferenciaCompleto,
|
|
|
+ onChanged:
|
|
|
+ transferenciaSeleccionada
|
|
|
+ ? (bool? value) {
|
|
|
+ setState(() {
|
|
|
+ transferenciaCompleto =
|
|
|
+ value ??
|
|
|
+ false;
|
|
|
+ if (transferenciaCompleto) {
|
|
|
+ transferenciaController
|
|
|
+ .text =
|
|
|
+ totalPedido
|
|
|
+ .toStringAsFixed(2);
|
|
|
+ _calcularCambio(
|
|
|
+ setState);
|
|
|
+ } else {
|
|
|
+ transferenciaController
|
|
|
+ .clear();
|
|
|
+ _calcularCambio(
|
|
|
+ setState);
|
|
|
+ }
|
|
|
+ });
|
|
|
+ }
|
|
|
+ : null,
|
|
|
+ ),
|
|
|
+ ],
|
|
|
+ ),
|
|
|
+ const SizedBox(
|
|
|
+ width: 5,
|
|
|
+ ),
|
|
|
+ Expanded(
|
|
|
+ child: AppTextField(
|
|
|
+ controller:
|
|
|
+ transferenciaController,
|
|
|
+ etiqueta: 'Cantidad',
|
|
|
+ hintText: '0.00',
|
|
|
+ keyboardType:
|
|
|
+ TextInputType.number,
|
|
|
+ onChanged: (value) {
|
|
|
+ _validarCantidad(setState,
|
|
|
+ transferenciaController);
|
|
|
+ },
|
|
|
+ ),
|
|
|
+ ),
|
|
|
+ ],
|
|
|
+ ),
|
|
|
+ ),
|
|
|
+ ],
|
|
|
+ ),
|
|
|
+ ),
|
|
|
+ const SizedBox(height: 10),
|
|
|
+ // Mostrar el total del pedido y la cantidad faltante
|
|
|
+ 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)),
|
|
|
+ ]),
|
|
|
+ ),
|
|
|
+ ],
|
|
|
+ ),
|
|
|
),
|
|
|
),
|
|
|
- ],
|
|
|
- ),
|
|
|
- const SizedBox(height: 10),
|
|
|
- // Transferencia
|
|
|
- Row(
|
|
|
- mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
|
|
- children: [
|
|
|
- Row(
|
|
|
- children: [
|
|
|
- Checkbox(
|
|
|
- activeColor: AppTheme.primary,
|
|
|
- value: transferenciaSeleccionada,
|
|
|
- onChanged:
|
|
|
- (totalCompletado && !transferenciaSeleccionada)
|
|
|
- ? null
|
|
|
- : (bool? value) {
|
|
|
- setState(() {
|
|
|
- transferenciaSeleccionada =
|
|
|
- value ?? false;
|
|
|
- if (!transferenciaSeleccionada) {
|
|
|
- transferenciaController.clear();
|
|
|
- _calcularCambio(setState);
|
|
|
- }
|
|
|
- });
|
|
|
- },
|
|
|
- ),
|
|
|
- const Text(
|
|
|
- "Transferencia",
|
|
|
- style: TextStyle(
|
|
|
- fontSize: 18, fontWeight: FontWeight.bold),
|
|
|
- ),
|
|
|
- ],
|
|
|
- ),
|
|
|
- if (transferenciaSeleccionada)
|
|
|
- SizedBox(
|
|
|
- width: 150,
|
|
|
- child: AppTextField(
|
|
|
- controller: transferenciaController,
|
|
|
- etiqueta: 'Cantidad',
|
|
|
- hintText: '0.00',
|
|
|
- keyboardType: TextInputType.number,
|
|
|
- onChanged: (value) {
|
|
|
- _validarCantidad(
|
|
|
- setState, transferenciaController);
|
|
|
- },
|
|
|
- ),
|
|
|
+ // Aquí mantenemos los botones fijos
|
|
|
+ Row(
|
|
|
+ mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
|
|
+ children: [
|
|
|
+ TextButton(
|
|
|
+ child: const Text('Cancelar',
|
|
|
+ style: TextStyle(fontSize: 18)),
|
|
|
+ onPressed: () {
|
|
|
+ Navigator.of(context).pop(false);
|
|
|
+ },
|
|
|
+ style: ButtonStyle(
|
|
|
+ padding: MaterialStatePropertyAll(
|
|
|
+ EdgeInsets.fromLTRB(30, 20, 30, 20)),
|
|
|
+ backgroundColor:
|
|
|
+ MaterialStatePropertyAll(Colors.red),
|
|
|
+ foregroundColor: MaterialStatePropertyAll(
|
|
|
+ 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: MaterialStatePropertyAll(
|
|
|
+ EdgeInsets.fromLTRB(30, 20, 30, 20)),
|
|
|
+ backgroundColor: MaterialStatePropertyAll(
|
|
|
+ totalCompletado
|
|
|
+ ? AppTheme.tertiary
|
|
|
+ : Colors.grey),
|
|
|
+ foregroundColor: MaterialStatePropertyAll(
|
|
|
+ AppTheme.quaternary)),
|
|
|
+ ),
|
|
|
+ ],
|
|
|
),
|
|
|
- ],
|
|
|
- ),
|
|
|
- const SizedBox(height: 10),
|
|
|
- // Mostrar el total del pedido y la cantidad faltante
|
|
|
- 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: <Widget>[
|
|
|
- Row(
|
|
|
- mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
|
|
- children: [
|
|
|
- TextButton(
|
|
|
- child: const Text('Cancelar',
|
|
|
- style: TextStyle(fontSize: 18)),
|
|
|
- onPressed: () {
|
|
|
- Navigator.of(context).pop(false);
|
|
|
- },
|
|
|
- style: ButtonStyle(
|
|
|
- padding: MaterialStatePropertyAll(
|
|
|
- EdgeInsets.fromLTRB(30, 20, 30, 20)),
|
|
|
- backgroundColor: MaterialStatePropertyAll(Colors.red),
|
|
|
- foregroundColor:
|
|
|
- MaterialStatePropertyAll(AppTheme.secondary)),
|
|
|
- ),
|
|
|
- const SizedBox(width: 100),
|
|
|
- TextButton(
|
|
|
- child:
|
|
|
- const Text('Guardar', style: TextStyle(fontSize: 18)),
|
|
|
- onPressed: () {
|
|
|
- if (nombreController.text.trim().isEmpty) {
|
|
|
- setState(() => errorMessage =
|
|
|
- "El nombre del cliente es obligatorio.");
|
|
|
- return;
|
|
|
- }
|
|
|
- Navigator.of(context).pop(true);
|
|
|
- },
|
|
|
- style: ButtonStyle(
|
|
|
- padding: MaterialStatePropertyAll(
|
|
|
- EdgeInsets.fromLTRB(30, 20, 30, 20)),
|
|
|
- backgroundColor:
|
|
|
- MaterialStatePropertyAll(AppTheme.tertiary),
|
|
|
- foregroundColor:
|
|
|
- MaterialStatePropertyAll(AppTheme.quaternary)),
|
|
|
+ ],
|
|
|
),
|
|
|
- ],
|
|
|
- )
|
|
|
- ],
|
|
|
- );
|
|
|
+ ),
|
|
|
+ ));
|
|
|
},
|
|
|
);
|
|
|
},
|
|
@@ -446,11 +725,10 @@ class _PedidoFormState extends State<PedidoForm> {
|
|
|
}
|
|
|
|
|
|
void prepararPedidoActual(String nombreCliente, String comentarios) async {
|
|
|
- DateTime now = DateTime.now();
|
|
|
- String formattedDate = DateFormat('dd-MM-yyyy kk:mm:ss').format(now);
|
|
|
+ String now = DateTime.now().toUtc().toIso8601String();
|
|
|
|
|
|
Pedido nuevoPedido = Pedido(
|
|
|
- peticion: formattedDate,
|
|
|
+ peticion: now,
|
|
|
nombreCliente: nombreCliente,
|
|
|
comentarios: comentarios,
|
|
|
estatus: "NUEVO",
|
|
@@ -480,7 +758,7 @@ class _PedidoFormState extends State<PedidoForm> {
|
|
|
return PedidoProducto(
|
|
|
idProducto: item.producto.id,
|
|
|
producto: item.producto,
|
|
|
- costoUnitario: item.producto.precio,
|
|
|
+ costoUnitario: item.producto.precio.toString(),
|
|
|
cantidad: item.cantidad,
|
|
|
comentario: comentarios,
|
|
|
toppings: selectedToppings,
|
|
@@ -537,6 +815,7 @@ class _PedidoFormState extends State<PedidoForm> {
|
|
|
void dispose() {
|
|
|
_gridViewController.dispose();
|
|
|
_searchController.dispose();
|
|
|
+ _categoryScrollController.dispose();
|
|
|
super.dispose();
|
|
|
}
|
|
|
|
|
@@ -860,9 +1139,7 @@ class _PedidoFormState extends State<PedidoForm> {
|
|
|
MainAxisAlignment.spaceBetween,
|
|
|
children: [
|
|
|
Text(topping.nombre!),
|
|
|
- if (double.tryParse(
|
|
|
- topping.precio!) !=
|
|
|
- 0.0)
|
|
|
+ if (topping.precio != 0.0)
|
|
|
Text(
|
|
|
'+\$${topping.precio}',
|
|
|
style: TextStyle(
|
|
@@ -969,6 +1246,8 @@ class _PedidoFormState extends State<PedidoForm> {
|
|
|
Widget _buildProductsSection() {
|
|
|
return Column(
|
|
|
children: [
|
|
|
+ const SizedBox(height: 5),
|
|
|
+ _buildSearchBar(),
|
|
|
const SizedBox(height: 10),
|
|
|
_buildCategoryButtons(),
|
|
|
const SizedBox(height: 15),
|
|
@@ -1040,34 +1319,44 @@ class _PedidoFormState extends State<PedidoForm> {
|
|
|
List<CategoriaProducto> categoriasFiltradas =
|
|
|
categorias.where((categoria) => categoria.esToping == 0).toList();
|
|
|
|
|
|
- return Container(
|
|
|
- height: 50,
|
|
|
- child: ListView.builder(
|
|
|
- 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,
|
|
|
+ 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!),
|
|
|
+ ),
|
|
|
+ );
|
|
|
+ },
|
|
|
),
|
|
|
- child: Text(categoria.nombre!),
|
|
|
- ),
|
|
|
- );
|
|
|
- },
|
|
|
- ),
|
|
|
+ ],
|
|
|
+ ),
|
|
|
+ ),
|
|
|
+ ],
|
|
|
);
|
|
|
}
|
|
|
|
|
@@ -1079,9 +1368,23 @@ class _PedidoFormState extends State<PedidoForm> {
|
|
|
decoration: InputDecoration(
|
|
|
hintText: 'Buscar producto...',
|
|
|
prefixIcon: const Icon(Icons.search),
|
|
|
+ suffixIcon: IconButton(
|
|
|
+ icon: Icon(Icons.clear),
|
|
|
+ onPressed: () {
|
|
|
+ _searchController.clear();
|
|
|
+ _onSearchChanged('');
|
|
|
+ },
|
|
|
+ ),
|
|
|
border: OutlineInputBorder(
|
|
|
borderRadius: BorderRadius.circular(12.0),
|
|
|
),
|
|
|
+ enabledBorder: OutlineInputBorder(
|
|
|
+ borderRadius: BorderRadius.circular(12.0),
|
|
|
+ borderSide: BorderSide(width: 1.5)),
|
|
|
+ focusedBorder: OutlineInputBorder(
|
|
|
+ borderSide: BorderSide(color: Colors.black),
|
|
|
+ borderRadius: BorderRadius.circular(12.0),
|
|
|
+ ),
|
|
|
),
|
|
|
onChanged: _onSearchChanged,
|
|
|
),
|