# 订单与出库 约定见 [00-conventions.md](./00-conventions.md)。库存变化见 [40-inventory.md](./40-inventory.md)。Phase 1 锁定。订单与出库单在 Phase 10 写迁移,波次、拣货、打包、发货在 Phase 11 写迁移。 订单是统一需求模型。表内没有平台专有字段。上游平台、店铺、ERP 只通过 `channel`、`source_code`、`external_no` 表达。平台报文的转换放在 Connector,不进这些表。 `wms_order_line` 对应领域模型里的 order item。 承运商主数据在 Phase 12。发货单上的 `carrier_code`、`tracking_no` 先作为普通字符串保存。 ## wms_order 订单 用途:出库需求。手工单、API、ERP、电商平台都落这张表。 | 字段 | 类型 | 可空 | 默认 | 说明 | | --- | --- | --- | --- | --- | | id | BIGINT UNSIGNED | N | AUTO_INCREMENT | 内部主键 | | tenant_code | VARCHAR(32) | N | | 租户编码 | | order_no | VARCHAR(64) | N | | 系统订单号 | | warehouse_code | VARCHAR(32) | N | | 发货仓库 | | customer_code | VARCHAR(64) | N | | 货主 | | channel | VARCHAR(32) | N | | 来源通道 | | source_code | VARCHAR(64) | Y | | 来源实例,如店铺或 ERP 连接编码;手工单为空 | | external_no | VARCHAR(64) | Y | | 上游单号;手工单为空 | | status | VARCHAR(32) | N | draft | 状态 | | version | INT UNSIGNED | N | 0 | 乐观锁 | | remark | VARCHAR(500) | Y | | | | created_at | TIMESTAMP | Y | | | | updated_at | TIMESTAMP | Y | | | 索引: | 名称 | 类型 | 字段 | 支撑的查询 | | --- | --- | --- | --- | | uk_tenant_order_no | UNIQUE | (tenant_code, order_no) | 按订单号查找 | | uk_tenant_source_external | UNIQUE | (tenant_code, source_code, external_no) | 上游单去重 | | idx_tenant_status_created | INDEX | (tenant_code, status, created_at) | 列表 | `channel`:`manual`、`api`、`erp`、`marketplace`。 `channel = manual` 时 `source_code` 与 `external_no` 必须为 `NULL`。其他通道两者必填。MySQL 唯一索引对 `NULL` 不判重,因此多张手工单可以并存。 状态: ``` draft → confirmed → processing → shipped → completed draft → cancelled confirmed → cancelled processing → cancelled (关联出库单尚未 shipped) ``` `completed`、`cancelled` 为终态。出库单进入 `shipped` 时,订单改为 `shipped`。不软删除。 ## wms_order_line 订单明细 用途:订单要发的 SKU 与数量。 | 字段 | 类型 | 可空 | 默认 | 说明 | | --- | --- | --- | --- | --- | | id | BIGINT UNSIGNED | N | AUTO_INCREMENT | 内部主键 | | tenant_code | VARCHAR(32) | N | | 租户编码 | | order_no | VARCHAR(64) | N | | 订单号 | | line_no | INT UNSIGNED | N | | 行号 | | sku_code | VARCHAR(64) | N | | SKU | | qty | DECIMAL(16,4) | N | | 需求数量,大于 0 | | allocated_qty | DECIMAL(16,4) | N | 0 | 已分配,由出库分配回写 | | created_at | TIMESTAMP | Y | | | | updated_at | TIMESTAMP | Y | | | 索引: | 名称 | 类型 | 字段 | 支撑的查询 | | --- | --- | --- | --- | | uk_tenant_order_line | UNIQUE | (tenant_code, order_no, line_no) | 定位明细 | 关联:`wms_order`。`allocated_qty <= qty`。 ## wms_outbound 出库单 用途:仓库执行单。第一阶段业务上一单一张出库单;表允许同一 `order_no` 对应多张出库单。无订单的手工出库 `order_no` 为空。 | 字段 | 类型 | 可空 | 默认 | 说明 | | --- | --- | --- | --- | --- | | id | BIGINT UNSIGNED | N | AUTO_INCREMENT | 内部主键 | | tenant_code | VARCHAR(32) | N | | 租户编码 | | outbound_no | VARCHAR(64) | N | | 出库单号 | | order_no | VARCHAR(64) | Y | | 订单号 | | warehouse_code | VARCHAR(32) | N | | 仓库 | | customer_code | VARCHAR(64) | N | | 货主 | | status | VARCHAR(32) | N | draft | 状态 | | version | INT UNSIGNED | N | 0 | 乐观锁 | | created_at | TIMESTAMP | Y | | | | updated_at | TIMESTAMP | Y | | | 索引: | 名称 | 类型 | 字段 | 支撑的查询 | | --- | --- | --- | --- | | uk_tenant_outbound_no | UNIQUE | (tenant_code, outbound_no) | 按出库单号查找 | | idx_tenant_order | INDEX | (tenant_code, order_no) | 订单下的出库单 | | idx_tenant_status_created | INDEX | (tenant_code, status, created_at) | 列表 | 状态(与领域模型一致,并补上取消): ``` draft → allocated → picking → picked → packing → packed → shipped → completed draft → cancelled allocated → cancelled (必须释放预占) ``` `completed`、`cancelled` 为终态。禁止跳步。`picking` 之后取消不在本阶段打开,避免已拣库存没有回退规则。状态变更写 `wms_status_log`,`biz_type = outbound`。 短拣不新增头状态。行上的已拣数量可以小于需求数量,头状态仍按上面的路径走;是否允许短拣完成,在 Phase 11 做业务校验。 ## wms_outbound_line 出库明细 用途:出库单的 SKU 行。 | 字段 | 类型 | 可空 | 默认 | 说明 | | --- | --- | --- | --- | --- | | id | BIGINT UNSIGNED | N | AUTO_INCREMENT | 内部主键 | | tenant_code | VARCHAR(32) | N | | 租户编码 | | outbound_no | VARCHAR(64) | N | | 出库单号 | | line_no | INT UNSIGNED | N | | 行号 | | order_line_no | INT UNSIGNED | Y | | 对应订单行,无订单时为空 | | sku_code | VARCHAR(64) | N | | SKU | | qty | DECIMAL(16,4) | N | | 需求数量,大于 0 | | allocated_qty | DECIMAL(16,4) | N | 0 | 已分配 | | picked_qty | DECIMAL(16,4) | N | 0 | 已拣 | | packed_qty | DECIMAL(16,4) | N | 0 | 已打包 | | shipped_qty | DECIMAL(16,4) | N | 0 | 已发 | | created_at | TIMESTAMP | Y | | | | updated_at | TIMESTAMP | Y | | | 索引: | 名称 | 类型 | 字段 | 支撑的查询 | | --- | --- | --- | --- | | uk_tenant_outbound_line | UNIQUE | (tenant_code, outbound_no, line_no) | 定位明细 | 关联:数量由分配、拣货、打包、发货事务回写。`shipped_qty <= packed_qty <= picked_qty <= allocated_qty <= qty`。 ## wms_outbound_allocation 库存分配 用途:出库行占住的具体库位与批次。分配成功才把可用转为预占。 | 字段 | 类型 | 可空 | 默认 | 说明 | | --- | --- | --- | --- | --- | | id | BIGINT UNSIGNED | N | AUTO_INCREMENT | 内部主键 | | tenant_code | VARCHAR(32) | N | | 租户编码 | | allocation_no | VARCHAR(64) | N | | 分配号 | | outbound_no | VARCHAR(64) | N | | 出库单号 | | line_no | INT UNSIGNED | N | | 出库行号 | | sku_code | VARCHAR(64) | N | | SKU | | location_code | VARCHAR(64) | N | | 库位,不能是在途空库位 | | batch_no | VARCHAR(64) | N | '' | 批次 | | qty | DECIMAL(16,4) | N | | 分配数量,大于 0 | | status | VARCHAR(32) | N | allocated | 状态 | | created_at | TIMESTAMP | Y | | | | updated_at | TIMESTAMP | Y | | | 索引: | 名称 | 类型 | 字段 | 支撑的查询 | | --- | --- | --- | --- | | uk_tenant_allocation_no | UNIQUE | (tenant_code, allocation_no) | 按分配号查找 | | idx_tenant_outbound_line | INDEX | (tenant_code, outbound_no, line_no) | 某行的分配 | 状态:`allocated → picked`,`allocated → cancelled`。 创建时原子更新库存(可用减、预占加),流水 `biz_type = reserve`,`biz_no = outbound_no`。取消时反向,`biz_type = release`。拣货确认时预占减、已拣加,`biz_type = pick`,本行改为 `picked`。 全部需求行 `allocated_qty = qty` 后,出库单才从 `draft` 到 `allocated`。库存不足则整单分配回滚,不产生部分成功的预占。 ## wms_wave 波次 用途:把多张出库单放进一次拣货。不使用波次时,拣货任务的 `wave_no` 为空,按单拣货。 | 字段 | 类型 | 可空 | 默认 | 说明 | | --- | --- | --- | --- | --- | | id | BIGINT UNSIGNED | N | AUTO_INCREMENT | 内部主键 | | tenant_code | VARCHAR(32) | N | | 租户编码 | | wave_no | VARCHAR(64) | N | | 波次号 | | warehouse_code | VARCHAR(32) | N | | 仓库 | | status | VARCHAR(32) | N | draft | 状态 | | version | INT UNSIGNED | N | 0 | 乐观锁 | | created_at | TIMESTAMP | Y | | | | updated_at | TIMESTAMP | Y | | | 索引: | 名称 | 类型 | 字段 | 支撑的查询 | | --- | --- | --- | --- | | uk_tenant_wave_no | UNIQUE | (tenant_code, wave_no) | 按波次号查找 | | idx_tenant_wh_status | INDEX | (tenant_code, warehouse_code, status) | 仓库内的波次 | 状态:`draft → released → picking → completed`,`draft → cancelled`。释放波次时,关联出库单从 `allocated` 进入 `picking`。 ## wms_wave_outbound 波次与出库单 用途:波次包含哪些出库单。一张出库单同时只属于一个未取消的波次。 | 字段 | 类型 | 可空 | 默认 | 说明 | | --- | --- | --- | --- | --- | | id | BIGINT UNSIGNED | N | AUTO_INCREMENT | 内部主键 | | tenant_code | VARCHAR(32) | N | | 租户编码 | | wave_no | VARCHAR(64) | N | | 波次号 | | outbound_no | VARCHAR(64) | N | | 出库单号 | | created_at | TIMESTAMP | Y | | | | updated_at | TIMESTAMP | Y | | | 索引: | 名称 | 类型 | 字段 | 支撑的查询 | | --- | --- | --- | --- | | uk_tenant_wave_outbound | UNIQUE | (tenant_code, wave_no, outbound_no) | 防止重复加入同一波次 | | idx_tenant_outbound | INDEX | (tenant_code, outbound_no) | 查出库单所在波次 | 一张出库单同时只能属于一个未取消波次,由应用在加入时检查,不靠删除行来腾出唯一约束。波次取消后这些行保留,出库单回到 `allocated`。不软删除。 ## wms_pick_task 拣货任务 用途:拣货执行行。由分配生成。 | 字段 | 类型 | 可空 | 默认 | 说明 | | --- | --- | --- | --- | --- | | id | BIGINT UNSIGNED | N | AUTO_INCREMENT | 内部主键 | | tenant_code | VARCHAR(32) | N | | 租户编码 | | picking_no | VARCHAR(64) | N | | 拣货任务号 | | wave_no | VARCHAR(64) | Y | | 波次,按单拣货时为空 | | outbound_no | VARCHAR(64) | N | | 出库单号 | | allocation_no | VARCHAR(64) | N | | 分配号 | | sku_code | VARCHAR(64) | N | | SKU | | location_code | VARCHAR(64) | N | | 拣货库位 | | batch_no | VARCHAR(64) | N | '' | 批次 | | qty | DECIMAL(16,4) | N | | 应拣数量 | | picked_qty | DECIMAL(16,4) | N | 0 | 实拣数量 | | status | VARCHAR(32) | N | pending | 状态 | | created_at | TIMESTAMP | Y | | | | updated_at | TIMESTAMP | Y | | | 索引: | 名称 | 类型 | 字段 | 支撑的查询 | | --- | --- | --- | --- | | uk_tenant_picking_no | UNIQUE | (tenant_code, picking_no) | 按任务号查找 | | uk_tenant_allocation | UNIQUE | (tenant_code, allocation_no) | 一条分配一个任务 | | idx_tenant_outbound | INDEX | (tenant_code, outbound_no, status) | 出库单的拣货任务 | 状态:`pending → picked`。确认拣货时 `picked_qty` 等于 `qty`(短拣规则 Phase 11 再放开),并完成分配行的库存桶转换。该出库单全部分配都已 `picked` 后,出库单才到 `picked`。 ## wms_package 包裹 用途:复核打包结果。一张出库单可有多个包裹。 | 字段 | 类型 | 可空 | 默认 | 说明 | | --- | --- | --- | --- | --- | | id | BIGINT UNSIGNED | N | AUTO_INCREMENT | 内部主键 | | tenant_code | VARCHAR(32) | N | | 租户编码 | | package_no | VARCHAR(64) | N | | 包裹号 | | outbound_no | VARCHAR(64) | N | | 出库单号 | | status | VARCHAR(32) | N | open | 状态 | | created_at | TIMESTAMP | Y | | | | updated_at | TIMESTAMP | Y | | | 索引: | 名称 | 类型 | 字段 | 支撑的查询 | | --- | --- | --- | --- | | uk_tenant_package_no | UNIQUE | (tenant_code, package_no) | 按包裹号查找 | | idx_tenant_outbound | INDEX | (tenant_code, outbound_no) | 出库单的包裹 | 状态:`open → sealed`。全部应发数量进入已封箱包裹后,出库单从 `packing` 到 `packed`。打包不改变库存桶,只增加明细 `packed_qty`。 ## wms_package_line 包裹明细 用途:包裹里的 SKU 数量。 | 字段 | 类型 | 可空 | 默认 | 说明 | | --- | --- | --- | --- | --- | | id | BIGINT UNSIGNED | N | AUTO_INCREMENT | 内部主键 | | tenant_code | VARCHAR(32) | N | | 租户编码 | | package_no | VARCHAR(64) | N | | 包裹号 | | line_no | INT UNSIGNED | N | | 行号 | | sku_code | VARCHAR(64) | N | | SKU | | qty | DECIMAL(16,4) | N | | 数量,大于 0 | | created_at | TIMESTAMP | Y | | | | updated_at | TIMESTAMP | Y | | | 索引: | 名称 | 类型 | 字段 | 支撑的查询 | | --- | --- | --- | --- | | uk_tenant_package_line | UNIQUE | (tenant_code, package_no, line_no) | 定位明细 | 关联:`wms_package`。封箱后不改行。 ## wms_shipment 发货 用途:出库单发出的记录。物流渠道主数据尚未建表,承运商与运单号先记在这里。 | 字段 | 类型 | 可空 | 默认 | 说明 | | --- | --- | --- | --- | --- | | id | BIGINT UNSIGNED | N | AUTO_INCREMENT | 内部主键 | | tenant_code | VARCHAR(32) | N | | 租户编码 | | shipment_no | VARCHAR(64) | N | | 发货单号 | | outbound_no | VARCHAR(64) | N | | 出库单号 | | package_no | VARCHAR(64) | Y | | 包裹号,整单发货可空 | | carrier_code | VARCHAR(64) | Y | | 承运商编码,Phase 12 再约束到主数据 | | tracking_no | VARCHAR(64) | Y | | 运单号 | | status | VARCHAR(32) | N | shipped | 状态 | | shipped_at | TIMESTAMP | Y | | 发货时间 | | created_at | TIMESTAMP | Y | | | | updated_at | TIMESTAMP | Y | | | 索引: | 名称 | 类型 | 字段 | 支撑的查询 | | --- | --- | --- | --- | | uk_tenant_shipment_no | UNIQUE | (tenant_code, shipment_no) | 按发货单号查找 | | idx_tenant_outbound | INDEX | (tenant_code, outbound_no) | 出库单的发货记录 | | idx_tenant_tracking | INDEX | (tenant_code, tracking_no) | 按运单号查 | 状态:`shipped`。本阶段发货即终态。同一事务把已拣数量减掉(离开仓库),流水 `biz_type = ship`,并回写出库明细 `shipped_qty`。出库单改为 `shipped`,若有订单则订单改为 `shipped`。之后出库单再进入 `completed` 只表示作业关闭,不再改库存。