test(business): 业务数据管理模块集成用例(70 例)与两处缺陷修复

- 覆盖:编号格式与序号递增/状态机终态拦截/三处汇总冗余一致性/跨业务批次
  400/已核算禁改禁删/毛利润金标准与防除零/asOf 回溯/评级重叠/审计双记录
- 缺陷修复①:change_status 留痕 old_status 取到赋值后属性(时间线失真 3→3)
- 缺陷修复②:freight_amount 漏金额校验(负数/三位小数可落库污染运费合计)
- conftest 扩展业务六表清理(编号前缀 + FK 逆序收口)与评级种子基线恢复
- 全量回归 603 passed(单元 381 / 集成 222)

Co-Authored-By: Claude Code <noreply@anthropic.com>
This commit is contained in:
liuwenxinandClaude Code committed 2026-10-13 13:39:09 +08:00
1 parent 77e578085a
commit ca4cc72eea
8 files changed
+2017 -5

No files matched your search

+61
View File
@@ -12,9 +12,15 @@
消失,必须显式清理,否则残留会影响后续用例对留痕条数的断言
- 操作日志的清理同时按 username 与 user_id —— 账号被删除后其 ``user_id``
会被 SET NULL,只能靠冗余的 username 追溯,清理逻辑必须覆盖两种状态
- M2 业务数据(业务/发货/回款)单据编号由系统生成(YW/SH/HK 前缀 + 当日
序号),**没有 t_ 前缀可用**,兜底清理按编号前缀全量清(三类单据表目前
只有测试会写);外键均为 RESTRICT,删除顺序不能倒,且必须在客户/人员
清理之前(FK 会挡住删客户/删人员);评级判定表则恢复种子基线 ——
评级用例会删改种子构造重叠/相邻场景,恢复保证跨文件幂等
"""
import uuid
from decimal import Decimal
import pytest
from sqlalchemy import delete, select, update
@@ -23,18 +29,35 @@ from app.core.constants import ROLE_ADMIN, ROLE_USER
from app.core.db import get_session_factory
from app.core.security import create_access_token, hash_password
from app.models import (
Business,
BusinessItem,
BusinessRole,
BusinessStatusLog,
Customer,
CustomerRatingGrade,
OperationLog,
OwnershipChangeLog,
Personnel,
PersonnelPlan,
PersonnelRegion,
Product,
Receipt,
Role,
Shipment,
ShipmentItem,
User,
UserRole,
)
from app.models.customer import OWNER_TYPE_BUSINESS
# 评级判定表的种子基线(与 db/migration/V5 一致):判定按 sort_order 升序
# 取首个 min_score ≤ 总分的等级,种子铺满 0-100
_RATING_SEEDS = [
("A", Decimal("90"), Decimal("100"), 1, "优秀"),
("B", Decimal("75"), Decimal("89"), 2, "良好"),
("C", Decimal("60"), Decimal("74"), 3, "合格"),
("D", Decimal("0"), Decimal("59"), 4, "待改进"),
]
TEST_PASSWORD = "TestPwd@2026"
# 模块级预计算一次:bcrypt(rounds=12)单次约 0.2–0.4 秒,逐用例重复哈希
@@ -69,6 +92,44 @@ def _purge_test_data() -> None:
session.execute(delete(UserRole).where(UserRole.role_id.in_(role_ids)))
session.execute(delete(Role).where(Role.id.in_(role_ids)))
# ---- M2 业务数据(T11):业务/发货/回款三类单据 ----
# 编号系统生成、无 t_ 前缀可用,按业务编号前缀全量清(三类单据表目前
# 只有测试会写)。外键均为 RESTRICT,删除顺序不能倒:回款 → 发货
# 明细 → 发货单 → 业务明细 → 状态留痕 → 业务维度归属留痕(无外键,
# 须显式清)→ 业务主表;且必须在下方客户/人员清理之前,否则 FK 挡住
# 删客户/删人员(businesses.customer_id / .owner_personnel_id RESTRICT)
business_ids = list(
session.execute(select(Business.id).where(Business.business_no.like("YW%"))).scalars()
)
shipment_ids = list(
session.execute(select(Shipment.id).where(Shipment.shipment_no.like("SH%"))).scalars()
)
if business_ids:
session.execute(delete(Receipt).where(Receipt.business_id.in_(business_ids)))
if shipment_ids:
session.execute(delete(ShipmentItem).where(ShipmentItem.shipment_id.in_(shipment_ids)))
session.execute(delete(Shipment).where(Shipment.id.in_(shipment_ids)))
if business_ids:
session.execute(delete(BusinessItem).where(BusinessItem.business_id.in_(business_ids)))
session.execute(
delete(BusinessStatusLog).where(BusinessStatusLog.business_id.in_(business_ids))
)
session.execute(
delete(OwnershipChangeLog).where(
OwnershipChangeLog.owner_type == OWNER_TYPE_BUSINESS,
OwnershipChangeLog.business_id.in_(business_ids),
)
)
session.execute(delete(Business).where(Business.id.in_(business_ids)))
# 评级判定表恢复种子基线:评级用例会删改种子构造重叠/相邻场景,
# 兜底恢复保证每个用例开始时都是纯种子(判定表无下游引用,全删全插安全)
session.execute(delete(CustomerRatingGrade))
session.add_all(
CustomerRatingGrade(grade_name=n, min_score=lo, max_score=hi, sort_order=so, remark=rm)
for n, lo, hi, so, rm in _RATING_SEEDS
)
# ---- M2 基础数据:客户/归属留痕/人员/产品/测试岗位 ----
# 外键均为 RESTRICT,删除顺序不能倒:留痕无外键先按 customer_id 清,
# 再删客户(子表)→ 人员(先置空 manager_id 自引用、清子表)→ 产品