119 lines
4.2 KiB
Dart
119 lines
4.2 KiB
Dart
// 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,
|
||
),
|
||
),
|
||
],
|
||
),
|
||
),
|
||
);
|
||
}
|
||
}
|