|
@@ -0,0 +1,522 @@
|
|
|
+import 'package:conalep_pos/models/models.dart';
|
|
|
+import 'package:conalep_pos/themes/themes.dart';
|
|
|
+import 'package:conalep_pos/viewmodels/viewmodels.dart';
|
|
|
+import 'package:conalep_pos/views/pedido_mesa/pedido_mesa_detalle.dart';
|
|
|
+import 'package:conalep_pos/views/pedido_mesa/pedido_mesa_form.dart';
|
|
|
+import 'package:conalep_pos/widgets/widgets.dart';
|
|
|
+import 'package:flutter/material.dart';
|
|
|
+import 'package:provider/provider.dart';
|
|
|
+import '../../widgets/widgets_components.dart' as clase;
|
|
|
+
|
|
|
+class PedidoMesaScreen extends StatefulWidget {
|
|
|
+ const PedidoMesaScreen({Key? key}) : super(key: key);
|
|
|
+
|
|
|
+ @override
|
|
|
+ State<PedidoMesaScreen> createState() => _PedidoMesaScreenState();
|
|
|
+}
|
|
|
+
|
|
|
+class _PedidoMesaScreenState extends State<PedidoMesaScreen> {
|
|
|
+ final _busqueda = TextEditingController(text: '');
|
|
|
+ DateTime? fechaInicio;
|
|
|
+ DateTime? fechaFin;
|
|
|
+ ScrollController horizontalScrollController = ScrollController();
|
|
|
+
|
|
|
+ @override
|
|
|
+ void initState() {
|
|
|
+ super.initState();
|
|
|
+ WidgetsBinding.instance.addPostFrameCallback((_) {
|
|
|
+ Provider.of<PedidoViewModel>(context, listen: false)
|
|
|
+ .fetchLocalMesaPedidosForScreen();
|
|
|
+ });
|
|
|
+ }
|
|
|
+
|
|
|
+ // void exportCSV() async {
|
|
|
+ // final pedidosViewModel =
|
|
|
+ // Provider.of<PedidoViewModel>(context, listen: false);
|
|
|
+ // List<Pedido> pedidosConProductos = [];
|
|
|
+
|
|
|
+ // for (Pedido pedido in pedidosViewModel.pedidos) {
|
|
|
+ // Pedido? pedidoConProductos =
|
|
|
+ // await pedidosViewModel.fetchPedidoConProductos(pedido.id);
|
|
|
+ // if (pedidoConProductos != null) {
|
|
|
+ // pedidosConProductos.add(pedidoConProductos);
|
|
|
+ // }
|
|
|
+ // }
|
|
|
+
|
|
|
+ // if (pedidosConProductos.isNotEmpty) {
|
|
|
+ // String fileName = 'Pedidos_OlivaMia_POS';
|
|
|
+ // if (fechaInicio != null && fechaFin != null) {
|
|
|
+ // String startDateStr = DateFormat('dd-MM-yyyy').format(fechaInicio!);
|
|
|
+ // String endDateStr = DateFormat('dd-MM-yyyy').format(fechaFin!);
|
|
|
+ // fileName += '_${startDateStr}_al_${endDateStr}';
|
|
|
+ // }
|
|
|
+ // fileName += '.csv';
|
|
|
+
|
|
|
+ // await exportarPedidosACSV(pedidosConProductos, fileName);
|
|
|
+ // ScaffoldMessenger.of(context).showSnackBar(SnackBar(
|
|
|
+ // content: Text('Archivo CSV descargado! Archivo: $fileName')));
|
|
|
+ // } else {
|
|
|
+ // ScaffoldMessenger.of(context).showSnackBar(
|
|
|
+ // SnackBar(content: Text('No hay pedidos para exportar.')));
|
|
|
+ // }
|
|
|
+ // }
|
|
|
+
|
|
|
+ void clearSearchAndReset() {
|
|
|
+ setState(() {
|
|
|
+ _busqueda.clear();
|
|
|
+ fechaInicio = null;
|
|
|
+ fechaFin = null;
|
|
|
+ Provider.of<PedidoViewModel>(context, listen: false)
|
|
|
+ .fetchLocalPedidosForScreen();
|
|
|
+ });
|
|
|
+ }
|
|
|
+
|
|
|
+ void go(Pedido item) async {
|
|
|
+ Pedido? pedidoCompleto =
|
|
|
+ await Provider.of<PedidoViewModel>(context, listen: false)
|
|
|
+ .fetchPedidoConProductos(item.id);
|
|
|
+ if (pedidoCompleto != null) {
|
|
|
+ if (pedidoCompleto.estatus == 'EN PROCESO') {
|
|
|
+ Navigator.push(
|
|
|
+ context,
|
|
|
+ MaterialPageRoute(
|
|
|
+ builder: (context) =>
|
|
|
+ PedidoMesaForm(pedido: pedidoCompleto),
|
|
|
+ ),
|
|
|
+ );
|
|
|
+ } else {
|
|
|
+ Navigator.push(
|
|
|
+ context,
|
|
|
+ MaterialPageRoute(
|
|
|
+ builder: (context) =>
|
|
|
+ PedidoMesaDetalleScreen(pedido: pedidoCompleto),
|
|
|
+ ),
|
|
|
+ );
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ print("Error al cargar el pedido con productos");
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ @override
|
|
|
+ Widget build(BuildContext context) {
|
|
|
+ final pvm = Provider.of<PedidoViewModel>(context);
|
|
|
+ double screenWidth = MediaQuery.of(context).size.width;
|
|
|
+ final isMobile = screenWidth < 1250;
|
|
|
+ final double? columnSpacing = isMobile ? null : 0;
|
|
|
+ TextStyle estilo = const TextStyle(fontWeight: FontWeight.bold);
|
|
|
+ List<DataRow> registros = [];
|
|
|
+ for (Pedido item in pvm.pedidos) {
|
|
|
+ registros.add(DataRow(cells: [
|
|
|
+ DataCell(
|
|
|
+ Row(mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [
|
|
|
+ PopupMenuButton(
|
|
|
+ itemBuilder: (context) => [
|
|
|
+ PopupMenuItem(
|
|
|
+ child: const Text('Detalle'),
|
|
|
+ onTap: () => go(item),
|
|
|
+ ),
|
|
|
+ PopupMenuItem(
|
|
|
+ child: const Text('Cancelar Pedido'),
|
|
|
+ onTap: () async {
|
|
|
+ bool confirmado = await showDialog<bool>(
|
|
|
+ context: context,
|
|
|
+ builder: (context) {
|
|
|
+ return AlertDialog(
|
|
|
+ title: const Text("Cancelar Pedido",
|
|
|
+ style: TextStyle(
|
|
|
+ fontWeight: FontWeight.w500, fontSize: 22)),
|
|
|
+ content: const Text(
|
|
|
+ '¿Estás seguro de que deseas cancelar este pedido?',
|
|
|
+ style: TextStyle(fontSize: 18)),
|
|
|
+ actions: [
|
|
|
+ Row(
|
|
|
+ mainAxisAlignment:
|
|
|
+ MainAxisAlignment.spaceBetween,
|
|
|
+ children: [
|
|
|
+ TextButton(
|
|
|
+ onPressed: () =>
|
|
|
+ Navigator.of(context).pop(false),
|
|
|
+ child: const Text('No',
|
|
|
+ style: TextStyle(fontSize: 18)),
|
|
|
+ style: ButtonStyle(
|
|
|
+ padding: MaterialStatePropertyAll(
|
|
|
+ EdgeInsets.fromLTRB(
|
|
|
+ 20, 10, 20, 10)),
|
|
|
+ backgroundColor:
|
|
|
+ MaterialStatePropertyAll(
|
|
|
+ Colors.red),
|
|
|
+ foregroundColor:
|
|
|
+ MaterialStatePropertyAll(
|
|
|
+ AppTheme.secondary)),
|
|
|
+ ),
|
|
|
+ TextButton(
|
|
|
+ onPressed: () =>
|
|
|
+ Navigator.of(context).pop(true),
|
|
|
+ child: const Text('Sí',
|
|
|
+ style: TextStyle(fontSize: 18)),
|
|
|
+ style: ButtonStyle(
|
|
|
+ padding: MaterialStatePropertyAll(
|
|
|
+ EdgeInsets.fromLTRB(
|
|
|
+ 20, 10, 20, 10)),
|
|
|
+ backgroundColor:
|
|
|
+ MaterialStatePropertyAll(
|
|
|
+ AppTheme.tertiary),
|
|
|
+ foregroundColor:
|
|
|
+ MaterialStatePropertyAll(
|
|
|
+ AppTheme.quaternary)),
|
|
|
+ ),
|
|
|
+ ],
|
|
|
+ )
|
|
|
+ ],
|
|
|
+ );
|
|
|
+ },
|
|
|
+ ) ??
|
|
|
+ false;
|
|
|
+
|
|
|
+ if (confirmado) {
|
|
|
+ await Provider.of<PedidoViewModel>(context, listen: false)
|
|
|
+ .cancelarPedido(item.id);
|
|
|
+ ScaffoldMessenger.of(context).showSnackBar(SnackBar(
|
|
|
+ content: Text("Pedido cancelado correctamente")));
|
|
|
+ }
|
|
|
+ },
|
|
|
+ )
|
|
|
+ ],
|
|
|
+ icon: const Icon(Icons.more_vert),
|
|
|
+ )
|
|
|
+ ])),
|
|
|
+ DataCell(
|
|
|
+ Text(item.id.toString()),
|
|
|
+ onTap: () => go(item),
|
|
|
+ ),
|
|
|
+ DataCell(
|
|
|
+ Text(item.folio.toString()),
|
|
|
+ onTap: () => go(item),
|
|
|
+ ),
|
|
|
+ DataCell(
|
|
|
+ Text(item.idLocal.toString()),
|
|
|
+ onTap: () => go(item),
|
|
|
+ ),
|
|
|
+ DataCell(
|
|
|
+ Text(item.nombreCliente ?? "Sin nombre"),
|
|
|
+ onTap: () => go(item),
|
|
|
+ ),
|
|
|
+ DataCell(
|
|
|
+ Text(item.comentarios ?? "Sin comentarios"),
|
|
|
+ onTap: () => go(item),
|
|
|
+ ),
|
|
|
+ DataCell(
|
|
|
+ Text(item.estatus ?? "Sin Estatus"),
|
|
|
+ onTap: () => go(item),
|
|
|
+ ),
|
|
|
+ DataCell(
|
|
|
+ Text(item.peticion ?? "Sin fecha"),
|
|
|
+ onTap: () => go(item),
|
|
|
+ ),
|
|
|
+ ]));
|
|
|
+ }
|
|
|
+
|
|
|
+ return Scaffold(
|
|
|
+ appBar: AppBar(
|
|
|
+ title: Text(
|
|
|
+ 'Pedidos de Mesa',
|
|
|
+ style: TextStyle(
|
|
|
+ color: AppTheme.secondary, fontWeight: FontWeight.w500),
|
|
|
+ ),
|
|
|
+ // actions: <Widget>[
|
|
|
+ // IconButton(
|
|
|
+ // icon: const Icon(Icons.save_alt),
|
|
|
+ // onPressed: exportCSV,
|
|
|
+ // tooltip: 'Exportar a CSV',
|
|
|
+ // ),
|
|
|
+ // ],
|
|
|
+ iconTheme: IconThemeData(color: AppTheme.secondary)),
|
|
|
+ floatingActionButton: FloatingActionButton.extended(
|
|
|
+ onPressed: () async {
|
|
|
+ await Navigator.push(
|
|
|
+ context,
|
|
|
+ MaterialPageRoute(
|
|
|
+ builder: (context) => PedidoMesaForm(),
|
|
|
+ ),
|
|
|
+ ).then((_) => Provider.of<PedidoViewModel>(context, listen: false)
|
|
|
+ .fetchLocalPedidosForScreen());
|
|
|
+ },
|
|
|
+ icon: Icon(Icons.add, size: 30, color: AppTheme.quaternary),
|
|
|
+ label: Text(
|
|
|
+ "Agregar Pedido",
|
|
|
+ style: TextStyle(fontSize: 20, color: AppTheme.quaternary),
|
|
|
+ ),
|
|
|
+ shape: RoundedRectangleBorder(
|
|
|
+ borderRadius: BorderRadius.circular(8),
|
|
|
+ ),
|
|
|
+ materialTapTargetSize: MaterialTapTargetSize.shrinkWrap,
|
|
|
+ backgroundColor: AppTheme.tertiary,
|
|
|
+ ),
|
|
|
+ body: Column(
|
|
|
+ children: [
|
|
|
+ Expanded(
|
|
|
+ child: ListView(
|
|
|
+ padding: const EdgeInsets.fromLTRB(8, 0, 8, 0),
|
|
|
+ children: [
|
|
|
+ const SizedBox(height: 8),
|
|
|
+ clase.tarjeta(
|
|
|
+ Padding(
|
|
|
+ padding: const EdgeInsets.all(8.0),
|
|
|
+ child: LayoutBuilder(
|
|
|
+ builder: (context, constraints) {
|
|
|
+ if (screenWidth > 1000) {
|
|
|
+ return Row(
|
|
|
+ crossAxisAlignment: CrossAxisAlignment.end,
|
|
|
+ children: [
|
|
|
+ Expanded(
|
|
|
+ flex: 7,
|
|
|
+ child: _buildDateRangePicker(),
|
|
|
+ ),
|
|
|
+ const SizedBox(width: 5),
|
|
|
+ botonBuscar()
|
|
|
+ ],
|
|
|
+ );
|
|
|
+ } else {
|
|
|
+ return Column(
|
|
|
+ children: [
|
|
|
+ Row(
|
|
|
+ children: [_buildDateRangePicker()],
|
|
|
+ ),
|
|
|
+ Row(
|
|
|
+ children: [botonBuscar()],
|
|
|
+ ),
|
|
|
+ ],
|
|
|
+ );
|
|
|
+ }
|
|
|
+ },
|
|
|
+ ),
|
|
|
+ ),
|
|
|
+ ),
|
|
|
+ const SizedBox(height: 8),
|
|
|
+ pvm.isLoading
|
|
|
+ ? const Center(child: CircularProgressIndicator())
|
|
|
+ : Container(),
|
|
|
+ clase.tarjeta(
|
|
|
+ Column(
|
|
|
+ children: [
|
|
|
+ LayoutBuilder(builder: (context, constraints) {
|
|
|
+ return SingleChildScrollView(
|
|
|
+ scrollDirection: Axis.vertical,
|
|
|
+ child: Scrollbar(
|
|
|
+ controller: horizontalScrollController,
|
|
|
+ interactive: true,
|
|
|
+ thumbVisibility: true,
|
|
|
+ thickness: 10.0,
|
|
|
+ child: SingleChildScrollView(
|
|
|
+ controller: horizontalScrollController,
|
|
|
+ scrollDirection: Axis.horizontal,
|
|
|
+ child: ConstrainedBox(
|
|
|
+ constraints: BoxConstraints(
|
|
|
+ minWidth: isMobile
|
|
|
+ ? constraints.maxWidth
|
|
|
+ : screenWidth),
|
|
|
+ child: DataTable(
|
|
|
+ columnSpacing: columnSpacing,
|
|
|
+ sortAscending: true,
|
|
|
+ sortColumnIndex: 1,
|
|
|
+ columns: [
|
|
|
+ DataColumn(label: Text(" ", style: estilo)),
|
|
|
+ DataColumn(
|
|
|
+ label: Text("ID", style: estilo)),
|
|
|
+ DataColumn(
|
|
|
+ label: Text("FOLIO", style: estilo)),
|
|
|
+ DataColumn(
|
|
|
+ label: Text("IDLOCAL", style: estilo)),
|
|
|
+ DataColumn(
|
|
|
+ label: Text("NOMBRE", style: estilo)),
|
|
|
+ DataColumn(
|
|
|
+ label:
|
|
|
+ Text("COMENTARIOS", style: estilo)),
|
|
|
+ DataColumn(
|
|
|
+ label: Text("ESTATUS", style: estilo)),
|
|
|
+ DataColumn(
|
|
|
+ label: Text("FECHA", style: estilo)),
|
|
|
+ ],
|
|
|
+ rows: registros,
|
|
|
+ ),
|
|
|
+ ),
|
|
|
+ ),
|
|
|
+ ),
|
|
|
+ );
|
|
|
+ }),
|
|
|
+ ],
|
|
|
+ ),
|
|
|
+ ),
|
|
|
+ const SizedBox(height: 15),
|
|
|
+ if (!pvm.isLoading)
|
|
|
+ Row(
|
|
|
+ mainAxisAlignment: MainAxisAlignment.center,
|
|
|
+ children: [
|
|
|
+ TextButton(
|
|
|
+ onPressed:
|
|
|
+ pvm.currentPage > 1 ? pvm.previousPage : null,
|
|
|
+ child: Text('Anterior'),
|
|
|
+ style: ButtonStyle(
|
|
|
+ backgroundColor:
|
|
|
+ MaterialStateProperty.resolveWith<Color?>(
|
|
|
+ (Set<MaterialState> states) {
|
|
|
+ if (states.contains(MaterialState.disabled)) {
|
|
|
+ return Colors.grey;
|
|
|
+ }
|
|
|
+ return AppTheme.tertiary;
|
|
|
+ },
|
|
|
+ ),
|
|
|
+ foregroundColor:
|
|
|
+ MaterialStateProperty.resolveWith<Color?>(
|
|
|
+ (Set<MaterialState> states) {
|
|
|
+ if (states.contains(MaterialState.disabled)) {
|
|
|
+ return Colors.black;
|
|
|
+ }
|
|
|
+ return Colors.white;
|
|
|
+ },
|
|
|
+ ),
|
|
|
+ ),
|
|
|
+ ),
|
|
|
+ SizedBox(width: 15),
|
|
|
+ Text('Página ${pvm.currentPage} de ${pvm.totalPages}'),
|
|
|
+ SizedBox(width: 15),
|
|
|
+ TextButton(
|
|
|
+ onPressed: pvm.currentPage < pvm.totalPages
|
|
|
+ ? pvm.nextPage
|
|
|
+ : null,
|
|
|
+ child: Text('Siguiente'),
|
|
|
+ style: ButtonStyle(
|
|
|
+ backgroundColor:
|
|
|
+ MaterialStateProperty.resolveWith<Color?>(
|
|
|
+ (Set<MaterialState> states) {
|
|
|
+ if (states.contains(MaterialState.disabled)) {
|
|
|
+ return Colors.grey;
|
|
|
+ }
|
|
|
+ return AppTheme.tertiary;
|
|
|
+ },
|
|
|
+ ),
|
|
|
+ foregroundColor:
|
|
|
+ MaterialStateProperty.resolveWith<Color?>(
|
|
|
+ (Set<MaterialState> states) {
|
|
|
+ if (states.contains(MaterialState.disabled)) {
|
|
|
+ return Colors.black;
|
|
|
+ }
|
|
|
+ return Colors.white;
|
|
|
+ },
|
|
|
+ ),
|
|
|
+ ),
|
|
|
+ ),
|
|
|
+ ],
|
|
|
+ ),
|
|
|
+ const SizedBox(height: 15),
|
|
|
+ ],
|
|
|
+ ),
|
|
|
+ ),
|
|
|
+ ],
|
|
|
+ ),
|
|
|
+ );
|
|
|
+ }
|
|
|
+
|
|
|
+ Widget _buildDateRangePicker() {
|
|
|
+ return Row(
|
|
|
+ children: [
|
|
|
+ Expanded(
|
|
|
+ flex: 3,
|
|
|
+ child: AppTextField(
|
|
|
+ prefixIcon: const Icon(Icons.search),
|
|
|
+ etiqueta: 'Búsqueda por folio...',
|
|
|
+ controller: _busqueda,
|
|
|
+ hintText: 'Búsqueda por folio...',
|
|
|
+ ),
|
|
|
+ ),
|
|
|
+ const SizedBox(width: 5),
|
|
|
+ Expanded(
|
|
|
+ flex: 3,
|
|
|
+ child: clase.FechaSelectWidget(
|
|
|
+ fecha: fechaInicio,
|
|
|
+ onFechaChanged: (d) {
|
|
|
+ setState(() {
|
|
|
+ fechaInicio = d;
|
|
|
+ });
|
|
|
+ },
|
|
|
+ etiqueta: "Fecha Inicial",
|
|
|
+ context: context,
|
|
|
+ ),
|
|
|
+ ),
|
|
|
+ const SizedBox(width: 5),
|
|
|
+ Expanded(
|
|
|
+ flex: 3,
|
|
|
+ child: clase.FechaSelectWidget(
|
|
|
+ fecha: fechaFin,
|
|
|
+ onFechaChanged: (d) {
|
|
|
+ setState(() {
|
|
|
+ fechaFin = d;
|
|
|
+ });
|
|
|
+ },
|
|
|
+ etiqueta: "Fecha Final",
|
|
|
+ context: context,
|
|
|
+ ),
|
|
|
+ ),
|
|
|
+ ],
|
|
|
+ );
|
|
|
+ }
|
|
|
+
|
|
|
+ Widget botonBuscar() {
|
|
|
+ return Expanded(
|
|
|
+ flex: 2,
|
|
|
+ child: Row(
|
|
|
+ children: [
|
|
|
+ Expanded(
|
|
|
+ flex: 2,
|
|
|
+ child: Padding(
|
|
|
+ padding: const EdgeInsets.only(bottom: 5),
|
|
|
+ child: ElevatedButton(
|
|
|
+ onPressed: clearSearchAndReset,
|
|
|
+ style: ElevatedButton.styleFrom(
|
|
|
+ shape: RoundedRectangleBorder(
|
|
|
+ borderRadius: BorderRadius.circular(20.0),
|
|
|
+ ),
|
|
|
+ backgroundColor: AppTheme.tertiary,
|
|
|
+ padding: const EdgeInsets.symmetric(vertical: 25),
|
|
|
+ ),
|
|
|
+ child: Text('Limpiar',
|
|
|
+ style: TextStyle(color: AppTheme.quaternary)),
|
|
|
+ ),
|
|
|
+ ),
|
|
|
+ ),
|
|
|
+ const SizedBox(width: 8),
|
|
|
+ Expanded(
|
|
|
+ flex: 2,
|
|
|
+ child: Padding(
|
|
|
+ padding: const EdgeInsets.only(bottom: 5),
|
|
|
+ child: ElevatedButton(
|
|
|
+ onPressed: () async {
|
|
|
+ if (_busqueda.text.isNotEmpty) {
|
|
|
+ await Provider.of<PedidoViewModel>(context, listen: false)
|
|
|
+ .buscarPedidosPorFolio(_busqueda.text.trim());
|
|
|
+ } else if (fechaInicio != null && fechaFin != null) {
|
|
|
+ await Provider.of<PedidoViewModel>(context, listen: false)
|
|
|
+ .buscarPedidosPorFecha(fechaInicio!, fechaFin!);
|
|
|
+ } else {
|
|
|
+ ScaffoldMessenger.of(context).showSnackBar(const SnackBar(
|
|
|
+ content: Text(
|
|
|
+ 'Introduce un folio o selecciona un rango de fechas para buscar.')));
|
|
|
+ }
|
|
|
+ },
|
|
|
+ style: ElevatedButton.styleFrom(
|
|
|
+ shape: RoundedRectangleBorder(
|
|
|
+ borderRadius: BorderRadius.circular(20.0),
|
|
|
+ ),
|
|
|
+ backgroundColor: AppTheme.tertiary,
|
|
|
+ padding: const EdgeInsets.symmetric(vertical: 25),
|
|
|
+ ),
|
|
|
+ child: Text('Buscar',
|
|
|
+ style: TextStyle(color: AppTheme.quaternary)),
|
|
|
+ ),
|
|
|
+ ),
|
|
|
+ ),
|
|
|
+ ],
|
|
|
+ ));
|
|
|
+ }
|
|
|
+}
|