diff --git a/internal/processor/letter_outgoing_processor.go b/internal/processor/letter_outgoing_processor.go index 1104869..20bc03a 100644 --- a/internal/processor/letter_outgoing_processor.go +++ b/internal/processor/letter_outgoing_processor.go @@ -573,12 +573,47 @@ func (p *LetterOutgoingProcessorImpl) getApprovalsByStepForRevision(ctx context. } // isStepCompleted checks if all required approvals in a step are approved +// For parallel groups, at least one approval per group must be completed func (p *LetterOutgoingProcessorImpl) isStepCompleted(stepApprovals []entities.LetterOutgoingApproval) bool { + // Group approvals by parallel group + approvalsByParallelGroup := make(map[int][]entities.LetterOutgoingApproval) for _, approval := range stepApprovals { - if approval.IsRequired && approval.Status != entities.ApprovalStatusApproved { + approvalsByParallelGroup[approval.ParallelGroup] = append( + approvalsByParallelGroup[approval.ParallelGroup], + approval, + ) + } + + // Check each parallel group + for _, groupApprovals := range approvalsByParallelGroup { + // For each parallel group, check if it has at least one approved + groupHasApproval := false + hasRequiredPending := false + + for _, approval := range groupApprovals { + if approval.Status == entities.ApprovalStatusApproved { + groupHasApproval = true + } + if approval.IsRequired && approval.Status != entities.ApprovalStatusApproved { + hasRequiredPending = true + } + } + + // If this group has required approvals that are still pending, step is not complete + if hasRequiredPending { return false } + + // If this group has no approvals at all and contains required approvals, step is not complete + if !groupHasApproval { + for _, approval := range groupApprovals { + if approval.IsRequired { + return false + } + } + } } + return true } diff --git a/internal/service/letter_outgoing_service.go b/internal/service/letter_outgoing_service.go index 1091115..8cefb1f 100644 --- a/internal/service/letter_outgoing_service.go +++ b/internal/service/letter_outgoing_service.go @@ -997,45 +997,72 @@ func (s *LetterOutgoingServiceImpl) GetLetterApprovalInfo(ctx context.Context, l return nil, err } - // Group approvals by step order to understand the workflow - approvalsByStep := make(map[int][]entities.LetterOutgoingApproval) + // Group approvals by step order and parallel group to understand the workflow + approvalsByStepAndGroup := make(map[int]map[int][]entities.LetterOutgoingApproval) for _, approval := range approvals { - approvalsByStep[approval.StepOrder] = append(approvalsByStep[approval.StepOrder], approval) + if approvalsByStepAndGroup[approval.StepOrder] == nil { + approvalsByStepAndGroup[approval.StepOrder] = make(map[int][]entities.LetterOutgoingApproval) + } + approvalsByStepAndGroup[approval.StepOrder][approval.ParallelGroup] = append( + approvalsByStepAndGroup[approval.StepOrder][approval.ParallelGroup], + approval, + ) } // Find the current active step (lowest step order with pending approvals) var currentStepOrder int = -1 var userApproval *entities.LetterOutgoingApproval - var isApproverOnActiveStep bool var canApprove bool // Find the minimum step order that has pending approvals - for stepOrder, stepApprovals := range approvalsByStep { - hasPending := false - for _, approval := range stepApprovals { - if approval.Status == entities.ApprovalStatusPending { - hasPending = true - // Check if this user is an approver for this pending approval - if approval.ApproverID != nil && *approval.ApproverID == userID { - if currentStepOrder == -1 || stepOrder < currentStepOrder { - currentStepOrder = stepOrder - userApproval = &approval - isApproverOnActiveStep = true + for stepOrder, groupApprovals := range approvalsByStepAndGroup { + stepHasPending := false + + // Check each parallel group in this step + for _, groupMembers := range groupApprovals { + groupHasPending := false + var userApprovalInGroup *entities.LetterOutgoingApproval + + // Check if this group has pending approvals and if user is in this group + for i := range groupMembers { + approval := groupMembers[i] + if approval.Status == entities.ApprovalStatusPending { + groupHasPending = true + stepHasPending = true + + // Check if this user is an approver in this group + if approval.ApproverID != nil && *approval.ApproverID == userID { + userApprovalInGroup = &approval } } } + + // If this is the earliest step with pending approvals and user is in a pending group + if groupHasPending && userApprovalInGroup != nil { + if currentStepOrder == -1 || stepOrder < currentStepOrder { + currentStepOrder = stepOrder + userApproval = userApprovalInGroup + // User can approve if they're in a parallel group with pending approvals at the current active step + canApprove = true + } else if stepOrder == currentStepOrder { + // Same step order - user can still approve if in parallel group + userApproval = userApprovalInGroup + canApprove = true + } + } } - // Track the lowest pending step - if hasPending && (currentStepOrder == -1 || stepOrder < currentStepOrder) { + + // Track the lowest step with pending approvals + if stepHasPending && (currentStepOrder == -1 || stepOrder < currentStepOrder) { currentStepOrder = stepOrder + // Reset canApprove if we found a lower step and user is not in it + if userApproval == nil || userApproval.StepOrder != stepOrder { + canApprove = false + userApproval = nil + } } } - // User can approve if they have a pending approval on the current active step - if isApproverOnActiveStep && userApproval != nil && userApproval.Status == entities.ApprovalStatusPending { - canApprove = true - } - // Build actions based on eligibility var actions []contract.ApprovalAction if canApprove && userApproval != nil { @@ -1095,7 +1122,7 @@ func (s *LetterOutgoingServiceImpl) GetLetterApprovalInfo(ctx context.Context, l } info := &contract.LetterApprovalInfoResponse{ - IsApproverOnActiveStep: isApproverOnActiveStep, + IsApproverOnActiveStep: canApprove, // User can approve means they're on the active step DecisionStatus: decisionStatus, CanApprove: canApprove, Actions: actions,