hanbai1/lib/services/pdf_generator.dart
2026-02-09 09:06:36 +09:00

99 lines
No EOL
3.7 KiB
Dart

// lib/services/pdf_generator.dart
// Version: 1.0.1
// Updated: 2026-02-08
// Description: 日本語対応PDF生成および命名規則に基づいたファイル保存
import 'dart:io';
import 'dart:convert';
import 'package:flutter/services.dart';
import 'package:intl/intl.dart';
import 'package:pdf/pdf.dart';
import 'package:pdf/widgets.dart' as pw;
import 'package:path_provider/path_provider.dart';
import 'package:crypto/crypto.dart';
import '../models/invoice_models.dart';
class PdfGenerator {
/// 請求書PDFを生成し、一意のファイル名で保存する
static Future<File> generateInvoicePdf(Invoice invoice) async {
final pdf = pw.Document();
// 1. フォントの読み込み
final fontData = await rootBundle.load("assets/fonts/ipaexg.ttf");
final ttf = pw.Font.ttf(fontData);
// 2. PDFコンテンツの構築
pdf.addPage(
pw.MultiPage(
theme: pw.ThemeData.withFont(base: ttf),
build: (pw.Context context) {
return [
pw.Header(
level: 0,
child: pw.Text("請求書", style: pw.TextStyle(fontSize: 24)),
),
pw.SizedBox(height: 20),
pw.Text("請求先: ${invoice.customer.formalName} 御中"),
pw.Text("請求番号: ${invoice.invoiceNumber}"),
pw.Text("日付: ${DateFormat('yyyy/MM/dd').format(invoice.date)}"),
pw.Divider(),
pw.Table.fromTextArray(
headers: ['内容', '数量', '単価', '金額'],
data: invoice.items.map((item) {
return [
item.description,
item.quantity.toString(),
"¥${item.unitPrice}",
"¥${item.subtotal}",
];
}).toList(),
),
pw.SizedBox(height: 20),
pw.Container(
alignment: pw.Alignment.centerRight,
child: pw.Column(
crossAxisAlignment: pw.CrossAxisAlignment.end,
children: [
pw.Text("小計: ¥${invoice.subtotal}"),
pw.Text("消費税: ¥${invoice.tax}"),
pw.Text("合計金額: ¥${invoice.totalAmount}",
style: pw.TextStyle(fontWeight: pw.FontWeight.bold)),
],
),
),
if (invoice.notes != null) ...[
pw.SizedBox(height: 20),
pw.Text("備考:"),
pw.Text(invoice.notes!),
],
];
},
),
);
// 3. 命名規則に基づくファイル名の生成
// 規則: [日付]_[依頼種別]_[顧客名]_[タイトル]_[金額]_[SHA256ハッシュ].pdf
final dateStr = DateFormat('yyyyMMdd').format(invoice.date);
// モデルにない項目は安全な代替値を使用
const typeStr = "請求書"; // 固定またはInvoiceにプロパティ追加検討
final customerStr = invoice.customer.formalName.replaceAll(RegExp(r'[\\/:*?"<>|]'), '');
final titleStr = (invoice.notes != null && invoice.notes!.length > 10)
? invoice.notes!.substring(0, 10)
: (invoice.items.isNotEmpty ? invoice.items.first.description : "名称未設定");
final amountStr = invoice.totalAmount.toString();
// SHA256ハッシュの生成
final rawData = "$dateStr$customerStr$amountStr${invoice.invoiceNumber}";
final hash = sha256.convert(utf8.encode(rawData)).toString().substring(0, 8);
final fileName = "${dateStr}_${typeStr}_${customerStr}_${titleStr}_${amountStr}_$hash.pdf";
// 4. 保存
final directory = await getApplicationDocumentsDirectory();
final file = File("${directory.path}/$fileName");
await file.writeAsBytes(await pdf.save());
return file;
}
}