update get approval by letter

This commit is contained in:
efrilm 2025-10-15 17:26:06 +07:00
parent 58372ca195
commit da2246d45a
3 changed files with 179 additions and 127 deletions

View File

@ -294,6 +294,7 @@ type EnhancedOutgoingLetterApprovalResponse struct {
ParallelGroup int `json:"parallel_group"` ParallelGroup int `json:"parallel_group"`
IsRequired bool `json:"is_required"` IsRequired bool `json:"is_required"`
ApproverID *uuid.UUID `json:"approver_id,omitempty"` ApproverID *uuid.UUID `json:"approver_id,omitempty"`
RevisionNumber int `json:"revision_number"`
Status string `json:"status"` Status string `json:"status"`
Remarks *string `json:"remarks,omitempty"` Remarks *string `json:"remarks,omitempty"`
ActedAt *time.Time `json:"acted_at,omitempty"` ActedAt *time.Time `json:"acted_at,omitempty"`
@ -302,6 +303,11 @@ type EnhancedOutgoingLetterApprovalResponse struct {
Approver *UserResponse `json:"approver,omitempty"` Approver *UserResponse `json:"approver,omitempty"`
} }
type OutgoingLetterApprovalRevisionNumberResponse struct {
RevisionNumber int `json:"revision_number"`
Approvals []EnhancedOutgoingLetterApprovalResponse `json:"approvals"`
}
// GetLetterApprovalsResponse represents the list of approvals for a letter // GetLetterApprovalsResponse represents the list of approvals for a letter
type GetLetterApprovalsResponse struct { type GetLetterApprovalsResponse struct {
LetterID uuid.UUID `json:"letter_id"` LetterID uuid.UUID `json:"letter_id"`
@ -309,7 +315,8 @@ type GetLetterApprovalsResponse struct {
LetterStatus string `json:"letter_status"` LetterStatus string `json:"letter_status"`
TotalSteps int `json:"total_steps"` TotalSteps int `json:"total_steps"`
CurrentStep int `json:"current_step"` CurrentStep int `json:"current_step"`
Approvals []EnhancedOutgoingLetterApprovalResponse `json:"approvals"` CurrentRevisionNumber int `json:"current_revision_number"`
Approvals []OutgoingLetterApprovalRevisionNumberResponse `json:"approvals"`
} }
// OutgoingLetterDiscussionResponse represents a discussion on an outgoing letter // OutgoingLetterDiscussionResponse represents a discussion on an outgoing letter

View File

@ -41,6 +41,7 @@ type LetterOutgoingProcessor interface {
DeleteDiscussion(ctx context.Context, id uuid.UUID) error DeleteDiscussion(ctx context.Context, id uuid.UUID) error
GetApprovalsByLetter(ctx context.Context, letterID uuid.UUID) ([]entities.LetterOutgoingApproval, error) GetApprovalsByLetter(ctx context.Context, letterID uuid.UUID) ([]entities.LetterOutgoingApproval, error)
GetAllApprovalsByLetter(ctx context.Context, letterID uuid.UUID) ([]entities.LetterOutgoingApproval, error)
GetApprovalsByLetterAndRevision(ctx context.Context, letterID uuid.UUID, revisionNumber int) ([]entities.LetterOutgoingApproval, error) GetApprovalsByLetterAndRevision(ctx context.Context, letterID uuid.UUID, revisionNumber int) ([]entities.LetterOutgoingApproval, error)
GetApprovalFlow(ctx context.Context, flowID uuid.UUID) (*entities.ApprovalFlow, error) GetApprovalFlow(ctx context.Context, flowID uuid.UUID) (*entities.ApprovalFlow, error)
@ -989,6 +990,15 @@ func (p *LetterOutgoingProcessorImpl) GetApprovalsByLetter(ctx context.Context,
return p.GetApprovalsByLetterAndRevision(ctx, letterID, letter.RevisionNumber) return p.GetApprovalsByLetterAndRevision(ctx, letterID, letter.RevisionNumber)
} }
func (p *LetterOutgoingProcessorImpl) GetAllApprovalsByLetter(ctx context.Context, letterID uuid.UUID) ([]entities.LetterOutgoingApproval, error) {
approvals, err := p.approvalRepo.ListByLetter(ctx, letterID)
if err != nil {
return nil, err
}
return approvals, nil
}
func (p *LetterOutgoingProcessorImpl) GetApprovalsByLetterAndRevision(ctx context.Context, letterID uuid.UUID, revisionNumber int) ([]entities.LetterOutgoingApproval, error) { func (p *LetterOutgoingProcessorImpl) GetApprovalsByLetterAndRevision(ctx context.Context, letterID uuid.UUID, revisionNumber int) ([]entities.LetterOutgoingApproval, error) {
// Get all approvals for this letter // Get all approvals for this letter
approvals, err := p.approvalRepo.ListByLetter(ctx, letterID) approvals, err := p.approvalRepo.ListByLetter(ctx, letterID)

View File

@ -1112,38 +1112,78 @@ func (s *LetterOutgoingServiceImpl) GetLetterApprovals(ctx context.Context, lett
return nil, err return nil, err
} }
// Get all approvals for this letter's current revision // Get all approvals for this letter (all revisions)
approvals, err := s.processor.GetApprovalsByLetterAndRevision(ctx, letterID, letter.RevisionNumber) approvals, err := s.processor.GetAllApprovalsByLetter(ctx, letterID)
if err != nil { if err != nil {
return nil, err return nil, err
} }
// Sort approvals by step order and parallel group // Group approvals by revision number from approval itself
sort.Slice(approvals, func(i, j int) bool { revisionMap := make(map[int][]entities.LetterOutgoingApproval)
if approvals[i].StepOrder != approvals[j].StepOrder { for _, approval := range approvals {
return approvals[i].StepOrder < approvals[j].StepOrder revisionMap[approval.RevisionNumber] = append(revisionMap[approval.RevisionNumber], approval)
} }
return approvals[i].ParallelGroup < approvals[j].ParallelGroup
// Get sorted revision numbers
revisionNumbers := make([]int, 0, len(revisionMap))
for revNum := range revisionMap {
revisionNumbers = append(revisionNumbers, revNum)
}
sort.Sort(sort.Reverse(sort.IntSlice(revisionNumbers)))
// Process each revision
revisionResponses := make([]contract.OutgoingLetterApprovalRevisionNumberResponse, 0, len(revisionNumbers))
totalSteps := 0
currentStep := 0
for _, revNum := range revisionNumbers {
revisionApprovals := revisionMap[revNum]
// Sort approvals within this revision by step order and parallel group
sort.Slice(revisionApprovals, func(i, j int) bool {
if revisionApprovals[i].StepOrder != revisionApprovals[j].StepOrder {
return revisionApprovals[i].StepOrder < revisionApprovals[j].StepOrder
}
return revisionApprovals[i].ParallelGroup < revisionApprovals[j].ParallelGroup
}) })
// Transform to response format // Transform to response format
approvalResponses := make([]contract.EnhancedOutgoingLetterApprovalResponse, 0, len(approvals)) approvalResponses := make([]contract.EnhancedOutgoingLetterApprovalResponse, 0, len(revisionApprovals))
totalSteps := 0
currentStep := 0 // Only calculate totalSteps and currentStep for the current letter's revision
if revNum == letter.RevisionNumber {
stepOrdersSeen := make(map[int]bool) stepOrdersSeen := make(map[int]bool)
for _, approval := range approvals { for _, approval := range revisionApprovals {
// Count unique step orders for total steps // Count unique step orders for total steps
if !stepOrdersSeen[approval.StepOrder] { if !stepOrdersSeen[approval.StepOrder] {
stepOrdersSeen[approval.StepOrder] = true stepOrdersSeen[approval.StepOrder] = true
totalSteps++ totalSteps++
} }
// Determine current step (lowest step with pending/not_started status) // Determine current step (lowest step with pending status)
if approval.Status == entities.ApprovalStatusPending && (currentStep == 0 || approval.StepOrder < currentStep) { if approval.Status == entities.ApprovalStatusPending && (currentStep == 0 || approval.StepOrder < currentStep) {
currentStep = approval.StepOrder currentStep = approval.StepOrder
} }
}
// If no current step found but there are approvals, check if all are completed
if currentStep == 0 && len(revisionApprovals) > 0 {
allCompleted := true
for _, approval := range revisionApprovals {
if approval.IsRequired && approval.Status != entities.ApprovalStatusApproved {
allCompleted = false
break
}
}
if allCompleted {
currentStep = totalSteps // All steps completed
}
}
}
for _, approval := range revisionApprovals {
approvalResp := contract.EnhancedOutgoingLetterApprovalResponse{ approvalResp := contract.EnhancedOutgoingLetterApprovalResponse{
ID: approval.ID, ID: approval.ID,
LetterID: approval.LetterID, LetterID: approval.LetterID,
@ -1152,6 +1192,7 @@ func (s *LetterOutgoingServiceImpl) GetLetterApprovals(ctx context.Context, lett
ParallelGroup: approval.ParallelGroup, ParallelGroup: approval.ParallelGroup,
IsRequired: approval.IsRequired, IsRequired: approval.IsRequired,
ApproverID: approval.ApproverID, ApproverID: approval.ApproverID,
RevisionNumber: approval.RevisionNumber,
Status: string(approval.Status), Status: string(approval.Status),
Remarks: approval.Remarks, Remarks: approval.Remarks,
ActedAt: approval.ActedAt, ActedAt: approval.ActedAt,
@ -1200,18 +1241,11 @@ func (s *LetterOutgoingServiceImpl) GetLetterApprovals(ctx context.Context, lett
approvalResponses = append(approvalResponses, approvalResp) approvalResponses = append(approvalResponses, approvalResp)
} }
// If no current step found but there are approvals, check if all are completed // Add revision response
if currentStep == 0 && len(approvals) > 0 { revisionResponses = append(revisionResponses, contract.OutgoingLetterApprovalRevisionNumberResponse{
allCompleted := true RevisionNumber: revNum,
for _, approval := range approvals { Approvals: approvalResponses,
if approval.IsRequired && approval.Status != entities.ApprovalStatusApproved { })
allCompleted = false
break
}
}
if allCompleted {
currentStep = totalSteps // All steps completed
}
} }
response := &contract.GetLetterApprovalsResponse{ response := &contract.GetLetterApprovalsResponse{
@ -1220,12 +1254,12 @@ func (s *LetterOutgoingServiceImpl) GetLetterApprovals(ctx context.Context, lett
LetterStatus: string(letter.Status), LetterStatus: string(letter.Status),
TotalSteps: totalSteps, TotalSteps: totalSteps,
CurrentStep: currentStep, CurrentStep: currentStep,
Approvals: approvalResponses, CurrentRevisionNumber: letter.RevisionNumber,
Approvals: revisionResponses,
} }
return response, nil return response, nil
} }
func getUserIDFromContext(ctx context.Context) uuid.UUID { func getUserIDFromContext(ctx context.Context) uuid.UUID {
appCtx := appcontext.FromGinContext(ctx) appCtx := appcontext.FromGinContext(ctx)
if appCtx != nil { if appCtx != nil {
@ -1264,6 +1298,7 @@ func (s *LetterOutgoingServiceImpl) GetApprovalDiscussions(ctx context.Context,
ParallelGroup: approval.ParallelGroup, ParallelGroup: approval.ParallelGroup,
IsRequired: approval.IsRequired, IsRequired: approval.IsRequired,
ApproverID: approval.ApproverID, ApproverID: approval.ApproverID,
RevisionNumber: approval.RevisionNumber,
Status: string(approval.Status), Status: string(approval.Status),
Remarks: approval.Remarks, Remarks: approval.Remarks,
ActedAt: approval.ActedAt, ActedAt: approval.ActedAt,