# 出库与配送闭环 · 最终方案(2026-09-19 定稿) > 本文为**唯一版本**,覆盖此前所有草稿;外部建议(DeepSeek 两轮)**仅作参考**,逐条给了采纳/否决理由。 > 全部结论**基于真实代码逐行核实**,位置标注 `文件:行`。 > **本文是方案,未改任何代码。全部改动需授权后执行。** --- ## 0. 结论先行 1. **现状不是"缺功能",而是"同一份料的库存被扣两次、同一个字段被两个语义写"。** 这是数据正确性缺陷,必须先于补功能修 —— 否则功能越全,账越乱。 2. 外部方案描述的「四条链路」**约 70% 已在代码里跑着**(工单物料台账、AGV 任务、接料确认、退库、手动出库、接驳台主数据、叫料、分批出库、工位级拆单、工序-物料绑定**全部已实现**)。照它重建会推翻现有可用结构。 3. **唯一真空白**:数量不符的**处理侧**(上报有、处理无)。已确认 `material_qty_report.resolved_at` 是**从未被写入的死字段**。 4. **1 个真 bug**:叫料单不写工位号 → 工位终端「待接料」查不到自己叫的料(本轮逐行复核再次确认)。 5. 改进方向:**修 1 处重复扣减、拆 2 个被污染的字段、补 1 处空白、修 1 个 bug、语义化 1 个枚举**。 6. **已定调**:库存扣减走 **A(出库扣、反冲只核销)**。但 A 有三个前提必须先落,否则会从"双扣"变成"漏扣"(§3.2)。 7. **已否决**:在 MES 建「线边库存」实体表(客户从未提出、可完全派生、会引出第三本账)。 --- ## 1. 根因:三个"唯一性"缺失 ### 1.1 `inventories.quantity` 的全部写入点(事实核查) | # | 位置 | 触发动作 | 上限校验 | |---|---|---|---| | 1 | `bj_power_wms/internal/handler/outbound.go:107` 工单出库 | 仓管发料 | ✅ 409 | | 2 | `bj_power_wms/internal/handler/outbound.go:436` 通用出库 | 仓管发料 | ✅ 409 | | 3 | `bj_power_wms/internal/handler/outbound.go:deductStockHandler`(`/api/internal/stock/deduct`) | **MES 备料自动出库** | ✅ 409 | | 4 | `bj_power_wms/internal/handler/backflush.go:192` `fifoConsume` | **报工反冲** | ❌ **无任何上限** | **第 3 条是关键**:MES `logic/material.go:231` 的备料自动出库走的正是 `/api/internal/stock/deduct`,**已经扣过一次库存**;随后 MES 报工 `logic/workpiece.go:379` 调 `/api/internal/material/backflush`,**同一份料又被 `fifoConsume` 扣一次**。 > **料只出一次门,库存扣两次。** 且反冲按 FIFO 从"当前在库任意合格批次"扣,不认工位实领的那批 —— 领料后若有新批次入库,还会扣错批。 ### 1.2 `out_qty` / `sent_qty` 的多写者 **WMS `order_material_ledger.out_qty`(字段注释"累计已出库数量")有 3 个写者,其中 1 个语义完全不同:** | 写者 | 语义 | 校验 | |---|---|---| | `outbound.go` 工单出库 | 已发出 | ✅ | | `outbound.go:deductStockHandler` | 已发出 | ✅ | | `backflush.go:100` `newOut := ledger.OutQty + consumed` | **已消耗** | ❌ 无上限 → 可 > `total_qty` | **MES `material_request.sent_qty` 有 2 个写者,语义互斥:** | 写者 | 语义 | 后果 | |---|---|---| | `logic/material.go:241` `AddSentQty(sent)`(自动出库) | 已发出 | 同时是 `:192 need = ReqQty − SentQty` 的幂等基准 | | `logic/material.go:383` `SetSentQty(newSent)`(接料) | **已接料** | 把 sent_qty 顶到 ReqQty → **`need` 恒为 0 → 后续补发永久失效**;且 `add<=0` 时算出的实数不落库 → **实收量根本没记** | ### 1.3 三处结构性缺陷 | # | 缺陷 | 证据 | |---|---|---| | A | **反冲被实现成了"第二次出库"** | `backflush.go:63` 创建 `outbound_type=backflush` 出库单 + `:192` 扣库存 + `:100` 累加 `out_qty`。已核实**该出库单无任何消费方**(列表/导出按 type 过滤,导出 `outbound.go:641` 只认 `workorder`,反冲单被归入"通用出库")→ 纯账目污染 | | B | **退库不回收缺口** | `return_order.go:59/72` 建单 PENDING → `:137` 收货 RECEIVED → `:158` `AddQuantity(ro.Qty)` 回库存,**但不回写台账**。来源确认是**工位退料**(MES `logic/station_ops.go:128` 调 `wmsclient.CreateReturnOrder`,注释「工位退料回库房」) | | C | **台账缺字段** | `schema/order_material_ledger.go` 仅有 `total_qty / out_qty / status / target_station / completed_at / remark`,**无 `consumed_qty`、无 `returned_qty`** → 消耗与退回无处安放,只能挤进 `out_qty` | ### 1.4 "退库 → 补发"死锁(本轮新发现,外部建议与上一版方案均未点到) `outbound.go` 与 `deductStockHandler` 的超量判定都是 `newOutQty > TotalQty → 409`,而退库不回写台账: | 步骤 | total_qty | out_qty | 结果 | |---|---|---|---| | 出库 100 | 100 | 100 | 领料完结 | | 工位退料 20(多发) | 100 | 100 | 库存 +20,**台账缺口仍为 0** | | 工位再叫料 20 | 100 | **120 > 100** | **409 拒绝 → 补料永远发不出去** | → 必须引入 `returned_qty`,缺口与完结判定改**净额**。 ### 1.5 其他 - 补料与备料**同表同状态无来源标记**(无 `source`),AGV 待发料列表无法按"工位急件优先"排序。 - 接料**实收量不落库**,与"数量不符"无法自动对账。 - 出库单**无 `need_agv`**,分不清"这批走没走车"。 - 数据现状(硬证据,查库):`order_material_ledgers` **0 行**、`outbound_orders` **0 行** → 隐患**尚未触发**,库里无坏数据。属"上线即坏",非"已经坏了"。 --- ## 2. 目标态:三条铁律 ### 铁律 1 —— 一次物理位移 = 一次库存账变 `inventories.quantity` **只在两处变**:出库(−)、退库收货(+)。**消耗不动库存。** ### 铁律 2 —— 一个字段只有一个写者 | 字段 | 唯一写者 | 语义 | |---|---|---| | `inventories.quantity` | 出库单 / 退库收货 | 仓库实物 | | `order_material_ledger.out_qty` | 出库(含自动出库) | 已发出 | | `order_material_ledger.returned_qty` 🆕 | 退库收货(仅挂工单的) | 已退回 | | `order_material_ledger.consumed_qty` 🆕 | 报工反冲 | 已消耗 | | `material_request.sent_qty` | 自动出库 | 已发出 | | `material_request.received_qty` 🆕 | 工位接料 | 已接料 | ### 铁律 3 —— 不新增账本,在途/线边全部派生 ``` 缺口 gap = total_qty − (out_qty − returned_qty) ← 净额,修 1.4 死锁 在途 in_transit = out_qty − received_qty 线边 line_side = received_qty − consumed_qty − 工位退回量 恒等式:入库总量 = 在库 + 在途 + 线边 + 已消耗 + 退库回仓 + 盘点差异 ``` **派生依据全部现成**:在途 → `agv_task`(已有 `DISPATCHED/DELIVERING/DONE` 与 `arrived_at`);线边 → `material_request`;已消耗 → `ledger.consumed_qty`。**不需要新表。** ### 四时点口径表(对外沟通用) | 时点 | 触发 | 写什么 | 动库存 | |---|---|---|---| | T1 出库提交 | 仓管 / MES 自动出库 | `ledger.out_qty += n`、`mr.sent_qty += n` | ✅ **唯一减点** | | T2 AGV 送达 | RCS 回调 | `agv_task.status / arrived_at` | ❌ | | T3 工位接料 | 工位终端扫码 | `mr.received_qty += n` | ❌ | | T4 报工消耗 | MES 报工 | `ledger.consumed_qty += n` | ❌ | | T5 退库收货 | WMS 退库确认 | `ledger.returned_qty += n` | ✅ 加点 | --- ## 3. 方案 ### 3.1 P0(必须先修)—— 数据正确性:不改,库存与台账本身就是错的 #### 0-1 反冲停扣库存、停写 `out_qty`、停产反冲出库单 **文件**:`bj_power_wms/internal/handler/backflush.go` - 删除 `:63-77` 反冲出库单创建(**已核实无任何消费方,删除零影响**);`outboundNo` 随之去掉。 - 删除 `:86` 对 `fifoConsume` 的调用;`:145` `fifoConsume` 整体删除;其中写 `inventories`(`:174` `SetStatus("出库")`、`:192` `AddQuantity(-take)`)与 `OutboundDetail.Create`(`:180/:200`)的分支全部删除。 - 台账改写新字段:`SetConsumedQty(ledger.ConsumedQty + n)`;完结判定改净额(见 0-2)。 - **新增核销上限**:`可核销量 = (out_qty − returned_qty) − consumed_qty`,实际核销 `min(应核销, 可核销量)`;超出部分**不写台账**,落差异记录 + 预警(`material_over_consume`),**不阻断报工**(生产线不能被系统卡死)。响应体保留 `consumed`(实际核销)与 `shortfall`(未核销额)。 #### 0-2 台账加两列 + 退库回收缺口 **文件**:`bj_power_wms/schema/order_material_ledger.go`、`internal/handler/return_order.go`、`internal/db/db.go` - schema 加 `consumed_qty`(默认 0,「累计已消耗数量(报工反冲)」)、`returned_qty`(默认 0,「累计已退回数量」)。 - ent 重生成:`cd bj_power_wms && go run -tags entgenerate ./tools`。 - `internal/db/db.go` 的 `applyColumnPatches` 登记两列(**带默认值 → 无需进 `applyPreMigratePatches`**,不阻断启动)。 - `return_order.go` 收货(`:137` RECEIVED)事务内:若退库单**挂工单** → `ledger.returned_qty += qty`;不挂工单(仓库自身退料)不动台账。 #### 0-3 所有超量判定改净额 **文件**:`bj_power_wms/internal/handler/outbound.go` - `deductStockHandler` 与工单出库的 409 判定:`newOutQty > TotalQty` → `newOutQty > TotalQty + ReturnedQty`。 - 「领料完结」判定同步改净额。 - 错误文案中文化:`出库数量超过工单需求(需求 X,已退 Y,累计将达 Z)`。 #### 0-4 MES 接料改写 `received_qty` **文件**:`bj_power_mes/schema/material_request.go`、`internal/logic/material.go:366-395` - schema 加 `received_qty`(默认 0)、`received_at`(可选)、`receive_by`(可选)。 - `ReceiveMaterialRequest`:`:383-387` 改为 `SetReceivedQty + SetReceivedAt + SetReceiveBy(operator)`,**删掉 `SetSentQty`**;`add<=0` 的兜底改用 `ReqQty − ReceivedQty` 计算。 - `sent_qty` 语义**锁定为「已发出」**(唯一写者 = 自动出库 `:241`),`need = ReqQty − SentQty` 保持成立。 - 「DONE」推进改由 `received_qty >= req_qty` 触发。 - MES 侧 `internal/db/db.go` 的 `schemaPatchSQL` 登记三个新列。 #### 0-5 出库类型语义化 - `outbound_type` 最终三值:`workorder`(备料/工单领料)/ `refill`(补料)/ `general`(手动出库),移除 `backflush`。 - 导出与列表类型文案同步(`outbound.go:641` 的 `if ob.OutboundType == "workorder"` 分支补 `refill`)。 **P0 验收**:连续跑「备料自动出库 → 报工反冲」,`inventories.quantity` 只减一次;`out_qty` 不因反冲变化;`out_qty − returned_qty` 与 `consumed_qty` 均可解释。 ### 3.2 A 方案的三个前提(已采纳 A,但前提不落则变"漏扣") | # | 前提 | 不做的后果 | |---|---|---| | 1 | 反冲核销**有上限** = `net_out_qty − consumed_qty` | 工位自取的料(辅料/胶水/现场替换)被全额核销,但库存从没扣过 → **库存永不下降** | | 2 | **退库必须回收缺口**(`returned_qty`) | §1.4 死锁:退料后补料被 409 永久拦死 | | 3 | 出库扣到**实领批次/SN** | 只解决"扣几次",不解决"扣哪批"(现有出库本就按批次/SN 指定,已满足) | ### 3.3 P1(该做,但不动现有数据)—— 功能空白与真 bug #### 1-1 修 bug:叫料单补工位号(零成本,必做) **文件**:`bj_power_mes/internal/logic/line.go` `CallMaterial` - 已逐行复核:建单只有 `SetRequestNo/OrderNo/PlanDate/MaterialCode/MaterialName/Unit/ManageMode/ReqQty/Status/TargetDock/Operator`,**无 `SetStationNo`、无 `SetProcessCode`** → `station_no=0`。 - 而工位终端「待接料」按 `stationNo=本工位` 查询 → **工位看不见自己叫的料**(扫码接料直查备料单号,不受影响,所以隐蔽)。 - 修法:`SetStationNo(...)` + `SetProcessCode(...)`(按该物料在本工位的装配工序取,取不到则 0)+ `SetSource("REFILL")`。 - 一次性数据修复:历史 `stationNo=0` 的叫料单按 `target_dock → dock.station_no` 映射回填(幂等)。 #### 1-2 补空白:数量不符处理侧 —— **真源留在 MES,处理页也放 MES** **理由(关键,与上一版方案的结论相反)**: - 上报链路、表、预警**全在 MES**:工位终端 `InspectPanel ⑥` → `/api/qty-report` → 代理 MES `/api/internal/station/qty-report` → `logic.CreateQtyReport`(`logic/station_ops.go:64`)落 `material_qty_report` + 预警 `type=qty_diff`。 - 处理动作在 MES 侧可完全闭环:**补发** = 生成 `source=REFILL` 备料单 → 走已有自动出库链路;**退库** = 走已有 `wmsclient.CreateReturnOrder`(MES→WMS 方向已存在);**调整** = 本地记账。 - 仓管本来就在 MES 操作备料(`MaterialRequest.vue` 已含「按日排产生成备料」「一键自动出库」)。 - 放 WMS 虽技术可行(`bj_power_wms/internal/mesclient/client.go` 已具备反向调用能力),但需新增 3 个接口,且补发要绕 `WMS→MES→WMS` 一圈,**没有理由多绕**。 **实施**: - 表:`material_qty_report` 已有 `status`(PENDING/RESOLVED)、`resolved_at`(**当前死字段**)→ 只需补 `handle_type`(REFILL/RETURN/ADJUST)、`handle_note`、`handled_by`、`handle_ref_no`。 - 接口:`GET /api/v1/qty-diffs`(状态/工位/物料/时间筛选 + **后端 SQL 真分页**)、`GET /api/v1/qty-diffs/:id`(明细,配 `el-drawer` 懒加载)、`POST /api/v1/qty-diffs/:id/handle`。 - 处理分支: - `REFILL` → 生成 `source=REFILL` 备料单(可走自动出库)→ 回写 `handle_ref_no` - `RETURN` → `wmsclient.CreateReturnOrder` → 回写退库单号 - `ADJUST` → 仅记账关单 - 照片:走**统一附件体系** `biz_type=QTY_DIFF` + `biz_id=差异记录ID`,**不加列**。 - 权限:新增 `produce.qtydiff:view` / `produce.qtydiff:handle`,**同时登记进 `logic/seed.go` 的 menus 与 buttons** 及 `handler/perm.go` 的 `pathPermMap`(本项目栽过一次"菜单码未登记 → 任何角色永远授不了权")。 - 前端:MES 新增「数量不符处理」页 + `MainLayout` 菜单 + `help.js`。 - **客户 DOC 同步**:`seed.go:165 migrateLegacyQtyReport` 留的「明细由 WMS 处理」改为「在 MES 数量不符处理页处理」。 #### 1-3 补料来源标记 - `material_request` 加 `source`(默认 `PLAN`);叫料与差异补发写 `REFILL`。 - `AutoOutboundRequests` 与 WMS 待发料列表按 `source=REFILL` 优先排序(**不新建单据类型、不新建优先级字段**)。 - 补料出库默认叫 AGV(沿用现有 AGV 下发条件,不新增 `need_agv` 字段)。 #### 1-4 只读「配送进度」聚合 - **不给出库单加五态**(状态分散在 `agv_task` / `material_request`,硬塞会互斗)。改一条只读接口按出库单号聚合:出库单(已出库)+ AGV 任务(配送中/已送达)+ 备料单(已接料/完结)。 ### 3.4 P2(可有可无)—— 可选增强 | 项 | 说明 | |---|---| | 「物料去向」只读对账页 | 按物料展示 在库/在途/线边/已消耗,直接验 §2 恒等式 | | 在途对账预警 | `out_qty − received_qty` 长期 > 0(滞留在途)或 < 0(未发先接)→ 预警 | | 消耗超接料预警 | `consumed_qty > received_qty` → 提示"未接料先消耗",不阻断 | --- ## 4. 明确不做(含理由) | 不做 | 理由 | |---|---| | **MES「线边库存」实体表** | ① 客户《软件系统问题-hd.doc》全文检索「线边/工位库存/工位上的料/余料」**命中 0**,只提过仓库盘点(第 102 段)→ 为未提出的需求建表即过度设计;② 会在 WMS 库存 + WMS 台账 + MES 备料单 + MES 线边**四处同步**,本项目已栽过一次跨系统对不上;③ 线边量可由 `received_qty − consumed_qty` **完全派生**。**唯一例外**:客户明确要求"盘工位上的料"时再建,并接受同步成本(列入待确认) | | **出库单五态**(待出库→已出库→已送达→已接料→已完成) | 状态分散在 `agv_task` / `material_request`,硬塞进出库单会产生三份互相打架的状态副本。改只读聚合(1-4) | | **新建 `difference_report` 表** | `material_qty_report` 已存在且字段齐全(含 `resolved_at` 死字段),补 4 列即可,不造同义表 | | **双写两份账本** | 违反单一真源。`sent_qty` 的教训已经很清楚 | | **接料"可选"、AGV"可选"** | 接料确认是消耗核销的唯一依据,保持强校验 | | **补料另建单据类型** | 用 `source` 字段标记即可,避免两套单据各自维护缺口 | | **OA 对接** | 客户已定:永久不做,不再提 | --- ## 5. 影响面与风险 | 改动 | 影响 | 处置 | |---|---|---| | 反冲单不再产生 | 出库列表/导出少掉 `backflush` 类单据 | **已核实无消费方**;导出文案补 `refill` | | 反冲不再扣库存 | 若存在"从未出库的料被消耗"(工位自取/辅料) | 由 0-1 的**核销上限**兜住:超出部分落差异+预警,不写台账、不阻断报工 | | `sent_qty` 语义锁定 | MES 页面/看板若把 `sent_qty` 当"已接料"展示 | 一并改为读 `received_qty` | | 台账新增两列 | 存量 **0 行**,无回填 | 直接加列 | | `material_request` 新增列 | 存量可能有数据 | 均带默认值,安全 | --- ## 6. 验收标准 1. **库存账**:`备料自动出库 → 报工反冲` 连续两轮,`inventories.quantity` 只在出库时减一次;反冲前后库存不变。 2. **台账账**:`out_qty` 只由出库写;反冲只增 `consumed_qty`;超量报工不使 `out_qty > total_qty`。 3. **死锁解除**:出库满额 → 工位退料 → 再补料,**不再被 409 拦住**。 4. **接料账**:`received_qty` 落库;接料后 `need = ReqQty − SentQty` 不被污染,补发仍可执行。 5. **叫料 bug**:工位终端叫料后,该工位「待接料」列表**能看到自己叫的料**。 6. **数量不符闭环**:工位上报 → 处理页可见 → 补发/退库/调整 → 差异关闭,生成单据可追溯。 7. **负向验证**:非 admin 角色在缺权限时看不到/点不动处理按钮(防"菜单码未登记"老坑)。 --- ## 7. 待确认 1. **`refill` 补料是否允许超出工单 BOM 需求**?现规则是硬 409(含 0-3 净额修正)。补料语义上可能确实需要超额(损耗/报废),那就必须走"数量不符/额外需求"通道而非工单领料通道 —— **需业务确认**。本方案默认仍受净额上限约束。 2. **是否要求「工位线边盘点」**?要 → 建 `line_stock` 实体表并接受四处同步成本;不要 → 线边量走派生视图(**默认**)。 3. **数量不符处理页放 MES** 与客户 DOC 原写「明细由 WMS 处理」不一致 → 建议改 DOC(理由见 1-2)。 4. **`outbound_type` 由三值变四值**(新增 `refill`)是否影响历史统计口径(现存量 `workorder` 中是否混有旧叫料出库)。 5. **环境依赖**:AGV 真机联调需海康 RCS 地址与 appKey/appSecret(现 `Agv.EnableMock: true`);附件归档需外部备份路径(现 `ArchiveDir` 为空)。 --- ## 附:实施顺序(一次性做完,不分阶段交付) ``` 【编号说明】P0=必须先修(P0-1..P0-6);P1=该做(P1-1..P1-4);P2=可选。 P0-1 WMS schema: ledger 加 consumed_qty / returned_qty → ent 重生成 → applyColumnPatches 登记 P0-2 MES schema: material_request 加 received_qty/received_at/receive_by → ent 重生成 → schemaPatchSQL 登记 P0-3 WMS backflush.go: 删出库单 + 删 fifoConsume + 改 consumed_qty + 加核销上限 P0-4 WMS return_order.go: 收货回写 returned_qty P0-5 WMS outbound.go: 两处 409 与完结判定改净额 P0-6 MES material.go: ReceiveMaterialRequest 改写 received_qty P1-1 MES line.go: CallMaterial 补 station_no/process_code/source + 历史回填 P1-2 MES: material_qty_report 加 4 列 + 处理接口(真分页) + 权限(菜单+按钮) + 页面 + help.js P1-3 MES: material_request 加 source + 排序优先 P1-4 MES: 只读配送进度聚合接口 P2 只读「物料去向」对账页 + 两条对账预警(可选) DOC 同步客户问题 DOC;改「明细由 WMS 处理」措辞 ``` --- ## 增强⑧「物料去向对账」最优方案(2026-09-19 定稿,待授权实施) ### 0. 结论 1. **对账后端已经 100% 存在**:上一轮实施时顺手落了 `GET /api/v1/material-requests/progress`(MES 聚合 WMS `ledger/progress` + 本地已接料),返回字段正是对账表全部列——需求/已发出/已退回/已接料/已消耗/在途/线边/待发缺口/台账状态。备料单页已有「配送进度」弹窗在读它。 2. DeepSeek 方案**定位对(只读/不建表/不做自动修正),但四个硬伤**:①"差异=需求−已出、非0标红"是错的(在产工单缺口天然非0,照做满屏假红);②示例第3行自己算错符号;③需求总量重算 BOM 是重复建设(total_qty 已随台账同步,重算必与备料时点不一致);④工位余公式漏了退库。 3. 剩余工作量 = **纯前端**:把已有进度弹窗升级为对账视图(异常标记 + 明细展开 + 导出)。后端 0 行新代码(明细过滤 `/event-logs` 已支持 orderNo+eventType)。 ### 1. 公式(纠正版,与后端派生完全一致) ``` 待发缺口 gap = totalQty − (outQty − returnedQty) ← 生产中非0是常态,不标红 在途 inTransit = outQty − receivedQty 工位余 lineSide = receivedQty − consumedQty − returnedQty ← DeepSeek 漏了退库 ``` 恒等式:入库总量 = 在库 + 在途 + 工位余 + 已消耗 + 退库回仓 + 盘点差异 ### 2. 异常判定(三条硬异常,全部可从现有字段判定,前端现算) | 异常 | 判定 | 含义 | 标记 | |---|---|---|---| | 超领 | netOut > totalQty | 实领超过 BOM(超领原因在 WMS 留痕 material.over) | 红 | | 送达未接 | inTransit > 0 且该工单物料存在 status=已送达 的出库单 | 料到了工位没人扫码确认 | 红 | | 完结剩料 | 台账状态=领料完结 且 lineSide > 0 | 工单做完了工位还压着料,没消耗也没退库 | 红 | | 在途等待 | inTransit > 0 且无已送达出库单 | AGV 正在送,正常 | 黄(提醒) | ### 3. 页面形态(升级既有弹窗,不新建菜单) - 位置:MES 备料单页「配送进度」弹窗 → 改名「物料去向对账」。 - 列:现有 9 列 + 「异常」列(红/黄/无,hover 显示判定原因)。 - 行展开明细(全部现成数据,无后端改动): - 出库明细 → 行内备料单已含出库单号;超领原因在 WMS 出库记录页(按工单筛)查看,不跨系统重复建设; - 接料明细 → material_request 行(receivedQty/receiveBy/receivedAt); - 消耗明细 → `/event-logs?orderNo=xxx&eventType=material.backflush`(含工位/数量/缺口)。 - 导出:xlsx,复用 exportXlsxFetch。 - 权限:沿用 produce.material(只读,不新增权限码)。 ### 4. 明确不做 - 不重算 BOM 需求量(读 total_qty); - 不做"差异=需求−已出"列与标红(就是 gap 列,完结后必为 0,出现非 0 即数据错,属可发现的脏数据而非业务异常); - 不做跨系统明细钻取(WMS 出库/核销明细由 WMS 出库记录页承担,已可按工单筛选); - 不做自动对账修正、不建新表、不加权限码。 ### 5. 工作量与顺序 前端单文件(MaterialRequest.vue)+ help.js 一条说明,约半天。排在"①~⑦ 编译验证"之后做。