package processor import ( "context" "time" "eslogad-be/internal/contract" "eslogad-be/internal/entities" "eslogad-be/internal/repository" "eslogad-be/internal/transformer" "github.com/google/uuid" ) type LetterDispositionDepartmentProcessor interface { GetByLetterIncomingID(ctx context.Context, letterIncomingID uuid.UUID) ([]entities.LetterIncomingDispositionDepartment, error) GetDepartmentDispositionStatus(ctx context.Context, letterIncomingID uuid.UUID) (*contract.ListDepartmentDispositionStatusResponse, error) UpdateDispositionStatus(ctx context.Context, letterIncomingID uuid.UUID, departmentID uuid.UUID, userID uuid.UUID, req *contract.UpdateDispositionStatusRequest) (*contract.DepartmentDispositionStatusResponse, error) CheckAndUpdateLetterCompletionStatus(ctx context.Context, letterIncomingID uuid.UUID) error } type LetterDispositionDepartmentProcessorImpl struct { dispositionDeptRepo *repository.LetterIncomingDispositionDepartmentRepository dispositionNoteRepo *repository.DispositionNoteRepository letterRepo *repository.LetterIncomingRepository } func NewLetterDispositionDepartmentProcessor( dispositionDeptRepo *repository.LetterIncomingDispositionDepartmentRepository, dispositionNoteRepo *repository.DispositionNoteRepository, letterRepo *repository.LetterIncomingRepository, ) *LetterDispositionDepartmentProcessorImpl { return &LetterDispositionDepartmentProcessorImpl{ dispositionDeptRepo: dispositionDeptRepo, dispositionNoteRepo: dispositionNoteRepo, letterRepo: letterRepo, } } // GetByLetterIncomingID retrieves all disposition departments for a letter func (p *LetterDispositionDepartmentProcessorImpl) GetByLetterIncomingID(ctx context.Context, letterIncomingID uuid.UUID) ([]entities.LetterIncomingDispositionDepartment, error) { return p.dispositionDeptRepo.GetByLetterIncomingID(ctx, letterIncomingID) } // GetDepartmentDispositionStatus retrieves disposition status for a specific letter func (p *LetterDispositionDepartmentProcessorImpl) GetDepartmentDispositionStatus(ctx context.Context, letterIncomingID uuid.UUID) (*contract.ListDepartmentDispositionStatusResponse, error) { dispositions, err := p.dispositionDeptRepo.GetByLetterIncomingID(ctx, letterIncomingID) if err != nil { return nil, err } response := p.buildDispositionStatusResponse(dispositions) return response, nil } func (p *LetterDispositionDepartmentProcessorImpl) UpdateDispositionStatus(ctx context.Context, letterIncomingID uuid.UUID, departmentID uuid.UUID, userID uuid.UUID, req *contract.UpdateDispositionStatusRequest) (*contract.DepartmentDispositionStatusResponse, error) { dispDept, err := p.dispositionDeptRepo.GetByDispositionAndDepartment(ctx, letterIncomingID, departmentID) if err != nil { return nil, err } now := time.Now() var dispositionStatus entities.LetterIncomingDispositionDepartmentStatus var readAt, completedAt *time.Time switch req.Status { case "completed": dispositionStatus = entities.DispositionDepartmentStatusCompleted completedAt = &now readAt = &now // Mark as read when completing case "read": dispositionStatus = entities.DispositionDepartmentStatusRead readAt = &now case "dispositioned": dispositionStatus = entities.DispositionDepartmentStatusDispositioned default: dispositionStatus = entities.DispositionDepartmentStatusPending } // Extract notes for the update notes := "" if req.Notes != nil && *req.Notes != "" { notes = *req.Notes } if err := p.dispositionDeptRepo.UpdateStatus(ctx, dispDept.ID, dispositionStatus, notes, readAt, completedAt); err != nil { return nil, err } // Check and update letter completion status if err := p.CheckAndUpdateLetterCompletionStatus(ctx, letterIncomingID); err != nil { // Log error but don't fail the status update } // Get updated record for response updatedDispDept, err := p.dispositionDeptRepo.GetByID(ctx, dispDept.ID) if err != nil { return nil, err } return p.buildSingleDispositionStatusResponse(updatedDispDept), nil } // CheckAndUpdateLetterCompletionStatus checks if all dispositions are completed and updates letter status func (p *LetterDispositionDepartmentProcessorImpl) CheckAndUpdateLetterCompletionStatus(ctx context.Context, letterIncomingID uuid.UUID) error { // Get all disposition departments for this letter dispositions, err := p.dispositionDeptRepo.GetByLetterIncomingID(ctx, letterIncomingID) if err != nil { return err } // Check if all dispositions are completed allCompleted := true for _, disp := range dispositions { if disp.Status == entities.DispositionDepartmentStatusPending { allCompleted = false break } } // If all dispositions are completed, update the letter status to completed if allCompleted && len(dispositions) > 0 { letter, err := p.letterRepo.GetByID(ctx, letterIncomingID) if err != nil { return err } letter.Status = "completed" if err := p.letterRepo.Update(ctx, letter); err != nil { return err } } return nil } // Helper methods func (p *LetterDispositionDepartmentProcessorImpl) buildDispositionStatusResponse(dispositions []entities.LetterIncomingDispositionDepartment) *contract.ListDepartmentDispositionStatusResponse { var response []contract.DepartmentDispositionStatusResponse for _, disp := range dispositions { response = append(response, *p.buildSingleDispositionStatusResponse(&disp)) } return &contract.ListDepartmentDispositionStatusResponse{ Dispositions: response, Pagination: contract.PaginationResponse{ TotalCount: len(response), Page: 1, Limit: len(response), TotalPages: 1, }, } } func (p *LetterDispositionDepartmentProcessorImpl) buildSingleDispositionStatusResponse(dispDept *entities.LetterIncomingDispositionDepartment) *contract.DepartmentDispositionStatusResponse { letterResp := transformer.LetterIncomingEntityToContract(dispDept.LetterIncoming) var fromDept *contract.DepartmentResponse if dispDept.LetterIncomingDisposition != nil && dispDept.LetterIncomingDisposition.DepartmentID != nil { fromDept = transformer.DepartmentEntityToContract(&dispDept.LetterIncomingDisposition.Department) } return &contract.DepartmentDispositionStatusResponse{ ID: dispDept.ID, LetterID: dispDept.LetterIncomingID, Letter: letterResp, FromDepartmentID: dispDept.LetterIncomingDisposition.DepartmentID, FromDepartment: fromDept, ToDepartmentID: dispDept.DepartmentID, ToDepartment: transformer.DepartmentEntityToContract(dispDept.Department), Status: string(dispDept.Status), Notes: dispDept.LetterIncomingDisposition.Notes, ReadAt: dispDept.ReadAt, CompletedAt: dispDept.CompletedAt, CreatedAt: dispDept.CreatedAt, UpdatedAt: dispDept.UpdatedAt, } }