diff --git a/docs/superpowers/specs/2026-07-22-outbound-create-design.md b/docs/superpowers/specs/2026-07-22-outbound-create-design.md deleted file mode 100644 index b41f0264..00000000 --- a/docs/superpowers/specs/2026-07-22-outbound-create-design.md +++ /dev/null @@ -1,256 +0,0 @@ -# 出库单新增页面设计 - -## 概述 - -将出入库单主表(`iostorinv`)的新增弹窗从简单表单重构为出库单新增页面,包含主表表单 + 出库明细 table。参考原型图 `.image/出库新增.png`。 - -### 范围 - -- 仅涉及**出库**场景(`ioType = "OUT"`) -- 后端:新增 `createWithDetails` 接口,新增库存查询 Mapper SQL -- 前端:重构 `form.vue`,新增库存选择弹窗和手动新增汇总弹窗 -- **不新增数据库字段** - ---- - -## 主表字段映射 - -| 页面字段 | 数据来源/逻辑 | 对应 DO 字段 | 必填 | 可编辑 | -|---------|-------------|-------------|------|-------| -| 仓库 | `wms_bsrealstorattr` 启用列表 | `storId / storCode / storName` | 是 | 是 | -| 业务类型 | 字典 `out_bill_type` | `billType` | 是 | 是 | -| 单据号 | `CodeGenApi.generate("IO_CODE")` | `billCode` | 是 | 否(自动生成) | -| 业务日期 | 默认当天 | `bizDate` | 是 | 是 | -| 单据状态 | 固定"生成" | `billStatus` | 是 | 否 | -| 明细数 | `detailTable.length` | `detailCount` | 是 | 否(自动联动) | -| 总重量 | `sum(detail.planQty)` | `totalWeight` | 是 | 否(自动联动) | -| 备注 | 手动输入 | `remark` | 否 | 是 | -| 出入类型 | 固定 "OUT" | `ioType` | 是 | 否 | - ---- - -## 出库明细字段映射(仅使用现有 DO 字段) - -| 列 | 对应 DO 字段 | 数据来源 | 说明 | -|----|------------|---------|------| -| 序号 | `seqNo` | 自动编号 1,2,3... | | -| 箱号 | *(不存)* | `wms_group_plate.vehicle_code` | 仅页面展示,不入库 | -| 物料编码 | `materialCode` | 库存带入 / 手动选择 | | -| 物料名称 | *(不存)* | `MaterialBaseApi` 实时反查 | 仅页面展示 | -| 子卷号 | `pcsn` | `wms_group_plate.pcsn` 或手动输入 | 方式 B 时为空 | -| 出库重量 | `planQty` | 库存 `qty - frozenQty`(可编辑)或手动输入 | | -| 未分配数量 | `unassignQty` | 初始 = `planQty` | | -| 已分配数量 | `assignQty` | 初始 = 0 | | -| 单位 | `qtyUnitName` | 库存带入 / 物料默认单位 | | -| 批次 | `pcsn` | 同子卷号 | | -| 备注 | `remark` | 可选输入 | | -| 来源单据号 | `sourceBillCode` | `group_plate.extCode` | 库存带入时有值 | -| 来源单据类型 | `sourceBillType` | `group_plate.extType` | 库存带入时有值 | -| 来源单据明细 | `sourceBilldtlId` | `group_plate.extDtlCode` | 库存带入时有值 | - ---- - -## 明细新增的两种方式 - -### 方式 A:从库存选择带入 - -1. 查询逻辑: - - 通过 `wms_structattr.storagevehicle_code = wms_group_plate.vehicle_code` 关联仓库 - - 过滤条件:`storId` = 主表所选仓库、`status` = 可用、`qty - frozenQty > 0` - - 支持物料编码、批次、载具编码等可选筛选条件 -2. **一对多联动**:一个 `vehicle_code` 对应多个 `pcsn`,勾选其中一条时,同一 `vehicle_code` 的所有记录自动勾选并带入明细 -3. 出库重量默认带入可用数量(`qty - frozenQty`),用户可编辑 - -### 方式 B:手动新增汇总 - -1. 弹出简单表单:选择物料、输入出库重量、批次(可选)、备注(可选) -2. 单位自动带出物料的基本计量单位 -3. 箱号、子卷号为空(仅页面不展示,不存 `pcsn`) - ---- - -## 后端设计 - -### 新增接口 - -**`POST /wms/iostor-inv/create-with-details`** - -```java -@PostMapping("/create-with-details") -@Operation(summary = "创建出库单(含明细)") -@PreAuthorize("@ss.hasPermission('wms:iostor-inv:create')") -public CommonResult createIostorInvWithDetails( - @Valid @RequestBody IostorInvWithDetailsSaveReqVO reqVO) -``` - -### 请求 VO - -```java -public class IostorInvWithDetailsSaveReqVO { - // 主表字段 - private String billType; // 业务类型 - private String storId; // 仓库标识 - private String storCode; // 仓库编码 - private String storName; // 仓库名称 - private LocalDateTime bizDate; // 业务日期 - private String remark; // 备注 - - // 明细列表 - private List details; -} - -public class DetailVO { - private String materialCode; - private String materialId; - private String pcsn; // 子卷号/批次 - private BigDecimal planQty; // 出库重量 - private String qtyUnitId; - private String qtyUnitName; - private String remark; - private String sourceBillCode; // 来源单据号 - private String sourceBillType; // 来源单据类型 - private String sourceBilldtlId; // 来源单据明细标识 -} -``` - -### Service 层逻辑 - -1. 调用 `CodeGenApi.generate("IO_CODE")` 生成单据号 -2. 设置 `ioType = "OUT"`、`billStatus = "生成"` -3. 计算 `detailCount = details.size()`、`totalWeight = sum(details.planQty)` -4. 插入主表 `wms_iostorinv` -5. 批量插入明细 `wms_iostorinvdtl`: - - `planQty` = 出库重量 - - `unassignQty` = `planQty`(初始等于计划数量) - - `assignQty` = 0 - - `seqNo` 自动编号 -6. `@Transactional` 保证原子性 - -### 库存查询(Mapper XML) - -在 `IostorinvDtlMapper.xml` 中新增通用查询 SQL,方便后续扩展: - -```xml - -``` - -Controller 通过 Service → Mapper 调用,不新增独立接口。 - -### 数据关联图 - -``` -wms_bsrealstorattr (仓库) - │ - │ storId - ▼ -wms_structattr (仓位) - │ storagevehicle_code - │ - ▼ -wms_group_plate (库存/组盘) - │ - │ 选择带入 - ▼ -wms_iostorinvdtl (出库明细) - │ - │ iostorinvId - ▼ -wms_iostorinv (出库单主表) -``` - ---- - -## 前端设计 - -### 文件结构 - -``` -nl-ui/.../views/wms/iostorinv/ -├── index.vue # 列表页 (不改) -├── data.ts # 列表 schema (不改) -├── modules/ -│ ├── form.vue # [重构] 大弹窗:主表表单 + 明细 table -│ ├── inventory-select.vue # [新增] 库存选择弹窗 -│ └── manual-detail.vue # [新增] 手动新增汇总弹窗 - -nl-ui/.../api/wms/iostorinv/ -└── index.ts # [扩展] 新增 createIostorInvWithDetails -``` - -### form.vue 重构 - -- 弹窗宽度 1200px 或全屏 -- 主表区域 `useVbenForm`,水平布局,字段排列参照原型图 -- 明细 table 内嵌在表单下方,使用 vxe-table -- 联动: - - 仓库切换 → 清空明细 - - 明细增删 → 自动更新明细数、总重量 - - 单据号在弹窗打开时通过预览接口展示,提交时由后端生成 - -### inventory-select.vue(库存选择弹窗) - -- 搜索区:物料编码、批次、载具编码 -- 表格多选 + 同 vehicle_code 联动勾选 -- 可编辑出库重量列(默认可用数量) -- 确认后返回选中数据 - -### manual-detail.vue(手动新增弹窗) - -- 物料选择器(可复用 MaterialSelectModal) -- 出库重量输入 -- 批次输入(可选) -- 备注输入(可选) - ---- - -## 错误处理 - -| 场景 | 处理方式 | -|------|---------| -| 单据号生成失败(CodeGenApi 不可用) | 抛出异常,事务回滚,前端提示"单据号生成失败" | -| 未选择仓库时点"新增明细(方式A)" | 前端拦截,提示"请先选择仓库" | -| 提交时明细为空 | 后端校验,返回"请至少添加一条明细" | -| 库存已被其他单据消耗(并发) | 提交时校验可用数量,不足则回滚提示 | -| Feign 调用超时 | 兜底:打印日志 + 抛出业务异常 | - ---- - -## 不考虑的内容 - -- 入库单场景(本次只做出库) -- 编辑/修改出库单明细(本次只做新增) -- 明细的分配/确认流程 -- 回传/上传逻辑