82 lines
3.8 KiB
Python
82 lines
3.8 KiB
Python
# ============================================================
|
||
# 交期承诺看板的数据来源回退(固定产线通道 → 柔性通道)
|
||
# 现场现象:已经跑出柔性试排,但「交期分析」5 张订单整列显示"未排产"。
|
||
# 根因:due_view 只读 scheduleVersions / productionOrders,柔性版本不在这两张表里,
|
||
# 没有固定版本时 promised 恒为 None → risk 恒为 "none"。
|
||
# ============================================================
|
||
from __future__ import annotations
|
||
|
||
from server.aps_domain.views import due_view
|
||
|
||
|
||
def _so(order_id: int, order_no: str, delivery: str) -> dict:
|
||
"""最小销售订单行(due_view 只用到这几个字段)。"""
|
||
return {
|
||
"id": order_id, "orderNo": order_no, "customerName": "演示客户",
|
||
"customerLevel": "A", "isRush": False, "deliveryDate": delivery,
|
||
"status": "APPROVED",
|
||
}
|
||
|
||
|
||
def _world(**overrides) -> dict:
|
||
"""默认:既没有固定版本,也没有柔性版本。"""
|
||
world: dict = {
|
||
"salesOrders": [
|
||
_so(1, "SO-001", "2026-10-16"),
|
||
_so(2, "SO-002", "2026-10-10"),
|
||
_so(3, "SO-003", "2026-10-17"),
|
||
],
|
||
"scheduleVersions": [], "productionOrders": [],
|
||
"flexScheduleVersions": [], "flexVirtualLines": [],
|
||
}
|
||
world.update(overrides)
|
||
return world
|
||
|
||
|
||
def test_fixed_channel_still_wins_when_present():
|
||
"""固定通道有版本时行为不变:不因存在柔性版本而改取柔性时间。"""
|
||
rows = {r["orderNo"]: r for r in due_view(_world(
|
||
scheduleVersions=[{"id": 7, "versionNo": "FV-7"}],
|
||
productionOrders=[{"id": 1, "salesOrderId": 1, "schedulingVersionId": 7,
|
||
"plannedEndDate": "2026-10-15 12:00"}],
|
||
flexScheduleVersions=[{"id": 3, "versionNo": "FLEX-3"}],
|
||
flexVirtualLines=[{"versionId": 3, "orderNo": "SO-001",
|
||
"plannedEnd": "2026-10-09 08:00"}],
|
||
))}
|
||
assert rows["SO-001"]["promisedEnd"] == "2026-10-15 12:00"
|
||
assert rows["SO-002"]["risk"] == "none" # 该版本没有它的 PO
|
||
|
||
|
||
def test_flex_fallback_computes_commit_and_risk():
|
||
"""没有固定版本时回退柔性版本:承诺完成取该订单虚拟产线的最晚完成。"""
|
||
rows = {r["orderNo"]: r for r in due_view(_world(
|
||
flexScheduleVersions=[{"id": 3, "versionNo": "FLEX-3"}, {"id": 4, "versionNo": "FLEX-4"}],
|
||
flexVirtualLines=[
|
||
{"versionId": 4, "orderNo": "SO-001", "plannedEnd": "2026-10-14 09:00"},
|
||
{"versionId": 4, "orderNo": "SO-001", "plannedEnd": "2026-10-16 09:00"}, # 同订单取最晚
|
||
{"versionId": 3, "orderNo": "SO-002", "plannedEnd": "2026-10-01 09:00"}, # 旧版本不参与
|
||
{"versionId": 4, "orderNo": "SO-003", "plannedEnd": "2026-10-20 09:00"}, # 晚于交期 → 超期
|
||
],
|
||
))}
|
||
assert rows["SO-001"]["promisedEnd"] == "2026-10-16 09:00"
|
||
assert rows["SO-001"]["marginHours"] == 9.0 # 交期 10-16 18:00 前 9 小时
|
||
assert rows["SO-001"]["risk"] == "mid"
|
||
assert rows["SO-002"]["promisedEnd"] is None # 最新版本没排 → 仍是未排产
|
||
assert rows["SO-002"]["risk"] == "none"
|
||
assert rows["SO-003"]["marginHours"] == -63.0 # 10-20 09:00 比交期(10-17 18:00)晚 63 小时
|
||
assert rows["SO-003"]["risk"] == "high"
|
||
|
||
|
||
def test_no_schedule_anywhere_keeps_empty_board():
|
||
"""两个通道都没有版本时不报错、也不编造承诺完成。"""
|
||
rows = due_view(_world())
|
||
assert [r["risk"] for r in rows] == ["none", "none", "none"]
|
||
assert all(r["promisedEnd"] is None and r["marginHours"] is None for r in rows)
|
||
|
||
|
||
def test_cancelled_order_is_not_listed():
|
||
world = _world()
|
||
world["salesOrders"][1]["status"] = "CANCELLED"
|
||
rows = due_view(world)
|
||
assert [r["orderNo"] for r in rows] == ["SO-001", "SO-003"]
|