fix(outbound): 修复扫码出库 500 —— applicant_id 用了尚未赋值的 approval

现象
----
所有扫码出库报 500(前端 create.vue 提交即失败)。

根因
----
上一个提交把 'applicant_id': approval.applicant_id 写进了 common_data,
但 common_data 构造在第 234 行,而 approval 直到第 269 行(「强制按单出库」
那一段)才查询赋值 —— 典型的变量先用后赋,直接 UnboundLocalError。
它影响的是**每一条**出库请求,属于必然复现而非偶发。

修复
----
把赋值移到 approval 取出并校验**之后**:common_data['applicant_id'] = ...
并在原处留注释说明为什么不能写在字典字面量里,避免后人搬回去。

★ 我的测试为什么没抓到
  上一轮只测了「退回 → 补发」这条路径,而 applicant_id 的写入在**扫码出库**
  路径上 —— 两条路径不重合,所以漏了。本次补测了完整的
  「出库申请(预占) → 扫码出库(扣减)」链路。

验证(6 项断言全通过,走真实接口链路)
  申请单创建(status=1,申请人=12,预占 stock_buy#2058 两件)
    → 扫码出库不再抛 500
    → 出库明细生成且 applicant_id = 12(不再是 NULL)
    → consumer_name 照常写入
    → 审批单置为已完成
    → 库存实际扣减 2
  清理后库存与数据零残留。

  ★ 顺带在真实数据上得到印证:生产库中已有一笔由业务方重试成功的出库
    (OUT-20260917-1333-0001),其 applicant_id 正确等于所关联申请单的申请人。
This commit is contained in:
yueli
2026-09-17 13:35:47 +08:00
parent 07567d2f76
commit 191724a176

View File

@ -238,10 +238,9 @@ class OutboundService:
'signature_path': data.get('signature_path'),
'operator_name': operator_name,
'remark': data.get('remark'),
# ★ 申请人从**关联审批单**带出。落这一列是为了让后续「退回 → 补发」
# 能把补发单挂回真正该拿东西的人名下 —— consumer_name 是自由文本
# (可能是客户名),不能作为依据
'applicant_id': approval.applicant_id,
# ⚠ applicant_id 不在这里赋值 —— 此刻 approval 尚未查询(见下方
# 第 2 步「强制按单出库」),在这里引用它会直接 UnboundLocalError。
# 待 approval 取出并校验后再补进本字典
}
beijing_tz = timezone(timedelta(hours=8))
@ -279,6 +278,13 @@ class OutboundService:
f"仅已通过的审批单方可执行出库"
)
# ★ 申请人从**关联审批单**带出。落这一列是为了让后续「退回 → 补发」能把
# 补发单挂回真正该拿东西的人名下 —— consumer_name 是扫码时自由填写的
# 领用人/客户名,不能作为依据。
# ⚠ 必须放在 approval 取出并校验**之后**common_data 在上面就已构造,
# 在那里引用 approval 会直接 UnboundLocalError上线时踩过
common_data['applicant_id'] = approval.applicant_id
model_map = {
'stock_buy': StockBuy,
'stock_semi': StockSemi,