import 'package:flutter/material.dart'; import 'package:intl/intl.dart'; import 'package:pdf/pdf.dart'; import 'package:pdf/widgets.dart' as pw; import 'package:printing/printing.dart'; /// Service for generating and printing warehouse export forms class PrintService { /// Generate and print a warehouse export form static Future printWarehouseExport({ required BuildContext context, required String warehouseName, required int productId, required String productCode, required String productName, String? stageName, required double passedKg, required int passedPcs, required double issuedKg, required int issuedPcs, String? responsibleName, String? receiverName, String? barcodeData, }) async { // Load Vietnamese-compatible fonts using PdfGoogleFonts // Noto Sans has excellent Vietnamese character support final fontRegular = await PdfGoogleFonts.notoSansRegular(); final fontBold = await PdfGoogleFonts.notoSansBold(); final pdf = pw.Document(); // Format current date final dt = DateFormat('dd/MM/yyyy HH:mm').format(DateTime.now()); // Add page to PDF with theme for Vietnamese font support pdf.addPage( pw.Page( pageFormat: PdfPageFormat.a4, margin: const pw.EdgeInsets.all(12), theme: pw.ThemeData.withFont( base: fontRegular, bold: fontBold, ), build: (pw.Context pdfContext) { return pw.Container( padding: const pw.EdgeInsets.all(12), child: pw.Column( crossAxisAlignment: pw.CrossAxisAlignment.start, children: [ // Title pw.Center( child: pw.Column( children: [ pw.Text( 'PHIẾU XUẤT KHO', style: pw.TextStyle( fontSize: 20, fontWeight: pw.FontWeight.bold, ), ), pw.SizedBox(height: 8), pw.Text( 'Công ty TNHH Cơ Khí Chính Xác Minh Thư', style: const pw.TextStyle(fontSize: 16), ), pw.SizedBox(height: 4), pw.Text( warehouseName, style: const pw.TextStyle(fontSize: 14), ), pw.SizedBox(height: 4), pw.Text( 'Ngày: $dt', style: pw.TextStyle( fontSize: 12, color: PdfColors.grey700, ), ), ], ), ), pw.SizedBox(height: 16), // Product information box pw.Container( decoration: pw.BoxDecoration( border: pw.Border.all(color: PdfColors.black, width: 0.5), borderRadius: pw.BorderRadius.circular(8), ), padding: const pw.EdgeInsets.all(8), child: pw.Column( children: [ pw.Row( children: [ pw.Expanded( child: pw.Column( crossAxisAlignment: pw.CrossAxisAlignment.start, children: [ pw.Text( 'ProductId', style: pw.TextStyle( fontSize: 10, color: PdfColors.grey700, ), ), pw.SizedBox(height: 2), pw.Text( '$productId', style: pw.TextStyle( fontSize: 12, fontWeight: pw.FontWeight.bold, ), ), ], ), ), pw.Expanded( child: pw.Column( crossAxisAlignment: pw.CrossAxisAlignment.start, children: [ pw.Text( 'Mã sản phẩm', style: pw.TextStyle( fontSize: 10, color: PdfColors.grey700, ), ), pw.SizedBox(height: 2), pw.Text( productCode, style: pw.TextStyle( fontSize: 12, fontWeight: pw.FontWeight.bold, ), ), ], ), ), ], ), pw.SizedBox(height: 8), pw.Row( children: [ pw.Expanded( child: pw.Column( crossAxisAlignment: pw.CrossAxisAlignment.start, children: [ pw.Text( 'Tên sản phẩm', style: pw.TextStyle( fontSize: 10, color: PdfColors.grey700, ), ), pw.SizedBox(height: 2), pw.Text( productName, style: pw.TextStyle( fontSize: 12, fontWeight: pw.FontWeight.bold, ), ), ], ), ), pw.Expanded( child: pw.Column( crossAxisAlignment: pw.CrossAxisAlignment.start, children: [ pw.Text( 'Công đoạn', style: pw.TextStyle( fontSize: 10, color: PdfColors.grey700, ), ), pw.SizedBox(height: 2), pw.Text( stageName ?? '-', style: pw.TextStyle( fontSize: 12, fontWeight: pw.FontWeight.bold, ), ), ], ), ), ], ), ], ), ), pw.SizedBox(height: 12), // Quantities box pw.Container( decoration: pw.BoxDecoration( border: pw.Border.all(color: PdfColors.black, width: 0.5), borderRadius: pw.BorderRadius.circular(8), ), padding: const pw.EdgeInsets.all(8), child: pw.Column( crossAxisAlignment: pw.CrossAxisAlignment.start, children: [ pw.Text( 'Số lượng:', style: pw.TextStyle( fontSize: 10, color: PdfColors.grey700, ), ), pw.SizedBox(height: 6), pw.Table( border: pw.TableBorder.all( color: PdfColors.black, width: 0.5, ), children: [ // Header pw.TableRow( decoration: const pw.BoxDecoration( color: PdfColors.grey300, ), children: [ pw.Padding( padding: const pw.EdgeInsets.all(8), child: pw.Text( 'Loại', style: pw.TextStyle( fontWeight: pw.FontWeight.bold, ), ), ), pw.Padding( padding: const pw.EdgeInsets.all(8), child: pw.Text( 'KG', style: pw.TextStyle( fontWeight: pw.FontWeight.bold, ), ), ), pw.Padding( padding: const pw.EdgeInsets.all(8), child: pw.Text( 'PCS', style: pw.TextStyle( fontWeight: pw.FontWeight.bold, ), ), ), ], ), // Passed quantity row pw.TableRow( children: [ pw.Padding( padding: const pw.EdgeInsets.all(8), child: pw.Text('Hàng đạt'), ), pw.Padding( padding: const pw.EdgeInsets.all(8), child: pw.Text(passedKg.toStringAsFixed(2)), ), pw.Padding( padding: const pw.EdgeInsets.all(8), child: pw.Text('$passedPcs'), ), ], ), // Issued quantity row pw.TableRow( children: [ pw.Padding( padding: const pw.EdgeInsets.all(8), child: pw.Text('Hàng lỗi'), ), pw.Padding( padding: const pw.EdgeInsets.all(8), child: pw.Text(issuedKg.toStringAsFixed(2)), ), pw.Padding( padding: const pw.EdgeInsets.all(8), child: pw.Text('$issuedPcs'), ), ], ), ], ), ], ), ), pw.SizedBox(height: 12), // Responsible person box pw.Container( decoration: pw.BoxDecoration( border: pw.Border.all(color: PdfColors.black, width: 0.5), borderRadius: pw.BorderRadius.circular(8), ), padding: const pw.EdgeInsets.all(8), child: pw.Row( children: [ pw.Text( 'Nhân viên kho: ', style: pw.TextStyle( fontSize: 10, color: PdfColors.grey700, ), ), pw.Text( responsibleName ?? '-', style: pw.TextStyle( fontSize: 12, fontWeight: pw.FontWeight.bold, ), ), ], ), ), pw.SizedBox(height: 12), pw.Container( decoration: pw.BoxDecoration( border: pw.Border.all(color: PdfColors.black, width: 0.5), borderRadius: pw.BorderRadius.circular(8), ), padding: const pw.EdgeInsets.all(8), child: pw.Row( children: [ pw.Text( 'Nhân viên tiếp nhận: ', style: pw.TextStyle( fontSize: 10, color: PdfColors.grey700, ), ), pw.Text( receiverName ?? '-', style: pw.TextStyle( fontSize: 12, fontWeight: pw.FontWeight.bold, ), ), ], ), ), pw.SizedBox(height: 12), // Barcode section if (barcodeData != null && barcodeData.isNotEmpty) pw.Center( child: pw.BarcodeWidget( barcode: pw.Barcode.code128(), data: barcodeData, width: 200, height: 60, ), ), pw.Spacer(), // Footer signature section pw.Row( mainAxisAlignment: pw.MainAxisAlignment.center, children: [ pw.Container( width: 150, child: pw.Column( children: [ pw.Text( 'Người nhận', style: pw.TextStyle( fontSize: 10, color: PdfColors.grey700, ), ), pw.SizedBox(height: 40), pw.Container( height: 1, color: PdfColors.grey700, ), ], ), ), ], ), ], ), ); }, ), ); // Show print preview dialog await Printing.layoutPdf( onLayout: (PdfPageFormat format) async => pdf.save(), name: 'warehouse_export_${productCode}_${DateTime.now().millisecondsSinceEpoch}.pdf', ); } }