material_manage_system/internal/service/admin_master.go

230 lines
7.1 KiB
Go
Raw 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.

package service
import (
"errors"
"strings"
"github.com/wmms/wmms/internal/model"
"github.com/wmms/wmms/internal/repository"
)
var errInvalidMasterData = errors.New("code and name are required")
var errUpdateRequiresID = errors.New("id is required for update")
var ErrProtectedRole = repository.ErrProtectedRole
type OrganizationService struct {
repo *repository.OrganizationRepository
}
func NewOrganizationService() *OrganizationService {
return &OrganizationService{repo: repository.NewOrganizationRepository()}
}
func (s *OrganizationService) List() ([]model.Organization, error) { return s.repo.List() }
func (s *OrganizationService) ListRoles() ([]model.Role, error) { return s.repo.ListRoles() }
func (s *OrganizationService) ListPermissions() ([]model.Permission, error) {
return s.repo.ListPermissions()
}
func (s *OrganizationService) UpdateRole(roleID int64, req *model.UpdateRoleRequest) error {
req.Name = strings.TrimSpace(req.Name)
if req.Name == "" {
return errors.New("role name is required")
}
return s.repo.UpdateRole(roleID, req.Name, req.Enabled, req.PermissionCodes)
}
func (s *OrganizationService) Create(item *model.Organization) error {
item.Code = strings.TrimSpace(item.Code)
item.Name = strings.TrimSpace(item.Name)
if item.Code == "" || item.Name == "" {
return errInvalidMasterData
}
applyDefaults(&item.Status, &item.DefaultTimezone)
return s.repo.Create(item)
}
func (s *OrganizationService) Update(item *model.Organization) error {
item.Name = strings.TrimSpace(item.Name)
if item.Name == "" {
return errInvalidMasterData
}
applyDefaults(&item.Status, &item.DefaultTimezone)
return s.repo.Update(item)
}
func applyDefaults(status, timezone *string) {
if *status == "" {
*status = "active"
}
if *timezone == "" {
*timezone = "Asia/Shanghai"
}
}
type MasterDataService struct {
repo *repository.MasterDataRepository
}
func NewMasterDataService() *MasterDataService {
return &MasterDataService{repo: repository.NewMasterDataRepository()}
}
func (s *MasterDataService) ListCategories(orgID int64) ([]model.MaterialCategory, error) {
return s.repo.ListCategories(orgID)
}
func (s *MasterDataService) SaveCategory(item *model.MaterialCategory) error {
item.Code = strings.TrimSpace(item.Code)
item.Name = strings.TrimSpace(item.Name)
item.Description = strings.TrimSpace(item.Description)
if item.Code == "" || item.Name == "" {
return errInvalidMasterData
}
if item.Status == "" {
item.Status = "active"
}
if item.ID == 0 {
return s.repo.CreateCategory(item)
}
return s.repo.UpdateCategory(item)
}
func (s *MasterDataService) ListUnits(orgID int64) ([]model.Unit, error) {
return s.repo.ListUnits(orgID)
}
func (s *MasterDataService) SaveUnit(item *model.Unit) error {
item.Code = strings.TrimSpace(item.Code)
item.Name = strings.TrimSpace(item.Name)
if item.Code == "" || item.Name == "" {
return errInvalidMasterData
}
if item.Status == "" {
item.Status = "active"
}
if item.PrecisionScale < 0 || item.PrecisionScale > 6 {
return errors.New("precision_scale must be between 0 and 6")
}
if item.ID == 0 {
return s.repo.CreateUnit(item)
}
return s.repo.UpdateUnit(item)
}
func (s *MasterDataService) ListCodeRules(orgID int64) ([]model.CodeRule, error) {
return s.repo.ListCodeRules(orgID)
}
// NextMaterialCode 预览新增物料时按小类规则自动生成的编码。这里只读不落库,
// 返回的编码只是候选值;真正占号发生在创建物料的事务里。
func (s *MasterDataService) NextMaterialCode(orgID, categoryID int64) (*model.NextMaterialCode, error) {
rule, err := s.repo.FindActiveCodeRule(orgID, categoryID)
if err != nil {
return nil, err
}
if rule == nil {
return &model.NextMaterialCode{Available: false}, nil
}
return &model.NextMaterialCode{Available: true, Code: rule.FormatCode(rule.NextSequence)}, nil
}
func (s *MasterDataService) SaveCodeRule(item *model.CodeRule) error {
item.Prefix = strings.TrimSpace(item.Prefix)
if item.CategoryID <= 0 || item.Prefix == "" {
return errors.New("category_id and prefix are required")
}
if item.SequenceLength < 1 || item.SequenceLength > 12 {
return errors.New("sequence_length must be between 1 and 12")
}
if item.NextSequence <= 0 {
item.NextSequence = 1
}
if item.Status == "" {
item.Status = "active"
}
if item.ID == 0 {
return s.repo.CreateCodeRule(item)
}
return s.repo.UpdateCodeRule(item)
}
type PartnerService struct{ repo *repository.PartnerRepository }
func NewPartnerService() *PartnerService {
return &PartnerService{repo: repository.NewPartnerRepository()}
}
func (s *PartnerService) List(orgID int64, role string) ([]model.BusinessPartner, error) {
if role != "" && role != "supplier" && role != "customer" {
return nil, errors.New("role must be supplier or customer")
}
return s.repo.List(orgID, role)
}
// Create 新建伙伴。请求体可能带着前端表单残留的 id,这里不读取 item.ID,
// repository 的 INSERT 也不写 id 列,所以脏 id 不会把新建变成覆盖。
func (s *PartnerService) Create(item *model.BusinessPartner) error {
if err := preparePartner(item); err != nil {
return err
}
return s.repo.Create(item)
}
// Update 更新伙伴。id 来自路由参数,必须显式存在。
func (s *PartnerService) Update(item *model.BusinessPartner) error {
if item.ID == 0 {
return errUpdateRequiresID
}
if err := preparePartner(item); err != nil {
return err
}
return s.repo.Update(item)
}
// preparePartner 是新建与更新共用的字段清理、校验和默认值补全。
func preparePartner(item *model.BusinessPartner) error {
item.PartnerCode = strings.TrimSpace(item.PartnerCode)
item.LegalName = strings.TrimSpace(item.LegalName)
if item.PartnerCode == "" || item.LegalName == "" {
return errors.New("partner_code and legal_name are required")
}
if !item.IsSupplier && !item.IsCustomer {
return errors.New("at least one partner role is required")
}
if item.IsSupplier && item.SupplierStatus == "" {
item.SupplierStatus = "active"
}
if item.IsCustomer && item.CustomerStatus == "" {
item.CustomerStatus = "active"
}
return nil
}
type WarehouseService struct {
repo *repository.WarehouseRepository
}
func NewWarehouseService() *WarehouseService {
return &WarehouseService{repo: repository.NewWarehouseRepository()}
}
func (s *WarehouseService) List(orgID int64) ([]model.Warehouse, error) {
return s.repo.List(orgID)
}
func (s *WarehouseService) Save(item *model.Warehouse) error {
item.Code = strings.TrimSpace(item.Code)
item.Name = strings.TrimSpace(item.Name)
if item.Code == "" || item.Name == "" {
return errInvalidMasterData
}
if item.Status == "" {
item.Status = "active"
}
if item.ID == 0 {
return s.repo.Create(item)
}
return s.repo.Update(item)
}
func (s *WarehouseService) SaveLocation(item *model.WarehouseLocation, orgID int64) error {
item.Code = strings.TrimSpace(item.Code)
item.Name = strings.TrimSpace(item.Name)
if item.Code == "" || item.Name == "" || item.WarehouseID <= 0 {
return errInvalidMasterData
}
if item.Status == "" {
item.Status = "active"
}
if item.ID == 0 {
return s.repo.CreateLocation(item, orgID)
}
return s.repo.UpdateLocation(item, orgID)
}