pedido_screen.dart 30 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740
  1. import 'dart:typed_data';
  2. import 'package:flutter/material.dart';
  3. import 'package:intl/intl.dart';
  4. import 'package:provider/provider.dart';
  5. import '../pedido/pedido_csv.dart';
  6. import '../pedido/pedido_detalle_screen.dart';
  7. import '../../widgets/widgets.dart';
  8. import '../../themes/themes.dart';
  9. import '../../models/models.dart';
  10. import '../../viewmodels/viewmodels.dart';
  11. import '../../widgets/widgets_components.dart' as clase;
  12. import 'pedido_form.dart';
  13. import 'package:otp/otp.dart';
  14. import 'package:pdf/widgets.dart' as pw;
  15. import 'package:timezone/data/latest.dart' as timezone;
  16. import 'package:timezone/timezone.dart' as timezone;
  17. import 'pedido_ticket.dart';
  18. class PedidoScreen extends StatefulWidget {
  19. const PedidoScreen({Key? key}) : super(key: key);
  20. @override
  21. State<PedidoScreen> createState() => _PedidoScreenState();
  22. }
  23. class _PedidoScreenState extends State<PedidoScreen> {
  24. final _busqueda = TextEditingController(text: '');
  25. DateTime? fechaInicio;
  26. DateTime? fechaFin;
  27. ScrollController horizontalScrollController = ScrollController();
  28. TextEditingController codeController = new TextEditingController();
  29. bool _isMesasActive = false;
  30. @override
  31. void initState() {
  32. super.initState();
  33. WidgetsBinding.instance.addPostFrameCallback((_) {
  34. Provider.of<PedidoViewModel>(context, listen: false)
  35. .fetchLocalPedidosForScreen();
  36. Provider.of<MesaViewModel>(context, listen: false)
  37. .fetchLocalAll(sinLimite: true);
  38. });
  39. Future.microtask(() async {
  40. bool isMesasActive =
  41. await Provider.of<VariableViewModel>(context, listen: false)
  42. .isVariableActive('MESAS');
  43. setState(() {
  44. _isMesasActive = isMesasActive;
  45. });
  46. });
  47. }
  48. void exportCSV() async {
  49. final pedidosViewModel =
  50. Provider.of<PedidoViewModel>(context, listen: false);
  51. List<Pedido> pedidosConProductos = [];
  52. for (Pedido pedido in pedidosViewModel.pedidos) {
  53. Pedido? pedidoConProductos =
  54. await pedidosViewModel.fetchPedidoConProductos(pedido.id);
  55. if (pedidoConProductos != null) {
  56. pedidosConProductos.add(pedidoConProductos);
  57. }
  58. }
  59. if (pedidosConProductos.isNotEmpty) {
  60. String fileName = 'Pedidos_Turquessa_POS';
  61. if (fechaInicio != null && fechaFin != null) {
  62. String startDateStr = DateFormat('dd-MM-yyyy').format(fechaInicio!);
  63. String endDateStr = DateFormat('dd-MM-yyyy').format(fechaFin!);
  64. fileName += '_${startDateStr}_al_${endDateStr}';
  65. }
  66. fileName += '.csv';
  67. await exportarPedidosACSV(pedidosConProductos, fileName);
  68. ScaffoldMessenger.of(context).showSnackBar(SnackBar(
  69. content: Text('Archivo CSV descargado! Archivo: $fileName')));
  70. } else {
  71. ScaffoldMessenger.of(context).showSnackBar(
  72. SnackBar(content: Text('No hay pedidos para exportar.')));
  73. }
  74. }
  75. void clearSearchAndReset() {
  76. setState(() {
  77. _busqueda.clear();
  78. fechaInicio = null;
  79. fechaFin = null;
  80. Provider.of<PedidoViewModel>(context, listen: false)
  81. .fetchLocalPedidosForScreen();
  82. });
  83. }
  84. void go(Pedido item, {bool? detalle = false}) async {
  85. Pedido? pedidoCompleto =
  86. await Provider.of<PedidoViewModel>(context, listen: false)
  87. .fetchPedidoConProductos(item.id);
  88. if (pedidoCompleto != null) {
  89. if (pedidoCompleto.estatus == 'TERMINADO' ||
  90. pedidoCompleto.estatus == 'CANCELADO' ||
  91. !_isMesasActive ||
  92. detalle!) {
  93. Navigator.push(
  94. context,
  95. MaterialPageRoute(
  96. builder: (context) => PedidoDetalleScreen(pedido: pedidoCompleto),
  97. ),
  98. );
  99. } else {
  100. Navigator.push(
  101. context,
  102. MaterialPageRoute(
  103. builder: (context) => PedidoForm(
  104. pedidoExistente: pedidoCompleto,
  105. ),
  106. ),
  107. ).then((value) async {
  108. await Provider.of<PedidoViewModel>(context, listen: false)
  109. .fetchLocalPedidosForScreen();
  110. });
  111. }
  112. } else {
  113. print("Error al cargar el pedido con productos");
  114. }
  115. }
  116. @override
  117. Widget build(BuildContext context) {
  118. final pvm = Provider.of<PedidoViewModel>(context);
  119. double screenWidth = MediaQuery.of(context).size.width;
  120. final isMobile = screenWidth < 1250;
  121. final double? columnSpacing = isMobile ? null : 0;
  122. TextStyle estilo = const TextStyle(fontWeight: FontWeight.bold);
  123. List<DataRow> registros = [];
  124. final permisoViewModel = Provider.of<PermisoViewModel>(context);
  125. List<String> userPermisos = permisoViewModel.userPermisos;
  126. for (Pedido item in pvm.pedidos) {
  127. final sincronizadoStatus =
  128. item.sincronizado == null || item.sincronizado!.isEmpty
  129. ? "No Sincronizado"
  130. : _formatDateTime(item.sincronizado);
  131. final mesaViewModel = Provider.of<MesaViewModel>(context, listen: false);
  132. final mesa = mesaViewModel.fetchLocalById(idMesa: item.idMesa);
  133. registros.add(DataRow(cells: [
  134. DataCell(
  135. Row(mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [
  136. PopupMenuButton(
  137. itemBuilder: (context) => [
  138. PopupMenuItem(
  139. child: const Text('Detalle'),
  140. onTap: () => go(item, detalle: true),
  141. ),
  142. PopupMenuItem(
  143. child: const Text('Estado Ticket'),
  144. onTap: () async {
  145. await imprimirEstadoTicket(context, item);
  146. },
  147. ),
  148. if (userPermisos.contains(Usuario.CANCELAR_PEDIDO))
  149. PopupMenuItem(
  150. child: const Text('Cancelar Pedido'),
  151. onTap: () async {
  152. bool confirmado = await showDialog<bool>(
  153. context: context,
  154. builder: (context) {
  155. return AlertDialog(
  156. title: const Text("Cancelar Pedido",
  157. style: TextStyle(
  158. fontWeight: FontWeight.w500,
  159. fontSize: 22)),
  160. content: const Text(
  161. '¿Estás seguro de que deseas cancelar este pedido?',
  162. style: TextStyle(fontSize: 18)),
  163. actions: [
  164. Column(
  165. children: [
  166. Container(
  167. padding: EdgeInsets.all(8.0),
  168. child: TextField(
  169. controller: codeController,
  170. decoration: InputDecoration(
  171. label: Text(
  172. "Para cancelar debe capturar el código")),
  173. ),
  174. ),
  175. Row(
  176. mainAxisAlignment:
  177. MainAxisAlignment.spaceBetween,
  178. children: [
  179. TextButton(
  180. onPressed: () =>
  181. Navigator.of(context).pop(false),
  182. child: const Text('No',
  183. style: TextStyle(fontSize: 18)),
  184. style: ButtonStyle(
  185. padding: MaterialStatePropertyAll(
  186. EdgeInsets.fromLTRB(
  187. 20, 10, 20, 10)),
  188. backgroundColor:
  189. MaterialStatePropertyAll(
  190. Colors.red),
  191. foregroundColor:
  192. MaterialStatePropertyAll(
  193. AppTheme.secondary)),
  194. ),
  195. TextButton(
  196. onPressed: () {
  197. final now = DateTime.now().toUtc();
  198. timezone.initializeTimeZones();
  199. final pacificTimeZone =
  200. timezone.getLocation(
  201. 'America/Los_Angeles');
  202. final date =
  203. timezone.TZDateTime.from(
  204. now, pacificTimeZone);
  205. final codigoTotp =
  206. OTP.generateTOTPCodeString(
  207. 'TYSNE4CMT5LVLGWS',
  208. date.millisecondsSinceEpoch,
  209. algorithm: Algorithm.SHA1,
  210. isGoogle: true);
  211. String codigo = codeController.text;
  212. print(
  213. "totp: $codigoTotp codigo:$codigo");
  214. List<String> codigosEstaticos = [
  215. '172449',
  216. '827329',
  217. // Agregar más token fijos
  218. ];
  219. bool esCodigoEstatico =
  220. codigosEstaticos
  221. .contains(codigo);
  222. print(
  223. "¿Es código estático? ${esCodigoEstatico} ${!esCodigoEstatico && codigo != codigoTotp}");
  224. if (!esCodigoEstatico &&
  225. codigo != codigoTotp) {
  226. ScaffoldMessenger.of(context)
  227. .showSnackBar(SnackBar(
  228. content: Text(
  229. 'El código no es correcto'),
  230. duration: Duration(seconds: 2),
  231. ));
  232. return;
  233. } else {
  234. codeController.text = '';
  235. Navigator.of(context).pop(true);
  236. }
  237. },
  238. child: const Text('Sí',
  239. style: TextStyle(fontSize: 18)),
  240. style: ButtonStyle(
  241. padding: MaterialStatePropertyAll(
  242. EdgeInsets.fromLTRB(
  243. 20, 10, 20, 10)),
  244. backgroundColor:
  245. MaterialStatePropertyAll(
  246. AppTheme.tertiary),
  247. foregroundColor:
  248. MaterialStatePropertyAll(
  249. AppTheme.quaternary)),
  250. ),
  251. ],
  252. )
  253. ],
  254. ),
  255. ],
  256. );
  257. },
  258. ) ??
  259. false;
  260. if (confirmado) {
  261. await Provider.of<PedidoViewModel>(context, listen: false)
  262. .cancelarPedido(item.id);
  263. ScaffoldMessenger.of(context).showSnackBar(SnackBar(
  264. content: Text("Pedido cancelado correctamente")));
  265. }
  266. },
  267. )
  268. ],
  269. icon: const Icon(Icons.more_vert),
  270. )
  271. ])),
  272. if (_isMesasActive)
  273. DataCell(
  274. Text(mesa.nombre ?? "Mesa desconocida"),
  275. onTap: () => go(item),
  276. ),
  277. DataCell(
  278. Text(item.folio.toString()),
  279. onTap: () => go(item),
  280. ),
  281. DataCell(
  282. Text(item.nombreCliente ?? "Sin nombre"),
  283. onTap: () => go(item),
  284. ),
  285. DataCell(
  286. Text(item.comentarios ?? "Sin comentarios"),
  287. onTap: () => go(item),
  288. ),
  289. DataCell(
  290. Text(item.estatus ?? "Sin Estatus"),
  291. onTap: () => go(item),
  292. ),
  293. DataCell(
  294. Text(_formatDateTime(item.peticion)),
  295. onTap: () => go(item),
  296. ),
  297. DataCell(
  298. Text(sincronizadoStatus),
  299. onTap: () => go(item),
  300. ),
  301. ]));
  302. }
  303. return Scaffold(
  304. appBar: AppBar(
  305. title: Text(
  306. 'Pedidos',
  307. style: TextStyle(
  308. color: AppTheme.secondary, fontWeight: FontWeight.w500),
  309. ),
  310. actions: <Widget>[
  311. if (userPermisos.contains(Usuario.VER_REPORTE))
  312. IconButton(
  313. icon: const Icon(Icons.save_alt),
  314. onPressed: exportCSV,
  315. tooltip: 'Exportar a CSV',
  316. ),
  317. ],
  318. iconTheme: IconThemeData(color: AppTheme.secondary)),
  319. body: Stack(
  320. children: [
  321. Column(
  322. children: [
  323. Expanded(
  324. child: ListView(
  325. padding: const EdgeInsets.fromLTRB(8, 0, 8, 0),
  326. children: [
  327. const SizedBox(height: 8),
  328. clase.tarjeta(
  329. Padding(
  330. padding: const EdgeInsets.all(8.0),
  331. child: LayoutBuilder(
  332. builder: (context, constraints) {
  333. if (screenWidth > 1000) {
  334. return Row(
  335. crossAxisAlignment: CrossAxisAlignment.end,
  336. children: [
  337. Expanded(
  338. flex: 7,
  339. child: _buildDateRangePicker(),
  340. ),
  341. const SizedBox(width: 5),
  342. botonBuscar()
  343. ],
  344. );
  345. } else {
  346. return Column(
  347. children: [
  348. Row(
  349. children: [_buildDateRangePicker()],
  350. ),
  351. Row(
  352. children: [botonBuscar()],
  353. ),
  354. ],
  355. );
  356. }
  357. },
  358. ),
  359. ),
  360. ),
  361. const SizedBox(height: 8),
  362. pvm.isLoading
  363. ? const Center(child: CircularProgressIndicator())
  364. : Container(),
  365. clase.tarjeta(
  366. Column(
  367. children: [
  368. LayoutBuilder(builder: (context, constraints) {
  369. return SingleChildScrollView(
  370. scrollDirection: Axis.vertical,
  371. child: Scrollbar(
  372. controller: horizontalScrollController,
  373. interactive: true,
  374. thumbVisibility: true,
  375. thickness: 10.0,
  376. child: SingleChildScrollView(
  377. controller: horizontalScrollController,
  378. scrollDirection: Axis.horizontal,
  379. child: ConstrainedBox(
  380. constraints: BoxConstraints(
  381. minWidth: isMobile
  382. ? constraints.maxWidth
  383. : screenWidth),
  384. child: DataTable(
  385. columnSpacing: columnSpacing,
  386. sortAscending: true,
  387. sortColumnIndex: 1,
  388. columns: [
  389. DataColumn(
  390. label: Text(" ", style: estilo)),
  391. if (_isMesasActive)
  392. DataColumn(
  393. label:
  394. Text("MESA", style: estilo)),
  395. DataColumn(
  396. label:
  397. Text("FOLIO", style: estilo)),
  398. DataColumn(
  399. label:
  400. Text("NOMBRE", style: estilo)),
  401. DataColumn(
  402. label: Text("COMENTARIOS",
  403. style: estilo)),
  404. DataColumn(
  405. label:
  406. Text("ESTATUS", style: estilo)),
  407. DataColumn(
  408. label:
  409. Text("FECHA", style: estilo)),
  410. DataColumn(
  411. label: Text("SINCRONIZADO",
  412. style: estilo)),
  413. ],
  414. rows: registros,
  415. ),
  416. ),
  417. ),
  418. ),
  419. );
  420. }),
  421. ],
  422. ),
  423. ),
  424. const SizedBox(height: 15),
  425. if (!pvm.isLoading)
  426. Row(
  427. mainAxisAlignment: MainAxisAlignment.center,
  428. children: [
  429. TextButton(
  430. onPressed:
  431. pvm.currentPage > 1 ? pvm.previousPage : null,
  432. child: Text('Anterior'),
  433. style: ButtonStyle(
  434. backgroundColor:
  435. MaterialStateProperty.resolveWith<Color?>(
  436. (Set<MaterialState> states) {
  437. if (states.contains(MaterialState.disabled)) {
  438. return Colors.grey;
  439. }
  440. return AppTheme.tertiary;
  441. },
  442. ),
  443. foregroundColor:
  444. MaterialStateProperty.resolveWith<Color?>(
  445. (Set<MaterialState> states) {
  446. if (states.contains(MaterialState.disabled)) {
  447. return Colors.black;
  448. }
  449. return Colors.white;
  450. },
  451. ),
  452. ),
  453. ),
  454. SizedBox(width: 15),
  455. Text(
  456. 'Página ${pvm.currentPage} de ${pvm.totalPages}'),
  457. SizedBox(width: 15),
  458. TextButton(
  459. onPressed: pvm.currentPage < pvm.totalPages
  460. ? pvm.nextPage
  461. : null,
  462. child: Text('Siguiente'),
  463. style: ButtonStyle(
  464. backgroundColor:
  465. MaterialStateProperty.resolveWith<Color?>(
  466. (Set<MaterialState> states) {
  467. if (states.contains(MaterialState.disabled)) {
  468. return Colors.grey;
  469. }
  470. return AppTheme.tertiary;
  471. },
  472. ),
  473. foregroundColor:
  474. MaterialStateProperty.resolveWith<Color?>(
  475. (Set<MaterialState> states) {
  476. if (states.contains(MaterialState.disabled)) {
  477. return Colors.black;
  478. }
  479. return Colors.white;
  480. },
  481. ),
  482. ),
  483. ),
  484. ],
  485. ),
  486. const SizedBox(height: 15),
  487. ],
  488. ),
  489. ),
  490. ],
  491. ),
  492. Positioned(
  493. bottom: 16,
  494. right: 16,
  495. child: FloatingActionButton.extended(
  496. heroTag: 'addPedido',
  497. onPressed: () async {
  498. final corteCajaViewModel =
  499. Provider.of<CorteCajaViewModel>(context, listen: false);
  500. if (corteCajaViewModel.hasOpenCorteCaja()) {
  501. await Navigator.push(
  502. context,
  503. MaterialPageRoute(
  504. builder: (context) => PedidoForm(),
  505. ),
  506. ).then((_) =>
  507. Provider.of<PedidoViewModel>(context, listen: false)
  508. .fetchLocalPedidosForScreen());
  509. } else {
  510. alerta(context,
  511. etiqueta:
  512. 'Solo se pueden realizar pedidos cuando se encuentre la caja abierta.');
  513. }
  514. },
  515. icon: Icon(Icons.add, size: 30, color: AppTheme.quaternary),
  516. label: Text(
  517. "Agregar Pedido",
  518. style: TextStyle(fontSize: 20, color: AppTheme.quaternary),
  519. ),
  520. shape: RoundedRectangleBorder(
  521. borderRadius: BorderRadius.circular(8),
  522. ),
  523. materialTapTargetSize: MaterialTapTargetSize.shrinkWrap,
  524. backgroundColor: AppTheme.tertiary,
  525. ),
  526. ),
  527. ],
  528. ),
  529. );
  530. }
  531. Widget _buildDateRangePicker() {
  532. return Row(
  533. children: [
  534. Expanded(
  535. flex: 3,
  536. child: AppTextField(
  537. prefixIcon: const Icon(Icons.search),
  538. etiqueta: 'Búsqueda por folio...',
  539. controller: _busqueda,
  540. hintText: 'Búsqueda por folio...',
  541. ),
  542. ),
  543. const SizedBox(width: 5),
  544. Expanded(
  545. flex: 3,
  546. child: clase.FechaSelectWidget(
  547. fecha: fechaInicio,
  548. onFechaChanged: (d) {
  549. setState(() {
  550. fechaInicio = d;
  551. });
  552. },
  553. etiqueta: "Fecha Inicial",
  554. context: context,
  555. ),
  556. ),
  557. const SizedBox(width: 5),
  558. Expanded(
  559. flex: 3,
  560. child: clase.FechaSelectWidget(
  561. fecha: fechaFin,
  562. onFechaChanged: (d) {
  563. setState(() {
  564. fechaFin = d;
  565. });
  566. },
  567. etiqueta: "Fecha Final",
  568. context: context,
  569. ),
  570. ),
  571. ],
  572. );
  573. }
  574. Widget botonBuscar() {
  575. return Expanded(
  576. flex: 2,
  577. child: Row(
  578. children: [
  579. Expanded(
  580. flex: 2,
  581. child: Padding(
  582. padding: const EdgeInsets.only(bottom: 5),
  583. child: ElevatedButton(
  584. onPressed: clearSearchAndReset,
  585. style: ElevatedButton.styleFrom(
  586. shape: RoundedRectangleBorder(
  587. borderRadius: BorderRadius.circular(20.0),
  588. ),
  589. backgroundColor: AppTheme.tertiary,
  590. padding: const EdgeInsets.symmetric(vertical: 25),
  591. ),
  592. child: Text('Limpiar',
  593. style: TextStyle(color: AppTheme.quaternary)),
  594. ),
  595. ),
  596. ),
  597. const SizedBox(width: 8),
  598. Expanded(
  599. flex: 2,
  600. child: Padding(
  601. padding: const EdgeInsets.only(bottom: 5),
  602. child: ElevatedButton(
  603. onPressed: () async {
  604. if (_busqueda.text.isNotEmpty) {
  605. await Provider.of<PedidoViewModel>(context, listen: false)
  606. .buscarPedidosPorFolio(_busqueda.text.trim());
  607. } else if (fechaInicio != null && fechaFin != null) {
  608. DateTime fechaInicioUTC = DateTime(fechaInicio!.year,
  609. fechaInicio!.month, fechaInicio!.day)
  610. .toUtc();
  611. DateTime fechaFinUTC = DateTime(fechaFin!.year,
  612. fechaFin!.month, fechaFin!.day, 23, 59, 59)
  613. .toUtc();
  614. await Provider.of<PedidoViewModel>(context, listen: false)
  615. .buscarPedidosPorFecha(fechaInicioUTC, fechaFinUTC);
  616. } else {
  617. ScaffoldMessenger.of(context).showSnackBar(const SnackBar(
  618. content: Text(
  619. 'Introduce un folio o selecciona un rango de fechas para buscar.')));
  620. }
  621. },
  622. style: ElevatedButton.styleFrom(
  623. shape: RoundedRectangleBorder(
  624. borderRadius: BorderRadius.circular(20.0),
  625. ),
  626. backgroundColor: AppTheme.tertiary,
  627. padding: const EdgeInsets.symmetric(vertical: 25),
  628. ),
  629. child: Text('Buscar',
  630. style: TextStyle(color: AppTheme.quaternary)),
  631. ),
  632. ),
  633. ),
  634. ],
  635. ),
  636. );
  637. }
  638. void alertaSync(BuildContext context) {
  639. showDialog(
  640. context: context,
  641. builder: (BuildContext context) {
  642. return AlertDialog(
  643. title: const Text('Confirmación',
  644. style: TextStyle(fontWeight: FontWeight.w500, fontSize: 22)),
  645. content: const Text('¿Deseas sincronizar todos los pedidos de nuevo?',
  646. style: TextStyle(fontSize: 18)),
  647. actions: [
  648. Row(
  649. mainAxisAlignment: MainAxisAlignment.spaceBetween,
  650. children: [
  651. TextButton(
  652. child: const Text('No', style: TextStyle(fontSize: 18)),
  653. style: ButtonStyle(
  654. padding: MaterialStatePropertyAll(
  655. EdgeInsets.fromLTRB(20, 10, 20, 10)),
  656. backgroundColor: MaterialStatePropertyAll(Colors.red),
  657. foregroundColor:
  658. MaterialStatePropertyAll(AppTheme.secondary)),
  659. onPressed: () {
  660. Navigator.of(context).pop();
  661. },
  662. ),
  663. TextButton(
  664. child: const Text('Sí', style: TextStyle(fontSize: 18)),
  665. style: ButtonStyle(
  666. padding: MaterialStatePropertyAll(
  667. EdgeInsets.fromLTRB(20, 10, 20, 10)),
  668. backgroundColor:
  669. MaterialStatePropertyAll(AppTheme.tertiary),
  670. foregroundColor:
  671. MaterialStatePropertyAll(AppTheme.quaternary)),
  672. onPressed: () {
  673. // No hace nada por el momento
  674. Navigator.of(context).pop();
  675. },
  676. ),
  677. ],
  678. )
  679. ],
  680. );
  681. },
  682. );
  683. }
  684. String _formatDateTime(String? dateTimeString) {
  685. if (dateTimeString == null) return "Sin fecha";
  686. DateTime parsedDate = DateTime.parse(dateTimeString);
  687. var formatter = DateFormat('dd-MM-yyyy HH:mm:ss');
  688. return formatter.format(parsedDate.toLocal());
  689. }
  690. Future<void> imprimirEstadoTicket(BuildContext context, Pedido pedido) async {
  691. if (pedido.productos == null || pedido.productos.isEmpty) {
  692. pedido = (await Provider.of<PedidoViewModel>(context, listen: false)
  693. .fetchPedidoConProductos(pedido.id))!;
  694. if (pedido == null) {
  695. ScaffoldMessenger.of(context).showSnackBar(
  696. SnackBar(content: Text('No se pudo cargar el pedido')),
  697. );
  698. return;
  699. }
  700. }
  701. final pdf = pw.Document();
  702. pdf.addPage(generarPaginaSegundoTicket(pedido));
  703. final pdfBytes = Uint8List.fromList(await pdf.save());
  704. await printPdf(pdfBytes);
  705. }
  706. }