2026-08-28 15:06:01 +08:00
|
|
|
|
package logic
|
|
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
|
"context"
|
|
|
|
|
|
"errors"
|
2026-09-08 11:44:04 +08:00
|
|
|
|
"fmt"
|
|
|
|
|
|
"strings"
|
2026-08-28 15:06:01 +08:00
|
|
|
|
|
2026-09-08 11:44:04 +08:00
|
|
|
|
"bj_power_mes/ent/bomitem"
|
|
|
|
|
|
"bj_power_mes/ent/workorder"
|
|
|
|
|
|
"bj_power_mes/internal/wmsclient"
|
2026-08-28 15:06:01 +08:00
|
|
|
|
)
|
|
|
|
|
|
|
2026-09-08 11:44:04 +08:00
|
|
|
|
// 产品型号(成品档案):编码主数据单一来源 = WMS(materials item_type=3)。
|
2026-09-22 07:41:40 +08:00
|
|
|
|
// MES 本页实时代理 WMS;不再保留本地 product_types 缓存/双写(方案 A:WMS 不可达即报错,
|
|
|
|
|
|
// 不降级读本地缓存,避免给界面喂空/陈旧数据)。
|
2026-09-08 11:44:04 +08:00
|
|
|
|
|
2026-08-28 15:06:01 +08:00
|
|
|
|
type ProductTypeReq struct {
|
|
|
|
|
|
Id int `json:"id"`
|
|
|
|
|
|
Name string `json:"name"`
|
|
|
|
|
|
Code string `json:"code"`
|
2026-09-19 10:54:15 +08:00
|
|
|
|
Spec string `json:"spec"`
|
|
|
|
|
|
Unit string `json:"unit"`
|
2026-09-22 11:32:29 +08:00
|
|
|
|
Category string `json:"category"`
|
|
|
|
|
|
ManageMode int `json:"manageMode"` // 1结构件(批次)/2电气件(SN);MES 成品只列这两个
|
|
|
|
|
|
IsActive bool `json:"isActive"`
|
2026-08-28 15:06:01 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
func (s *Service) CreateProductType(ctx context.Context, req ProductTypeReq) error {
|
|
|
|
|
|
active := req.IsActive
|
|
|
|
|
|
if !req.IsActive {
|
|
|
|
|
|
active = true
|
|
|
|
|
|
}
|
2026-09-19 10:54:15 +08:00
|
|
|
|
if strings.TrimSpace(req.Name) == "" {
|
|
|
|
|
|
return errors.New("名称为必填项")
|
|
|
|
|
|
}
|
|
|
|
|
|
if strings.TrimSpace(req.Spec) == "" {
|
|
|
|
|
|
return errors.New("规格型号为必填项")
|
|
|
|
|
|
}
|
|
|
|
|
|
if strings.TrimSpace(req.Unit) == "" {
|
|
|
|
|
|
return errors.New("单位为必填项")
|
|
|
|
|
|
}
|
2026-09-22 11:32:29 +08:00
|
|
|
|
if err := s.ctx.Wms.CreateProductType(ctx, req.Code, req.Name, req.Spec, req.Unit, req.Category, active, req.ManageMode); err != nil {
|
2026-09-08 11:44:04 +08:00
|
|
|
|
return errors.New("WMS 不可达或拒绝写入(编码主数据以 WMS 为单一来源):" + err.Error())
|
|
|
|
|
|
}
|
|
|
|
|
|
return nil
|
2026-08-28 15:06:01 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
func (s *Service) UpdateProductType(ctx context.Context, req ProductTypeReq) error {
|
|
|
|
|
|
if req.Id == 0 {
|
|
|
|
|
|
return errors.New("缺少 id")
|
|
|
|
|
|
}
|
2026-09-19 10:54:15 +08:00
|
|
|
|
if strings.TrimSpace(req.Name) == "" {
|
|
|
|
|
|
return errors.New("名称为必填项")
|
|
|
|
|
|
}
|
|
|
|
|
|
if strings.TrimSpace(req.Spec) == "" {
|
|
|
|
|
|
return errors.New("规格型号为必填项")
|
|
|
|
|
|
}
|
|
|
|
|
|
if strings.TrimSpace(req.Unit) == "" {
|
|
|
|
|
|
return errors.New("单位为必填项")
|
|
|
|
|
|
}
|
2026-09-22 11:32:29 +08:00
|
|
|
|
if err := s.ctx.Wms.UpdateProductType(ctx, req.Id, req.Name, req.Spec, req.Unit, req.Category, req.IsActive, req.ManageMode); err != nil {
|
2026-09-08 11:44:04 +08:00
|
|
|
|
return errors.New("WMS 不可达或拒绝写入:" + err.Error())
|
|
|
|
|
|
}
|
|
|
|
|
|
return nil
|
2026-08-28 15:06:01 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-09-08 11:44:04 +08:00
|
|
|
|
// DeleteProductType 删除保护:被工单/BOM 引用过的型号禁删(MES 侧引用校验,
|
|
|
|
|
|
// WMS 侧库存引用由 WMS 内部接口再校验一层)
|
2026-08-28 15:06:01 +08:00
|
|
|
|
func (s *Service) DeleteProductType(ctx context.Context, id int) error {
|
2026-09-22 07:41:40 +08:00
|
|
|
|
row, err := s.ProductTypeByID(ctx, id)
|
2026-09-08 11:44:04 +08:00
|
|
|
|
if err != nil {
|
|
|
|
|
|
return err
|
|
|
|
|
|
}
|
|
|
|
|
|
woCnt, _ := s.ctx.EntClient.WorkOrder.Query().Where(workorder.ProductCode(row.Code)).Count(ctx)
|
|
|
|
|
|
if woCnt > 0 {
|
2026-09-18 18:54:54 +08:00
|
|
|
|
return fmt.Errorf("该物料档案已被 %d 张工单引用,不可删除;建议停用(下架)", woCnt)
|
2026-09-08 11:44:04 +08:00
|
|
|
|
}
|
|
|
|
|
|
bomCnt, _ := s.ctx.EntClient.BomItem.Query().Where(bomitem.ProductCode(row.Code)).Count(ctx)
|
|
|
|
|
|
if bomCnt > 0 {
|
2026-09-18 18:54:54 +08:00
|
|
|
|
return fmt.Errorf("该物料档案在物料清单中有 %d 条配置,不可删除;建议停用(下架)", bomCnt)
|
2026-09-08 11:44:04 +08:00
|
|
|
|
}
|
|
|
|
|
|
if err := s.ctx.Wms.DeleteProductType(ctx, id); err != nil {
|
|
|
|
|
|
return errors.New("WMS 不可达或拒绝删除:" + err.Error())
|
|
|
|
|
|
}
|
|
|
|
|
|
return nil
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// ListProductTypes 全量列表(下拉/兼容形态,id asc 新建在底部)。
|
2026-09-22 07:41:40 +08:00
|
|
|
|
// 纯 WMS 代理:WMS 不可达直接报错(不再降级本地缓存)。degraded 恒为 true(仅兼容前端字段)。
|
2026-09-22 11:32:29 +08:00
|
|
|
|
func (s *Service) ListProductTypes(ctx context.Context, isActive string) ([]wmsclient.ProductTypeRow, bool, error) {
|
|
|
|
|
|
list, err := s.ctx.Wms.ProductTypes(ctx, isActive)
|
2026-09-22 07:41:40 +08:00
|
|
|
|
if err != nil {
|
|
|
|
|
|
return nil, false, errors.New("WMS 不可达,无法读取成品档案:" + err.Error())
|
2026-09-08 11:44:04 +08:00
|
|
|
|
}
|
2026-09-22 07:41:40 +08:00
|
|
|
|
return list, true, nil
|
2026-09-08 11:44:04 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-09-08 13:43:08 +08:00
|
|
|
|
// ListProductTypesPage 分页+搜索(管理页真分页)。
|
|
|
|
|
|
// 2026-09-08 禁止关键字混搜:编码/名称/类别各自独立模糊。
|
2026-09-22 07:41:40 +08:00
|
|
|
|
// 纯 WMS 代理:WMS 不可达直接报错。wmsOnline 恒为 true(仅兼容前端字段)。
|
2026-09-08 13:43:08 +08:00
|
|
|
|
func (s *Service) ListProductTypesPage(ctx context.Context, code, name, category, isActive string, page, pageSize int) (int64, []wmsclient.ProductTypeRow, bool, error) {
|
|
|
|
|
|
total, list, err := s.ctx.Wms.ProductTypePage(ctx, code, name, category, isActive, page, pageSize)
|
2026-09-22 07:41:40 +08:00
|
|
|
|
if err != nil {
|
|
|
|
|
|
return 0, nil, false, errors.New("WMS 不可达,无法读取成品档案:" + err.Error())
|
2026-09-08 11:44:04 +08:00
|
|
|
|
}
|
2026-09-22 07:41:40 +08:00
|
|
|
|
return total, list, true, nil
|
2026-09-08 11:44:04 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-09-22 07:41:40 +08:00
|
|
|
|
// ProductTypeByID 从 WMS 取成品档案(item_type=3)按 id;WMS 不可达即报错。
|
|
|
|
|
|
func (s *Service) ProductTypeByID(ctx context.Context, id int) (*wmsclient.ProductTypeRow, error) {
|
2026-09-08 13:43:08 +08:00
|
|
|
|
list, err := s.ctx.Wms.ProductTypes(ctx, "")
|
2026-09-08 11:44:04 +08:00
|
|
|
|
if err != nil {
|
2026-09-22 07:41:40 +08:00
|
|
|
|
return nil, errors.New("WMS 不可达,无法读取成品档案:" + err.Error())
|
|
|
|
|
|
}
|
|
|
|
|
|
for i := range list {
|
|
|
|
|
|
if list[i].ID == id {
|
|
|
|
|
|
return &list[i], nil
|
|
|
|
|
|
}
|
2026-09-08 11:44:04 +08:00
|
|
|
|
}
|
2026-09-22 07:41:40 +08:00
|
|
|
|
return nil, errors.New("物料档案不存在")
|
2026-08-28 15:06:01 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-09-22 07:41:40 +08:00
|
|
|
|
// ProductTypeByCode 从 WMS 取成品档案按 code。
|
|
|
|
|
|
func (s *Service) ProductTypeByCode(ctx context.Context, code string) (*wmsclient.ProductTypeRow, error) {
|
|
|
|
|
|
list, err := s.ctx.Wms.ProductTypes(ctx, "")
|
2026-09-08 11:44:04 +08:00
|
|
|
|
if err != nil {
|
2026-09-22 07:41:40 +08:00
|
|
|
|
return nil, errors.New("WMS 不可达,无法读取成品档案:" + err.Error())
|
|
|
|
|
|
}
|
|
|
|
|
|
for i := range list {
|
|
|
|
|
|
if list[i].Code == code {
|
|
|
|
|
|
return &list[i], nil
|
2026-09-08 11:44:04 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
2026-09-22 07:41:40 +08:00
|
|
|
|
return nil, errors.New("物料档案不存在")
|
2026-09-08 11:44:04 +08:00
|
|
|
|
}
|