from pydantic import BaseModel, ConfigDict, Field from typing import Optional, List, Literal from datetime import datetime from decimal import Decimal 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: Decimal = Field(gt=0) class FinanceTransactionCreate(BaseModel): amount: Decimal = 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: Decimal model_config = ConfigDict(from_attributes=True) class FinanceTransactionResponse(BaseModel): id: int txn_no: str txn_type: TxnType partner_type: PartnerType partner_id: int partner_name: Optional[str] = None amount: Decimal txn_date: datetime method: str account_name: Optional[str] status: TxnStatus remark: Optional[str] created_at: datetime allocations: List[FinanceAllocationResponse] = [] model_config = ConfigDict(from_attributes=True) class FinanceSummaryResponse(BaseModel): receivable_total: Decimal payable_total: Decimal monthly_receipt_total: Decimal monthly_payment_total: Decimal selected_year: int selected_quarter: Optional[int] = None period_label: str period_receipt_total: Decimal = Decimal("0") period_payment_total: Decimal = Decimal("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: Decimal received_amount: Decimal receivable_amount: Decimal status: str class PayableItemResponse(BaseModel): order_id: int order_no: str supplier_id: int supplier_name: str order_date: datetime total_amount: Decimal paid_amount: Decimal payable_amount: Decimal status: str class PartnerStatementItemResponse(BaseModel): partner_id: int partner_name: str order_count: int transaction_count: int order_total: Decimal settled_total: Decimal transaction_total: Decimal outstanding_total: Decimal 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: Decimal settled_total: Decimal transaction_total: Decimal outstanding_total: Decimal 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: Decimal order_amount: Decimal settled_amount: Decimal outstanding_amount: Decimal 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: Decimal settled_amount_total: Decimal outstanding_amount_total: Decimal items: List[PartnerProductStatementItemResponse] = []