import 'package:flutter/material.dart'; import 'package:uuid/uuid.dart'; import '../models/customer_model.dart'; import '../models/invoice_models.dart'; import '../services/pdf_generator.dart'; import '../services/invoice_repository.dart'; import 'customer_picker_modal.dart'; /// 請求書の初期入力(ヘッダー部分)を管理するウィジェット class InvoiceInputForm extends StatefulWidget { final Function(Invoice invoice, String filePath) onInvoiceGenerated; const InvoiceInputForm({Key? key, required this.onInvoiceGenerated}) : super(key: key); @override State createState() => _InvoiceInputFormState(); } class _InvoiceInputFormState extends State { final _clientController = TextEditingController(); final _amountController = TextEditingController(text: "250000"); final _repository = InvoiceRepository(); String _status = "取引先を選択してPDFを生成してください"; List _customerBuffer = []; Customer? _selectedCustomer; @override void initState() { super.initState(); _selectedCustomer = Customer( id: const Uuid().v4(), displayName: "佐々木製作所", formalName: "株式会社 佐々木製作所", ); _customerBuffer.add(_selectedCustomer!); _clientController.text = _selectedCustomer!.formalName; // 起動時に不要なPDFを掃除する _repository.cleanupOrphanedPdfs().then((count) { if (count > 0) { debugPrint('Cleaned up $count orphaned PDF files.'); } }); } @override void dispose() { _clientController.dispose(); _amountController.dispose(); super.dispose(); } Future _openCustomerPicker() async { setState(() => _status = "顧客マスターを開いています..."); await showModalBottomSheet( context: context, isScrollControlled: true, backgroundColor: Colors.transparent, builder: (context) => FractionallySizedBox( heightFactor: 0.9, child: CustomerPickerModal( onCustomerSelected: (customer) { setState(() { bool exists = _customerBuffer.any((c) => c.id == customer.id); if (!exists) { _customerBuffer.add(customer); } _selectedCustomer = customer; _clientController.text = customer.formalName; _status = "「${customer.formalName}」を選択しました"; }); Navigator.pop(context); }, ), ), ); } Future _handleInitialGenerate() async { if (_selectedCustomer == null) { setState(() => _status = "取引先を選択してください"); return; } final unitPrice = int.tryParse(_amountController.text) ?? 0; final initialItems = [ InvoiceItem(description: "ご請求分", quantity: 1, unitPrice: unitPrice), ]; final invoice = Invoice( customer: _selectedCustomer!, date: DateTime.now(), items: initialItems, ); setState(() => _status = "A4請求書を生成中..."); final path = await PdfGenerator.generateInvoicePdf(invoice); if (path != null) { final updatedInvoice = invoice.copyWith(filePath: path.path); // オリジナルDBに保存 await _repository.saveInvoice(updatedInvoice); widget.onInvoiceGenerated(updatedInvoice, path.path); setState(() => _status = "PDFを生成しDBに登録しました。"); } else { setState(() => _status = "PDFの生成に失敗しました"); } } @override Widget build(BuildContext context) { return Padding( padding: const EdgeInsets.all(16.0), child: SingleChildScrollView( child: Column( children: [ const Text( "ステップ1: 宛先と基本金額の設定", style: TextStyle( fontWeight: FontWeight.bold, color: Colors.blueGrey, ), ), const SizedBox(height: 16), Row( children: [ Expanded( child: TextField( controller: _clientController, readOnly: true, onTap: _openCustomerPicker, decoration: const InputDecoration( labelText: "取引先名 (タップして選択)", hintText: "電話帳から取り込むか、マスターから選択", prefixIcon: Icon(Icons.business), border: OutlineInputBorder(), ), ), ), const SizedBox(width: 8), IconButton( icon: const Icon( Icons.person_add_alt_1, color: Colors.indigo, size: 40, ), onPressed: _openCustomerPicker, tooltip: "顧客を選択・登録", ), ], ), const SizedBox(height: 16), TextField( controller: _amountController, keyboardType: TextInputType.number, decoration: const InputDecoration( labelText: "基本金額 (税抜)", hintText: "明細の1行目として登録されます", prefixIcon: Icon(Icons.currency_yen), border: OutlineInputBorder(), ), ), const SizedBox(height: 24), ElevatedButton.icon( onPressed: _handleInitialGenerate, icon: const Icon(Icons.description), label: const Text("A4請求書を作成して詳細編集へ"), style: ElevatedButton.styleFrom( minimumSize: const Size(double.infinity, 60), backgroundColor: Colors.indigo, foregroundColor: Colors.white, elevation: 4, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(12), ), ), ), const SizedBox(height: 24), Container( width: double.infinity, padding: const EdgeInsets.all(12), decoration: BoxDecoration( color: Colors.grey[100], borderRadius: BorderRadius.circular(8), border: Border.all(color: Colors.grey.shade300), ), child: Text( _status, style: const TextStyle(fontSize: 12, color: Colors.black54), textAlign: TextAlign.center, ), ), ], ), ), ); } }