inv/gemi_invoice/lib/main.dart
2026-01-31 13:40:15 +09:00

119 lines
4.2 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.2 (Japanese Support & SHA-256 Edition)
import 'dart:io';
import 'dart:typed_data';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart' show rootBundle;
import 'package:pdf/widgets.dart' as pw;
import 'package:path_provider/path_provider.dart';
import 'package:crypto/crypto.dart';
import 'package:intl/intl.dart';
void main() => runApp(const MaterialApp(home: InvoiceApp()));
class InvoiceApp extends StatefulWidget {
const InvoiceApp({super.key});
@override
State<InvoiceApp> createState() => _InvoiceAppState();
}
class _InvoiceAppState extends State<InvoiceApp> {
final _clientController = TextEditingController(text: "現場太郎 様");
final _amountController = TextEditingController(text: "1500");
String _status = "フォントを配置してPDFを発行してください";
Future<void> _generateInvoice() async {
final pdf = pw.Document();
// 1. 日本語フォントの読み込み (assets/fonts/ に配置済み前提)
// もしまだ配置してなければ、この3行をコメントアウトしてください
final fontData = await rootBundle.load("assets/fonts/ipaexg.ttf");
final ttf = pw.Font.ttf(fontData);
final myTheme = pw.ThemeData.withFont(base: ttf, bold: ttf);
final clientName = _clientController.text;
final int unitPrice = int.tryParse(_amountController.text) ?? 0;
final int tax = (unitPrice * 0.1).floor();
final int total = unitPrice + tax;
final dateStr = DateFormat('yyyy/MM/dd HH:mm').format(DateTime.now());
// PDFドキュメントの作成
pdf.addPage(
pw.Page(
theme: myTheme, // 日本語テーマ適用
build: (context) => pw.Column(
crossAxisAlignment:
pw.CrossAxisAlignment.start, // 'cross' を 'crossAxisAlignment' に修正
children: [
pw.Text("請求書 (Invoice)", style: pw.TextStyle(fontSize: 30)),
pw.Divider(),
pw.Text("宛名: $clientName"),
pw.Text("日付: $dateStr"),
pw.SizedBox(height: 20),
pw.Text("単価(税抜): $unitPrice"),
pw.Text("消費税 (10%): $tax"),
pw.Text(
"合計金額(税込): $total",
style: pw.TextStyle(fontWeight: pw.FontWeight.bold, fontSize: 20),
),
pw.SizedBox(height: 40),
pw.Text("※本書類はシステムにより自動生成され、改ざん防止ハッシュが付与されています。"),
],
),
),
);
final Uint8List bytes = await pdf.save();
// 2. SHA-256ハッシュ計算
final hash = sha256.convert(bytes).toString().substring(0, 10);
final fileName =
"${DateFormat('yyyyMMdd').format(DateTime.now())}_${total}円_hash_$hash.pdf";
// 3. 保存
final directory = await getExternalStorageDirectory();
final file = File("${directory!.path}/$fileName");
await file.writeAsBytes(bytes);
setState(() {
_status = "【発行成功】\nファイル: $fileName\nハッシュ: $hash";
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text("現場のじぇみエモン請求書 V1.0")),
body: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
children: [
TextField(
controller: _clientController,
decoration: const InputDecoration(labelText: "取引先名"),
),
TextField(
controller: _amountController,
decoration: const InputDecoration(labelText: "単価(10%抜き)"),
),
const SizedBox(height: 20),
ElevatedButton(
onPressed: _generateInvoice,
style: ElevatedButton.styleFrom(
minimumSize: const Size(double.infinity, 50),
),
child: const Text("日本語PDF発行ハッシュ付与"),
),
const SizedBox(height: 20),
SelectableText(
_status,
style: const TextStyle(
color: Colors.blue,
fontWeight: FontWeight.bold,
),
),
],
),
),
);
}
}