aps-agent/tests/golden/test_params.py

119 lines
4.7 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.

# ============================================================
# OR-02 排产参数 / 客户等级权重黄金测试
# ============================================================
from __future__ import annotations
from server.aps_domain.params import (
apply_params_update, confirmation_for_params_update, get_schedule_params,
)
from server.engines import get_engine
from server.engines.base import EngineParams
from server.state.seed import seed_world
from server.timeutil import add_minutes, fmt_date, parse_dt, today0
def _next_id_factory():
counters: dict[str, int] = {}
def next_id(kind: str) -> int:
counters[kind] = counters.get(kind, 0) + 1
return counters[kind]
return next_id
def _two_orders_world(vip_due_offset=10, c_due_offset=5):
"""构造仅两单、同优先级的世界,便于观察等级权重对开工顺序的影响。"""
world = seed_world()
base = today0()
due_vip = fmt_date(add_minutes(base, vip_due_offset * 24 * 60))
due_c = fmt_date(add_minutes(base, c_due_offset * 24 * 60))
order_date = fmt_date(base)
product = next(m for m in world["materials"] if m["type"] == "FINISHED_PRODUCT")
def make(oid, no, level, due):
return {
"id": oid, "orderNo": no, "customerId": f"C{oid}", "customerName": f"客户{level}",
"customerLevel": level, "orderDate": order_date, "deliveryDate": due,
"priority": 5, "manualPriority": None, "status": "CONFIRMED",
"source": "MANUAL", "specialRequirements": "", "totalAmount": 1000,
"isRush": False, "rushStrategy": None, "changes": [],
"createdBy": "test", "createdAt": order_date + " 09:00", "updatedAt": order_date + " 09:00",
"items": [{
"id": oid * 10, "orderId": oid, "lineNo": 1,
"productId": product["id"], "productName": product["name"], "productCode": product["code"],
"quantity": 100, "unit": "件", "bomVersion": "V1.0", "routingVersion": "V1.0",
"status": "PENDING", "note": "",
}],
}
world["salesOrders"] = [
make(1, "SO-VIP-1", "VIP", due_vip),
make(2, "SO-C-1", "C", due_c),
]
world["scheduleVersions"] = []
world["productionOrders"] = []
world["workOrders"] = []
world["conflicts"] = []
return world
def _run(world, strategy="COMPREHENSIVE"):
start = fmt_date(add_minutes(today0(), 24 * 60))
params = EngineParams(orderIds=[], engineType="RULE", strategyTemplate=strategy,
planningHorizonDays=14, startDate=start)
return get_engine("RULE").solve(world, params, _next_id_factory())
def _earliest_level(world) -> str:
pos = world["productionOrders"]
earliest = min(pos, key=lambda p: parse_dt(p["plannedStartDate"]))
so = next(s for s in world["salesOrders"] if s["id"] == earliest["salesOrderId"])
return so["customerLevel"]
def test_default_params_have_level_weights():
p = get_schedule_params(seed_world())
assert p["customerLevelWeights"]["VIP"] == 3.0
assert p["customerLevelWeights"]["C"] == 1.0
def test_vip_weight_pulls_order_earlier():
"""提高 VIP 权重后,即便 VIP 交期更晚,综合策略下 VIP 也应更早开工。"""
# 默认:VIP=3,C=1;VIP 交期更晚 → 仍可能 VIP 先(等级键优先)
w1 = _two_orders_world(vip_due_offset=12, c_due_offset=5)
_run(w1)
assert _earliest_level(w1) == "VIP"
# 把 VIP 压到 1、C 提到 10 → C 应先开工
w2 = _two_orders_world(vip_due_offset=12, c_due_offset=5)
apply_params_update(w2, {"customerLevelWeights": {"VIP": 1, "C": 10}})
_run(w2)
assert _earliest_level(w2) == "C"
# 再把 VIP 提到 20 → VIP 又应先开工
w3 = _two_orders_world(vip_due_offset=12, c_due_offset=5)
apply_params_update(w3, {"customerLevelWeights": {"VIP": 20, "C": 1}})
_run(w3)
assert _earliest_level(w3) == "VIP"
def test_delivery_first_still_edd():
"""交期优先仍以交期为主键(G5 不变式)。"""
world = _two_orders_world(vip_due_offset=12, c_due_offset=5)
apply_params_update(world, {"customerLevelWeights": {"VIP": 20, "C": 1}})
_run(world, strategy="DELIVERY_FIRST")
assert _earliest_level(world) == "C" # C 交期更早
def test_params_update_roundtrip_and_confirm_text():
world = seed_world()
title, lines = confirmation_for_params_update(
world, {"customerLevelWeights": {"VIP": 9}})
assert "排产参数" in title
assert any("VIP" in x for x in lines)
applied = apply_params_update(world, {"customerLevelWeights": {"VIP": 9}})
assert applied["after"]["customerLevelWeights"]["VIP"] == 9.0
apply_params_update(world, {"resetDefaults": True})
assert get_schedule_params(world)["customerLevelWeights"]["VIP"] == 3.0