from pydantic import BaseModel, Field from typing import Optional, List, Literal from datetime import datetime TxnType = Literal["receipt", "payment"] PartnerType = Literal["customer", "supplier"] OrderType = Literal["sales", "purchase"] TxnStatus = Literal["confirmed", "voided"] class FinanceAllocationCreate(BaseModel): order_type: OrderType order_id: int allocated_amount: float = Field(gt=0) class FinanceTransactionCreate(BaseModel): amount: float = Field(gt=0) txn_date: Optional[datetime] = None method: str = "bank" account_name: Optional[str] = None remark: Optional[str] = None allocations: List[FinanceAllocationCreate] class ReceiptCreate(FinanceTransactionCreate): customer_id: int class PaymentCreate(FinanceTransactionCreate): supplier_id: int class FinanceAllocationResponse(BaseModel): id: int order_type: str order_id: int allocated_amount: float class Config: from_attributes = True class FinanceTransactionResponse(BaseModel): id: int txn_no: str txn_type: TxnType partner_type: PartnerType partner_id: int amount: float txn_date: datetime method: str account_name: Optional[str] status: TxnStatus remark: Optional[str] created_at: datetime allocations: List[FinanceAllocationResponse] = [] class Config: from_attributes = True class FinanceSummaryResponse(BaseModel): receivable_total: float payable_total: float monthly_receipt_total: float monthly_payment_total: float selected_year: int selected_quarter: Optional[int] = None period_label: str period_receipt_total: float = 0 period_payment_total: float = 0 overdue_receivable_count: int = 0 overdue_payable_count: int = 0 class ReceivableItemResponse(BaseModel): order_id: int order_no: str customer_id: int customer_name: str order_date: datetime total_amount: float received_amount: float receivable_amount: float status: str class PayableItemResponse(BaseModel): order_id: int order_no: str supplier_id: int supplier_name: str order_date: datetime total_amount: float paid_amount: float payable_amount: float status: str class PartnerStatementItemResponse(BaseModel): partner_id: int partner_name: str order_count: int transaction_count: int order_total: float settled_total: float transaction_total: float outstanding_total: float period_year: int period_quarter: Optional[int] = None class FinancePartnerStatementResponse(BaseModel): partner_type: PartnerType year: int quarter: Optional[int] = None period_label: str order_total: float settled_total: float transaction_total: float outstanding_total: float items: List[PartnerStatementItemResponse] = [] class PartnerProductStatementItemResponse(BaseModel): partner_id: int partner_name: str product_id: int product_sku: Optional[str] = None product_name: str order_count: int order_quantity: float order_amount: float settled_amount: float outstanding_amount: float period_year: int period_quarter: Optional[int] = None class FinancePartnerProductStatementResponse(BaseModel): partner_type: PartnerType year: int quarter: Optional[int] = None period_label: str partner_id: Optional[int] = None order_amount_total: float settled_amount_total: float outstanding_amount_total: float items: List[PartnerProductStatementItemResponse] = []