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

237 lines
10 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)。数量桶的业务含义见 [01-er-overview.md](./01-er-overview.md) 与 [05-inventory.md](../05-inventory.md)。Phase 1 锁定,Phase 7、8 写迁移。
冻结、锁定不单独建单据表。冻结是可用与 `hold_qty` 的对转;订单锁定是可用与 `reserved_qty` 的对转,锁定对象记在 `wms_outbound_allocation` 和流水的 `biz_no` 上。
## wms_batch 批次
用途:批次属性。不管理批次的库存使用 `batch_no = ''`,不写本表。
| 字段 | 类型 | 可空 | 默认 | 说明 |
| --- | --- | --- | --- | --- |
| id | BIGINT UNSIGNED | N | AUTO_INCREMENT | 内部主键 |
| tenant_code | VARCHAR(32) | N | | 租户编码 |
| customer_code | VARCHAR(64) | N | | 货主 |
| sku_code | VARCHAR(64) | N | | SKU 编码 |
| batch_no | VARCHAR(64) | N | | 批次号,禁止空字符串 |
| supplier_code | VARCHAR(64) | Y | | 供应商 |
| mfg_date | DATE | Y | | 生产日期 |
| expiry_date | DATE | Y | | 失效日期 |
| created_at | TIMESTAMP | Y | | |
| updated_at | TIMESTAMP | Y | | |
索引:
| 名称 | 类型 | 字段 | 支撑的查询 |
| --- | --- | --- | --- |
| uk_tenant_customer_sku_batch | UNIQUE | (tenant_code, customer_code, sku_code, batch_no) | 定位批次 |
| idx_tenant_sku_expiry | INDEX | (tenant_code, sku_code, expiry_date) | 临期查询 |
关联:`wms_inventory.batch_no`。首次收货写入,之后只允许补空缺的日期,不改已有批次号。
并发 / 审计:无软删除。属性变更写 `wms_operation_log`。
## wms_inventory 库存余额
用途:某一货主、仓库、库位、SKU、批次上的数量结果。余额是结果,不是事实。
| 字段 | 类型 | 可空 | 默认 | 说明 |
| --- | --- | --- | --- | --- |
| id | BIGINT UNSIGNED | N | AUTO_INCREMENT | 内部主键 |
| tenant_code | VARCHAR(32) | N | | 租户编码 |
| customer_code | VARCHAR(64) | N | | 货主 |
| warehouse_code | VARCHAR(32) | N | | 仓库编码 |
| location_code | VARCHAR(64) | N | '' | 库位;在途为空字符串 |
| sku_code | VARCHAR(64) | N | | SKU 编码 |
| batch_no | VARCHAR(64) | N | '' | 批次;无批次为空字符串 |
| available_qty | DECIMAL(16,4) | N | 0 | 可用 |
| reserved_qty | DECIMAL(16,4) | N | 0 | 预占 |
| picked_qty | DECIMAL(16,4) | N | 0 | 已拣 |
| hold_qty | DECIMAL(16,4) | N | 0 | 冻结 |
| damaged_qty | DECIMAL(16,4) | N | 0 | 残损 |
| in_transit_qty | DECIMAL(16,4) | N | 0 | 在途 |
| version | INT UNSIGNED | N | 0 | 乐观锁,随数量更新递增 |
| created_at | TIMESTAMP | Y | | |
| updated_at | TIMESTAMP | Y | | |
索引:
| 名称 | 类型 | 字段 | 支撑的查询 |
| --- | --- | --- | --- |
| uk_inventory_grain | UNIQUE | (tenant_code, customer_code, warehouse_code, location_code, sku_code, batch_no) | 定位余额行 |
| idx_tenant_wh_sku | INDEX | (tenant_code, warehouse_code, sku_code) | 仓库内某 SKU 库存 |
| idx_tenant_customer_sku | INDEX | (tenant_code, customer_code, sku_code) | 货主下某 SKU 库存 |
关联:维度对应仓库、库位、SKU、批次。在途行的 `location_code` 不引用 `wms_location`。
并发 / 审计:
- 禁止先查出数量再在 PHP 里相减后回写。
- 扣减使用单条条件更新,例如可用转预占:
```sql
UPDATE wms_inventory
SET available_qty = available_qty - :qty,
reserved_qty = reserved_qty + :qty,
version = version + 1
WHERE tenant_code = :tenant_code
AND id = :id
AND available_qty >= :qty;
```
- 受影响行数为 0:库存不足或版本冲突,抛业务异常,整单事务回滚。
- 同一事务插入对应的 `wms_stock_log`。
- 各数量桶不得为负。不软删除;数量全为 0 的行保留,避免并发下反复插入同一粒度。
## wms_stock_log 库存流水
用途:库存变化的事实。任何余额变化都要有流水。只追加。
一次业务动作若改变多个数量桶或多行余额,每个桶一行,共用同一个 `movement_no`。
| 字段 | 类型 | 可空 | 默认 | 说明 |
| --- | --- | --- | --- | --- |
| id | BIGINT UNSIGNED | N | AUTO_INCREMENT | 内部主键 |
| tenant_code | VARCHAR(32) | N | | 租户编码 |
| movement_no | VARCHAR(64) | N | | 同一次动作的组号 |
| customer_code | VARCHAR(64) | N | | 货主 |
| warehouse_code | VARCHAR(32) | N | | 仓库 |
| location_code | VARCHAR(64) | N | '' | 库位 |
| sku_code | VARCHAR(64) | N | | SKU |
| batch_no | VARCHAR(64) | N | '' | 批次 |
| qty_bucket | VARCHAR(32) | N | | `available`、`reserved`、`picked`、`hold`、`damaged`、`in_transit` |
| qty_before | DECIMAL(16,4) | N | | 该桶变更前 |
| qty_delta | DECIMAL(16,4) | N | | 有符号变化量 |
| qty_after | DECIMAL(16,4) | N | | 该桶变更后 |
| biz_type | VARCHAR(32) | N | | 业务类型 |
| biz_no | VARCHAR(64) | N | | 业务单号 |
| biz_line_no | INT UNSIGNED | Y | | 单据行号 |
| operator_user_code | VARCHAR(64) | Y | | 操作人 |
| request_id | VARCHAR(64) | Y | | 请求标识 |
| created_at | TIMESTAMP | Y | | 发生时间 |
| updated_at | TIMESTAMP | Y | | 行创建后不更新 |
索引:
| 名称 | 类型 | 字段 | 支撑的查询 |
| --- | --- | --- | --- |
| idx_tenant_sku_created | INDEX | (tenant_code, sku_code, created_at) | 某 SKU 的库存为何变化 |
| idx_tenant_biz | INDEX | (tenant_code, biz_type, biz_no) | 某单据的流水 |
| idx_tenant_movement | INDEX | (tenant_code, movement_no) | 同一次动作的各行 |
`biz_type`:`inbound_confirm`、`inbound_receive`、`inbound_putaway`、`reserve`、`release`、`pick`、`ship`、`hold`、`unhold`、`damage`、`transfer`、`adjust`、`stocktake`。
关联:`biz_no` 指向入库单、出库单、调整单或盘点单。
并发 / 审计:禁止更新和删除。`qty_after = qty_before + qty_delta`,且 `qty_after` 等于同一事务中余额桶的新值。
## wms_stock_adjust 库存调整单
用途:人工调整的单据头。确认时才改余额并写流水。
| 字段 | 类型 | 可空 | 默认 | 说明 |
| --- | --- | --- | --- | --- |
| id | BIGINT UNSIGNED | N | AUTO_INCREMENT | 内部主键 |
| tenant_code | VARCHAR(32) | N | | 租户编码 |
| adjust_no | VARCHAR(64) | N | | 调整单号 |
| warehouse_code | VARCHAR(32) | N | | 仓库 |
| customer_code | VARCHAR(64) | N | | 货主 |
| status | VARCHAR(32) | N | draft | 状态 |
| reason | VARCHAR(500) | N | | 原因 |
| version | INT UNSIGNED | N | 0 | 乐观锁 |
| created_at | TIMESTAMP | Y | | |
| updated_at | TIMESTAMP | Y | | |
索引:
| 名称 | 类型 | 字段 | 支撑的查询 |
| --- | --- | --- | --- |
| uk_tenant_adjust_no | UNIQUE | (tenant_code, adjust_no) | 按单号查找 |
| idx_tenant_status_created | INDEX | (tenant_code, status, created_at) | 列表 |
状态:`draft → confirmed`,`draft → cancelled`。`confirmed`、`cancelled` 为终态。
确认时按明细对指定数量桶做原子增减,`biz_type = adjust`。减少时受影响行数为 0 则整单回滚。状态变更写 `wms_status_log`。不软删除。
## wms_stock_adjust_line 调整明细
用途:调整单的一行,只改一个数量桶。
| 字段 | 类型 | 可空 | 默认 | 说明 |
| --- | --- | --- | --- | --- |
| id | BIGINT UNSIGNED | N | AUTO_INCREMENT | 内部主键 |
| tenant_code | VARCHAR(32) | N | | 租户编码 |
| adjust_no | VARCHAR(64) | N | | 调整单号 |
| line_no | INT UNSIGNED | N | | 行号,单内从 1 递增 |
| location_code | VARCHAR(64) | N | '' | 库位 |
| sku_code | VARCHAR(64) | N | | SKU |
| batch_no | VARCHAR(64) | N | '' | 批次 |
| qty_bucket | VARCHAR(32) | N | | 数量桶 |
| qty_delta | DECIMAL(16,4) | N | | 有符号数量,禁止 0 |
| created_at | TIMESTAMP | Y | | |
| updated_at | TIMESTAMP | Y | | |
索引:
| 名称 | 类型 | 字段 | 支撑的查询 |
| --- | --- | --- | --- |
| uk_tenant_adjust_line | UNIQUE | (tenant_code, adjust_no, line_no) | 定位明细 |
关联:头表 `wms_stock_adjust`。确认后明细不再修改。
## wms_stocktake 盘点单
用途:盘点单据头。确认时把账面与实盘的差额写成流水,`biz_type = stocktake`。
| 字段 | 类型 | 可空 | 默认 | 说明 |
| --- | --- | --- | --- | --- |
| id | BIGINT UNSIGNED | N | AUTO_INCREMENT | 内部主键 |
| tenant_code | VARCHAR(32) | N | | 租户编码 |
| stocktake_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_stocktake_no | UNIQUE | (tenant_code, stocktake_no) | 按单号查找 |
| idx_tenant_status_created | INDEX | (tenant_code, status, created_at) | 列表 |
状态:`draft → counting → confirmed`,`draft` 或 `counting` 可到 `cancelled`。`confirmed`、`cancelled` 为终态。
盘点期间不锁死全仓。确认时按当时账面重算差额;若并发导致目标桶不足,确认失败并回滚。不软删除。
## wms_stocktake_line 盘点明细
用途:一个库位、SKU、批次、数量桶上的账面与实盘。
| 字段 | 类型 | 可空 | 默认 | 说明 |
| --- | --- | --- | --- | --- |
| id | BIGINT UNSIGNED | N | AUTO_INCREMENT | 内部主键 |
| tenant_code | VARCHAR(32) | N | | 租户编码 |
| stocktake_no | VARCHAR(64) | N | | 盘点单号 |
| line_no | INT UNSIGNED | N | | 行号 |
| customer_code | VARCHAR(64) | N | | 货主 |
| location_code | VARCHAR(64) | N | | 库位 |
| sku_code | VARCHAR(64) | N | | SKU |
| batch_no | VARCHAR(64) | N | '' | 批次 |
| qty_bucket | VARCHAR(32) | N | available | 盘点的数量桶 |
| system_qty | DECIMAL(16,4) | N | | 生成明细时的账面 |
| counted_qty | DECIMAL(16,4) | Y | | 实盘,未盘为空 |
| created_at | TIMESTAMP | Y | | |
| updated_at | TIMESTAMP | Y | | |
索引:
| 名称 | 类型 | 字段 | 支撑的查询 |
| --- | --- | --- | --- |
| uk_tenant_stocktake_line | UNIQUE | (tenant_code, stocktake_no, line_no) | 定位明细 |
| idx_tenant_stocktake_loc | INDEX | (tenant_code, stocktake_no, location_code) | 按库位录入 |
关联:头表 `wms_stocktake`。确认时差额为 `counted_qty - 当前账面`,不以生成时的 `system_qty` 作为扣减依据。`system_qty` 只说明生成时刻的账面。