surat keluar approval

This commit is contained in:
efrilm 2025-09-21 14:39:02 +07:00
parent acb9f75a18
commit 646af36795
3 changed files with 125 additions and 2 deletions

View File

@ -401,6 +401,7 @@ func (a *App) initServices(processors *processors, repos *repositories, cfg *con
processors.letterApprovalProcessor,
processors.letterAttachmentProcessor,
processors.letterOutgoingRecipientProcessor,
processors.notificationProcessor,
processors.letterActivityProcessor,
)

View File

@ -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,
},
})
}

View File

@ -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())
}
}