# 原材料库存批次驱动的生产工单与入库闭环设计

## 背景

当前 ERP 已经保留“工单台账”，小程序报工也已经能按生产相关标识回写 ERP。用户希望现场员工不要再理解复杂的“生产工单号”概念，而是围绕“材料库存批次号”报工：员工只要知道这批产品用的是哪一批材料，系统就能追溯到材料入库成本、成品入库、发货和利润。

本设计保留 ERP 内部的生产工单台账，但把生产执行入口和小程序选择语义调整为“材料库存批次号驱动”。ERP 工单继续承担生产过程容器的职责，负责承接领料、报工、成品入库、余料入库、废料入库和结单。

## 目标

- 原材料库存批次号改为原材料库全库统一递增，不再按物料分别递增。
- 原材料库存批次号默认格式为 `YL0001`，前缀从“系统拓展”配置，不配置默认 `YL`。
- 批次数字不足 4 位补 0，超过 `9999` 后自然扩展为 5 位及以上，例如 `YL9999 -> YL10000`。
- 原材料库“生产出库”强制一次只能选择一个原材料库存批次。
- 生产工单号改为 `YYYYMMDD-库存批次号-产品名称`，例如 `20260607-YL0001-钢制碗`。
- 同一天、同一个库存批次号、同一个产品的生产出库合并为同一张未结单工单。
- 小程序原“工单号”业务语义调整为“材料库存批次号”，员工按材料库存批次号报工。
- 嘉恒仓库“流水”按钮旁新增“生产工单入库”入口，统一处理成品入库、生产余料入库、生产废料入库。
- “生产工单入库”中系统计算理论成品、理论余料、理论废料作为辅助默认值，用户可修改。
- 当用户修改余料或废料后，与理论值偏差超过上下 15%，必须弹窗填写说明，但不禁止入库。
- “生产工单入库”不默认结单。只有用户主动勾选“本次入库后结单”才结单，并且提交前必须二次确认。

## 非目标

- 不取消工单台账。
- 不把 ERP 内部所有生产关系改成只靠字符串批次号追溯，核心追溯仍依赖数据库主键和外键关系。
- 不在初版强制限制理论成品、理论余料、理论废料的入库上限，只做偏差说明。
- 不在初版重构所有历史成品批次号、废料批次号、半成品批次号的编号规则。
- 不在初版做复杂的多原材料批次混合生产。生产出库阶段先强制单原材料库存批次。

## 编号规则

### 原材料库存批次号

新增系统拓展配置项：

```text
配置项名称：原材料库存批次号前缀
配置项编码：RAW_MATERIAL_LOT_PREFIX
默认值：YL
```

生成规则：

```text
批次号 = 前缀 + 序号
```

序号规则：

- 在原材料库范围内统一递增。
- 只统计符合“当前前缀 + 纯数字”的原材料库存批次号。
- 数字不足 4 位时左侧补 0。
- 数字达到 10000 后不截断、不回卷，直接显示完整数字。

示例：

```text
YL0001
YL0002
YL9999
YL10000
YL10001
```

如果后续把前缀从 `YL` 改成 `RM`，则新批次按 `RM0001` 重新从该前缀的最大序号递增。历史 `YL` 批次不改号。

### 生产工单号

生产工单号格式：

```text
YYYYMMDD-库存批次号-产品名称
```

示例：

```text
20260607-YL0001-钢制碗
```

日期按服务器业务时间所在日期生成，使用当前项目部署时区。产品名称做长度截断，保证不超过数据库字段长度。

## 生产出库规则

原材料库“生产出库”从多选库存批次改为单选库存批次。

提交校验：

- 必须选择产品。
- 必须选择且只能选择一个原材料库存批次。
- 选择的库存批次必须属于该产品需规清单默认用料。
- 选择的库存批次必须属于原材料库。
- 选择的库存批次必须可用且质检合格。
- 本次出库重量必须大于 0，且不能超过该批次可用重量。

### 同日同批同产品合并

提交生产出库时，系统按以下条件查找工单：

```text
工单日期 = 当天
产品 = 当前产品
来源原材料库存批次 = 当前选择批次
状态 != 已结单
```

如果找到，则本次生产出库合并到已有工单：

- 追加一条工单领料明细。
- 扣减该库存批次剩余重量。
- 增加工单领料重量。
- 按 BOM 单件毛重重新计算参考计划生产数。
- 工单号不变。

如果未找到，则新建工单：

- 工单号按 `YYYYMMDD-库存批次号-产品名称` 生成。
- 新建工单物料、工序路线。
- 写入工单领料明细。
- 扣减原材料库存批次。

如果同日同批同产品的工单已结单，本次不合并旧工单，直接拦截：

```text
该库存批次今日对应产品的生产工单已结单，不能继续合并生产出库。
```

这样避免结单后再次追加领料，造成闭环记录不可信。

## 小程序报工语义

小程序界面原“工单号”业务语义调整为：

```text
材料库存批次号
```

为降低上线风险，接口字段名可以先保持原字段，例如继续使用小程序现有的 `raw_material_batch_no` 或原“工单号”字段承载值，但页面展示文案和业务解释改为“材料库存批次号”。

小程序报工传回 ERP 时，ERP 后端按以下信息匹配生产工单：

```text
产品
工序
材料库存批次号
未结单状态
```

匹配逻辑：

- 先通过材料库存批次号找到原材料库存批次。
- 再找该产品、该批次关联的未结单生产工单。
- 再按小程序扫码上下文中的工序名称匹配工单工序。
- 匹配成功后报工写入该工单和该工序。
- 匹配失败时返回清晰错误，提示 ERP 还没有为该产品和该材料批次创建生产出库工单。

结单后的工单不再参与小程序待选项，也不再接收小程序报工。

## 生产工单入库入口

在嘉恒仓库页面，“流水”按钮旁新增：

```text
生产工单入库
```

入口跟随嘉恒仓库整体，不放在某一个具体仓库的入库/出库行里。因为这个操作一次会同时影响成品库、原材料库和废料库。

点击后打开较宽抽屉，避免表格和计算字段拥挤。该入口不使用小弹窗承载主流程。

## 生产工单入库交互

第一步选择生产工单：

- 下拉只显示未结单生产工单。
- 显示格式建议为 `工单号 · 产品 · 领料重量kg · 已入库成品数`。
- 支持搜索工单号、产品、材料库存批次号。

选择工单后展示工单摘要：

- 工单号
- 产品名称
- 材料库存批次号
- 领料重量
- 单件毛重
- 单件净重
- 参考计划生产数
- 当前已报工成品数
- 当前已报工废品数
- 已入库成品数
- 已归还余料重量
- 已入库废料重量

用户填写：

- 成品入库数量
- 余料入库重量
- 废料入库重量
- 备注
- 本次入库后结单，默认不勾选

当用户输入“成品入库数量”后，系统自动计算并填入：

- 理论余料入库重量
- 理论废料重量

用户可以直接修改系统填入的值。

## 理论值计算

设：

```text
领料重量 = L
成品入库数量 = Q
产品单件毛重 = G
产品单件净重 = N
当前工单累计报废数量 = S
已归还余料重量 = R
已入库废料重量 = W
```

理论已用材料重量：

```text
Q * G
```

理论本次余料入库重量：

```text
max(L - R - Q * G, 0)
```

理论本次废料重量：

```text
max(Q * (G - N) + S * G - W, 0)
```

说明：

- 初版理论值只作为辅助默认值。
- 不作为硬性库存上限。
- 用户可以自由修改。
- 如果理论值为 0，而用户填写了大于 0 的值，应视为超过 15% 偏差，需要填写说明。

## 15% 偏差说明

当用户提交时，系统比较实际填写值和理论值。

需要校验的字段：

- 余料入库重量
- 废料入库重量

偏差公式：

```text
偏差率 = abs(实际值 - 理论值) / 理论值
```

规则：

- 理论值大于 0，偏差率超过 15%，必须填写说明。
- 理论值等于 0，实际值大于 0，必须填写说明。
- 理论值等于 0，实际值等于 0，不需要说明。

弹窗文案示例：

```text
本次入库数据与理论值存在较大偏差

余料入库：
理论值：80kg
实际值：100kg
偏差：+25%

废料入库：
理论值：12kg
实际值：10kg
偏差：-16.67%

请填写偏差说明后继续入库。
```

用户填写说明后，可以继续提交。说明必须写入相关库存流水和工单备注/扩展记录中，保证后续可查。

## 结单确认

“本次入库后结单”默认不勾选。

只有用户主动勾选后才结单。勾选后提交，必须弹出二次确认框。

确认框展示：

```text
请确认本次生产工单入库

工单：20260607-YL0001-钢制碗
成品入库：xxx 件
余料入库：xxx kg
废料入库：xxx kg
偏差说明：xxx

勾选结单后：
1. 该工单状态将变为已结单
2. 小程序不再显示该工单对应的材料库存批次号
3. 后续不能再对该工单做生产工单入库
4. 该工单对应的成品、余料、废料流水将作为最终闭环记录
```

用户确认后才提交。

## 入库落账

生产工单入库提交后，根据用户填写值分别落账。

### 成品库

如果成品入库数量大于 0：

- 生成成品库生产入库流水。
- 增加成品库存数量。
- 重量作为辅助信息，可按成品数量和单件毛重/净重计算或记录为辅助重量。
- 成品批次必须记录来源原材料库存批次号。
- 成品入库关联生产工单。

### 原材料库

如果余料入库重量大于 0：

- 生成原材料库生产余料入库流水。
- 余料归还到原材料原库存批次，不新建独立余料批次。
- 增加该库存批次剩余重量。
- 流水备注写明“生产余料入库”和偏差说明。
- 更新工单已归还余料重量。

### 废料库

如果废料重量大于 0：

- 生成废料库生产废料入库流水。
- 废料明细记录来源原材料库存批次号。
- 增加废料库存重量。
- 流水备注写明“生产废料入库”和偏差说明。
- 更新工单已入库废料重量。

## 工单台账展示

工单台账保留，并强化以下字段：

- 生产工单号
- 产品名称
- 材料库存批次号
- 领料重量
- 参考计划生产数
- 各工序累计成品报工
- 各工序累计废品报工
- 已入库最终成品数
- 已归还余料重量
- 已入库废料重量
- 状态

状态至少包含：

- 进行中
- 已结单

工单台账中的材料库存批次号应显示短批次号，例如 `YL0001`，并在详情里展示对应原材料名称、规格、单价、来源入库单等追溯信息。

## 数据结构影响

初步涉及以下数据：

- `wh_stock_lot.lot_no`：原材料库存批次号生成规则改变。
- `pp_work_order.work_order_no`：生产工单号生成规则改变。
- `pp_work_order_material_issue.source_lot_id`：继续作为工单和原材料库存批次的核心关联。
- `pp_work_order_material_issue.material_sub_batch_no`：后续语义应等同于来源原材料库存批次号，值保存短批次号。
- `wh_inventory_txn`：继续记录所有库存流水。
- 系统拓展配置表：新增原材料库存批次号前缀配置。

如果现有配置表不适合存储该项，可沿用项目当前“系统拓展”模块已有配置结构，不新增独立配置系统。

## 兼容与历史数据

历史原材料库存批次号不强制立即改号，除非用户明确要求清理测试环境或迁移展示效果。

新入库批次使用新规则。

历史工单号不强制改号。新生产出库产生或合并的工单使用新规则。

小程序同步报工在过渡期应兼容旧值：

- 如果传入值是新材料库存批次号，按新逻辑匹配工单。
- 如果传入值是旧工单短码或旧工单号，可保留兼容一段时间，避免线上历史报工数据无法同步。

正式切换后，小程序展示文案应统一为“材料库存批次号”。

## 错误处理

生产出库常见错误：

- 未选择库存批次：提示“生产出库必须选择一个材料库存批次号”。
- 选择多个库存批次：提示“一次生产出库只能选择一个材料库存批次号”。
- 批次不可用：提示具体批次号和不可用原因。
- 批次不属于产品需规用料：提示“该材料库存批次不属于当前产品需规默认用料”。
- 同日同批同产品工单已结单：提示不能继续合并。

生产工单入库常见错误：

- 未选择工单：提示“请选择生产工单”。
- 工单已结单：提示“该生产工单已结单，不能继续入库”。
- 成品、余料、废料均为 0：提示“本次至少填写一项入库数据”。
- 偏差超过 15% 且无说明：弹窗要求填写说明。

小程序报工常见错误：

- 材料库存批次号不存在。
- 当前产品和材料库存批次没有未结单生产工单。
- 当前工序不属于匹配到的生产工单。

错误提示必须使用中文，不允许暴露英文状态码或内部字段名。

## 测试策略

后端测试：

- 原材料库存批次号默认从 `YL0001` 开始。
- 自定义前缀生效。
- `YL9999` 后生成 `YL10000`。
- 不同物料共用同一个原材料库递增序列。
- 生产出库选择多个库存批次被拒绝。
- 同日同批同产品重复生产出库合并到同一工单。
- 同日同批同产品已结单后再次生产出库被拒绝。
- 生产工单入库能同时写成品库、原材料库、废料库流水。
- 余料/废料偏差超过 15% 时必须填写说明。
- 勾选结单后工单状态变为已结单。
- 未勾选结单时工单保持进行中。

前端测试：

- 原材料库生产出库 UI 只能选择一个材料库存批次号。
- 生产出库提交 payload 只包含一个库存批次。
- 嘉恒仓库流水按钮旁显示“生产工单入库”。
- 生产工单入库选择工单后自动填理论值。
- 修改成品数量后自动重算余料和废料理论值。
- 偏差超过 15% 时弹出说明框。
- 未勾选结单时不弹结单影响确认。
- 勾选结单时弹出结单影响确认。

集成测试：

- 原材料入库生成 `YL0001`。
- 原材料生产出库生成 `20260607-YL0001-产品名称`。
- 小程序报工传 `YL0001` 能同步到对应工单和工序。
- 生产工单入库后，成品批次能追溯到 `YL0001`。
- 销售出库后，发货台账能看到来源原材料库存批次号和材料成本。

## 实施顺序建议

1. 新增系统拓展配置和原材料库存批次号生成规则。
2. 调整采购入库、客料入库、特殊入库等原材料入库路径，统一使用新批次号生成。
3. 调整生产出库为单库存批次，并实现同日同批同产品工单合并。
4. 调整小程序报工同步逻辑，按材料库存批次号匹配未结单工单。
5. 新增生产工单入库后端接口。
6. 新增嘉恒仓库“生产工单入库”前端入口和交互。
7. 联调工单台账、成品入库、余料入库、废料入库、发货追溯。
8. 补齐自动化测试和启动检查。

## 待用户确认后进入计划

本设计已按用户确认规则固定：

- 原材料库存批次号默认格式为 `YL0001`。
- “生产工单入库”不默认勾选“本次入库后结单”。
- 只有用户主动勾选结单，并确认结单影响后，才执行结单。

用户确认本设计文档后，再拆分具体实现计划并进入代码实现。
