Files
2026-09-30 11:11:54 +08:00

325 lines
14 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 订单与出库
约定见 [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` 只表示作业关闭,不再改库存。