This commit is contained in:
Aditya Siregar 2025-10-20 00:32:53 +07:00
parent 7463a86299
commit 92aba06d67
2 changed files with 86 additions and 24 deletions

View File

@ -573,12 +573,47 @@ func (p *LetterOutgoingProcessorImpl) getApprovalsByStepForRevision(ctx context.
} }
// isStepCompleted checks if all required approvals in a step are approved // 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 { func (p *LetterOutgoingProcessorImpl) isStepCompleted(stepApprovals []entities.LetterOutgoingApproval) bool {
// Group approvals by parallel group
approvalsByParallelGroup := make(map[int][]entities.LetterOutgoingApproval)
for _, approval := range stepApprovals { 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 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 return true
} }

View File

@ -997,43 +997,70 @@ func (s *LetterOutgoingServiceImpl) GetLetterApprovalInfo(ctx context.Context, l
return nil, err return nil, err
} }
// Group approvals by step order to understand the workflow // Group approvals by step order and parallel group to understand the workflow
approvalsByStep := make(map[int][]entities.LetterOutgoingApproval) approvalsByStepAndGroup := make(map[int]map[int][]entities.LetterOutgoingApproval)
for _, approval := range approvals { 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) // Find the current active step (lowest step order with pending approvals)
var currentStepOrder int = -1 var currentStepOrder int = -1
var userApproval *entities.LetterOutgoingApproval var userApproval *entities.LetterOutgoingApproval
var isApproverOnActiveStep bool
var canApprove bool var canApprove bool
// Find the minimum step order that has pending approvals // Find the minimum step order that has pending approvals
for stepOrder, stepApprovals := range approvalsByStep { for stepOrder, groupApprovals := range approvalsByStepAndGroup {
hasPending := false stepHasPending := false
for _, approval := range stepApprovals {
if approval.Status == entities.ApprovalStatusPending { // Check each parallel group in this step
hasPending = true for _, groupMembers := range groupApprovals {
// Check if this user is an approver for this pending approval groupHasPending := false
if approval.ApproverID != nil && *approval.ApproverID == userID { var userApprovalInGroup *entities.LetterOutgoingApproval
if currentStepOrder == -1 || stepOrder < currentStepOrder {
currentStepOrder = stepOrder // Check if this group has pending approvals and if user is in this group
userApproval = &approval for i := range groupMembers {
isApproverOnActiveStep = true 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
} }
} }
} }
}
// Track the lowest pending step
if hasPending && (currentStepOrder == -1 || stepOrder < currentStepOrder) {
currentStepOrder = stepOrder
}
}
// User can approve if they have a pending approval on the current active step // If this is the earliest step with pending approvals and user is in a pending group
if isApproverOnActiveStep && userApproval != nil && userApproval.Status == entities.ApprovalStatusPending { if groupHasPending && userApprovalInGroup != nil {
canApprove = true 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 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
}
}
} }
// Build actions based on eligibility // Build actions based on eligibility
@ -1095,7 +1122,7 @@ func (s *LetterOutgoingServiceImpl) GetLetterApprovalInfo(ctx context.Context, l
} }
info := &contract.LetterApprovalInfoResponse{ info := &contract.LetterApprovalInfoResponse{
IsApproverOnActiveStep: isApproverOnActiveStep, IsApproverOnActiveStep: canApprove, // User can approve means they're on the active step
DecisionStatus: decisionStatus, DecisionStatus: decisionStatus,
CanApprove: canApprove, CanApprove: canApprove,
Actions: actions, Actions: actions,