2026-07-28 02:12:46 +08:00
|
|
|
# ============================================================
|
|
|
|
|
# 柔性/SQL 订单应出现在订单管理列表
|
|
|
|
|
# ============================================================
|
|
|
|
|
from __future__ import annotations
|
|
|
|
|
|
|
|
|
|
from server.aps_domain.orders import list_orders, list_production_orders, pool_summary, sync_flex_orders_to_sales
|
|
|
|
|
from server.state.seed import empty_world
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def test_list_orders_includes_flex():
|
|
|
|
|
world = empty_world()
|
|
|
|
|
world["flexOrders"] = [{
|
|
|
|
|
"id": 1, "orderNo": "SYS001", "productCode": "P1", "productName": "成品",
|
|
|
|
|
"quantity": 10, "dueDate": "2026-08-01", "status": "RELEASED",
|
|
|
|
|
"customerName": "现场", "priority": 3, "craftlCode": "C1",
|
|
|
|
|
}]
|
|
|
|
|
world["materials"] = [{
|
|
|
|
|
"id": 1, "code": "P1", "name": "成品", "type": "FINISHED_PRODUCT", "unit": "件",
|
|
|
|
|
"stock": 0, "inTransit": 0, "safetyStock": 0, "procurementLeadTime": 0,
|
|
|
|
|
}]
|
|
|
|
|
rows = list_orders(world)
|
|
|
|
|
assert len(rows) >= 1
|
|
|
|
|
assert rows[0]["orderNo"] == "SYS001"
|
|
|
|
|
assert rows[0]["status"] == "APPROVED"
|
|
|
|
|
assert pool_summary(world)["all"] >= 1
|
|
|
|
|
assert pool_summary(world)["schedulable"] >= 1
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def test_sync_flex_to_sales_and_production():
|
|
|
|
|
world = empty_world()
|
|
|
|
|
world["flexOrders"] = [{
|
|
|
|
|
"id": 1, "orderNo": "SYS002", "productCode": "P2", "productName": "轮",
|
|
|
|
|
"quantity": 5, "dueDate": "2026-09-01", "status": "RELEASED", "craftlCode": "X",
|
|
|
|
|
}]
|
|
|
|
|
world["flexScheduleVersions"] = [{"id": 9, "versionNo": "FV-1"}]
|
|
|
|
|
world["flexVirtualLines"] = [{
|
|
|
|
|
"id": 1, "versionId": 9, "vlNo": "VL-1", "orderNo": "SYS002",
|
|
|
|
|
"productCode": "P2", "quantity": 5, "plannedStart": "2026-08-01 08:00",
|
|
|
|
|
"plannedEnd": "2026-08-01 12:00", "status": "PLANNED",
|
|
|
|
|
}]
|
|
|
|
|
world["flexWorkOrders"] = [
|
|
|
|
|
{"id": 1, "versionId": 9, "vlId": 1, "seq": 10, "operationCode": "OP1"},
|
|
|
|
|
]
|
|
|
|
|
n = sync_flex_orders_to_sales(world)
|
|
|
|
|
assert n == 1
|
|
|
|
|
assert any(so["orderNo"] == "SYS002" for so in world["salesOrders"])
|
|
|
|
|
pos = list_production_orders(world)
|
|
|
|
|
assert len(pos) == 1
|
|
|
|
|
assert pos[0]["orderNo"] == "SYS002"
|
|
|
|
|
assert pos[0]["stepCount"] == 1
|
2026-08-11 00:54:05 +08:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def test_sync_flex_to_sales_is_semantically_idempotent():
|
|
|
|
|
world = empty_world()
|
|
|
|
|
world["businessDate"] = "2026-08-03"
|
|
|
|
|
world["flexOrders"] = [{
|
|
|
|
|
"id": 1,
|
|
|
|
|
"orderNo": "SYS-IDEMPOTENT",
|
|
|
|
|
"productCode": "P-IDEMPOTENT",
|
|
|
|
|
"productName": "Idempotent Product",
|
|
|
|
|
"quantity": 5,
|
|
|
|
|
"dueDate": "2026-09-01",
|
|
|
|
|
"status": "RELEASED",
|
|
|
|
|
"customerName": "Shop Floor",
|
|
|
|
|
"priority": 3,
|
|
|
|
|
}]
|
|
|
|
|
world["materials"] = [{
|
|
|
|
|
"id": 1,
|
|
|
|
|
"code": "P-IDEMPOTENT",
|
|
|
|
|
"name": "Idempotent Product",
|
|
|
|
|
"type": "FINISHED_PRODUCT",
|
|
|
|
|
"unit": "PCS",
|
|
|
|
|
"stock": 0,
|
|
|
|
|
"inTransit": 0,
|
|
|
|
|
"safetyStock": 0,
|
|
|
|
|
"procurementLeadTime": 0,
|
|
|
|
|
}]
|
|
|
|
|
|
|
|
|
|
assert sync_flex_orders_to_sales(world) == 1
|
|
|
|
|
first = [dict(row) for row in world["salesOrders"]]
|
|
|
|
|
first_item = [dict(row["items"][0]) for row in world["salesOrders"]]
|
|
|
|
|
assert sync_flex_orders_to_sales(world) == 0
|
|
|
|
|
assert world["salesOrders"] == first
|
|
|
|
|
assert [row["items"][0] for row in world["salesOrders"]] == first_item
|
|
|
|
|
|
|
|
|
|
world["flexOrders"][0]["quantity"] = 6
|
|
|
|
|
assert sync_flex_orders_to_sales(world) == 1
|
|
|
|
|
assert world["salesOrders"][0]["items"][0]["quantity"] == 6
|
|
|
|
|
assert world["salesOrders"][0]["id"] == first[0]["id"]
|
|
|
|
|
assert world["salesOrders"][0]["items"][0]["id"] == first_item[0]["id"]
|