fix: WIP矩阵-左外连接纳入无任务新品归待接收,PENDING并入待接收,空负责人显式计数为未分配; 表头去待确认列并做TOP N折叠
This commit is contained in:
@ -672,15 +672,17 @@ async def get_wip_matrix(
|
||||
Product.material_name,
|
||||
Task.task_name,
|
||||
Task.assignee_id,
|
||||
Task.status.label("task_status"),
|
||||
Task.created_at,
|
||||
Task.completed_at,
|
||||
Product.current_location_id,
|
||||
Product.overall_status,
|
||||
Product.status,
|
||||
)
|
||||
.join(Task, Task.product_id == Product.id)
|
||||
.outerjoin(Task, Task.product_id == Product.id)
|
||||
.where(
|
||||
or_(
|
||||
Task.id.is_(None), # 🔧 允许该产品完全没有任务记录(新建档/待接收)
|
||||
Task.parent_task_id.is_(None),
|
||||
Task.task_type.in_(["TRANSFER", "RECOVERY", "WAREHOUSE"]),
|
||||
)
|
||||
@ -692,7 +694,7 @@ async def get_wip_matrix(
|
||||
# 每台设备 → (spec, 当前工序/负责人, 当前负责人ID)
|
||||
device_cur: dict[str, tuple] = {}
|
||||
seen: set[str] = set()
|
||||
for pid, spec, product_name, task_name, assignee, created, completed, loc, overall_status, product_status in rows:
|
||||
for pid, spec, product_name, task_name, assignee, tstatus, created, completed, loc, overall_status, product_status in rows:
|
||||
if pid in seen:
|
||||
continue
|
||||
seen.add(pid)
|
||||
@ -730,41 +732,65 @@ async def get_wip_matrix(
|
||||
elif overall_status == "待仓库收货" or loc == "virtual_warehouse":
|
||||
key = "已完成"
|
||||
else:
|
||||
key = task_name or "—"
|
||||
# 🔧 对齐全景/产品管理:PENDING(含 task_name=待确认) 与 无任务新品 统一归「待接收」
|
||||
if tstatus == "PENDING" or (tstatus is None and task_name is None):
|
||||
key = "待接收"
|
||||
else:
|
||||
key = task_name if (task_name and task_name != "—") else "待接收"
|
||||
else:
|
||||
key = assignee or "未分配"
|
||||
device_cur[pid] = (spec or "未知型号", key, assignee or "", product_name or "")
|
||||
|
||||
# 聚合:规格 × 当前工序 → 设备数;同时收集负责人 与 产品名称
|
||||
from collections import Counter
|
||||
|
||||
# 聚合:规格 × 当前工序 → 设备数;负责人按人头计数,空值显式计为「未分配」
|
||||
agg: dict[tuple, int] = {}
|
||||
assignee_map: dict[tuple, set] = {}
|
||||
assignee_counter: dict[tuple, Counter] = {}
|
||||
product_name_map: dict[tuple, str] = {}
|
||||
for spec, key, assignee, product_name in device_cur.values():
|
||||
k = (spec, key)
|
||||
agg[k] = agg.get(k, 0) + 1
|
||||
product_name_map.setdefault(k, product_name)
|
||||
if assignee:
|
||||
assignee_map.setdefault(k, set()).add(assignee)
|
||||
raw = assignee if assignee and str(assignee).strip() not in ("", "—", "-", "null", "None") else ""
|
||||
assignee_counter.setdefault(k, Counter())[raw] += 1
|
||||
|
||||
# 负责人 ID → 中文名
|
||||
raw_ids: set[str] = set()
|
||||
for s in assignee_map.values():
|
||||
raw_ids |= s
|
||||
for counter in assignee_counter.values():
|
||||
raw_ids |= set(counter.keys())
|
||||
raw_ids.discard("")
|
||||
name_map: dict[str, str] = {}
|
||||
if raw_ids:
|
||||
from app.services.mom_cache import get_display_names
|
||||
name_map = get_display_names(list(raw_ids))
|
||||
|
||||
def _disp(raw: str) -> str:
|
||||
"""负责人显示名:空值统一为 未分配"""
|
||||
return "未分配" if not raw else name_map.get(raw, raw)
|
||||
|
||||
def _fmt_label(raw: str, n: int) -> str:
|
||||
"""负责人标签:>1 台必须带数量后缀;1 台不带"""
|
||||
disp = _disp(raw)
|
||||
return f"{disp}({n})" if n > 1 else disp
|
||||
|
||||
items: list[WipMatrixRow] = []
|
||||
for (spec, key), cnt in agg.items():
|
||||
dim_display = name_map.get(key, key) if dimension == "assignee" else key
|
||||
assignees = [name_map.get(a, a) for a in assignee_map.get((spec, key), set())] or []
|
||||
dim_display = _disp(key) if dimension == "assignee" else key
|
||||
counter = assignee_counter.get((spec, key), Counter())
|
||||
# 负责人名单:未分配固定排最前,其余按台数降序、名称升序;带数量后缀(>1 台)
|
||||
labels = [
|
||||
_fmt_label(raw, n)
|
||||
for raw, n in sorted(
|
||||
counter.items(),
|
||||
key=lambda kv: (0 if kv[0] == "" else 1, -kv[1], _disp(kv[0])),
|
||||
)
|
||||
]
|
||||
items.append(WipMatrixRow(
|
||||
spec_model=spec,
|
||||
product_name=product_name_map.get((spec, key), ""),
|
||||
dimension_key=dim_display,
|
||||
count=cnt,
|
||||
assignees=assignees,
|
||||
assignees=labels,
|
||||
))
|
||||
items.sort(key=lambda x: (x.spec_model, x.dimension_key))
|
||||
return items
|
||||
@ -812,9 +838,10 @@ async def get_wip_matrix_detail(
|
||||
Task.completed_at,
|
||||
Task.received_at,
|
||||
)
|
||||
.join(Task, Task.product_id == Product.id)
|
||||
.outerjoin(Task, Task.product_id == Product.id)
|
||||
.where(
|
||||
or_(
|
||||
Task.id.is_(None), # 🔧 允许该产品完全没有任务记录(新建档/待接收)
|
||||
Task.parent_task_id.is_(None),
|
||||
Task.task_type.in_(["TRANSFER", "RECOVERY", "WAREHOUSE"]),
|
||||
)
|
||||
@ -863,7 +890,11 @@ async def get_wip_matrix_detail(
|
||||
elif overall == "待仓库收货" or loc == "virtual_warehouse":
|
||||
key = "已完成"
|
||||
else:
|
||||
key = task_name or "—"
|
||||
# 🔧 对齐全景/产品管理:PENDING(含 task_name=待确认) 与 无任务新品 统一归「待接收」
|
||||
if tstatus == "PENDING" or (tstatus is None and task_name is None):
|
||||
key = "待接收"
|
||||
else:
|
||||
key = task_name if (task_name and task_name != "—") else "待接收"
|
||||
|
||||
if process and key != process:
|
||||
continue
|
||||
|
||||
Reference in New Issue
Block a user