打通 T+ 快照、业务、发货、回款和核算准备链路,保留来源差异并提供待补状态。 新增业务自动接入任务、跨平台读取服务托管、权限复核、来源关联和前端核对页面。 补充迁移、隔离集成测试、浏览器模拟验收及角色授权边界修复。
113 lines
4.7 KiB
Python
113 lines
4.7 KiB
Python
"""回款管理的请求/响应模型(需求 2.2.3)。
|
||
|
||
金额入参出参一律 str(服务层 Decimal,全程禁 float);字段契约**纯蛇形
|
||
直出**(2026-10-13 定稿,无驼峰 alias)。
|
||
|
||
payment_days 是派生值(发货日 → 回款日的天数,对照表 #1),不在写接口
|
||
入参内 —— 落库列只有 shipment_id 源数据,派生值实时计算,发货批次变更
|
||
后无需回写历史。
|
||
"""
|
||
|
||
from datetime import date, datetime
|
||
|
||
from pydantic import BaseModel, ConfigDict, Field, field_validator
|
||
|
||
from app.core.response import PageQuery
|
||
|
||
# 金额校验是工资系统的红线口径(数字/非负/容量/两位小数拒绝),跨 schema
|
||
# 单点复用 T7 定稿工具,禁止各处复制导致口径漂移;AMOUNT_MAX 即
|
||
# DECIMAL(14,2) 的容量上限,与回款列容量同源
|
||
from app.schemas.business import AMOUNT_MAX, _check_amount
|
||
|
||
|
||
class ReceiptCreateIn(BaseModel):
|
||
"""新增回款:业务必选,发货批次可暂不指定(手动指定,对照表 #18)。
|
||
|
||
shipment_id 指向的发货单必须与本回款同属一个业务(服务层强校验,
|
||
数据完整性红线);shipment_id 可空 = 暂未指定,指定后回款天数方可派生。
|
||
"""
|
||
|
||
model_config = ConfigDict(str_strip_whitespace=True)
|
||
|
||
business_id: int = Field(ge=1, description="业务ID")
|
||
shipment_id: int | None = Field(default=None, ge=1, description="发货单ID,可空=暂未指定")
|
||
receipt_date: date = Field(description="回款到账日(M3 按此日匹配参数版本)")
|
||
amount: str = Field(description="回款金额(元),≥0,最多两位小数")
|
||
is_barter: int = Field(default=0, ge=0, le=1, description="以物抵顶标记:1是 0否")
|
||
remark: str | None = Field(default=None, max_length=255, description="备注")
|
||
|
||
@field_validator("amount")
|
||
@classmethod
|
||
def _check_amount_value(cls, value: str) -> str:
|
||
return _check_amount(value, "回款金额", AMOUNT_MAX) or value
|
||
|
||
|
||
class ReceiptUpdateIn(BaseModel):
|
||
"""编辑回款(全量替换语义)。
|
||
|
||
shipment_id 传 None = 清除指定(回到「暂未指定」);amount 是必填项,
|
||
全量替换下不存在「不传即不变」的歧义。已核算(settlement_status=1)
|
||
的回款在服务层禁改,schema 层不感知该状态。
|
||
"""
|
||
|
||
model_config = ConfigDict(str_strip_whitespace=True)
|
||
|
||
shipment_id: int | None = Field(
|
||
default=None, ge=1, description="发货单ID,可空=暂未指定(传空清除既有指定)"
|
||
)
|
||
receipt_date: date = Field(description="回款到账日")
|
||
amount: str = Field(description="回款金额(元),≥0,最多两位小数")
|
||
is_barter: int = Field(default=0, ge=0, le=1, description="以物抵顶标记:1是 0否")
|
||
remark: str | None = Field(default=None, max_length=255, description="备注")
|
||
|
||
@field_validator("amount")
|
||
@classmethod
|
||
def _check_amount_value(cls, value: str) -> str:
|
||
return _check_amount(value, "回款金额", AMOUNT_MAX) or value
|
||
|
||
|
||
class ReceiptOut(BaseModel):
|
||
"""回款响应(列表与写操作回包共用)。
|
||
|
||
business_no/shipment_no 为关联冗余展示;payment_days 为派生值:
|
||
未指定发货批次时为 None,指定后 = 发货日到回款日的天数(发货日首日
|
||
计第 1 天)。
|
||
"""
|
||
|
||
model_config = ConfigDict(from_attributes=True)
|
||
|
||
id: int
|
||
receipt_batch_no: str
|
||
business_id: int
|
||
business_no: str
|
||
shipment_id: int | None = None
|
||
shipment_no: str | None = None
|
||
receipt_date: date
|
||
amount: str
|
||
is_barter: int
|
||
settlement_status: int
|
||
confirmation_status: int = 0
|
||
confirmed_by: str | None = None
|
||
confirmed_at: datetime | None = None
|
||
confirmation_revision: str = ""
|
||
payment_days: int | None = None
|
||
remark: str | None = None
|
||
|
||
|
||
class ReceiptQuery(PageQuery):
|
||
"""回款列表查询条件(筛选参数纯蛇形直出,同 BusinessQuery 契约)。"""
|
||
|
||
business_id: int | None = Field(default=None, ge=1, description="业务筛选")
|
||
date_from: date | None = Field(default=None, description="回款到账日起(含)")
|
||
date_to: date | None = Field(default=None, description="回款到账日止(含)")
|
||
is_barter: int | None = Field(default=None, ge=0, le=1, description="以物抵顶筛选:1是 0否")
|
||
settlement_status: int | None = Field(
|
||
default=None, ge=0, le=1, description="核算状态筛选:0未核算 1已核算"
|
||
)
|
||
confirmation_status: int | None = Field(default=None, ge=0, le=1, description="人工确认状态筛选")
|
||
|
||
|
||
class ReceiptConfirmIn(BaseModel):
|
||
model_config = ConfigDict(extra="forbid")
|
||
expected_revision: str = Field(pattern=r"^[0-9a-f]{64}$", description="页面核对的回款关键内容指纹")
|