diff --git a/lib/models/customer_model.dart b/lib/models/customer_model.dart index 589518a..e9e5d06 100644 --- a/lib/models/customer_model.dart +++ b/lib/models/customer_model.dart @@ -4,6 +4,7 @@ class Customer { final String id; final String displayName; // 表示用(電話帳名など) final String formalName; // 請求書用正式名称 + final String title; // 敬称(様、殿など) final String? department; // 部署名 final String? address; // 住所 @@ -11,21 +12,24 @@ class Customer { required this.id, required this.displayName, required this.formalName, + this.title = "様", this.department, this.address, }); String get invoiceName { + String name = formalName; if (department != null && department!.isNotEmpty) { - return "$formalName\n$department"; + name = "$formalName\n$department"; } - return formalName; + return "$name $title"; } Customer copyWith({ String? id, String? displayName, String? formalName, + String? title, String? department, String? address, }) { @@ -33,6 +37,7 @@ class Customer { id: id ?? this.id, displayName: displayName ?? this.displayName, formalName: formalName ?? this.formalName, + title: title ?? this.title, department: department ?? this.department, address: address ?? this.address, ); diff --git a/lib/models/invoice_models.dart b/lib/models/invoice_models.dart index 94efcb2..a8ff9ea 100644 --- a/lib/models/invoice_models.dart +++ b/lib/models/invoice_models.dart @@ -2,9 +2,9 @@ import 'customer_model.dart'; import 'package:intl/intl.dart'; class InvoiceItem { - final String description; - final num quantity; - final int unitPrice; + String description; + int quantity; + int unitPrice; InvoiceItem({ required this.description, @@ -12,7 +12,7 @@ class InvoiceItem { required this.unitPrice, }); - int get subtotal => (quantity * unitPrice).floor(); + int get subtotal => quantity * unitPrice; } class Invoice { @@ -32,12 +32,30 @@ class Invoice { this.filePath, }) : id = id ?? DateTime.now().millisecondsSinceEpoch.toString(); - String get invoiceNumber => "INV-${DateFormat('yyyyMMdd').format(date)}-${id.substring(id.length - 4)}"; + String get invoiceNumber => "INV-${DateFormat('yyyyMMdd').format(date)}-${id.substring(id.length > 4 ? id.length - 4 : 0)}"; int get subtotal => items.fold(0, (sum, item) => sum + item.subtotal); int get tax => (subtotal * 0.1).floor(); int get totalAmount => subtotal + tax; + String toCsv() { + final dateFormatter = DateFormat('yyyy/MM/dd'); + final amountFormatter = NumberFormat("###"); + + StringBuffer buffer = StringBuffer(); + // ヘッダー (例) + buffer.writeln("日付,請求番号,取引先,合計金額,備考"); + buffer.writeln("${dateFormatter.format(date)},$invoiceNumber,${customer.formalName},$totalAmount,${notes ?? ""}"); + buffer.writeln(""); + buffer.writeln("品名,数量,単価,小計"); + + for (var item in items) { + buffer.writeln("${item.description},${item.quantity},${item.unitPrice},${item.subtotal}"); + } + + return buffer.toString(); + } + Invoice copyWith({ String? id, Customer? customer, @@ -50,7 +68,7 @@ class Invoice { id: id ?? this.id, customer: customer ?? this.customer, date: date ?? this.date, - items: items ?? this.items, + items: items ?? List.from(this.items), // コピーを作成 notes: notes ?? this.notes, filePath: filePath ?? this.filePath, ); diff --git a/lib/screens/product_picker_modal.dart b/lib/screens/product_picker_modal.dart new file mode 100644 index 0000000..80c127d --- /dev/null +++ b/lib/screens/product_picker_modal.dart @@ -0,0 +1,62 @@ +import 'package:flutter/material.dart'; +import '../models/invoice_models.dart'; + +/// 商品マスターから項目を選択するためのモーダル(スタブ実装) +class ProductPickerModal extends StatefulWidget { + final Function(InvoiceItem) onItemSelected; + + const ProductPickerModal({Key? key, required this.onItemSelected}) : super(key: key); + + @override + State createState() => _ProductPickerModalState(); +} + +class _ProductPickerModalState extends State { + // 本来はデータベースから取得しますが、現時点ではスタブデータを表示します + final List _masterProducts = [ + InvoiceItem(description: "技術料", quantity: 1, unitPrice: 50000), + InvoiceItem(description: "部品代 A", quantity: 1, unitPrice: 15000), + InvoiceItem(description: "部品代 B", quantity: 1, unitPrice: 3000), + InvoiceItem(description: "出張費", quantity: 1, unitPrice: 10000), + InvoiceItem(description: "諸経費", quantity: 1, unitPrice: 5000), + ]; + + @override + Widget build(BuildContext context) { + return Container( + decoration: const BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.vertical(top: Radius.circular(20)), + ), + child: Column( + children: [ + Padding( + padding: const EdgeInsets.all(16.0), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + const Text("商品・サービス選択", style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold)), + IconButton(icon: const Icon(Icons.close), onPressed: () => Navigator.pop(context)), + ], + ), + ), + const Divider(), + Expanded( + child: ListView.builder( + itemCount: _masterProducts.length, + itemBuilder: (context, index) { + final product = _masterProducts[index]; + return ListTile( + leading: const Icon(Icons.inventory_2_outlined), + title: Text(product.description), + subtitle: Text("単価: ¥${product.unitPrice}"), + onTap: () => widget.onItemSelected(product), + ); + }, + ), + ), + ], + ), + ); + } +}