Files
flux/lib/features/tickets/utils/ticket_shipping_pdf_service.dart

206 lines
7.5 KiB
Dart
Raw Normal View History

2026-05-16 11:51:26 +02:00
import 'dart:typed_data';
import 'package:flux/features/company/models/company_model.dart';
2026-05-16 14:30:23 +02:00
import 'package:flux/features/tickets/models/shipment_document_model.dart';
2026-05-16 11:51:26 +02:00
import 'package:flux/features/master_data/providers/models/provider_location_model.dart';
import 'package:flux/features/master_data/providers/models/provider_model.dart';
import 'package:flux/features/tickets/models/ticket_model.dart';
import 'package:pdf/pdf.dart';
import 'package:pdf/widgets.dart' as pw;
import 'package:intl/intl.dart';
class TicketShippingPdfService {
static Future<Uint8List> generateDdt({
required CompanyModel company,
required ProviderModel provider,
required ProviderLocationModel location,
required ShipmentDocumentModel document,
required List<TicketModel> tickets,
}) async {
final pdf = pw.Document();
// Formattatore per le date
final dateFormat = DateFormat('dd/MM/yyyy');
pdf.addPage(
pw.MultiPage(
pageFormat: PdfPageFormat.a4,
margin: const pw.EdgeInsets.all(32),
// --- INTESTAZIONE (Ripetuta su ogni pagina) ---
header: (pw.Context context) {
return pw.Column(
crossAxisAlignment: pw.CrossAxisAlignment.start,
children: [
pw.Row(
mainAxisAlignment: pw.MainAxisAlignment.spaceBetween,
crossAxisAlignment: pw.CrossAxisAlignment.start,
children: [
// Dati Mittente (La tua Company)
pw.Expanded(
child: pw.Column(
crossAxisAlignment: pw.CrossAxisAlignment.start,
children: [
pw.Text(
company.name,
style: pw.TextStyle(
fontWeight: pw.FontWeight.bold,
fontSize: 16,
),
),
pw.Text(company.address),
pw.Text(
'${company.city} (${company.province}) - ${company.zipCode}',
),
pw.Text('P.IVA: ${company.vatId}'),
],
),
),
// Dati Destinatario (Il Laboratorio e la Sede)
pw.Expanded(
child: pw.Container(
padding: const pw.EdgeInsets.all(8),
decoration: pw.BoxDecoration(
border: pw.Border.all(color: PdfColors.grey),
borderRadius: const pw.BorderRadius.all(
pw.Radius.circular(4),
),
),
child: pw.Column(
crossAxisAlignment: pw.CrossAxisAlignment.start,
children: [
pw.Text(
'DESTINATARIO:',
style: pw.TextStyle(
fontSize: 10,
color: PdfColors.grey700,
),
),
pw.Text(
provider.name,
style: pw.TextStyle(fontWeight: pw.FontWeight.bold),
),
pw.SizedBox(height: 4),
pw.Text('Destinazione merce:'),
pw.Text(location.address),
pw.Text(
'${location.zipCode} ${location.city} (${location.province})',
),
],
),
),
),
],
),
pw.SizedBox(height: 20),
// Titolo Documento
pw.Center(
child: pw.Text(
'DOCUMENTO DI TRASPORTO (D.D.T.)',
style: pw.TextStyle(
fontSize: 18,
fontWeight: pw.FontWeight.bold,
),
),
),
pw.SizedBox(height: 10),
// Dati Documento
pw.Row(
mainAxisAlignment: pw.MainAxisAlignment.spaceAround,
children: [
pw.Text(
'Numero: ${document.docNumber}',
style: pw.TextStyle(fontWeight: pw.FontWeight.bold),
),
pw.Text(
'Data: ${dateFormat.format(document.docDate)}',
style: pw.TextStyle(fontWeight: pw.FontWeight.bold),
),
pw.Text('Causale: ${document.shippingReason}'),
],
),
pw.SizedBox(height: 20),
],
);
},
// --- IL CORPO (La tabella dei ticket che scorre) ---
build: (pw.Context context) {
return [
pw.TableHelper.fromTextArray(
headers: ['Rif. Ticket', 'Modello', 'Difetto / Note', 'Q.tà'],
headerStyle: pw.TextStyle(
fontWeight: pw.FontWeight.bold,
color: PdfColors.white,
),
headerDecoration: const pw.BoxDecoration(
color: PdfColors.blueGrey800,
),
cellAlignment: pw.Alignment.centerLeft,
data: tickets.map((t) {
return [
t.id?.substring(0, 8).toUpperCase() ??
'-', // Magari hai un ID progressivo migliore
t.targetModelName ?? 'Sconosciuto',
t.request,
'1', // Tipicamente 1 per ogni ticket
];
}).toList(),
),
];
},
// --- PIÈ DI PAGINA (Ripetuto su ogni pagina, ma con le firme) ---
footer: (pw.Context context) {
return pw.Column(
children: [
pw.Divider(),
pw.Row(
mainAxisAlignment: pw.MainAxisAlignment.spaceAround,
children: [
pw.Text('Colli: ${document.packageCount}'),
pw.Text('Peso: ${document.weight ?? '-'} Kg'),
pw.Text(
'Aspetto: ${document.notes ?? 'Scatola'}',
), // Adatta se hai un campo specifico
],
),
pw.SizedBox(height: 20),
// Spazio Firme
pw.Row(
mainAxisAlignment: pw.MainAxisAlignment.spaceBetween,
children: [
_buildSignatureBox('Firma Mittente'),
_buildSignatureBox('Firma Vettore (Corriere)'),
_buildSignatureBox('Firma Destinatario'),
],
),
pw.SizedBox(height: 10),
pw.Align(
alignment: pw.Alignment.centerRight,
child: pw.Text(
'Pagina ${context.pageNumber} di ${context.pagesCount}',
style: const pw.TextStyle(
fontSize: 10,
color: PdfColors.grey,
),
),
),
],
);
},
),
);
return pdf.save();
}
static pw.Widget _buildSignatureBox(String title) {
return pw.Column(
children: [
pw.Text(title, style: const pw.TextStyle(fontSize: 10)),
pw.SizedBox(height: 40),
pw.Container(width: 120, height: 1, color: PdfColors.black),
],
);
}
}