120 lines
5.3 KiB
Python
120 lines
5.3 KiB
Python
|
|
"""生命周期阶段(Lifecycle Phase)词汇表与选项隔离 — 单一事实来源
|
|||
|
|
|
|||
|
|
生产制造(PRODUCTION)与售后回流(AFTER_SALES)各自拥有一套合法的
|
|||
|
|
宏观状态 / 工序名。回流设备绝不允许被重新排产回「备货 / 生产」等前期环节,
|
|||
|
|
本模块集中定义两套词表并提供校验,前端各端复制同一份口径即可。
|
|||
|
|
|
|||
|
|
设计要点
|
|||
|
|
--------
|
|||
|
|
1. 售后阶段使用**独立工序名**(发货测试 / 售后维修),不复用生产阶段的
|
|||
|
|
「测试 / 维修」。这样报表、看板、导出无需 JOIN lifecycle_phase 就能区分,
|
|||
|
|
也不会出现"同一个词两种含义"。
|
|||
|
|
2. 选定售后专属工序 = 设备进入售后生命周期,单向不可回退。
|
|||
|
|
这条规则同时覆盖两类设备:
|
|||
|
|
- 已出库后被重新派发任务(由 task_service 的 outbound 判定捕获)
|
|||
|
|
- 无任何历史记录、直接走售后流程的老设备(首次选定售后工序即判定)
|
|||
|
|
3. 「待确认」与仓库虚拟节点是建单占位符,不参与阶段校验,否则会卡死建单流程。
|
|||
|
|
"""
|
|||
|
|
from __future__ import annotations
|
|||
|
|
|
|||
|
|
# ============================================================
|
|||
|
|
# 生命周期阶段
|
|||
|
|
# ============================================================
|
|||
|
|
LIFECYCLE_PRODUCTION = "PRODUCTION" # 生产制造阶段(发货前)
|
|||
|
|
LIFECYCLE_AFTER_SALES = "AFTER_SALES" # 已出库后再次回流返厂(售后)
|
|||
|
|
|
|||
|
|
PHASE_LABELS = {
|
|||
|
|
LIFECYCLE_PRODUCTION: "生产制造阶段",
|
|||
|
|
LIFECYCLE_AFTER_SALES: "售后回流阶段",
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
# ============================================================
|
|||
|
|
# 售后专属工序名 — 一旦选中即代表设备进入售后生命周期
|
|||
|
|
# ============================================================
|
|||
|
|
STEP_SHIP_TEST = "发货测试" # 售后返修后的出货测试
|
|||
|
|
STEP_AFTER_SALES_REPAIR = "售后维修" # 售后返修本体
|
|||
|
|
|
|||
|
|
AFTER_SALES_ONLY_STEPS = frozenset({STEP_SHIP_TEST, STEP_AFTER_SALES_REPAIR})
|
|||
|
|
|
|||
|
|
# ============================================================
|
|||
|
|
# 各阶段合法的工序名 / 宏观状态名
|
|||
|
|
# ============================================================
|
|||
|
|
|
|||
|
|
# ── 生产制造阶段(原有词表,一字未改,保证存量流程不受影响)──
|
|||
|
|
PRODUCTION_TASK_STEPS = ("备货", "生产", "测试", "维修", "在库")
|
|||
|
|
PRODUCTION_OVERALL_STEPS = (
|
|||
|
|
"备货", "生产", "测试", "维修", "在库", "待仓库收货", "已入库", "已出库",
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
# ── 售后回流阶段:只保留「发货测试 / 售后维修 / 入库出库」──
|
|||
|
|
AFTER_SALES_TASK_STEPS = (STEP_SHIP_TEST, STEP_AFTER_SALES_REPAIR, "在库")
|
|||
|
|
AFTER_SALES_OVERALL_STEPS = (
|
|||
|
|
STEP_SHIP_TEST, STEP_AFTER_SALES_REPAIR, "在库", "待仓库收货", "已入库", "已出库",
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
# 仅属于生产制造阶段的工序名 — 售后设备出现即视为"跨阶段误排"。
|
|||
|
|
# 注意:「在库 / 已入库 / 已出库」是两阶段通用的,不在此列。
|
|||
|
|
PRODUCTION_ONLY_STEPS = frozenset({"备货", "生产", "测试", "维修"})
|
|||
|
|
|
|||
|
|
# 建单占位工序名 — 建单时为「待确认」,接收时才由操作员选定真实工序
|
|||
|
|
PLACEHOLDER_STEPS = frozenset({"待确认"})
|
|||
|
|
|
|||
|
|
# 仓库虚拟节点标记(转交入库时作为 assignee 传递)
|
|||
|
|
VIRTUAL_WAREHOUSE = "virtual_warehouse"
|
|||
|
|
|
|||
|
|
# 两阶段并集 — 用于"完全非法取值"的第一道粗筛
|
|||
|
|
ALL_OVERALL_STEPS = frozenset(PRODUCTION_OVERALL_STEPS) | frozenset(AFTER_SALES_OVERALL_STEPS)
|
|||
|
|
|
|||
|
|
_ALLOWED_BY_PHASE = {
|
|||
|
|
LIFECYCLE_PRODUCTION: frozenset(PRODUCTION_OVERALL_STEPS),
|
|||
|
|
LIFECYCLE_AFTER_SALES: frozenset(AFTER_SALES_OVERALL_STEPS),
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
|
|||
|
|
# ============================================================
|
|||
|
|
# 查询 / 校验
|
|||
|
|
# ============================================================
|
|||
|
|
|
|||
|
|
def phase_label(phase: str | None) -> str:
|
|||
|
|
"""阶段的中文名(用于报错文案)"""
|
|||
|
|
return PHASE_LABELS.get(phase or "", PHASE_LABELS[LIFECYCLE_PRODUCTION])
|
|||
|
|
|
|||
|
|
|
|||
|
|
def allowed_steps(phase: str | None) -> tuple[str, ...]:
|
|||
|
|
"""该阶段合法的全部工序/状态名"""
|
|||
|
|
if (phase or "") == LIFECYCLE_AFTER_SALES:
|
|||
|
|
return AFTER_SALES_OVERALL_STEPS
|
|||
|
|
return PRODUCTION_OVERALL_STEPS
|
|||
|
|
|
|||
|
|
|
|||
|
|
def is_placeholder_step(step: str | None) -> bool:
|
|||
|
|
"""建单占位符(待确认)/ 仓库虚拟节点 — 不做阶段校验
|
|||
|
|
|
|||
|
|
否则 create_task 会被「待确认」卡住,转交入库会被
|
|||
|
|
「🏭 入库 (virtual_warehouse)」卡住,整条流程直接断掉。
|
|||
|
|
"""
|
|||
|
|
if not step:
|
|||
|
|
return True
|
|||
|
|
return step in PLACEHOLDER_STEPS or VIRTUAL_WAREHOUSE in step
|
|||
|
|
|
|||
|
|
|
|||
|
|
def resolve_phase_for_step(current_phase: str | None, step: str | None) -> str:
|
|||
|
|
"""选定售后专属工序 → 设备进入售后生命周期(单向,不可回退)
|
|||
|
|
|
|||
|
|
这是"无历史记录的老设备"进入售后阶段的唯一入口:它首次选定
|
|||
|
|
「发货测试 / 售后维修」时即被判定为售后回流设备。
|
|||
|
|
"""
|
|||
|
|
if step and step.strip() in AFTER_SALES_ONLY_STEPS:
|
|||
|
|
return LIFECYCLE_AFTER_SALES
|
|||
|
|
return current_phase or LIFECYCLE_PRODUCTION
|
|||
|
|
|
|||
|
|
|
|||
|
|
def is_step_allowed(phase: str | None, step: str | None) -> bool:
|
|||
|
|
"""工序名在该生命周期阶段下是否合法"""
|
|||
|
|
if is_placeholder_step(step):
|
|||
|
|
return True
|
|||
|
|
allowed = _ALLOWED_BY_PHASE.get(
|
|||
|
|
phase or LIFECYCLE_PRODUCTION, _ALLOWED_BY_PHASE[LIFECYCLE_PRODUCTION],
|
|||
|
|
)
|
|||
|
|
return step.strip() in allowed
|