feat(scrap): 报废一律需审批,不再按物料标记区分
业务规则变更:所有报废申请都必须由指定审批人审批通过后才能执行。
原逻辑走 resolve_approval_control 判定是否需审批,而 material_base 表中
仅 1/3012 个物料标记了 is_approval_required,意味着 99.97% 的报废申请会
走免审批分支——status 直接置 1、actual_approver_id 被赋为申请人自己、
审批人参数被静默丢弃。前端即便做了必填也只是摆设。
改动(规则收敛到单一来源):
· 新增 SCRAP_ALWAYS_REQUIRES_APPROVAL = True,作为唯一开关;
· submit_approval() 无审批人一律拒绝;恒置 status=0(待审批);
删除免审批自动通过分支;
· resolve_approval_control 仍调用,但仅用于生成提示文案,
不再参与是否审批的判定;
· /request/check-approval 返回 need_approval=SCRAP_ALWAYS_REQUIRES_APPROVAL,
否则该接口会继续返回 false,导致前端预检结果失真。
实测:不传审批人 → 拒绝;传审批人 → status=0 且 actual_approver_id 为空。
⚠ 注意:此前自动通过的单据今后一律进入审批队列。
This commit is contained in:
@ -503,14 +503,19 @@ def _current_user_role():
|
|||||||
@jwt_required()
|
@jwt_required()
|
||||||
@permission_required('scrap_apply')
|
@permission_required('scrap_apply')
|
||||||
def scrap_check_approval():
|
def scrap_check_approval():
|
||||||
"""提交前预检:判断所选库存(物料)是否命中“需审批”"""
|
"""
|
||||||
|
提交前预检:报废一律需审批,本接口返回 need_approval=true,
|
||||||
|
并附带命中「需审批物料」标记的明细,供前端展示审批提示文案。
|
||||||
|
"""
|
||||||
try:
|
try:
|
||||||
from app.services.approval_control import resolve_approval_control
|
from app.services.approval_control import resolve_approval_control
|
||||||
|
from app.services.scrap_approval_service import SCRAP_ALWAYS_REQUIRES_APPROVAL
|
||||||
data = request.get_json() or {}
|
data = request.get_json() or {}
|
||||||
items = data.get('items', []) or []
|
items = data.get('items', []) or []
|
||||||
need, flagged = resolve_approval_control(items)
|
_, flagged = resolve_approval_control(items)
|
||||||
return jsonify({'code': 200, 'msg': 'success',
|
return jsonify({'code': 200, 'msg': 'success',
|
||||||
'data': {'need_approval': need, 'materials': flagged}}), 200
|
'data': {'need_approval': SCRAP_ALWAYS_REQUIRES_APPROVAL,
|
||||||
|
'materials': flagged}}), 200
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
traceback.print_exc()
|
traceback.print_exc()
|
||||||
return jsonify({'code': 500, 'msg': f'预检失败: {str(e)}'}), 500
|
return jsonify({'code': 500, 'msg': f'预检失败: {str(e)}'}), 500
|
||||||
|
|||||||
@ -21,6 +21,16 @@ def _beijing():
|
|||||||
return beijing_time()
|
return beijing_time()
|
||||||
|
|
||||||
|
|
||||||
|
# =============================================================================
|
||||||
|
# ★ 业务规则(单一事实来源):报废一律需审批
|
||||||
|
#
|
||||||
|
# 与出库/借库不同,报废不过滤 is_approval_required —— 无论物料是否命中该标记,
|
||||||
|
# 所有报废申请都必须由指定审批人审批通过后才能执行。
|
||||||
|
# 前端 apply/index.vue 的「审批人」必填项与此规则保持一致。
|
||||||
|
# =============================================================================
|
||||||
|
SCRAP_ALWAYS_REQUIRES_APPROVAL = True
|
||||||
|
|
||||||
|
|
||||||
STOCK_MODELS = {}
|
STOCK_MODELS = {}
|
||||||
|
|
||||||
|
|
||||||
@ -101,21 +111,19 @@ class ScrapApprovalService:
|
|||||||
'available_at_apply': avail,
|
'available_at_apply': avail,
|
||||||
})
|
})
|
||||||
|
|
||||||
# ★ 是否需审批:命中需审批物料 或 库管代建(force_approval)
|
# ★ 报废一律需审批(见 SCRAP_ALWAYS_REQUIRES_APPROVAL)。
|
||||||
|
# resolve_approval_control 仍调用,但仅用于生成「哪些物料命中需审批」的提示文案,
|
||||||
|
# 不再用它决定是否需要审批。
|
||||||
from app.services.approval_control import resolve_approval_control
|
from app.services.approval_control import resolve_approval_control
|
||||||
need_approval, flagged_materials = resolve_approval_control(normalized)
|
_, flagged_materials = resolve_approval_control(normalized)
|
||||||
if force_approval:
|
|
||||||
need_approval = True
|
|
||||||
if need_approval and not approver_id:
|
|
||||||
if force_approval:
|
|
||||||
raise ValueError("库管代建报废申请必须选择审批人后再提交")
|
|
||||||
_names = ";".join(f"{m['name']}({m['spec_model'] or '-'})" for m in flagged_materials)
|
|
||||||
raise ValueError(f"以下物料需审批报废:{_names}。请选择审批人后再提交")
|
|
||||||
|
|
||||||
if approver_id:
|
if not approver_id:
|
||||||
allowed_approvers = [{"type": "user", "value": int(approver_id)}]
|
if flagged_materials:
|
||||||
elif not need_approval:
|
_names = ";".join(f"{m['name']}({m['spec_model'] or '-'})" for m in flagged_materials)
|
||||||
allowed_approvers = []
|
raise ValueError(f"以下物料需审批报废:{_names}。请选择审批人后再提交")
|
||||||
|
raise ValueError("报废申请必须选择审批人后再提交")
|
||||||
|
|
||||||
|
allowed_approvers = [{"type": "user", "value": int(approver_id)}]
|
||||||
|
|
||||||
req = ScrapApproval(
|
req = ScrapApproval(
|
||||||
request_no=ScrapApprovalService.generate_request_no(),
|
request_no=ScrapApprovalService.generate_request_no(),
|
||||||
@ -125,17 +133,12 @@ class ScrapApprovalService:
|
|||||||
req.set_items(normalized)
|
req.set_items(normalized)
|
||||||
req.set_allowed_approvers(allowed_approvers)
|
req.set_allowed_approvers(allowed_approvers)
|
||||||
|
|
||||||
if need_approval:
|
# ★ 恒为「待审批」,不再走免审批自动通过分支
|
||||||
req.status = 0 # 待审批
|
req.status = 0
|
||||||
else:
|
|
||||||
# 默认不审批:直接进入“待执行”
|
|
||||||
req.status = 1
|
|
||||||
req.actual_approver_id = applicant_id
|
|
||||||
req.approved_at = _beijing()
|
|
||||||
|
|
||||||
db.session.add(req)
|
db.session.add(req)
|
||||||
db.session.commit()
|
db.session.commit()
|
||||||
logger.info(f"[ScrapApproval] 提交成功 {req.request_no} need_approval={need_approval}")
|
logger.info(f"[ScrapApproval] 提交成功 {req.request_no} approver={approver_id}")
|
||||||
return req
|
return req
|
||||||
|
|
||||||
# ------------------------------------------------------------------
|
# ------------------------------------------------------------------
|
||||||
|
|||||||
Reference in New Issue
Block a user