"""CRM 合同 Pydantic Schemas — 对照 CrmContractBo / CrmContractVo""" from typing import List, Optional from pydantic import BaseModel, ConfigDict, Field class ContractProductPriceTierItem(BaseModel): model_config = ConfigDict(populate_by_name=True) id: Optional[str] = None contractId: Optional[str] = None contractProductId: Optional[str] = None startDate: Optional[str] = None endDate: Optional[str] = None price: Optional[float] = None letterOssIds: Optional[List[str]] = None letterOssId: Optional[str] = None sort: Optional[int] = None remark: Optional[str] = None class ContractProductItem(BaseModel): model_config = ConfigDict(populate_by_name=True) id: Optional[str] = None contractId: Optional[str] = None productId: Optional[str] = None productName: Optional[str] = None productNo: Optional[str] = None categoryId: Optional[str] = None categoryName: Optional[str] = None model: Optional[str] = None unit: Optional[str] = None quantity: float = Field(default=1, ge=0) quantityMode: Optional[str] = Field(default="fixed") price: float = Field(default=0, ge=0) discount: Optional[float] = Field(default=0) taxRate: Optional[float] = Field(default=6.0) taxAmount: Optional[float] = Field(default=0) subtotal: Optional[float] = None priceMode: Optional[str] = Field(default="fixed") deliveryDate: Optional[str] = None deliveryStatus: Optional[str] = None deliveredQuantity: Optional[float] = None description: Optional[str] = None packageSpec: Optional[str] = None deliveryMethod: Optional[str] = None sort: Optional[int] = None remark: Optional[str] = None brandMode: Optional[str] = None brandId: Optional[str] = None brandName: Optional[str] = None priceTiers: List[ContractProductPriceTierItem] = Field(default_factory=list) class ContractListQuery(BaseModel): """GET /contract/list 查询参数(对齐 CrmContractBo + pageNum/pageSize)""" model_config = ConfigDict(populate_by_name=True) pageNum: int = Field(default=1, ge=1) pageSize: int = Field(default=10, ge=1, le=500) # 即时检索:合同编号 / 合同产品名称 / 产品拼音首字母 keyword: Optional[str] = None contractNo: Optional[str] = None contractName: Optional[str] = None customerName: Optional[str] = None customerId: Optional[str] = None opportunityId: Optional[str] = None quoteId: Optional[str] = None contractType: Optional[str] = None status: Optional[str] = None excludeStatus: Optional[str] = None approvalStatus: Optional[str] = None ownerUserId: Optional[str] = None pendingApprovalUserId: Optional[str] = None approvedByUserId: Optional[str] = None minAmount: Optional[float] = None maxAmount: Optional[float] = None signDateBegin: Optional[str] = None signDateEnd: Optional[str] = None beginTime: Optional[str] = None endTime: Optional[str] = None class ContractCreateBody(BaseModel): model_config = ConfigDict(populate_by_name=True) contractNo: Optional[str] = None contractName: Optional[str] = None customerId: str customerName: Optional[str] = None opportunityId: Optional[str] = None quoteId: Optional[str] = None parentContractId: Optional[str] = None renewalCount: Optional[int] = None templateId: Optional[str] = None contractTemplate: Optional[str] = None contactId: Optional[str] = None contractType: str contractNature: Optional[str] = None amount: float = Field(default=0, ge=0) currency: Optional[str] = Field(default="CNY") taxRate: Optional[float] = Field(default=6.0) amountWithTax: Optional[float] = None freight: Optional[float] = Field(default=0, ge=0) barrelDeposit: Optional[float] = Field(default=0, ge=0) signDate: Optional[str] = None actualSignDate: Optional[str] = None startDate: Optional[str] = None endDate: Optional[str] = None contractPeriod: Optional[int] = None performanceStatus: Optional[str] = None performanceRate: Optional[float] = None customerSatisfaction: Optional[int] = None ownerUserId: Optional[str] = None partyA: Optional[str] = None partyAAddress: Optional[str] = None partyAContact: Optional[str] = None partyAMobile: Optional[str] = None partyALegalPerson: Optional[str] = None partyAAgent: Optional[str] = None partyAPhoneFax: Optional[str] = None partyABank: Optional[str] = None partyAAccount: Optional[str] = None supplierId: Optional[str] = None settlementMethod: Optional[str] = None partyB: Optional[str] = None partyBAddress: Optional[str] = None partyBContact: Optional[str] = None partyBMobile: Optional[str] = None partyBLegalPerson: Optional[str] = None partyBAgent: Optional[str] = None partyBPhoneFax: Optional[str] = None partyBBank: Optional[str] = None partyBAccount: Optional[str] = None paymentTerms: Optional[str] = None deliveryTerms: Optional[str] = None contractTerms: Optional[str] = None contractFile: Optional[str] = None attachmentFiles: Optional[str] = None templateContent: Optional[str] = None settlementClauseDrafts: Optional[str] = None invalidReason: Optional[str] = None remark: Optional[str] = None approverIds: List[str] = Field(default_factory=list) products: List[ContractProductItem] = Field(default_factory=list) class ContractUpdateBody(ContractCreateBody): id: str class ContractSubmitBody(BaseModel): id: str approverIds: List[str] = Field(default_factory=list) remark: Optional[str] = None class ContractBatchSubmitBody(BaseModel): ids: List[str] approverIds: List[str] = Field(default_factory=list) remark: Optional[str] = None class ContractWithdrawBody(BaseModel): id: str remark: Optional[str] = None class ContractStatusBody(BaseModel): id: str remark: Optional[str] = None class ContractInvalidBody(BaseModel): id: str invalidReason: str remark: Optional[str] = None class ContractTemplateSaveBody(BaseModel): id: str templateContent: str settlementClauseDrafts: Optional[str] = None class ContractDeleteBody(BaseModel): ids: List[str] class ContractApprovalActionBody(BaseModel): """POST /contract/approval — 按合同 ID 审批""" model_config = ConfigDict(populate_by_name=True) id: str approvalResult: str approvalOpinion: Optional[str] = None attachments: Optional[str] = None remark: Optional[str] = None class ContractUploadFileBody(BaseModel): id: str fileUrl: str class ContractUploadAttachmentsBody(BaseModel): model_config = ConfigDict(populate_by_name=True) id: str ossIds: Optional[str] = None attachments: Optional[List[dict]] = None class ContractExportBody(ContractListQuery): """POST /contract/export 筛选条件(无分页或沿用 list 参数)""" ids: Optional[List[str]] = Field(default=None, description="导出指定ID列表") columns: Optional[List[str]] = Field(default=None, description="导出列(前端 visibleColumns)") class ContractStatisticsOverviewQuery(BaseModel): ownerUserId: Optional[str] = None beginTime: Optional[str] = None endTime: Optional[str] = None class ContractStatisticsTrendQuery(BaseModel): type: Optional[str] = "month" beginTime: Optional[str] = None endTime: Optional[str] = None class ContractRenewBody(ContractCreateBody): """续签 — 字段同创建,parent 由路径提供""" class ContractApprovalPassBody(BaseModel): id: str approvalOpinion: Optional[str] = None class ContractApprovalRejectBody(BaseModel): id: str approvalOpinion: str class ContractApprovalRefreshNamesBody(BaseModel): contractId: Optional[str] = None class ContractApprovalListQuery(BaseModel): pageNum: int = Field(default=1, ge=1) pageSize: int = Field(default=10, ge=1, le=500) contractId: Optional[str] = None contractNo: Optional[str] = None contractName: Optional[str] = None approverName: Optional[str] = None approvalResult: Optional[str] = None beginTime: Optional[str] = None endTime: Optional[str] = None class ContractChangeListQuery(BaseModel): pageNum: int = Field(default=1, ge=1) pageSize: int = Field(default=10, ge=1, le=500) contractId: Optional[str] = None contractNo: Optional[str] = None contractName: Optional[str] = None changeType: Optional[str] = None approvalStatus: Optional[str] = None beginTime: Optional[str] = None endTime: Optional[str] = None class ContractTemplateListQuery(BaseModel): pageNum: int = Field(default=1, ge=1) pageSize: int = Field(default=10, ge=1, le=500) templateNo: Optional[str] = None templateName: Optional[str] = None templateType: Optional[str] = None isActive: Optional[int] = None beginTime: Optional[str] = None endTime: Optional[str] = None class ContractSignBody(BaseModel): id: str actualSignDate: str contractFile: str remark: Optional[str] = None class ContractBatchAssignBody(BaseModel): ids: List[str] ownerUserId: str remark: Optional[str] = None class ContractChangeCreateBody(BaseModel): model_config = ConfigDict(populate_by_name=True) contractId: str contractNo: Optional[str] = None changeType: str changeReason: str changeContent: Optional[str] = None oldValue: Optional[str] = None newValue: Optional[str] = None changeAmount: Optional[float] = None attachmentFiles: Optional[str] = None remark: Optional[str] = None class ContractChangeUpdateBody(ContractChangeCreateBody): id: str class ContractChangeSubmitBody(BaseModel): id: str approverIds: List[str] = Field(default_factory=list) remark: Optional[str] = None class ContractChangeExportBody(ContractChangeListQuery): ids: Optional[List[str]] = Field(default=None, description="导出指定ID列表") columns: Optional[List[str]] = Field(default=None, description="导出列(前端 visibleColumns)") class ContractTemplateCreateBody(BaseModel): model_config = ConfigDict(populate_by_name=True) templateNo: Optional[str] = None templateName: str contractType: Optional[str] = None templateContent: Optional[str] = None paymentTerms: Optional[str] = None deliveryTerms: Optional[str] = None contractTerms: Optional[str] = None partyB: Optional[str] = None partyBAddress: Optional[str] = None partyBContact: Optional[str] = None partyBMobile: Optional[str] = None defaultCurrency: Optional[str] = Field(default="CNY") defaultTaxRate: Optional[float] = Field(default=6.0) defaultPeriod: Optional[int] = Field(default=12) isActive: Optional[int] = Field(default=1) sort: Optional[int] = Field(default=0) remark: Optional[str] = None class ContractTemplateUpdateBody(ContractTemplateCreateBody): id: str class ContractTemplateExportBody(ContractTemplateListQuery): ids: Optional[List[str]] = Field(default=None, description="导出指定ID列表") columns: Optional[List[str]] = Field(default=None, description="导出列(前端 visibleColumns)") class ContractApprovalTransferBody(BaseModel): id: str targetApproverId: str transferReason: str class ContractApprovalExportBody(ContractApprovalListQuery): ids: Optional[List[str]] = Field(default=None, description="导出指定ID列表") columns: Optional[List[str]] = Field(default=None, description="导出列(前端 visibleColumns)")