325 lines
14 KiB
Markdown
325 lines
14 KiB
Markdown
# 订单与出库
|
||
|
||
约定见 [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` 只表示作业关闭,不再改库存。
|