# 工单物料需求列表(含库存校验)- 前端联调方案 ## 涉及页面 - 工单详情页 / 工单物料需求 Tab - 工单列表 → 物料需求查看(弹窗或侧边栏) - **ERP 采购订单新增页面**(库存不足时跳转) ## 业务流程与数据带入 1. 工单详情页 → 物料需求 Tab:传入当前工单 `workOrderId` 2. 接口根据工单的最后一道工序投料,沿工序产出链向前递推,展开出所有底层原材料的总需求数量 3. 需求数量 = 每单位成品用量 × 工单生产数量 4. 联查库存返回可用量和充足标识,前端据此展示库存状态 ## API | 方法 | 路径 | 说明 | |------|------|------| | GET | `/mes/pro/work-order-bom/item-list-by-work-order-id` | 获得工单物料需求列表(含库存) | **请求参数:** | 参数 | 类型 | 必填 | 说明 | |------|------|------|------| | workOrderId | Long | 是 | 工单编号 | **响应字段:** | 字段 | 类型 | 说明 | |------|------|------| | itemId | Long | 物料编号 | | itemCode | String | 物料编码 | | itemName | String | 物料名称 | | itemSpecification | String | 规格型号 | | unitMeasureName | String | 单位名称 | | quantity | BigDecimal | 需求数量(工单数量 × BOM 用量) | | stockQuantity | BigDecimal | 可用库存(在库 - 冻结 - 占用) | | purchaseOrderQuantity | BigDecimal | 在途采购数(来源为本工单的采购订单未入库数量汇总) | | stockSufficient | Boolean | 库存是否充足(可用库存 + 在途采购数 >= 需求数量),true=是 false=否 | | itemOrProduct | String | 物料/产品标识 | **响应示例:** ```json { "code": 0, "data": [ { "itemId": 41, "itemCode": "ITEM_20260716004", "itemName": "铝杆", "itemSpecification": "φ9.5mm", "unitMeasureName": "kg", "quantity": 600.00, "stockQuantity": 500.00, "purchaseOrderQuantity": 200.00, "stockSufficient": true, "itemOrProduct": "ITEM" }, { "itemId": 44, "itemCode": "ITEM_20260716008", "itemName": "镀锌钢线", "itemSpecification": "φ1.6mm", "unitMeasureName": "kg", "quantity": 100.00, "stockQuantity": 50.00, "purchaseOrderQuantity": 0.00, "stockSufficient": false, "itemOrProduct": "ITEM" } ] } ``` ## 字段展示规则 | 字段 | 展示位置 | 说明 | |------|----------|------| | itemCode | 列表列 | 物料编码 | | itemName | 列表列 | 物料名称 | | itemSpecification | 列表列 | 规格型号 | | unitMeasureName | 列表列 | 单位 | | quantity | 列表列 | 需求数量,右对齐,千分位格式 | | stockQuantity | 列表列 | 可用库存,右对齐,千分位格式 | | purchaseOrderQuantity | 列表列 | 在途采购数,右对齐,千分位格式 | | stockSufficient | 列表列 | 库存状态,见下方状态展示规则 | ### 库存状态展示规则 | stockSufficient | 展示文本 | 建议颜色 | 说明 | |-----------------|---------|---------|------| | true | 充足 | 绿色 | 可用库存 ≥ 需求数量 | | false | 不足 | 红色 | 可用库存 < 需求数量 | ### 数据带入关系 | 来源 | 带入字段 | 说明 | |------|----------|------| | 工单详情 | workOrderId | 通过页面路由参数或行数据获取,传给接口 | ## 接口调用时序 ``` 工单详情页加载 ├── GET /mes/pro/work-order/get?workOrderId=47 → 获取工单基本信息 ├── GET /mes/pro/work-order-process/list-by-work-order-id?workOrderId=47 → 获取工序列表 └── GET /mes/pro/work-order-bom/item-list-by-work-order-id?workOrderId=47 → 获取物料需求(含库存) ``` 无需前置调用,传入 workOrderId 即可独立请求。 ## 业务规则说明 | 场景 | 规则 | |------|------| | 工单无工序 | 返回空列表 | | 工单无 BOM 投料 | 返回空列表 | | 工单产品未关联工艺路线 | 返回空列表 | | 物料需求计算 | 从最后一道工序(sort 最大)的投料出发,沿工序产出链向前递推展开 | | 数量计算 | 每单位成品用量 × 工单生产数量(quantity) | | 可用库存计算 | 汇总所有库位的(在库数量 - 冻结数量 - 占用数量),≤0 的库位不计入 | | 在途采购数计算 | 查询来源为本工单(sourceType=1, sourceId=工单ID)的采购订单明细,汇总(订购数量 - 已入库数量) | | 充足判断 | 可用库存 + 在途采购数 >= 需求数量 | ## 采购申请(库存不足时跳转) ### 触发条件 列表中 `stockSufficient = false` 的行,在操作列展示「**申请采购**」按钮。 ### 跳转目标 ERP 采购订单新增页面:`/erp/purchase-order/create`(前端路由,非接口路径) ### 数据带入 从物料需求行 + 当前工单携带以下数据跳转到采购订单新增页面: **工单来源字段(采购订单独有的追溯字段):** | 来源字段 | 目标字段 | 类型 | 说明 | |----------|----------|------|------| | 工单 `id` | `sourceId` | Long | 来源单据编号 | | 工单 `code` | `sourceNo` | String | 来源单号 | | — | `sourceType` | Integer | 固定传 `1`(生产工单) | **产品行字段:** | 来源字段 | 目标字段 | 说明 | |----------|----------|------| | `itemId` | `items[].productId` | 产品编号 | | `itemCode` | — | 产品编码,供页面显示 | | `itemName` | — | 产品名称,供页面显示 | | `itemSpecification` | — | 规格型号,供页面显示 | | `quantity - stockQuantity` | `items[].count` | 建议采购数量(缺口 = 需求 - 库存) | | `unitMeasureName` | — | 单位名称,供页面显示 | ### 页面跳转流程 ``` 物料需求列表(库存不足行) │ ├─ 点击「申请采购」按钮 │ ├─ 跳转到 ERP 采购订单新增页面 │ 携带:工单来源信息(sourceId、sourceNo、sourceType=1) │ 产品行信息(productId、数量、编码、名称、规格、单位) │ └─ 采购订单新增页面 ├─ 自动预填来源字段(不可编辑) ├─ 自动预填产品行(productId + count + unit) ├─ 用户选择供应商(supplierId,必填) ├─ 用户补充单价、税率等信息 └─ 提交 POST /erp/purchase-order/create ``` ### 交互说明 | 场景 | 处理方式 | |------|----------| | 单个物料不足 | 点击该行「申请采购」→ 跳转采购订单页面,预填 1 行产品 | | 多个物料不足 | 可逐行点击分别申请,或支持多选后批量带到同一个采购订单 | | 全部充足 | 不展示「申请采购」按钮 | | 可用库存为 0 | `stockSufficient = false`,同样展示「申请采购」,缺口 = 全部需求数量 | ### 采购订单创建 API | 方法 | 路径 | 说明 | |------|------|------| | POST | `/erp/purchase-order/create` | 创建采购订单 | **完整请求参数:** | 参数 | 类型 | 必填 | 来源 | |------|------|------|------| | `sourceType` | Integer | 否 | 固定 `1`(生产工单),由页面携带 | | `sourceNo` | String | 否 | 工单编码 `workOrder.code` | | `sourceId` | Long | 否 | 工单 ID `workOrder.id` | | `supplierId` | Long | 是 | 用户在采购页面选择供应商 | | `orderTime` | LocalDateTime | 是 | 默认当天 | | `items[].productId` | Long | 是 | 物料需求的 `itemId` | | `items[].productUnitId` | Long | 是 | 物料对应的计量单位 ID | | `items[].count` | BigDecimal | 是 | 缺口数量(需求 - 库存) | | `items[].productPrice` | BigDecimal | 否 | 用户手工填写 | | `items[].taxPercent` | BigDecimal | 否 | 用户手工填写 | **请求示例:** ```json { "sourceType": 1, "sourceNo": "GD20260730001", "sourceId": 47, "supplierId": 10, "orderTime": "2026-07-30", "items": [ { "productId": 41, "productUnitId": 5, "count": 100.00, "productPrice": 12.50, "taxPercent": 13.00 } ] } ``` **响应:** ```json { "code": 0, "data": { "id": 123, "no": "CGD20260730001", "sourceType": 1, "sourceNo": "GD20260730001", "sourceId": 47, "sourceTypeName": "生产工单", "supplierId": 10, "items": [...] } } ``` ## 注意事项 - `stockSufficient` 为 `false` 时,前端应**差异化展示**(如红色高亮),并在操作列显示「申请采购」按钮 - 可用库存为 0 时,`stockQuantity` 返回 `0`,`stockSufficient` 为 `false` - 接口返回的物料列表可能为空(工单无工序/无 BOM/未关联路线),前端需处理空状态提示 - 数量字段为 `BigDecimal`,前端展示时注意小数位处理,建议保留 2 位小数 - 跳转采购订单时,`productUnitId` 需要通过物料详情接口获取(当前物料需求列表未返回此字段),或者采购订单页面根据 `productId` 自行查询默认单位 - 同一个物料可能在多个工单中都需要采购,采购订单页面应支持用户修改数量 - 来源字段 `sourceType`、`sourceNo`、`sourceId` 在采购订单页面展示为**只读**,不允许用户修改 - 采购订单详情/列表中可通过 `sourceTypeName`(如"生产工单")和 `sourceNo`(工单编码)追溯到来源工单