h-1.flutter.4/lib/pdf_templates/estimate_template.dart

142 lines
No EOL
5.1 KiB
Dart
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// Version: 1.0 - 見積書用 PDF テンプレート
import 'package:flutter/material.dart';
import 'package:flutter_pdfgenerator/flutter_pdfgenerator.dart';
import 'package:pdf/pdf.dart';
import 'package:pdf/widgets.dart' as pw;
import '../models/customer.dart';
import '../models/product.dart';
import '../models/estimate.dart';
/// 見積書用 PDF テンプレート生成クラス
class EstimateTemplate {
/// 見積書の PDF を生成
static Future<pw.Document> generateEstimatePdf(Estimate estimate) async {
final doc = pw.Document();
doc.addPage(
pw.MultiPage(
pageFormat: PdfPageSize.a5, // A5 サイズ
build: (pw.Context context) => [
_buildHeader(context, estimate),
...estimate.items.map((item) => _buildItemLine(item, context)),
_buildSummary(context, estimate),
_buildFooter(context, estimate),
],
),
);
return doc;
}
/// ヘッダーエリア
static pw.Widget _buildHeader(pw.Context context, Estimate estimate) {
return pw.Container(
padding: const EdgeInsets.all(20),
decoration: pw.BoxDecoration(color: PdfColors.white),
child: pw.Row(
children: [
pw.Expanded(
child: pw.Column(
crossAxisAlignment: pw.CrossAxisAlignment.start,
children: [
pw.Text('母艦 お局様', style: pw.TextStyle(fontSize: 16, fontWeight: PdfFontWeight.bold)),
pw.Text('会社名:〇〇株式会社'),
pw.Text('住所東京都港区_test 1-1-1'),
pw.Text('TEL:03-1234-5678 / FAX:03-1234-5679'),
pw.Text('📧 mail@example.com'),
],
),
),
pw.Container(
padding: const EdgeInsets.all(10),
decoration: pw.BoxDecoration(color: PdfColors.blueAccent.withOpacity(0.1)),
child: pw.Row(
children: [
pw.Text('見積書', style: pw.TextStyle(fontSize: 14, fontWeight: PdfFontWeight.bold)),
const Spacer(),
pw.Text('No. ${estimate.estimateNumber}', style: pw.TextStyle(fontSize: 12)),
],
),
),
],
),
);
}
/// 商品明細行
static pw.Widget _buildItemLine(EstimateItem item, pw.Context context) {
final isEven = context.pageNumber % 2 == 0;
return pw.Container(
padding: const EdgeInsets.symmetric(vertical: 2),
decoration: pw.BoxDecoration(
color: isEven ? PdfColors.grey.shade50 : PdfColors.white,
border: Border(top: pw.BorderSide(color: PdfColors.grey.shade300)),
),
child: pw.Row(
children: [
pw.Text('#${context.pageNumber.toString().padLeft(2, '0')}', style: pw.TextStyle(fontSize: 10)),
pw SizedBox(width: 5),
pw.Expanded(
child: pw.Text(item.productName, style: pw.TextStyle(fontSize: 10, overflow: pw.Overflow.ellipsis)),
),
pw Container(width: 20),
pw Text('¥${item.unitPrice.toStringAsFixed(0)}', style: pw.TextStyle(fontSize: 10)),
pw Text(item.quantity.toString(), style: pw.TextStyle(fontSize: 10)),
pw Text('¥${item.subtotal.toStringAsFixed(0)}', style: pw.TextStyle(fontSize: 10)),
],
),
);
}
/// 合計金額エリア
static pw.Widget _buildSummary(pw.Context context, Estimate estimate) {
return pw.Container(
padding: const EdgeInsets.all(20),
decoration: pw.BoxDecoration(color: PdfColors.blue.shade50),
child: pw.Column(
crossAxisAlignment: pw.CrossAxisAlignment.start,
children: [
pw.Text('合計', style: pw.TextStyle(fontSize: 14, fontWeight: PdfFontWeight.bold)),
const SizedBox(height: 8),
pw.Text('¥${estimate.totalAmount.toStringAsFixed(0)}', style: pw.TextStyle(fontSize: 16, fontWeight: PdfFontWeight.bold, color: PdfColors.orange.shade500)),
const Spacer(),
pw.Padding(
padding: const EdgeInsets.only(top: 20),
child: pw.Text('備考:納期・決済条件などの注意事項', style: pw.TextStyle(fontSize: 10, fontStyle: PdfFontStyle.italic)),
),
],
),
);
}
/// フッターエリア
static pw.Widget _buildFooter(pw.Context context, Estimate estimate) {
return pw.Container(
padding: const EdgeInsets.all(20),
decoration: pw.BoxDecoration(color: PdfColors.white),
child: pw.Row(
children: [
pw.Expanded(
child: pw.Column(
crossAxisAlignment: pw.CrossAxisAlignment.start,
children: [
pw.Text('発行日:${estimate.createdAt.toLocal()}'),
if (estimate.expiryDate != null) pw.Text('有効期限:${estimate.expiryDate!.toLocal()}'),
],
),
),
pw const Spacer(),
pw Container(
width: 120,
height: 80,
decoration: pw.BoxDecoration(
color: PdfColors.grey.shade200,
child: pw.Center(child: pw.Text('QR コードエリア', style: pw.TextStyle(fontSize: 8))),
),
),
],
),
);
}
}