dukcapil/internal/service/disposition_route_service.go
2025-09-08 12:24:37 +07:00

224 lines
6.9 KiB
Go

package service
import (
"context"
"sort"
"eslogad-be/internal/contract"
"eslogad-be/internal/entities"
"eslogad-be/internal/repository"
"eslogad-be/internal/transformer"
"github.com/google/uuid"
)
type DispositionRouteServiceImpl struct {
repo *repository.DispositionRouteRepository
}
func NewDispositionRouteService(repo *repository.DispositionRouteRepository) *DispositionRouteServiceImpl {
return &DispositionRouteServiceImpl{repo: repo}
}
// CreateOrUpdate handles bulk create or update of disposition routes
func (s *DispositionRouteServiceImpl) CreateOrUpdate(ctx context.Context, req *contract.CreateDispositionRouteRequest) (*contract.BulkCreateDispositionRouteResponse, error) {
// Set default values
isActive := true
if req.IsActive != nil {
isActive = *req.IsActive
}
var allowedActions entities.JSONB
if req.AllowedActions != nil {
allowedActions = entities.JSONB(*req.AllowedActions)
}
// Perform bulk upsert
created, updated, err := s.repo.BulkUpsert(ctx, req.FromDepartmentID, req.ToDepartmentIDs, isActive, allowedActions)
if err != nil {
return nil, err
}
// Fetch all routes for the from_department_id to return
routes, err := s.repo.ListByFromDept(ctx, req.FromDepartmentID)
if err != nil {
return nil, err
}
// Transform to response
routeResponses := transformer.DispositionRoutesToContract(routes)
return &contract.BulkCreateDispositionRouteResponse{
Created: created,
Updated: updated,
Routes: routeResponses,
}, nil
}
// Create maintains backward compatibility for single route creation
func (s *DispositionRouteServiceImpl) Create(ctx context.Context, req *contract.CreateDispositionRouteRequest) (*contract.DispositionRouteResponse, error) {
// If only one to_department_id is provided, create a single route
if len(req.ToDepartmentIDs) == 1 {
entity := &entities.DispositionRoute{
FromDepartmentID: req.FromDepartmentID,
ToDepartmentID: req.ToDepartmentIDs[0],
}
if req.IsActive != nil {
entity.IsActive = *req.IsActive
} else {
entity.IsActive = true
}
if req.AllowedActions != nil {
entity.AllowedActions = entities.JSONB(*req.AllowedActions)
}
// Use upsert to handle create or update
if err := s.repo.Upsert(ctx, entity); err != nil {
return nil, err
}
// Fetch the created/updated route
route, err := s.repo.Get(ctx, entity.ID)
if err != nil {
// If we can't get by ID (new creation), try to get by from/to combination
routes, err := s.repo.ListByFromDept(ctx, req.FromDepartmentID)
if err != nil {
return nil, err
}
for _, r := range routes {
if r.ToDepartmentID == req.ToDepartmentIDs[0] {
resp := transformer.DispositionRoutesToContract([]entities.DispositionRoute{r})[0]
return &resp, nil
}
}
}
resp := transformer.DispositionRoutesToContract([]entities.DispositionRoute{*route})[0]
return &resp, nil
}
// For multiple to_department_ids, use bulk create/update
bulkResp, err := s.CreateOrUpdate(ctx, req)
if err != nil {
return nil, err
}
// Return the first route as response for backward compatibility
if len(bulkResp.Routes) > 0 {
return &bulkResp.Routes[0], nil
}
return nil, nil
}
func (s *DispositionRouteServiceImpl) Update(ctx context.Context, id uuid.UUID, req *contract.UpdateDispositionRouteRequest) (*contract.DispositionRouteResponse, error) {
entity, err := s.repo.Get(ctx, id)
if err != nil {
return nil, err
}
if req.IsActive != nil {
entity.IsActive = *req.IsActive
}
if req.AllowedActions != nil {
entity.AllowedActions = entities.JSONB(*req.AllowedActions)
}
if err := s.repo.Update(ctx, entity); err != nil {
return nil, err
}
resp := transformer.DispositionRoutesToContract([]entities.DispositionRoute{*entity})[0]
return &resp, nil
}
func (s *DispositionRouteServiceImpl) Get(ctx context.Context, id uuid.UUID) (*contract.DispositionRouteResponse, error) {
entity, err := s.repo.Get(ctx, id)
if err != nil {
return nil, err
}
resp := transformer.DispositionRoutesToContract([]entities.DispositionRoute{*entity})[0]
return &resp, nil
}
func (s *DispositionRouteServiceImpl) ListByFromDept(ctx context.Context, from uuid.UUID) (*contract.ListDispositionRoutesResponse, error) {
list, err := s.repo.ListByFromDept(ctx, from)
if err != nil {
return nil, err
}
return &contract.ListDispositionRoutesResponse{Routes: transformer.DispositionRoutesToContract(list)}, nil
}
func (s *DispositionRouteServiceImpl) SetActive(ctx context.Context, id uuid.UUID, active bool) error {
return s.repo.SetActive(ctx, id, active)
}
// ListGrouped returns all disposition routes grouped by from_department_id with clean department structure
func (s *DispositionRouteServiceImpl) ListGrouped(ctx context.Context) (*contract.ListDispositionRoutesGroupedResponse, error) {
// Get routes with department details
routes, err := s.repo.ListAllGroupedWithDepartments(ctx)
if err != nil {
return nil, err
}
// Group routes by from_department_id and collect department info
type groupedData struct {
fromDept contract.DepartmentMapping
toDepts []contract.DepartmentMapping
toDeptMap map[uuid.UUID]bool // To avoid duplicates
}
grouped := make(map[uuid.UUID]*groupedData)
for _, route := range routes {
if _, exists := grouped[route.FromDepartmentID]; !exists {
grouped[route.FromDepartmentID] = &groupedData{
fromDept: contract.DepartmentMapping{
ID: route.FromDepartmentID,
Name: route.FromDepartment.Name,
},
toDepts: []contract.DepartmentMapping{},
toDeptMap: make(map[uuid.UUID]bool),
}
}
// Add to_department if not already added (avoid duplicates)
if !grouped[route.FromDepartmentID].toDeptMap[route.ToDepartmentID] {
grouped[route.FromDepartmentID].toDepts = append(
grouped[route.FromDepartmentID].toDepts,
contract.DepartmentMapping{
ID: route.ToDepartmentID,
Name: route.ToDepartment.Name,
},
)
grouped[route.FromDepartmentID].toDeptMap[route.ToDepartmentID] = true
}
}
// Convert to response format
var dispositions []contract.DispositionRouteGroupedItem
for _, data := range grouped {
dispositions = append(dispositions, contract.DispositionRouteGroupedItem{
FromDepartment: data.fromDept,
ToDepartments: data.toDepts,
})
}
// Sort by from department name for consistent ordering
sort.Slice(dispositions, func(i, j int) bool {
return dispositions[i].FromDepartment.Name < dispositions[j].FromDepartment.Name
})
return &contract.ListDispositionRoutesGroupedResponse{
Dispositions: dispositions,
}, nil
}
// ListAll returns all disposition routes with department details
func (s *DispositionRouteServiceImpl) ListAll(ctx context.Context) (*contract.ListDispositionRoutesDetailedResponse, error) {
routes, err := s.repo.ListAll(ctx)
if err != nil {
return nil, err
}
routeResponses := transformer.DispositionRoutesToContract(routes)
return &contract.ListDispositionRoutesDetailedResponse{
Routes: routeResponses,
Total: len(routeResponses),
}, nil
}