freight.py 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229
  1. """运输计划管理 - 运费结算 Schema"""
  2. from datetime import date, datetime
  3. from decimal import Decimal
  4. from typing import Any, Optional
  5. from pydantic import BaseModel, Field, model_validator
  6. # =============================================================================
  7. # 运费结算 Schema
  8. # =============================================================================
  9. class FreightSettlementBase(BaseModel):
  10. total_freight: Decimal = Field(default=Decimal("0.00"), alias="totalFreight", description="运费合计")
  11. invoice_amount: Optional[Decimal] = Field(default=None, alias="invoiceAmount", description="发票金额")
  12. invoice_no: Optional[str] = Field(default=None, max_length=50, alias="invoiceNo", description="发票号")
  13. invoice_date: Optional[date] = Field(default=None, alias="invoiceDate", description="开票日期")
  14. invoice_attachment_urls: Optional[str] = Field(
  15. default=None, alias="invoiceAttachmentUrls", description="发票附件URLs,逗号分隔"
  16. )
  17. settlement_status: str = Field(default="pending", alias="settlementStatus", description="结算状态")
  18. remark: Optional[str] = None
  19. class FreightSettlementCreate(BaseModel):
  20. transport_ids: Optional[list[str]] = Field(default=None, alias="transportIds", description="运输ID列表")
  21. plan_ids: Optional[list[str]] = Field(default=None, alias="planIds", description="计划ID列表")
  22. model_config = {"populate_by_name": True}
  23. @model_validator(mode="after")
  24. def check_ids(self):
  25. if not self.transport_ids and not self.plan_ids:
  26. raise ValueError("transportIds 或 planIds 至少提供一个")
  27. return self
  28. class FreightSettlementUpdate(BaseModel):
  29. invoice_amount: Optional[Decimal] = Field(default=None, alias="invoiceAmount")
  30. invoice_no: Optional[str] = Field(default=None, max_length=50, alias="invoiceNo")
  31. invoice_date: Optional[date] = Field(default=None, alias="invoiceDate")
  32. invoice_attachment_urls: Optional[str] = Field(default=None, alias="invoiceAttachmentUrls")
  33. remark: Optional[str] = None
  34. transport_ids: Optional[list[str]] = Field(
  35. default=None, alias="transportIds", description="关联运输ID全量列表,仅草稿可更新"
  36. )
  37. model_config = {"populate_by_name": True}
  38. class FreightSettlementRead(FreightSettlementBase):
  39. id: str
  40. settlement_no: str = Field(alias="settlementNo")
  41. tenant_no: str = Field(alias="tenantNo")
  42. order_num: Optional[int] = Field(default=None, alias="orderNum")
  43. create_by: Optional[str] = Field(default=None, alias="createBy")
  44. create_dept_id: Optional[str] = Field(default=None, alias="createDeptId")
  45. create_time: datetime = Field(alias="createTime")
  46. update_by: Optional[str] = Field(default=None, alias="updateBy")
  47. update_time: datetime = Field(alias="updateTime")
  48. del_flag: int = Field(default=0, alias="delFlag")
  49. model_config = {"from_attributes": True, "populate_by_name": True}
  50. class FreightSettlementListItem(FreightSettlementRead):
  51. plan_count: int = Field(default=0, alias="planCount")
  52. license_plate_summary: Optional[str] = Field(default=None, alias="licensePlateSummary")
  53. current_approver_name: Optional[str] = Field(default=None, alias="currentApproverName")
  54. can_approve: bool = Field(default=False, alias="canApprove")
  55. can_withdraw: bool = Field(default=False, alias="canWithdraw")
  56. current_approval_id: Optional[str] = Field(default=None, alias="currentApprovalId")
  57. approval_finished_time: Optional[datetime] = Field(default=None, alias="approvalFinishedTime")
  58. disburse_time: Optional[datetime] = Field(default=None, alias="disburseTime")
  59. disburse_amount: Optional[Decimal] = Field(default=None, alias="disburseAmount")
  60. submitter_name: Optional[str] = Field(default=None, alias="submitterName")
  61. model_config = {"from_attributes": True, "populate_by_name": True}
  62. class FreightSettlementTransportRead(BaseModel):
  63. id: str
  64. settlement_id: str = Field(alias="settlementId")
  65. transport_id: str = Field(alias="transportId")
  66. freight_amount: Decimal = Field(alias="freightAmount")
  67. tenant_no: str = Field(alias="tenantNo")
  68. create_time: datetime = Field(alias="createTime")
  69. model_config = {"from_attributes": True, "populate_by_name": True}
  70. class UnsettledPlanItem(BaseModel):
  71. id: str
  72. plan_no: str = Field(alias="planNo")
  73. plan_date: Optional[date] = Field(default=None, alias="planDate")
  74. plan_type: str = Field(alias="planType")
  75. customer_name: Optional[str] = Field(default=None, alias="customerName")
  76. supplier_name: Optional[str] = Field(default=None, alias="supplierName")
  77. license_plate: Optional[str] = Field(default=None, alias="licensePlate")
  78. transport_company: Optional[str] = Field(default=None, alias="transportCompany")
  79. freight_amount: Decimal = Field(default=Decimal("0"), alias="freightAmount")
  80. archive_time: Optional[datetime] = Field(default=None, alias="archiveTime")
  81. transport_id: str = Field(alias="transportId")
  82. transport_no: Optional[str] = Field(default=None, alias="transportNo")
  83. driver_name: Optional[str] = Field(default=None, alias="driverName")
  84. transport_medium: Optional[str] = Field(default=None, alias="transportMedium")
  85. start_point: Optional[str] = Field(default=None, alias="startPoint")
  86. end_point: Optional[str] = Field(default=None, alias="endPoint")
  87. model_config = {"populate_by_name": True}
  88. class UnsettledPlanListResponse(BaseModel):
  89. total: int
  90. list: list[UnsettledPlanItem]
  91. class SettlementPlanSummary(BaseModel):
  92. id: str
  93. plan_no: str = Field(alias="planNo")
  94. plan_date: Optional[date] = Field(default=None, alias="planDate")
  95. license_plate: Optional[str] = Field(default=None, alias="licensePlate")
  96. freight_amount: Optional[Decimal] = Field(default=None, alias="freightAmount")
  97. model_config = {"populate_by_name": True}
  98. class SettlementApprovalFlowItem(BaseModel):
  99. id: str
  100. step: int
  101. step_name: Optional[str] = Field(default=None, alias="stepName")
  102. approver_id: str = Field(alias="approverId")
  103. approver_name: Optional[str] = Field(default=None, alias="approverName")
  104. approval_type: Optional[str] = Field(default=None, alias="approvalType")
  105. approval_result: str = Field(alias="approvalResult")
  106. approval_opinion: Optional[str] = Field(default=None, alias="approvalOpinion")
  107. approval_time: Optional[datetime] = Field(default=None, alias="approvalTime")
  108. is_current: int = Field(default=0, alias="isCurrent")
  109. model_config = {"populate_by_name": True}
  110. class FreightSettlementWithTransports(FreightSettlementRead):
  111. transports: list = []
  112. plans: list[SettlementPlanSummary] = []
  113. approval_flow: list[SettlementApprovalFlowItem] = Field(default_factory=list, alias="approvalFlow")
  114. approval_history: list[SettlementApprovalFlowItem] = Field(default_factory=list, alias="approvalHistory")
  115. vouchers: list = []
  116. plan_count: int = Field(default=0, alias="planCount")
  117. license_plate_summary: Optional[str] = Field(default=None, alias="licensePlateSummary")
  118. current_approver_name: Optional[str] = Field(default=None, alias="currentApproverName")
  119. can_approve: bool = Field(default=False, alias="canApprove")
  120. can_withdraw: bool = Field(default=False, alias="canWithdraw")
  121. current_approval_id: Optional[str] = Field(default=None, alias="currentApprovalId")
  122. approval_finished_time: Optional[datetime] = Field(default=None, alias="approvalFinishedTime")
  123. disburse_time: Optional[datetime] = Field(default=None, alias="disburseTime")
  124. disburse_amount: Optional[Decimal] = Field(default=None, alias="disburseAmount")
  125. submitter_name: Optional[str] = Field(default=None, alias="submitterName")
  126. class FreightSettlementSubmitApprovalBody(BaseModel):
  127. approval_type: str = Field(default="sequential", alias="approvalType")
  128. approver_ids: list[str] = Field(..., alias="approverIds", min_length=1)
  129. remark: Optional[str] = None
  130. model_config = {"populate_by_name": True}
  131. class FreightSettlementApprovalActionBody(BaseModel):
  132. action: str = Field(..., description="approve|reject")
  133. comment: Optional[str] = None
  134. class FreightSettlementDisburseBody(BaseModel):
  135. remittance_date: date = Field(..., alias="remittanceDate")
  136. remittance_amount: Decimal = Field(..., alias="remittanceAmount", gt=0)
  137. voucher_urls: list[str] = Field(..., alias="voucherUrls", min_length=1)
  138. remark: Optional[str] = None
  139. model_config = {"populate_by_name": True}
  140. # =============================================================================
  141. # 汇款凭证 Schema
  142. # =============================================================================
  143. class RemittanceVoucherCreate(BaseModel):
  144. settlement_id: str = Field(..., alias="settlementId", description="结算单ID")
  145. remittance_date: Optional[date] = Field(default=None, alias="remittanceDate", description="汇款日期")
  146. remittance_amount: Optional[Decimal] = Field(default=None, alias="remittanceAmount", description="汇款金额")
  147. voucher_urls: Optional[list[str]] = Field(default=None, alias="voucherUrls", description="凭证文件URLs")
  148. remark: Optional[str] = None
  149. model_config = {"populate_by_name": True}
  150. class RemittanceVoucherRead(BaseModel):
  151. id: str
  152. settlement_id: str = Field(alias="settlementId")
  153. voucher_no: Optional[str] = Field(default=None, alias="voucherNo")
  154. remittance_date: Optional[date] = Field(default=None, alias="remittanceDate")
  155. remittance_amount: Optional[Decimal] = Field(default=None, alias="remittanceAmount")
  156. voucher_urls: Optional[str] = Field(default=None, alias="voucherUrls")
  157. remark: Optional[str] = None
  158. tenant_no: str = Field(alias="tenantNo")
  159. create_by: Optional[str] = Field(default=None, alias="createBy")
  160. create_time: datetime = Field(alias="createTime")
  161. model_config = {"from_attributes": True, "populate_by_name": True}
  162. # =============================================================================
  163. # 列表查询
  164. # =============================================================================
  165. class FreightSettlementListQuery(BaseModel):
  166. q: Optional[str] = Field(default=None, description="模糊搜索")
  167. settlement_status: Optional[str] = Field(default=None, alias="settlementStatus", description="结算状态")
  168. tab: Optional[str] = Field(default=None, description="bills|disburse|settled")
  169. page: int = Field(default=1, ge=1)
  170. page_size: int = Field(default=20, ge=1, le=100, alias="pageSize")
  171. model_config = {"populate_by_name": True}
  172. class FreightSettlementListResponse(BaseModel):
  173. total: int
  174. list: list[FreightSettlementListItem]
  175. class FreightSettlementByTransportRead(BaseModel):
  176. id: str
  177. settlement_no: str = Field(alias="settlementNo")
  178. invoice_amount: Optional[Decimal] = Field(default=None, alias="invoiceAmount")
  179. invoice_no: Optional[str] = Field(default=None, alias="invoiceNo")
  180. invoice_date: Optional[date] = Field(default=None, alias="invoiceDate")
  181. invoice_attachment_urls: Optional[str] = Field(default=None, alias="invoiceAttachmentUrls")
  182. settlement_status: str = Field(alias="settlementStatus")
  183. model_config = {"populate_by_name": True}