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

252 lines
8.0 KiB
Go

package processor
import (
"context"
"fmt"
"time"
"eslogad-be/internal/appcontext"
"eslogad-be/internal/contract"
"eslogad-be/internal/entities"
"eslogad-be/internal/transformer"
"github.com/google/uuid"
)
func (p *LetterProcessorImpl) GetDepartmentDispositionStatus(ctx context.Context, req *contract.GetDepartmentDispositionStatusRequest) (*contract.ListDepartmentDispositionStatusResponse, error) {
dispositions, err := p.dispositionDeptRepo.GetByLetterIncomingID(ctx, req.LetterIncomingID)
if err != nil {
return nil, err
}
var response []contract.DepartmentDispositionStatusResponse
for _, disp := range dispositions {
letterResp := transformer.LetterIncomingEntityToContract(disp.LetterIncoming)
var fromDept *contract.DepartmentResponse
if disp.LetterIncomingDisposition != nil && disp.LetterIncomingDisposition.DepartmentID != nil {
fromDept = transformer.DepartmentEntityToContract(&disp.LetterIncomingDisposition.Department)
}
response = append(response, contract.DepartmentDispositionStatusResponse{
ID: disp.ID,
LetterID: disp.LetterIncomingID,
Letter: letterResp,
FromDepartmentID: disp.LetterIncomingDisposition.DepartmentID,
FromDepartment: fromDept,
ToDepartmentID: disp.DepartmentID,
ToDepartment: transformer.DepartmentEntityToContract(disp.Department),
Status: string(disp.Status),
Notes: disp.LetterIncomingDisposition.Notes,
ReadAt: disp.ReadAt,
CompletedAt: disp.CompletedAt,
CreatedAt: disp.CreatedAt,
UpdatedAt: disp.UpdatedAt,
})
}
return &contract.ListDepartmentDispositionStatusResponse{
Dispositions: response,
Pagination: contract.PaginationResponse{
TotalCount: len(response),
Page: 1,
Limit: len(response),
TotalPages: 1,
},
}, nil
}
func (p *LetterProcessorImpl) UpdateDispositionStatus(ctx context.Context, req *contract.UpdateDispositionStatusRequest) (*contract.DepartmentDispositionStatusResponse, error) {
var result *contract.DepartmentDispositionStatusResponse
err := p.txManager.WithTransaction(ctx, func(txCtx context.Context) error {
userID := appcontext.FromGinContext(txCtx).UserID
departmentID := appcontext.FromGinContext(txCtx).DepartmentID
dispDept, err := p.dispositionDeptRepo.GetByDispositionAndDepartment(txCtx, req.LetterIncomingID, departmentID)
if err != nil {
return err
}
notes := ""
if req.Notes != nil {
notes = *req.Notes
}
if err := p.updateDispositionDepartmentStatus(txCtx, dispDept.ID, req.Status, notes); err != nil {
return err
}
p.activity.LogLetterDispositionStatusUpdate(txCtx, req.LetterIncomingID, userID, req.Status)
if err := p.checkAndUpdateLetterCompletionStatus(txCtx, req.LetterIncomingID); err != nil {
return err
}
updatedDispDept, err := p.dispositionDeptRepo.GetByID(txCtx, dispDept.ID)
if err != nil {
return err
}
result = p.buildDispositionStatusResponse(updatedDispDept)
return nil
})
if err != nil {
return nil, err
}
return result, nil
}
// updateDispositionDepartmentStatus updates the status of a disposition department
func (p *LetterProcessorImpl) updateDispositionDepartmentStatus(ctx context.Context, dispDeptID uuid.UUID, status, notes string) error {
now := time.Now()
var dispositionStatus entities.LetterIncomingDispositionDepartmentStatus
var readAt, completedAt *time.Time
switch status {
case "completed":
dispositionStatus = entities.DispositionDepartmentStatusCompleted
completedAt = &now
readAt = &now // Mark as read when completing
case "read":
dispositionStatus = entities.DispositionDepartmentStatusRead
readAt = &now
default:
dispositionStatus = entities.DispositionDepartmentStatusPending
}
return p.dispositionDeptRepo.UpdateStatus(ctx, dispDeptID, dispositionStatus, notes, readAt, completedAt)
}
// addDispositionNoteIfProvided adds a note to the disposition if provided
func (p *LetterProcessorImpl) addDispositionNoteIfProvided(ctx context.Context, dispositionID uuid.UUID, userID uuid.UUID, notes *string) error {
if notes == nil || *notes == "" {
return nil
}
note := &entities.DispositionNote{
DispositionID: dispositionID,
UserID: &userID,
Note: *notes,
}
return p.dispositionNoteRepo.Create(ctx, note)
}
func (p *LetterProcessorImpl) checkAndUpdateLetterCompletionStatus(ctx context.Context, letterIncomingID uuid.UUID) error {
dispositions, err := p.dispositionDeptRepo.GetByLetterIncomingID(ctx, letterIncomingID)
if err != nil {
return err
}
allCompleted := true
for _, disp := range dispositions {
if disp.Status == entities.DispositionDepartmentStatusPending {
allCompleted = false
break
}
}
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
}
// buildDispositionStatusResponse builds the response for disposition status
func (p *LetterProcessorImpl) buildDispositionStatusResponse(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,
}
}
func (p *LetterProcessorImpl) GetLetterCTA(ctx context.Context, letterIncomingID uuid.UUID, departmentID uuid.UUID) (*contract.LetterCTAResponse, error) {
letter, err := p.letterRepo.GetByID(ctx, letterIncomingID)
if err != nil {
return nil, err
}
response := &contract.LetterCTAResponse{
LetterIncomingID: letterIncomingID,
Actions: []contract.LetterCTAAction{},
Message: "",
}
isEligibleForDispo, err := p.dispoRoutes.IsEligibleForDisposition(ctx, departmentID)
if err != nil {
return nil, err
}
if letter.Status == "completed" || letter.Status == "archived" {
response.Message = "Letter is no longer accepting actions"
return response, nil
}
dispDepts, err := p.dispositionDeptRepo.GetByLetterAndDepartment(ctx, letterIncomingID, departmentID)
if err != nil {
return nil, err
}
if len(dispDepts) == 0 {
response.Message = "Your department is not a recipient of this letter"
return response, nil
}
for _, dispDept := range dispDepts {
if dispDept.Status == entities.DispositionDepartmentStatusPending {
response.DispositionID = &dispDept.LetterIncomingDispositionID
currentStatus := string(dispDept.Status)
response.CurrentStatus = &currentStatus
if isEligibleForDispo {
response.Actions = append(response.Actions, contract.LetterCTAAction{
Type: "create_disposition",
Label: "Disposisi",
Path: fmt.Sprintf("/api/v1/letters/%s/dispositions", letterIncomingID),
Method: "POST",
Description: "Create a new disposition for this letter",
})
}
response.Actions = append(response.Actions, contract.LetterCTAAction{
Type: "update_status",
Label: "Tindak Lanjut",
Path: fmt.Sprintf("/api/v1/letters/dispositions/%s/status", response.LetterIncomingID),
Method: "PUT",
Description: "Update the status of your disposition",
})
}
}
return response, nil
}