package processor import ( "context" "time" "eslogad-be/internal/entities" "eslogad-be/internal/repository" "github.com/google/uuid" ) type LetterApprovalProcessor interface { CreateApprovalSteps(ctx context.Context, letter *entities.LetterOutgoing) error ProcessApprovalAction(ctx context.Context, letterID uuid.UUID, approval *entities.LetterOutgoingApproval, userID uuid.UUID, isApproval bool) error UpdateApprovalStatus(ctx context.Context, approval *entities.LetterOutgoingApproval, status entities.ApprovalStatus, userID uuid.UUID) error CheckAllApprovalsComplete(ctx context.Context, letterID uuid.UUID) (bool, error) } type LetterApprovalProcessorImpl struct { approvalRepo *repository.LetterOutgoingApprovalRepository approvalFlowRepo *repository.ApprovalFlowRepository letterRepo *repository.LetterOutgoingRepository } func NewLetterApprovalProcessor( approvalRepo *repository.LetterOutgoingApprovalRepository, approvalFlowRepo *repository.ApprovalFlowRepository, letterRepo *repository.LetterOutgoingRepository, ) *LetterApprovalProcessorImpl { return &LetterApprovalProcessorImpl{ approvalRepo: approvalRepo, approvalFlowRepo: approvalFlowRepo, letterRepo: letterRepo, } } func (p *LetterApprovalProcessorImpl) CreateApprovalSteps(ctx context.Context, letter *entities.LetterOutgoing) error { if letter.ApprovalFlowID == nil { return nil } flow, err := p.approvalFlowRepo.Get(ctx, *letter.ApprovalFlowID) if err != nil || flow == nil || len(flow.Steps) == 0 { return err } // Find the minimum step order (first step) minStepOrder := flow.Steps[0].StepOrder for _, step := range flow.Steps { if step.StepOrder < minStepOrder { minStepOrder = step.StepOrder } } // Create approval records for each step for _, step := range flow.Steps { approval := entities.LetterOutgoingApproval{ LetterID: letter.ID, StepID: step.ID, StepOrder: step.StepOrder, ParallelGroup: step.ParallelGroup, IsRequired: step.Required, ApproverID: step.ApproverUserID, } // Set initial status if step.StepOrder == minStepOrder { approval.Status = entities.ApprovalStatusPending } else { approval.Status = entities.ApprovalStatusNotStarted } if err := p.approvalRepo.Create(ctx, &approval); err != nil { return err } } return nil } func (p *LetterApprovalProcessorImpl) ProcessApprovalAction(ctx context.Context, letterID uuid.UUID, approval *entities.LetterOutgoingApproval, userID uuid.UUID, isApproval bool) error { status := entities.ApprovalStatusApproved if !isApproval { status = entities.ApprovalStatusRejected } return p.UpdateApprovalStatus(ctx, approval, status, userID) } func (p *LetterApprovalProcessorImpl) UpdateApprovalStatus(ctx context.Context, approval *entities.LetterOutgoingApproval, status entities.ApprovalStatus, userID uuid.UUID) error { approval.Status = status approval.ApproverID = &userID now := time.Now() approval.ActedAt = &now return p.approvalRepo.Update(ctx, approval) } func (p *LetterApprovalProcessorImpl) CheckAllApprovalsComplete(ctx context.Context, letterID uuid.UUID) (bool, error) { approvals, err := p.approvalRepo.ListByLetter(ctx, letterID) if err != nil { return false, err } for _, approval := range approvals { if approval.IsRequired && approval.Status != entities.ApprovalStatusApproved { return false, nil } } return true, nil }