venta_screen.dart 9.6 KB

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