|
@@ -1,14 +1,18 @@
|
|
import 'package:flutter/material.dart';
|
|
import 'package:flutter/material.dart';
|
|
import 'package:flutter/services.dart';
|
|
import 'package:flutter/services.dart';
|
|
import 'package:provider/provider.dart';
|
|
import 'package:provider/provider.dart';
|
|
|
|
+import '../../models/models.dart';
|
|
import '../../themes/themes.dart';
|
|
import '../../themes/themes.dart';
|
|
import 'package:intl/intl.dart';
|
|
import 'package:intl/intl.dart';
|
|
import '../../models/corte_caja_model.dart';
|
|
import '../../models/corte_caja_model.dart';
|
|
import '../../viewmodels/corte_caja_view_model.dart';
|
|
import '../../viewmodels/corte_caja_view_model.dart';
|
|
|
|
+import '../../viewmodels/viewmodels.dart';
|
|
import '../../widgets/app_textfield.dart';
|
|
import '../../widgets/app_textfield.dart';
|
|
import '../../widgets/widgets.dart';
|
|
import '../../widgets/widgets.dart';
|
|
import 'dart:async';
|
|
import 'dart:async';
|
|
|
|
|
|
|
|
+import '../pedido/pedido_screen.dart';
|
|
|
|
+
|
|
class CorteCajaForm extends StatefulWidget {
|
|
class CorteCajaForm extends StatefulWidget {
|
|
final String? corteCajaId;
|
|
final String? corteCajaId;
|
|
|
|
|
|
@@ -72,11 +76,9 @@ class _CorteCajaFormState extends State<CorteCajaForm> {
|
|
? formatNumber(viewModel.selectedCorte!.fondoDiaSig!)
|
|
? formatNumber(viewModel.selectedCorte!.fondoDiaSig!)
|
|
: '';
|
|
: '';
|
|
|
|
|
|
- if (fechaApertura != null) {
|
|
|
|
- viewModel.cargarVentasDesdeFechaApertura(fechaApertura!).then((_) {
|
|
|
|
- viewModel.calcularCorteFinal();
|
|
|
|
- });
|
|
|
|
- }
|
|
|
|
|
|
+ viewModel.cargarVentasPorCorteId(corte.id!).then((_) {
|
|
|
|
+ viewModel.calcularCorteFinal();
|
|
|
|
+ });
|
|
});
|
|
});
|
|
});
|
|
});
|
|
}
|
|
}
|
|
@@ -109,6 +111,163 @@ class _CorteCajaFormState extends State<CorteCajaForm> {
|
|
});
|
|
});
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+ void _mostrarDialogoPedidosNuevos(
|
|
|
|
+ BuildContext context,
|
|
|
|
+ List<Pedido> pedidos, {
|
|
|
|
+ required Function(List<Pedido>) onAbrirNuevaCaja,
|
|
|
|
+ }) {
|
|
|
|
+ showDialog(
|
|
|
|
+ context: context,
|
|
|
|
+ builder: (ctx) {
|
|
|
|
+ return AlertDialog(
|
|
|
|
+ title: Text(
|
|
|
|
+ 'Atención: Aún cuentas con pedidos sin terminar.',
|
|
|
|
+ style: TextStyle(fontWeight: FontWeight.bold),
|
|
|
|
+ ),
|
|
|
|
+ content: SingleChildScrollView(
|
|
|
|
+ child: Column(
|
|
|
|
+ crossAxisAlignment: CrossAxisAlignment.start,
|
|
|
|
+ children: [
|
|
|
|
+ Text(
|
|
|
|
+ '¿Deseas terminar los pedidos o abrir una nueva caja con ellos?',
|
|
|
|
+ style: TextStyle(fontSize: 18, fontWeight: FontWeight.w600),
|
|
|
|
+ ),
|
|
|
|
+ SizedBox(height: 10),
|
|
|
|
+ Text(
|
|
|
|
+ 'Pedidos pendientes:',
|
|
|
|
+ style: TextStyle(fontSize: 16, fontWeight: FontWeight.w600),
|
|
|
|
+ ),
|
|
|
|
+ SizedBox(height: 5),
|
|
|
|
+ Column(
|
|
|
|
+ children: pedidos.map((p) {
|
|
|
|
+ return Row(
|
|
|
|
+ mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
|
|
|
+ children: [
|
|
|
|
+ Text(
|
|
|
|
+ 'Folio: ${p.folio}',
|
|
|
|
+ style: TextStyle(
|
|
|
|
+ fontSize: 16, fontWeight: FontWeight.w600),
|
|
|
|
+ ),
|
|
|
|
+ Text(
|
|
|
|
+ '\$${p.totalPedido?.toStringAsFixed(2) ?? '0.00'}',
|
|
|
|
+ style: TextStyle(
|
|
|
|
+ fontSize: 16, fontWeight: FontWeight.w600),
|
|
|
|
+ ),
|
|
|
|
+ ],
|
|
|
|
+ );
|
|
|
|
+ }).toList(),
|
|
|
|
+ ),
|
|
|
|
+ ],
|
|
|
|
+ ),
|
|
|
|
+ ),
|
|
|
|
+ actions: [
|
|
|
|
+ TextButton(
|
|
|
|
+ style: ButtonStyle(
|
|
|
|
+ backgroundColor: WidgetStatePropertyAll(AppTheme.rojo),
|
|
|
|
+ foregroundColor: WidgetStatePropertyAll(AppTheme.quaternary),
|
|
|
|
+ ),
|
|
|
|
+ child: Text(
|
|
|
|
+ 'Terminar mis pedidos primero',
|
|
|
|
+ style: TextStyle(fontSize: 16),
|
|
|
|
+ ),
|
|
|
|
+ onPressed: () {
|
|
|
|
+ Navigator.of(ctx).pop();
|
|
|
|
+ Navigator.push(
|
|
|
|
+ context,
|
|
|
|
+ MaterialPageRoute(
|
|
|
|
+ builder: (context) => PedidoScreen(),
|
|
|
|
+ ),
|
|
|
|
+ );
|
|
|
|
+ },
|
|
|
|
+ ),
|
|
|
|
+ TextButton(
|
|
|
|
+ style: ButtonStyle(
|
|
|
|
+ backgroundColor: WidgetStatePropertyAll(AppTheme.verde),
|
|
|
|
+ foregroundColor: WidgetStatePropertyAll(AppTheme.quaternary)),
|
|
|
|
+ child: Text(
|
|
|
|
+ 'Abrir una caja nueva con esos pedidos',
|
|
|
|
+ style: TextStyle(fontSize: 16),
|
|
|
|
+ ),
|
|
|
|
+ onPressed: () {
|
|
|
|
+ Navigator.of(ctx).pop();
|
|
|
|
+ onAbrirNuevaCaja(pedidos);
|
|
|
|
+ },
|
|
|
|
+ ),
|
|
|
|
+ ],
|
|
|
|
+ );
|
|
|
|
+ },
|
|
|
|
+ );
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ void _mostrarDialogoDespuesDe6pm(
|
|
|
|
+ BuildContext context,
|
|
|
|
+ List<Pedido> pedidos,
|
|
|
|
+ ) {
|
|
|
|
+ showDialog(
|
|
|
|
+ context: context,
|
|
|
|
+ builder: (ctx) {
|
|
|
|
+ return AlertDialog(
|
|
|
|
+ title: Text(
|
|
|
|
+ 'Pedidos sin terminar',
|
|
|
|
+ style: TextStyle(fontWeight: FontWeight.bold),
|
|
|
|
+ ),
|
|
|
|
+ content: SingleChildScrollView(
|
|
|
|
+ child: Column(
|
|
|
|
+ crossAxisAlignment: CrossAxisAlignment.start,
|
|
|
|
+ children: [
|
|
|
|
+ Text(
|
|
|
|
+ 'Debes terminar estos pedidos para poder realizar el corte:',
|
|
|
|
+ style: TextStyle(fontSize: 18, fontWeight: FontWeight.w600),
|
|
|
|
+ ),
|
|
|
|
+ SizedBox(height: 10),
|
|
|
|
+ Column(
|
|
|
|
+ children: pedidos.map((p) {
|
|
|
|
+ return Row(
|
|
|
|
+ mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
|
|
|
+ children: [
|
|
|
|
+ Text(
|
|
|
|
+ 'Folio: ${p.folio}',
|
|
|
|
+ style: TextStyle(
|
|
|
|
+ fontSize: 16, fontWeight: FontWeight.w600),
|
|
|
|
+ ),
|
|
|
|
+ Text(
|
|
|
|
+ '\$${p.totalPedido?.toStringAsFixed(2) ?? '0.00'}',
|
|
|
|
+ style: TextStyle(
|
|
|
|
+ fontSize: 16, fontWeight: FontWeight.w600),
|
|
|
|
+ ),
|
|
|
|
+ ],
|
|
|
|
+ );
|
|
|
|
+ }).toList(),
|
|
|
|
+ ),
|
|
|
|
+ ],
|
|
|
|
+ ),
|
|
|
|
+ ),
|
|
|
|
+ actions: [
|
|
|
|
+ TextButton(
|
|
|
|
+ style: ButtonStyle(
|
|
|
|
+ backgroundColor: WidgetStatePropertyAll(AppTheme.rojo),
|
|
|
|
+ foregroundColor: WidgetStatePropertyAll(AppTheme.quaternary),
|
|
|
|
+ ),
|
|
|
|
+ child: Text(
|
|
|
|
+ 'Terminar mis pedidos',
|
|
|
|
+ style: TextStyle(fontSize: 16),
|
|
|
|
+ ),
|
|
|
|
+ onPressed: () {
|
|
|
|
+ Navigator.of(ctx).pop();
|
|
|
|
+ Navigator.push(
|
|
|
|
+ context,
|
|
|
|
+ MaterialPageRoute(
|
|
|
|
+ builder: (context) => PedidoScreen(),
|
|
|
|
+ ),
|
|
|
|
+ );
|
|
|
|
+ },
|
|
|
|
+ ),
|
|
|
|
+ ],
|
|
|
|
+ );
|
|
|
|
+ },
|
|
|
|
+ );
|
|
|
|
+ }
|
|
|
|
+
|
|
@override
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
Widget build(BuildContext context) {
|
|
final viewModel = Provider.of<CorteCajaViewModel>(context);
|
|
final viewModel = Provider.of<CorteCajaViewModel>(context);
|
|
@@ -317,13 +476,19 @@ class _CorteCajaFormState extends State<CorteCajaForm> {
|
|
trailing: IconButton(
|
|
trailing: IconButton(
|
|
icon: Icon(Icons.remove_circle_outline,
|
|
icon: Icon(Icons.remove_circle_outline,
|
|
color: Colors.red),
|
|
color: Colors.red),
|
|
- onPressed: () {
|
|
|
|
- cuadroConfirmacion(
|
|
|
|
- context,
|
|
|
|
- etiqueta:
|
|
|
|
- "¿Estás seguro de eliminar el depósito?",
|
|
|
|
- onConfirm: () {
|
|
|
|
- viewModel.eliminarDeposito(deposito.id!);
|
|
|
|
|
|
+ onPressed: () async {
|
|
|
|
+ showDialog(
|
|
|
|
+ context: context,
|
|
|
|
+ builder: (context) {
|
|
|
|
+ return TotpCuadroConfirmacion(
|
|
|
|
+ title: "Eliminar Deposito",
|
|
|
|
+ content:
|
|
|
|
+ "Por favor, ingresa el código de autenticación para continuar.",
|
|
|
|
+ onSuccess: () {
|
|
|
|
+ viewModel
|
|
|
|
+ .eliminarDeposito(deposito.id!);
|
|
|
|
+ },
|
|
|
|
+ );
|
|
},
|
|
},
|
|
);
|
|
);
|
|
},
|
|
},
|
|
@@ -406,13 +571,18 @@ class _CorteCajaFormState extends State<CorteCajaForm> {
|
|
trailing: IconButton(
|
|
trailing: IconButton(
|
|
icon: Icon(Icons.remove_circle_outline,
|
|
icon: Icon(Icons.remove_circle_outline,
|
|
color: Colors.red),
|
|
color: Colors.red),
|
|
- onPressed: () {
|
|
|
|
- cuadroConfirmacion(
|
|
|
|
- context,
|
|
|
|
- etiqueta:
|
|
|
|
- "¿Estás seguro de eliminar el retiro?",
|
|
|
|
- onConfirm: () {
|
|
|
|
- viewModel.eliminarRetiro(retiro.id!);
|
|
|
|
|
|
+ onPressed: () async {
|
|
|
|
+ showDialog(
|
|
|
|
+ context: context,
|
|
|
|
+ builder: (context) {
|
|
|
|
+ return TotpCuadroConfirmacion(
|
|
|
|
+ title: "Eliminar Retiro",
|
|
|
|
+ content:
|
|
|
|
+ "Por favor, ingresa el código de autenticación para continuar.",
|
|
|
|
+ onSuccess: () {
|
|
|
|
+ viewModel.eliminarRetiro(retiro.id!);
|
|
|
|
+ },
|
|
|
|
+ );
|
|
},
|
|
},
|
|
);
|
|
);
|
|
},
|
|
},
|
|
@@ -494,13 +664,18 @@ class _CorteCajaFormState extends State<CorteCajaForm> {
|
|
trailing: IconButton(
|
|
trailing: IconButton(
|
|
icon: Icon(Icons.remove_circle_outline,
|
|
icon: Icon(Icons.remove_circle_outline,
|
|
color: Colors.red),
|
|
color: Colors.red),
|
|
- onPressed: () {
|
|
|
|
- cuadroConfirmacion(
|
|
|
|
- context,
|
|
|
|
- etiqueta:
|
|
|
|
- "¿Estás seguro de eliminar el gasto?",
|
|
|
|
- onConfirm: () {
|
|
|
|
- viewModel.eliminarGasto(gasto.id!);
|
|
|
|
|
|
+ onPressed: () async {
|
|
|
|
+ showDialog(
|
|
|
|
+ context: context,
|
|
|
|
+ builder: (context) {
|
|
|
|
+ return TotpCuadroConfirmacion(
|
|
|
|
+ title: "Eliminar Gasto",
|
|
|
|
+ content:
|
|
|
|
+ "Por favor, ingresa el código de autenticación para continuar.",
|
|
|
|
+ onSuccess: () {
|
|
|
|
+ viewModel.eliminarGasto(gasto.id!);
|
|
|
|
+ },
|
|
|
|
+ );
|
|
},
|
|
},
|
|
);
|
|
);
|
|
},
|
|
},
|
|
@@ -584,15 +759,61 @@ class _CorteCajaFormState extends State<CorteCajaForm> {
|
|
const SizedBox(width: 15),
|
|
const SizedBox(width: 15),
|
|
if (viewModel.selectedCorte?.modificado != null)
|
|
if (viewModel.selectedCorte?.modificado != null)
|
|
boton('Realizar Corte', () async {
|
|
boton('Realizar Corte', () async {
|
|
- cuadroConfirmacion(
|
|
|
|
- context,
|
|
|
|
- etiqueta: "¿Estás seguro realizar el corte?",
|
|
|
|
- onConfirm: () async {
|
|
|
|
- await viewModel.guardarCorte(esCorte: true);
|
|
|
|
- Navigator.pop(context);
|
|
|
|
- await viewModel.imprimirCorteCajaTicket();
|
|
|
|
- },
|
|
|
|
- );
|
|
|
|
|
|
+ // 1) Verificar si hay pedidos con estatus NUEVO de este corte
|
|
|
|
+ final pedidosViewModel = Provider.of<PedidoViewModel>(
|
|
|
|
+ context,
|
|
|
|
+ listen: false);
|
|
|
|
+ List<Pedido> pedidosNuevos =
|
|
|
|
+ await pedidosViewModel.fetchPedidosNuevosByCorteId(
|
|
|
|
+ viewModel.selectedCorte!.id!);
|
|
|
|
+
|
|
|
|
+ // 2) Si no hay pedidos con estatus NUEVO se continúa flujo normal
|
|
|
|
+ if (pedidosNuevos.isEmpty) {
|
|
|
|
+ cuadroConfirmacion(
|
|
|
|
+ context,
|
|
|
|
+ etiqueta: "¿Estás seguro de realizar el corte?",
|
|
|
|
+ onConfirm: () async {
|
|
|
|
+ await viewModel.guardarCorte(esCorte: true);
|
|
|
|
+ Navigator.pop(context);
|
|
|
|
+ await viewModel.imprimirCorteCajaTicket();
|
|
|
|
+ },
|
|
|
|
+ );
|
|
|
|
+ return;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ // 3) Hay pedidos con estatus NUEVO se va a revisar hora local
|
|
|
|
+ DateTime now = DateTime.now().toLocal();
|
|
|
|
+ print(DateFormat('dd/MM/yyyy HH:mm:ss').format(now));
|
|
|
|
+ if (now.hour < 18) {
|
|
|
|
+ // Si son antes de las 6pm mostramos el diálogo para pasar los pedidos al siguiente corte
|
|
|
|
+ _mostrarDialogoPedidosNuevos(
|
|
|
|
+ context,
|
|
|
|
+ pedidosNuevos,
|
|
|
|
+ onAbrirNuevaCaja: (nuevosPedidos) async {
|
|
|
|
+ final oldCorteId = viewModel.selectedCorte!.id!;
|
|
|
|
+
|
|
|
|
+ await viewModel.guardarCorte(esCorte: true);
|
|
|
|
+
|
|
|
|
+ String? nuevaCajaId =
|
|
|
|
+ await viewModel.createCorteCaja();
|
|
|
|
+ if (nuevaCajaId == null) return;
|
|
|
|
+
|
|
|
|
+ List<int> ids =
|
|
|
|
+ nuevosPedidos.map((p) => p.id!).toList();
|
|
|
|
+ await pedidosViewModel
|
|
|
|
+ .actualizarCorteCajaEnPedidos(
|
|
|
|
+ ids, nuevaCajaId);
|
|
|
|
+
|
|
|
|
+ Navigator.pop(context);
|
|
|
|
+
|
|
|
|
+ await viewModel.imprimirCorteCajaTicket(
|
|
|
|
+ corteCajaId: oldCorteId);
|
|
|
|
+ },
|
|
|
|
+ );
|
|
|
|
+ } else {
|
|
|
|
+ // Si son de las 6pm mostramos el diálogo que forza el terminar esos pedidos
|
|
|
|
+ _mostrarDialogoDespuesDe6pm(context, pedidosNuevos);
|
|
|
|
+ }
|
|
}, height: 60, width: 250),
|
|
}, height: 60, width: 250),
|
|
],
|
|
],
|
|
),
|
|
),
|
|
@@ -640,7 +861,6 @@ class _CorteCajaFormState extends State<CorteCajaForm> {
|
|
controller: _personaDepositoController,
|
|
controller: _personaDepositoController,
|
|
),
|
|
),
|
|
SizedBox(height: 20),
|
|
SizedBox(height: 20),
|
|
- // Lista de depósitos
|
|
|
|
Text(
|
|
Text(
|
|
"Lista de Depósitos",
|
|
"Lista de Depósitos",
|
|
style: TextStyle(fontWeight: FontWeight.bold, fontSize: 18),
|
|
style: TextStyle(fontWeight: FontWeight.bold, fontSize: 18),
|
|
@@ -663,14 +883,19 @@ class _CorteCajaFormState extends State<CorteCajaForm> {
|
|
trailing: IconButton(
|
|
trailing: IconButton(
|
|
icon: Icon(Icons.remove_circle_outline,
|
|
icon: Icon(Icons.remove_circle_outline,
|
|
color: Colors.red),
|
|
color: Colors.red),
|
|
- onPressed: () {
|
|
|
|
- cuadroConfirmacion(
|
|
|
|
- context,
|
|
|
|
- etiqueta:
|
|
|
|
- "¿Estás seguro de eliminar el depósito?",
|
|
|
|
- onConfirm: () {
|
|
|
|
- viewModel
|
|
|
|
- .eliminarDeposito(deposito.id!);
|
|
|
|
|
|
+ onPressed: () async {
|
|
|
|
+ showDialog(
|
|
|
|
+ context: context,
|
|
|
|
+ builder: (context) {
|
|
|
|
+ return TotpCuadroConfirmacion(
|
|
|
|
+ title: "Eliminar Deposito",
|
|
|
|
+ content:
|
|
|
|
+ "Por favor, ingresa el código de autenticación para continuar.",
|
|
|
|
+ onSuccess: () {
|
|
|
|
+ viewModel
|
|
|
|
+ .eliminarDeposito(deposito.id!);
|
|
|
|
+ },
|
|
|
|
+ );
|
|
},
|
|
},
|
|
);
|
|
);
|
|
},
|
|
},
|
|
@@ -832,13 +1057,19 @@ class _CorteCajaFormState extends State<CorteCajaForm> {
|
|
trailing: IconButton(
|
|
trailing: IconButton(
|
|
icon: Icon(Icons.remove_circle_outline,
|
|
icon: Icon(Icons.remove_circle_outline,
|
|
color: Colors.red),
|
|
color: Colors.red),
|
|
- onPressed: () {
|
|
|
|
- cuadroConfirmacion(
|
|
|
|
- context,
|
|
|
|
- etiqueta:
|
|
|
|
- "¿Estás seguro de eliminar el retiro?",
|
|
|
|
- onConfirm: () {
|
|
|
|
- viewModel.eliminarRetiro(retiro.id!);
|
|
|
|
|
|
+ onPressed: () async {
|
|
|
|
+ showDialog(
|
|
|
|
+ context: context,
|
|
|
|
+ builder: (context) {
|
|
|
|
+ return TotpCuadroConfirmacion(
|
|
|
|
+ title: "Eliminar Retiro",
|
|
|
|
+ content:
|
|
|
|
+ "Por favor, ingresa el código de autenticación para continuar.",
|
|
|
|
+ onSuccess: () {
|
|
|
|
+ viewModel
|
|
|
|
+ .eliminarRetiro(retiro.id!);
|
|
|
|
+ },
|
|
|
|
+ );
|
|
},
|
|
},
|
|
);
|
|
);
|
|
},
|
|
},
|
|
@@ -999,13 +1230,18 @@ class _CorteCajaFormState extends State<CorteCajaForm> {
|
|
trailing: IconButton(
|
|
trailing: IconButton(
|
|
icon: Icon(Icons.remove_circle_outline,
|
|
icon: Icon(Icons.remove_circle_outline,
|
|
color: Colors.red),
|
|
color: Colors.red),
|
|
- onPressed: () {
|
|
|
|
- cuadroConfirmacion(
|
|
|
|
- context,
|
|
|
|
- etiqueta:
|
|
|
|
- "¿Estás seguro de eliminar el gasto?",
|
|
|
|
- onConfirm: () {
|
|
|
|
- viewModel.eliminarGasto(gasto.id!);
|
|
|
|
|
|
+ onPressed: () async {
|
|
|
|
+ showDialog(
|
|
|
|
+ context: context,
|
|
|
|
+ builder: (context) {
|
|
|
|
+ return TotpCuadroConfirmacion(
|
|
|
|
+ title: "Eliminar Gasto",
|
|
|
|
+ content:
|
|
|
|
+ "Por favor, ingresa el código de autenticación para continuar.",
|
|
|
|
+ onSuccess: () {
|
|
|
|
+ viewModel.eliminarGasto(gasto.id!);
|
|
|
|
+ },
|
|
|
|
+ );
|
|
},
|
|
},
|
|
);
|
|
);
|
|
},
|
|
},
|