venta_screen.dart 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442
  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? fechaInicialSeleccionada;
  17. DateTime? fechaFinalSeleccionada;
  18. List<Pedido> pedidosNoCancelados = [];
  19. List<Pedido> pedidosCancelados = [];
  20. List<Propinas> listaPropinas = [];
  21. final _busqueda = TextEditingController(text: '');
  22. double totalDelDia = 0.0;
  23. double totalPropinas = 0.0;
  24. double totalCancelados = 0.0;
  25. double totalEfectivoDelDia = 0.0;
  26. double totalTarjetaDelDia = 0.0;
  27. double totalTransferenciaDelDia = 0.0;
  28. double cambio = 0.0;
  29. double totalSinCambio = 0.0;
  30. String formatCurrency(double amount) {
  31. final format = NumberFormat("#,##0.00", "es_MX");
  32. return format.format(amount);
  33. }
  34. void clearSearchAndReset() {
  35. setState(() {
  36. _busqueda.clear();
  37. fechaInicialSeleccionada = null;
  38. fechaFinalSeleccionada = null;
  39. });
  40. }
  41. @override
  42. Widget build(BuildContext context) {
  43. return Scaffold(
  44. appBar: AppBar(
  45. title: Text(
  46. "Resumen de Pedidos por Periodo",
  47. style: TextStyle(color: AppTheme.secondary),
  48. ),
  49. iconTheme: IconThemeData(color: AppTheme.secondary)),
  50. body: Padding(
  51. padding: const EdgeInsets.all(16.0),
  52. child: Column(
  53. children: [
  54. Row(
  55. children: [
  56. Expanded(
  57. flex: 3,
  58. child: Padding(
  59. padding: const EdgeInsets.symmetric(horizontal: 0.0),
  60. child: tarjeta(
  61. ListTile(
  62. title: Text(
  63. "Fecha Inicial",
  64. style: TextStyle(
  65. color: AppTheme.quaternary,
  66. fontWeight: FontWeight.bold),
  67. ),
  68. subtitle: Text(
  69. fechaInicialSeleccionada == null
  70. ? ""
  71. : DateFormat("dd/MM/yyyy")
  72. .format(fechaInicialSeleccionada!),
  73. style: TextStyle(
  74. color: AppTheme.quaternary,
  75. fontWeight: FontWeight.bold),
  76. ),
  77. trailing: Icon(Icons.calendar_month,
  78. color: AppTheme.quaternary),
  79. onTap: () async {
  80. DateTime? d = await showDatetimePicker(
  81. context, fechaInicialSeleccionada,
  82. inicia: fechaInicialSeleccionada,
  83. tipo: OmniDateTimePickerType.date,
  84. solofecha: true);
  85. if (d == null) return;
  86. setState(() {
  87. fechaInicialSeleccionada = d;
  88. });
  89. cargarPedidos(fechaInicialSeleccionada,
  90. fechaFinalSeleccionada);
  91. }),
  92. color: AppTheme.tertiary,
  93. ),
  94. )),
  95. const SizedBox(
  96. width: 10,
  97. ),
  98. Expanded(
  99. flex: 3,
  100. child: Padding(
  101. padding: const EdgeInsets.symmetric(horizontal: 0.0),
  102. child: tarjeta(
  103. ListTile(
  104. title: Text(
  105. "Fecha Final",
  106. style: TextStyle(
  107. color: AppTheme.quaternary,
  108. fontWeight: FontWeight.bold),
  109. ),
  110. subtitle: Text(
  111. fechaFinalSeleccionada == null
  112. ? ""
  113. : DateFormat("dd/MM/yyyy")
  114. .format(fechaFinalSeleccionada!),
  115. style: TextStyle(
  116. color: AppTheme.quaternary,
  117. fontWeight: FontWeight.bold),
  118. ),
  119. trailing: Icon(Icons.calendar_month,
  120. color: AppTheme.quaternary),
  121. onTap: () async {
  122. DateTime? d = await showDatetimePicker(
  123. context, fechaFinalSeleccionada,
  124. inicia: fechaFinalSeleccionada,
  125. tipo: OmniDateTimePickerType.date,
  126. solofecha: true);
  127. if (d == null) return;
  128. setState(() {
  129. fechaFinalSeleccionada = d;
  130. });
  131. cargarPedidos(fechaInicialSeleccionada,
  132. fechaFinalSeleccionada);
  133. }),
  134. color: AppTheme.tertiary,
  135. ),
  136. )),
  137. const SizedBox(
  138. width: 500,
  139. ),
  140. Expanded(
  141. flex: 2,
  142. child: Padding(
  143. padding: const EdgeInsets.only(top: 0),
  144. child: ElevatedButton(
  145. onPressed: clearSearchAndReset,
  146. style: ElevatedButton.styleFrom(
  147. shape: RoundedRectangleBorder(
  148. borderRadius: BorderRadius.circular(20.0),
  149. ),
  150. backgroundColor: AppTheme.tertiary,
  151. padding: const EdgeInsets.symmetric(vertical: 25),
  152. ),
  153. child: Text('Limpiar',
  154. style: TextStyle(
  155. color: AppTheme.quaternary, fontSize: 18)),
  156. ),
  157. ),
  158. ),
  159. ],
  160. ),
  161. Expanded(
  162. child: tarjeta(
  163. Padding(
  164. padding: const EdgeInsets.all(8.0),
  165. child: ListView.builder(
  166. itemCount:
  167. pedidosNoCancelados.length + pedidosCancelados.length,
  168. itemBuilder: (context, index) {
  169. if (index < pedidosNoCancelados.length) {
  170. final pedido = pedidosNoCancelados[index];
  171. return Row(
  172. mainAxisAlignment: MainAxisAlignment.spaceBetween,
  173. children: [
  174. Text(
  175. "Folio: ${pedido.folio}",
  176. style: TextStyle(
  177. fontSize: 20, fontWeight: FontWeight.w500),
  178. ),
  179. Text(
  180. "Fecha/Hora: ${_formatDateTime(pedido.peticion)}",
  181. style: TextStyle(
  182. fontSize: 20, fontWeight: FontWeight.w500),
  183. ),
  184. Text(
  185. "Propina: \$${_propinaPedido(pedido.id)}",
  186. style: TextStyle(
  187. fontSize: 20, fontWeight: FontWeight.w500),
  188. ),
  189. Text(
  190. "Total: \$${formatCurrency(pedido.totalPedido ?? 0)}",
  191. style: TextStyle(
  192. fontSize: 20, fontWeight: FontWeight.w500)),
  193. ],
  194. );
  195. } else {
  196. final pedido =
  197. pedidosCancelados[index - pedidosNoCancelados.length];
  198. return Row(
  199. mainAxisAlignment: MainAxisAlignment.spaceBetween,
  200. children: [
  201. Text(
  202. "Folio: ${pedido.folio} (Cancelado)",
  203. style: TextStyle(
  204. fontSize: 20,
  205. fontWeight: FontWeight.w500,
  206. color: Colors.red),
  207. ),
  208. Text(
  209. "Total: \$${formatCurrency(pedido.totalPedido ?? 0)}",
  210. style: TextStyle(
  211. fontSize: 20,
  212. fontWeight: FontWeight.w500,
  213. color: Colors.red)),
  214. ],
  215. );
  216. }
  217. },
  218. ),
  219. ),
  220. )),
  221. Row(
  222. mainAxisAlignment: MainAxisAlignment.spaceBetween,
  223. children: [
  224. ElevatedButton.icon(
  225. icon: Icon(
  226. Icons.receipt_long_outlined,
  227. color: AppTheme.quaternary,
  228. size: 30,
  229. ),
  230. onPressed: pedidosNoCancelados.isNotEmpty ||
  231. pedidosCancelados.isNotEmpty
  232. ? () => imprimirResumenPedidos(
  233. pedidosNoCancelados + pedidosCancelados)
  234. : null,
  235. label: Text(
  236. "Imprimir Resumen",
  237. style: TextStyle(color: AppTheme.quaternary, fontSize: 18),
  238. ),
  239. style: ButtonStyle(
  240. backgroundColor:
  241. MaterialStatePropertyAll(AppTheme.tertiary),
  242. padding: MaterialStatePropertyAll(
  243. EdgeInsets.fromLTRB(30, 20, 30, 20))),
  244. ),
  245. Column(
  246. crossAxisAlignment: CrossAxisAlignment.end,
  247. children: [
  248. Row(
  249. children: [
  250. Padding(
  251. padding: const EdgeInsets.all(16.0),
  252. child: Text(
  253. "Total Propina: \$${formatCurrency(totalPropinas)}",
  254. style: TextStyle(
  255. fontSize: 20, fontWeight: FontWeight.bold),
  256. ),
  257. ),
  258. Padding(
  259. padding: const EdgeInsets.all(16.0),
  260. child: Text(
  261. "Total: \$${formatCurrency(totalDelDia)}",
  262. style: TextStyle(
  263. fontSize: 20, fontWeight: FontWeight.bold),
  264. ),
  265. ),
  266. ],
  267. ),
  268. Row(
  269. children: [
  270. if (totalTarjetaDelDia > 0)
  271. Padding(
  272. padding: const EdgeInsets.all(16.0),
  273. child: Text(
  274. "Total en Tarjeta: \$${formatCurrency(totalTarjetaDelDia)}",
  275. style: TextStyle(
  276. fontSize: 20, fontWeight: FontWeight.bold),
  277. ),
  278. ),
  279. if (totalTransferenciaDelDia > 0)
  280. Padding(
  281. padding: const EdgeInsets.all(16.0),
  282. child: Text(
  283. "Total en Transferencia: \$${formatCurrency(totalTransferenciaDelDia)}",
  284. style: TextStyle(
  285. fontSize: 20, fontWeight: FontWeight.bold),
  286. ),
  287. ),
  288. if (totalEfectivoDelDia > 0)
  289. Padding(
  290. padding: const EdgeInsets.all(16.0),
  291. child: Text(
  292. "Total en Efectivo: \$${formatCurrency(totalEfectivoDelDia)}",
  293. style: TextStyle(
  294. fontSize: 20, fontWeight: FontWeight.bold),
  295. ),
  296. ),
  297. if (cambio > 0)
  298. Padding(
  299. padding: const EdgeInsets.all(16.0),
  300. child: Text(
  301. "Cambio Entregado: \$${formatCurrency(cambio)}",
  302. style: TextStyle(
  303. fontSize: 20, fontWeight: FontWeight.bold),
  304. ),
  305. ),
  306. ],
  307. ),
  308. if (totalCancelados > 0)
  309. Padding(
  310. padding: const EdgeInsets.all(16.0),
  311. child: Text(
  312. "Total cancelados: \$${formatCurrency(totalCancelados)}",
  313. style: TextStyle(
  314. fontSize: 20,
  315. fontWeight: FontWeight.bold,
  316. color: Colors.red),
  317. ),
  318. ),
  319. ],
  320. ),
  321. ],
  322. )
  323. ],
  324. ),
  325. ),
  326. );
  327. }
  328. void cargarPedidos(DateTime? fechaInicial, DateTime? fechaFinal) async {
  329. if (fechaInicial != null && fechaFinal != null) {
  330. // Convertir el inicio y fin del día local a UTC
  331. final inicioDelDia =
  332. DateTime(fechaInicial.year, fechaInicial.month, fechaInicial.day)
  333. .toUtc(); // Convierte la fecha local de inicio del día a UTC
  334. final finDelDia = DateTime(
  335. fechaFinal.year, fechaFinal.month, fechaFinal.day, 23, 59, 59)
  336. .toUtc(); // Convierte la fecha local de fin del día a UTC
  337. print('Buscando pedidos desde: $inicioDelDia hasta: $finDelDia (en UTC)');
  338. // Realizar la búsqueda en UTC
  339. final pedidos = await Provider.of<PedidoViewModel>(context, listen: false)
  340. .buscarPorFecha(inicioDelDia, finDelDia);
  341. print('Pedidos obtenidos: ${pedidos.length}');
  342. pedidos.forEach((pedido) => print(
  343. 'Pedido: ${pedido.folio}, Total: ${pedido.totalPedido}, Estatus: ${pedido.estatus}, Propinas: ${pedido.propinas.length}'));
  344. final pedidosNoCancelados =
  345. pedidos.where((p) => p.estatus != "CANCELADO").toList();
  346. final pedidosCancelados =
  347. pedidos.where((p) => p.estatus == "CANCELADO").toList();
  348. totalDelDia = 0.0;
  349. totalPropinas = 0.0;
  350. totalCancelados = 0.0;
  351. totalEfectivoDelDia = 0.0;
  352. totalTarjetaDelDia = 0.0;
  353. totalTransferenciaDelDia = 0.0;
  354. cambio = 0.0;
  355. totalSinCambio = 0.0;
  356. for (var pedido in pedidosNoCancelados) {
  357. totalDelDia += pedido.totalPedido ?? 0.0;
  358. totalEfectivoDelDia += pedido.cantEfectivo ?? 0.0;
  359. totalTarjetaDelDia += pedido.cantTarjeta ?? 0.0;
  360. totalTransferenciaDelDia += pedido.cantTransferencia ?? 0.0;
  361. totalSinCambio =
  362. totalEfectivoDelDia + totalTarjetaDelDia + totalTransferenciaDelDia;
  363. cambio = totalSinCambio - totalDelDia;
  364. print("Propinas: ${pedido.propinas.length}");
  365. // if (pedido.propinas.length > 0) {
  366. // for (var propina in pedido.propinas) {
  367. // totalPropinas += propina.cantidad ?? 0.0;
  368. // }
  369. // }
  370. final propinas =
  371. await Provider.of<PropinaViewModel>(context, listen: false)
  372. .obtenerPropinasPorPedido(pedido.id);
  373. listaPropinas = propinas;
  374. for (var propina in propinas) {
  375. totalPropinas += propina.cantidad ?? 0.0;
  376. }
  377. print("Total propinas: ${totalPropinas}");
  378. }
  379. totalCancelados = pedidosCancelados.fold(
  380. 0.0, (sum, current) => sum + (current.totalPedido ?? 0.0));
  381. setState(() {
  382. this.pedidosNoCancelados = pedidosNoCancelados;
  383. this.pedidosCancelados = pedidosCancelados;
  384. });
  385. }
  386. }
  387. Future<void> imprimirResumenPedidos(List<Pedido> pedidos) async {
  388. if (fechaInicialSeleccionada == null) {
  389. print("No se ha seleccionado una fecha inicial.");
  390. return;
  391. }
  392. if (fechaFinalSeleccionada == null) {
  393. print("No se ha seleccionado una fecha final.");
  394. return;
  395. }
  396. await VentaTicket.imprimirResumenPedidos(
  397. pedidos, fechaInicialSeleccionada!);
  398. }
  399. String _formatDateTime(String? dateTimeString) {
  400. if (dateTimeString == null) return "Sin fecha";
  401. DateTime parsedDate = DateTime.parse(dateTimeString);
  402. var formatter = DateFormat('dd-MM-yyyy HH:mm:ss');
  403. return formatter.format(parsedDate.toLocal());
  404. }
  405. double? _propinaPedido(int? idPedido) {
  406. double? propina = 0.0;
  407. for (var p in listaPropinas) {
  408. if (p.idPedido == idPedido) {
  409. propina = p.cantidad;
  410. }
  411. }
  412. return propina;
  413. }
  414. }