add revision number
This commit is contained in:
@@ -24,8 +24,9 @@ type LetterOutgoingProcessor interface {
|
||||
ArchiveOutgoingLetter(ctx context.Context, letterID uuid.UUID, userID uuid.UUID) error
|
||||
|
||||
ProcessApprovalSubmission(ctx context.Context, letterID uuid.UUID, approvalFlowID uuid.UUID, userID uuid.UUID) error
|
||||
ProcessApproval(ctx context.Context, letterID uuid.UUID, approval *entities.LetterOutgoingApproval, userID uuid.UUID, allApproved bool) error
|
||||
ProcessApproval(ctx context.Context, letterID uuid.UUID, approval *entities.LetterOutgoingApproval, userID uuid.UUID) error
|
||||
ProcessRejection(ctx context.Context, letterID uuid.UUID, approval *entities.LetterOutgoingApproval, userID uuid.UUID) error
|
||||
ProcessRevision(ctx context.Context, letterID uuid.UUID, attachment entities.LetterOutgoingAttachment, userID uuid.UUID) error
|
||||
|
||||
AddRecipients(ctx context.Context, letterID uuid.UUID, recipients []entities.LetterOutgoingRecipient, userID uuid.UUID) error
|
||||
UpdateRecipient(ctx context.Context, recipient *entities.LetterOutgoingRecipient) error
|
||||
@@ -40,6 +41,7 @@ type LetterOutgoingProcessor interface {
|
||||
DeleteDiscussion(ctx context.Context, id uuid.UUID) error
|
||||
|
||||
GetApprovalsByLetter(ctx context.Context, letterID uuid.UUID) ([]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)
|
||||
|
||||
// GetOutgoingLetterWithDetails fetches letter with all related data
|
||||
@@ -397,6 +399,12 @@ func (p *LetterOutgoingProcessorImpl) ProcessApprovalSubmission(ctx context.Cont
|
||||
}
|
||||
|
||||
return p.txManager.WithTransaction(ctx, func(txCtx context.Context) error {
|
||||
// Get the letter to get the current revision number
|
||||
letter, err := p.letterRepo.Get(txCtx, letterID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Find the minimum step order (first step)
|
||||
minStepOrder := flow.Steps[0].StepOrder
|
||||
for _, step := range flow.Steps {
|
||||
@@ -408,13 +416,14 @@ func (p *LetterOutgoingProcessorImpl) ProcessApprovalSubmission(ctx context.Cont
|
||||
approvals := make([]entities.LetterOutgoingApproval, len(flow.Steps))
|
||||
for i, step := range flow.Steps {
|
||||
approvals[i] = entities.LetterOutgoingApproval{
|
||||
LetterID: letterID,
|
||||
StepID: step.ID,
|
||||
StepOrder: step.StepOrder,
|
||||
ParallelGroup: step.ParallelGroup,
|
||||
IsRequired: step.Required,
|
||||
ApproverID: step.ApproverUserID,
|
||||
Status: entities.ApprovalStatusPending,
|
||||
LetterID: letterID,
|
||||
StepID: step.ID,
|
||||
RevisionNumber: letter.RevisionNumber,
|
||||
StepOrder: step.StepOrder,
|
||||
ParallelGroup: step.ParallelGroup,
|
||||
IsRequired: step.Required,
|
||||
ApproverID: step.ApproverUserID,
|
||||
Status: entities.ApprovalStatusPending,
|
||||
}
|
||||
|
||||
// Set status based on step order
|
||||
@@ -483,15 +492,15 @@ func (p *LetterOutgoingProcessorImpl) ProcessApprovalSubmission(ctx context.Cont
|
||||
})
|
||||
}
|
||||
|
||||
func (p *LetterOutgoingProcessorImpl) ProcessApproval(ctx context.Context, letterID uuid.UUID, approval *entities.LetterOutgoingApproval, userID uuid.UUID, allApproved bool) error {
|
||||
func (p *LetterOutgoingProcessorImpl) ProcessApproval(ctx context.Context, letterID uuid.UUID, approval *entities.LetterOutgoingApproval, userID uuid.UUID) error {
|
||||
return p.txManager.WithTransaction(ctx, func(txCtx context.Context) error {
|
||||
// Step 1: Update the approval record
|
||||
if err := p.updateApprovalRecord(txCtx, approval, userID); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Step 2: Get all approvals and organize by step
|
||||
approvalsByStep, err := p.getApprovalsByStep(txCtx, letterID)
|
||||
// Step 2: Get all approvals FOR THE SAME REVISION and organize by step
|
||||
approvalsByStep, err := p.getApprovalsByStepForRevision(txCtx, letterID, approval.RevisionNumber)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -499,12 +508,12 @@ func (p *LetterOutgoingProcessorImpl) ProcessApproval(ctx context.Context, lette
|
||||
// Step 3: Check if current step is completed
|
||||
if p.isStepCompleted(approvalsByStep[approval.StepOrder]) {
|
||||
// Step 4: Activate next step if exists
|
||||
if err := p.activateNextStep(txCtx, letterID, approval.StepOrder, approvalsByStep); err != nil {
|
||||
if err := p.activateNextStepForRevision(txCtx, letterID, approval.StepOrder, approval.RevisionNumber, approvalsByStep); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// Step 5: Check if all required approvals are completed
|
||||
// Step 5: Check if all required approvals are completed FOR THIS REVISION
|
||||
if p.areAllRequiredApprovalsCompleted(approvalsByStep) {
|
||||
// Step 6: Update letter status to approved
|
||||
if err := p.letterRepo.UpdateStatus(txCtx, letterID, entities.LetterOutgoingStatusApproved); err != nil {
|
||||
@@ -542,6 +551,24 @@ func (p *LetterOutgoingProcessorImpl) getApprovalsByStep(ctx context.Context, le
|
||||
return approvalsByStep, nil
|
||||
}
|
||||
|
||||
// getApprovalsByStepForRevision fetches approvals for a specific revision and organizes them by step order
|
||||
func (p *LetterOutgoingProcessorImpl) getApprovalsByStepForRevision(ctx context.Context, letterID uuid.UUID, revisionNumber int) (map[int][]entities.LetterOutgoingApproval, error) {
|
||||
allApprovals, err := p.approvalRepo.ListByLetter(ctx, letterID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
approvalsByStep := make(map[int][]entities.LetterOutgoingApproval)
|
||||
for _, approval := range allApprovals {
|
||||
// Only include approvals from the same revision
|
||||
if approval.RevisionNumber == revisionNumber {
|
||||
approvalsByStep[approval.StepOrder] = append(approvalsByStep[approval.StepOrder], approval)
|
||||
}
|
||||
}
|
||||
|
||||
return approvalsByStep, nil
|
||||
}
|
||||
|
||||
// isStepCompleted checks if all required approvals in a step are approved
|
||||
func (p *LetterOutgoingProcessorImpl) isStepCompleted(stepApprovals []entities.LetterOutgoingApproval) bool {
|
||||
for _, approval := range stepApprovals {
|
||||
@@ -582,6 +609,39 @@ func (p *LetterOutgoingProcessorImpl) activateNextStep(ctx context.Context, lett
|
||||
return nil
|
||||
}
|
||||
|
||||
// activateNextStepForRevision activates the next approval step for a specific revision
|
||||
func (p *LetterOutgoingProcessorImpl) activateNextStepForRevision(ctx context.Context, letterID uuid.UUID, currentStepOrder int, revisionNumber int, approvalsByStep map[int][]entities.LetterOutgoingApproval) error {
|
||||
nextStepOrder := currentStepOrder + 1
|
||||
nextStepApprovals, exists := approvalsByStep[nextStepOrder]
|
||||
if !exists {
|
||||
return nil // No next step
|
||||
}
|
||||
|
||||
// Get existing recipients to avoid duplicates
|
||||
existingUserIDs, err := p.getExistingRecipientUserIDs(ctx, letterID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Process each approval in the next step (already filtered by revision in approvalsByStep)
|
||||
for _, nextApproval := range nextStepApprovals {
|
||||
// Only process if it's the same revision
|
||||
if nextApproval.RevisionNumber == revisionNumber {
|
||||
// Activate approval if not started
|
||||
if err := p.activateApprovalIfNotStarted(ctx, &nextApproval); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Add approver as recipient if not already exists
|
||||
if err := p.addApproverAsRecipientIfNeeded(ctx, letterID, nextApproval.ApproverID, existingUserIDs); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// getExistingRecipientUserIDs gets a set of existing recipient user IDs
|
||||
func (p *LetterOutgoingProcessorImpl) getExistingRecipientUserIDs(ctx context.Context, letterID uuid.UUID) (map[uuid.UUID]bool, error) {
|
||||
currentRecipients, err := p.recipientRepo.ListByLetter(ctx, letterID)
|
||||
@@ -665,12 +725,39 @@ func (p *LetterOutgoingProcessorImpl) ProcessRejection(ctx context.Context, lett
|
||||
return err
|
||||
}
|
||||
|
||||
// Mark all other pending approvals in the same revision as rejected
|
||||
allApprovals, err := p.approvalRepo.ListByLetter(txCtx, letterID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for i := range allApprovals {
|
||||
// Only update other pending approvals from the same revision
|
||||
if allApprovals[i].RevisionNumber == approval.RevisionNumber &&
|
||||
allApprovals[i].ID != approval.ID &&
|
||||
allApprovals[i].Status == entities.ApprovalStatusPending {
|
||||
allApprovals[i].Status = entities.ApprovalStatusRejected
|
||||
if err := p.approvalRepo.Update(txCtx, &allApprovals[i]); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if err := p.letterRepo.UpdateStatus(txCtx, letterID, entities.LetterOutgoingStatusRejected); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fromStatus := string(entities.LetterOutgoingStatusPendingApproval)
|
||||
toStatus := string(entities.LetterOutgoingStatusRejected)
|
||||
|
||||
// Include rejection remarks in activity log context
|
||||
var context entities.JSONB
|
||||
if approval.Remarks != nil && *approval.Remarks != "" {
|
||||
context = entities.JSONB{"remarks": *approval.Remarks, "revision_number": approval.RevisionNumber}
|
||||
} else {
|
||||
context = entities.JSONB{"revision_number": approval.RevisionNumber}
|
||||
}
|
||||
|
||||
activityLog := &entities.LetterOutgoingActivityLog{
|
||||
LetterID: letterID,
|
||||
ActionType: entities.LetterOutgoingActionRejected,
|
||||
@@ -678,6 +765,79 @@ func (p *LetterOutgoingProcessorImpl) ProcessRejection(ctx context.Context, lett
|
||||
TargetID: &approval.ID,
|
||||
FromStatus: &fromStatus,
|
||||
ToStatus: &toStatus,
|
||||
Context: context,
|
||||
}
|
||||
if err := p.activityLogRepo.Create(txCtx, activityLog); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
func (p *LetterOutgoingProcessorImpl) ProcessRevision(ctx context.Context, letterID uuid.UUID, attachment entities.LetterOutgoingAttachment, userID uuid.UUID) error {
|
||||
return p.txManager.WithTransaction(ctx, func(txCtx context.Context) error {
|
||||
// Get the current letter
|
||||
letter, err := p.letterRepo.Get(txCtx, letterID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Increment revision number
|
||||
letter.RevisionNumber++
|
||||
|
||||
// Set revision number on the new attachment
|
||||
attachment.RevisionNumber = letter.RevisionNumber
|
||||
|
||||
// Add the new attachment
|
||||
if err := p.attachmentRepo.Create(txCtx, &attachment); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Update letter with new revision number
|
||||
if err := p.letterRepo.Update(txCtx, letter); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Update status to pending approval (ready for re-submission)
|
||||
if err := p.letterRepo.UpdateStatus(txCtx, letterID, entities.LetterOutgoingStatusPendingApproval); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Get existing approvals for the current revision
|
||||
approvals, err := p.approvalRepo.ListByLetter(txCtx, letterID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Create new approval records for the new revision
|
||||
for _, approval := range approvals {
|
||||
// Create a new approval for the new revision
|
||||
newApproval := entities.LetterOutgoingApproval{
|
||||
LetterID: approval.LetterID,
|
||||
StepID: approval.StepID,
|
||||
RevisionNumber: letter.RevisionNumber,
|
||||
StepOrder: approval.StepOrder,
|
||||
ParallelGroup: approval.ParallelGroup,
|
||||
IsRequired: approval.IsRequired,
|
||||
Status: entities.ApprovalStatusPending,
|
||||
}
|
||||
if err := p.approvalRepo.Create(txCtx, &newApproval); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// Log activity
|
||||
fromStatus := string(entities.LetterOutgoingStatusRejected)
|
||||
toStatus := string(entities.LetterOutgoingStatusPendingApproval)
|
||||
activityLog := &entities.LetterOutgoingActivityLog{
|
||||
LetterID: letterID,
|
||||
ActionType: entities.LetterOutgoingActionRevised,
|
||||
ActorUserID: &userID,
|
||||
TargetID: &attachment.ID,
|
||||
FromStatus: &fromStatus,
|
||||
ToStatus: &toStatus,
|
||||
Context: entities.JSONB{"attachment": attachment.FileName, "revision_number": letter.RevisionNumber},
|
||||
}
|
||||
if err := p.activityLogRepo.Create(txCtx, activityLog); err != nil {
|
||||
return err
|
||||
@@ -732,6 +892,17 @@ func (p *LetterOutgoingProcessorImpl) RemoveRecipient(ctx context.Context, lette
|
||||
|
||||
func (p *LetterOutgoingProcessorImpl) AddAttachments(ctx context.Context, letterID uuid.UUID, attachments []entities.LetterOutgoingAttachment, userID uuid.UUID) error {
|
||||
return p.txManager.WithTransaction(ctx, func(txCtx context.Context) error {
|
||||
// Get the letter to get the current revision number
|
||||
letter, err := p.letterRepo.Get(txCtx, letterID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Set revision number on all attachments
|
||||
for i := range attachments {
|
||||
attachments[i].RevisionNumber = letter.RevisionNumber
|
||||
}
|
||||
|
||||
if err := p.attachmentRepo.CreateBulk(txCtx, attachments); err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -808,7 +979,31 @@ func (p *LetterOutgoingProcessorImpl) DeleteDiscussion(ctx context.Context, id u
|
||||
}
|
||||
|
||||
func (p *LetterOutgoingProcessorImpl) GetApprovalsByLetter(ctx context.Context, letterID uuid.UUID) ([]entities.LetterOutgoingApproval, error) {
|
||||
return p.approvalRepo.ListByLetter(ctx, letterID)
|
||||
// Get the letter first to know the current revision
|
||||
letter, err := p.letterRepo.Get(ctx, letterID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return p.GetApprovalsByLetterAndRevision(ctx, letterID, letter.RevisionNumber)
|
||||
}
|
||||
|
||||
func (p *LetterOutgoingProcessorImpl) GetApprovalsByLetterAndRevision(ctx context.Context, letterID uuid.UUID, revisionNumber int) ([]entities.LetterOutgoingApproval, error) {
|
||||
// Get all approvals for this letter
|
||||
approvals, err := p.approvalRepo.ListByLetter(ctx, letterID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Filter to only return approvals for the specified revision
|
||||
var currentRevisionApprovals []entities.LetterOutgoingApproval
|
||||
for _, approval := range approvals {
|
||||
if approval.RevisionNumber == revisionNumber {
|
||||
currentRevisionApprovals = append(currentRevisionApprovals, approval)
|
||||
}
|
||||
}
|
||||
|
||||
return currentRevisionApprovals, nil
|
||||
}
|
||||
|
||||
func (p *LetterOutgoingProcessorImpl) GetApprovalFlow(ctx context.Context, flowID uuid.UUID) (*entities.ApprovalFlow, error) {
|
||||
|
||||
Reference in New Issue
Block a user