first commit

This commit is contained in:
strepsiades
2026-03-09 16:31:42 +08:00
commit 4a9c444b10
486 changed files with 52479 additions and 0 deletions
@@ -0,0 +1,101 @@
from pydantic import Field, BaseModel,field_validator,model_validator
from fastapi import HTTPException
from typing import List, Optional, Literal
from ..common.validators import DateStr
from ..common.base import BaseResponseWithID
class PlanNode(BaseModel):
date: DateStr = Field(..., description="计划节点时间", examples=["2024-01-01"])
quantity: float = Field(..., description="计划完成量", examples=[100.0])
is_audit: Optional[bool] = Field(False, description="是否已审核", examples=[False])
@field_validator("quantity")
def validate_quantity(cls, v):
"""验证计划完成量必须大于0"""
if v < 0:
raise HTTPException(status_code=400, detail='计划完成量不能小于0')
return v
class ContractSettlementResponse(BaseResponseWithID):
"""
合同结算返回
"""
project_id: str = Field(..., description="所属项目ID", examples=["project123"])
contract_id: str = Field(..., description="所属合同ID", examples=["contract456"])
contract_type: Optional[str] = Field(None, description="合同类型")
show_name: Optional[str] = Field(None, description="合同简称", examples=["主变采购合同"])
complete_price: float = Field(0.0, description="实际完成金额", examples=[100000.0])
contract_price: float = Field(0.0, description="合同金额", examples=[120000.0])
actual_complete_date: Optional[DateStr] = Field(None, description="实际结束时间", examples=["2024-06-30"])
actual_start_date: Optional[DateStr] = Field(None, description="实际开始时间", examples=["2024-01-01"])
plan_complete_date: Optional[DateStr] = Field(None, description="计划结束时间", examples=["2024-06-30"])
plan_start_date: Optional[DateStr] = Field(None, description="计划开始时间", examples=["2024-01-01"])
plan_node: List[PlanNode] = Field(default_factory=list, description="计划节点列表")
status: Optional[int] = Field(None,description="供货状态", examples=[0])
# 2025-10-20新增字段,延迟时间
delay_days: Optional[int] = Field(None, description="延迟时间(天)", examples=[0])
statistic_date: Optional[DateStr] = Field(
None, description="统计节点截止日期", examples=["2024-05-31"]
)
class ContractSettlementCreate(BaseModel):
"""
合同结算创建模型
CONTRACT_SETTLEMENT_BASE_INFO = "contract_settlement.base_info"
"""
project_id: str = Field(..., description="所属项目ID", examples=["project123"])
contract_id: str = Field(..., description="所属合同ID", examples=["contract456"])
show_name: Optional[str] = Field(None, description="合同简称", examples=["主变采购合同"])
class ContractSettlementUpdate(BaseModel):
"""
合同结算更新模型
CONTRACT_SETTLEMENT_BASE_INFO = "contract_settlement.base_info"
"""
id: str = Field(..., description="合同结算ID")
contract_id: str = Field(..., description="所属合同ID", examples=["contract456"])
show_name: Optional[str] = Field(None, description="合同简称", examples=["主变采购合同"])
class ContractSettlementPlanUpdate(BaseModel):
"""
合同结算计划节点更新模型
CONTRACT_SETTLEMENT_PLAN_NODE = "contract_settlement.plan_node"
开始时间和金额在合同中维护,这里只维护计划节点和计划结束时间
业务逻辑说明:
- API要求计划节点时间必须晚于计划开始时间(不能等于)
- 自动清理脏数据:如果 plan_node 有2个或以上节点,删除边界节点
"""
id: str = Field(..., description="合同结算ID")
plan_start_date: Optional[DateStr] = Field(None, exclude=True, description="计划开始时间(不推往后端,仅用于本地首节点清洗)", examples=["2024-06-30"])
plan_complete_date: Optional[DateStr] = Field(None, description="计划结束时间", examples=["2024-06-30"])
plan_node: List[PlanNode] = Field(default_factory=list, description="计划节点列表")
@model_validator(mode='after')
def clean_plan_nodes(self):
"""
清理计划节点列表中的边界节点
注:settlement的plan_start_date在contract中维护,此处利用外部提供的辅助字段清除非法首尾节点。
"""
if not self.plan_node or len(self.plan_node) < 2:
return self
cleaned_nodes = list(self.plan_node)
# 删除开头的边界节点
if (self.plan_start_date and
cleaned_nodes and
cleaned_nodes[0].date == self.plan_start_date):
cleaned_nodes.pop(0)
# 删除结尾的边界节点
if (self.plan_complete_date and
cleaned_nodes and
cleaned_nodes[-1].date == self.plan_complete_date):
cleaned_nodes.pop()
self.plan_node = cleaned_nodes
return self