Files
bj_power/临时.md
T
SunYF 05b0b8a909 feat: 完成客户第12条需求+多系统改造
1. 新增预警表添加工单号字段,支持按工单号检索预警
2. 添加工单过滤在制品/绩效报表/预警中心查询
3. 补全WMS台账字段口径与权限配置
4. 修复备料单与数量不符处理流程
5. 新增配送进度/库位联动/自动出库功能
6. 修复AGV配送状态更新逻辑
7. 优化退料与库存管理细节
2026-09-19 17:04:58 +08:00

336 lines
23 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 出库与配送闭环 · 最终方案(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 一条说明,约半天。排在"①~⑦ 编译验证"之后做。