PayrollManagementSystem/backend/app/schemas/payroll.py
liuwenxin 108bf87225 feat(核算)!: 添加 T+ 正式核算与采集托管
补齐源快照、资料预演、正式导入及工资核算的依赖链路。
新增版本化输入、逐段 FIFO、生成锁和结构化当月调整,
并提供默认关闭的周期采集、有限重试及 Windows 托管。

原始客户资料、私有配置及首页改动不纳入本次提交。

BREAKING CHANGE: 升级前需应用新增迁移,并通过
PAYROLL_MAKER_USER_ID 绑定实际工资制作人,否则业务只读。
旧单批次核算默认关闭;历史兼容需显式启用
PAYROLL_ALLOW_LEGACY_CALCULATION。生产启用和真实金额待验收。
2026-11-02 09:42:53 +08:00

60 lines
1.8 KiB
Python

"""正式工资核算的最小输入契约;金额使用字符串,资料版本显式校验。"""
from typing import Literal
from pydantic import BaseModel, ConfigDict, Field, field_validator
from app.core.month import validate_month
from app.schemas.tplus_workbench import HandoverFact, _decimal
class PayrollMonthIn(BaseModel):
month: str
reason: str | None = Field(default=None, min_length=1, max_length=255)
@field_validator("month")
@classmethod
def check_month(cls, value):
return validate_month(value)
class ShipmentOwnershipIn(BaseModel):
model_config = ConfigDict(extra="forbid")
shipment_item_id: int = Field(ge=1)
personnel_id: int = Field(ge=1)
manager_id: int | None = Field(default=None, ge=1)
unit_cost: str
@field_validator("unit_cost")
@classmethod
def check_cost(cls, value):
_decimal(value, 4)
return value
class PayrollSetupIn(PayrollMonthIn):
model_config = ConfigDict(extra="forbid")
expected_version: int = Field(default=0, ge=0)
evidence: str = Field(min_length=1, max_length=500)
opening_balance: str
history_confirmed: Literal[True]
guidance_finalized: Literal[True]
shipment_ownerships: list[ShipmentOwnershipIn] = Field(min_length=1, max_length=10000)
handovers: list[HandoverFact] = Field(default_factory=list, max_length=1000)
@field_validator("opening_balance")
@classmethod
def check_opening(cls, value):
_decimal(value, 2)
return value
class AllocationAdjustmentIn(BaseModel):
"""分配差额仅允许归生成当月;关联已结算原台账留痕。"""
ledger_id: int = Field(ge=1)
personnel_id: int = Field(ge=1)
direction: Literal[1, 2]
amount: str = Field(pattern=r"^\d{1,12}(\.\d{1,2})?$")
reason: str = Field(min_length=1, max_length=255)