634 lines
19 KiB
Go
634 lines
19 KiB
Go
package service
|
|
|
|
import (
|
|
"context"
|
|
"sort"
|
|
"strings"
|
|
|
|
"eslogad-be/config"
|
|
"eslogad-be/internal/contract"
|
|
"eslogad-be/internal/entities"
|
|
"eslogad-be/internal/repository"
|
|
"eslogad-be/internal/transformer"
|
|
|
|
"github.com/google/uuid"
|
|
)
|
|
|
|
type MasterServiceImpl struct {
|
|
labelRepo *repository.LabelRepository
|
|
priorityRepo *repository.PriorityRepository
|
|
institutionRepo *repository.InstitutionRepository
|
|
dispRepo *repository.DispositionActionRepository
|
|
departmentRepo *repository.DepartmentRepository
|
|
config *config.Config
|
|
}
|
|
|
|
func NewMasterService(label *repository.LabelRepository, priority *repository.PriorityRepository, institution *repository.InstitutionRepository, disp *repository.DispositionActionRepository, department *repository.DepartmentRepository, cfg *config.Config) *MasterServiceImpl {
|
|
return &MasterServiceImpl{labelRepo: label, priorityRepo: priority, institutionRepo: institution, dispRepo: disp, departmentRepo: department, config: cfg}
|
|
}
|
|
|
|
// Labels
|
|
func (s *MasterServiceImpl) CreateLabel(ctx context.Context, req *contract.CreateLabelRequest) (*contract.LabelResponse, error) {
|
|
entity := &entities.Label{Name: req.Name, Color: req.Color}
|
|
if err := s.labelRepo.Create(ctx, entity); err != nil {
|
|
return nil, err
|
|
}
|
|
resp := transformer.LabelsToContract([]entities.Label{*entity})[0]
|
|
return &resp, nil
|
|
}
|
|
func (s *MasterServiceImpl) UpdateLabel(ctx context.Context, id uuid.UUID, req *contract.UpdateLabelRequest) (*contract.LabelResponse, error) {
|
|
entity := &entities.Label{ID: id}
|
|
if req.Name != nil {
|
|
entity.Name = *req.Name
|
|
}
|
|
if req.Color != nil {
|
|
entity.Color = req.Color
|
|
}
|
|
if err := s.labelRepo.Update(ctx, entity); err != nil {
|
|
return nil, err
|
|
}
|
|
e, err := s.labelRepo.Get(ctx, id)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
resp := transformer.LabelsToContract([]entities.Label{*e})[0]
|
|
return &resp, nil
|
|
}
|
|
func (s *MasterServiceImpl) DeleteLabel(ctx context.Context, id uuid.UUID) error {
|
|
return s.labelRepo.Delete(ctx, id)
|
|
}
|
|
func (s *MasterServiceImpl) ListLabels(ctx context.Context) (*contract.ListLabelsResponse, error) {
|
|
list, err := s.labelRepo.List(ctx)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return &contract.ListLabelsResponse{Labels: transformer.LabelsToContract(list)}, nil
|
|
}
|
|
|
|
// Priorities
|
|
func (s *MasterServiceImpl) CreatePriority(ctx context.Context, req *contract.CreatePriorityRequest) (*contract.PriorityResponse, error) {
|
|
entity := &entities.Priority{Name: req.Name, Level: req.Level}
|
|
if err := s.priorityRepo.Create(ctx, entity); err != nil {
|
|
return nil, err
|
|
}
|
|
resp := transformer.PrioritiesToContract([]entities.Priority{*entity})[0]
|
|
return &resp, nil
|
|
}
|
|
func (s *MasterServiceImpl) UpdatePriority(ctx context.Context, id uuid.UUID, req *contract.UpdatePriorityRequest) (*contract.PriorityResponse, error) {
|
|
entity := &entities.Priority{ID: id}
|
|
if req.Name != nil {
|
|
entity.Name = *req.Name
|
|
}
|
|
if req.Level != nil {
|
|
entity.Level = *req.Level
|
|
}
|
|
if err := s.priorityRepo.Update(ctx, entity); err != nil {
|
|
return nil, err
|
|
}
|
|
e, err := s.priorityRepo.Get(ctx, id)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
resp := transformer.PrioritiesToContract([]entities.Priority{*e})[0]
|
|
return &resp, nil
|
|
}
|
|
func (s *MasterServiceImpl) DeletePriority(ctx context.Context, id uuid.UUID) error {
|
|
return s.priorityRepo.Delete(ctx, id)
|
|
}
|
|
func (s *MasterServiceImpl) ListPriorities(ctx context.Context) (*contract.ListPrioritiesResponse, error) {
|
|
list, err := s.priorityRepo.List(ctx)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return &contract.ListPrioritiesResponse{Priorities: transformer.PrioritiesToContract(list)}, nil
|
|
}
|
|
|
|
// Institutions
|
|
func (s *MasterServiceImpl) CreateInstitution(ctx context.Context, req *contract.CreateInstitutionRequest) (*contract.InstitutionResponse, error) {
|
|
entity := &entities.Institution{Name: req.Name, Type: entities.InstitutionType(req.Type), Address: req.Address, ContactPerson: req.ContactPerson, Phone: req.Phone, Email: req.Email}
|
|
if err := s.institutionRepo.Create(ctx, entity); err != nil {
|
|
return nil, err
|
|
}
|
|
resp := transformer.InstitutionsToContract([]entities.Institution{*entity})[0]
|
|
return &resp, nil
|
|
}
|
|
func (s *MasterServiceImpl) UpdateInstitution(ctx context.Context, id uuid.UUID, req *contract.UpdateInstitutionRequest) (*contract.InstitutionResponse, error) {
|
|
entity := &entities.Institution{ID: id}
|
|
if req.Name != nil {
|
|
entity.Name = *req.Name
|
|
}
|
|
if req.Type != nil {
|
|
entity.Type = entities.InstitutionType(*req.Type)
|
|
}
|
|
if req.Address != nil {
|
|
entity.Address = req.Address
|
|
}
|
|
if req.ContactPerson != nil {
|
|
entity.ContactPerson = req.ContactPerson
|
|
}
|
|
if req.Phone != nil {
|
|
entity.Phone = req.Phone
|
|
}
|
|
if req.Email != nil {
|
|
entity.Email = req.Email
|
|
}
|
|
if err := s.institutionRepo.Update(ctx, entity); err != nil {
|
|
return nil, err
|
|
}
|
|
e, err := s.institutionRepo.Get(ctx, id)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
resp := transformer.InstitutionsToContract([]entities.Institution{*e})[0]
|
|
return &resp, nil
|
|
}
|
|
func (s *MasterServiceImpl) DeleteInstitution(ctx context.Context, id uuid.UUID) error {
|
|
return s.institutionRepo.Delete(ctx, id)
|
|
}
|
|
func (s *MasterServiceImpl) ListInstitutions(ctx context.Context, req *contract.ListInstitutionsRequest) (*contract.ListInstitutionsResponse, error) {
|
|
list, err := s.institutionRepo.ListWithSearch(ctx, req.Search)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return &contract.ListInstitutionsResponse{Institutions: transformer.InstitutionsToContract(list)}, nil
|
|
}
|
|
|
|
// Disposition Actions
|
|
func (s *MasterServiceImpl) CreateDispositionAction(ctx context.Context, req *contract.CreateDispositionActionRequest) (*contract.DispositionActionResponse, error) {
|
|
entity := &entities.DispositionAction{Code: req.Code, Label: req.Label, Description: req.Description}
|
|
if req.RequiresNote != nil {
|
|
entity.RequiresNote = *req.RequiresNote
|
|
}
|
|
if req.GroupName != nil {
|
|
entity.GroupName = req.GroupName
|
|
}
|
|
if req.SortOrder != nil {
|
|
entity.SortOrder = req.SortOrder
|
|
}
|
|
if req.IsActive != nil {
|
|
entity.IsActive = *req.IsActive
|
|
}
|
|
if err := s.dispRepo.Create(ctx, entity); err != nil {
|
|
return nil, err
|
|
}
|
|
resp := transformer.DispositionActionsToContract([]entities.DispositionAction{*entity})[0]
|
|
return &resp, nil
|
|
}
|
|
func (s *MasterServiceImpl) UpdateDispositionAction(ctx context.Context, id uuid.UUID, req *contract.UpdateDispositionActionRequest) (*contract.DispositionActionResponse, error) {
|
|
entity := &entities.DispositionAction{ID: id}
|
|
if req.Code != nil {
|
|
entity.Code = *req.Code
|
|
}
|
|
if req.Label != nil {
|
|
entity.Label = *req.Label
|
|
}
|
|
if req.Description != nil {
|
|
entity.Description = req.Description
|
|
}
|
|
if req.RequiresNote != nil {
|
|
entity.RequiresNote = *req.RequiresNote
|
|
}
|
|
if req.GroupName != nil {
|
|
entity.GroupName = req.GroupName
|
|
}
|
|
if req.SortOrder != nil {
|
|
entity.SortOrder = req.SortOrder
|
|
}
|
|
if req.IsActive != nil {
|
|
entity.IsActive = *req.IsActive
|
|
}
|
|
if err := s.dispRepo.Update(ctx, entity); err != nil {
|
|
return nil, err
|
|
}
|
|
e, err := s.dispRepo.Get(ctx, id)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
resp := transformer.DispositionActionsToContract([]entities.DispositionAction{*e})[0]
|
|
return &resp, nil
|
|
}
|
|
func (s *MasterServiceImpl) DeleteDispositionAction(ctx context.Context, id uuid.UUID) error {
|
|
return s.dispRepo.Delete(ctx, id)
|
|
}
|
|
func (s *MasterServiceImpl) ListDispositionActions(ctx context.Context) (*contract.ListDispositionActionsResponse, error) {
|
|
list, err := s.dispRepo.List(ctx)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return &contract.ListDispositionActionsResponse{Actions: transformer.DispositionActionsToContract(list)}, nil
|
|
}
|
|
|
|
// Departments
|
|
func (s *MasterServiceImpl) CreateDepartment(ctx context.Context, req *contract.CreateDepartmentRequest) (*contract.GetDepartmentResponse, error) {
|
|
// Build the path based on parent
|
|
var path string
|
|
if req.ParentID != nil {
|
|
// Get parent department to build the path
|
|
parent, err := s.departmentRepo.GetByID(ctx, *req.ParentID)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
// Build path as parent.path + code
|
|
path = parent.Path + "." + req.Code
|
|
} else {
|
|
// Root level department, just use the code as path
|
|
path = req.Code
|
|
}
|
|
|
|
entity := &entities.Department{
|
|
Name: req.Name,
|
|
Code: req.Code,
|
|
Path: path,
|
|
}
|
|
if err := s.departmentRepo.Create(ctx, entity); err != nil {
|
|
return nil, err
|
|
}
|
|
// Get parent name if parent exists
|
|
var parentName *string
|
|
if req.ParentID != nil {
|
|
if parent, err := s.departmentRepo.GetByID(ctx, *req.ParentID); err == nil {
|
|
parentName = &parent.Name
|
|
}
|
|
}
|
|
|
|
return &contract.GetDepartmentResponse{
|
|
ID: entity.ID,
|
|
Name: entity.Name,
|
|
Code: entity.Code,
|
|
Path: entity.Path,
|
|
ParentID: req.ParentID,
|
|
ParentName: parentName,
|
|
CreatedAt: entity.CreatedAt,
|
|
UpdatedAt: entity.UpdatedAt,
|
|
}, nil
|
|
}
|
|
|
|
func (s *MasterServiceImpl) GetDepartment(ctx context.Context, id uuid.UUID) (*contract.GetDepartmentResponse, error) {
|
|
entity, err := s.departmentRepo.Get(ctx, id)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// Derive parent_id and parent_name from path
|
|
var parentID *uuid.UUID
|
|
var parentName *string
|
|
parts := strings.Split(entity.Path, ".")
|
|
if len(parts) > 1 {
|
|
// Has parent, try to find it
|
|
parentPath := strings.Join(parts[:len(parts)-1], ".")
|
|
if parent, err := s.departmentRepo.GetByPath(ctx, parentPath); err == nil {
|
|
parentID = &parent.ID
|
|
parentName = &parent.Name
|
|
}
|
|
}
|
|
|
|
return &contract.GetDepartmentResponse{
|
|
ID: entity.ID,
|
|
Name: entity.Name,
|
|
Code: entity.Code,
|
|
Path: entity.Path,
|
|
ParentID: parentID,
|
|
ParentName: parentName,
|
|
CreatedAt: entity.CreatedAt,
|
|
UpdatedAt: entity.UpdatedAt,
|
|
}, nil
|
|
}
|
|
|
|
func (s *MasterServiceImpl) UpdateDepartment(ctx context.Context, id uuid.UUID, req *contract.UpdateDepartmentRequest) (*contract.GetDepartmentResponse, error) {
|
|
entity, err := s.departmentRepo.Get(ctx, id)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// Store the old path before changes
|
|
oldPath := entity.Path
|
|
|
|
if req.Name != nil {
|
|
entity.Name = *req.Name
|
|
}
|
|
if req.Code != nil {
|
|
entity.Code = *req.Code
|
|
}
|
|
|
|
// Rebuild path if parent is being changed or code is being changed
|
|
if req.ParentID != nil || req.Code != nil {
|
|
// Determine the code to use (new code if provided, otherwise existing)
|
|
code := entity.Code
|
|
if req.Code != nil {
|
|
code = *req.Code
|
|
}
|
|
|
|
// Build the new path based on parent
|
|
var path string
|
|
if req.ParentID != nil {
|
|
if *req.ParentID == uuid.Nil {
|
|
// Moving to root level
|
|
path = code
|
|
} else {
|
|
// Get parent department to build the path
|
|
parent, err := s.departmentRepo.GetByID(ctx, *req.ParentID)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
// Build path as parent.path + code
|
|
path = parent.Path + "." + code
|
|
}
|
|
} else if req.Code != nil {
|
|
// Code changed but parent not specified, rebuild path with current parent
|
|
// Extract parent path from current path
|
|
parts := strings.Split(entity.Path, ".")
|
|
if len(parts) > 1 {
|
|
// Has parent, rebuild with new code
|
|
parentPath := strings.Join(parts[:len(parts)-1], ".")
|
|
path = parentPath + "." + code
|
|
} else {
|
|
// Root level, just use new code
|
|
path = code
|
|
}
|
|
}
|
|
|
|
if path != "" {
|
|
entity.Path = path
|
|
}
|
|
}
|
|
|
|
// Update the department
|
|
if err := s.departmentRepo.Update(ctx, entity); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// If the path changed, update all children paths
|
|
if oldPath != entity.Path {
|
|
if err := s.departmentRepo.UpdateChildrenPaths(ctx, oldPath, entity.Path); err != nil {
|
|
// Log the error but don't fail the operation
|
|
// You might want to handle this differently based on your requirements
|
|
// For now, we'll continue since the parent update succeeded
|
|
}
|
|
}
|
|
|
|
// Derive parent_id and parent_name from path for response
|
|
var parentID *uuid.UUID
|
|
var parentName *string
|
|
if req.ParentID != nil {
|
|
parentID = req.ParentID
|
|
// Get parent name
|
|
if parent, err := s.departmentRepo.GetByID(ctx, *req.ParentID); err == nil {
|
|
parentName = &parent.Name
|
|
}
|
|
} else {
|
|
// Derive from path if not provided in request
|
|
parts := strings.Split(entity.Path, ".")
|
|
if len(parts) > 1 {
|
|
parentPath := strings.Join(parts[:len(parts)-1], ".")
|
|
if parent, err := s.departmentRepo.GetByPath(ctx, parentPath); err == nil {
|
|
parentID = &parent.ID
|
|
parentName = &parent.Name
|
|
}
|
|
}
|
|
}
|
|
|
|
return &contract.GetDepartmentResponse{
|
|
ID: entity.ID,
|
|
Name: entity.Name,
|
|
Code: entity.Code,
|
|
Path: entity.Path,
|
|
ParentID: parentID,
|
|
ParentName: parentName,
|
|
CreatedAt: entity.CreatedAt,
|
|
UpdatedAt: entity.UpdatedAt,
|
|
}, nil
|
|
}
|
|
|
|
func (s *MasterServiceImpl) DeleteDepartment(ctx context.Context, id uuid.UUID) error {
|
|
return s.departmentRepo.Delete(ctx, id)
|
|
}
|
|
|
|
func (s *MasterServiceImpl) GetOrganizationalChartByID(ctx context.Context, departmentID uuid.UUID) (*contract.OrganizationalChartResponse, error) {
|
|
// First get the department to find its path
|
|
department, err := s.departmentRepo.Get(ctx, departmentID)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// Now get the organizational chart starting from this department's path
|
|
return s.GetOrganizationalChart(ctx, department.Path)
|
|
}
|
|
|
|
func (s *MasterServiceImpl) GetOrganizationalChart(ctx context.Context, rootPath string) (*contract.OrganizationalChartResponse, error) {
|
|
var departments []entities.Department
|
|
var err error
|
|
|
|
// Get config values
|
|
parentPath := s.config.Department.ParentPath
|
|
excludedPaths := s.config.Department.ExcludedPaths
|
|
|
|
if rootPath == "" {
|
|
// Get all departments with parent filter
|
|
departments, err = s.departmentRepo.GetAllWithParentFilter(ctx, parentPath, excludedPaths)
|
|
} else {
|
|
// Get departments under specific path
|
|
departments, err = s.departmentRepo.GetByPathPrefix(ctx, rootPath)
|
|
// Filter out excluded paths manually for specific path queries
|
|
filteredDepts := make([]entities.Department, 0)
|
|
for _, dept := range departments {
|
|
excluded := false
|
|
for _, excludedPath := range excludedPaths {
|
|
if strings.Contains(dept.Path, excludedPath) {
|
|
excluded = true
|
|
break
|
|
}
|
|
}
|
|
if !excluded {
|
|
filteredDepts = append(filteredDepts, dept)
|
|
}
|
|
}
|
|
departments = filteredDepts
|
|
}
|
|
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// Build the tree structure
|
|
nodeMap := make(map[string]*contract.DepartmentNode)
|
|
roots := make([]*contract.DepartmentNode, 0)
|
|
|
|
// Calculate base level offset based on parent path
|
|
baseLevelOffset := 0
|
|
if parentPath != "" {
|
|
baseLevelOffset = len(strings.Split(parentPath, ".")) - 1
|
|
}
|
|
|
|
// First pass: create all nodes including missing parents
|
|
for _, dept := range departments {
|
|
pathParts := strings.Split(dept.Path, ".")
|
|
|
|
// Create any missing parent nodes
|
|
for i := 1; i <= len(pathParts); i++ {
|
|
currentPath := strings.Join(pathParts[:i], ".")
|
|
if _, exists := nodeMap[currentPath]; !exists {
|
|
// Calculate level for this path
|
|
adjustedLevel := i - baseLevelOffset
|
|
if adjustedLevel < 1 {
|
|
adjustedLevel = 1
|
|
}
|
|
|
|
// Create node (placeholder for missing parents, real data for existing)
|
|
var node *contract.DepartmentNode
|
|
if currentPath == dept.Path {
|
|
// This is the actual department
|
|
node = &contract.DepartmentNode{
|
|
ID: dept.ID,
|
|
Name: dept.Name,
|
|
Code: dept.Code,
|
|
Path: dept.Path,
|
|
Level: adjustedLevel,
|
|
Children: make([]*contract.DepartmentNode, 0),
|
|
}
|
|
} else {
|
|
// This is a missing parent - create placeholder
|
|
// Extract the last segment as the name
|
|
lastSegment := pathParts[i-1]
|
|
node = &contract.DepartmentNode{
|
|
ID: uuid.Nil, // Use nil UUID for placeholder
|
|
Name: strings.ToUpper(strings.ReplaceAll(lastSegment, "_", " ")),
|
|
Code: lastSegment,
|
|
Path: currentPath,
|
|
Level: adjustedLevel,
|
|
Children: make([]*contract.DepartmentNode, 0),
|
|
}
|
|
}
|
|
nodeMap[currentPath] = node
|
|
}
|
|
}
|
|
}
|
|
|
|
// Second pass: build the tree relationships
|
|
// Only process nodes that actually exist in the database (not placeholders)
|
|
processedPaths := make(map[string]bool)
|
|
for _, dept := range departments {
|
|
if processedPaths[dept.Path] {
|
|
continue
|
|
}
|
|
processedPaths[dept.Path] = true
|
|
|
|
node := nodeMap[dept.Path]
|
|
pathParts := strings.Split(dept.Path, ".")
|
|
|
|
// Check if this should be a root node
|
|
isRoot := false
|
|
if rootPath != "" && dept.Path == rootPath {
|
|
// Explicitly requested root
|
|
isRoot = true
|
|
} else if rootPath == "" && parentPath != "" && dept.Path == parentPath {
|
|
// The configured parent path is the root when showing all
|
|
isRoot = true
|
|
} else if len(pathParts) == 1 {
|
|
// Single segment path
|
|
isRoot = true
|
|
} else {
|
|
// Find parent path
|
|
parentPathStr := strings.Join(pathParts[:len(pathParts)-1], ".")
|
|
if parent, exists := nodeMap[parentPathStr]; exists {
|
|
// Check if this child is already added
|
|
alreadyAdded := false
|
|
for _, child := range parent.Children {
|
|
if child.Path == node.Path {
|
|
alreadyAdded = true
|
|
break
|
|
}
|
|
}
|
|
if !alreadyAdded {
|
|
parent.Children = append(parent.Children, node)
|
|
}
|
|
} else {
|
|
// Parent doesn't exist - this is an orphaned node
|
|
// Only include it as a root if it's a direct child of the parent path
|
|
if parentPath != "" {
|
|
// Check if this is a direct child of the configured parent
|
|
expectedParent := parentPath
|
|
actualParent := strings.Join(pathParts[:len(pathParts)-1], ".")
|
|
if actualParent != expectedParent {
|
|
// This is an orphaned node - skip it
|
|
continue
|
|
}
|
|
}
|
|
isRoot = true
|
|
}
|
|
}
|
|
|
|
if isRoot {
|
|
// Check for duplicates in roots
|
|
alreadyInRoots := false
|
|
for _, r := range roots {
|
|
if r.Path == node.Path {
|
|
alreadyInRoots = true
|
|
break
|
|
}
|
|
}
|
|
if !alreadyInRoots {
|
|
roots = append(roots, node)
|
|
}
|
|
}
|
|
}
|
|
|
|
// Sort children at each level
|
|
var sortChildren func([]*contract.DepartmentNode)
|
|
sortChildren = func(nodes []*contract.DepartmentNode) {
|
|
for _, node := range nodes {
|
|
if len(node.Children) > 0 {
|
|
// Sort children by name
|
|
sort.Slice(node.Children, func(i, j int) bool {
|
|
return node.Children[i].Name < node.Children[j].Name
|
|
})
|
|
sortChildren(node.Children)
|
|
}
|
|
}
|
|
}
|
|
|
|
// Sort root nodes
|
|
sort.Slice(roots, func(i, j int) bool {
|
|
return roots[i].Name < roots[j].Name
|
|
})
|
|
sortChildren(roots)
|
|
|
|
return &contract.OrganizationalChartResponse{
|
|
Chart: roots,
|
|
TotalNodes: len(departments),
|
|
}, nil
|
|
}
|
|
|
|
func (s *MasterServiceImpl) ListDepartments(ctx context.Context, req *contract.ListDepartmentsRequest) (*contract.ListDepartmentsResponse, error) {
|
|
// Set default values if not provided
|
|
page := req.Page
|
|
if page < 1 {
|
|
page = 1
|
|
}
|
|
|
|
limit := req.Limit
|
|
if limit < 1 {
|
|
limit = 10
|
|
}
|
|
if limit > 100 {
|
|
limit = 100 // Max limit to prevent performance issues
|
|
}
|
|
|
|
offset := (page - 1) * limit
|
|
|
|
// Use filtered list with parent path from config
|
|
parentPath := s.config.Department.ParentPath
|
|
excludedPaths := s.config.Department.ExcludedPaths
|
|
|
|
list, total, err := s.departmentRepo.ListWithParentFilter(ctx, req.Search, limit, offset, parentPath, excludedPaths)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return &contract.ListDepartmentsResponse{
|
|
Departments: transformer.DepartmentsToContract(list),
|
|
Total: total,
|
|
Page: page,
|
|
Limit: limit,
|
|
}, nil
|
|
}
|