docs: define WCS inventory outbound fixes

This commit is contained in:
zgw
2026-07-15 15:11:23 +08:00
parent 302cc0135f
commit 9d3aa7a3bd

View File

@@ -0,0 +1,130 @@
# WCS、库存追溯与出库状态约束设计
## 目标
在不破坏现有 WCS 作业状态机、03 仓两阶段选层、通道混放规则和库存主子表结构的前提下,完成以下六项调整:
1. 仅 WCS 主动请求作业接口 `/api/external/wcs/inbound/job` 返回的作业对象将 `executionPhase` 输出为 `"0"`。数据库中的领取后阶段仍为 `"1"`,其他 WCS 接口保持现有返回。
2. 03 仓入库货位按入口就近优先:`03-AU-01`、`03-AU-03` 优先 G、H 区;`03-AU-02`、`03-AU-04` 优先 A、B 区。优先区域无可用货位时允许回退其他区域。
3. 盘点回库生成的新库存必须保留原库存的入库主单和入库类型;能够可靠追溯的历史盘点库存通过迁移脚本回填。
4. 库存信息页面的库存物料明细子表显示项目号。
5. 库存明细新增、修改弹窗中的子作业 ID 和入库子单 ID 不可编辑,后端更新接口也不得改变这两个追溯字段;物料状态改用正常/异常开关。
6. 出库创建选料、手动分配和自动分配只能使用库存状态正常且物料状态正常的库存明细。
## 方案选择
采用分层约束方案:接口层只控制对外响应语义,服务层传递入口和保护追溯字段,Mapper/存储过程负责原子筛选与并发安全,前端负责减少误操作。相比把所有规则集中到 SQL 或 Java,这种方式保留了现有 SQL 锁定能力,也能防止绕过页面调用接口。
## 详细设计
### 1. WCS 请求作业响应阶段
`MydoJobServiceImpl.claimDenseInboundJob` 和 `MydoJobMapper.claimDenseInboundJob` 继续把数据库作业更新为状态 `3`、执行阶段 `1`,用于标识该作业已被 WCS 领取并防止重复领取。
`ExternalApiController.claimDenseInboundJob` 在调用服务成功后复制返回的 `MydoJob`,只把副本的 `executionPhase` 设置为 `"0"` 再包装为 WCS 成功响应。不得修改服务返回对象所代表的数据库状态,也不得改动通用 `wcsSuccess`,以免货位请求、异常处理和完成回调等其他 WCS 接口被一并归零。
验收条件:
- 主动请求作业接口响应 `data.executionPhase == "0"`。
- Mapper 领取 SQL 仍写入 `execution_phase = '1'`。
- `/api/external/wcs/inbound/location` 返回已领取作业时保留其实际执行阶段。
### 2. 03 仓入口区域优先
入口号沿现有链路传递:
`ExternalApiController` → `PdaService.planInboundLayer` / `lockInboundCargoSpace` → `PdaServiceImpl` 参数 Map → `PdaMapper.xml`。
选层候选 SQL 和最终锁位 SQL都计算 `entrance_area_priority`:
- 入口为 `03-AU-01` 或 `03-AU-03`:G、H 区优先级为 0,其他区为 1。
- 入口为 `03-AU-02` 或 `03-AU-04`:A、B 区优先级为 0,其他区为 1。
- 其他或空入口:所有区域优先级为 0,保持现有排序。
排序顺序保留物理兼容性:高度完全匹配优先,然后应用入口区域优先,再应用现有空托随机通道、同类物料通道聚合、排层列方向等规则。区域是软优先,不是硬过滤,因此目标区域满位时仍可完成分配。
`BasCargoSpaceMapper.selectAvailableInboundCargoSpaceInRoadway` 是放货有货异常后的重新分配路径,也使用 `MydoJob.entranceId` 应用同一优先级,避免异常重分配改变就近原则。
### 3. 盘点回库库存追溯
盘点确认读取出库作业物料时,在 `PdaMapper.selectInventoryTrayMaterials` 中通过原库存主表取得 `stockin_id` 和 `in_type`。创建盘点回库作业明细时,把原 `stockin_id` 写入 `mydo_job_detail.stockin_id`。
完成盘点回库的 `proc_complete_in_job` 使用作业明细的 `stockin_id` 写入新 `bas_stock.stockin_id`,并按以下顺序恢复入库类型:
1. 当前入库主表 `in_stockin.in_type`;
2. 历史入库主表 `in_stockin_finish.in_type`;
3. 若两者均不存在,则通过回库作业明细保存的原库存明细 ID,关联 `bas_stock_detail_finish` 与 `bas_stock_finish`,使用原库存归档的 `in_type` 兜底。
为避免给通用作业明细新增与库存无关的字段,盘点回库完成前所需的原 `in_type` 通过原库存归档链路查询;新迁移脚本同时回填可通过盘点回库作业、原库存明细归档和库存主表归档可靠关联的历史 `bas_stock.stockin_id`、`bas_stock.in_type`。无法可靠关联的数据保持为空,不猜测来源。
完成过程必须兼容普通采购/生产入库,不改变其库存生成逻辑。
### 4. 库存子表项目号
当前 `BasStockDetail` 和 `BasStockDetailMapper.xml` 已具备 `projectNo` 字段与查询映射。管理端 `ruoyi-ui/src/views/bas/stock/index.vue` 在库存物料明细表中增加“项目号”列,直接显示 `scope.row.projectNo`。
### 5. 库存明细追溯字段与物料状态开关
前端弹窗中:
- `jobDetailId` 和 `stockinDetailId` 输入框设置为只读;新增时为空,修改时仅展示原值。
- `productStatus` 使用 Element UI `el-switch`,`active-value="1"`、`inactive-value="2"`,文案为“正常/异常”。新增明细默认正常。
后端中:
- `BasStockServiceImpl.updateBasStockDetail` 先读取原明细,把原 `jobDetailId`、`stockinDetailId` 恢复到待更新对象。
- `BasStockDetailMapper.updateBasStockDetail` 不生成这两个字段的更新语句,形成第二道保护。
- 新增服务仍允许系统内部在创建可追溯库存明细时写入追溯字段;管理端新增弹窗不会提供可编辑入口。
### 6. 出库双状态约束
候选列表:
- 出库创建页面查询库存明细时固定传入 `stockStatus: '1'`、`productStatus: '1'`。
- 出库单手动分配弹窗同样传入两项状态。
服务端原子分配:
- `proc_out_hand_allocation` 的候选明细查询增加 `bs.stock_status = '1'` 与 `bsd.product_status = '1'`。
- `proc_out_auto_allocation` 的游标候选查询增加相同条件。
- 显式指定异常库存或异常物料时,手动分配返回“没有可分配库存明细”,不得生成作业或占用货位。
新的日期迁移脚本更新生产存储过程;`sql/out_stockout_flow.sql` 同步更新,保证新环境和增量部署行为一致。
## 错误处理与兼容性
- 未知入口号不报错,按原有货位排序分配。
- 优先区域无可用货位时回退其他区域,不把软优先变成缺货错误。
- 盘点追溯回填只更新可证明来源的记录,避免错误填充入库主单。
- 追溯字段更新被忽略时,其他允许修改的库存明细字段照常保存。
- 出库双状态校验在 SQL 事务内执行,避免列表打开后库存状态变化造成越权分配。
## 测试与验证
### 后端测试
- 控制器测试:主动请求作业响应阶段为 0,服务返回对象和其他 WCS 接口阶段不被修改。
- Mapper SQL 测试:领取 SQL仍写阶段 1;选层、锁位和异常重分配 SQL均包含四个入口及 A/B、G/H 优先表达式。
- PDA 服务测试:入口号正确传入选层和锁位参数;盘点回库明细复制 `stockinId`。
- 库存服务测试:修改库存明细不能改变两个追溯字段。
- 盘点追溯 SQL 测试:当前/历史入库主单类型恢复和历史库存回填语句存在。
- 出库 SQL 测试:手动、自动分配都同时限制主库存状态和物料状态。
### 前端验证
- 静态验证库存子表包含项目号列、两个追溯输入框只读、物料状态使用开关。
- 静态验证出库创建和手动分配查询都传入两项正常状态。
- 执行 `npm run build:prod`。
### 综合验证
- 运行相关 `ruoyi-admin` JUnit 测试。
- 运行后端 Maven 构建,确认编译和既有测试不回归。
- 运行 SQL 脚本结构检查;如本机没有可用的 SQL Server,只声明未执行真实数据库迁移,不以静态检查替代数据库验证。
## 非目标
- 不调整 WCS 其他接口的执行阶段返回。
- 不改变领取后数据库执行阶段为 1 的内部状态机。
- 不把入口区域优先改成硬性区域限制。
- 不允许根据无法追溯的数据猜测历史入库主单或入库类型。