"""回款管理的请求/响应模型(需求 2.2.3)。 金额入参出参一律 str(服务层 Decimal,全程禁 float);字段契约**纯蛇形 直出**(2026-10-13 定稿,无驼峰 alias)。 payment_days 是派生值(发货日 → 回款日的天数,对照表 #1),不在写接口 入参内 —— 落库列只有 shipment_id 源数据,派生值实时计算,发货批次变更 后无需回写历史。 """ from datetime import date 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 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已核算" )