Appearance
【单据】出库
出库单是 WMS 发出物料的业务凭证,由主表 + 明细子表两张表实现,通过 type 字段区分 3 种出库场景,所有类型共用同一套状态机与库存事务逻辑(详见 《【库存】库存记录、流水、统计》§3 库存事务)。
整体结构与 《【单据】入库》 高度同构,差异点:
- 关联往来企业:出库关联客户(
type为客户或客户/供应商),入库关联供应商。 - 明细选 SKU:出库必须基于已有库存,用 InventorySelect.vue 库存选择器(仅含
quantity > 0的库存行);入库可用 ItemSkuSelect.vue SKU 选择器。 - 库存事务方向:完成出库时明细
quantity取负数写入库存事务,触发扣减并由库存事务在内存计算时校验afterQuantity ≥ 0。 出库单模块由yudao-module-wms后端模块的order.shipment包实现,前端实现在@/views/wms/order/shipment目录。
1. 出库单
出库单,由 WmsShipmentOrderController 提供接口(/wms/shipment-order);明细子表由 WmsShipmentOrderDetailController 提供接口。
1.1 主表表结构
省略 creator/create_time/updater/update_time/deleted/tenant_id 等通用字段
sql
CREATE TABLE `wms_shipment_order` (
`id` bigint NOT NULL AUTO_INCREMENT COMMENT '编号',
`no` varchar(64) NOT NULL COMMENT '出库单号',
`type` tinyint NOT NULL COMMENT '出库类型',
`status` tinyint NOT NULL DEFAULT '0' COMMENT '出库状态',
`merchant_id` bigint DEFAULT NULL COMMENT '客户编号',
`order_time` datetime NOT NULL COMMENT '单据日期',
`biz_order_no` varchar(64) DEFAULT NULL COMMENT '业务单号',
`remark` varchar(255) DEFAULT NULL COMMENT '备注',
`warehouse_id` bigint NOT NULL COMMENT '仓库编号',
`total_quantity` decimal(14,3) DEFAULT NULL COMMENT '总数量',
`total_price` decimal(14,2) DEFAULT NULL COMMENT '总金额',
PRIMARY KEY (`id`),
UNIQUE KEY `uk_no` (`no`)
) ENGINE=InnoDB COMMENT='WMS 出库单';① no 出库单号,新增时由前端默认按 CK + 月日 + 4 位随机数 生成(详见 《功能开启》 ①),允许手动修改,由后端 validateShipmentOrderNoUnique 校验全局唯一。
② 枚举 type 出库类型(WmsShipmentOrderTypeEnum),3 个值共用同一套状态机与库存事务逻辑: | 值 | 类型 | 业务场景 | | 200 | 退货出库 | 给供应商退货 | | 201 | 销售出库 | 销售发货给客户(最常见) | | 202 | 生产出库 | 领料给产线消耗 |
③ status 与入库单共用 WmsOrderStatusEnum(0 = 草稿,4 = 已完成,5 = 已作废)。
④ merchant_id 关联 wms_merchant 表,可空(如生产出库无客户)。传入时由 WmsMerchantServiceImpl 的 validateCustomerMerchantExists 校验类型必须为客户或客户/供应商。
⑤ total_quantity / total_price 保存时由后端 fillShipmentOrderTotal 按明细自动汇总写入。
该表包含一个子表:
wms_shipment_order_detail(出库明细):在新增 / 编辑弹窗中维护,至少 1 条(完成出库时由validateShipmentOrderDetailListExists强校验)。
1.2 明细子表结构
sql
CREATE TABLE `wms_shipment_order_detail` (
`id` bigint NOT NULL AUTO_INCREMENT COMMENT '编号',
`order_id` bigint NOT NULL COMMENT '出库单编号',
`sku_id` bigint NOT NULL COMMENT '商品 SKU 编号',
`warehouse_id` bigint NOT NULL COMMENT '仓库编号',
`quantity` decimal(14,3) NOT NULL COMMENT '出库数量',
`price` decimal(14,2) DEFAULT NULL COMMENT '单价',
`total_price` decimal(14,2) DEFAULT NULL COMMENT '行金额',
PRIMARY KEY (`id`)
) ENGINE=InnoDB COMMENT='WMS 出库单明细';字段结构与 入库明细表 完全一致:order_id 关联主表、sku_id 出库 SKU、warehouse_id 从主表继承的冗余字段、quantity / price / total_price 数量金额。
唯一差异在前端:SKU 必须从库存选择器中选择(仅 quantity > 0 的 SKU + 仓库行),且 quantity 不能超过可用库存。
1.3 状态流转
与入库单 §1.3 完全一致(共用 WmsOrderStatusEnum,4 个操作方法签名同形): | 状态 | 值 | 可执行操作 | | 草稿 | 0 | 编辑、完成出库、作废、删除 | | 已完成 | 4 | — | | 已作废 | 5 | 删除 |
四个核心操作方法:创建(createShipmentOrder)、修改(updateShipmentOrder)、完成出库(completeShipmentOrder)、作废(cancelShipmentOrder)。状态机迁移全部使用 updateByIdAndStatus(id, expectedStatus, newStatus) 乐观锁。
删除操作 deleteShipmentOrder 只允许在 草稿 或 已作废 状态下执行。
1.4 管理后台
对应 [WMS 系统 -> 出库管理] 菜单,对应 yudao-ui-admin-vue3 项目的 @/views/wms/order/shipment 目录。
列表
搜索条件、列表展示结构与入库单一致,仅"供应商"换为"客户"(用 MerchantSelect.vue customer 限定为客户或客户/供应商)、"入库类型"换为"出库类型"。

新增
通过弹窗 ShipmentOrderForm.vue 完成。表单上半部分是单据基础信息(出库单号 + 自动生成、出库类型、单据日期、仓库、客户、业务单号、备注),下半部分是明细子表。
明细行的 SKU 选择通过 InventorySelect.vue 库存选择器弹出,必须先选仓库才能打开(否则提示"请先选择仓库"),所选 SKU 自动带入"可用库存"作为出库数量上限。

修改
弹窗结构与新增相同,仅在草稿状态下可打开。明细的 SKU 选择器同样必须先有仓库;切换仓库会清空已有明细。
完成出库
编辑弹窗底部的「完成出库」按钮触发(仅草稿状态显示)。前端先做脏检查:若表单有改动则先调 updateShipmentOrder 保存,再调 /wms/shipment-order/complete?id=。后端在同一事务内:① CAS 翻状态为已完成;② 调用 changeInventory 扣减库存(明细 quantity 取负、total_price 同步取负)。若库存不足,库存事务抛 INVENTORY_QUANTITY_NOT_ENOUGH 异常并整单回滚,单据保持草稿状态。
作废
编辑弹窗底部的「作废」按钮触发(仅草稿状态显示),二次确认后调 /wms/shipment-order/cancel?id=。作废后单据进入终态,可被删除。
1.5 库存影响
完成出库时调用 inventoryService.changeInventory 写入库存与流水:
- 库存记录:按明细的
(sku_id, warehouse_id)锁定wms_inventory行,quantity扣减明细出库数量。若 `afterQuantity
2. 网页打印
ShipmentOrderPrint.vue(@/views/wms/order/shipment/ShipmentOrderPrint.vue)通过 v-print 指令将单据渲染为可打印 HTML:
- 页头:标题"出库单" + 右上角的出库单号 CODE39 条码。
- 基础信息:3 列网格展示出库单号、出库类型、仓库、状态、单据日期、客户、业务单号、总数量、总金额,以及创建 / 更新人 + 时间、备注。
- 明细表格:商品信息、规格信息、数量、单价、金额 5 列。

