fix UI
This commit is contained in:
@@ -8,6 +8,8 @@ PODS:
|
|||||||
- path_provider_foundation (0.0.1):
|
- path_provider_foundation (0.0.1):
|
||||||
- Flutter
|
- Flutter
|
||||||
- FlutterMacOS
|
- FlutterMacOS
|
||||||
|
- printing (1.0.0):
|
||||||
|
- Flutter
|
||||||
- sqflite_darwin (0.0.4):
|
- sqflite_darwin (0.0.4):
|
||||||
- Flutter
|
- Flutter
|
||||||
- FlutterMacOS
|
- FlutterMacOS
|
||||||
@@ -17,6 +19,7 @@ DEPENDENCIES:
|
|||||||
- flutter_secure_storage (from `.symlinks/plugins/flutter_secure_storage/ios`)
|
- flutter_secure_storage (from `.symlinks/plugins/flutter_secure_storage/ios`)
|
||||||
- mobile_scanner (from `.symlinks/plugins/mobile_scanner/darwin`)
|
- mobile_scanner (from `.symlinks/plugins/mobile_scanner/darwin`)
|
||||||
- path_provider_foundation (from `.symlinks/plugins/path_provider_foundation/darwin`)
|
- path_provider_foundation (from `.symlinks/plugins/path_provider_foundation/darwin`)
|
||||||
|
- printing (from `.symlinks/plugins/printing/ios`)
|
||||||
- sqflite_darwin (from `.symlinks/plugins/sqflite_darwin/darwin`)
|
- sqflite_darwin (from `.symlinks/plugins/sqflite_darwin/darwin`)
|
||||||
|
|
||||||
EXTERNAL SOURCES:
|
EXTERNAL SOURCES:
|
||||||
@@ -28,15 +31,18 @@ EXTERNAL SOURCES:
|
|||||||
:path: ".symlinks/plugins/mobile_scanner/darwin"
|
:path: ".symlinks/plugins/mobile_scanner/darwin"
|
||||||
path_provider_foundation:
|
path_provider_foundation:
|
||||||
:path: ".symlinks/plugins/path_provider_foundation/darwin"
|
:path: ".symlinks/plugins/path_provider_foundation/darwin"
|
||||||
|
printing:
|
||||||
|
:path: ".symlinks/plugins/printing/ios"
|
||||||
sqflite_darwin:
|
sqflite_darwin:
|
||||||
:path: ".symlinks/plugins/sqflite_darwin/darwin"
|
:path: ".symlinks/plugins/sqflite_darwin/darwin"
|
||||||
|
|
||||||
SPEC CHECKSUMS:
|
SPEC CHECKSUMS:
|
||||||
Flutter: cabc95a1d2626b1b06e7179b784ebcf0c0cde467
|
Flutter: cabc95a1d2626b1b06e7179b784ebcf0c0cde467
|
||||||
flutter_secure_storage: 1ed9476fba7e7a782b22888f956cce43e2c62f13
|
flutter_secure_storage: d33dac7ae2ea08509be337e775f6b59f1ff45f12
|
||||||
mobile_scanner: 9157936403f5a0644ca3779a38ff8404c5434a93
|
mobile_scanner: 77265f3dc8d580810e91849d4a0811a90467ed5e
|
||||||
path_provider_foundation: 080d55be775b7414fd5a5ef3ac137b97b097e564
|
path_provider_foundation: 2b6b4c569c0fb62ec74538f866245ac84301af46
|
||||||
sqflite_darwin: 20b2a3a3b70e43edae938624ce550a3cbf66a3d0
|
printing: 233e1b73bd1f4a05615548e9b5a324c98588640b
|
||||||
|
sqflite_darwin: 5a7236e3b501866c1c9befc6771dfd73ffb8702d
|
||||||
|
|
||||||
PODFILE CHECKSUM: 3c63482e143d1b91d2d2560aee9fb04ecc74ac7e
|
PODFILE CHECKSUM: 3c63482e143d1b91d2d2560aee9fb04ecc74ac7e
|
||||||
|
|
||||||
|
|||||||
368
lib/core/services/print_service.dart
Normal file
368
lib/core/services/print_service.dart
Normal file
@@ -0,0 +1,368 @@
|
|||||||
|
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? barcodeData,
|
||||||
|
}) async {
|
||||||
|
final pdf = pw.Document();
|
||||||
|
|
||||||
|
// Format current date
|
||||||
|
final dt = DateFormat('dd/MM/yyyy HH:mm').format(DateTime.now());
|
||||||
|
|
||||||
|
// Add page to PDF
|
||||||
|
pdf.addPage(
|
||||||
|
pw.Page(
|
||||||
|
pageFormat: PdfPageFormat.a4,
|
||||||
|
margin: const pw.EdgeInsets.all(12),
|
||||||
|
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),
|
||||||
|
|
||||||
|
// 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',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
106
lib/docs/import.html
Normal file
106
lib/docs/import.html
Normal file
@@ -0,0 +1,106 @@
|
|||||||
|
<!doctype html>
|
||||||
|
<html lang="vi">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8"/>
|
||||||
|
<title>Phiếu xuất kho</title>
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1"/>
|
||||||
|
<style>
|
||||||
|
:root { --fg:#111; --muted:#666; --border:#000; --primary:#2563eb; }
|
||||||
|
html,body { margin:0; padding:0; font:14px/1.4 system-ui, -apple-system, Segoe UI, Roboto, 'Helvetica Neue', Arial, 'Noto Sans', 'Liberation Sans', sans-serif; color:var(--fg); }
|
||||||
|
.wrap { max-width:720px; margin:0 auto; padding:12px; }
|
||||||
|
.actions { position: sticky; top:0; background:#fff; padding:8px 0; display:flex; gap:8px; justify-content:flex-end; border-bottom:0.1mm solid var(--border); }
|
||||||
|
.actions button { padding:6px 12px; cursor:pointer; border:0.1mm solid var(--border); background:#fff; border-radius:6px; }
|
||||||
|
h1 { font-size:20px; margin:8px 0 4px; text-align:center; }
|
||||||
|
.meta { text-align:center; color:var(--muted); margin-bottom:8px; }
|
||||||
|
.box { border:0.1mm solid var(--border); border-radius:8px; padding:8px; margin:8px 0; }
|
||||||
|
.row { display:flex; gap:8px; margin:6px 0; }
|
||||||
|
.row > div { flex:1; }
|
||||||
|
.label { color:var(--muted); font-size:12px; }
|
||||||
|
.value { font-weight:600; }
|
||||||
|
table { width:100%; border-collapse:collapse; margin-top:6px; }
|
||||||
|
th, td { border:0.1mm solid var(--border); padding:8px; text-align:left; }
|
||||||
|
th { background:#f8fafc; }
|
||||||
|
.barcode { text-align:center; margin:12px 0; }
|
||||||
|
.footer { display:flex; gap:12px; margin:8px 0 4px; }
|
||||||
|
.sign { flex:1; text-align:center; color:var(--muted); padding-top:24px; }
|
||||||
|
/* Ensure printer keeps border colors/thickness */
|
||||||
|
* { -webkit-print-color-adjust: exact; print-color-adjust: exact; }
|
||||||
|
/* Print margins and padding */
|
||||||
|
@page {
|
||||||
|
size: auto;
|
||||||
|
margin: 3mm 0mm; /* outer page margin */
|
||||||
|
}
|
||||||
|
@media print {
|
||||||
|
.actions { display:none; }
|
||||||
|
.wrap { padding:0 4px ; }
|
||||||
|
th { background:#eee; } /* light gray still visible on most printers */
|
||||||
|
/* Force black borders on print */
|
||||||
|
.box, table, th, td { border-color:#000 !important; border-width:0.1mm !important; }
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="wrap">
|
||||||
|
<div class="actions">
|
||||||
|
<button onclick="printAndClose()">In</button>
|
||||||
|
<button onclick="window.close()">Đóng</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<h1>PHIẾU XUẤT KHO</h1>
|
||||||
|
<h3>Công ty TNHH Cơ Khí Chính Xác Minh Thư</h3>
|
||||||
|
<h4>${wareHouseText}</h4>
|
||||||
|
<div class="meta">Ngày: ${dt}</div>
|
||||||
|
<div class="box">
|
||||||
|
<div class="row">
|
||||||
|
<div><div class="label">ProductId</div><div class="value">${productId}</div></div>
|
||||||
|
<div><div class="label">Mã sản phẩm</div><div class="value">${productCode}</div></div>
|
||||||
|
</div>
|
||||||
|
<div class="row">
|
||||||
|
<div><div class="label">Tên sản phẩm</div><div class="value">${productName}</div></div>
|
||||||
|
<div><div class="label">Công đoạn</div><div class="value">${stageName || '-'}</div></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="box">
|
||||||
|
<div class="label">Số lượng:</div>
|
||||||
|
<table>
|
||||||
|
<thead>
|
||||||
|
<tr><th>Loại</th><th>KG</th><th>PCS</th></tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td>Hàng đạt</td>
|
||||||
|
<td>${Number(qty.passedKg || 0)}</td>
|
||||||
|
<td>${Number(qty.passedPcs || 0)}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>Hàng lỗi</td>
|
||||||
|
<td>${Number(qty.issuedKg || 0)}</td>
|
||||||
|
<td>${Number(qty.issuedPcs || 0)}</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
<div class="box">
|
||||||
|
<div class="row">
|
||||||
|
<div><div class="label">Nhân viên kho</div><div class="value">${responsibleName || '-'}</div></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="barcode">
|
||||||
|
${barcodeDataUrl ? `<img alt="Barcode" src="${barcodeDataUrl}" />` : ''}
|
||||||
|
</div>
|
||||||
|
<div class="footer">
|
||||||
|
<div class="sign">
|
||||||
|
.
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
let printed = false;
|
||||||
|
function printAndClose() { printed = true; window.print(); }
|
||||||
|
window.addEventListener('afterprint', () => setTimeout(() => window.close(), 200));
|
||||||
|
window.addEventListener('focus', () => { if (printed) setTimeout(() => window.close(), 400); });
|
||||||
|
window.onload = () => { printAndClose(); };
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -2,6 +2,7 @@ import 'package:flutter/material.dart';
|
|||||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||||
|
|
||||||
import '../../../../core/di/providers.dart';
|
import '../../../../core/di/providers.dart';
|
||||||
|
import '../../../../core/services/print_service.dart';
|
||||||
import '../../../users/domain/entities/user_entity.dart';
|
import '../../../users/domain/entities/user_entity.dart';
|
||||||
import '../../data/models/create_product_warehouse_request.dart';
|
import '../../data/models/create_product_warehouse_request.dart';
|
||||||
import '../../domain/entities/product_stage_entity.dart';
|
import '../../domain/entities/product_stage_entity.dart';
|
||||||
@@ -118,21 +119,7 @@ class _ProductDetailPageState extends ConsumerState<ProductDetailPage> {
|
|||||||
|
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
appBar: AppBar(
|
appBar: AppBar(
|
||||||
title: Column(
|
title: Text('${operationTitle} ${productName}'),
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
|
||||||
children: [
|
|
||||||
Text(
|
|
||||||
operationTitle,
|
|
||||||
style: textTheme.titleMedium,
|
|
||||||
),
|
|
||||||
Text(
|
|
||||||
productName,
|
|
||||||
style: textTheme.bodySmall?.copyWith(
|
|
||||||
color: theme.colorScheme.onSurfaceVariant,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
actions: [
|
actions: [
|
||||||
IconButton(
|
IconButton(
|
||||||
icon: const Icon(Icons.refresh),
|
icon: const Icon(Icons.refresh),
|
||||||
@@ -149,6 +136,11 @@ class _ProductDetailPageState extends ConsumerState<ProductDetailPage> {
|
|||||||
selectedIndex: selectedIndex,
|
selectedIndex: selectedIndex,
|
||||||
theme: theme,
|
theme: theme,
|
||||||
),
|
),
|
||||||
|
bottomNavigationBar: _buildBottomActionBar(
|
||||||
|
selectedStage: selectedStage,
|
||||||
|
stages: stages,
|
||||||
|
theme: theme,
|
||||||
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -277,7 +269,7 @@ class _ProductDetailPageState extends ConsumerState<ProductDetailPage> {
|
|||||||
if (displayStages.isNotEmpty)
|
if (displayStages.isNotEmpty)
|
||||||
Container(
|
Container(
|
||||||
width: double.infinity,
|
width: double.infinity,
|
||||||
padding: const EdgeInsets.all(16),
|
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 4),
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: theme.colorScheme.surfaceContainerHighest,
|
color: theme.colorScheme.surfaceContainerHighest,
|
||||||
border: Border(
|
border: Border(
|
||||||
@@ -289,39 +281,6 @@ class _ProductDetailPageState extends ConsumerState<ProductDetailPage> {
|
|||||||
child: Column(
|
child: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
Row(
|
|
||||||
children: [
|
|
||||||
Text(
|
|
||||||
widget.stageId != null
|
|
||||||
? 'Công đoạn'
|
|
||||||
: 'Công đoạn (${displayStages.length})',
|
|
||||||
style: theme.textTheme.titleSmall?.copyWith(
|
|
||||||
fontWeight: FontWeight.bold,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
if (widget.stageId != null) ...[
|
|
||||||
const Spacer(),
|
|
||||||
Container(
|
|
||||||
padding: const EdgeInsets.symmetric(
|
|
||||||
horizontal: 8,
|
|
||||||
vertical: 4,
|
|
||||||
),
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
color: theme.colorScheme.primaryContainer,
|
|
||||||
borderRadius: BorderRadius.circular(4),
|
|
||||||
),
|
|
||||||
child: Text(
|
|
||||||
'ID: ${widget.stageId}',
|
|
||||||
style: theme.textTheme.bodySmall?.copyWith(
|
|
||||||
color: theme.colorScheme.onPrimaryContainer,
|
|
||||||
fontWeight: FontWeight.bold,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
],
|
|
||||||
),
|
|
||||||
const SizedBox(height: 12),
|
|
||||||
Wrap(
|
Wrap(
|
||||||
spacing: 8,
|
spacing: 8,
|
||||||
runSpacing: 8,
|
runSpacing: 8,
|
||||||
@@ -376,43 +335,24 @@ class _ProductDetailPageState extends ConsumerState<ProductDetailPage> {
|
|||||||
padding: const EdgeInsets.all(16),
|
padding: const EdgeInsets.all(16),
|
||||||
child: Column(
|
child: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
spacing: 16,
|
spacing: 8,
|
||||||
children: [
|
children: [
|
||||||
// Stage header
|
// Stage header
|
||||||
_buildStageHeader(stageToShow, theme),
|
// _buildStageHeader(stageToShow, theme),
|
||||||
|
//
|
||||||
_buildSectionCard(
|
// _buildSectionCard(
|
||||||
theme: theme,
|
// theme: theme,
|
||||||
title: 'Thông tin công đoạn',
|
// title: 'Thông tin công đoạn',
|
||||||
icon: Icons.info_outlined,
|
// icon: Icons.info_outlined,
|
||||||
children: [
|
// children: [
|
||||||
_buildInfoRow('Mã sản phẩm', '${stageToShow.productId}'),
|
// _buildInfoRow('Mã sản phẩm', '${stageToShow.productId}'),
|
||||||
if (stageToShow.productStageId != null)
|
// if (stageToShow.productStageId != null)
|
||||||
_buildInfoRow('Mã công đoạn', '${stageToShow.productStageId}'),
|
// _buildInfoRow('Mã công đoạn', '${stageToShow.productStageId}'),
|
||||||
if (stageToShow.actionTypeId != null)
|
// if (stageToShow.actionTypeId != null)
|
||||||
_buildInfoRow('Mã loại thao tác', '${stageToShow.actionTypeId}'),
|
// _buildInfoRow('Mã loại thao tác', '${stageToShow.actionTypeId}'),
|
||||||
_buildInfoRow('Tên công đoạn', stageToShow.displayName),
|
// _buildInfoRow('Tên công đoạn', stageToShow.displayName),
|
||||||
],
|
// ],
|
||||||
),
|
// ),
|
||||||
|
|
||||||
// Current Quantity information
|
|
||||||
_buildSectionCard(
|
|
||||||
theme: theme,
|
|
||||||
title: 'Số lượng hiện tại',
|
|
||||||
icon: Icons.info_outlined,
|
|
||||||
children: [
|
|
||||||
_buildInfoRow('Số lượng đạt', '${stageToShow.passedQuantity}'),
|
|
||||||
_buildInfoRow(
|
|
||||||
'Khối lượng đạt',
|
|
||||||
'${stageToShow.passedQuantityWeight.toStringAsFixed(2)} kg',
|
|
||||||
),
|
|
||||||
_buildInfoRow('Số lượng lỗi', '${stageToShow.issuedQuantity}'),
|
|
||||||
_buildInfoRow(
|
|
||||||
'Khối lượng lỗi',
|
|
||||||
'${stageToShow.issuedQuantityWeight.toStringAsFixed(2)} kg',
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
|
|
||||||
// Add New Quantities section
|
// Add New Quantities section
|
||||||
_buildSectionCard(
|
_buildSectionCard(
|
||||||
@@ -462,6 +402,7 @@ class _ProductDetailPageState extends ConsumerState<ProductDetailPage> {
|
|||||||
},
|
},
|
||||||
theme: theme,
|
theme: theme,
|
||||||
),
|
),
|
||||||
|
const SizedBox(height: 8),
|
||||||
// All Employees Dropdown
|
// All Employees Dropdown
|
||||||
_buildUserDropdown(
|
_buildUserDropdown(
|
||||||
label: 'Nhân viên',
|
label: 'Nhân viên',
|
||||||
@@ -476,30 +417,21 @@ class _ProductDetailPageState extends ConsumerState<ProductDetailPage> {
|
|||||||
),
|
),
|
||||||
]),
|
]),
|
||||||
|
|
||||||
// Action buttons
|
// Current Quantity information
|
||||||
Row(
|
_buildSectionCard(
|
||||||
spacing: 12,
|
theme: theme,
|
||||||
|
title: 'Số lượng hiện tại',
|
||||||
|
icon: Icons.info_outlined,
|
||||||
children: [
|
children: [
|
||||||
|
_buildInfoRow('Số lượng đạt', '${stageToShow.passedQuantity}'),
|
||||||
Expanded(
|
_buildInfoRow(
|
||||||
child: OutlinedButton.icon(
|
'Khối lượng đạt',
|
||||||
onPressed: () => _printQuantities(stageToShow),
|
'${stageToShow.passedQuantityWeight.toStringAsFixed(2)} kg',
|
||||||
icon: const Icon(Icons.print),
|
|
||||||
label: const Text('In'),
|
|
||||||
style: OutlinedButton.styleFrom(
|
|
||||||
padding: const EdgeInsets.symmetric(vertical: 16),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
Expanded(
|
_buildInfoRow('Số lượng lỗi', '${stageToShow.issuedQuantity}'),
|
||||||
child: FilledButton.icon(
|
_buildInfoRow(
|
||||||
onPressed: () => _addNewQuantities(stageToShow),
|
'Khối lượng lỗi',
|
||||||
icon: const Icon(Icons.save),
|
'${stageToShow.issuedQuantityWeight.toStringAsFixed(2)} kg',
|
||||||
label: const Text('Lưu'),
|
|
||||||
style: FilledButton.styleFrom(
|
|
||||||
padding: const EdgeInsets.symmetric(vertical: 16),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
@@ -513,14 +445,55 @@ class _ProductDetailPageState extends ConsumerState<ProductDetailPage> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
void _printQuantities(ProductStageEntity stage) {
|
Future<void> _printQuantities(ProductStageEntity stage) async {
|
||||||
// TODO: Implement print functionality
|
// Get the current quantity values (entered by user or use current values)
|
||||||
ScaffoldMessenger.of(context).showSnackBar(
|
final passedQuantity = int.tryParse(_passedQuantityController.text) ?? 0;
|
||||||
const SnackBar(
|
final passedWeight = double.tryParse(_passedWeightController.text) ?? 0.0;
|
||||||
content: Text('Tính năng in đang phát triển'),
|
final issuedQuantity = int.tryParse(_issuedQuantityController.text) ?? 0;
|
||||||
duration: Duration(seconds: 2),
|
final issuedWeight = double.tryParse(_issuedWeightController.text) ?? 0.0;
|
||||||
),
|
|
||||||
);
|
// Use entered values if available, otherwise use current stock values
|
||||||
|
final finalPassedPcs = passedQuantity > 0 ? passedQuantity : stage.passedQuantity;
|
||||||
|
final finalPassedKg = passedWeight > 0.0 ? passedWeight : stage.passedQuantityWeight;
|
||||||
|
final finalIssuedPcs = issuedQuantity > 0 ? issuedQuantity : stage.issuedQuantity;
|
||||||
|
final finalIssuedKg = issuedWeight > 0.0 ? issuedWeight : stage.issuedQuantityWeight;
|
||||||
|
|
||||||
|
// Get responsible user name
|
||||||
|
final responsibleName = _selectedWarehouseUser != null
|
||||||
|
? '${_selectedWarehouseUser!.name} ${_selectedWarehouseUser!.firstName}'
|
||||||
|
: null;
|
||||||
|
|
||||||
|
// Generate barcode data (using product code or product ID)
|
||||||
|
final barcodeData = stage.productCode.isNotEmpty
|
||||||
|
? stage.productCode
|
||||||
|
: 'P${stage.productId}';
|
||||||
|
|
||||||
|
try {
|
||||||
|
await PrintService.printWarehouseExport(
|
||||||
|
context: context,
|
||||||
|
warehouseName: widget.warehouseName,
|
||||||
|
productId: stage.productId,
|
||||||
|
productCode: stage.productCode,
|
||||||
|
productName: stage.productName,
|
||||||
|
stageName: stage.displayName,
|
||||||
|
passedKg: finalPassedKg,
|
||||||
|
passedPcs: finalPassedPcs,
|
||||||
|
issuedKg: finalIssuedKg,
|
||||||
|
issuedPcs: finalIssuedPcs,
|
||||||
|
responsibleName: responsibleName,
|
||||||
|
barcodeData: barcodeData,
|
||||||
|
);
|
||||||
|
} catch (e) {
|
||||||
|
if (mounted) {
|
||||||
|
ScaffoldMessenger.of(context).showSnackBar(
|
||||||
|
SnackBar(
|
||||||
|
content: Text('Error printing: ${e.toString()}'),
|
||||||
|
backgroundColor: Colors.red,
|
||||||
|
duration: const Duration(seconds: 3),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> _addNewQuantities(ProductStageEntity stage) async {
|
Future<void> _addNewQuantities(ProductStageEntity stage) async {
|
||||||
@@ -708,28 +681,11 @@ class _ProductDetailPageState extends ConsumerState<ProductDetailPage> {
|
|||||||
}) {
|
}) {
|
||||||
return Card(
|
return Card(
|
||||||
child: Padding(
|
child: Padding(
|
||||||
padding: const EdgeInsets.all(16),
|
padding: const EdgeInsets.all(8),
|
||||||
child: Column(
|
child: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
spacing: 12,
|
spacing: 4,
|
||||||
children: [
|
children: [
|
||||||
Row(
|
|
||||||
children: [
|
|
||||||
Icon(
|
|
||||||
icon,
|
|
||||||
size: 20,
|
|
||||||
color: theme.colorScheme.primary,
|
|
||||||
),
|
|
||||||
const SizedBox(width: 8),
|
|
||||||
Text(
|
|
||||||
title,
|
|
||||||
style: theme.textTheme.titleMedium?.copyWith(
|
|
||||||
fontWeight: FontWeight.bold,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
const SizedBox(height: 16),
|
|
||||||
...children,
|
...children,
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
@@ -921,4 +877,69 @@ class _ProductDetailPageState extends ConsumerState<ProductDetailPage> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Widget? _buildBottomActionBar({
|
||||||
|
required ProductStageEntity? selectedStage,
|
||||||
|
required List<ProductStageEntity> stages,
|
||||||
|
required ThemeData theme,
|
||||||
|
}) {
|
||||||
|
// Determine which stage to show
|
||||||
|
// When stageId is provided, use the filtered stage
|
||||||
|
final displayStages = widget.stageId != null
|
||||||
|
? stages.where((stage) => stage.productStageId == widget.stageId).toList()
|
||||||
|
: stages;
|
||||||
|
|
||||||
|
final stageToShow = widget.stageId != null && displayStages.isNotEmpty
|
||||||
|
? displayStages.first
|
||||||
|
: selectedStage;
|
||||||
|
|
||||||
|
// Don't show action bar if there's no stage to work with
|
||||||
|
if (stageToShow == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return Container(
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: theme.colorScheme.surface,
|
||||||
|
boxShadow: [
|
||||||
|
BoxShadow(
|
||||||
|
color: theme.colorScheme.shadow.withValues(alpha: 0.1),
|
||||||
|
blurRadius: 8,
|
||||||
|
offset: const Offset(0, -2),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
child: SafeArea(
|
||||||
|
top: false,
|
||||||
|
child: Padding(
|
||||||
|
padding: const EdgeInsets.all(16.0),
|
||||||
|
child: Row(
|
||||||
|
spacing: 12,
|
||||||
|
children: [
|
||||||
|
Expanded(
|
||||||
|
child: OutlinedButton.icon(
|
||||||
|
onPressed: () => _printQuantities(stageToShow),
|
||||||
|
icon: const Icon(Icons.print),
|
||||||
|
label: const Text('In'),
|
||||||
|
style: OutlinedButton.styleFrom(
|
||||||
|
padding: const EdgeInsets.symmetric(vertical: 16),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Expanded(
|
||||||
|
child: FilledButton.icon(
|
||||||
|
onPressed: () => _addNewQuantities(stageToShow),
|
||||||
|
icon: const Icon(Icons.save),
|
||||||
|
label: const Text('Lưu'),
|
||||||
|
style: FilledButton.styleFrom(
|
||||||
|
padding: const EdgeInsets.symmetric(vertical: 16),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
64
pubspec.lock
64
pubspec.lock
@@ -49,6 +49,30 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.13.0"
|
version: "2.13.0"
|
||||||
|
barcode:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: barcode
|
||||||
|
sha256: "7b6729c37e3b7f34233e2318d866e8c48ddb46c1f7ad01ff7bb2a8de1da2b9f4"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.2.9"
|
||||||
|
barcode_widget:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
name: barcode_widget
|
||||||
|
sha256: "6f2c5b08659b1a5f4d88d183e6007133ea2f96e50e7b8bb628f03266c3931427"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.0.4"
|
||||||
|
bidi:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: bidi
|
||||||
|
sha256: "77f475165e94b261745cf1032c751e2032b8ed92ccb2bf5716036db79320637d"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.0.13"
|
||||||
boolean_selector:
|
boolean_selector:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -664,6 +688,14 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.9.1"
|
version: "1.9.1"
|
||||||
|
path_parsing:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: path_parsing
|
||||||
|
sha256: "883402936929eac138ee0a45da5b0f2c80f89913e6dc3bf77eb65b84b409c6ca"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.1.0"
|
||||||
path_provider:
|
path_provider:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
@@ -712,6 +744,22 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.3.0"
|
version: "2.3.0"
|
||||||
|
pdf:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
name: pdf
|
||||||
|
sha256: "28eacad99bffcce2e05bba24e50153890ad0255294f4dd78a17075a2ba5c8416"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "3.11.3"
|
||||||
|
pdf_widget_wrapper:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: pdf_widget_wrapper
|
||||||
|
sha256: c930860d987213a3d58c7ec3b7ecf8085c3897f773e8dc23da9cae60a5d6d0f5
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.0.4"
|
||||||
petitparser:
|
petitparser:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -752,6 +800,14 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "6.0.3"
|
version: "6.0.3"
|
||||||
|
printing:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
name: printing
|
||||||
|
sha256: "482cd5a5196008f984bb43ed0e47cbfdca7373490b62f3b27b3299275bf22a93"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "5.14.2"
|
||||||
pub_semver:
|
pub_semver:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -768,6 +824,14 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.5.0"
|
version: "1.5.0"
|
||||||
|
qr:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: qr
|
||||||
|
sha256: "5a1d2586170e172b8a8c8470bbbffd5eb0cd38a66c0d77155ea138d3af3a4445"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "3.0.2"
|
||||||
riverpod:
|
riverpod:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
|||||||
@@ -36,6 +36,11 @@ dependencies:
|
|||||||
cached_network_image: ^3.3.1
|
cached_network_image: ^3.3.1
|
||||||
cupertino_icons: ^1.0.6
|
cupertino_icons: ^1.0.6
|
||||||
|
|
||||||
|
# Printing & PDF
|
||||||
|
printing: ^5.13.4
|
||||||
|
pdf: ^3.11.3
|
||||||
|
barcode_widget: ^2.0.4
|
||||||
|
|
||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
flutter_test:
|
flutter_test:
|
||||||
sdk: flutter
|
sdk: flutter
|
||||||
|
|||||||
Reference in New Issue
Block a user