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 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) } 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) } func (s *PartnerService) Save(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" } if item.ID == 0 { return s.repo.Create(item) } return s.repo.Update(item) } 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) }