venta_screen.dart 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332
  1. import 'package:flutter/material.dart';
  2. import 'package:intl/intl.dart';
  3. import 'package:omni_datetime_picker/omni_datetime_picker.dart';
  4. import 'package:provider/provider.dart';
  5. import '../../widgets/widgets_components.dart';
  6. import '../../models/models.dart';
  7. import '../../viewmodels/viewmodels.dart';
  8. import 'venta_ticket.dart';
  9. import '../../widgets/widgets.dart';
  10. import '../../themes/themes.dart';
  11. class VentaScreen extends StatefulWidget {
  12. @override
  13. _VentaScreenState createState() => _VentaScreenState();
  14. }
  15. class _VentaScreenState extends State<VentaScreen> {
  16. DateTime? fechaSeleccionada;
  17. List<Pedido> pedidosNoCancelados = [];
  18. List<Pedido> pedidosCancelados = [];
  19. final _busqueda = TextEditingController(text: '');
  20. double totalDelDia = 0.0;
  21. double totalCancelados = 0.0;
  22. double totalEfectivoDelDia = 0.0;
  23. double totalTarjetaDelDia = 0.0;
  24. double totalTransferenciaDelDia = 0.0;
  25. double cambio = 0.0;
  26. double totalSinCambio = 0.0;
  27. String formatCurrency(double amount) {
  28. final format = NumberFormat("#,##0.00", "es_MX");
  29. return format.format(amount);
  30. }
  31. void clearSearchAndReset() {
  32. setState(() {
  33. _busqueda.clear();
  34. fechaSeleccionada = null;
  35. });
  36. }
  37. @override
  38. Widget build(BuildContext context) {
  39. return Scaffold(
  40. appBar: AppBar(
  41. title: Text(
  42. "Resumen de Pedidos por Día",
  43. style: TextStyle(color: AppTheme.secondary),
  44. ),
  45. iconTheme: IconThemeData(color: AppTheme.secondary)),
  46. body: Padding(
  47. padding: const EdgeInsets.all(16.0),
  48. child: Column(
  49. children: [
  50. Row(
  51. children: [
  52. Expanded(
  53. flex: 3,
  54. child: Padding(
  55. padding: const EdgeInsets.symmetric(horizontal: 0.0),
  56. child: tarjeta(
  57. ListTile(
  58. title: Text(
  59. "Fecha",
  60. style: TextStyle(
  61. color: AppTheme.quaternary,
  62. fontWeight: FontWeight.bold),
  63. ),
  64. subtitle: Text(
  65. fechaSeleccionada == null
  66. ? ""
  67. : DateFormat("dd/MM/yyyy")
  68. .format(fechaSeleccionada!),
  69. style: TextStyle(
  70. color: AppTheme.quaternary,
  71. fontWeight: FontWeight.bold),
  72. ),
  73. trailing: Icon(Icons.calendar_month,
  74. color: AppTheme.quaternary),
  75. onTap: () async {
  76. DateTime? d = await showDatetimePicker(
  77. context, fechaSeleccionada,
  78. inicia: fechaSeleccionada,
  79. tipo: OmniDateTimePickerType.date,
  80. solofecha: true);
  81. if (d == null) return;
  82. setState(() {
  83. fechaSeleccionada = d;
  84. });
  85. cargarPedidos(fechaSeleccionada!);
  86. }),
  87. color: AppTheme.tertiary,
  88. ),
  89. )),
  90. const SizedBox(
  91. width: 500,
  92. ),
  93. Expanded(
  94. flex: 2,
  95. child: Padding(
  96. padding: const EdgeInsets.only(top: 0),
  97. child: ElevatedButton(
  98. onPressed: clearSearchAndReset,
  99. style: ElevatedButton.styleFrom(
  100. shape: RoundedRectangleBorder(
  101. borderRadius: BorderRadius.circular(20.0),
  102. ),
  103. primary: AppTheme.tertiary,
  104. padding: const EdgeInsets.symmetric(vertical: 25),
  105. ),
  106. child: Text('Limpiar',
  107. style: TextStyle(
  108. color: AppTheme.quaternary, fontSize: 18)),
  109. ),
  110. ),
  111. ),
  112. ],
  113. ),
  114. Expanded(
  115. child: tarjeta(
  116. Padding(
  117. padding: const EdgeInsets.all(8.0),
  118. child: ListView.builder(
  119. itemCount:
  120. pedidosNoCancelados.length + pedidosCancelados.length,
  121. itemBuilder: (context, index) {
  122. if (index < pedidosNoCancelados.length) {
  123. final pedido = pedidosNoCancelados[index];
  124. return Row(
  125. mainAxisAlignment: MainAxisAlignment.spaceBetween,
  126. children: [
  127. Text(
  128. "Folio: ${pedido.folio}",
  129. style: TextStyle(
  130. fontSize: 20, fontWeight: FontWeight.w500),
  131. ),
  132. Text(
  133. "Total: \$${formatCurrency(pedido.totalPedido ?? 0)}",
  134. style: TextStyle(
  135. fontSize: 20, fontWeight: FontWeight.w500)),
  136. ],
  137. );
  138. } else {
  139. final pedido =
  140. pedidosCancelados[index - pedidosNoCancelados.length];
  141. return Row(
  142. mainAxisAlignment: MainAxisAlignment.spaceBetween,
  143. children: [
  144. Text(
  145. "Folio: ${pedido.folio} (Cancelado)",
  146. style: TextStyle(
  147. fontSize: 20,
  148. fontWeight: FontWeight.w500,
  149. color: Colors.red),
  150. ),
  151. Text(
  152. "Total: \$${formatCurrency(pedido.totalPedido ?? 0)}",
  153. style: TextStyle(
  154. fontSize: 20,
  155. fontWeight: FontWeight.w500,
  156. color: Colors.red)),
  157. ],
  158. );
  159. }
  160. },
  161. ),
  162. ),
  163. )),
  164. Row(
  165. mainAxisAlignment: MainAxisAlignment.spaceBetween,
  166. children: [
  167. ElevatedButton.icon(
  168. icon: Icon(
  169. Icons.receipt_long_outlined,
  170. color: AppTheme.quaternary,
  171. size: 30,
  172. ),
  173. onPressed: pedidosNoCancelados.isNotEmpty ||
  174. pedidosCancelados.isNotEmpty
  175. ? () => imprimirResumenPedidos(
  176. pedidosNoCancelados + pedidosCancelados)
  177. : null,
  178. label: Text(
  179. "Imprimir Resumen",
  180. style: TextStyle(color: AppTheme.quaternary, fontSize: 18),
  181. ),
  182. style: ButtonStyle(
  183. backgroundColor:
  184. MaterialStatePropertyAll(AppTheme.tertiary),
  185. padding: MaterialStatePropertyAll(
  186. EdgeInsets.fromLTRB(30, 20, 30, 20))),
  187. ),
  188. Column(
  189. crossAxisAlignment: CrossAxisAlignment.end,
  190. children: [
  191. Padding(
  192. padding: const EdgeInsets.all(16.0),
  193. child: Text(
  194. "Total del día: \$${formatCurrency(totalDelDia)}",
  195. style: TextStyle(
  196. fontSize: 20, fontWeight: FontWeight.bold),
  197. ),
  198. ),
  199. Row(
  200. children: [
  201. if (totalTarjetaDelDia > 0)
  202. Padding(
  203. padding: const EdgeInsets.all(16.0),
  204. child: Text(
  205. "Tarjeta: \$${formatCurrency(totalTarjetaDelDia)}",
  206. style: TextStyle(
  207. fontSize: 20, fontWeight: FontWeight.bold),
  208. ),
  209. ),
  210. if (totalTransferenciaDelDia > 0)
  211. Padding(
  212. padding: const EdgeInsets.all(16.0),
  213. child: Text(
  214. "Transferencia: \$${formatCurrency(totalTransferenciaDelDia)}",
  215. style: TextStyle(
  216. fontSize: 20, fontWeight: FontWeight.bold),
  217. ),
  218. ),
  219. ],
  220. ),
  221. Row(
  222. children: [
  223. if (totalEfectivoDelDia > 0)
  224. Padding(
  225. padding: const EdgeInsets.all(16.0),
  226. child: Text(
  227. "Efectivo: \$${formatCurrency(totalEfectivoDelDia)}",
  228. style: TextStyle(
  229. fontSize: 20, fontWeight: FontWeight.bold),
  230. ),
  231. ),
  232. if (cambio > 0)
  233. Padding(
  234. padding: const EdgeInsets.all(16.0),
  235. child: Text(
  236. "Cambio: \$${formatCurrency(cambio)}",
  237. style: TextStyle(
  238. fontSize: 20, fontWeight: FontWeight.bold),
  239. ),
  240. ),
  241. ],
  242. ),
  243. if (totalCancelados > 0)
  244. Padding(
  245. padding: const EdgeInsets.all(16.0),
  246. child: Text(
  247. "Total cancelados: \$${formatCurrency(totalCancelados)}",
  248. style: TextStyle(
  249. fontSize: 20,
  250. fontWeight: FontWeight.bold,
  251. color: Colors.red),
  252. ),
  253. ),
  254. ],
  255. ),
  256. ],
  257. )
  258. ],
  259. ),
  260. ),
  261. );
  262. }
  263. void cargarPedidos(DateTime fecha) async {
  264. // Convertir el inicio y fin del día local a UTC
  265. final inicioDelDia = DateTime(fecha.year, fecha.month, fecha.day)
  266. .toUtc(); // Convierte la fecha local de inicio del día a UTC
  267. final finDelDia = DateTime(fecha.year, fecha.month, fecha.day, 23, 59, 59)
  268. .toUtc(); // Convierte la fecha local de fin del día a UTC
  269. print('Buscando pedidos desde: $inicioDelDia hasta: $finDelDia (en UTC)');
  270. // Realizar la búsqueda en UTC
  271. final pedidos = await Provider.of<PedidoViewModel>(context, listen: false)
  272. .buscarPorFecha(inicioDelDia, finDelDia);
  273. print('Pedidos obtenidos: ${pedidos.length}');
  274. pedidos.forEach((pedido) => print(
  275. 'Pedido: ${pedido.folio}, Total: ${pedido.totalPedido}, Estatus: ${pedido.estatus}'));
  276. final pedidosNoCancelados =
  277. pedidos.where((p) => p.estatus != "CANCELADO").toList();
  278. final pedidosCancelados =
  279. pedidos.where((p) => p.estatus == "CANCELADO").toList();
  280. totalDelDia = 0.0;
  281. totalCancelados = 0.0;
  282. totalEfectivoDelDia = 0.0;
  283. totalTarjetaDelDia = 0.0;
  284. totalTransferenciaDelDia = 0.0;
  285. cambio = 0.0;
  286. totalSinCambio = 0.0;
  287. for (var pedido in pedidosNoCancelados) {
  288. totalDelDia += pedido.totalPedido ?? 0.0;
  289. totalEfectivoDelDia += pedido.cantEfectivo ?? 0.0;
  290. totalTarjetaDelDia += pedido.cantTarjeta ?? 0.0;
  291. totalTransferenciaDelDia += pedido.cantTransferencia ?? 0.0;
  292. totalSinCambio =
  293. totalEfectivoDelDia + totalTarjetaDelDia + totalTransferenciaDelDia;
  294. cambio = totalSinCambio - totalDelDia;
  295. }
  296. print("Total del dia sin cambios $totalSinCambio");
  297. print("Cambio $cambio");
  298. totalCancelados = pedidosCancelados.fold(
  299. 0.0, (sum, current) => sum + (current.totalPedido ?? 0.0));
  300. setState(() {
  301. this.pedidosNoCancelados = pedidosNoCancelados;
  302. this.pedidosCancelados = pedidosCancelados;
  303. });
  304. }
  305. Future<void> imprimirResumenPedidos(List<Pedido> pedidos) async {
  306. if (fechaSeleccionada == null) {
  307. print("No se ha seleccionado una fecha.");
  308. return;
  309. }
  310. await VentaTicket.imprimirResumenPedidos(pedidos, fechaSeleccionada!);
  311. }
  312. }