Files
KCGL/inventory-backend
yueli a910a6ea72 feat(bom): 后端承接 BOM 库存分配,消除多批次物料只能加 1 件的缺陷
问题现象
--------
BOM 选单中物料显示需求 10、聚合可用 839,加入购物车却只剩 1 件,
并提示库存不足。

根因
----
两个接口口径不一致:
  · GET  /bom/stock/<bom_no>      按 base_id 聚合 → current_stock=839
  · POST /outbound/bom-match-stock 不聚合,每批次一行 → 某行只有 1
前端用 stockList.find(s => s.base_id == child_id) 只取第一条库存行,
若首行恰好只剩 1 件,需求量又被 Math.min 压到 1,现象即如此。

为何必须放在后端
----------------
前端 stockList 由多个入口写入(手动选单/搜索/BOM),随时可能被覆盖;
且 base_id 与 stock_id 的类型差异会让匹配静默落空,表现同样是「库存不足」。
更关键的是:分配需要「该 base_id 全部可用库存行」的完整视图,
而这必须与出库扣减(create_outbound_batch 按 stock_id 逐行加锁扣减)
使用同一份数据源。

改动
----
bom-match-stock 新增分配模式:
  请求 { requirements: [{base_id, required_qty, name, spec_model}] }
  响应 { items: [...已分配行], shortages: [...缺料明细] }

_allocate_bom_requirements() 在 DB 层完成:
  1. 三张库存表按 base_id 一次性取全部 available_quantity > 0 的行
     (带公司隔离,join base 取名称规格);
  2. 可用量降序排序 —— 优先进大行,减少购物车拆分行数;
  3. 逐物料扣减 required_qty,产出真实 stock_id + source_table + allocated_qty;
  4. 分配不足记录 shortage 但不阻断其它物料。
  每行仍携带 uniqueKey,前端可直接入购物车。
  返回前剥离价格成本字段(Fail-Closed)。

旧查询模式(child_ids)保留,兼容未改造的调用方。

顺带修复一处静默失败
--------------------
查询块的 except 原为直接 continue,会把 NameError 等错误吞成「该物料无库存」。
改为 logger.error 输出,避免同类问题再次以业务结论的形式出现。

实测(base_id=2405,聚合 841,需 10):分配 1 行 stock_id=1961 分配 10,无短缺;
base_id=2963(9 行各 1),需 5 → 5 行各 1 合计 5;
需 20(聚合仅 9)→ 9 行合计 9,短缺 11。
2026-09-10 14:16:47 +08:00
..
2026-01-26 13:47:53 +08:00
2026-05-12 15:17:42 +08:00
2026-02-02 15:06:20 +08:00