# ============================================================ # SC-08 批次/战役合并黄金测试 # ============================================================ from __future__ import annotations from server.aps_domain.campaign import merge_entries_to_campaigns, preview_campaigns from server.engines.base import EngineParams from server.engines.rule_engine import RuleEngine from server.state.seed import seed_world from server.timeutil import add_minutes, fmt_date, today0 def test_preview_merges_same_product_in_window(): world = seed_world() report = preview_campaigns(world, window_days=7) assert report["summary"]["inputOrders"] == 7 assert report["summary"]["poSaved"] >= 1 assert report["summary"]["mergedCampaigns"] >= 1 # CTRL-A 两单交期差 1 天,应合并 a_camps = [c for c in report["campaigns"] if c["productCode"] == "CTRL-A" and c["isCampaign"]] assert a_camps assert a_camps[0]["memberCount"] == 2 assert a_camps[0]["totalQty"] == 1300 # 500+800 def test_rush_not_merged(): world = seed_world() report = preview_campaigns(world, window_days=30) rush_rows = [c for c in report["campaigns"] if c.get("isRush")] assert rush_rows assert all(c["memberCount"] == 1 for c in rush_rows) def test_campaign_strategy_fewer_pos(): world = seed_world() start = fmt_date(add_minutes(today0(), 24 * 60)) counter = {"n": 4000} def next_id(_k: str) -> int: counter["n"] += 1 return counter["n"] base = EngineParams( strategyTemplate="COMPREHENSIVE", name="base", planningHorizonDays=14, startDate=start, constraints={"materialKit": False, "changeover": True, "capacity": True, "dueDate": True, "equipment": True, "personnel": True, "tooling": True, "freeze": True}, ) r0 = RuleEngine("RULE").solve(world, base, next_id) assert r0.poCount == 7 world2 = seed_world() camp = EngineParams( strategyTemplate="CAMPAIGN", name="campaign", planningHorizonDays=14, startDate=start, constraints={"materialKit": False, "changeover": True, "capacity": True, "dueDate": True, "equipment": True, "personnel": True, "tooling": True, "freeze": True}, ) r1 = RuleEngine("RULE").solve(world2, camp, next_id) assert r1.orderCount == 7 # 源订单仍计 7 assert r1.poCount < 7 ver = next(v for v in world2["scheduleVersions"] if v["id"] == r1.versionId) assert ver["campaign"]["enabled"] is True assert ver["campaign"]["poSaved"] >= 1 camp_pos = [p for p in world2["productionOrders"] if p.get("schedulingVersionId") == r1.versionId and p.get("isCampaign")] assert camp_pos assert camp_pos[0]["campaignMemberCount"] >= 2 assert len(camp_pos[0]["campaignSourceOrderNos"]) >= 2 def test_merge_entries_unit(): world = seed_world() entries = [] for so in world["salesOrders"]: for item in so["items"]: entries.append({"so": so, "item": item}) camps = merge_entries_to_campaigns(world, entries, window_days=7) assert len(camps) < len(entries)