186 lines
7.1 KiB
Dart
186 lines
7.1 KiB
Dart
import 'package:flutter/material.dart';
|
||
import 'package:intl/intl.dart';
|
||
import '../models/invoice_models.dart';
|
||
import '../services/invoice_repository.dart';
|
||
import 'invoice_detail_page.dart';
|
||
|
||
/// 帳票(見積・納品・請求・領収)の履歴一覧を表示・管理する画面
|
||
class InvoiceHistoryScreen extends StatefulWidget {
|
||
const InvoiceHistoryScreen({Key? key}) : super(key: key);
|
||
|
||
@override
|
||
State<InvoiceHistoryScreen> createState() => _InvoiceHistoryScreenState();
|
||
}
|
||
|
||
class _InvoiceHistoryScreenState extends State<InvoiceHistoryScreen> {
|
||
final InvoiceRepository _repository = InvoiceRepository();
|
||
List<Invoice> _invoices = [];
|
||
bool _isLoading = true;
|
||
|
||
@override
|
||
void initState() {
|
||
super.initState();
|
||
_loadInvoices();
|
||
}
|
||
|
||
/// DBから履歴を読み込む
|
||
Future<void> _loadInvoices() async {
|
||
setState(() => _isLoading = true);
|
||
final data = await _repository.getAllInvoices();
|
||
setState(() {
|
||
_invoices = data;
|
||
_isLoading = false;
|
||
});
|
||
}
|
||
|
||
/// 不要な(DBに紐付かない)PDFファイルを一括削除
|
||
Future<void> _cleanupFiles() async {
|
||
final count = await _repository.cleanupOrphanedPdfs();
|
||
if (mounted) {
|
||
ScaffoldMessenger.of(context).showSnackBar(
|
||
SnackBar(content: Text('$count 個の不要なPDFファイルを削除しました')),
|
||
);
|
||
}
|
||
}
|
||
|
||
/// 履歴から個別に削除
|
||
Future<void> _deleteInvoice(Invoice invoice) async {
|
||
final confirmed = await showDialog<bool>(
|
||
context: context,
|
||
builder: (context) => AlertDialog(
|
||
title: const Text("削除の確認"),
|
||
content: Text("${invoice.type.label}番号: ${invoice.invoiceNumber}\nこのデータを削除しますか?\n(実体PDFファイルも削除されます)"),
|
||
actions: [
|
||
TextButton(onPressed: () => Navigator.pop(context, false), child: const Text("キャンセル")),
|
||
TextButton(
|
||
onPressed: () => Navigator.pop(context, true),
|
||
child: const Text("削除する", style: TextStyle(color: Colors.red)),
|
||
),
|
||
],
|
||
),
|
||
);
|
||
|
||
if (confirmed == true) {
|
||
await _repository.deleteInvoice(invoice);
|
||
_loadInvoices();
|
||
}
|
||
}
|
||
|
||
@override
|
||
Widget build(BuildContext context) {
|
||
final amountFormatter = NumberFormat("#,###");
|
||
final dateFormatter = DateFormat('yyyy/MM/dd HH:mm');
|
||
|
||
return Scaffold(
|
||
appBar: AppBar(
|
||
title: const Text("発行履歴管理"),
|
||
backgroundColor: Colors.blueGrey,
|
||
foregroundColor: Colors.white,
|
||
actions: [
|
||
IconButton(
|
||
icon: const Icon(Icons.cleaning_services),
|
||
tooltip: "ゴミファイルを掃除",
|
||
onPressed: _cleanupFiles,
|
||
),
|
||
IconButton(
|
||
icon: const Icon(Icons.refresh),
|
||
onPressed: _loadInvoices,
|
||
),
|
||
],
|
||
),
|
||
body: _isLoading
|
||
? const Center(child: CircularProgressIndicator())
|
||
: _invoices.isEmpty
|
||
? Center(
|
||
child: Column(
|
||
mainAxisAlignment: MainAxisAlignment.center,
|
||
children: [
|
||
Icon(Icons.history, size: 64, color: Colors.grey.shade300),
|
||
const SizedBox(height: 16),
|
||
const Text("発行済みの帳票はありません", style: TextStyle(color: Colors.grey)),
|
||
],
|
||
),
|
||
)
|
||
: ListView.builder(
|
||
itemCount: _invoices.length,
|
||
itemBuilder: (context, index) {
|
||
final invoice = _invoices[index];
|
||
return Card(
|
||
margin: const EdgeInsets.symmetric(horizontal: 12, vertical: 6),
|
||
child: ListTile(
|
||
leading: Stack(
|
||
alignment: Alignment.bottomRight,
|
||
children: [
|
||
const CircleAvatar(
|
||
backgroundColor: Colors.indigo,
|
||
child: Icon(Icons.description, color: Colors.white),
|
||
),
|
||
if (invoice.isShared)
|
||
Container(
|
||
decoration: const BoxDecoration(
|
||
color: Colors.white,
|
||
shape: BoxShape.circle,
|
||
),
|
||
child: const Icon(
|
||
Icons.check_circle,
|
||
color: Colors.green,
|
||
size: 18,
|
||
),
|
||
),
|
||
],
|
||
),
|
||
title: Row(
|
||
children: [
|
||
Container(
|
||
padding: const EdgeInsets.symmetric(horizontal: 6, vertical: 2),
|
||
decoration: BoxDecoration(
|
||
color: Colors.blueGrey.shade100,
|
||
borderRadius: BorderRadius.circular(4),
|
||
),
|
||
child: Text(
|
||
invoice.type.label,
|
||
style: const TextStyle(fontSize: 10, fontWeight: FontWeight.bold),
|
||
),
|
||
),
|
||
const SizedBox(width: 8),
|
||
Expanded(
|
||
child: Text(
|
||
invoice.customer.formalName,
|
||
style: const TextStyle(fontWeight: FontWeight.bold),
|
||
overflow: TextOverflow.ellipsis,
|
||
),
|
||
),
|
||
],
|
||
),
|
||
subtitle: Column(
|
||
crossAxisAlignment: CrossAxisAlignment.start,
|
||
children: [
|
||
Text("No: ${invoice.invoiceNumber}"),
|
||
Text(dateFormatter.format(invoice.date), style: const TextStyle(fontSize: 12)),
|
||
],
|
||
),
|
||
trailing: Text(
|
||
"¥${amountFormatter.format(invoice.totalAmount)}",
|
||
style: const TextStyle(
|
||
fontWeight: FontWeight.bold,
|
||
color: Colors.indigo,
|
||
fontSize: 16,
|
||
),
|
||
),
|
||
onTap: () async {
|
||
await Navigator.push(
|
||
context,
|
||
MaterialPageRoute(
|
||
builder: (context) => InvoiceDetailPage(invoice: invoice),
|
||
),
|
||
);
|
||
_loadInvoices();
|
||
},
|
||
onLongPress: () => _deleteInvoice(invoice),
|
||
),
|
||
);
|
||
},
|
||
),
|
||
);
|
||
}
|
||
}
|