feat&fix: 完善半成品/序列号闭环流转逻辑
1. 调整物料编码字段为可空,优化注释说明 2. 修复Vite构建自动清空产物目录冲突问题 3. 优化前端提示文案与表单占位符 4. 为各前端项目添加内联SVG图标 5. 重构MES半成品流转逻辑,不再静默吞掉WMS错误并记录日志 6. 生成并添加空的WMS客户端静态文件 7. 完善Ent ORM模型的空值支持与查询谓词 8. 优化半成品入库接口,支持doneProcessCodes数组格式,新增SN反查物料编码逻辑 9. 更新帮助文档与操作手册
This commit is contained in:
@@ -3,7 +3,9 @@
|
|||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8" />
|
<meta charset="UTF-8" />
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
<title>生产看板 - 北京电力智能产线</title>
|
<title>生产看板</title>
|
||||||
|
<!-- 系统图标:看板柱状图(内联 SVG,避免依赖外部文件) -->
|
||||||
|
<link rel="icon" type="image/svg+xml" href="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 32 32'><rect width='32' height='32' rx='6' fill='%231668dc'/><path d='M8 20v5M14 13v12M20 16v9M26 8v17' stroke='%23fff' stroke-width='2.6' stroke-linecap='round'/></svg>">
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div id="root"></div>
|
<div id="root"></div>
|
||||||
|
|||||||
@@ -4,6 +4,8 @@
|
|||||||
<meta charset="utf-8" />
|
<meta charset="utf-8" />
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no" />
|
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no" />
|
||||||
<title>MES 产线控制</title>
|
<title>MES 产线控制</title>
|
||||||
|
<!-- 系统图标:产线齿轮(内联 SVG,避免依赖外部文件) -->
|
||||||
|
<link rel="icon" type="image/svg+xml" href="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 32 32'><rect width='32' height='32' rx='6' fill='%231668dc'/><circle cx='16' cy='16' r='4.2' fill='none' stroke='%23fff' stroke-width='2'/><path d='M16 5v3M16 24v3M5 16h3M24 16h3M8.2 8.2l2.1 2.1M21.7 21.7l2.1 2.1M23.8 8.2l-2.1 2.1M10.3 21.7l-2.1 2.1' stroke='%23fff' stroke-width='2' stroke-linecap='round'/></svg>">
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div id="app"></div>
|
<div id="app"></div>
|
||||||
|
|||||||
@@ -31,7 +31,8 @@ func (s *Service) SemiFlow(ctx context.Context, req SemiReq, operator string) er
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
// 调用 WMS 内部接口
|
// 调用 WMS 内部接口(本地 SemiFlow 已保存,下面把 WMS 同步结果如实返回,
|
||||||
|
// 不再静默吞掉——WMS 不可达或校验失败时必须让调用方感知,才能形成闭环)。
|
||||||
var wmsErr error
|
var wmsErr error
|
||||||
if action == "INBOUND" {
|
if action == "INBOUND" {
|
||||||
wmsErr = s.ctx.Wms.SemiInbound(ctx, req.Sn, req.OrderNo, req.DoneProcessCodes, operator)
|
wmsErr = s.ctx.Wms.SemiInbound(ctx, req.Sn, req.OrderNo, req.DoneProcessCodes, operator)
|
||||||
@@ -39,7 +40,9 @@ func (s *Service) SemiFlow(ctx context.Context, req SemiReq, operator string) er
|
|||||||
wmsErr = s.ctx.Wms.SemiOutbound(ctx, req.Sn, req.OrderNo, req.DoneProcessCodes, operator)
|
wmsErr = s.ctx.Wms.SemiOutbound(ctx, req.Sn, req.OrderNo, req.DoneProcessCodes, operator)
|
||||||
}
|
}
|
||||||
if wmsErr != nil {
|
if wmsErr != nil {
|
||||||
_ = wmsErr
|
s.ctx.EventLog.Write(ctx, "semi.flow.wms_error", req.OrderNo, operator, "semi_flow", req.Sn,
|
||||||
|
"WMS 同步失败", map[string]any{"action": action, "error": wmsErr.Error()})
|
||||||
|
return wmsErr
|
||||||
}
|
}
|
||||||
s.ctx.EventLog.Write(ctx, "semi.flow", req.OrderNo, operator, "semi_flow", req.Sn, "半成品流转",
|
s.ctx.EventLog.Write(ctx, "semi.flow", req.OrderNo, operator, "semi_flow", req.Sn, "半成品流转",
|
||||||
map[string]any{"action": action, "doneProcessCodes": req.DoneProcessCodes})
|
map[string]any{"action": action, "doneProcessCodes": req.DoneProcessCodes})
|
||||||
|
|||||||
@@ -373,7 +373,7 @@ var (
|
|||||||
SemiFinishedsColumns = []*schema.Column{
|
SemiFinishedsColumns = []*schema.Column{
|
||||||
{Name: "id", Type: field.TypeInt, Increment: true},
|
{Name: "id", Type: field.TypeInt, Increment: true},
|
||||||
{Name: "sn", Type: field.TypeString, Unique: true},
|
{Name: "sn", Type: field.TypeString, Unique: true},
|
||||||
{Name: "material_code", Type: field.TypeString},
|
{Name: "material_code", Type: field.TypeString, Nullable: true},
|
||||||
{Name: "completed_process", Type: field.TypeString, Nullable: true},
|
{Name: "completed_process", Type: field.TypeString, Nullable: true},
|
||||||
{Name: "quantity", Type: field.TypeInt, Default: 1},
|
{Name: "quantity", Type: field.TypeInt, Default: 1},
|
||||||
{Name: "zone_code", Type: field.TypeString, Nullable: true},
|
{Name: "zone_code", Type: field.TypeString, Nullable: true},
|
||||||
@@ -404,7 +404,7 @@ var (
|
|||||||
SerialNumbersColumns = []*schema.Column{
|
SerialNumbersColumns = []*schema.Column{
|
||||||
{Name: "id", Type: field.TypeInt, Increment: true},
|
{Name: "id", Type: field.TypeInt, Increment: true},
|
||||||
{Name: "sn_code", Type: field.TypeString, Unique: true},
|
{Name: "sn_code", Type: field.TypeString, Unique: true},
|
||||||
{Name: "material_code", Type: field.TypeString},
|
{Name: "material_code", Type: field.TypeString, Nullable: true},
|
||||||
{Name: "batch_no", Type: field.TypeString, Nullable: true},
|
{Name: "batch_no", Type: field.TypeString, Nullable: true},
|
||||||
{Name: "status", Type: field.TypeString, Default: "在库"},
|
{Name: "status", Type: field.TypeString, Default: "在库"},
|
||||||
{Name: "current_zone", Type: field.TypeString, Nullable: true},
|
{Name: "current_zone", Type: field.TypeString, Nullable: true},
|
||||||
|
|||||||
@@ -10250,9 +10250,22 @@ func (m *SemiFinishedMutation) OldMaterialCode(ctx context.Context) (v string, e
|
|||||||
return oldValue.MaterialCode, nil
|
return oldValue.MaterialCode, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ClearMaterialCode clears the value of the "material_code" field.
|
||||||
|
func (m *SemiFinishedMutation) ClearMaterialCode() {
|
||||||
|
m.material_code = nil
|
||||||
|
m.clearedFields[semifinished.FieldMaterialCode] = struct{}{}
|
||||||
|
}
|
||||||
|
|
||||||
|
// MaterialCodeCleared returns if the "material_code" field was cleared in this mutation.
|
||||||
|
func (m *SemiFinishedMutation) MaterialCodeCleared() bool {
|
||||||
|
_, ok := m.clearedFields[semifinished.FieldMaterialCode]
|
||||||
|
return ok
|
||||||
|
}
|
||||||
|
|
||||||
// ResetMaterialCode resets all changes to the "material_code" field.
|
// ResetMaterialCode resets all changes to the "material_code" field.
|
||||||
func (m *SemiFinishedMutation) ResetMaterialCode() {
|
func (m *SemiFinishedMutation) ResetMaterialCode() {
|
||||||
m.material_code = nil
|
m.material_code = nil
|
||||||
|
delete(m.clearedFields, semifinished.FieldMaterialCode)
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetCompletedProcess sets the "completed_process" field.
|
// SetCompletedProcess sets the "completed_process" field.
|
||||||
@@ -10862,6 +10875,9 @@ func (m *SemiFinishedMutation) AddField(name string, value ent.Value) error {
|
|||||||
// mutation.
|
// mutation.
|
||||||
func (m *SemiFinishedMutation) ClearedFields() []string {
|
func (m *SemiFinishedMutation) ClearedFields() []string {
|
||||||
var fields []string
|
var fields []string
|
||||||
|
if m.FieldCleared(semifinished.FieldMaterialCode) {
|
||||||
|
fields = append(fields, semifinished.FieldMaterialCode)
|
||||||
|
}
|
||||||
if m.FieldCleared(semifinished.FieldCompletedProcess) {
|
if m.FieldCleared(semifinished.FieldCompletedProcess) {
|
||||||
fields = append(fields, semifinished.FieldCompletedProcess)
|
fields = append(fields, semifinished.FieldCompletedProcess)
|
||||||
}
|
}
|
||||||
@@ -10885,6 +10901,9 @@ func (m *SemiFinishedMutation) FieldCleared(name string) bool {
|
|||||||
// error if the field is not defined in the schema.
|
// error if the field is not defined in the schema.
|
||||||
func (m *SemiFinishedMutation) ClearField(name string) error {
|
func (m *SemiFinishedMutation) ClearField(name string) error {
|
||||||
switch name {
|
switch name {
|
||||||
|
case semifinished.FieldMaterialCode:
|
||||||
|
m.ClearMaterialCode()
|
||||||
|
return nil
|
||||||
case semifinished.FieldCompletedProcess:
|
case semifinished.FieldCompletedProcess:
|
||||||
m.ClearCompletedProcess()
|
m.ClearCompletedProcess()
|
||||||
return nil
|
return nil
|
||||||
@@ -11171,9 +11190,22 @@ func (m *SerialNumberMutation) OldMaterialCode(ctx context.Context) (v string, e
|
|||||||
return oldValue.MaterialCode, nil
|
return oldValue.MaterialCode, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ClearMaterialCode clears the value of the "material_code" field.
|
||||||
|
func (m *SerialNumberMutation) ClearMaterialCode() {
|
||||||
|
m.material_code = nil
|
||||||
|
m.clearedFields[serialnumber.FieldMaterialCode] = struct{}{}
|
||||||
|
}
|
||||||
|
|
||||||
|
// MaterialCodeCleared returns if the "material_code" field was cleared in this mutation.
|
||||||
|
func (m *SerialNumberMutation) MaterialCodeCleared() bool {
|
||||||
|
_, ok := m.clearedFields[serialnumber.FieldMaterialCode]
|
||||||
|
return ok
|
||||||
|
}
|
||||||
|
|
||||||
// ResetMaterialCode resets all changes to the "material_code" field.
|
// ResetMaterialCode resets all changes to the "material_code" field.
|
||||||
func (m *SerialNumberMutation) ResetMaterialCode() {
|
func (m *SerialNumberMutation) ResetMaterialCode() {
|
||||||
m.material_code = nil
|
m.material_code = nil
|
||||||
|
delete(m.clearedFields, serialnumber.FieldMaterialCode)
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetBatchNo sets the "batch_no" field.
|
// SetBatchNo sets the "batch_no" field.
|
||||||
@@ -11877,6 +11909,9 @@ func (m *SerialNumberMutation) AddField(name string, value ent.Value) error {
|
|||||||
// mutation.
|
// mutation.
|
||||||
func (m *SerialNumberMutation) ClearedFields() []string {
|
func (m *SerialNumberMutation) ClearedFields() []string {
|
||||||
var fields []string
|
var fields []string
|
||||||
|
if m.FieldCleared(serialnumber.FieldMaterialCode) {
|
||||||
|
fields = append(fields, serialnumber.FieldMaterialCode)
|
||||||
|
}
|
||||||
if m.FieldCleared(serialnumber.FieldBatchNo) {
|
if m.FieldCleared(serialnumber.FieldBatchNo) {
|
||||||
fields = append(fields, serialnumber.FieldBatchNo)
|
fields = append(fields, serialnumber.FieldBatchNo)
|
||||||
}
|
}
|
||||||
@@ -11906,6 +11941,9 @@ func (m *SerialNumberMutation) FieldCleared(name string) bool {
|
|||||||
// error if the field is not defined in the schema.
|
// error if the field is not defined in the schema.
|
||||||
func (m *SerialNumberMutation) ClearField(name string) error {
|
func (m *SerialNumberMutation) ClearField(name string) error {
|
||||||
switch name {
|
switch name {
|
||||||
|
case serialnumber.FieldMaterialCode:
|
||||||
|
m.ClearMaterialCode()
|
||||||
|
return nil
|
||||||
case serialnumber.FieldBatchNo:
|
case serialnumber.FieldBatchNo:
|
||||||
m.ClearBatchNo()
|
m.ClearBatchNo()
|
||||||
return nil
|
return nil
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ type SemiFinished struct {
|
|||||||
ID int `json:"id,omitempty"`
|
ID int `json:"id,omitempty"`
|
||||||
// 半成品 SN
|
// 半成品 SN
|
||||||
Sn string `json:"sn,omitempty"`
|
Sn string `json:"sn,omitempty"`
|
||||||
// 物料编码
|
// 物料编码(MES 未传时按 SN 反查,可空)
|
||||||
MaterialCode string `json:"materialCode,omitempty"`
|
MaterialCode string `json:"materialCode,omitempty"`
|
||||||
// 已完成工序(如 135)
|
// 已完成工序(如 135)
|
||||||
CompletedProcess string `json:"completedProcess,omitempty"`
|
CompletedProcess string `json:"completedProcess,omitempty"`
|
||||||
|
|||||||
@@ -218,6 +218,16 @@ func MaterialCodeHasSuffix(v string) predicate.SemiFinished {
|
|||||||
return predicate.SemiFinished(sql.FieldHasSuffix(FieldMaterialCode, v))
|
return predicate.SemiFinished(sql.FieldHasSuffix(FieldMaterialCode, v))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MaterialCodeIsNil applies the IsNil predicate on the "material_code" field.
|
||||||
|
func MaterialCodeIsNil() predicate.SemiFinished {
|
||||||
|
return predicate.SemiFinished(sql.FieldIsNull(FieldMaterialCode))
|
||||||
|
}
|
||||||
|
|
||||||
|
// MaterialCodeNotNil applies the NotNil predicate on the "material_code" field.
|
||||||
|
func MaterialCodeNotNil() predicate.SemiFinished {
|
||||||
|
return predicate.SemiFinished(sql.FieldNotNull(FieldMaterialCode))
|
||||||
|
}
|
||||||
|
|
||||||
// MaterialCodeEqualFold applies the EqualFold predicate on the "material_code" field.
|
// MaterialCodeEqualFold applies the EqualFold predicate on the "material_code" field.
|
||||||
func MaterialCodeEqualFold(v string) predicate.SemiFinished {
|
func MaterialCodeEqualFold(v string) predicate.SemiFinished {
|
||||||
return predicate.SemiFinished(sql.FieldEqualFold(FieldMaterialCode, v))
|
return predicate.SemiFinished(sql.FieldEqualFold(FieldMaterialCode, v))
|
||||||
|
|||||||
@@ -33,6 +33,14 @@ func (_c *SemiFinishedCreate) SetMaterialCode(v string) *SemiFinishedCreate {
|
|||||||
return _c
|
return _c
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SetNillableMaterialCode sets the "material_code" field if the given value is not nil.
|
||||||
|
func (_c *SemiFinishedCreate) SetNillableMaterialCode(v *string) *SemiFinishedCreate {
|
||||||
|
if v != nil {
|
||||||
|
_c.SetMaterialCode(*v)
|
||||||
|
}
|
||||||
|
return _c
|
||||||
|
}
|
||||||
|
|
||||||
// SetCompletedProcess sets the "completed_process" field.
|
// SetCompletedProcess sets the "completed_process" field.
|
||||||
func (_c *SemiFinishedCreate) SetCompletedProcess(v string) *SemiFinishedCreate {
|
func (_c *SemiFinishedCreate) SetCompletedProcess(v string) *SemiFinishedCreate {
|
||||||
_c.mutation.SetCompletedProcess(v)
|
_c.mutation.SetCompletedProcess(v)
|
||||||
@@ -189,9 +197,6 @@ func (_c *SemiFinishedCreate) check() error {
|
|||||||
if _, ok := _c.mutation.Sn(); !ok {
|
if _, ok := _c.mutation.Sn(); !ok {
|
||||||
return &ValidationError{Name: "sn", err: errors.New(`ent: missing required field "SemiFinished.sn"`)}
|
return &ValidationError{Name: "sn", err: errors.New(`ent: missing required field "SemiFinished.sn"`)}
|
||||||
}
|
}
|
||||||
if _, ok := _c.mutation.MaterialCode(); !ok {
|
|
||||||
return &ValidationError{Name: "material_code", err: errors.New(`ent: missing required field "SemiFinished.material_code"`)}
|
|
||||||
}
|
|
||||||
if _, ok := _c.mutation.Quantity(); !ok {
|
if _, ok := _c.mutation.Quantity(); !ok {
|
||||||
return &ValidationError{Name: "quantity", err: errors.New(`ent: missing required field "SemiFinished.quantity"`)}
|
return &ValidationError{Name: "quantity", err: errors.New(`ent: missing required field "SemiFinished.quantity"`)}
|
||||||
}
|
}
|
||||||
@@ -343,6 +348,12 @@ func (u *SemiFinishedUpsert) UpdateMaterialCode() *SemiFinishedUpsert {
|
|||||||
return u
|
return u
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ClearMaterialCode clears the value of the "material_code" field.
|
||||||
|
func (u *SemiFinishedUpsert) ClearMaterialCode() *SemiFinishedUpsert {
|
||||||
|
u.SetNull(semifinished.FieldMaterialCode)
|
||||||
|
return u
|
||||||
|
}
|
||||||
|
|
||||||
// SetCompletedProcess sets the "completed_process" field.
|
// SetCompletedProcess sets the "completed_process" field.
|
||||||
func (u *SemiFinishedUpsert) SetCompletedProcess(v string) *SemiFinishedUpsert {
|
func (u *SemiFinishedUpsert) SetCompletedProcess(v string) *SemiFinishedUpsert {
|
||||||
u.Set(semifinished.FieldCompletedProcess, v)
|
u.Set(semifinished.FieldCompletedProcess, v)
|
||||||
@@ -531,6 +542,13 @@ func (u *SemiFinishedUpsertOne) UpdateMaterialCode() *SemiFinishedUpsertOne {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ClearMaterialCode clears the value of the "material_code" field.
|
||||||
|
func (u *SemiFinishedUpsertOne) ClearMaterialCode() *SemiFinishedUpsertOne {
|
||||||
|
return u.Update(func(s *SemiFinishedUpsert) {
|
||||||
|
s.ClearMaterialCode()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
// SetCompletedProcess sets the "completed_process" field.
|
// SetCompletedProcess sets the "completed_process" field.
|
||||||
func (u *SemiFinishedUpsertOne) SetCompletedProcess(v string) *SemiFinishedUpsertOne {
|
func (u *SemiFinishedUpsertOne) SetCompletedProcess(v string) *SemiFinishedUpsertOne {
|
||||||
return u.Update(func(s *SemiFinishedUpsert) {
|
return u.Update(func(s *SemiFinishedUpsert) {
|
||||||
@@ -903,6 +921,13 @@ func (u *SemiFinishedUpsertBulk) UpdateMaterialCode() *SemiFinishedUpsertBulk {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ClearMaterialCode clears the value of the "material_code" field.
|
||||||
|
func (u *SemiFinishedUpsertBulk) ClearMaterialCode() *SemiFinishedUpsertBulk {
|
||||||
|
return u.Update(func(s *SemiFinishedUpsert) {
|
||||||
|
s.ClearMaterialCode()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
// SetCompletedProcess sets the "completed_process" field.
|
// SetCompletedProcess sets the "completed_process" field.
|
||||||
func (u *SemiFinishedUpsertBulk) SetCompletedProcess(v string) *SemiFinishedUpsertBulk {
|
func (u *SemiFinishedUpsertBulk) SetCompletedProcess(v string) *SemiFinishedUpsertBulk {
|
||||||
return u.Update(func(s *SemiFinishedUpsert) {
|
return u.Update(func(s *SemiFinishedUpsert) {
|
||||||
|
|||||||
@@ -55,6 +55,12 @@ func (_u *SemiFinishedUpdate) SetNillableMaterialCode(v *string) *SemiFinishedUp
|
|||||||
return _u
|
return _u
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ClearMaterialCode clears the value of the "material_code" field.
|
||||||
|
func (_u *SemiFinishedUpdate) ClearMaterialCode() *SemiFinishedUpdate {
|
||||||
|
_u.mutation.ClearMaterialCode()
|
||||||
|
return _u
|
||||||
|
}
|
||||||
|
|
||||||
// SetCompletedProcess sets the "completed_process" field.
|
// SetCompletedProcess sets the "completed_process" field.
|
||||||
func (_u *SemiFinishedUpdate) SetCompletedProcess(v string) *SemiFinishedUpdate {
|
func (_u *SemiFinishedUpdate) SetCompletedProcess(v string) *SemiFinishedUpdate {
|
||||||
_u.mutation.SetCompletedProcess(v)
|
_u.mutation.SetCompletedProcess(v)
|
||||||
@@ -239,6 +245,9 @@ func (_u *SemiFinishedUpdate) sqlSave(ctx context.Context) (_node int, err error
|
|||||||
if value, ok := _u.mutation.MaterialCode(); ok {
|
if value, ok := _u.mutation.MaterialCode(); ok {
|
||||||
_spec.SetField(semifinished.FieldMaterialCode, field.TypeString, value)
|
_spec.SetField(semifinished.FieldMaterialCode, field.TypeString, value)
|
||||||
}
|
}
|
||||||
|
if _u.mutation.MaterialCodeCleared() {
|
||||||
|
_spec.ClearField(semifinished.FieldMaterialCode, field.TypeString)
|
||||||
|
}
|
||||||
if value, ok := _u.mutation.CompletedProcess(); ok {
|
if value, ok := _u.mutation.CompletedProcess(); ok {
|
||||||
_spec.SetField(semifinished.FieldCompletedProcess, field.TypeString, value)
|
_spec.SetField(semifinished.FieldCompletedProcess, field.TypeString, value)
|
||||||
}
|
}
|
||||||
@@ -326,6 +335,12 @@ func (_u *SemiFinishedUpdateOne) SetNillableMaterialCode(v *string) *SemiFinishe
|
|||||||
return _u
|
return _u
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ClearMaterialCode clears the value of the "material_code" field.
|
||||||
|
func (_u *SemiFinishedUpdateOne) ClearMaterialCode() *SemiFinishedUpdateOne {
|
||||||
|
_u.mutation.ClearMaterialCode()
|
||||||
|
return _u
|
||||||
|
}
|
||||||
|
|
||||||
// SetCompletedProcess sets the "completed_process" field.
|
// SetCompletedProcess sets the "completed_process" field.
|
||||||
func (_u *SemiFinishedUpdateOne) SetCompletedProcess(v string) *SemiFinishedUpdateOne {
|
func (_u *SemiFinishedUpdateOne) SetCompletedProcess(v string) *SemiFinishedUpdateOne {
|
||||||
_u.mutation.SetCompletedProcess(v)
|
_u.mutation.SetCompletedProcess(v)
|
||||||
@@ -540,6 +555,9 @@ func (_u *SemiFinishedUpdateOne) sqlSave(ctx context.Context) (_node *SemiFinish
|
|||||||
if value, ok := _u.mutation.MaterialCode(); ok {
|
if value, ok := _u.mutation.MaterialCode(); ok {
|
||||||
_spec.SetField(semifinished.FieldMaterialCode, field.TypeString, value)
|
_spec.SetField(semifinished.FieldMaterialCode, field.TypeString, value)
|
||||||
}
|
}
|
||||||
|
if _u.mutation.MaterialCodeCleared() {
|
||||||
|
_spec.ClearField(semifinished.FieldMaterialCode, field.TypeString)
|
||||||
|
}
|
||||||
if value, ok := _u.mutation.CompletedProcess(); ok {
|
if value, ok := _u.mutation.CompletedProcess(); ok {
|
||||||
_spec.SetField(semifinished.FieldCompletedProcess, field.TypeString, value)
|
_spec.SetField(semifinished.FieldCompletedProcess, field.TypeString, value)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ type SerialNumber struct {
|
|||||||
ID int `json:"id,omitempty"`
|
ID int `json:"id,omitempty"`
|
||||||
// 序列号
|
// 序列号
|
||||||
SnCode string `json:"snCode,omitempty"`
|
SnCode string `json:"snCode,omitempty"`
|
||||||
// 物料编码
|
// 物料编码(可为空)
|
||||||
MaterialCode string `json:"materialCode,omitempty"`
|
MaterialCode string `json:"materialCode,omitempty"`
|
||||||
// 所属批次
|
// 所属批次
|
||||||
BatchNo string `json:"batchNo,omitempty"`
|
BatchNo string `json:"batchNo,omitempty"`
|
||||||
|
|||||||
@@ -228,6 +228,16 @@ func MaterialCodeHasSuffix(v string) predicate.SerialNumber {
|
|||||||
return predicate.SerialNumber(sql.FieldHasSuffix(FieldMaterialCode, v))
|
return predicate.SerialNumber(sql.FieldHasSuffix(FieldMaterialCode, v))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MaterialCodeIsNil applies the IsNil predicate on the "material_code" field.
|
||||||
|
func MaterialCodeIsNil() predicate.SerialNumber {
|
||||||
|
return predicate.SerialNumber(sql.FieldIsNull(FieldMaterialCode))
|
||||||
|
}
|
||||||
|
|
||||||
|
// MaterialCodeNotNil applies the NotNil predicate on the "material_code" field.
|
||||||
|
func MaterialCodeNotNil() predicate.SerialNumber {
|
||||||
|
return predicate.SerialNumber(sql.FieldNotNull(FieldMaterialCode))
|
||||||
|
}
|
||||||
|
|
||||||
// MaterialCodeEqualFold applies the EqualFold predicate on the "material_code" field.
|
// MaterialCodeEqualFold applies the EqualFold predicate on the "material_code" field.
|
||||||
func MaterialCodeEqualFold(v string) predicate.SerialNumber {
|
func MaterialCodeEqualFold(v string) predicate.SerialNumber {
|
||||||
return predicate.SerialNumber(sql.FieldEqualFold(FieldMaterialCode, v))
|
return predicate.SerialNumber(sql.FieldEqualFold(FieldMaterialCode, v))
|
||||||
|
|||||||
@@ -33,6 +33,14 @@ func (_c *SerialNumberCreate) SetMaterialCode(v string) *SerialNumberCreate {
|
|||||||
return _c
|
return _c
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SetNillableMaterialCode sets the "material_code" field if the given value is not nil.
|
||||||
|
func (_c *SerialNumberCreate) SetNillableMaterialCode(v *string) *SerialNumberCreate {
|
||||||
|
if v != nil {
|
||||||
|
_c.SetMaterialCode(*v)
|
||||||
|
}
|
||||||
|
return _c
|
||||||
|
}
|
||||||
|
|
||||||
// SetBatchNo sets the "batch_no" field.
|
// SetBatchNo sets the "batch_no" field.
|
||||||
func (_c *SerialNumberCreate) SetBatchNo(v string) *SerialNumberCreate {
|
func (_c *SerialNumberCreate) SetBatchNo(v string) *SerialNumberCreate {
|
||||||
_c.mutation.SetBatchNo(v)
|
_c.mutation.SetBatchNo(v)
|
||||||
@@ -217,9 +225,6 @@ func (_c *SerialNumberCreate) check() error {
|
|||||||
if _, ok := _c.mutation.SnCode(); !ok {
|
if _, ok := _c.mutation.SnCode(); !ok {
|
||||||
return &ValidationError{Name: "sn_code", err: errors.New(`ent: missing required field "SerialNumber.sn_code"`)}
|
return &ValidationError{Name: "sn_code", err: errors.New(`ent: missing required field "SerialNumber.sn_code"`)}
|
||||||
}
|
}
|
||||||
if _, ok := _c.mutation.MaterialCode(); !ok {
|
|
||||||
return &ValidationError{Name: "material_code", err: errors.New(`ent: missing required field "SerialNumber.material_code"`)}
|
|
||||||
}
|
|
||||||
if _, ok := _c.mutation.Status(); !ok {
|
if _, ok := _c.mutation.Status(); !ok {
|
||||||
return &ValidationError{Name: "status", err: errors.New(`ent: missing required field "SerialNumber.status"`)}
|
return &ValidationError{Name: "status", err: errors.New(`ent: missing required field "SerialNumber.status"`)}
|
||||||
}
|
}
|
||||||
@@ -379,6 +384,12 @@ func (u *SerialNumberUpsert) UpdateMaterialCode() *SerialNumberUpsert {
|
|||||||
return u
|
return u
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ClearMaterialCode clears the value of the "material_code" field.
|
||||||
|
func (u *SerialNumberUpsert) ClearMaterialCode() *SerialNumberUpsert {
|
||||||
|
u.SetNull(serialnumber.FieldMaterialCode)
|
||||||
|
return u
|
||||||
|
}
|
||||||
|
|
||||||
// SetBatchNo sets the "batch_no" field.
|
// SetBatchNo sets the "batch_no" field.
|
||||||
func (u *SerialNumberUpsert) SetBatchNo(v string) *SerialNumberUpsert {
|
func (u *SerialNumberUpsert) SetBatchNo(v string) *SerialNumberUpsert {
|
||||||
u.Set(serialnumber.FieldBatchNo, v)
|
u.Set(serialnumber.FieldBatchNo, v)
|
||||||
@@ -597,6 +608,13 @@ func (u *SerialNumberUpsertOne) UpdateMaterialCode() *SerialNumberUpsertOne {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ClearMaterialCode clears the value of the "material_code" field.
|
||||||
|
func (u *SerialNumberUpsertOne) ClearMaterialCode() *SerialNumberUpsertOne {
|
||||||
|
return u.Update(func(s *SerialNumberUpsert) {
|
||||||
|
s.ClearMaterialCode()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
// SetBatchNo sets the "batch_no" field.
|
// SetBatchNo sets the "batch_no" field.
|
||||||
func (u *SerialNumberUpsertOne) SetBatchNo(v string) *SerialNumberUpsertOne {
|
func (u *SerialNumberUpsertOne) SetBatchNo(v string) *SerialNumberUpsertOne {
|
||||||
return u.Update(func(s *SerialNumberUpsert) {
|
return u.Update(func(s *SerialNumberUpsert) {
|
||||||
@@ -1004,6 +1022,13 @@ func (u *SerialNumberUpsertBulk) UpdateMaterialCode() *SerialNumberUpsertBulk {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ClearMaterialCode clears the value of the "material_code" field.
|
||||||
|
func (u *SerialNumberUpsertBulk) ClearMaterialCode() *SerialNumberUpsertBulk {
|
||||||
|
return u.Update(func(s *SerialNumberUpsert) {
|
||||||
|
s.ClearMaterialCode()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
// SetBatchNo sets the "batch_no" field.
|
// SetBatchNo sets the "batch_no" field.
|
||||||
func (u *SerialNumberUpsertBulk) SetBatchNo(v string) *SerialNumberUpsertBulk {
|
func (u *SerialNumberUpsertBulk) SetBatchNo(v string) *SerialNumberUpsertBulk {
|
||||||
return u.Update(func(s *SerialNumberUpsert) {
|
return u.Update(func(s *SerialNumberUpsert) {
|
||||||
|
|||||||
@@ -55,6 +55,12 @@ func (_u *SerialNumberUpdate) SetNillableMaterialCode(v *string) *SerialNumberUp
|
|||||||
return _u
|
return _u
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ClearMaterialCode clears the value of the "material_code" field.
|
||||||
|
func (_u *SerialNumberUpdate) ClearMaterialCode() *SerialNumberUpdate {
|
||||||
|
_u.mutation.ClearMaterialCode()
|
||||||
|
return _u
|
||||||
|
}
|
||||||
|
|
||||||
// SetBatchNo sets the "batch_no" field.
|
// SetBatchNo sets the "batch_no" field.
|
||||||
func (_u *SerialNumberUpdate) SetBatchNo(v string) *SerialNumberUpdate {
|
func (_u *SerialNumberUpdate) SetBatchNo(v string) *SerialNumberUpdate {
|
||||||
_u.mutation.SetBatchNo(v)
|
_u.mutation.SetBatchNo(v)
|
||||||
@@ -272,6 +278,9 @@ func (_u *SerialNumberUpdate) sqlSave(ctx context.Context) (_node int, err error
|
|||||||
if value, ok := _u.mutation.MaterialCode(); ok {
|
if value, ok := _u.mutation.MaterialCode(); ok {
|
||||||
_spec.SetField(serialnumber.FieldMaterialCode, field.TypeString, value)
|
_spec.SetField(serialnumber.FieldMaterialCode, field.TypeString, value)
|
||||||
}
|
}
|
||||||
|
if _u.mutation.MaterialCodeCleared() {
|
||||||
|
_spec.ClearField(serialnumber.FieldMaterialCode, field.TypeString)
|
||||||
|
}
|
||||||
if value, ok := _u.mutation.BatchNo(); ok {
|
if value, ok := _u.mutation.BatchNo(); ok {
|
||||||
_spec.SetField(serialnumber.FieldBatchNo, field.TypeString, value)
|
_spec.SetField(serialnumber.FieldBatchNo, field.TypeString, value)
|
||||||
}
|
}
|
||||||
@@ -368,6 +377,12 @@ func (_u *SerialNumberUpdateOne) SetNillableMaterialCode(v *string) *SerialNumbe
|
|||||||
return _u
|
return _u
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ClearMaterialCode clears the value of the "material_code" field.
|
||||||
|
func (_u *SerialNumberUpdateOne) ClearMaterialCode() *SerialNumberUpdateOne {
|
||||||
|
_u.mutation.ClearMaterialCode()
|
||||||
|
return _u
|
||||||
|
}
|
||||||
|
|
||||||
// SetBatchNo sets the "batch_no" field.
|
// SetBatchNo sets the "batch_no" field.
|
||||||
func (_u *SerialNumberUpdateOne) SetBatchNo(v string) *SerialNumberUpdateOne {
|
func (_u *SerialNumberUpdateOne) SetBatchNo(v string) *SerialNumberUpdateOne {
|
||||||
_u.mutation.SetBatchNo(v)
|
_u.mutation.SetBatchNo(v)
|
||||||
@@ -615,6 +630,9 @@ func (_u *SerialNumberUpdateOne) sqlSave(ctx context.Context) (_node *SerialNumb
|
|||||||
if value, ok := _u.mutation.MaterialCode(); ok {
|
if value, ok := _u.mutation.MaterialCode(); ok {
|
||||||
_spec.SetField(serialnumber.FieldMaterialCode, field.TypeString, value)
|
_spec.SetField(serialnumber.FieldMaterialCode, field.TypeString, value)
|
||||||
}
|
}
|
||||||
|
if _u.mutation.MaterialCodeCleared() {
|
||||||
|
_spec.ClearField(serialnumber.FieldMaterialCode, field.TypeString)
|
||||||
|
}
|
||||||
if value, ok := _u.mutation.BatchNo(); ok {
|
if value, ok := _u.mutation.BatchNo(); ok {
|
||||||
_spec.SetField(serialnumber.FieldBatchNo, field.TypeString, value)
|
_spec.SetField(serialnumber.FieldBatchNo, field.TypeString, value)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,8 @@ package handler
|
|||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"bj_power_wms/ent"
|
"bj_power_wms/ent"
|
||||||
"bj_power_wms/ent/ordermaterialledger"
|
"bj_power_wms/ent/ordermaterialledger"
|
||||||
@@ -15,11 +17,15 @@ import (
|
|||||||
// 半成品 / 包装 / 台账 相关(盘点见 stocktake.go)
|
// 半成品 / 包装 / 台账 相关(盘点见 stocktake.go)
|
||||||
|
|
||||||
// semiInboundHandler 半成品入库(携带已完成工序)
|
// semiInboundHandler 半成品入库(携带已完成工序)
|
||||||
|
// 与 MES 闭环:MES 工位报工暂存退库时调用本接口,把半成品 SN 入 WMS 库。
|
||||||
|
// 为保持两系统解耦,materialCode 不强制要求——优先用请求传入,
|
||||||
|
// 其次按 SN 反查 WMS 序列号台账,都没有则留空(SN 本身即追溯锚点)。
|
||||||
func semiInboundHandler(ctx *svc.ServiceContext) http.HandlerFunc {
|
func semiInboundHandler(ctx *svc.ServiceContext) http.HandlerFunc {
|
||||||
return func(w http.ResponseWriter, r *http.Request) {
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
var req struct {
|
var req struct {
|
||||||
Sn string `json:"sn"`
|
Sn string `json:"sn"`
|
||||||
MaterialCode string `json:"materialCode"`
|
MaterialCode string `json:"materialCode"`
|
||||||
|
DoneProcessCodes []int `json:"doneProcessCodes"`
|
||||||
CompletedProcess string `json:"completedProcess"`
|
CompletedProcess string `json:"completedProcess"`
|
||||||
ZoneCode string `json:"zoneCode"`
|
ZoneCode string `json:"zoneCode"`
|
||||||
OrderNo string `json:"orderNo"`
|
OrderNo string `json:"orderNo"`
|
||||||
@@ -29,8 +35,8 @@ func semiInboundHandler(ctx *svc.ServiceContext) http.HandlerFunc {
|
|||||||
fail(w, http.StatusBadRequest, "参数错误")
|
fail(w, http.StatusBadRequest, "参数错误")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if req.Sn == "" || req.MaterialCode == "" {
|
if req.Sn == "" {
|
||||||
fail(w, http.StatusBadRequest, "sn 和 materialCode 必填")
|
fail(w, http.StatusBadRequest, "sn 必填")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -46,10 +52,29 @@ func semiInboundHandler(ctx *svc.ServiceContext) http.HandlerFunc {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 已完成工序:MES 传 doneProcessCodes([]int) 优先,否则用 completedProcess 字符串
|
||||||
|
completed := req.CompletedProcess
|
||||||
|
if len(req.DoneProcessCodes) > 0 {
|
||||||
|
parts := make([]string, 0, len(req.DoneProcessCodes))
|
||||||
|
for _, c := range req.DoneProcessCodes {
|
||||||
|
parts = append(parts, strconv.Itoa(c))
|
||||||
|
}
|
||||||
|
completed = strings.Join(parts, ",")
|
||||||
|
}
|
||||||
|
|
||||||
|
// 物料编码:优先请求传入,其次按 SN 反查序列号台账
|
||||||
|
materialCode := req.MaterialCode
|
||||||
|
if materialCode == "" {
|
||||||
|
if snRec, e := ctx.EntClient.SerialNumber.Query().
|
||||||
|
Where(serialnumber.SnCodeEQ(req.Sn)).Only(ctx0()); e == nil {
|
||||||
|
materialCode = snRec.MaterialCode
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
rec, err := ctx.EntClient.SemiFinished.Create().
|
rec, err := ctx.EntClient.SemiFinished.Create().
|
||||||
SetSn(req.Sn).
|
SetSn(req.Sn).
|
||||||
SetMaterialCode(req.MaterialCode).
|
SetNillableMaterialCode(strPtr(materialCode)).
|
||||||
SetNillableCompletedProcess(strPtr(req.CompletedProcess)).
|
SetNillableCompletedProcess(strPtr(completed)).
|
||||||
SetNillableZoneCode(strPtr(req.ZoneCode)).
|
SetNillableZoneCode(strPtr(req.ZoneCode)).
|
||||||
SetStatus("在库").
|
SetStatus("在库").
|
||||||
SetNillableOrderNo(strPtr(req.OrderNo)).
|
SetNillableOrderNo(strPtr(req.OrderNo)).
|
||||||
@@ -59,10 +84,10 @@ func semiInboundHandler(ctx *svc.ServiceContext) http.HandlerFunc {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// 同时记入 SN 台账(半成品也是精密件)
|
// 同时记入 SN 台账(半成品也是精密件,便于按 SN 追溯)
|
||||||
ctx.EntClient.SerialNumber.Create().
|
ctx.EntClient.SerialNumber.Create().
|
||||||
SetSnCode(req.Sn).
|
SetSnCode(req.Sn).
|
||||||
SetMaterialCode(req.MaterialCode).
|
SetNillableMaterialCode(strPtr(materialCode)).
|
||||||
SetStatus("在库").
|
SetStatus("在库").
|
||||||
SetNillableCurrentZone(strPtr(req.ZoneCode)).
|
SetNillableCurrentZone(strPtr(req.ZoneCode)).
|
||||||
SaveX(ctx0())
|
SaveX(ctx0())
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ type SemiFinished struct {
|
|||||||
func (SemiFinished) Fields() []ent.Field {
|
func (SemiFinished) Fields() []ent.Field {
|
||||||
return []ent.Field{
|
return []ent.Field{
|
||||||
field.String("sn").Unique().Comment("半成品 SN"),
|
field.String("sn").Unique().Comment("半成品 SN"),
|
||||||
field.String("material_code").Comment("物料编码"),
|
field.String("material_code").Optional().Comment("物料编码(MES 未传时按 SN 反查,可空)"),
|
||||||
field.String("completed_process").Optional().Comment("已完成工序(如 135)"),
|
field.String("completed_process").Optional().Comment("已完成工序(如 135)"),
|
||||||
field.Int("quantity").Default(1).Comment("数量"),
|
field.Int("quantity").Default(1).Comment("数量"),
|
||||||
field.String("zone_code").Optional().Comment("所在区域"),
|
field.String("zone_code").Optional().Comment("所在区域"),
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ type SerialNumber struct {
|
|||||||
func (SerialNumber) Fields() []ent.Field {
|
func (SerialNumber) Fields() []ent.Field {
|
||||||
return []ent.Field{
|
return []ent.Field{
|
||||||
field.String("sn_code").Unique().Comment("序列号"),
|
field.String("sn_code").Unique().Comment("序列号"),
|
||||||
field.String("material_code").Comment("物料编码"),
|
field.String("material_code").Optional().Comment("物料编码(可为空)"),
|
||||||
field.String("batch_no").Optional().Comment("所属批次"),
|
field.String("batch_no").Optional().Comment("所属批次"),
|
||||||
// 状态:在库/锁定/出库/使用/报废
|
// 状态:在库/锁定/出库/使用/报废
|
||||||
field.String("status").Default("在库").Comment("在库/锁定/出库/使用/报废"),
|
field.String("status").Default("在库").Comment("在库/锁定/出库/使用/报废"),
|
||||||
|
|||||||
@@ -4,9 +4,11 @@
|
|||||||
<meta charset="UTF-8">
|
<meta charset="UTF-8">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
<title>库房客户端</title>
|
<title>库房客户端</title>
|
||||||
|
<!-- 系统图标:库房箱体(内联 SVG,避免依赖外部文件) -->
|
||||||
|
<link rel="icon" type="image/svg+xml" href="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 32 32'><rect width='32' height='32' rx='6' fill='%231668dc'/><path d='M16 7l9 4.5v9L16 25l-9-4.5v-9L16 7z' fill='none' stroke='%23fff' stroke-width='2' stroke-linejoin='round'/><path d='M7 11.5L16 16l9-4.5M16 16v9' fill='none' stroke='%23fff' stroke-width='2' stroke-linejoin='round'/></svg>">
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div id="app"></div>
|
<div id="app"></div>
|
||||||
<script type="module" src="/src/main.js"></script>
|
<script type="module" src="/src/main.js"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
@@ -69,7 +69,7 @@ export const helpInspection = {
|
|||||||
{ name: '目标类型', source: '单选。', purpose: '检验对象是批次还是 SN', fill: '批次 BATCH / 序列号 SN' },
|
{ name: '目标类型', source: '单选。', purpose: '检验对象是批次还是 SN', fill: '批次 BATCH / 序列号 SN' },
|
||||||
{ name: '检验结论', source: '单选。', purpose: '判定结果', fill: '合格 / 不合格' },
|
{ name: '检验结论', source: '单选。', purpose: '判定结果', fill: '合格 / 不合格' },
|
||||||
{ name: '检验员', source: '手动填写(默认当前用户)。', purpose: '谁检验的,追溯用', fill: '检验员姓名' },
|
{ name: '检验员', source: '手动填写(默认当前用户)。', purpose: '谁检验的,追溯用', fill: '检验员姓名' },
|
||||||
{ name: '扫码输入', source: '扫码枪。来自批次号/SN条码。', purpose: '单个待检目标编号', fill: '扫批次号或 SN,回车提交' },
|
{ name: '扫码输入', source: '扫码枪。来自批次号/SN条码。', purpose: '单个待检的批次号或 SN', fill: '扫批次号或 SN,回车提交' },
|
||||||
{ name: '批量编号', source: '扫码枪连续录入。', purpose: '一次性翻转多个目标的检验状态', fill: '每行一个编号' }
|
{ name: '批量编号', source: '扫码枪连续录入。', purpose: '一次性翻转多个目标的检验状态', fill: '每行一个编号' }
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
@@ -103,14 +103,19 @@ export const helpPackage = {
|
|||||||
export const helpSemi = {
|
export const helpSemi = {
|
||||||
title: '半成品/成品(流转)',
|
title: '半成品/成品(流转)',
|
||||||
overview:
|
overview:
|
||||||
'产线工位"暂存/退回"的半成品或完工成品可在此入库(记录已完成工序),需要时再出库(重上线),MES 从下一工序继续。\n\n半成品生命周期:工位暂存退库 → 本页入库(记已完成工序) → 需要时本页出库→ MES 下发下一工序 → 工位继续装配。',
|
'产线工位"暂存/退回"的半成品或完工成品先在此入库(记已完成工序),需要时再出库(重上线),MES 从下一工序继续。\n\n闭环逻辑(与 MES 自动打通):\n' +
|
||||||
|
'① 工位终端"暂存退库" → 调 MES 工序流转接口 → MES 自动调 WMS /api/internal/semi/inbound 入库(带 SN、工单号、已完成工序码)。\n' +
|
||||||
|
'② WMS 收到后生成"半成品库存"记录,并写入 SN 台账,状态=在库。\n' +
|
||||||
|
'③ 该件后续要接着做时,在本页"半成品出库"或 MES 重上线 → WMS /api/internal/semi/outbound 把状态置为出库。\n' +
|
||||||
|
'④ MES 据此下发下一工序,工位继续装配,直至完工。\n\n' +
|
||||||
|
'SN 是两系统唯一的追溯锚点,因此物料编码非必填:MES 调用时不传,WMS 会优先用请求里的、其次按 SN 反查序列号台账,都没有则留空,不影响闭环。',
|
||||||
sections: [
|
sections: [
|
||||||
{
|
{
|
||||||
title: '半成品/成品入库',
|
title: '半成品/成品入库',
|
||||||
overview: '把产线退回的半成品或成品收进库房。',
|
overview: '把产线退回的半成品或成品收进库房(手工录入或 MES 自动同步均走此接口)。',
|
||||||
fields: [
|
fields: [
|
||||||
{ name: '物料', source: '下拉选择。来自物料档案。', purpose: '入库物料', fill: '选择物料编码' },
|
{ name: '物料', source: '下拉选择。来自物料档案。', purpose: '入库物料(MES 自动同步时通常留空,由 WMS 反查)', fill: '选择物料编码' },
|
||||||
{ name: '已完成工序', source: '自动带出/手工填写。数据来自工位终端的"已完成工序"上报。', purpose: '该件做到第几道工序,重上线时 MES 从这里继续', fill: '如 135,可空' },
|
{ name: '已完成工序', source: 'MES 自动带出(doneProcessCodes,如 [1,3,5]);手工录入填逗号分隔,如 1,3,5。', purpose: '该件已做到哪些工序,重上线时 MES 从下一工序继续', fill: '如 1,3,5,可空' },
|
||||||
{ name: '工单号', source: '自动带出/手工填写。来自工单。', purpose: '来源工单', fill: '输入工单号' },
|
{ name: '工单号', source: '自动带出/手工填写。来自工单。', purpose: '来源工单', fill: '输入工单号' },
|
||||||
{ name: '区域', source: '手动填写(可空)。', purpose: '存放库区', fill: '如 Z05' },
|
{ name: '区域', source: '手动填写(可空)。', purpose: '存放库区', fill: '如 Z05' },
|
||||||
{ name: '扫码录入', source: '扫码枪连续扫码。来自工件 SN 条码。', purpose: '半成品 SN 清单', fill: '扫入 SN 回车追加' }
|
{ name: '扫码录入', source: '扫码枪连续扫码。来自工件 SN 条码。', purpose: '半成品 SN 清单', fill: '扫入 SN 回车追加' }
|
||||||
@@ -118,7 +123,7 @@ export const helpSemi = {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: '半成品出库(重上线)',
|
title: '半成品出库(重上线)',
|
||||||
overview: '把半成品重新送上线,MES 会从记录的下一个工序继续。',
|
overview: '把半成品重新送上线,WMS 状态置为出库,MES 从记录的下一工序继续。',
|
||||||
fields: [
|
fields: [
|
||||||
{ name: 'SN', source: '扫码枪/手输。来自工件 SN 条码。', purpose: '要重上线的半成品 SN', fill: '扫描或输入 SN,回车出库' }
|
{ name: 'SN', source: '扫码枪/手输。来自工件 SN 条码。', purpose: '要重上线的半成品 SN', fill: '扫描或输入 SN,回车出库' }
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ onMounted(() => nextTick(() => scanRef.value?.focus()))
|
|||||||
async function submitSingle() {
|
async function submitSingle() {
|
||||||
const id = targetId.value.trim()
|
const id = targetId.value.trim()
|
||||||
if (!id) {
|
if (!id) {
|
||||||
ElMessage.warning('请扫入或输入目标编号')
|
ElMessage.warning('请扫入或输入批次号或 SN 序列号')
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if (!inspector.value.trim()) {
|
if (!inspector.value.trim()) {
|
||||||
|
|||||||
@@ -133,7 +133,7 @@ function search() {
|
|||||||
</el-select>
|
</el-select>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item label="已完成工序">
|
<el-form-item label="已完成工序">
|
||||||
<el-input v-model="inForm.completedProcess" placeholder="如:135(可空)" clearable />
|
<el-input v-model="inForm.completedProcess" placeholder="如:1,3,5(可空)" clearable />
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item label="工单号">
|
<el-form-item label="工单号">
|
||||||
<el-input v-model="inForm.orderNo" placeholder="来源工单号(可空)" clearable />
|
<el-input v-model="inForm.orderNo" placeholder="来源工单号(可空)" clearable />
|
||||||
|
|||||||
@@ -183,7 +183,7 @@ async function loadHistory() {
|
|||||||
style="margin-bottom:10px" />
|
style="margin-bottom:10px" />
|
||||||
<el-table :data="finishResult.diffs || []" border size="small"
|
<el-table :data="finishResult.diffs || []" border size="small"
|
||||||
empty-text="无差异,账实相符">
|
empty-text="无差异,账实相符">
|
||||||
<el-table-column prop="targetId" label="目标编号" min-width="190" />
|
<el-table-column prop="targetId" label="批次号 / SN 序列号" min-width="190" />
|
||||||
<el-table-column prop="type" label="类型" width="100" align="center">
|
<el-table-column prop="type" label="类型" width="100" align="center">
|
||||||
<template #default="{ row }">{{ row.type === 'BATCH' ? '批次' : 'SN' }}</template>
|
<template #default="{ row }">{{ row.type === 'BATCH' ? '批次' : 'SN' }}</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
|
|||||||
@@ -6,7 +6,9 @@ export default defineConfig({
|
|||||||
plugins: [vue()],
|
plugins: [vue()],
|
||||||
build: {
|
build: {
|
||||||
outDir: '../web/static',
|
outDir: '../web/static',
|
||||||
emptyOutDir: true
|
// 不自动清空产物目录:自动清空会与系统安全删除机制冲突导致构建失败,
|
||||||
|
// 需要重新完整构建时手动执行 rm -rf ../web/static 后再 build。
|
||||||
|
emptyOutDir: false
|
||||||
},
|
},
|
||||||
server: {
|
server: {
|
||||||
port: 5173,
|
port: 5173,
|
||||||
|
|||||||
@@ -0,0 +1,15 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="zh-CN">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>库房客户端</title>
|
||||||
|
<!-- 系统图标:库房箱体(内联 SVG,避免依赖外部文件) -->
|
||||||
|
<link rel="icon" type="image/svg+xml" href="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 32 32'><rect width='32' height='32' rx='6' fill='%231668dc'/><path d='M16 7l9 4.5v9L16 25l-9-4.5v-9L16 7z' fill='none' stroke='%23fff' stroke-width='2' stroke-linejoin='round'/><path d='M7 11.5L16 16l9-4.5M16 16v9' fill='none' stroke='%23fff' stroke-width='2' stroke-linejoin='round'/></svg>">
|
||||||
|
<script type="module" crossorigin src="/assets/index-BWAZ_x_Q.js"></script>
|
||||||
|
<link rel="stylesheet" crossorigin href="/assets/index-DncRC_8X.css">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div id="app"></div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -4,6 +4,8 @@
|
|||||||
<meta charset="UTF-8" />
|
<meta charset="UTF-8" />
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
<title>工位终端</title>
|
<title>工位终端</title>
|
||||||
|
<!-- 系统图标:工位终端屏(内联 SVG,避免依赖外部文件) -->
|
||||||
|
<link rel="icon" type="image/svg+xml" href="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 32 32'><rect width='32' height='32' rx='6' fill='%231668dc'/><rect x='6' y='7' width='20' height='14' rx='2' fill='none' stroke='%23fff' stroke-width='2'/><path d='M12 25h8M16 21v4' stroke='%23fff' stroke-width='2' stroke-linecap='round'/></svg>">
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div id="app"></div>
|
<div id="app"></div>
|
||||||
|
|||||||
@@ -134,3 +134,36 @@ MES 是装配线的控制中心:从「产品类型 → 工单 → 物料清单
|
|||||||
| 排产保存失败 | 多天排产合计超过工单总数量,或该工单已完成/关闭 |
|
| 排产保存失败 | 多天排产合计超过工单总数量,或该工单已完成/关闭 |
|
||||||
| 工单删不掉 | 仅「已创建/待排产/已发布」且无排产、备料、生产数据的工单可删除 |
|
| 工单删不掉 | 仅「已创建/待排产/已发布」且无排产、备料、生产数据的工单可删除 |
|
||||||
| 修改密码后要重新登录 | 正常现象,密码变更即失效当前登录 |
|
| 修改密码后要重新登录 | 正常现象,密码变更即失效当前登录 |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 六、半成品闭环逻辑(MES ↔ WMS 重上线)
|
||||||
|
|
||||||
|
**结论:能闭环。** 半成品(或完工成品)在产线中途暂存、或需跨工单续做时,通过 MES 与 WMS 的内部接口打通,SN 是唯一追溯锚点,两系统尽量解耦。
|
||||||
|
|
||||||
|
### 闭环流程
|
||||||
|
|
||||||
|
1. **暂存退库(MES 工位终端)**
|
||||||
|
工位做到第 N 道工序后暂存退出 → 工位终端调 MES 工序流转接口(`action=INBOUND`)→ MES 本地记一条 `semi_flow`,并**自动**调用 WMS 内部接口 `/api/internal/semi/inbound`,带上:`sn`、`orderNo`、已完成的工序码 `doneProcessCodes`、操作人。
|
||||||
|
2. **WMS 入库建账**
|
||||||
|
WMS 生成一条「半成品库存」记录(状态=在库),并同步写入 SN 台账。
|
||||||
|
- 物料编码(materialCode)**不强制**:MES 调用时不传,WMS 优先用请求里的、其次按 SN 反查序列号台账,都没有则留空,不影响闭环。
|
||||||
|
- 已完成工序:MES 传 `doneProcessCodes`(如 `[1,3,5]`),WMS 存为逗号串 `1,3,5`。
|
||||||
|
3. **重上线(续做)**
|
||||||
|
该件后续要接着做时,在本页「半成品出库」或 MES 侧触发 → WMS `/api/internal/semi/outbound` 把该 SN 状态置为出库。
|
||||||
|
4. **MES 续做至完工**
|
||||||
|
MES 已知该件已完成的工序,下发下一工序 → 工位继续装配 → 直至完工、生成追溯。
|
||||||
|
|
||||||
|
> 也支持倒序:工单只建 1、3 序先做成半成品入库;新建另一个工单只勾 2 序,把半成品出库重上线,补齐中间工序变成成品。
|
||||||
|
|
||||||
|
### 同步失败怎么办
|
||||||
|
|
||||||
|
- MES 调 WMS 若失败(WMS 不可达 / 校验不通过),**不再静默吞掉**:MES 会写一条 `semi.flow.wms_error` 操作日志并返回错误,工位终端/接口会提示「WMS 同步失败」。
|
||||||
|
- 本地 `semi_flow` 记录已保存,待 WMS 恢复后重试流转即可补齐闭环。
|
||||||
|
|
||||||
|
### 在 WMS 客户端怎么看
|
||||||
|
|
||||||
|
- **半成品/成品入库页**:手工录入时选物料、填已完成工序(逗号分隔)、扫 SN 提交。
|
||||||
|
- **半成品出库页**:扫 SN 出库(重上线)。
|
||||||
|
- **查询页**:按物料编码 / 状态(在库/出库)查半成品库存。
|
||||||
|
- 帮助按钮(右上角)→ 本页有完整字段说明与数据链路。
|
||||||
|
|||||||
Reference in New Issue
Block a user