test(rush): 锁定试排世界急单重排走柔性引擎的回归用例

夹具还原「文件夹快试导入」世界形态(主干种子 + trialOnly + 最小柔性
主数据/班组),断言:急单桥接进 flexOrders(SALES/RELEASED)、生成 FV
草稿版本、急单真实排出虚拟产线、主轨不再新增空壳版本。
已做反向验证:临时禁用 is_trial_world 分支该用例必失败。
This commit is contained in:
z.zhang 2026-10-12 09:49:01 +08:00
parent 381bc03434
commit 724f3ebc91
1 changed files with 82 additions and 0 deletions

View File

@ -109,3 +109,85 @@ def test_rush_evaluate_existing_order():
assert not target.get("isRush") # 主干未改
assert impact["baseline"]["orderCount"] == 7
assert impact["after"]["orderCount"] == 7
def _trial_flex_world() -> dict:
"""试排世界夹具:主干种子数据 + planningContext.trialOnly + 最小柔性主数据。
模拟「文件夹快试导入」后的真实形态:排产只认 flex* 主数据,
主干 RULE 引擎在此类世界上只能产出 0 工单的空壳版本。
"""
world = seed_world()
world["planningContext"] = {"trialOnly": True, "dataDate": "2026-08-04"}
world["flexOperations"] = [{"id": 1, "code": "CUT", "name": "下料", "changeoverMin": 10}]
world["flexTeams"] = [{
"id": 1, "code": "TEAM-CUT", "name": "下料班", "memberCount": 4,
"supportOps": ["CUT"],
}]
world["flexRoutings"] = [{
"id": 1, "productCode": "CTRL-A", "productName": "控制柜 A", "seq": 1,
"operationCode": "CUT", "operationName": "下料", "stdTimePerUnit": 5.0,
}]
world["flexMaterials"] = [{
"id": 1, "code": "CTRL-A", "name": "控制柜 A", "type": "FINISHED_PRODUCT",
"unit": "件", "stock": 0, "inTransit": 0, "safetyStock": 0,
"procurementLeadTime": 0, "sourcingType": "MAKE",
}]
world["flexEquipment"] = [{
"id": 1, "code": "EQ-1", "name": "切割机", "status": "RUNNING",
"capabilities": ["CUT"], "availabilityRate": 1.0,
}]
return world
def test_rush_apply_in_trial_world_routes_to_flex_engine():
"""试排世界采用急单必须走柔性引擎(回归:急单入池却排不进/看不见)。
锁定三点:急单桥接进 flexOrders(source=SALES/RELEASED);生成 FV 柔性
草稿版本且急单真实排出虚拟产线;主轨 scheduleVersions 不再新增空壳版本。
"""
world = _trial_flex_world()
n_trunk_versions = len(world["scheduleVersions"])
n_flex_versions = len(world.get("flexScheduleVersions") or [])
applied = apply_rush(world, _next_id_factory(world), {
"payload": {
"customerName": "试排急单客户",
"customerLevel": "VIP",
"productCode": "CTRL-A",
"quantity": 3,
"deliveryDate": fmt_date(add_minutes(today0(), 5 * 24 * 60)),
"priority": 1,
},
"strategy": "DELIVERY_FIRST",
})
order = applied["order"]
result = applied["result"]
assert order["isRush"] is True
assert order["status"] == "APPROVED"
# 急单已桥接进柔性订单池
flex_row = next(
(o for o in world["flexOrders"] if o.get("orderNo") == order["orderNo"]),
None,
)
assert flex_row is not None, "急单未桥接进 flexOrders,柔性排产必然看不到它"
assert flex_row["source"] == "SALES"
assert flex_row["status"] == "RELEASED"
assert flex_row["isRush"] is True
# 生成的是柔性 FV 草稿版本,而不是主轨 RULE 空壳版本
assert str(result.versionNo).startswith("FV")
assert len(world["scheduleVersions"]) == n_trunk_versions, "主轨不应新增空壳版本"
assert len(world["flexScheduleVersions"]) == n_flex_versions + 1
assert world["flexScheduleVersions"][-1]["versionNo"] == result.versionNo
assert result.orderCount == len(world["flexOrders"])
# 急单真实排出虚拟产线(修复前在此类世界为 0 工单)
rush_vls = [
v for v in world["flexVirtualLines"]
if v.get("orderNo") == order["orderNo"]
and v.get("versionId") == world["flexScheduleVersions"][-1]["id"]
]
assert rush_vls, "急单在柔性版本中应有至少一条虚拟产线"