PayrollManagementSystem/backend/tests/unit/test_approval_helpers.py
liuwenxin 4c32a556de feat(approval): 审批单内容快照与二次分配方案版本化
补齐 code-review 遗留 2 项:审批单此前只固化 ref_id/refer_no 指针,复核者看到
的是业务当前内容;二次分配方案在编辑替换与驳回清空时一律物理删除,方案事实
无处回溯。

- 审批单加 version_no 与 snapshot_json(信封 schema_version/order_type/
  captured_at/data);二次分配、月结、调整单三类提交均固化送审内容,开关关闭
  的自动通过路径同样写快照;详情按 snapshot/live/missing 三态回显,历史单
  只读回退当前内容并显式告警,不回填历史快照
- 二次分配方案行改 append-only 版本化(version_no/is_active,唯一键扩为
  business+version+personnel):编辑替换与驳回清空只置历史,核算与详情默认
  只取生效行,业务详情新增 allocation_history
- 送审内容未变不重提、方案未变不新增版本:判据统一为送审内容,两个
  version_no 语义解耦(方案版本=内容第几版,审批单版本=第几次送审)
- 审批单列表、工作台待办、审批超时预警三处 defer 快照列,避免整列拉取
- 月结提交取行加 for_update,防并发提交算出重复的审批单版本号
- 修复审批类型筛选上界 le=2(调整单无法筛选)、快照金额把空值写成 0.00
- V11 迁移三件套同步(Alembic / db/migration / init-dev.sql)

测试:单元 +16(方案内容比较 10、审批入参 6)、集成 +12;全量 891 passed。

Co-Authored-By: Claude Code <noreply@anthropic.com>
2026-10-20 18:30:51 +08:00

73 lines
2.9 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"""审批与方案版本化的纯函数单元测试(秒级,无需容器)。
守的是「方案内容是否真的变了」这一判定(``business_service._plan_changed``):
它决定要不要新增一个方案版本行、要不要重提审批单。判错的后果是双向的——
判成「变了」会刷出版本与审批留痕噪音,判成「没变」会让改过的方案
沿用旧版本号,版本语义失真。
比较口径是**集合**(人员 ID + 其比例),与提交顺序无关:用户只是把行
拖了个序,不该被算成方案变了。
"""
from decimal import Decimal
import pytest
from app.models.business import SecondaryAllocation
from app.services.business_service import _plan_changed
def _row(personnel_id: int, ratio: str) -> SecondaryAllocation:
"""构造一个未入库的方案行(ORM 实例化不触数据库)。"""
return SecondaryAllocation(
business_id=1,
version_no=1,
personnel_id=personnel_id,
personnel_name=f"t_人员{personnel_id}",
ratio=Decimal(ratio),
is_active=1,
)
class TestPlanChanged:
"""方案内容比较。"""
def test_完全相同_判未变(self) -> None:
old = [_row(1, "50.00"), _row(2, "30.00")]
new = [_row(1, "50.00"), _row(2, "30.00")]
assert _plan_changed(old, new) is False
def test_顺序不同_判未变(self) -> None:
"""用户只是把行拖了个序 —— 送审快照按库内生效行顺序生成,
按顺序比较会把这种编辑误判成「方案变了」。"""
old = [_row(1, "50.00"), _row(2, "30.00")]
new = [_row(2, "30.00"), _row(1, "50.00")]
assert _plan_changed(old, new) is False
def test_比例变化_判已变(self) -> None:
old = [_row(1, "50.00")]
new = [_row(1, "80.00")]
assert _plan_changed(old, new) is True
def test_参与人增减_判已变(self) -> None:
old = [_row(1, "50.00")]
assert _plan_changed(old, [_row(1, "50.00"), _row(2, "30.00")]) is True
assert _plan_changed([_row(1, "50.00"), _row(2, "30.00")], [_row(1, "50.00")]) is True
def test_同一人的比例从一人换成另一人_判已变(self) -> None:
"""参与人不同但比例相同:人员 ID 是比较键的一部分,不能只看比例。"""
old = [_row(1, "50.00")]
new = [_row(2, "50.00")]
assert _plan_changed(old, new) is True
@pytest.mark.parametrize("written", ["50", "50.0", "50.00"])
def test_比例写法不同_判未变(self, written: str) -> None:
"""比例入参是字符串('50' 与 '50.00' 都合法),数值相同即未变。"""
assert _plan_changed([_row(1, "50.00")], [_row(1, written)]) is False
def test_空方案与空方案_判未变(self) -> None:
assert _plan_changed([], []) is False
def test_空方案与有方案_判已变(self) -> None:
assert _plan_changed([], [_row(1, "50.00")]) is True