feat(scrap): 报废执行改为按单扫码校验,废弃盲执行

execute() 原先只吃 request_id,按申请单快照全量扣减,用户反馈「扫码与报废脱节」。
现改为接收实扫明细 scanned_items:

  · 强制按单:未提交实扫明细一律拒绝执行;
  · 键为 (source_table, stock_id),与申请单 items_json 同口径,
    同一物品多次扫码自动累加,兼容「逐件扫」与「输数量」两种操作;
  · 校验实扫项必须落在批准明细内,且累计量不得超批准量,否则整单拒绝;
  · 允许合法子集(少扫 = 本次不报废该行);
  · 扣减以实扫量为准,同时扣 available_quantity 与 stock_quantity
    (报废即实物销毁,上一版只扣可用库存会让总库存虚挂)。

接口 POST /scrap/request/<id>/execute 由无 body 改为接收 {items:[...]}。
This commit is contained in:
yueli
2026-09-10 10:14:40 +08:00
parent b4b79c68a9
commit 7719943779
2 changed files with 108 additions and 21 deletions

View File

@ -610,12 +610,22 @@ def approve_scrap_request(request_id):
@jwt_required()
@permission_required('scrap_execute')
def execute_scrap_request(request_id):
"""按单报废:有 scrap_execute 权限者执行,扣减实物库存并写报废流水"""
"""
按单报废:有 scrap_execute 权限者执行,扣减实物库存并写报废流水。
Body: { "items": [{"source_table", "stock_id", "quantity", "name"/"sku"(可选)}, ...] }
实扫明细必须是该申请单批准明细的子集,且累计数量不得超过批准数量。
"""
try:
from app.services.scrap_approval_service import ScrapApprovalService
identity = int(get_jwt_identity())
u = SysUser.query.get(identity)
req = ScrapApprovalService.execute(request_id, operator_name=(u.username if u else str(identity)))
data = request.get_json(silent=True) or {}
req = ScrapApprovalService.execute(
request_id,
operator_name=(u.username if u else str(identity)),
scanned_items=data.get('items') or [],
)
return jsonify({'code': 200, 'msg': '已执行报废并扣减库存', 'data': req.to_dict()}), 200
except ValueError as e:
return jsonify({'code': 400, 'msg': str(e)}), 400