# 生产主链路仓库单据化与 PDF 归档设计

## 背景

系统已经完成销售订单、采购订单、到货入库单、质量校验单的正式单据化与 PDF 归档。嘉恒仓库仍以业务抽屉和库存流水为主，用户在办理生产出库、生产入库、生产余料入库、生产废料入库时，缺少一张可预览、可下载、可留档的正式业务单据。

仓库业务入口很多，6 大库中存在采购、生产、委外、退货、销售、特殊调整等多条链路。如果一次性把所有出入库按钮都接入 PDF，容易形成重复模板和重复归档逻辑。本次先以生产主链路作为第一期样板，抽出仓库作业单通用底座，后续再扩展到委外、退货、销售出库、特殊出入库。

## 本期目标

1. 将生产主链路的仓库作业改造成“保存即归档”的正式业务单据。
2. 不改变现有库存、生产台账、生产工单的核心业务计算逻辑。
3. 后端基于业务保存后的真实数据生成 PDF 归档快照。
4. PDF 失败不阻断业务保存，但必须能在界面看到归档失败并重新生成。
5. 前端保持现有操作入口，只把相关弹窗和归档操作升级为正式单据体验。
6. 第一版先不做批量下载，待仓库单据类型稳定后再统一扩展。

## 本期范围

本期只覆盖生产主链路：

- 原材料库 · 生产出库
- 生产台账入口 · 生产工单入库
- 成品库 · 生产入库
- 原材料库 · 生产余料入库
- 废料库 · 生产废料入库

本期不覆盖：

- 委外入库、委外出库、委外余料入库、委外废料入库
- 销售出库
- 客料入库
- 退货出库、退货入库、退货废料入库、返工出库
- 特殊入库、特殊出库
- 盘库单
- 批量归档下载

## 单据类型

后端归档类型先新增两类：

1. `生产领料出库单`
2. `生产入库结算单`

### 生产领料出库单

对应原材料库的生产出库。

业务含义：把某个原材料库存批次号中的材料领出，用于生产某个产品。由于系统已经限制生产出库一次只能选择一个原材料库存批次，本单据天然对应“材料库存批次号 + 产品”的生产台账维度。

### 生产入库结算单

对应生产过程中的入库结算动作。

业务含义：围绕某个在生产的材料库存批次号和产品，登记本次成品入库、余料入库、废料入库。无论用户从“生产工单入库”合并入口办理，还是从成品库、原材料库、废料库的分支入口单独办理，都归档为同一种结算单。

这样可以避免同一件生产结算业务因为入口不同而生成不同结构的单据。

## 归档业务主键

现有 `document_archives` 表通过 `document_type + business_id + archive_version` 管理归档。生产主链路需要明确 `business_id` 的来源。

### 生产领料出库单

推荐以生产台账记录 ID 作为 `business_id`。

原因：

- 生产出库会形成或更新“材料库存批次号 + 产品”的生产台账。
- 同一天同一库存批次同一产品允许多次生产出库，但业务上累计到同一条生产台账。
- 生产领料出库单应该表达这条生产台账当前的领料出库快照，而不是某一条孤立库存流水。

归档版本规则：

- 每次生产出库后，对同一生产台账生成新版本 `生产领料出库单`。
- 列表和详情默认展示最新版本。
- 历史版本保留，用于追溯每次领料变化后的快照。

### 生产入库结算单

推荐以本次入库动作形成的主库存流水 ID 作为 `business_id`。

原因：

- 生产工单入库合并入口一次可同时产生成品入库、余料入库、废料入库三类库存流水。
- 分支入口按入口类型只产生对应的一类库存流水。
- 归档单要代表“本次保存动作”，而不是覆盖整条生产台账。

对于合并入口，如果一次保存产生多条库存流水，需要在后端确定一条“主流水”：

- 优先使用成品入库流水。
- 如果没有成品入库流水，使用余料入库流水。
- 如果没有余料入库流水，使用废料入库流水。

归档内容中仍要列出本次保存产生的全部相关流水。

## 单据内容

### 生产领料出库单字段

单据抬头：

- 单据编号
- 单据日期
- 仓库：原材料库
- 业务类型：生产出库
- 生产台账号
- 生产工单号或材料生产编号
- 材料库存批次号
- 产品名称
- 产品编码
- 状态
- 操作人

明细字段：

- 序号
- 原材料编码
- 原材料名称
- 规格
- 库存批次号
- 本次领料重量
- 累计领料重量
- 单位成本
- 本次材料成本
- 来源仓库
- 备注

汇总字段：

- 本次领料重量合计
- 累计领料重量
- 本次材料成本合计
- 生产台账状态

签字栏：

- 领料人
- 仓库复核
- 生产确认
- 制单人

### 生产入库结算单字段

单据抬头：

- 单据编号
- 单据日期
- 生产台账号
- 生产工单号或材料生产编号
- 材料库存批次号
- 产品名称
- 产品编码
- 业务入口：生产工单入库、生产入库、生产余料入库、生产废料入库
- 是否结单
- 生产台账状态
- 操作人

明细字段：

- 序号
- 入库类型：成品入库、生产余料入库、生产废料入库
- 入库仓库
- 入库物料或产品
- 原材料库存批次号
- 本次数量
- 本次重量
- 理论数量或理论重量
- 偏差百分比
- 偏差说明
- 备注

汇总字段：

- 本次成品入库数量合计
- 本次余料入库重量合计
- 本次废料入库重量合计
- 该生产台账累计成品入库数量
- 该生产台账累计余料入库重量
- 该生产台账累计废料入库重量

签字栏：

- 入库确认
- 仓库复核
- 生产确认
- 制单人

## 前端交互

### 操作弹窗

生产主链路现有入口保留，不改变用户从嘉恒仓库进入业务的习惯。

本期只做两类升级：

1. 表单内部逐步向 `DocumentPaper`、`DocumentGrid`、`DocumentLineTable` 的正式单据形态靠拢。
2. 保存成功后明确提示 PDF 归档结果。

如果业务保存成功但 PDF 失败，提示示例：

`生产入库已保存，但 PDF 归档失败，可在归档操作中重新生成。`

### 列表与详情归档操作

本期优先在生产台账或相关仓库流水明细中展示归档操作。

展示内容：

- 归档状态：未生成、已归档、归档失败
- 预览 PDF
- 下载 PDF
- 重新生成

第一版不做批量下载，避免在仓库单据类型尚未全部稳定时扩大复杂度。

## 后端设计

### 文档归档服务扩展

在现有 `document_archives.py` 基础上新增：

- `DOCUMENT_TYPE_PRODUCTION_MATERIAL_OUT = "生产领料出库单"`
- `DOCUMENT_TYPE_PRODUCTION_INBOUND_SETTLEMENT = "生产入库结算单"`
- `collect_production_material_out_archive_context`
- `collect_production_inbound_settlement_archive_context`

现有 `collect_archive_context` 增加两类分支。

现有 `/document-archives/{document_type_key}/{business_id}/...` 通用路由增加 key：

- `production-material-out`
- `production-inbound-settlement`

### 仓库接口触发

生产出库保存成功后：

- 完成库存扣减。
- 完成生产台账更新。
- 提交业务事务。
- 调用归档生成。
- 如果归档失败，不回滚生产出库。
- 返回结果携带归档状态。

生产工单入库、生产入库、生产余料入库、生产废料入库保存成功后：

- 完成库存变更。
- 完成生产台账或生产工单联动。
- 提交业务事务。
- 以本次动作主流水 ID 生成 `生产入库结算单`。
- 如果归档失败，不回滚入库业务。
- 返回结果携带归档状态。

### PDF 模板

第一版沿用现有纸面归档 PDF 风格：

- 米白纸张底色
- 黑色表格线
- 单据标题居中
- 右上显示 `ERP留存联` 和 `PDF归档`
- 表格内容完整换行，不使用省略号
- 状态、备注、偏差说明全部使用中文

生产主链路字段比销售/采购更偏业务追溯，PDF 表格需要允许多行换行，不能因为库存批次号或备注过长而超格。

## 数据流

生产出库：

```mermaid
flowchart LR
    A["原材料库 · 生产出库"] --> B["扣减原材料库存批次"]
    B --> C["创建库存流水"]
    C --> D["创建或更新生产台账"]
    D --> E["生成生产领料出库单 PDF"]
    E --> F["写入 document_archives"]
```

生产入库结算：

```mermaid
flowchart LR
    A["生产工单入库或分支入库"] --> B["创建成品/余料/废料库存流水"]
    B --> C["联动生产台账累计量"]
    C --> D["确定本次动作主流水"]
    D --> E["生成生产入库结算单 PDF"]
    E --> F["写入 document_archives"]
```

## 错误处理

业务保存失败：

- 不生成 PDF。
- 返回原有业务错误。

PDF 生成失败：

- 不回滚业务保存。
- 写入或返回 `归档失败` 状态。
- 前端提示用户可重新生成。

重新生成失败：

- 不影响已有归档版本。
- 保留错误信息。
- 前端显示失败原因。

历史数据：

- 本期不强制自动补档。
- 已存在的生产台账和库存流水在列表中显示 `未生成`。
- 用户可以点 `重新生成` 补 PDF。

## 测试策略

后端测试：

- 生产出库后生成 `生产领料出库单` 归档记录。
- 同一生产台账多次生产出库后归档版本递增。
- 生产工单入库后生成 `生产入库结算单` 归档记录。
- 分支成品入库、余料入库、废料入库后生成 `生产入库结算单`。
- PDF 生成失败不回滚库存和生产台账。
- 归档服务能通过 `production-material-out` 和 `production-inbound-settlement` key 预览、下载、重新生成。

前端测试：

- 生产主链路保存成功后显示归档状态反馈。
- 相关列表或详情出现归档操作。
- 归档操作调用正确 document type key。
- PDF 相关文案不出现英文状态。

人工验证：

- 原材料库生产出库后可预览生产领料出库单。
- 生产工单入库后可预览生产入库结算单。
- 分支入口入库后可预览生产入库结算单。
- PDF 中长库存批次号、备注、偏差说明完整显示且不超格。

## 风险与边界

1. 生产台账和旧生产工单逻辑仍处在并存状态，实施时要按当前代码真实入口分别接入，不能只接其中一条。
2. 合并入口一次产生多条库存流水时，必须稳定确定主流水，否则归档 business_id 会不稳定。
3. 第一版不做批量下载，避免过早扩大接口和 UI 范围。
4. 本次只做生产主链路，不顺手改委外、退货、特殊调整。
5. PDF 是业务快照，不是重新计算入口；归档时以保存后的数据库事实为准。

## 验收标准

1. 原材料库生产出库保存后生成 `生产领料出库单` PDF 归档。
2. 生产工单入库保存后生成 `生产入库结算单` PDF 归档。
3. 成品库生产入库、原材料库生产余料入库、废料库生产废料入库保存后生成 `生产入库结算单` PDF 归档。
4. 业务保存成功但归档失败时，业务数据仍保留，页面能看到归档失败并重新生成。
5. 生产台账或相关流水详情能预览、下载、重新生成 PDF。
6. PDF 内容不使用省略号，长字段自动换行。
7. 用户可通过 PDF 看清材料库存批次号、产品、领料、成品入库、余料、废料、偏差说明、结单状态。

## 设计结论

第一期采用“生产主链路优先”的仓库单据化方案：

- 用 `生产领料出库单` 覆盖原材料库生产出库。
- 用 `生产入库结算单` 覆盖生产工单入库和成品/余料/废料分支入库。
- 后端复用现有 `document_archives` 通用归档表和路由。
- PDF 失败不阻断业务保存。
- 第一版先不做批量下载和全仓库入口覆盖。

该方案能先把最核心的生产闭环打造成正式单据样板，同时为后续 6 大库所有出入库单据化留下统一扩展路径。
