# ============================================================ # PL-04 库存投影黄金测试 # ============================================================ from __future__ import annotations from server.aps_domain.inventory import build_inventory_projection from server.state.seed import seed_world def test_inventory_projection_seed_summary(): world = seed_world() report = build_inventory_projection(world, horizon_days=30, bucket="DAY", include_forecast=True) assert report["bucket"] == "DAY" assert report["summary"]["materialCount"] == len(world["materials"]) assert report["summary"]["materialCount"] >= 10 assert all(len(m["series"]) == 30 for m in report["materials"]) def test_inventory_filter_finished_and_code(): world = seed_world() fg = build_inventory_projection(world, material_type="FINISHED_PRODUCT", horizon_days=14) assert all(m["type"] == "FINISHED_PRODUCT" for m in fg["materials"]) assert len(fg["materials"]) == 3 one = build_inventory_projection(world, material_code="CTRL-A", horizon_days=14) assert len(one["materials"]) == 1 assert one["materials"][0]["code"] == "CTRL-A" def test_inventory_week_bucket_and_alerts(): world = seed_world() # 压低库存制造断料 for m in world["materials"]: if m["code"] == "CTRL-A": m["stock"] = 0 m["inTransit"] = 0 m["safetyStock"] = 100 report = build_inventory_projection(world, material_code="CTRL-A", horizon_days=21, bucket="WEEK") row = report["materials"][0] assert row["alert"] in ("STOCKOUT", "BELOW_SAFETY") assert len(row["series"]) >= 1 assert row["series"][0].get("periodStart") def test_inventory_exclude_forecast_lower_demand(): world = seed_world() with_fc = build_inventory_projection(world, material_type="FINISHED_PRODUCT", include_forecast=True) no_fc = build_inventory_projection(world, material_type="FINISHED_PRODUCT", include_forecast=False) total_with = sum(m["totalDemand"] for m in with_fc["materials"]) total_no = sum(m["totalDemand"] for m in no_fc["materials"]) assert total_with >= total_no