Files
KCGL/inventory-backend/app/services/approval_control.py
yueli d06ce45a72 feat(borrow,outbound): 需审批判定收敛——ID优先 + 名称精确AND核心规格(斜杠前段)
- approval_control 重写:带 base_id 按主键绝对判定;无 id 才名称100%一致 AND 核心规格(_code_of)一致兜底,杜绝同名不同规误杀
- 借/出库服务命中需审批但未选审批人时,报错列出需审批物料名/规格
2026-09-09 10:47:24 +08:00

89 lines
3.0 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

from app.models.base import MaterialBase
def _code_of(spec):
"""取规格的首段编码('S0013/S0013' -> 'S0013''S0013' -> 'S0013'"""
s = (spec or '').strip()
if not s:
return ''
return s.split('/')[0].strip().upper()
def _materials_flagged():
"""一次性取出所有“启用且需审批”的物料(量小,逐项判定前先全量拉一次)"""
rows = MaterialBase.query.filter(
MaterialBase.is_enabled == True,
MaterialBase.is_approval_required == True
).all()
return rows
def _is_int(v):
try:
int(v)
return True
except (TypeError, ValueError):
return False
def resolve_approval_control(items):
"""
判断一批出库/借库申请明细是否需要审批。
判定优先级:
1) 方案1最高明细带有效 base_id → 直接按物料主键查 is_approval_required
且只要有有效 ID无论命中与否都不再做任何字符串兜底杜绝同名/同规格误杀)。
2) 方案2无 ID 兜底):名称必须 100% 精确一致 AND 核心规格一致(斜杠前段,
或前端未传规格视为防呆拦截),两条件同时成立才判定命中。
"""
flagged = []
seen = set()
def _record(m_obj, req_name):
if m_obj.id in seen:
return
seen.add(m_obj.id)
flagged.append({
'name': m_obj.name or req_name,
'spec_model': m_obj.spec_model or '',
'base_id': m_obj.id,
})
# 1. 查出所有“启用且需审批”的危险物料库
flagged_materials = _materials_flagged()
for item in items or []:
name = str(item.get('name') or '').strip()
spec = str(item.get('spec_model') or '').strip()
base_id = item.get('base_id')
# --- 方案 1最高优先级依据 base_id 绝对判定 ---
if base_id is not None:
bid = int(base_id) if _is_int(base_id) else 0
if bid > 0:
m = MaterialBase.query.get(bid)
if m and m.is_enabled and m.is_approval_required:
_record(m, name)
# ★ 关键:只要有有效 ID无论是否需审批绝不允许往下走字符串兜底
continue
# --- 方案 2无 ID 时的降级兜底 (名称精确相等 AND 核心规格相等) ---
if not name:
continue
# 提取当前申请行的核心规格
req_core_spec = _code_of(spec)
for fm in flagged_materials:
# 条件 A名称必须 100% 绝对一致
if fm.name != name:
continue
# 条件 B核心规格一致 (或前端未传规格视为防呆拦截)
fm_core_spec = _code_of(fm.spec_model)
if not req_core_spec or req_core_spec == fm_core_spec:
_record(fm, name)
break # 命中即判定本申请行需审批,跳出内层循环
return bool(flagged), flagged