h-1.flet.3/models/invoice_models.py
2026-02-20 23:24:01 +09:00

267 lines
9.2 KiB
Python

"""
伝票データモデル
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 InvoiceItem:
"""伝票の各明細行を表すモデル"""
def __init__(self, description: str, quantity: int, unit_price: int, is_discount: bool = False):
self.description = description
self.quantity = quantity
self.unit_price = unit_price
self.is_discount = is_discount # 値引き項目かどうかを示すフラグ
@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)
)
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
}
@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)
)
class Customer:
"""顧客情報モデル"""
def __init__(self, id: int, name: str, formal_name: str, address: str = "", phone: str = ""):
self.id = id
self.name = name
self.formal_name = formal_name
self.address = address
self.phone = phone
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', '')
)
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