269 lines
7.6 KiB
Python
269 lines
7.6 KiB
Python
"""CRM 报价单 Pydantic Schemas"""
|
||
|
||
from typing import List, Optional
|
||
|
||
from pydantic import BaseModel, ConfigDict, Field
|
||
|
||
|
||
class QuoteProductItem(BaseModel):
|
||
model_config = ConfigDict(populate_by_name=True)
|
||
|
||
id: 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, gt=0)
|
||
price: float = Field(default=0, ge=0)
|
||
discount: Optional[float] = Field(default=0)
|
||
taxRate: Optional[float] = Field(default=13.0)
|
||
taxAmount: Optional[float] = Field(default=0)
|
||
subtotal: Optional[float] = None
|
||
packageSpec: Optional[str] = None
|
||
deliveryMethod: Optional[str] = None
|
||
description: Optional[str] = None
|
||
sort: Optional[int] = None
|
||
remark: Optional[str] = None
|
||
brandMode: Optional[str] = None
|
||
brandId: Optional[str] = None
|
||
brandName: Optional[str] = None
|
||
|
||
|
||
class QuoteColumnFilterMixin(BaseModel):
|
||
"""列头漏斗参数:前端 camelCase ↔ 后端 snake_case,供 build_quote_column_filters。"""
|
||
|
||
model_config = ConfigDict(populate_by_name=True)
|
||
|
||
quote_no: Optional[str] = Field(default=None, alias="quoteNo", description="报价单号")
|
||
customer_name: Optional[str] = Field(default=None, alias="customerName", description="客户名称")
|
||
quote_no_in: Optional[str] = Field(default=None, alias="quoteNoIn", description="报价单号多选(逗号)")
|
||
customer_name_in: Optional[str] = Field(
|
||
default=None, alias="customerNameIn", description="客户名称多选(逗号)"
|
||
)
|
||
|
||
|
||
class QuoteListBody(QuoteColumnFilterMixin):
|
||
"""列表/导出查询;extra=allow 接收列头漏斗动态参数(*In / *From|To / *Op|Min|Max 等)。"""
|
||
|
||
model_config = ConfigDict(populate_by_name=True, extra="allow")
|
||
|
||
page: int = Field(default=1, ge=1)
|
||
pageSize: int = Field(default=10, ge=1, le=500)
|
||
keyword: Optional[str] = None
|
||
quoteTitle: Optional[str] = None
|
||
customerId: Optional[str] = None
|
||
opportunityId: Optional[str] = None
|
||
status: Optional[str] = None
|
||
excludeStatus: Optional[str] = None
|
||
quoteType: Optional[str] = None
|
||
ownerUserId: Optional[str] = None
|
||
isLatest: Optional[int] = None
|
||
minAmount: Optional[float] = None
|
||
maxAmount: Optional[float] = None
|
||
quoteDateBegin: Optional[str] = None
|
||
quoteDateEnd: Optional[str] = None
|
||
beginTime: Optional[str] = None
|
||
endTime: Optional[str] = None
|
||
approvedByUserId: Optional[str] = None
|
||
pendingApprovalUserId: Optional[str] = None
|
||
sortField: Optional[str] = None
|
||
sortOrder: Optional[str] = None
|
||
|
||
ids: Optional[List[str]] = Field(default=None, description="导出指定ID列表")
|
||
columns: Optional[List[str]] = Field(default=None, description="导出列(前端 visibleColumns)")
|
||
|
||
|
||
class QuoteDetailBody(BaseModel):
|
||
id: str
|
||
|
||
|
||
class QuoteDeleteBody(BaseModel):
|
||
ids: List[str]
|
||
|
||
|
||
class QuoteCreateBody(BaseModel):
|
||
model_config = ConfigDict(populate_by_name=True)
|
||
|
||
quoteNo: Optional[str] = None
|
||
quoteTitle: Optional[str] = None
|
||
quoteType: Optional[str] = Field(default="sales")
|
||
customerId: str
|
||
customerName: Optional[str] = None
|
||
opportunityId: Optional[str] = None
|
||
contactId: Optional[str] = None
|
||
version: Optional[str] = None
|
||
parentId: Optional[str] = None
|
||
isLatest: Optional[int] = None
|
||
totalAmount: Optional[float] = None
|
||
discountRate: Optional[float] = Field(default=0)
|
||
discountAmount: Optional[float] = None
|
||
finalAmount: Optional[float] = None
|
||
validityDays: Optional[int] = Field(default=30)
|
||
quoteDate: Optional[str] = None
|
||
expireDate: Optional[str] = None
|
||
status: Optional[str] = Field(default="draft")
|
||
ownerUserId: Optional[str] = None
|
||
supplierId: Optional[str] = None
|
||
supplierName: Optional[str] = None
|
||
paymentTerms: Optional[str] = None
|
||
deliveryTerms: Optional[str] = None
|
||
freight: Optional[float] = Field(default=0)
|
||
barrelDeposit: Optional[float] = Field(default=0)
|
||
attachments: Optional[str] = None
|
||
templateContent: Optional[str] = None
|
||
remark: Optional[str] = None
|
||
products: List[QuoteProductItem] = Field(default_factory=list)
|
||
|
||
|
||
class QuoteUpdateBody(QuoteCreateBody):
|
||
id: str
|
||
products: Optional[List[QuoteProductItem]] = None
|
||
|
||
|
||
class QuoteSubmitBody(BaseModel):
|
||
id: str
|
||
approverIds: List[str] = Field(default_factory=list)
|
||
remark: Optional[str] = None
|
||
|
||
|
||
class QuoteTemplateSaveBody(BaseModel):
|
||
id: str
|
||
templateContent: str
|
||
|
||
|
||
class QuoteTemplateGetBody(BaseModel):
|
||
id: str
|
||
|
||
|
||
class QuotePdfBody(BaseModel):
|
||
id: str
|
||
|
||
|
||
class QuoteShareGenerateBody(BaseModel):
|
||
id: str
|
||
validityDays: Optional[int] = None
|
||
password: Optional[str] = None
|
||
|
||
|
||
class QuoteAcceptBody(BaseModel):
|
||
id: str
|
||
remark: Optional[str] = None
|
||
|
||
|
||
class QuoteRejectBody(BaseModel):
|
||
id: str
|
||
rejectReason: Optional[str] = None
|
||
remark: Optional[str] = None
|
||
|
||
|
||
class QuoteToContractBody(BaseModel):
|
||
id: str
|
||
contractNo: Optional[str] = None
|
||
contractTitle: Optional[str] = None
|
||
signDate: Optional[str] = None
|
||
startDate: Optional[str] = None
|
||
endDate: Optional[str] = None
|
||
|
||
|
||
class QuoteNewVersionBody(BaseModel):
|
||
id: str
|
||
version: str
|
||
remark: Optional[str] = None
|
||
|
||
|
||
class QuoteVersionsBody(BaseModel):
|
||
quoteNo: str
|
||
|
||
|
||
class QuoteInvalidBody(BaseModel):
|
||
id: str
|
||
invalidReason: Optional[str] = None
|
||
remark: Optional[str] = None
|
||
|
||
|
||
class QuoteStatisticsOverviewBody(BaseModel):
|
||
ownerUserId: Optional[str] = None
|
||
beginTime: Optional[str] = None
|
||
endTime: Optional[str] = None
|
||
|
||
|
||
class QuoteStatisticsTrendBody(BaseModel):
|
||
type: Optional[str] = Field(default="month")
|
||
beginTime: Optional[str] = None
|
||
endTime: Optional[str] = None
|
||
|
||
|
||
class QuoteProductListBody(BaseModel):
|
||
quoteId: str
|
||
|
||
|
||
class QuoteProductCreateBody(BaseModel):
|
||
model_config = ConfigDict(populate_by_name=True)
|
||
|
||
quoteId: str
|
||
productId: Optional[str] = None
|
||
productName: Optional[str] = None
|
||
quantity: float = Field(default=1, gt=0)
|
||
price: float = Field(default=0, ge=0)
|
||
discount: Optional[float] = 0
|
||
taxRate: Optional[float] = 13.0
|
||
model: Optional[str] = None
|
||
unit: Optional[str] = None
|
||
categoryId: Optional[str] = None
|
||
categoryName: Optional[str] = None
|
||
description: Optional[str] = None
|
||
sort: Optional[int] = None
|
||
remark: Optional[str] = None
|
||
brandMode: Optional[str] = None
|
||
brandId: Optional[str] = None
|
||
brandName: Optional[str] = None
|
||
|
||
|
||
class QuoteProductUpdateBody(QuoteProductCreateBody):
|
||
id: str
|
||
|
||
|
||
class QuoteProductDeleteBody(BaseModel):
|
||
ids: List[str]
|
||
|
||
|
||
class QuoteApprovalListBody(BaseModel):
|
||
quoteId: str
|
||
|
||
|
||
class QuoteApprovalApproveBody(BaseModel):
|
||
id: str
|
||
approvalResult: str
|
||
approvalOpinion: Optional[str] = None
|
||
remark: Optional[str] = None
|
||
|
||
|
||
class QuoteApprovalWithdrawBody(BaseModel):
|
||
quoteId: str
|
||
remark: Optional[str] = None
|
||
|
||
|
||
class QuoteApprovalRefreshNamesBody(BaseModel):
|
||
quoteId: Optional[str] = None
|
||
|
||
|
||
class QuoteShareLogListBody(BaseModel):
|
||
quoteId: str
|
||
|
||
|
||
class QuoteShareLogStatisticsBody(BaseModel):
|
||
quoteId: str
|
||
|
||
|
||
class QuoteColumnFacetBody(QuoteColumnFilterMixin):
|
||
"""报价列表列头可选值(去重;可附带列表上下文)"""
|
||
|
||
model_config = ConfigDict(populate_by_name=True, extra="allow")
|
||
|
||
field: str = Field(..., description="quoteNo | customerName")
|
||
keyword: Optional[str] = Field(default=None, description="列表搜索关键词")
|
||
limit: Optional[int] = Field(default=500, ge=1, le=500, description="返回条数上限")
|