Files
minhthu/lib/core/services/print_service.dart
2025-11-02 20:40:11 +07:00

408 lines
16 KiB
Dart

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<void> 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',
);
}
}