import 'dart:typed_data'; import 'package:flutter/material.dart'; import 'package:intl/intl.dart'; import 'package:pdf/pdf.dart'; import 'package:pdf/widgets.dart' as pw; import 'package:provider/provider.dart'; import '../../models/models.dart'; import '../../viewmodels/viewmodels.dart'; import 'package:printing/printing.dart'; import 'package:flutter/services.dart' show rootBundle; Future imprimirTicketsJuntos(BuildContext context, Pedido pedido) async { bool ticketCocinaActivo = await Provider.of(context, listen: false) .isVariableActive('ticket_cocina'); bool ticketVentaActivo = await Provider.of(context, listen: false) .isVariableActive('ticket_venta'); final pdf = pw.Document(); final image = pw.MemoryImage( (await rootBundle.load('assets/icono-BN.png')).buffer.asUint8List(), ); if (ticketVentaActivo) { pdf.addPage( generarPaginaPrimerTicket(pedido, image), ); } if (ticketCocinaActivo) { final paginaSegundoTicket = await generarPaginaSegundoTicket(context, pedido); pdf.addPage(paginaSegundoTicket); } await printPdf(Uint8List.fromList(await pdf.save())); } pw.Page generarPaginaPrimerTicket(Pedido pedido, pw.MemoryImage image) { final numberFormat = NumberFormat('#,##0.00', 'es_MX'); double totalSinDescuento = 0; double totalConDescuento = 0; double descuento = pedido.descuento?.toDouble() ?? 0.0; double precioDescuento = 0; final productList = pedido.productos .map( (producto) { final productPrice = producto.producto?.precio ?? 0.0; final productTotal = productPrice * (producto.cantidad ?? 1); totalSinDescuento += productTotal; final toppingsList = producto.toppings.where((topping) { final toppingPrice = topping.topping?.precio ?? 0.0; return toppingPrice > 0; }).map((topping) { final toppingPrice = topping.topping?.precio ?? 0.0; totalSinDescuento += toppingPrice * (producto.cantidad ?? 1); return pw.Row( children: [ pw.Text( '- ${topping.topping?.nombre ?? "Topping no especificado"}', style: const pw.TextStyle(fontSize: 7)), pw.Spacer(), pw.Text('\$${numberFormat.format(toppingPrice)}', style: const pw.TextStyle(fontSize: 7)), ], ); }).toList(); return [ pw.Row( mainAxisAlignment: pw.MainAxisAlignment.spaceBetween, children: [ pw.Expanded( flex: 2, child: pw.Text( producto.producto?.nombre ?? "Producto no especificado", style: const pw.TextStyle(fontSize: 7)), ), pw.Expanded( flex: 1, child: pw.Text('x${producto.cantidad}', style: const pw.TextStyle(fontSize: 7)), ), pw.Padding( padding: pw.EdgeInsets.only(right: 2), child: pw.Expanded( flex: 1, child: pw.Text('\$${numberFormat.format(productPrice)}', style: const pw.TextStyle(fontSize: 8)), ), ) ], ), ...toppingsList, ]; }, ) .expand((e) => e) .toList(); precioDescuento = totalSinDescuento * (descuento / 100); totalConDescuento = totalSinDescuento - precioDescuento; return pw.Page( pageFormat: PdfPageFormat.roll57, build: (pw.Context context) { return pw.Column( crossAxisAlignment: pw.CrossAxisAlignment.center, children: [ pw.Padding( padding: const pw.EdgeInsets.only(right: 20), child: pw.Center(child: pw.Image(image, width: 50, height: 50))), pw.SizedBox(height: 10), pw.Padding( padding: const pw.EdgeInsets.only(right: 15), child: pw.Column(children: [ pw.SizedBox(height: 5), pw.Text('Fecha: ${pedido.peticion}', style: const pw.TextStyle(fontSize: 9)), pw.Text('Conalep', style: const pw.TextStyle(fontSize: 9)), pw.Text('Hermosillo', style: const pw.TextStyle(fontSize: 9)), ])), pw.SizedBox(height: 10), pw.Text('Pedido: ${pedido.folio}', style: pw.TextStyle( fontWeight: pw.FontWeight.bold, fontSize: 10)), pw.SizedBox(height: 10), pw.Padding( padding: const pw.EdgeInsets.only(right: 20), child: pw.Column(children: productList)), pw.Divider(), pw.Row( mainAxisAlignment: pw.MainAxisAlignment.spaceBetween, children: [ pw.Text('Subtotal:', style: pw.TextStyle( fontWeight: pw.FontWeight.bold, fontSize: 9)), pw.Padding( padding: const pw.EdgeInsets.only(right: 30), child: pw.Text( '\$${numberFormat.format(totalSinDescuento)}', style: pw.TextStyle( fontWeight: pw.FontWeight.bold, fontSize: 9)), ), ], ), if (descuento > 0) ...[ pw.Row( mainAxisAlignment: pw.MainAxisAlignment.spaceBetween, children: [ pw.Text('Descuento:', style: pw.TextStyle( fontWeight: pw.FontWeight.bold, fontSize: 9)), pw.Padding( padding: const pw.EdgeInsets.only(right: 30), child: pw.Text( '-\$${numberFormat.format(precioDescuento)}', style: pw.TextStyle( fontWeight: pw.FontWeight.bold, fontSize: 9)), ), ], ), ], pw.Row( mainAxisAlignment: pw.MainAxisAlignment.spaceBetween, children: [ pw.Text('Total:', style: pw.TextStyle( fontWeight: pw.FontWeight.bold, fontSize: 9)), pw.Padding( padding: const pw.EdgeInsets.only(right: 30), child: pw.Text( '\$${numberFormat.format(totalConDescuento)}', style: pw.TextStyle( fontWeight: pw.FontWeight.bold, fontSize: 9)), ), ], ), pw.SizedBox(height: 5), pw.Padding( padding: const pw.EdgeInsets.only(right: 15), child: pw.Text('¡GRACIAS POR SU COMPRA!', style: pw.TextStyle( fontSize: 8, fontWeight: pw.FontWeight.bold))), pw.Divider(), pw.SizedBox(height: 20), pw.Text('.', style: pw.TextStyle(fontSize: 1)), ]); }); } //Ticket PC // Future generarPaginaSegundoTicket( // BuildContext context, Pedido pedido) async { // final numberFormat = NumberFormat('#,##0.00', 'es_MX'); // double subtotal = 0; // double totalConDescuento = 0; // double descuento = pedido.descuento?.toDouble() ?? 0.0; // double precioDescuento = 0; // final sucursalVariable = // await Provider.of(context, listen: false) // .getVariableByClave('Sucursal'); // final sucursalDescripcion = sucursalVariable?.descripcion ?? ''; // List content = [ // pw.Padding( // padding: pw.EdgeInsets.only(right: 10), // child: pw.Row( // mainAxisAlignment: pw.MainAxisAlignment.spaceAround, // children: [ // pw.Text('${pedido.folio}/ ', // style: // pw.TextStyle(fontSize: 10.5, fontWeight: pw.FontWeight.bold)), // if (sucursalDescripcion.isNotEmpty) // pw.Text('$sucursalDescripcion/ ', // style: pw.TextStyle( // fontWeight: pw.FontWeight.bold, fontSize: 10.5)), // ], // ), // ), // pw.SizedBox(height: 2), // pw.Row( // mainAxisAlignment: pw.MainAxisAlignment.spaceAround, // children: [ // pw.Text('${_formatDateTime(pedido.peticion)}', // style: // pw.TextStyle(fontSize: 10.5, fontWeight: pw.FontWeight.bold)), // ], // ), // pw.SizedBox(height: 2), // if (pedido.nombreCliente != null && pedido.nombreCliente!.isNotEmpty) // pw.Text('Cliente: ${pedido.nombreCliente}', // style: pw.TextStyle(fontWeight: pw.FontWeight.bold, fontSize: 10.5)), // pw.SizedBox(height: 2), // ]; // // Mostrar los productos con la cantidad, el precio total y el precio de los toppings // content.addAll(pedido.productos // .map((producto) { // final productPrice = producto.producto?.precio ?? 0.0; // final productTotal = productPrice * (producto.cantidad ?? 1); // subtotal += productTotal; // final toppingsList = producto.toppings.map((topping) { // final toppingPrice = topping.topping?.precio ?? 0.0; // final toppingTotal = toppingPrice * (producto.cantidad ?? 1); // subtotal += toppingTotal; // return pw.Row( // mainAxisAlignment: pw.MainAxisAlignment.start, // children: [ // pw.Expanded( // flex: 3, // child: pw.Text( // '-${topping.topping?.nombre ?? "Topping no especificado"}', // style: const pw.TextStyle(fontSize: 8.5)), // ), // if (toppingPrice > 0) // pw.Expanded( // flex: 1, // child: pw.Text( // '\$${numberFormat.format(toppingTotal)}', // style: const pw.TextStyle(fontSize: 8.5), // textAlign: pw.TextAlign.right, // ), // ), // ], // ); // }).toList(); // return [ // pw.Row( // mainAxisAlignment: pw.MainAxisAlignment.spaceBetween, // children: [ // pw.Expanded( // flex: 1, // child: pw.Text('${producto.cantidad}', // style: const pw.TextStyle(fontSize: 10.5)), // ), // pw.Expanded( // flex: 6, // child: pw.Text( // producto.producto?.nombre ?? "Producto no especificado", // style: const pw.TextStyle(fontSize: 10.5)), // ), // pw.Expanded( // flex: 4, // child: pw.Align( // alignment: pw.Alignment.centerLeft, // child: pw.Text( // '\$${numberFormat.format(productTotal)}', // style: const pw.TextStyle(fontSize: 10.5), // ), // )), // ], // ), // ...toppingsList, // ]; // }) // .expand((e) => e) // .toList()); // // Calcular el descuento y el total final // precioDescuento = subtotal * (descuento / 100); // totalConDescuento = subtotal - precioDescuento; // content.add(pw.Divider()); // if (descuento > 0) { // content.addAll([ // pw.Row( // mainAxisAlignment: pw.MainAxisAlignment.spaceBetween, // children: [ // pw.Text('Subtotal:', // style: // pw.TextStyle(fontWeight: pw.FontWeight.bold, fontSize: 10.5)), // pw.Padding( // padding: pw.EdgeInsets.only(right: 15), // child: pw.Text('\$${numberFormat.format(subtotal)}', // style: pw.TextStyle( // fontWeight: pw.FontWeight.bold, fontSize: 10.5))), // ], // ), // pw.Row( // mainAxisAlignment: pw.MainAxisAlignment.spaceBetween, // children: [ // pw.Text('Descuento:', // style: // pw.TextStyle(fontWeight: pw.FontWeight.bold, fontSize: 10.5)), // pw.Padding( // padding: pw.EdgeInsets.only(right: 15), // child: pw.Text('-\$${numberFormat.format(precioDescuento)}', // style: pw.TextStyle( // fontWeight: pw.FontWeight.bold, fontSize: 10.5))), // ], // ), // ]); // } // content.add( // pw.Row( // mainAxisAlignment: pw.MainAxisAlignment.spaceBetween, // children: [ // pw.Text('Total:', // style: // pw.TextStyle(fontWeight: pw.FontWeight.bold, fontSize: 10.5)), // pw.Padding( // padding: pw.EdgeInsets.only(right: 20), // child: pw.Text( // '\$${numberFormat.format(descuento > 0 ? totalConDescuento : subtotal)}', // style: pw.TextStyle( // fontWeight: pw.FontWeight.bold, fontSize: 10.5))), // ], // ), // ); // if (pedido.comentarios != null && pedido.comentarios!.isNotEmpty) { // content.add(pw.SizedBox(height: 1)); // content.add(pw.Text('Comentarios:', // style: pw.TextStyle(fontWeight: pw.FontWeight.bold, fontSize: 10.5))); // content.add(pw.Padding( // padding: const pw.EdgeInsets.only(right: 15), // child: pw.Text(pedido.comentarios!, // style: const pw.TextStyle(fontSize: 10.5)), // )); // } // content.add(pw.SizedBox(height: 15)); // content.add(pw.Text('.', style: pw.TextStyle(fontSize: 1))); // return pw.Page( // pageFormat: PdfPageFormat.roll57, // margin: pw.EdgeInsets.all(5), // build: (pw.Context context) { // return pw.Column( // crossAxisAlignment: pw.CrossAxisAlignment.center, children: content); // }, // ); // } //Ticket Tablet Future generarPaginaSegundoTicket( BuildContext context, Pedido pedido) async { final numberFormat = NumberFormat('#,##0.00', 'es_MX'); double subtotal = 0; double totalConDescuento = 0; double descuento = pedido.descuento?.toDouble() ?? 0.0; double precioDescuento = 0; final sucursalVariable = await Provider.of(context, listen: false) .getVariableByClave('Sucursal'); final sucursalDescripcion = sucursalVariable?.descripcion ?? ''; List content = [ pw.Row( mainAxisAlignment: pw.MainAxisAlignment.spaceAround, children: [ pw.Text('${pedido.folio}/ ', style: pw.TextStyle(fontSize: 7, fontWeight: pw.FontWeight.bold)), if (sucursalDescripcion.isNotEmpty) pw.Text('$sucursalDescripcion/ ', style: pw.TextStyle(fontWeight: pw.FontWeight.bold, fontSize: 7)), ], ), pw.SizedBox(height: 2), pw.Row( mainAxisAlignment: pw.MainAxisAlignment.spaceAround, children: [ pw.Text('${_formatDateTime(pedido.peticion)}', style: pw.TextStyle(fontSize: 7, fontWeight: pw.FontWeight.bold)), ], ), pw.SizedBox(height: 2), if (pedido.nombreCliente != null && pedido.nombreCliente!.isNotEmpty) pw.Text('Cliente: ${pedido.nombreCliente}', style: pw.TextStyle(fontWeight: pw.FontWeight.bold, fontSize: 7)), pw.SizedBox(height: 2), ]; // Mostrar los productos con la cantidad, el precio total y el precio de los toppings content.addAll(pedido.productos .map((producto) { final productPrice = producto.producto?.precio ?? 0.0; final productTotal = productPrice * (producto.cantidad ?? 1); subtotal += productTotal; final toppingsList = producto.toppings.map((topping) { final toppingPrice = topping.topping?.precio ?? 0.0; final toppingTotal = toppingPrice * (producto.cantidad ?? 1); subtotal += toppingTotal; return pw.Row( mainAxisAlignment: pw.MainAxisAlignment.start, children: [ pw.Expanded( flex: 3, child: pw.Text( '-${topping.topping?.nombre ?? "Topping no especificado"}', style: const pw.TextStyle(fontSize: 6)), ), if (toppingPrice > 0) pw.Expanded( flex: 1, child: pw.Text( '\$${numberFormat.format(toppingTotal)}', style: const pw.TextStyle(fontSize: 6), textAlign: pw.TextAlign.right, ), ), ], ); }).toList(); return [ pw.Row( mainAxisAlignment: pw.MainAxisAlignment.spaceBetween, children: [ pw.Expanded( flex: 1, child: pw.Text('${producto.cantidad}', style: const pw.TextStyle(fontSize: 7)), ), pw.Expanded( flex: 5, child: pw.Text( producto.producto?.nombre ?? "Producto no especificado", style: const pw.TextStyle(fontSize: 7)), ), pw.Expanded( flex: 2, child: pw.Align( alignment: pw.Alignment.centerRight, child: pw.Text( '\$${numberFormat.format(productTotal)}', style: const pw.TextStyle(fontSize: 7), ), )), ], ), ...toppingsList, ]; }) .expand((e) => e) .toList()); // Calcular el descuento y el total final precioDescuento = subtotal * (descuento / 100); totalConDescuento = subtotal - precioDescuento; content.add(pw.Divider()); if (descuento > 0) { content.addAll([ pw.Row( mainAxisAlignment: pw.MainAxisAlignment.spaceBetween, children: [ pw.Text('Subtotal:', style: pw.TextStyle(fontWeight: pw.FontWeight.bold, fontSize: 7)), pw.Text('\$${numberFormat.format(subtotal)}', style: pw.TextStyle(fontWeight: pw.FontWeight.bold, fontSize: 7)), ], ), pw.Row( mainAxisAlignment: pw.MainAxisAlignment.spaceBetween, children: [ pw.Text('Descuento:', style: pw.TextStyle(fontWeight: pw.FontWeight.bold, fontSize: 7)), pw.Text('-\$${numberFormat.format(precioDescuento)}', style: pw.TextStyle(fontWeight: pw.FontWeight.bold, fontSize: 7)), ], ), ]); } content.add( pw.Row( mainAxisAlignment: pw.MainAxisAlignment.spaceBetween, children: [ pw.Text('Total:', style: pw.TextStyle(fontWeight: pw.FontWeight.bold, fontSize: 7)), pw.Text( '\$${numberFormat.format(descuento > 0 ? totalConDescuento : subtotal)}', style: pw.TextStyle(fontWeight: pw.FontWeight.bold, fontSize: 7)), ], ), ); if (pedido.comentarios != null && pedido.comentarios!.isNotEmpty) { content.add(pw.SizedBox(height: 1)); content.add(pw.Text('Comentarios:', style: pw.TextStyle(fontWeight: pw.FontWeight.bold, fontSize: 7))); content.add(pw.Padding( padding: const pw.EdgeInsets.only(right: 15), child: pw.Text(pedido.comentarios!, style: const pw.TextStyle(fontSize: 7)), )); } content.add(pw.SizedBox(height: 1)); return pw.Page( pageFormat: PdfPageFormat.roll57, margin: pw.EdgeInsets.all(5), build: (pw.Context context) { return pw.Column( crossAxisAlignment: pw.CrossAxisAlignment.center, children: content); }, ); } Future printPdf(Uint8List pdfBytes) async { await Printing.layoutPdf( onLayout: (PdfPageFormat format) => pdfBytes, ); } String _formatDateTime(String? dateTimeString) { if (dateTimeString == null) return "Sin fecha"; DateTime parsedDate = DateTime.parse(dateTimeString); var formatter = DateFormat('dd-MM-yyyy HH:mm:ss'); return formatter.format(parsedDate.toLocal()); }