pedido_detalle_screen.dart 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339
  1. import 'package:intl/intl.dart';
  2. import 'package:flutter/material.dart';
  3. import '../../models/models.dart';
  4. import '../../themes/themes.dart';
  5. import '../pedido/pedido_ticket.dart';
  6. class PedidoDetalleScreen extends StatelessWidget {
  7. final Pedido pedido;
  8. const PedidoDetalleScreen({Key? key, required this.pedido}) : super(key: key);
  9. String formatCurrency(double amount) {
  10. final format = NumberFormat("#,##0.00", "es_MX");
  11. return format.format(amount);
  12. }
  13. @override
  14. Widget build(BuildContext context) {
  15. double totalSinDescuento =
  16. pedido.productos.fold(0, (previousValue, element) {
  17. double productTotal = element.cantidad! *
  18. (double.tryParse(element.producto?.precio ?? '') ?? 0.0);
  19. double toppingsTotal = element.toppings.fold(0, (toppingTotal, topping) {
  20. return toppingTotal +
  21. (double.tryParse(topping.topping?.precio ?? '') ?? 0.0) *
  22. element.cantidad!;
  23. });
  24. return previousValue + productTotal + toppingsTotal;
  25. });
  26. double descuento = pedido.descuento?.toDouble() ?? 0.0;
  27. double precioDescuento = totalSinDescuento * (descuento / 100);
  28. double totalConDescuento = totalSinDescuento - precioDescuento;
  29. return Scaffold(
  30. appBar: AppBar(
  31. title: Text(
  32. 'Detalle del Pedido ${pedido.folio}',
  33. style: TextStyle(fontWeight: FontWeight.w500),
  34. ),
  35. ),
  36. body: SingleChildScrollView(
  37. padding: const EdgeInsets.all(12.0),
  38. child: Column(
  39. children: [
  40. Card(
  41. elevation: 4,
  42. color: Colors.white,
  43. child: Column(
  44. children: [
  45. // Colocamos la fecha y el cliente en una misma fila
  46. ListTile(
  47. title: Row(
  48. mainAxisAlignment: MainAxisAlignment.spaceBetween,
  49. children: [
  50. Text(
  51. 'Cliente: ${pedido.nombreCliente}',
  52. style: TextStyle(
  53. fontWeight: FontWeight.bold, fontSize: 22),
  54. ),
  55. Text(
  56. 'Fecha: ${_formatDateTime(pedido.peticion)}',
  57. style: TextStyle(
  58. fontWeight: FontWeight.bold, fontSize: 22),
  59. ),
  60. ],
  61. ),
  62. ),
  63. ListTile(
  64. subtitle: Text(
  65. 'Comentarios: ${pedido.comentarios}',
  66. style:
  67. TextStyle(fontSize: 20, fontWeight: FontWeight.w500),
  68. ),
  69. ),
  70. ListTile(
  71. title: Text(
  72. 'Estado del Pedido: ${pedido.estatus}',
  73. style: TextStyle(
  74. fontSize: 22,
  75. fontWeight: FontWeight.bold,
  76. ),
  77. ),
  78. ),
  79. ],
  80. ),
  81. ),
  82. SizedBox(height: 10),
  83. Card(
  84. elevation: 4,
  85. color: Colors.white,
  86. child: Padding(
  87. padding: const EdgeInsets.all(8.0),
  88. child: Column(
  89. crossAxisAlignment: CrossAxisAlignment.start,
  90. children: [
  91. Text('Productos',
  92. style: TextStyle(
  93. fontSize: 22, fontWeight: FontWeight.bold)),
  94. const SizedBox(height: 15),
  95. ListView.builder(
  96. shrinkWrap: true,
  97. physics: NeverScrollableScrollPhysics(),
  98. itemCount: pedido.productos.length,
  99. itemBuilder: (context, index) {
  100. final producto = pedido.productos[index];
  101. return Padding(
  102. padding: const EdgeInsets.symmetric(vertical: 4.0),
  103. child: Column(
  104. crossAxisAlignment: CrossAxisAlignment.start,
  105. children: [
  106. Row(
  107. children: [
  108. Expanded(
  109. flex: 6,
  110. child: Text(
  111. producto.producto?.nombre ??
  112. "Producto no especificado",
  113. style: TextStyle(
  114. fontWeight: FontWeight.bold,
  115. fontSize: 17),
  116. overflow: TextOverflow.ellipsis,
  117. ),
  118. ),
  119. Expanded(
  120. flex: 1,
  121. child: Text(
  122. 'x${producto.cantidad}',
  123. style: TextStyle(
  124. fontWeight: FontWeight.w500,
  125. fontSize: 17),
  126. textAlign: TextAlign.center,
  127. ),
  128. ),
  129. Expanded(
  130. flex: 2,
  131. child: Text(
  132. '\$${formatCurrency(double.tryParse(producto.producto?.precio ?? '0.0') ?? 0.0)}',
  133. style: TextStyle(
  134. fontWeight: FontWeight.w500,
  135. fontSize: 17),
  136. textAlign: TextAlign.right,
  137. ),
  138. ),
  139. ],
  140. ),
  141. if (producto.toppings.isNotEmpty)
  142. Padding(
  143. padding: const EdgeInsets.only(top: 4.0),
  144. child: Column(
  145. crossAxisAlignment:
  146. CrossAxisAlignment.start,
  147. children: producto.toppings.map((topping) {
  148. return Padding(
  149. padding: const EdgeInsets.symmetric(
  150. vertical: 2.0),
  151. child: Row(
  152. children: [
  153. Text(
  154. '- ${topping.topping?.nombre ?? "Topping no especificado"}',
  155. style: TextStyle(
  156. fontSize: 15,
  157. color: Colors.grey[600]),
  158. ),
  159. Spacer(),
  160. Text(
  161. '\$${formatCurrency(double.tryParse(topping.topping?.precio ?? '0.0') ?? 0.0)}',
  162. style: TextStyle(
  163. fontSize: 15,
  164. color: Colors.grey[600]),
  165. ),
  166. ],
  167. ),
  168. );
  169. }).toList(),
  170. ),
  171. ),
  172. ],
  173. ),
  174. );
  175. },
  176. ),
  177. Divider(),
  178. Padding(
  179. padding: const EdgeInsets.symmetric(vertical: 8.0),
  180. child: Column(
  181. crossAxisAlignment: CrossAxisAlignment.end,
  182. children: [
  183. Row(
  184. mainAxisAlignment: MainAxisAlignment.end,
  185. children: [
  186. const Text('Subtotal:',
  187. style: TextStyle(
  188. fontSize: 16,
  189. fontWeight: FontWeight.bold)),
  190. const SizedBox(width: 5),
  191. Text('\$${formatCurrency(totalSinDescuento)}',
  192. style: const TextStyle(
  193. fontSize: 16,
  194. fontWeight: FontWeight.bold)),
  195. ],
  196. ),
  197. if (descuento > 0) ...[
  198. Row(
  199. mainAxisAlignment: MainAxisAlignment.end,
  200. children: [
  201. Text(
  202. 'Descuento (${descuento.toStringAsFixed(0)}%):',
  203. style: const TextStyle(
  204. fontSize: 16,
  205. fontWeight: FontWeight.bold)),
  206. const SizedBox(width: 8),
  207. Text('-\$${formatCurrency(precioDescuento)}',
  208. style: const TextStyle(
  209. fontSize: 16,
  210. fontWeight: FontWeight.bold)),
  211. ],
  212. ),
  213. ],
  214. Row(
  215. mainAxisAlignment: MainAxisAlignment.end,
  216. children: [
  217. const Text('Total:',
  218. style: TextStyle(
  219. fontSize: 16,
  220. fontWeight: FontWeight.bold)),
  221. const SizedBox(width: 5),
  222. Text('\$${formatCurrency(totalConDescuento)}',
  223. style: const TextStyle(
  224. fontSize: 16,
  225. fontWeight: FontWeight.bold)),
  226. ],
  227. ),
  228. ],
  229. ),
  230. ),
  231. ],
  232. ),
  233. ),
  234. ),
  235. const SizedBox(height: 10),
  236. Card(
  237. elevation: 4,
  238. color: Colors.white,
  239. child: Padding(
  240. padding: const EdgeInsets.all(8.0),
  241. child: Column(
  242. crossAxisAlignment: CrossAxisAlignment.start,
  243. children: [
  244. Text('Pago',
  245. style: TextStyle(
  246. fontSize: 22, fontWeight: FontWeight.bold)),
  247. const SizedBox(height: 10),
  248. _buildPaymentDetails(),
  249. ],
  250. ),
  251. ),
  252. ),
  253. const SizedBox(height: 20),
  254. Align(
  255. alignment: Alignment.centerLeft,
  256. child: ElevatedButton.icon(
  257. icon: Icon(
  258. Icons.receipt_long_outlined,
  259. color: AppTheme.quaternary,
  260. size: 30,
  261. ),
  262. onPressed: () => imprimirTicketsJuntos(context, pedido),
  263. label: Text(
  264. 'Imprimir Ticket',
  265. style: TextStyle(
  266. fontWeight: FontWeight.w500,
  267. fontSize: 18,
  268. color: AppTheme.quaternary),
  269. ),
  270. style: ElevatedButton.styleFrom(
  271. padding: const EdgeInsets.fromLTRB(50, 20, 50, 20),
  272. primary: AppTheme.tertiary,
  273. ),
  274. ),
  275. )
  276. ],
  277. ),
  278. ),
  279. );
  280. }
  281. Widget _buildPaymentDetails() {
  282. List<Widget> paymentDetails = [];
  283. if (pedido.cantEfectivo != null && pedido.cantEfectivo! > 0) {
  284. paymentDetails.add(_buildPaymentRow("Efectivo", pedido.cantEfectivo!));
  285. }
  286. if (pedido.cantTarjeta != null && pedido.cantTarjeta! > 0) {
  287. paymentDetails.add(_buildPaymentRow("Tarjeta", pedido.cantTarjeta!));
  288. }
  289. if (pedido.cantTransferencia != null && pedido.cantTransferencia! > 0) {
  290. paymentDetails
  291. .add(_buildPaymentRow("Transferencia", pedido.cantTransferencia!));
  292. }
  293. if (paymentDetails.isEmpty) {
  294. paymentDetails.add(Text("No se especificaron métodos de pago.",
  295. style: TextStyle(fontSize: 16, color: Colors.grey[600])));
  296. }
  297. return Column(
  298. crossAxisAlignment: CrossAxisAlignment.start,
  299. children: paymentDetails,
  300. );
  301. }
  302. Widget _buildPaymentRow(String paymentType, double amount) {
  303. return Row(
  304. mainAxisAlignment: MainAxisAlignment.spaceBetween,
  305. children: [
  306. Text(
  307. paymentType,
  308. style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
  309. ),
  310. Text(
  311. '\$${formatCurrency(amount)}',
  312. style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
  313. ),
  314. ],
  315. );
  316. }
  317. String _formatDateTime(String? dateTimeString) {
  318. if (dateTimeString == null) return "Sin fecha";
  319. DateTime parsedDate = DateTime.parse(dateTimeString);
  320. var formatter = DateFormat('dd-MM-yyyy HH:mm:ss');
  321. return formatter.format(parsedDate.toLocal());
  322. }
  323. }