""" 伝票データモデル Flutter参考プロジェクトの構造をFletに適用 """ from enum import Enum from datetime import datetime from typing import List, Optional, Dict, Any import json class DocumentType(Enum): """帳票の種類を定義""" ESTIMATE = "見積書" DELIVERY = "納品書" INVOICE = "請求書" RECEIPT = "領収書" SALES = "売上伝票" class Product: """商品マスタモデル""" def __init__(self, id: Optional[int] = None, name: str = "", unit_price: int = 0, description: str = ""): self.id = id self.name = name self.unit_price = unit_price self.description = description def to_dict(self) -> Dict[str, Any]: """JSON変換""" return { 'id': self.id, 'name': self.name, 'unit_price': self.unit_price, 'description': self.description } @classmethod def from_dict(cls, data: Dict[str, Any]) -> 'Product': """JSONから復元""" return cls( id=data.get('id'), name=data.get('name', ''), unit_price=data.get('unit_price', 0), description=data.get('description', '') ) class InvoiceItem: """伝票の各明細行を表すモデル""" def __init__(self, description: str, quantity: int, unit_price: int, is_discount: bool = False, product_id: Optional[int] = None): self.description = description self.quantity = quantity self.unit_price = unit_price self.is_discount = is_discount # 値引き項目かどうかを示すフラグ self.product_id = product_id # 商品マスタID @property def subtotal(self) -> int: """小計 (数量 * 単価)""" return self.quantity * self.unit_price * (-1 if self.is_discount else 1) def copy_with(self, **kwargs) -> 'InvoiceItem': """編集用のコピーメソッド""" return InvoiceItem( description=kwargs.get('description', self.description), quantity=kwargs.get('quantity', self.quantity), unit_price=kwargs.get('unit_price', self.unit_price), is_discount=kwargs.get('is_discount', self.is_discount), product_id=kwargs.get('product_id', self.product_id) ) def to_dict(self) -> Dict[str, Any]: """JSON変換""" return { 'description': self.description, 'quantity': self.quantity, 'unit_price': self.unit_price, 'is_discount': self.is_discount, 'product_id': self.product_id } @classmethod def from_dict(cls, data: Dict[str, Any]) -> 'InvoiceItem': """JSONから復元""" return cls( description=data['description'], quantity=data['quantity'], unit_price=data['unit_price'], is_discount=data.get('is_discount', False), product_id=data.get('product_id') ) class Customer: """顧客情報モデル""" def __init__(self, id: int, name: str, formal_name: str, address: str = "", phone: str = "", email: str = ""): self.id = id self.name = name self.formal_name = formal_name self.address = address self.phone = phone self.email = email def to_dict(self) -> Dict[str, Any]: """JSON変換""" return { 'id': self.id, 'name': self.name, 'formal_name': self.formal_name, 'address': self.address, 'phone': self.phone } @classmethod def from_dict(cls, data: Dict[str, Any]) -> 'Customer': """JSONから復元""" return cls( id=data['id'], name=data['name'], formal_name=data['formal_name'], address=data.get('address', ''), phone=data.get('phone', ''), email=data.get('email', '') ) class Invoice: """帳票全体を管理するモデル (見積・納品・請求・領収に対応)""" def __init__(self, customer: Customer, date: datetime, items: List[InvoiceItem], invoice_number: Optional[str] = None, notes: Optional[str] = None, is_shared: bool = False, document_type: DocumentType = DocumentType.INVOICE, file_path: Optional[str] = None, uuid: Optional[str] = None): import uuid as uuid_module self.uuid = uuid or str(uuid_module.uuid4()) self.customer = customer self.date = date self.items = items self.invoice_number = invoice_number or self._generate_invoice_number() self.notes = notes self.is_shared = is_shared self.document_type = document_type self.file_path = file_path def _generate_invoice_number(self) -> str: """請求書番号を生成""" return self.date.strftime('%Y%m%d-%H%M') @property def client_name(self) -> str: """互換性のためのゲッター""" return self.customer.formal_name @property def subtotal(self) -> int: """税抜合計金額""" return sum(item.subtotal for item in self.items) @property def tax(self) -> int: """消費税 (10%固定として計算、端数切り捨て)""" return int(self.subtotal * 0.1) @property def total_amount(self) -> int: """税込合計金額""" return self.subtotal + self.tax def copy_with(self, **kwargs) -> 'Invoice': """状態更新のためのコピーメソッド""" return Invoice( customer=kwargs.get('customer', self.customer), date=kwargs.get('date', self.date), items=kwargs.get('items', self.items), invoice_number=kwargs.get('invoice_number', self.invoice_number), notes=kwargs.get('notes', self.notes), is_shared=kwargs.get('is_shared', self.is_shared), document_type=kwargs.get('document_type', self.document_type), file_path=kwargs.get('file_path', self.file_path) ) def to_csv(self) -> str: """CSV形式への変換""" lines = [ f"Type,{self.document_type.value}", f"Customer,{self.customer.formal_name}", f"Number,{self.invoice_number}", f"Date,{self.date.strftime('%Y/%m/%d')}", f"Shared,{'Yes' if self.is_shared else 'No'}", "", "Description,Quantity,UnitPrice,Subtotal,IsDiscount" ] for item in self.items: lines.append(f"{item.description},{item.quantity},{item.unit_price},{item.subtotal},{'Yes' if item.is_discount else 'No'}") return '\n'.join(lines) def to_dict(self) -> Dict[str, Any]: """JSON変換 (データベース保存用)""" return { 'uuid': self.uuid, 'customer': self.customer.to_dict(), 'date': self.date.isoformat(), 'items': [item.to_dict() for item in self.items], 'file_path': self.file_path, 'invoice_number': self.invoice_number, 'notes': self.notes, 'is_shared': self.is_shared, 'document_type': self.document_type.value } @classmethod def from_dict(cls, data: Dict[str, Any]) -> 'Invoice': """JSONから復元 (データベース読み込み用)""" customer = Customer.from_dict(data['customer']) date = datetime.fromisoformat(data['date']) items = [InvoiceItem.from_dict(item_data) for item_data in data['items']] # DocumentTypeの文字列からEnumに変換 doc_type_str = data.get('document_type', '請求書') doc_type = next((dt for dt in DocumentType if dt.value == doc_type_str), DocumentType.INVOICE) return cls( customer=customer, date=date, items=items, invoice_number=data['invoice_number'], notes=data.get('notes'), is_shared=data.get('is_shared', False), document_type=doc_type, file_path=data.get('file_path'), uuid=data.get('uuid') ) # サンプルデータ生成関数 def create_sample_invoices() -> List[Invoice]: """サンプル伝票データを生成""" customers = [ Customer(1, "田中商事", "田中商事株式会社", "東京都千代田区丸の内1-1-1", "03-1234-5678"), Customer(2, "鈴木商店", "鈴木商店", "東京都港区芝1-1-1", "03-2345-6789"), Customer(3, "佐藤工業", "佐藤工業株式会社", "東京都品川区東品川1-1-1", "03-3456-7890"), Customer(4, "高橋建設", "高橋建設株式会社", "東京都新宿区西新宿1-1-1", "03-4567-8901"), Customer(5, "伊藤電機", "伊藤電機株式会社", "東京都渋谷区渋谷1-1-1", "03-5678-9012") ] sample_invoices = [ Invoice( customer=customers[0], date=datetime(2024, 1, 15), items=[ InvoiceItem("A商品セット", 1, 150000), InvoiceItem("設置費用", 1, 25000) ], document_type=DocumentType.SALES, notes="A商品セット販売" ), Invoice( customer=customers[1], date=datetime(2024, 1, 14), items=[ InvoiceItem("B部品", 10, 8500) ], document_type=DocumentType.ESTIMATE, notes="B部品見積" ), Invoice( customer=customers[2], date=datetime(2024, 1, 13), items=[ InvoiceItem("C機器", 1, 120000) ], document_type=DocumentType.DELIVERY, notes="C機器納品" ), Invoice( customer=customers[3], date=datetime(2024, 1, 12), items=[ InvoiceItem("D工事費用", 1, 200000) ], document_type=DocumentType.INVOICE, notes="D工事請求" ), Invoice( customer=customers[4], date=datetime(2024, 1, 11), items=[ InvoiceItem("E製品", 1, 75000) ], document_type=DocumentType.RECEIPT, notes="E製品領収" ) ] return sample_invoices