"""运输计划管理 - 运费结算 Schema""" from datetime import date, datetime from decimal import Decimal from typing import Any, Optional from pydantic import BaseModel, Field, model_validator # ============================================================================= # 运费结算 Schema # ============================================================================= class FreightSettlementBase(BaseModel): total_freight: Decimal = Field(default=Decimal("0.00"), alias="totalFreight", description="运费合计") invoice_amount: Optional[Decimal] = Field(default=None, alias="invoiceAmount", description="发票金额") invoice_no: Optional[str] = Field(default=None, max_length=50, alias="invoiceNo", description="发票号") invoice_date: Optional[date] = Field(default=None, alias="invoiceDate", description="开票日期") invoice_attachment_urls: Optional[str] = Field( default=None, alias="invoiceAttachmentUrls", description="发票附件URLs,逗号分隔" ) settlement_status: str = Field(default="pending", alias="settlementStatus", description="结算状态") remark: Optional[str] = None class FreightSettlementCreate(BaseModel): transport_ids: Optional[list[str]] = Field(default=None, alias="transportIds", description="运输ID列表") plan_ids: Optional[list[str]] = Field(default=None, alias="planIds", description="计划ID列表") model_config = {"populate_by_name": True} @model_validator(mode="after") def check_ids(self): if not self.transport_ids and not self.plan_ids: raise ValueError("transportIds 或 planIds 至少提供一个") return self class FreightSettlementUpdate(BaseModel): invoice_amount: Optional[Decimal] = Field(default=None, alias="invoiceAmount") invoice_no: Optional[str] = Field(default=None, max_length=50, alias="invoiceNo") invoice_date: Optional[date] = Field(default=None, alias="invoiceDate") invoice_attachment_urls: Optional[str] = Field(default=None, alias="invoiceAttachmentUrls") remark: Optional[str] = None transport_ids: Optional[list[str]] = Field( default=None, alias="transportIds", description="关联运输ID全量列表,仅草稿可更新" ) model_config = {"populate_by_name": True} class FreightSettlementRead(FreightSettlementBase): id: str settlement_no: str = Field(alias="settlementNo") tenant_no: str = Field(alias="tenantNo") order_num: Optional[int] = Field(default=None, alias="orderNum") create_by: Optional[str] = Field(default=None, alias="createBy") create_dept_id: Optional[str] = Field(default=None, alias="createDeptId") create_time: datetime = Field(alias="createTime") update_by: Optional[str] = Field(default=None, alias="updateBy") update_time: datetime = Field(alias="updateTime") del_flag: int = Field(default=0, alias="delFlag") model_config = {"from_attributes": True, "populate_by_name": True} class FreightSettlementListItem(FreightSettlementRead): plan_count: int = Field(default=0, alias="planCount") license_plate_summary: Optional[str] = Field(default=None, alias="licensePlateSummary") current_approver_name: Optional[str] = Field(default=None, alias="currentApproverName") can_approve: bool = Field(default=False, alias="canApprove") can_withdraw: bool = Field(default=False, alias="canWithdraw") current_approval_id: Optional[str] = Field(default=None, alias="currentApprovalId") approval_finished_time: Optional[datetime] = Field(default=None, alias="approvalFinishedTime") disburse_time: Optional[datetime] = Field(default=None, alias="disburseTime") disburse_amount: Optional[Decimal] = Field(default=None, alias="disburseAmount") submitter_name: Optional[str] = Field(default=None, alias="submitterName") model_config = {"from_attributes": True, "populate_by_name": True} class FreightSettlementTransportRead(BaseModel): id: str settlement_id: str = Field(alias="settlementId") transport_id: str = Field(alias="transportId") freight_amount: Decimal = Field(alias="freightAmount") tenant_no: str = Field(alias="tenantNo") create_time: datetime = Field(alias="createTime") model_config = {"from_attributes": True, "populate_by_name": True} class UnsettledPlanItem(BaseModel): id: str plan_no: str = Field(alias="planNo") plan_date: Optional[date] = Field(default=None, alias="planDate") plan_type: str = Field(alias="planType") customer_name: Optional[str] = Field(default=None, alias="customerName") supplier_name: Optional[str] = Field(default=None, alias="supplierName") license_plate: Optional[str] = Field(default=None, alias="licensePlate") transport_company: Optional[str] = Field(default=None, alias="transportCompany") freight_amount: Decimal = Field(default=Decimal("0"), alias="freightAmount") freight_remark: Optional[str] = Field(default=None, alias="freightRemark") archive_time: Optional[datetime] = Field(default=None, alias="archiveTime") transport_id: str = Field(alias="transportId") transport_no: Optional[str] = Field(default=None, alias="transportNo") driver_name: Optional[str] = Field(default=None, alias="driverName") transport_medium: Optional[str] = Field(default=None, alias="transportMedium") start_point: Optional[str] = Field(default=None, alias="startPoint") end_point: Optional[str] = Field(default=None, alias="endPoint") model_config = {"populate_by_name": True} class UnsettledPlanListResponse(BaseModel): total: int list: list[UnsettledPlanItem] class SettlementPlanSummary(BaseModel): id: str plan_no: str = Field(alias="planNo") plan_date: Optional[date] = Field(default=None, alias="planDate") license_plate: Optional[str] = Field(default=None, alias="licensePlate") freight_amount: Optional[Decimal] = Field(default=None, alias="freightAmount") freight_remark: Optional[str] = Field(default=None, alias="freightRemark") model_config = {"populate_by_name": True} class SettlementApprovalFlowItem(BaseModel): id: str step: int step_name: Optional[str] = Field(default=None, alias="stepName") approver_id: str = Field(alias="approverId") approver_name: Optional[str] = Field(default=None, alias="approverName") approval_type: Optional[str] = Field(default=None, alias="approvalType") approval_result: str = Field(alias="approvalResult") approval_opinion: Optional[str] = Field(default=None, alias="approvalOpinion") approval_time: Optional[datetime] = Field(default=None, alias="approvalTime") is_current: int = Field(default=0, alias="isCurrent") model_config = {"populate_by_name": True} class FreightSettlementWithTransports(FreightSettlementRead): transports: list = [] plans: list[SettlementPlanSummary] = [] approval_flow: list[SettlementApprovalFlowItem] = Field(default_factory=list, alias="approvalFlow") approval_history: list[SettlementApprovalFlowItem] = Field(default_factory=list, alias="approvalHistory") vouchers: list = [] plan_count: int = Field(default=0, alias="planCount") license_plate_summary: Optional[str] = Field(default=None, alias="licensePlateSummary") current_approver_name: Optional[str] = Field(default=None, alias="currentApproverName") can_approve: bool = Field(default=False, alias="canApprove") can_withdraw: bool = Field(default=False, alias="canWithdraw") current_approval_id: Optional[str] = Field(default=None, alias="currentApprovalId") approval_finished_time: Optional[datetime] = Field(default=None, alias="approvalFinishedTime") disburse_time: Optional[datetime] = Field(default=None, alias="disburseTime") disburse_amount: Optional[Decimal] = Field(default=None, alias="disburseAmount") submitter_name: Optional[str] = Field(default=None, alias="submitterName") class FreightSettlementSubmitApprovalBody(BaseModel): approval_type: str = Field(default="sequential", alias="approvalType") approver_ids: list[str] = Field(..., alias="approverIds", min_length=1) remark: Optional[str] = None model_config = {"populate_by_name": True} class FreightSettlementApprovalActionBody(BaseModel): action: str = Field(..., description="approve|reject") comment: Optional[str] = None class FreightSettlementDisburseBody(BaseModel): remittance_date: date = Field(..., alias="remittanceDate") remittance_amount: Decimal = Field(..., alias="remittanceAmount", gt=0) voucher_urls: list[str] = Field(..., alias="voucherUrls", min_length=1) remark: Optional[str] = None model_config = {"populate_by_name": True} # ============================================================================= # 汇款凭证 Schema # ============================================================================= class RemittanceVoucherCreate(BaseModel): settlement_id: str = Field(..., alias="settlementId", description="结算单ID") remittance_date: Optional[date] = Field(default=None, alias="remittanceDate", description="汇款日期") remittance_amount: Optional[Decimal] = Field(default=None, alias="remittanceAmount", description="汇款金额") voucher_urls: Optional[list[str]] = Field(default=None, alias="voucherUrls", description="凭证文件URLs") remark: Optional[str] = None model_config = {"populate_by_name": True} class RemittanceVoucherRead(BaseModel): id: str settlement_id: str = Field(alias="settlementId") voucher_no: Optional[str] = Field(default=None, alias="voucherNo") remittance_date: Optional[date] = Field(default=None, alias="remittanceDate") remittance_amount: Optional[Decimal] = Field(default=None, alias="remittanceAmount") voucher_urls: Optional[str] = Field(default=None, alias="voucherUrls") remark: Optional[str] = None tenant_no: str = Field(alias="tenantNo") create_by: Optional[str] = Field(default=None, alias="createBy") create_time: datetime = Field(alias="createTime") model_config = {"from_attributes": True, "populate_by_name": True} # ============================================================================= # 列表查询 # ============================================================================= class FreightSettlementListQuery(BaseModel): q: Optional[str] = Field(default=None, description="模糊搜索") settlement_status: Optional[str] = Field(default=None, alias="settlementStatus", description="结算状态") tab: Optional[str] = Field(default=None, description="bills|disburse|settled") page: int = Field(default=1, ge=1) page_size: int = Field(default=20, ge=1, le=500, alias="pageSize") model_config = {"populate_by_name": True} class FreightSettlementListResponse(BaseModel): total: int list: list[FreightSettlementListItem] class FreightSettlementByTransportRead(BaseModel): id: str settlement_no: str = Field(alias="settlementNo") invoice_amount: Optional[Decimal] = Field(default=None, alias="invoiceAmount") invoice_no: Optional[str] = Field(default=None, alias="invoiceNo") invoice_date: Optional[date] = Field(default=None, alias="invoiceDate") invoice_attachment_urls: Optional[str] = Field(default=None, alias="invoiceAttachmentUrls") settlement_status: str = Field(alias="settlementStatus") model_config = {"populate_by_name": True}