|
@@ -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 '../pedido/pedido_ticket.dart';
|
|
@@ -32,6 +33,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;
|
|
@@ -91,12 +93,22 @@ class _PedidoFormState extends State<PedidoForm> {
|
|
|
Provider.of<DescuentoViewModel>(context, listen: false).cargarDescuentos();
|
|
|
}
|
|
|
|
|
|
- _onSearchChanged(String value) {
|
|
|
+ void _onSearchChanged(String value) async {
|
|
|
if (value.isEmpty) {
|
|
|
- cargarProductosIniciales();
|
|
|
+ cargarProductosPorCategoria(
|
|
|
+ categoriaSeleccionada?.id ?? categorias.first.id);
|
|
|
} else {
|
|
|
- Provider.of<ProductoViewModel>(context, listen: false)
|
|
|
+ setState(() {
|
|
|
+ _estadoBusqueda = true;
|
|
|
+ });
|
|
|
+
|
|
|
+ await Provider.of<ProductoViewModel>(context, listen: false)
|
|
|
.fetchLocalByName(nombre: value);
|
|
|
+
|
|
|
+ setState(() {
|
|
|
+ productos =
|
|
|
+ Provider.of<ProductoViewModel>(context, listen: false).productos;
|
|
|
+ });
|
|
|
}
|
|
|
}
|
|
|
|
|
@@ -158,6 +170,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) +
|
|
@@ -174,6 +189,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;
|
|
|
+ }
|
|
|
});
|
|
|
}
|
|
|
|
|
@@ -188,254 +210,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)),
|
|
|
+ ],
|
|
|
),
|
|
|
- ],
|
|
|
- )
|
|
|
- ],
|
|
|
- );
|
|
|
+ ),
|
|
|
+ ));
|
|
|
},
|
|
|
);
|
|
|
},
|
|
@@ -447,11 +731,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",
|
|
@@ -538,6 +821,7 @@ class _PedidoFormState extends State<PedidoForm> {
|
|
|
void dispose() {
|
|
|
_gridViewController.dispose();
|
|
|
_searchController.dispose();
|
|
|
+ _categoryScrollController.dispose();
|
|
|
super.dispose();
|
|
|
}
|
|
|
|
|
@@ -970,6 +1254,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),
|
|
@@ -986,7 +1272,9 @@ class _PedidoFormState extends State<PedidoForm> {
|
|
|
itemCount: productos.length,
|
|
|
itemBuilder: (context, index) {
|
|
|
final producto = productos[index];
|
|
|
- if (producto.idCategoria != categoriaSeleccionada?.id) {
|
|
|
+ // Si no se está buscando, aplicar el filtro de categoría
|
|
|
+ if (!_estadoBusqueda &&
|
|
|
+ producto.idCategoria != categoriaSeleccionada?.id) {
|
|
|
return Container();
|
|
|
}
|
|
|
return Card(
|
|
@@ -1043,31 +1331,38 @@ class _PedidoFormState extends State<PedidoForm> {
|
|
|
|
|
|
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,
|
|
|
+ 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!),
|
|
|
),
|
|
|
- child: Text(categoria.nombre!),
|
|
|
- ),
|
|
|
- );
|
|
|
- },
|
|
|
+ );
|
|
|
+ },
|
|
|
+ ),
|
|
|
),
|
|
|
);
|
|
|
}
|
|
@@ -1080,9 +1375,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,
|
|
|
),
|