import 'dart:io'; import 'package:flutter/foundation.dart'; import 'package:flutter/services.dart' show rootBundle; import 'package:intl/intl.dart'; import 'package:path/path.dart' as p; import 'package:path_provider/path_provider.dart'; import 'package:pdf/pdf.dart'; import 'package:pdf/widgets.dart' as pw; import '../models/shipment_models.dart'; import 'company_repository.dart'; class ShippingLabelService { ShippingLabelService({CompanyRepository? companyRepository}) : _companyRepository = companyRepository ?? CompanyRepository(); final CompanyRepository _companyRepository; Future generateLabel(Shipment shipment) async { try { final company = await _companyRepository.getCompanyInfo(); final fontData = await rootBundle.load('assets/fonts/ipaexg.ttf'); final ipaex = pw.Font.ttf(fontData); final dateFormat = DateFormat('yyyy/MM/dd'); final pdf = pw.Document(); pdf.addPage( pw.Page( pageFormat: PdfPageFormat.a5, margin: const pw.EdgeInsets.all(20), build: (context) { return pw.Column( crossAxisAlignment: pw.CrossAxisAlignment.start, children: [ pw.Text('出荷ラベル', style: pw.TextStyle(fontSize: 20, fontWeight: pw.FontWeight.bold, font: ipaex)), pw.SizedBox(height: 8), pw.Text('発行日: ${dateFormat.format(DateTime.now())}', style: pw.TextStyle(font: ipaex)), pw.Divider(), pw.Text('差出人', style: pw.TextStyle(fontWeight: pw.FontWeight.bold, font: ipaex)), pw.Text(company.name, style: pw.TextStyle(font: ipaex)), if (company.address != null) pw.Text(company.address!, style: pw.TextStyle(font: ipaex)), if (company.tel != null) pw.Text('TEL: ${company.tel}', style: pw.TextStyle(font: ipaex)), pw.SizedBox(height: 12), pw.Text('宛先', style: pw.TextStyle(fontWeight: pw.FontWeight.bold, font: ipaex)), pw.Text(shipment.customerNameSnapshot ?? '取引先未設定', style: pw.TextStyle(fontSize: 16, font: ipaex)), if (shipment.orderNumberSnapshot != null) pw.Text('受注番号: ${shipment.orderNumberSnapshot}', style: pw.TextStyle(font: ipaex)), pw.SizedBox(height: 12), pw.Text('配送情報', style: pw.TextStyle(fontWeight: pw.FontWeight.bold, font: ipaex)), pw.Text('配送業者: ${shipment.carrierName ?? '-'}', style: pw.TextStyle(font: ipaex)), pw.Text('追跡番号: ${shipment.trackingNumber ?? '-'}', style: pw.TextStyle(font: ipaex)), if (shipment.trackingUrl != null && shipment.trackingUrl!.isNotEmpty) pw.Text('追跡URL: ${shipment.trackingUrl}', style: pw.TextStyle(font: ipaex)), pw.SizedBox(height: 12), pw.Text('出荷明細', style: pw.TextStyle(fontWeight: pw.FontWeight.bold, font: ipaex)), pw.SizedBox(height: 6), if (shipment.items.isEmpty) pw.Text('明細登録なし', style: pw.TextStyle(font: ipaex)) else pw.TableHelper.fromTextArray( headers: const ['品目', '数量'], data: shipment.items .map( (item) => [ item.description, item.quantity.toString(), ], ) .toList(), headerStyle: pw.TextStyle(fontWeight: pw.FontWeight.bold, font: ipaex), cellStyle: pw.TextStyle(font: ipaex), columnWidths: const { 0: pw.FlexColumnWidth(3), 1: pw.FlexColumnWidth(1), }, ), if (shipment.notes?.isNotEmpty == true) ...[ pw.SizedBox(height: 12), pw.Text('備考', style: pw.TextStyle(fontWeight: pw.FontWeight.bold, font: ipaex)), pw.Text(shipment.notes!, style: pw.TextStyle(font: ipaex)), ], ], ); }, ), ); final directory = await getApplicationDocumentsDirectory(); final timestamp = DateFormat('yyyyMMdd_HHmmss').format(DateTime.now()); final orderFragment = shipment.orderNumberSnapshot ?? shipment.id.substring(0, 6); final fileName = 'shipping_label_${orderFragment}_$timestamp.pdf'; final file = File(p.join(directory.path, fileName)); await file.writeAsBytes(await pdf.save()); return file.path; } catch (e) { debugPrint('Shipping label generation failed: $e'); return null; } } }