// lib/screens/invoice_input_screen.dart 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 '../services/master_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 _invoiceRepository = InvoiceRepository(); final _masterRepository = MasterRepository(); DocumentType _selectedType = DocumentType.invoice; // デフォルトは請求書 String _status = "取引先を選択してPDFを生成してください"; List _customerBuffer = []; Customer? _selectedCustomer; bool _isLoading = true; @override void initState() { super.initState(); _loadInitialData(); } /// 初期データの読み込み Future _loadInitialData() async { setState(() => _isLoading = true); final savedCustomers = await _masterRepository.loadCustomers(); setState(() { _customerBuffer = savedCustomers; if (_customerBuffer.isNotEmpty) { _selectedCustomer = _customerBuffer.first; _clientController.text = _selectedCustomer!.formalName; } _isLoading = false; }); _invoiceRepository.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( existingCustomers: _customerBuffer, onCustomerSelected: (customer) async { setState(() { int index = _customerBuffer.indexWhere((c) => c.id == customer.id); if (index != -1) { _customerBuffer[index] = customer; } else { _customerBuffer.add(customer); } _selectedCustomer = customer; _clientController.text = customer.formalName; _status = "「${customer.formalName}」を選択しました"; }); await _masterRepository.saveCustomers(_customerBuffer); if (mounted) Navigator.pop(context); }, onCustomerDeleted: (customer) async { setState(() { _customerBuffer.removeWhere((c) => c.id == customer.id); if (_selectedCustomer?.id == customer.id) { _selectedCustomer = null; _clientController.clear(); } }); await _masterRepository.saveCustomers(_customerBuffer); }, ), ), ); } /// 初期PDFを生成して詳細画面へ進む Future _handleInitialGenerate() async { if (_selectedCustomer == null) { setState(() => _status = "取引先を選択してください"); return; } final unitPrice = int.tryParse(_amountController.text) ?? 0; final initialItems = [ InvoiceItem( description: "${_selectedType.label}分", quantity: 1, unitPrice: unitPrice, ) ]; final invoice = Invoice( customer: _selectedCustomer!, date: DateTime.now(), items: initialItems, type: _selectedType, ); setState(() => _status = "${_selectedType.label}を生成中..."); final path = await generateInvoicePdf(invoice); if (path != null) { final updatedInvoice = invoice.copyWith(filePath: path); await _invoiceRepository.saveInvoice(updatedInvoice); widget.onInvoiceGenerated(updatedInvoice, path); setState(() => _status = "${_selectedType.label}を生成しDBに登録しました。"); } else { setState(() => _status = "PDFの生成に失敗しました"); } } @override Widget build(BuildContext context) { if (_isLoading) { return const Center(child: CircularProgressIndicator()); } return Padding( padding: const EdgeInsets.all(16.0), child: SingleChildScrollView( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ const Text( "帳票の種類を選択", style: TextStyle(fontWeight: FontWeight.bold, color: Colors.blueGrey), ), const SizedBox(height: 8), Wrap( spacing: 8.0, children: DocumentType.values.map((type) { return ChoiceChip( label: Text(type.label), selected: _selectedType == type, onSelected: (selected) { if (selected) { setState(() => _selectedType = type); } }, selectedColor: Colors.indigo.shade100, ); }).toList(), ), const SizedBox(height: 24), const Text( "宛先と基本金額の設定", style: TextStyle(fontWeight: FontWeight.bold, color: Colors.blueGrey), ), const SizedBox(height: 12), 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: Text("${_selectedType.label}を作成して詳細編集へ"), 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, ), ), ], ), ), ); } }