venta_screen.dart 11 KB

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