From d49f722bda673bcf79be368593dad436b4a6b20b Mon Sep 17 00:00:00 2001
From: zhouz <>
Date: Wed, 22 Jul 2026 14:23:13 +0800
Subject: [PATCH] =?UTF-8?q?docs:=20=E6=B7=BB=E5=8A=A0=E5=87=BA=E5=BA=93?=
=?UTF-8?q?=E5=8D=95=E6=96=B0=E5=A2=9E=E5=AE=9E=E7=8E=B0=E8=AE=A1=E5=88=92?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../plans/2026-07-22-outbound-create-plan.md | 410 ++++++++++++++++++
1 file changed, 410 insertions(+)
create mode 100644 docs/superpowers/plans/2026-07-22-outbound-create-plan.md
diff --git a/docs/superpowers/plans/2026-07-22-outbound-create-plan.md b/docs/superpowers/plans/2026-07-22-outbound-create-plan.md
new file mode 100644
index 00000000..81aa2718
--- /dev/null
+++ b/docs/superpowers/plans/2026-07-22-outbound-create-plan.md
@@ -0,0 +1,410 @@
+# 出库单新增功能实现计划
+
+> **面向 AI 代理的工作者:** 必需子技能:使用 superpowers:subagent-driven-development(推荐)或 superpowers:executing-plans 逐任务实现此计划。步骤使用复选框(`- [ ]`)语法来跟踪进度。
+
+**目标:** 按确认的布局实现出库单新增页,通过库存箱号展开全部可用子卷或手工新增物料汇总,并事务保存 `wms_iostorinv` 主表和 `wms_iostorinvdtl` 明细。
+
+**架构:** 前端由主弹窗、库存选择弹窗、手工汇总弹窗组成,页面只维护现有表字段和临时展示字段。后端提供可用库存查询/箱号展开接口与聚合创建接口;聚合服务调用 base 编码 API、重新计算汇总值,并在一个事务内写入主表和明细。
+
+**技术栈:** Java 17、Spring Boot、MyBatis XML、OpenFeign、Vue 3、TypeScript、Ant Design Vue Next、Vben Form、Vxe Grid、Vitest
+
+---
+
+## 文件结构
+
+### 后端
+
+- 修改 `nl-module-wms/nl-module-wms-server/pom.xml`:引入 `nl-module-base-api`。
+- 创建 `nl-module-wms/nl-module-wms-server/src/main/java/cn/code/nl/module/wms/framework/rpc/config/RpcConfiguration.java`:注册 `CodeGenApi` Feign 客户端。
+- 创建 `nl-module-wms/nl-module-wms-server/src/main/java/cn/code/nl/module/wms/controller/admin/iostorinv/vo/IostorInvCreateReqVO.java`:聚合创建请求及内部明细对象。
+- 创建 `nl-module-wms/nl-module-wms-server/src/main/java/cn/code/nl/module/wms/controller/admin/iostorinv/vo/AvailableInventoryPageReqVO.java`:库存筛选与分页参数。
+- 创建 `nl-module-wms/nl-module-wms-server/src/main/java/cn/code/nl/module/wms/controller/admin/iostorinv/vo/AvailableInventoryRespVO.java`:库存与页面展示结果。
+- 创建 `nl-module-wms/nl-module-wms-server/src/main/java/cn/code/nl/module/wms/controller/admin/iostorinv/vo/ExpandAvailableInventoryReqVO.java`:仓库和已选箱号集合。
+- 修改 `nl-module-wms/nl-module-wms-server/src/main/java/cn/code/nl/module/wms/dal/mysql/iostorinvdtl/IostorinvDtlMapper.java`:声明库存查询与按箱号展开方法。
+- 修改 `nl-module-wms/nl-module-wms-server/src/main/resources/mapper/iostorinvdtl/IostorinvDtlMapper.xml`:实现跨仓库结构和组盘库存的 SQL。
+- 修改 `nl-module-wms/nl-module-wms-server/src/main/java/cn/code/nl/module/wms/service/iostorinv/IostorInvService.java`:声明库存查询、展开和聚合创建方法。
+- 修改 `nl-module-wms/nl-module-wms-server/src/main/java/cn/code/nl/module/wms/service/iostorinv/IostorInvServiceImpl.java`:实现校验、编码生成、汇总和事务写入。
+- 修改 `nl-module-wms/nl-module-wms-server/src/main/java/cn/code/nl/module/wms/controller/admin/iostorinv/IostorInvController.java`:暴露三个管理端接口。
+- 修改 `nl-module-wms/nl-module-wms-server/src/main/java/cn/code/nl/module/wms/enums/ErrorCodeConstants.java`:增加编码和库存失效错误码。
+- 创建 `nl-module-wms/nl-module-wms-server/src/test/java/cn/code/nl/module/wms/service/iostorinv/IostorInvServiceLocalSpringTest.java`:Spring 集成测试。
+
+### 前端
+
+- 修改 `nl-ui/nl-ui-admin-vben/yudao-ui-admin-vben/apps/web-antdv-next/src/api/wms/iostorinv/index.ts`:增加聚合请求、库存结果类型和接口函数。
+- 创建 `nl-ui/nl-ui-admin-vben/yudao-ui-admin-vben/apps/web-antdv-next/src/views/wms/iostorinv/modules/detail-summary.ts`:纯函数计算明细数、总重量和去重键。
+- 创建 `nl-ui/nl-ui-admin-vben/yudao-ui-admin-vben/apps/web-antdv-next/src/views/wms/iostorinv/modules/detail-summary.test.ts`:汇总联动的红绿测试。
+- 创建 `nl-ui/nl-ui-admin-vben/yudao-ui-admin-vben/apps/web-antdv-next/src/views/wms/iostorinv/modules/inventory-select.vue`:库存筛选、多选和确认展开。
+- 创建 `nl-ui/nl-ui-admin-vben/yudao-ui-admin-vben/apps/web-antdv-next/src/views/wms/iostorinv/modules/manual-detail.vue`:物料汇总新增。
+- 修改 `nl-ui/nl-ui-admin-vben/yudao-ui-admin-vben/apps/web-antdv-next/src/views/wms/iostorinv/modules/form.vue`:实现确认布局、明细联动和聚合提交。
+
+---
+
+## 任务 1:建立后端请求契约与 RPC 依赖
+
+- [ ] **步骤 1:先创建 Spring 集成测试骨架并验证当前编译失败**
+
+在 `IostorInvServiceLocalSpringTest.java` 中使用 `@SpringBootTest`、`@ActiveProfiles("local")` 和 `@Resource IostorInvService`,先引用尚不存在的 `IostorInvCreateReqVO` 与 `createOutbound`:
+
+```java
+@SpringBootTest
+@ActiveProfiles("local")
+class IostorInvServiceLocalSpringTest {
+ @Resource
+ private IostorInvService iostorInvService;
+
+ @Test
+ void createOutboundRejectsEmptyDetails() {
+ IostorInvCreateReqVO reqVO = new IostorInvCreateReqVO();
+ Assertions.assertThrows(ConstraintViolationException.class,
+ () -> iostorInvService.createOutbound(reqVO));
+ }
+}
+```
+
+运行:
+
+```bash
+mvn -pl nl-module-wms/nl-module-wms-server -am -DskipTests compile
+```
+
+预期:FAIL,提示 `IostorInvCreateReqVO` 或 `createOutbound` 不存在。
+
+- [ ] **步骤 2:增加 base API 依赖和 Feign 注册**
+
+在 WMS server 的 `pom.xml` 增加:
+
+```xml
+
+ cn.nl.cloud
+ nl-module-base-api
+ ${revision}
+
+```
+
+创建 `RpcConfiguration`,使用 `@Configuration(value = "wmsRpcConfiguration", proxyBeanMethods = false)` 与 `@EnableFeignClients(clients = CodeGenApi.class)`。
+
+- [ ] **步骤 3:创建带注解校验的聚合请求对象**
+
+`IostorInvCreateReqVO` 顶层包含 `@NotEmpty billType`、`@NotEmpty storId`、`@NotNull bizDate`、`remark`、`@NotEmpty @Valid List details`;内部 `Detail` 包含:
+
+```java
+@NotEmpty(message = "物料编码不能为空")
+private String materialCode;
+private String materialId;
+private String pcsn;
+@NotNull(message = "出库重量不能为空")
+@DecimalMin(value = "0.001", message = "出库重量必须大于0")
+private BigDecimal planQty;
+private String qtyUnitId;
+private String qtyUnitName;
+private String sourceBillCode;
+private String sourceBillType;
+private String sourceBilldtlId;
+private String remark;
+```
+
+不接收 `billCode`、`billStatus`、`detailCount`、`totalWeight` 和 `seqNo`,这些字段全部由服务端生成。
+
+- [ ] **步骤 4:声明 Service 方法并验证测试从编译失败进入校验失败/通过**
+
+```java
+@Validated
+public interface IostorInvService {
+ String createOutbound(@Valid IostorInvCreateReqVO reqVO);
+}
+```
+
+运行:
+
+```bash
+mvn -pl nl-module-wms/nl-module-wms-server -am -Dtest=IostorInvServiceLocalSpringTest -Dsurefire.failIfNoSpecifiedTests=false test
+```
+
+预期:测试能够启动且空请求因参数校验被拒绝;若 local 数据源不可用,保留测试并记录环境阻塞,继续用编译验证生产契约。
+
+- [ ] **步骤 5:提交契约变更**
+
+```bash
+git add nl-module-wms/nl-module-wms-server/pom.xml nl-module-wms/nl-module-wms-server/src/main/java/cn/code/nl/module/wms/framework/rpc/config/RpcConfiguration.java nl-module-wms/nl-module-wms-server/src/main/java/cn/code/nl/module/wms/controller/admin/iostorinv/vo/IostorInvCreateReqVO.java nl-module-wms/nl-module-wms-server/src/main/java/cn/code/nl/module/wms/service/iostorinv/IostorInvService.java nl-module-wms/nl-module-wms-server/src/test/java/cn/code/nl/module/wms/service/iostorinv/IostorInvServiceLocalSpringTest.java
+git commit -m "feat: 定义出库单聚合创建契约"
+```
+
+## 任务 2:实现可用库存查询和按箱号展开
+
+- [ ] **步骤 1:扩展失败测试,定义箱号展开行为**
+
+在集成测试准备同一仓库下箱号 `BOX-001` 的两个可用子卷和另一个仓库的同箱数据,断言:
+
+```java
+List rows = iostorInvService.expandAvailableInventory(
+ "STOR-001", List.of("BOX-001", "BOX-001"));
+Assertions.assertEquals(2, rows.size());
+Assertions.assertTrue(rows.stream().allMatch(row -> "BOX-001".equals(row.getVehicleCode())));
+```
+
+运行指定测试,预期:FAIL,方法和响应对象尚不存在。
+
+- [ ] **步骤 2:创建查询参数和响应对象**
+
+`AvailableInventoryPageReqVO extends PageParam`,字段为 `@NotEmpty storId`、`materialCode`、`vehicleCode`、`pcsn`;`AvailableInventoryRespVO` 使用库存现有字段:`vehicleCode`、`pcsn`、`materialId`、`materialCode`、`materialName`(仅返回展示)、`availableQty`、单位、来源字段及仓库标识。`ExpandAvailableInventoryReqVO` 包含 `@NotEmpty storId` 和 `@NotEmpty List vehicleCodes`。
+
+- [ ] **步骤 3:声明 Mapper 方法**
+
+```java
+PageResult selectAvailableInventoryPage(AvailableInventoryPageReqVO reqVO);
+
+List selectAvailableInventoryByVehicleCodes(
+ @Param("storId") String storId,
+ @Param("vehicleCodes") Collection vehicleCodes);
+```
+
+- [ ] **步骤 4:在 XML 实现统一库存 SQL**
+
+基础关联为 `wms_group_plate gp INNER JOIN wms_structattr sa ON sa.storagevehicle_code = gp.vehicle_code LEFT JOIN base_materialbase mb ON mb.material_id = gp.material_id AND mb.is_deleted = 0`,固定条件为 `sa.stor_id = #{storId}`、`gp.status = '可用'`、`gp.qty - gp.frozen_qty > 0` 和 WMS 两表 `is_deleted = 0`。分页查询增加物料、箱号、子卷号筛选;展开查询使用 `` 的箱号集合。结果中 `available_qty = gp.qty - gp.frozen_qty`,物料名称从 `mb.material_name` 返回但不写入明细表。
+
+- [ ] **步骤 5:Service 对箱号去重后查询全部子卷**
+
+```java
+List distinctVehicleCodes = vehicleCodes.stream().distinct().toList();
+return iostorinvDtlMapper.selectAvailableInventoryByVehicleCodes(storId, distinctVehicleCodes);
+```
+
+控制器提供:
+
+```java
+@GetMapping("/availableInventoryPage")
+public CommonResult> getAvailableInventoryPage(
+ @Valid AvailableInventoryPageReqVO reqVO)
+
+@PostMapping("/expandAvailableInventory")
+public CommonResult> expandAvailableInventory(
+ @Valid @RequestBody ExpandAvailableInventoryReqVO reqVO)
+```
+
+- [ ] **步骤 6:运行集成测试和 Mapper 编译**
+
+```bash
+mvn -pl nl-module-wms/nl-module-wms-server -am -Dtest=IostorInvServiceLocalSpringTest -Dsurefire.failIfNoSpecifiedTests=false test
+```
+
+预期:同箱两子卷全部返回、重复箱号不产生重复行、其他仓库数据不返回。
+
+- [ ] **步骤 7:提交库存查询变更**
+
+```bash
+git add nl-module-wms/nl-module-wms-server/src/main/java/cn/code/nl/module/wms/controller/admin/iostorinv/vo nl-module-wms/nl-module-wms-server/src/main/java/cn/code/nl/module/wms/dal/mysql/iostorinvdtl/IostorinvDtlMapper.java nl-module-wms/nl-module-wms-server/src/main/resources/mapper/iostorinvdtl/IostorinvDtlMapper.xml nl-module-wms/nl-module-wms-server/src/main/java/cn/code/nl/module/wms/service/iostorinv nl-module-wms/nl-module-wms-server/src/main/java/cn/code/nl/module/wms/controller/admin/iostorinv/IostorInvController.java nl-module-wms/nl-module-wms-server/src/test/java/cn/code/nl/module/wms/service/iostorinv/IostorInvServiceLocalSpringTest.java
+git commit -m "feat: 支持按箱号展开可用库存子卷"
+```
+
+## 任务 3:实现主表与明细事务保存
+
+- [ ] **步骤 1:补充失败测试**
+
+构造两条重量 `12.500`、`7.250` 的明细,创建后查询数据库并断言:主表 `ioType = "OUT"`、`billStatus = "生成"`、`detailCount = 2`、`totalWeight = 19.750`;明细序号为 1/2、`assignQty = 0`、`unassignQty = planQty`。测试请求不传任何汇总字段。
+
+- [ ] **步骤 2:实现编码生成和事务写入**
+
+`createOutbound` 标记 `@Transactional(rollbackFor = Exception.class)`:
+
+1. 构造 `CodeGenerateReqDTO`,设置项目现有出入库规则编码 `IO_CODE`。
+2. 调用 `CodeGenApi.generate`,检查 `CommonResult.isSuccess()` 且编码非空;失败抛出中文业务异常。
+3. 手工逐字段创建 `IostorInvDO`,固定 `ioType = "OUT"`、`billStatus = "生成"`。
+4. 使用 `details.stream().map(Detail::getPlanQty).reduce(BigDecimal.ZERO, BigDecimal::add)` 计算总重量并设置明细数。
+5. 插入主表后,循环构造 `IostorinvDtlDO`,写入关联 ID、连续序号、物料、`pcsn`、计划重量、分配数量、单位、来源和备注。
+
+- [ ] **步骤 3:增加控制器端点和错误码**
+
+```java
+@PostMapping("/createOutbound")
+@PreAuthorize("@ss.hasPermission('wms:iostor-inv:create')")
+public CommonResult createOutbound(@Valid @RequestBody IostorInvCreateReqVO reqVO) {
+ return success(iostorInvService.createOutbound(reqVO));
+}
+```
+
+错误码仅增加“单据号生成失败”和“所选库存已不可用”,不新增数据库字段或表。
+
+- [ ] **步骤 4:验证红绿与事务回滚**
+
+运行集成测试;再让测试用编码 API 失败配置触发异常,断言主表和明细数量均未增加。
+
+- [ ] **步骤 5:提交聚合保存**
+
+```bash
+git add nl-module-wms/nl-module-wms-server/src/main/java/cn/code/nl/module/wms/service/iostorinv nl-module-wms/nl-module-wms-server/src/main/java/cn/code/nl/module/wms/controller/admin/iostorinv nl-module-wms/nl-module-wms-server/src/main/java/cn/code/nl/module/wms/enums/ErrorCodeConstants.java nl-module-wms/nl-module-wms-server/src/test/java/cn/code/nl/module/wms/service/iostorinv/IostorInvServiceLocalSpringTest.java
+git commit -m "feat: 事务保存出库单及明细"
+```
+
+## 任务 4:扩展前端 API 与明细汇总纯函数
+
+- [ ] **步骤 1:先写失败的 Vitest 测试**
+
+```ts
+import { describe, expect, it } from 'vitest';
+import { summarizeDetails } from './detail-summary';
+
+describe('summarizeDetails', () => {
+ it('按明细条数和出库重量计算汇总', () => {
+ expect(summarizeDetails([{ planQty: 12.5 }, { planQty: 7.25 }])).toEqual({
+ detailCount: 2,
+ totalWeight: 19.75,
+ });
+ });
+});
+```
+
+运行:
+
+```bash
+pnpm --dir nl-ui/nl-ui-admin-vben/yudao-ui-admin-vben vitest run apps/web-antdv-next/src/views/wms/iostorinv/modules/detail-summary.test.ts --dom
+```
+
+预期:FAIL,模块不存在。
+
+- [ ] **步骤 2:实现最小汇总函数**
+
+```ts
+export function summarizeDetails(details: Array<{ planQty?: number }>) {
+ return {
+ detailCount: details.length,
+ totalWeight: details.reduce((sum, item) => sum + Number(item.planQty || 0), 0),
+ };
+}
+```
+
+- [ ] **步骤 3:在 API 层增加明确类型和三个接口**
+
+定义 `OutboundDetail`、`OutboundCreateReq`、`AvailableInventory`、`AvailableInventoryPageReq`,并增加:
+
+```ts
+export const getAvailableInventoryPage = (params: AvailableInventoryPageReq) =>
+ requestClient.get>('/wms/iostor-inv/availableInventoryPage', { params });
+export const expandAvailableInventory = (storId: string, vehicleCodes: string[]) =>
+ requestClient.post('/wms/iostor-inv/expandAvailableInventory', { storId, vehicleCodes });
+export const createOutbound = (data: OutboundCreateReq) =>
+ requestClient.post('/wms/iostor-inv/createOutbound', data);
+```
+
+- [ ] **步骤 4:运行 Vitest 和类型检查**
+
+```bash
+pnpm --dir nl-ui/nl-ui-admin-vben/yudao-ui-admin-vben vitest run apps/web-antdv-next/src/views/wms/iostorinv/modules/detail-summary.test.ts --dom
+pnpm --dir nl-ui/nl-ui-admin-vben/yudao-ui-admin-vben --filter @vben/web-antdv-next typecheck
+```
+
+预期:汇总测试通过,新增 API 类型无错误。
+
+- [ ] **步骤 5:提交前端基础能力**
+
+```bash
+git add nl-ui/nl-ui-admin-vben/yudao-ui-admin-vben/apps/web-antdv-next/src/api/wms/iostorinv/index.ts nl-ui/nl-ui-admin-vben/yudao-ui-admin-vben/apps/web-antdv-next/src/views/wms/iostorinv/modules/detail-summary.ts nl-ui/nl-ui-admin-vben/yudao-ui-admin-vben/apps/web-antdv-next/src/views/wms/iostorinv/modules/detail-summary.test.ts
+git commit -m "feat: 增加出库单前端聚合接口与汇总逻辑"
+```
+
+## 任务 5:实现库存选择与手工汇总弹窗
+
+- [ ] **步骤 1:实现库存选择弹窗**
+
+`inventory-select.vue` 接收主弹窗传入的 `storId`,表单筛选物料编码、箱号、子卷号,Vxe Grid 使用服务端分页和多选。确认时只收集选中行的 `vehicleCode`,调用 `expandAvailableInventory`;返回结果逐子卷映射为明细,`planQty` 默认为 `availableQty`。勾选过程不自动修改其他子卷的选中状态。
+
+- [ ] **步骤 2:实现手工汇总弹窗**
+
+复用 `views/base/materialbase/components/MaterialSelectModal.vue` 选择一个物料,填写大于 0 的总出库重量和备注;确认对象必须显式包含:
+
+```ts
+{
+ materialId,
+ materialCode,
+ materialName,
+ planQty,
+ qtyUnitId,
+ qtyUnitName,
+ vehicleCode: undefined,
+ pcsn: undefined,
+ sapBatchNo: undefined,
+ remark,
+}
+```
+
+- [ ] **步骤 3:运行前端类型检查**
+
+```bash
+pnpm --dir nl-ui/nl-ui-admin-vben/yudao-ui-admin-vben --filter @vben/web-antdv-next typecheck
+```
+
+预期:两个弹窗 props、emit、API 结果映射均无类型错误。
+
+- [ ] **步骤 4:提交两个弹窗**
+
+```bash
+git add nl-ui/nl-ui-admin-vben/yudao-ui-admin-vben/apps/web-antdv-next/src/views/wms/iostorinv/modules/inventory-select.vue nl-ui/nl-ui-admin-vben/yudao-ui-admin-vben/apps/web-antdv-next/src/views/wms/iostorinv/modules/manual-detail.vue
+git commit -m "feat: 增加出库库存选择和手工汇总弹窗"
+```
+
+## 任务 6:按参考图重构新增主弹窗
+
+- [ ] **步骤 1:实现主表布局与默认值**
+
+重构 `form.vue` 为宽屏弹窗:单据号显示“保存时自动生成”;仓库和业务类型为必选下拉;状态只读“生成”;业务日期使用 `dayjs()` 默认为当天;明细数和总重量只读;备注跨列。仓库使用 `getBsrealStorAttrSimpleList`,业务类型从现有字典缓存中过滤出库业务类型。
+
+- [ ] **步骤 2:实现明细表与两种入口**
+
+列顺序严格按确认原型:序号、物料编码、物料名称、箱号、子卷号、SAP 批次号、出库重量、单位、源单号、明细备注、操作。出库重量可编辑;删除、库存返回、手工汇总返回均调用 `summarizeDetails` 更新只读汇总。
+
+- [ ] **步骤 3:实现仓库切换和提交**
+
+仓库值发生实际变化且明细非空时清空明细。提交只发送 `billType`、`storId`、`bizDate`、`remark` 及 `wms_iostorinvdtl` 可持久化字段;不得发送箱号、物料名称、SAP 批次号、单据号、状态和汇总字段。
+
+- [ ] **步骤 4:运行前端测试、类型检查与构建**
+
+```bash
+pnpm --dir nl-ui/nl-ui-admin-vben/yudao-ui-admin-vben vitest run apps/web-antdv-next/src/views/wms/iostorinv/modules/detail-summary.test.ts --dom
+pnpm --dir nl-ui/nl-ui-admin-vben/yudao-ui-admin-vben --filter @vben/web-antdv-next typecheck
+pnpm --dir nl-ui/nl-ui-admin-vben/yudao-ui-admin-vben --filter @vben/web-antdv-next build
+```
+
+预期:测试、类型检查和生产构建均以退出码 0 完成。
+
+- [ ] **步骤 5:提交主弹窗**
+
+```bash
+git add nl-ui/nl-ui-admin-vben/yudao-ui-admin-vben/apps/web-antdv-next/src/views/wms/iostorinv/modules/form.vue
+git commit -m "feat: 按参考布局实现出库单新增页"
+```
+
+## 任务 7:端到端核对与最终验证
+
+- [ ] **步骤 1:核对数据库字段边界**
+
+检查最终 diff,确认没有 DDL、迁移脚本或 DO 新字段;前端展示字段没有进入 `createOutbound` 请求;所有明细只通过 `IostorinvDtlMapper` 写入 `wms_iostorinvdtl`。
+
+- [ ] **步骤 2:运行后端完整模块验证**
+
+```bash
+mvn -pl nl-module-wms/nl-module-wms-server -am clean test -DskipTests=false
+```
+
+预期:WMS server 及依赖模块编译成功,测试失败数为 0。
+
+- [ ] **步骤 3:运行前端完整验证**
+
+```bash
+pnpm --dir nl-ui/nl-ui-admin-vben/yudao-ui-admin-vben vitest run apps/web-antdv-next/src/views/wms/iostorinv/modules/detail-summary.test.ts --dom
+pnpm --dir nl-ui/nl-ui-admin-vben/yudao-ui-admin-vben --filter @vben/web-antdv-next typecheck
+pnpm --dir nl-ui/nl-ui-admin-vben/yudao-ui-admin-vben --filter @vben/web-antdv-next build
+```
+
+预期:三条命令均退出码 0。
+
+- [ ] **步骤 4:人工验收关键流程**
+
+启动前后端后确认:新增页默认值正确;未选仓库不能选择库存;选择任一库存记录会按箱号带回全部子卷;勾选本身不联动;手工汇总的箱号、子卷号、SAP 批次号为空;明细汇总实时变化;切换仓库清空明细;保存后主表和全部明细同时存在。
+
+- [ ] **步骤 5:提交验证阶段必要修正**
+
+仅当验证产生修正时提交相关文件:
+
+```bash
+git add nl-module-wms nl-ui/nl-ui-admin-vben/yudao-ui-admin-vben/apps/web-antdv-next/src/api/wms/iostorinv nl-ui/nl-ui-admin-vben/yudao-ui-admin-vben/apps/web-antdv-next/src/views/wms/iostorinv
+git commit -m "fix: 完善出库单新增流程验证问题"
+```