diff --git a/server/data/packs/optimize-simulation-v1.json b/server/data/packs/optimize-simulation-v1.json index 0098f22..f01bfbe 100644 --- a/server/data/packs/optimize-simulation-v1.json +++ b/server/data/packs/optimize-simulation-v1.json @@ -2,8 +2,8 @@ "packVersion": 1, "name": "Optimize synthetic debug V1", "aliases": ["Optimize 模拟数据", "Optimize synthetic data", "simulation"], - "description": "Generic APS world pack: 10 synthetic orders, 72 operations, and 6 virtual resources for Optimize development debugging only.", - "baseDate": "2026-09-03", + "description": "Generic APS world pack: 10 synthetic orders, 72 operations, and 6 virtual resources for Optimize development debugging only. baseDate is the case business day of the replayed Kangni case.", + "baseDate": "2026-08-02", "world": { "factories": [], "workshops": [], @@ -147,10 +147,23 @@ { "id": 1, "orderNo": "102285668", + "customerId": "CUST001", + "customerName": "模拟客户A", + "customerLevel": "A", + "orderDate": "2026-06-25", "status": "APPROVED", "deliveryDate": "2026-08-08", "priority": 1, + "manualPriority": null, "source": "FLEX", + "specialRequirements": "", + "totalAmount": 1000.0, + "isRush": false, + "rushStrategy": null, + "changes": [], + "createdBy": "synthetic-pack", + "createdAt": "2026-06-25 09:00", + "updatedAt": "2026-06-25 09:00", "items": [ { "id": 1, @@ -167,10 +180,23 @@ { "id": 2, "orderNo": "102289293", + "customerId": "CUST002", + "customerName": "模拟客户B", + "customerLevel": "VIP", + "orderDate": "2026-07-06", "status": "APPROVED", "deliveryDate": "2026-07-21", "priority": 1, + "manualPriority": null, "source": "FLEX", + "specialRequirements": "", + "totalAmount": 1000.0, + "isRush": true, + "rushStrategy": "STRATEGY_SHIFT", + "changes": [], + "createdBy": "synthetic-pack", + "createdAt": "2026-07-06 09:00", + "updatedAt": "2026-07-06 09:00", "items": [ { "id": 2, @@ -187,10 +213,23 @@ { "id": 3, "orderNo": "102342090", + "customerId": "CUST003", + "customerName": "模拟客户C", + "customerLevel": "B", + "orderDate": "2026-01-15", "status": "APPROVED", "deliveryDate": "2026-01-15", "priority": 1, + "manualPriority": null, "source": "FLEX", + "specialRequirements": "", + "totalAmount": 2000.0, + "isRush": false, + "rushStrategy": null, + "changes": [], + "createdBy": "synthetic-pack", + "createdAt": "2026-01-15 09:00", + "updatedAt": "2026-01-15 09:00", "items": [ { "id": 3, @@ -207,10 +246,23 @@ { "id": 4, "orderNo": "102342085", + "customerId": "CUST004", + "customerName": "模拟客户C", + "customerLevel": "B", + "orderDate": "2026-01-15", "status": "APPROVED", "deliveryDate": "2026-01-15", "priority": 1, + "manualPriority": null, "source": "FLEX", + "specialRequirements": "", + "totalAmount": 2000.0, + "isRush": false, + "rushStrategy": null, + "changes": [], + "createdBy": "synthetic-pack", + "createdAt": "2026-01-15 09:00", + "updatedAt": "2026-01-15 09:00", "items": [ { "id": 4, @@ -227,10 +279,23 @@ { "id": 5, "orderNo": "102342092", + "customerId": "CUST005", + "customerName": "模拟客户D", + "customerLevel": "C", + "orderDate": "2026-01-15", "status": "APPROVED", "deliveryDate": "2026-01-15", "priority": 1, + "manualPriority": null, "source": "FLEX", + "specialRequirements": "", + "totalAmount": 4000.0, + "isRush": false, + "rushStrategy": null, + "changes": [], + "createdBy": "synthetic-pack", + "createdAt": "2026-01-15 09:00", + "updatedAt": "2026-01-15 09:00", "items": [ { "id": 5, @@ -247,10 +312,23 @@ { "id": 6, "orderNo": "102352579", + "customerId": "CUST006", + "customerName": "模拟客户A", + "customerLevel": "A", + "orderDate": "2026-04-03", "status": "APPROVED", "deliveryDate": "2026-04-03", "priority": 1, + "manualPriority": null, "source": "FLEX", + "specialRequirements": "", + "totalAmount": 1000.0, + "isRush": false, + "rushStrategy": null, + "changes": [], + "createdBy": "synthetic-pack", + "createdAt": "2026-04-03 09:00", + "updatedAt": "2026-04-03 09:00", "items": [ { "id": 6, @@ -267,10 +345,23 @@ { "id": 7, "orderNo": "102314266", + "customerId": "CUST007", + "customerName": "模拟客户D", + "customerLevel": "C", + "orderDate": "2026-03-16", "status": "APPROVED", "deliveryDate": "2026-03-16", "priority": 1, + "manualPriority": null, "source": "FLEX", + "specialRequirements": "", + "totalAmount": 1000.0, + "isRush": false, + "rushStrategy": null, + "changes": [], + "createdBy": "synthetic-pack", + "createdAt": "2026-03-16 09:00", + "updatedAt": "2026-03-16 09:00", "items": [ { "id": 7, @@ -287,10 +378,23 @@ { "id": 8, "orderNo": "102375367", + "customerId": "CUST008", + "customerName": "模拟客户B", + "customerLevel": "VIP", + "orderDate": "2026-04-03", "status": "APPROVED", "deliveryDate": "2026-04-03", "priority": 1, + "manualPriority": null, "source": "FLEX", + "specialRequirements": "", + "totalAmount": 1000.0, + "isRush": false, + "rushStrategy": null, + "changes": [], + "createdBy": "synthetic-pack", + "createdAt": "2026-04-03 09:00", + "updatedAt": "2026-04-03 09:00", "items": [ { "id": 8, @@ -307,10 +411,23 @@ { "id": 9, "orderNo": "102375368", + "customerId": "CUST009", + "customerName": "模拟客户C", + "customerLevel": "B", + "orderDate": "2026-04-03", "status": "APPROVED", "deliveryDate": "2026-04-03", "priority": 1, + "manualPriority": null, "source": "FLEX", + "specialRequirements": "", + "totalAmount": 1000.0, + "isRush": false, + "rushStrategy": null, + "changes": [], + "createdBy": "synthetic-pack", + "createdAt": "2026-04-03 09:00", + "updatedAt": "2026-04-03 09:00", "items": [ { "id": 9, @@ -327,10 +444,23 @@ { "id": 10, "orderNo": "102378688", + "customerId": "CUST010", + "customerName": "模拟客户A", + "customerLevel": "A", + "orderDate": "2026-04-03", "status": "APPROVED", "deliveryDate": "2026-04-03", "priority": 1, + "manualPriority": null, "source": "FLEX", + "specialRequirements": "", + "totalAmount": 1000.0, + "isRush": false, + "rushStrategy": null, + "changes": [], + "createdBy": "synthetic-pack", + "createdAt": "2026-04-03 09:00", + "updatedAt": "2026-04-03 09:00", "items": [ { "id": 10, diff --git a/server/state/packs.py b/server/state/packs.py index 09eb06f..3c45eac 100644 --- a/server/state/packs.py +++ b/server/state/packs.py @@ -3,7 +3,7 @@ # 把「一套完整世界数据」封装为可重放的 JSON 包: # - 演示厂 = server/data/packs/demo.json(由脚本从种子生成) # - 行业种子 / 客户现场数据都可做成包,data.reset 即「重放数据包」 -# 日期重基:包内记录 baseDate(生成日),加载时把所有 YYYY-MM-DD +# 日期重基:包内记录 baseDate(世界业务基准日:演示包=生成日,回放包=案例业务日),加载时把所有 YYYY-MM-DD # 字面日期整体平移到「今天」,保证任意运行日行为一致(黄金测试稳定)。 # ============================================================ from __future__ import annotations diff --git a/tests/golden/test_optimize_simulation_world_pack.py b/tests/golden/test_optimize_simulation_world_pack.py index 40f936e..299bd76 100644 --- a/tests/golden/test_optimize_simulation_world_pack.py +++ b/tests/golden/test_optimize_simulation_world_pack.py @@ -2,9 +2,11 @@ from __future__ import annotations import copy import json +from datetime import date from pathlib import Path from server.aps_domain.closed_loop_runtime import run_closed_loop_candidate +from server.aps_domain.views import due_view from server.state.packs import load_pack, list_packs @@ -62,3 +64,30 @@ def test_simulation_world_pack_runs_all_optimize_dispatch_rules(): assert result["woCount"] == 72, rule assert result["validation"]["valid"] is True, rule assert result["validation"]["hardViolations"] == [], rule + + +def test_simulation_world_pack_keeps_sales_order_fields_the_due_board_reads(): + """交期承诺看板直取订单客户字段,数据包必须按订单契约补齐(回归:/api/world/due 500)。""" + world = load_pack(str(PACK_PATH)) + + rows = due_view(world) + + assert len(rows) == len(world["salesOrders"]) == 10 + assert all(row["customerName"] and row["customerLevel"] for row in rows) + assert all(isinstance(row["isRush"], bool) for row in rows) + assert any(row["isRush"] for row in rows) # 至少一张插单,加急标记在 UI 有数据可验 + assert {row["customerLevel"] for row in rows} >= {"VIP", "A", "B", "C"} # 等级过滤有数据可筛 + + +def test_simulation_world_pack_timeline_follows_its_case_business_day(): + """回归:baseDate 必须是案例业务日,否则世界日期随运行日漂移、黄金测试输入每天变。 + + 源案例(kangni-simulation-package-v1)的业务日交期结构固定为「9 张逾期 + 最晚交期在业务日 +6 天」, + 世界日期整体贴着 baseDate 平移后,这个结构在任意运行日都应原样落在"今天"上。 + """ + world = load_pack(str(PACK_PATH)) + due_dates = sorted(date.fromisoformat(order["dueDate"]) for order in world["flexOrders"]) + today = date.today() + + assert (due_dates[-1] - today).days == 6 # 最晚交期 = 案例业务日 +6 天 + assert sum(1 for due in due_dates if due < today) == 9 # 案例本身是 9 张逾期单