// lib/models/invoice_models.dart import 'package:intl/intl.dart'; import 'customer_model.dart'; /// 帳票の種類を定義 enum DocumentType { estimate('見積書'), delivery('納品書'), invoice('請求書'), receipt('領収書'); final String label; const DocumentType(this.label); } /// 請求書の各明細行を表すモデル class InvoiceItem { String description; int quantity; int unitPrice; bool isDiscount; // 値引き項目かどうかを示すフラグ InvoiceItem({ required this.description, required this.quantity, required this.unitPrice, this.isDiscount = false, // デフォルトはfalse (値引きではない) }); // 小計 (数量 * 単価) int get subtotal => quantity * unitPrice * (isDiscount ? -1 : 1); // 編集用のコピーメソッド InvoiceItem copyWith({ String? description, int? quantity, int? unitPrice, bool? isDiscount, }) { return InvoiceItem( description: description ?? this.description, quantity: quantity ?? this.quantity, unitPrice: unitPrice ?? this.unitPrice, isDiscount: isDiscount ?? this.isDiscount, ); } // JSON変換 Map toJson() { return { 'description': description, 'quantity': quantity, 'unit_price': unitPrice, 'is_discount': isDiscount, }; } // JSONから復元 factory InvoiceItem.fromJson(Map json) { return InvoiceItem( description: json['description'] as String, quantity: json['quantity'] as int, unitPrice: json['unit_price'] as int, isDiscount: json['is_discount'] ?? false, ); } } /// 帳票全体を管理するモデル (見積・納品・請求・領収に対応) class Invoice { Customer customer; // 顧客情報 DateTime date; List items; String? filePath; // 保存されたPDFのパス String invoiceNumber; // 請求書番号 String? notes; // 備考 bool isShared; // 外部共有(送信)済みフラグ。送信済みファイルは自動削除から保護する。 DocumentType type; // 帳票の種類 Invoice({ required this.customer, required this.date, required this.items, this.filePath, String? invoiceNumber, this.notes, this.isShared = false, this.type = DocumentType.invoice, }) : invoiceNumber = invoiceNumber ?? DateFormat('yyyyMMdd-HHmm').format(date); // 互換性のためのゲッター String get clientName => customer.formalName; // 税抜合計金額 int get subtotal { return items.fold(0, (sum, item) => sum + item.subtotal); } // 消費税 (10%固定として計算、端数切り捨て) int get tax { return (subtotal * 0.1).floor(); } // 税込合計金額 int get totalAmount { return subtotal + tax; } // 状態更新のためのコピーメソッド Invoice copyWith({ Customer? customer, DateTime? date, List? items, String? filePath, String? invoiceNumber, String? notes, bool? isShared, DocumentType? type, }) { return Invoice( customer: customer ?? this.customer, date: date ?? this.date, items: items ?? this.items, filePath: filePath ?? this.filePath, invoiceNumber: invoiceNumber ?? this.invoiceNumber, notes: notes ?? this.notes, isShared: isShared ?? this.isShared, type: type ?? this.type, ); } // CSV形式への変換 String toCsv() { StringBuffer sb = StringBuffer(); sb.writeln("Type,${type.label}"); sb.writeln("Customer,${customer.formalName}"); sb.writeln("Number,$invoiceNumber"); sb.writeln("Date,${DateFormat('yyyy/MM/dd').format(date)}"); sb.writeln("Shared,${isShared ? 'Yes' : 'No'}"); sb.writeln(""); sb.writeln("Description,Quantity,UnitPrice,Subtotal,IsDiscount"); // isDiscountを追加 for (var item in items) { sb.writeln("${item.description},${item.quantity},${item.unitPrice},${item.subtotal},${item.isDiscount ? 'Yes' : 'No'}"); } return sb.toString(); } // JSON変換 (データベース保存用) Map toJson() { return { 'customer': customer.toJson(), 'date': date.toIso8601String(), 'items': items.map((item) => item.toJson()).toList(), 'file_path': filePath, 'invoice_number': invoiceNumber, 'notes': notes, 'is_shared': isShared, 'type': type.name, // Enumの名前で保存 }; } // JSONから復元 (データベース読み込み用) factory Invoice.fromJson(Map json) { return Invoice( customer: Customer.fromJson(json['customer'] as Map), date: DateTime.parse(json['date'] as String), items: (json['items'] as List) .map((i) => InvoiceItem.fromJson(i as Map)) .toList(), filePath: json['file_path'] as String?, invoiceNumber: json['invoice_number'] as String, notes: (json['notes'] == 'null') ? null : json['notes'] as String?, // 'null'文字列の可能性も考慮 isShared: json['is_shared'] ?? false, type: DocumentType.values.firstWhere( (e) => e.name == (json['type'] ?? 'invoice'), orElse: () => DocumentType.invoice, ), ); } }