refactor(wms): 合并库存表为统一inventory,重构业务逻辑
1. 合并inventory_batch与serial_number为统一inventory表,用manage_mode区分结构件批次/精密件SN 2. 移除冗余的material_type字段与相关逻辑 3. 重构库存查询、检验、入库出库等业务逻辑适配新表结构 4. 调整前端路由与菜单,拆分基础数据为区域维护和物料档案 5. 优化入库校验逻辑,避免空单问题 6. 调整Vite构建配置,关闭自动清空输出目录 7. 为各模块添加详细中文注释,统一业务术语 8. 生成并更新Ent自动代码文件
This commit is contained in:
@@ -6,7 +6,9 @@ import (
|
||||
"entgo.io/ent/schema/index"
|
||||
)
|
||||
|
||||
// InboundDetail 入库明细(SN 逐条)
|
||||
// InboundDetail 入库明细(一张入库单可含多行)
|
||||
// 结构件记录 batch_no,精密件记录 sn_code(逐件一行),quantity 冗余记录该行数量。
|
||||
// 用于入库单正向追溯(查某入库单具体入了哪些批次/SN)。
|
||||
type InboundDetail struct {
|
||||
ent.Schema
|
||||
}
|
||||
|
||||
@@ -18,14 +18,14 @@ func (InboundOrder) Fields() []ent.Field {
|
||||
field.String("inbound_type").Default("purchase").Comment("入库类型"),
|
||||
field.String("material_code").Comment("物料编码"),
|
||||
field.String("material_name").Optional().Comment("物料名称(冗余)"),
|
||||
// 管理粒度 1批次/2序列号
|
||||
field.Int("manage_mode").Default(1),
|
||||
// 管理粒度 1批次(结构件)/2序列号(精密件),冗余自物料档案,便于单单据直接判断
|
||||
field.Int("manage_mode").Default(1).Comment("管理粒度 1批次/2序列号"),
|
||||
field.String("batch_no").Optional().Comment("批次号(结构件)"),
|
||||
field.String("zone_code").Optional().Comment("入库区域"),
|
||||
field.Int("quantity").Default(0).Comment("数量"),
|
||||
field.Int("quantity").Default(0).Comment("本次入库总数量(批次合计或SN条数)"),
|
||||
field.String("operator").Optional().Comment("操作人"),
|
||||
field.String("remark").Optional().Comment("备注"),
|
||||
field.Int64("created_at").DefaultFunc(nowUnix),
|
||||
field.Int64("created_at").DefaultFunc(nowUnix).Comment("创建时间"),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,83 @@
|
||||
package schema
|
||||
|
||||
import (
|
||||
"entgo.io/ent"
|
||||
"entgo.io/ent/schema/field"
|
||||
"entgo.io/ent/schema/index"
|
||||
)
|
||||
|
||||
// Inventory 统一库存台账
|
||||
//
|
||||
// 业务背景:原系统把库存拆成「结构件批次(inventory_batch)」与「精密件序列号(serial_number)」
|
||||
// 两张表,导致 锁库存 / 导出 / 汇总 都要分别写两套逻辑,运维成本高且容易不一致。
|
||||
// 现合并为一张表,用 manage_mode 区分管理粒度,对外提供统一的 锁/查询/汇总/盘点 能力。
|
||||
//
|
||||
// 两种管理粒度的存储约定:
|
||||
// - manage_mode = 1 结构件(批次管理):
|
||||
// 一条记录 = 一个批次,quantity 表示该批次当前余量,locked_qty 表示已锁定件数。
|
||||
// 可用量 = quantity - locked_qty。出库/盘点都按"数量"增减。
|
||||
// - manage_mode = 2 精密件(SN 管理):
|
||||
// 一条记录 = 一个单件(SN),quantity 恒为 1,locked_qty 为 0 或 1(锁定时=1)。
|
||||
// 生命周期用 status 字段跟踪:在库/锁定/出库/使用/报废。
|
||||
// 可用量 = quantity - locked_qty(锁定时为 0)。
|
||||
//
|
||||
// 区域(zone_code)只到 Z01~Z04 大分区级别,不引入货架/层/格(业务决策:小货架高频取货场景无需精确货位索引)。
|
||||
// 半成品(semifinished)是独立的固定业务类型,不进本表(由 MES 报工闭环单独管理)。
|
||||
type Inventory struct {
|
||||
ent.Schema
|
||||
}
|
||||
|
||||
func (Inventory) Fields() []ent.Field {
|
||||
return []ent.Field{
|
||||
// 管理粒度:1=结构件(批次) 2=精密件(SN)。决定本行按"数量"还是按"单件"解释。
|
||||
field.Int("manage_mode").Comment("管理粒度 1=结构件(批次) 2=精密件(SN)"),
|
||||
|
||||
// 物料编码,关联 materials 主数据。
|
||||
field.String("material_code").Comment("物料编码(关联 materials 表)"),
|
||||
// 物料名称冗余存储,查询时无需再 join 主数据。
|
||||
field.String("material_name").Optional().Comment("物料名称(冗余,便于直接展示)"),
|
||||
|
||||
// 批次号:结构件必填(系统生成);精密件可填所属批次用于分组追溯。
|
||||
field.String("batch_no").Optional().Comment("批次号(系统生成);精密件可关联所属批次"),
|
||||
// 序列号:仅精密件填写,作为单件唯一追溯锚点。空字符串不保证唯一,由代码层查重。
|
||||
field.String("sn_code").Optional().Comment("序列号(SN,仅精密件);空值表示非 SN 行"),
|
||||
|
||||
// 数量:结构件=批次余量;精密件恒为 1。
|
||||
field.Int("quantity").Default(0).Comment("数量:结构件=批次余量;精密件=1"),
|
||||
// 已锁定数量:结构件=锁定件数;精密件=0或1。可用量 = quantity - locked_qty。
|
||||
field.Int("locked_qty").Default(0).Comment("已锁定数量(库存占用):结构件=件数;精密件=0/1"),
|
||||
|
||||
// 检验状态,由质量检验模块翻转。
|
||||
field.String("quality_status").Default("未检").Comment("检验状态 未检/合格/不合格"),
|
||||
// 所在区域(大分区)。
|
||||
field.String("zone_code").Optional().Comment("所在区域(Z01~Z04 大分区)"),
|
||||
|
||||
// 生命周期状态:
|
||||
// 结构件一律"在库"(锁定看 locked_qty,不靠本字段);
|
||||
// 精密件随业务流转:在库/锁定/出库/使用/报废。
|
||||
field.String("status").Default("在库").Comment("生命周期:在库/锁定/出库/使用/报废(精密件用;结构件恒在库)"),
|
||||
|
||||
// 生产日期(结构件批次用)。
|
||||
field.String("production_date").Optional().Comment("生产日期"),
|
||||
// 供应商(结构件批次用)。
|
||||
field.String("supplier").Optional().Comment("供应商"),
|
||||
// 关联入库单号,便于正向追溯。
|
||||
field.String("inbound_no").Optional().Comment("关联入库单号"),
|
||||
// 备注。
|
||||
field.String("remark").Optional().Comment("备注"),
|
||||
|
||||
field.Int64("created_at").DefaultFunc(nowUnix),
|
||||
field.Int64("updated_at").DefaultFunc(nowUnix),
|
||||
}
|
||||
}
|
||||
|
||||
func (Inventory) Indexes() []ent.Index {
|
||||
return []ent.Index{
|
||||
index.Fields("manage_mode"),
|
||||
index.Fields("material_code"),
|
||||
index.Fields("quality_status"),
|
||||
index.Fields("zone_code"),
|
||||
index.Fields("batch_no"),
|
||||
index.Fields("sn_code"),
|
||||
}
|
||||
}
|
||||
@@ -1,37 +0,0 @@
|
||||
package schema
|
||||
|
||||
import (
|
||||
"entgo.io/ent"
|
||||
"entgo.io/ent/schema/field"
|
||||
"entgo.io/ent/schema/index"
|
||||
)
|
||||
|
||||
// InventoryBatch 结构件批次台账
|
||||
type InventoryBatch struct {
|
||||
ent.Schema
|
||||
}
|
||||
|
||||
func (InventoryBatch) Fields() []ent.Field {
|
||||
return []ent.Field{
|
||||
field.String("batch_no").Unique().Comment("批次号(系统自动生成:日期+自增)"),
|
||||
field.String("material_code").Comment("物料编码"),
|
||||
field.String("material_name").Optional().Comment("物料名称(冗余)"),
|
||||
field.Int("quantity").Default(0).Comment("当前可用数量"),
|
||||
field.Int("locked_qty").Default(0).Comment("已锁定数量"),
|
||||
field.String("production_date").Optional().Comment("生产日期"),
|
||||
field.String("supplier").Optional().Comment("供应商"),
|
||||
field.String("quality_status").Default("未检").Comment("检验状态 未检/合格/不合格"),
|
||||
field.String("zone_code").Optional().Comment("所在区域"),
|
||||
field.String("remark").Optional().Comment("备注"),
|
||||
field.Int64("created_at").DefaultFunc(nowUnix),
|
||||
field.Int64("updated_at").DefaultFunc(nowUnix),
|
||||
}
|
||||
}
|
||||
|
||||
func (InventoryBatch) Indexes() []ent.Index {
|
||||
return []ent.Index{
|
||||
index.Fields("material_code"),
|
||||
index.Fields("quality_status"),
|
||||
index.Fields("zone_code"),
|
||||
}
|
||||
}
|
||||
@@ -6,8 +6,13 @@ import (
|
||||
"entgo.io/ent/schema/index"
|
||||
)
|
||||
|
||||
// Material 物料档案
|
||||
// 精密件(manage_mode=2) 逐件 SN 管理;结构件(manage_mode=1) 批次管理
|
||||
// Material 物料档案(主数据)
|
||||
// 全系统物料的唯一定义来源,入库/出库/库存/检验都通过 material_code 关联本表。
|
||||
// 管理粒度由 manage_mode 决定,贯穿整个库存链路:
|
||||
// - manage_mode=1 结构件:按"批次"管理,一次到货入一批,库存以 quantity 计数;
|
||||
// - manage_mode=2 精密件:按"SN 序列号"逐件管理,库存以单件行计数,可单件追溯。
|
||||
// 注意:物料档案只描述"这个物料是什么、怎么管",不承载生命周期状态
|
||||
// (原材料→半成品→成品的阶段由各自库存实体/inventory 表承载,不在此预判)。
|
||||
type Material struct {
|
||||
ent.Schema
|
||||
}
|
||||
@@ -24,8 +29,6 @@ func (Material) Fields() []ent.Field {
|
||||
field.Int("manage_mode").Default(1).Comment("管理粒度 1批次/2序列号"),
|
||||
field.Bool("is_batch_managed").Default(false).Comment("是否批次管理"),
|
||||
field.Bool("is_serial_managed").Default(false).Comment("是否序列号管理"),
|
||||
// 物料类型:raw(原材料) semi(半成品) finished(成品)
|
||||
field.String("material_type").Default("raw").Comment("物料总状态 raw/semi/finished"),
|
||||
// 所属物料模板(种类)
|
||||
field.String("template_code").Optional().Comment("物料模板编码(物料种类)"),
|
||||
field.Int64("created_at").DefaultFunc(nowUnix).Comment("创建时间"),
|
||||
|
||||
@@ -6,7 +6,9 @@ import (
|
||||
"entgo.io/ent/schema/index"
|
||||
)
|
||||
|
||||
// OutboundDetail 出库明细
|
||||
// OutboundDetail 出库明细(一张出库单可含多行)
|
||||
// 与 InboundDetail 对称,记录本次出库涉及的具体批次/SN 与数量,
|
||||
// 是库存账与单据账闭环核对的关键凭证。
|
||||
type OutboundDetail struct {
|
||||
ent.Schema
|
||||
}
|
||||
|
||||
@@ -20,15 +20,15 @@ func (OutboundOrder) Fields() []ent.Field {
|
||||
field.String("order_no").Optional().Comment("关联工单号(工单为唯一挂靠载体)"),
|
||||
field.String("material_code").Comment("物料编码"),
|
||||
field.String("material_name").Optional().Comment("物料名称(冗余)"),
|
||||
field.Int("manage_mode").Default(1),
|
||||
field.Int("manage_mode").Default(1).Comment("管理粒度 1批次/2序列号"),
|
||||
field.String("batch_no").Optional().Comment("批次号(结构件)"),
|
||||
field.String("zone_code").Optional().Comment("出库区域"),
|
||||
field.Int("quantity").Default(0).Comment("数量"),
|
||||
field.Int("quantity").Default(0).Comment("本次出库总数量"),
|
||||
field.String("operator").Optional().Comment("操作人"),
|
||||
field.String("reviewer").Optional().Comment("复核人"),
|
||||
field.String("target_dock").Optional().Comment("目标接驳台(AGV配送)"),
|
||||
field.String("remark").Optional().Comment("备注"),
|
||||
field.Int64("created_at").DefaultFunc(nowUnix),
|
||||
field.Int64("created_at").DefaultFunc(nowUnix).Comment("创建时间"),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -6,7 +6,10 @@ import (
|
||||
"entgo.io/ent/schema/index"
|
||||
)
|
||||
|
||||
// PackageBox 包装箱 ↔ SN 关联
|
||||
// PackageBox 包装箱 ↔ SN 关联(装箱管理)
|
||||
// 将一个或多个精密件 SN 绑定到一个箱号,便于整箱出入库与追溯。
|
||||
// sn_list 以字符串存多个 SN(逗号分隔 / JSON),不强制外键关联 inventory,
|
||||
// 因为装箱是业务封装动作,箱内 SN 仍各自在 inventory 表中独立流转。
|
||||
type PackageBox struct {
|
||||
ent.Schema
|
||||
}
|
||||
|
||||
@@ -1,37 +0,0 @@
|
||||
package schema
|
||||
|
||||
import (
|
||||
"entgo.io/ent"
|
||||
"entgo.io/ent/schema/field"
|
||||
"entgo.io/ent/schema/index"
|
||||
)
|
||||
|
||||
// SerialNumber 精密件/成品 单件管理
|
||||
type SerialNumber struct {
|
||||
ent.Schema
|
||||
}
|
||||
|
||||
func (SerialNumber) Fields() []ent.Field {
|
||||
return []ent.Field{
|
||||
field.String("sn_code").Unique().Comment("序列号"),
|
||||
field.String("material_code").Optional().Comment("物料编码(可为空)"),
|
||||
field.String("batch_no").Optional().Comment("所属批次"),
|
||||
// 状态:在库/锁定/出库/使用/报废
|
||||
field.String("status").Default("在库").Comment("在库/锁定/出库/使用/报废"),
|
||||
field.String("current_zone").Optional().Comment("当前区域"),
|
||||
field.String("quality_status").Default("未检").Comment("检验状态 未检/合格/不合格"),
|
||||
field.String("last_inbound_at").Optional().Comment("最近入库时间"),
|
||||
field.String("last_outbound_at").Optional().Comment("最近出库时间"),
|
||||
field.String("remark").Optional().Comment("备注"),
|
||||
field.Int64("created_at").DefaultFunc(nowUnix),
|
||||
field.Int64("updated_at").DefaultFunc(nowUnix),
|
||||
}
|
||||
}
|
||||
|
||||
func (SerialNumber) Indexes() []ent.Index {
|
||||
return []ent.Index{
|
||||
index.Fields("material_code"),
|
||||
index.Fields("status"),
|
||||
index.Fields("batch_no"),
|
||||
}
|
||||
}
|
||||
@@ -38,7 +38,7 @@ func (StocktakeItem) Fields() []ent.Field {
|
||||
field.String("stocktake_no").Comment("所属盘点单号"),
|
||||
field.String("target_type").Comment("BATCH/SN"),
|
||||
field.String("target_id").Comment("批次号或SN码"),
|
||||
field.String("material_code").Optional(),
|
||||
field.String("material_code").Optional().Comment("物料编码(冗余,便于盘点差异按物料归类)"),
|
||||
field.String("zone_code").Optional().Comment("所在区域"),
|
||||
field.Int("book_qty").Default(0).Comment("账面数量"),
|
||||
field.Int("scanned_qty").Default(-1).Comment("实盘数量,-1=未盘"),
|
||||
|
||||
@@ -6,7 +6,9 @@ import (
|
||||
"entgo.io/ent/schema/index"
|
||||
)
|
||||
|
||||
// User 用户
|
||||
// User 系统用户(账号与权限)
|
||||
// 角色 role 控制功能权限:admin(管理员) / operator(库房保管员) / inspector(质检员)。
|
||||
// 密码以 bcrypt 哈希存储,登录走 JWT。
|
||||
type User struct {
|
||||
ent.Schema
|
||||
}
|
||||
|
||||
@@ -5,8 +5,10 @@ import (
|
||||
"entgo.io/ent/schema/field"
|
||||
)
|
||||
|
||||
// Zone 库房区域
|
||||
// 待检/合格/不合格/退货区 等
|
||||
// Zone 库房区域(大分区)
|
||||
// 仅作为"大分区"维度(如 待检区/标准存储区/暂存区/退货区),编码 Z01~Z04。
|
||||
// 业务决策:小货架高频取货场景无需"货架-层-格"精确货位,系统管到区域即可,
|
||||
// 货架内部由仓管经验定位,不给现场增加录入负担。库存(inventory)只关联 zone_code。
|
||||
type Zone struct {
|
||||
ent.Schema
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user