PayrollManagementSystem/backend/app/schemas/business_source.py
liuwenxin b8920c0d28 fix(T+): 补齐指导成本与读取服务保护
补充业务详情指导成本入口、来源版本和历史封存保护,接入月份锁并完善权限复核。
同步接入读取服务运行状态到页面,补充前端控制、回归测试和服务端只读连接授权校验。

定向隔离测试:89 项通过,1 条既有 Starlette 弃用警告。
2026-11-05 19:32:20 +08:00

110 lines
4.0 KiB
Python

"""来源业务的人工分类资料。"""
from decimal import Decimal, InvalidOperation
from typing import Literal
from pydantic import BaseModel, ConfigDict, Field, model_validator
from app.core.response import PageQuery
class SalesRelationQuery(PageQuery):
shipment_page: int = Field(default=1, ge=1, le=10000)
order_page: int = Field(default=1, ge=1, le=10000)
class SourceDocumentsQuery(PageQuery):
entity: str | None = Field(default=None, max_length=32)
class SourcePriceConfirmIn(BaseModel):
model_config = ConfigDict(extra="forbid")
revision: str = Field(pattern=r"^[0-9a-f]{64}$")
evidence: str = Field(min_length=1, max_length=500)
@model_validator(mode="after")
def validate_evidence(self):
self.evidence = self.evidence.strip()
if not self.evidence:
raise ValueError("核对依据不能为空")
return self
class GuidanceConfirmIn(BaseModel):
"""从业务发货明细登记对应发货月份的单位指导成本。"""
model_config = ConfigDict(extra="forbid")
shipment_item_id: int = Field(ge=1)
amount: str = Field(min_length=1, max_length=32)
evidence: str = Field(min_length=1, max_length=500)
@model_validator(mode="after")
def validate_values(self):
self.evidence = self.evidence.strip()
if not self.evidence:
raise ValueError("核对依据不能为空")
try:
value = Decimal(self.amount)
except (InvalidOperation, TypeError, ValueError):
raise ValueError("指导成本必须为有效数字") from None
if not value.is_finite() or value < 0 or value.as_tuple().exponent < -4:
raise ValueError("指导成本最多保留4位小数且不能为负数")
return self
class SourceProductCategory(BaseModel):
product_id: int = Field(ge=1)
category: Literal["municipal_water", "rail_transit"]
class SourceMaterialsIn(BaseModel):
model_config = ConfigDict(extra="forbid")
customer_type: Literal["personal", "manager_personal", "enterprise"] | None = None
products: list[SourceProductCategory] = Field(default_factory=list, max_length=200)
@model_validator(mode="after")
def unique_products(self):
if len({p.product_id for p in self.products}) != len(self.products):
raise ValueError("产品不能重复")
if self.customer_type is None and not self.products:
raise ValueError("请选择要补充的分类")
return self
class SalesRelationConfirmIn(BaseModel):
model_config = ConfigDict(extra="forbid")
item_id: int = Field(ge=1)
shipment_document_id: int = Field(ge=1)
order_document_id: int | None = Field(default=None, ge=1)
disposition: Literal["ORDER", "INDEPENDENT"]
item_revision: str = Field(pattern=r"^[0-9a-f]{64}$")
shipment_revision: str = Field(pattern=r"^[0-9a-f]{64}$")
order_revision: str | None = Field(default=None, pattern=r"^[0-9a-f]{64}$")
expected_version: int = Field(default=0, ge=0)
evidence: str = Field(min_length=1, max_length=500)
@model_validator(mode="after")
def validate_relation(self):
self.evidence = self.evidence.strip()
if not self.evidence:
raise ValueError("确认依据不能为空")
if self.disposition == "ORDER" and (self.order_document_id is None or self.order_revision is None):
raise ValueError("请选择并核对订单明细")
if self.disposition == "INDEPENDENT" and (self.order_document_id is not None or self.order_revision is not None):
raise ValueError("独立发货不应指定订单明细")
return self
class SalesRelationReleaseIn(BaseModel):
model_config = ConfigDict(extra="forbid")
item_id: int = Field(ge=1)
expected_version: int = Field(ge=1)
evidence: str = Field(min_length=1, max_length=500)
@model_validator(mode="after")
def validate_evidence(self):
self.evidence = self.evidence.strip()
if not self.evidence:
raise ValueError("撤销依据不能为空")
return self