// Version: 1.0.0 import 'package:flutter/material.dart'; import '../services/database_helper.dart'; import '../models/product.dart'; /// 受注入力画面(Material Design) class OrderScreen extends StatefulWidget { const OrderScreen({super.key}); @override State createState() => _OrderScreenState(); } class _OrderScreenState extends State { Customer? _selectedCustomer; final DatabaseHelper _db = DatabaseHelper.instance; List _products = []; List _customers = []; List _items = []; String? _orderDate; // Default: 現在時刻 @override void initState() { super.initState(); _loadProducts(); _loadCustomers(); } Future _loadProducts() async { try { final products = await _db.getProducts(); setState(() => _products = products); } catch (e) { debugPrint('Product loading failed: $e'); } } Future _loadCustomers() async { try { final customers = await _db.getCustomers(); setState(() => _customers = customers.where((c) => c.isDeleted == 0).toList()); } catch (e) { debugPrint('Customer loading failed: $e'); } } Future _showCustomerPicker() async { if (_customers.isEmpty) await _loadCustomers(); final selected = await showModalBottomSheet( context: context, builder: (ctx) => SizedBox( height: MediaQuery.of(context).size.height * 0.4, child: ListView.builder( padding: const EdgeInsets.all(8), itemCount: _customers.length, itemBuilder: (ctx, index) => ListTile( title: Text(_customers[index].name), subtitle: Text('コード:${_customers[index].customerCode}'), onTap: () => Navigator.pop(ctx, _customers[index]), ), ), ), ); if (selected is Customer && selected.id != _selectedCustomer?.id) { setState(() => _selectedCustomer = selected); } } void _addSelectedProducts() async { for (final product in _products) { final existingStock = product.stock; if (existingStock > 0 && !_items.any((i) => i.productId == product.id)) { setState(() => _items.add(LineItem( orderId: DateTime.now().millisecondsSinceEpoch, productId: product.id, productName: product.name, unitPrice: product.price, quantity: 1, total: product.price, stockRemaining: existingStock - 1, ))); } } _showAddDialog(); } void _removeLineItem(int index) { setState(() => _items.removeAt(index)); } String? get _orderId => _items.isNotEmpty ? _items.first.orderId.toString() : null; int get _totalAmount => _items.fold(0, (sum, item) => sum + item.total); @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: const Text('受注入力')), body: ListView( padding: const EdgeInsets.all(16), children: [ _buildCustomerField(), const SizedBox(height: 16), Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ const Text('発注日'), DropdownButton( value: _orderDate ?? '', items: ['', '2026-03-07'].map((s) => DropdownMenuItem(value: s, child: Text(s))).toList(), onChanged: (v) => setState(() => _orderDate = v), ), ], ), const SizedBox(height: 8), Card( margin: EdgeInsets.zero, child: ExpansionTile( title: const Text('受注商品'), children: [ if (_items.isEmpty) ...[ Padding( padding: const EdgeInsets.all(16), child: Column( children: [ Icon(Icons.shopping_cart_outlined, size: 48, color: Colors.grey.shade400), const SizedBox(height: 8), Text('商品を追加してください', style: TextStyle(color: Colors.grey.shade600)), ], ), ), ] else ...[ ListView.builder( shrinkWrap: true, padding: EdgeInsets.zero, itemCount: _items.length, itemBuilder: (context, index) => Card( margin: const EdgeInsets.symmetric(vertical: 4), child: ListTile( leading: CircleAvatar( backgroundColor: Colors.teal.shade100, child: Icon(Icons.shopping_cart, color: Colors.teal), ), title: Text(_items[index].productName), subtitle: Text('数量:${_items[index].quantity} / 単価:¥${_items[index].unitPrice}'), trailing: IconButton(icon: const Icon(Icons.delete, color: Colors.red), onPressed: () => _removeLineItem(index)), ), ), ), ], ), ), ), ], ), ); } Widget _buildCustomerField() { return TextField( decoration: InputDecoration( labelText: '得意先', hintText: _selectedCustomer != null ? _selectedCustomer.name : '得意先マスタから選択', prefixIcon: Icon(Icons.person_search), isReadOnly: true, ), onTap: () => _showCustomerPicker(), ); } void _showAddDialog() async { final selected = await showModalBottomSheet( context: context, builder: (ctx) => ListView.builder( padding: EdgeInsets.zero, itemCount: _products.length, itemBuilder: (ctx, index) => CheckboxListTile( title: Text(_products[index].name), subtitle: Text('¥${_products[index].price} / 在庫:${_products[index].stock}${_products[index].unit ?? ''}'), value: _items.any((i) => i.productId == _products[index].id), onChanged: (value) { if (value && _products[index].stock > 0) _addSelectedProducts(); }, ), ), ); if (selected != null && selected.id != null && _products[selected.id]?.stock! > 0 && !_items.any((i) => i.productId == selected.id)) { setState(() => _items.add(LineItem( orderId: _orderId ?? DateTime.now().millisecondsSinceEpoch, productId: selected.id, productName: selected.name, unitPrice: selected.price, quantity: 1, total: selected.price, stockRemaining: _products[selected.id].stock - 1, ))); } } void _showSaveDialog() async { if (_items.isEmpty) { ScaffoldMessenger.of(context).showSnackBar( const SnackBar(content: Text('商品を追加してください')), ); return; } showDialog( context: context, builder: (ctx) => AlertDialog( title: const Text('受注確定'), content: SingleChildScrollView( child: Column( mainAxisSize: MainAxisSize.min, children: [ if (_selectedCustomer != null) ...[ Padding( padding: const EdgeInsets.symmetric(vertical: 8), child: Text('得意先:${_selectedCustomer!.name}'), ), ], Text('合計:¥${_totalAmount}'), if (_items.isNotEmpty) ...[ Divider(), ..._items.map((item) => Padding( padding: const EdgeInsets.symmetric(vertical: 4), child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text(item.productName), Text('¥${item.unitPrice} × ${item.quantity} = ¥${item.total}'), ], ), )), ], ], ), ), actions: [ TextButton( onPressed: () => Navigator.pop(ctx), child: const Text('キャンセル'), ), ElevatedButton( onPressed: () { Navigator.pop(ctx); ScaffoldMessenger.of(context).showSnackBar( const SnackBar(content: Text('受注保存しました'))..behavior: SnackBarBehavior.floating, ); }, child: const Text('確定'), ), ], ), ); } } /// 受注行モデル(在庫振替付き) class LineItem { final String? orderId; final int? productId; final String productName; final int unitPrice; int quantity = 1; final int stockRemaining; // 追加後の在庫数 LineItem({this.orderId, required this.productId, required this.productName, required this.unitPrice, this.quantity = 1, required this.stockRemaining}); }