From 646af36795434eaaf408802c2841c761b402bb52 Mon Sep 17 00:00:00 2001 From: efrilm Date: Sun, 21 Sep 2025 14:39:02 +0700 Subject: [PATCH] surat keluar approval --- internal/app/app.go | 1 + internal/processor/notification_processor.go | 28 ++++++ internal/service/letter_outgoing_service.go | 98 +++++++++++++++++++- 3 files changed, 125 insertions(+), 2 deletions(-) diff --git a/internal/app/app.go b/internal/app/app.go index 439903f..9da475f 100644 --- a/internal/app/app.go +++ b/internal/app/app.go @@ -401,6 +401,7 @@ func (a *App) initServices(processors *processors, repos *repositories, cfg *con processors.letterApprovalProcessor, processors.letterAttachmentProcessor, processors.letterOutgoingRecipientProcessor, + processors.notificationProcessor, processors.letterActivityProcessor, ) diff --git a/internal/processor/notification_processor.go b/internal/processor/notification_processor.go index ef81747..f2d19b0 100644 --- a/internal/processor/notification_processor.go +++ b/internal/processor/notification_processor.go @@ -23,6 +23,7 @@ type NotificationProcessor interface { // Letter notifications SendIncomingLetterNotification(ctx context.Context, letterID uuid.UUID, recipientUserID uuid.UUID, subject string, body string) error + SendOutgoingLetterNotification(ctx context.Context, letterID uuid.UUID, recipientUserID uuid.UUID, subject string, body string) error } type NotificationProcessorImpl struct { @@ -359,3 +360,30 @@ func (p *NovuProvider) SendNotification(ctx context.Context, payload Notificatio return nil } + +func (p *NotificationProcessorImpl) SendOutgoingLetterNotification(ctx context.Context, letterID uuid.UUID, recipientUserID uuid.UUID, subject string, body string) error { + // Ensure subscriber exists + if err := p.provider.EnsureSubscriberExists(ctx, recipientUserID); err != nil { + return fmt.Errorf("failed to ensure subscriber exists: %w", err) + } + + // Build notification URL for outgoing letters + url := fmt.Sprintf("/en/apps/surat-menyurat/keluar-detail/%s", letterID.String()) + + // Use workflow ID from config (defaults to "notification-dashbpard") + workflowID := p.workflowID + if workflowID == "" { + workflowID = "notification-dashbpard" + } + + // Send notification + return p.provider.SendNotification(ctx, NotificationPayload{ + RecipientID: recipientUserID, + EventName: workflowID, + Data: map[string]interface{}{ + "subject": subject, + "body": body, + "url": url, + }, + }) +} diff --git a/internal/service/letter_outgoing_service.go b/internal/service/letter_outgoing_service.go index 3b85fb4..d33b075 100644 --- a/internal/service/letter_outgoing_service.go +++ b/internal/service/letter_outgoing_service.go @@ -3,6 +3,7 @@ package service import ( "context" "fmt" + "log" "sort" "time" @@ -60,6 +61,7 @@ type LetterOutgoingServiceImpl struct { approvalProcessor processor.LetterApprovalProcessor attachmentProcessor processor.LetterAttachmentProcessor recipientProcessor processor.LetterOutgoingRecipientProcessor + notificationProcessor processor.NotificationProcessor activityProcessor processor.LetterActivityProcessor } @@ -71,6 +73,7 @@ func NewLetterOutgoingService( approvalProcessor processor.LetterApprovalProcessor, attachmentProcessor processor.LetterAttachmentProcessor, recipientProcessor processor.LetterOutgoingRecipientProcessor, + notificationProcessor processor.NotificationProcessor, activityProcessor processor.LetterActivityProcessor, ) *LetterOutgoingServiceImpl { return &LetterOutgoingServiceImpl{ @@ -81,6 +84,7 @@ func NewLetterOutgoingService( approvalProcessor: approvalProcessor, attachmentProcessor: attachmentProcessor, recipientProcessor: recipientProcessor, + notificationProcessor: notificationProcessor, activityProcessor: activityProcessor, } } @@ -168,6 +172,15 @@ func (s *LetterOutgoingServiceImpl) CreateOutgoingLetter(ctx context.Context, re return nil, err } + // Send notifications if letter needs approval + log.Printf("[DEBUG] createOutgoingLetter Finsig") + log.Printf("[DEBUG] NotificationProcessor is nil: %v", s.notificationProcessor == nil) + if s.notificationProcessor != nil && len(result.Approvals) > 0 { + log.Printf("[DEBUG] sendFirstStepApprovalNotifications start") + go s.sendStepApprovalNotifications(context.Background(), result.ID, result.Subject, 1) + } + + return transformLetterToResponse(result), nil } @@ -457,7 +470,24 @@ func (s *LetterOutgoingServiceImpl) ApproveOutgoingLetter(ctx context.Context, l } } - return s.processor.ProcessApproval(ctx, letterID, currentApproval, userID, allApproved) + err = s.processor.ProcessApproval(ctx, letterID, currentApproval, userID, allApproved) + if err != nil { + return err + } + + // Send notifications after successful approval + if s.notificationProcessor != nil { + // Step approved but not final - notify creator about step completion AND next approvers + creatorMessage := fmt.Sprintf("Surat keluar '%s' telah disetujui pada tahap %d, menunggu persetujuan tahap berikutnya", letter.Subject, currentApproval.StepOrder) + go s.sendApprovalNotificationToCreator(context.Background(), letterID, letter.CreatedBy, "Surat Keluar Disetujui Tahap " + fmt.Sprintf("%d", currentApproval.StepOrder), creatorMessage) + + // Notify next step approvers + nextStepOrder := currentApproval.StepOrder + 1 + go s.sendStepApprovalNotifications(context.Background(), letterID, letter.Subject, nextStepOrder) + + } + + return nil } func (s *LetterOutgoingServiceImpl) RejectOutgoingLetter(ctx context.Context, letterID uuid.UUID, req *contract.RejectLetterRequest) error { @@ -495,7 +525,18 @@ func (s *LetterOutgoingServiceImpl) RejectOutgoingLetter(ctx context.Context, le currentApproval.Remarks = &req.Reason - return s.processor.ProcessRejection(ctx, letterID, currentApproval, userID) + err = s.processor.ProcessRejection(ctx, letterID, currentApproval, userID) + if err != nil { + return err + } + + // Send notification to letter creator (rejection always notifies creator) + if s.notificationProcessor != nil { + message := fmt.Sprintf("Surat keluar '%s' ditolak pada tahap %d dengan alasan: %s", letter.Subject, currentApproval.StepOrder, req.Reason) + go s.sendApprovalNotificationToCreator(context.Background(), letterID, letter.CreatedBy, "Surat Keluar Ditolak", message) + } + + return nil } func (s *LetterOutgoingServiceImpl) SendOutgoingLetter(ctx context.Context, letterID uuid.UUID) error { @@ -1539,3 +1580,56 @@ func (s *LetterOutgoingServiceImpl) BulkArchiveOutgoingLetters(ctx context.Conte ArchivedCount: int(archivedCount), }, nil } + +func (s *LetterOutgoingServiceImpl) sendStepApprovalNotifications(ctx context.Context, letterID uuid.UUID, subject string, stepOrder int) { + log.Printf("[DEBUG] sendStepApprovalNotifications START - LetterID: %s, StepOrder: %d", letterID.String(), stepOrder) + + approvals, err := s.processor.GetApprovalsByLetter(ctx, letterID) + if err != nil { + log.Printf("[ERROR] Failed to get approvals: %v", err) + return + } + + log.Printf("[DEBUG] Found %d approvals", len(approvals)) + + // Find approvers for the specified step + for _, approval := range approvals { + log.Printf("[DEBUG] Checking approval: Step=%d, Status=%s, ApproverID=%v", + approval.StepOrder, approval.Status, approval.ApproverID) + + if approval.StepOrder == stepOrder { + log.Printf("[DEBUG] Sending notification to approver %s for step %d", approval.ApproverID.String(), stepOrder) + + err := s.notificationProcessor.SendOutgoingLetterNotification( + ctx, + letterID, + *approval.ApproverID, + "Surat Keluar Perlu Persetujuan", + fmt.Sprintf("Surat keluar '%s' memerlukan persetujuan Anda pada tahap %d", subject, stepOrder)) + + if err != nil { + log.Printf("[ERROR] Failed to send notification to approver %s: %v", approval.ApproverID.String(), err) + } else { + log.Printf("[DEBUG] Successfully sent notification to approver %s", approval.ApproverID.String()) + } + } + } +} + +// Kirim notifikasi ke creator +func (s *LetterOutgoingServiceImpl) sendApprovalNotificationToCreator(ctx context.Context, letterID uuid.UUID, creatorID uuid.UUID, title string, message string) { + log.Printf("[DEBUG] sendApprovalNotificationToCreator START - LetterID: %s, CreatorID: %s", letterID.String(), creatorID.String()) + + err := s.notificationProcessor.SendOutgoingLetterNotification( + ctx, + letterID, + creatorID, + title, + message) + + if err != nil { + log.Printf("[ERROR] Failed to send notification to creator %s: %v", creatorID.String(), err) + } else { + log.Printf("[DEBUG] Successfully sent notification to creator %s", creatorID.String()) + } +}