From f73a5d533c5bfd35f37541d4553e62eb8c51cbc2 Mon Sep 17 00:00:00 2001 From: Efril Date: Sun, 10 May 2026 23:36:22 +0700 Subject: [PATCH] add notif at create order --- internal/app/app.go | 4 +- internal/repository/user_repository.go | 11 ++++ internal/service/order_service.go | 76 +++++++++++++++++++++++++- 3 files changed, 87 insertions(+), 4 deletions(-) diff --git a/internal/app/app.go b/internal/app/app.go index 1955fc9..3ba6f47 100644 --- a/internal/app/app.go +++ b/internal/app/app.go @@ -409,7 +409,7 @@ func (a *App) initServices(processors *processors, repos *repositories, cfg *con productService := service.NewProductService(processors.productProcessor) productVariantService := service.NewProductVariantService(processors.productVariantProcessor) inventoryService := service.NewInventoryService(processors.inventoryProcessor) - orderService := service.NewOrderServiceImpl(processors.orderProcessor, repos.tableRepo, nil, processors.orderIngredientTransactionProcessor, *repos.productRecipeRepo, repos.txManager, repos.sessionRepo) // Will be updated after orderIngredientTransactionService is created + orderService := service.NewOrderServiceImpl(processors.orderProcessor, repos.tableRepo, nil, processors.orderIngredientTransactionProcessor, *repos.productRecipeRepo, repos.txManager, repos.sessionRepo, processors.notificationProcessor, repos.userRepo) // Will be updated after orderIngredientTransactionService is created paymentMethodService := service.NewPaymentMethodService(processors.paymentMethodProcessor) fileService := service.NewFileServiceImpl(processors.fileProcessor) var customerService service.CustomerService = service.NewCustomerService(processors.customerProcessor) @@ -436,7 +436,7 @@ func (a *App) initServices(processors *processors, repos *repositories, cfg *con notificationService := service.NewNotificationService(processors.notificationProcessor) // Update order service with order ingredient transaction service - orderService = service.NewOrderServiceImpl(processors.orderProcessor, repos.tableRepo, orderIngredientTransactionService, processors.orderIngredientTransactionProcessor, *repos.productRecipeRepo, repos.txManager, repos.sessionRepo) + orderService = service.NewOrderServiceImpl(processors.orderProcessor, repos.tableRepo, orderIngredientTransactionService, processors.orderIngredientTransactionProcessor, *repos.productRecipeRepo, repos.txManager, repos.sessionRepo, processors.notificationProcessor, repos.userRepo) return &services{ userService: service.NewUserService(processors.userProcessor), diff --git a/internal/repository/user_repository.go b/internal/repository/user_repository.go index a9e2747..bef93a3 100644 --- a/internal/repository/user_repository.go +++ b/internal/repository/user_repository.go @@ -61,6 +61,17 @@ func (r *UserRepositoryImpl) GetActiveUsers(ctx context.Context, organizationID return users, err } +func (r *UserRepositoryImpl) GetActiveByOutletID(ctx context.Context, organizationID, outletID uuid.UUID) ([]*entities.User, error) { + var users []*entities.User + err := r.db.WithContext(ctx). + Where( + "organization_id = ? AND is_active = ? AND (outlet_id = ? OR role IN ?)", + organizationID, true, outletID, []string{"admin", "manager"}, + ). + Find(&users).Error + return users, err +} + func (r *UserRepositoryImpl) Update(ctx context.Context, user *entities.User) error { return r.db.WithContext(ctx).Save(user).Error } diff --git a/internal/service/order_service.go b/internal/service/order_service.go index 056fc99..f205d2d 100644 --- a/internal/service/order_service.go +++ b/internal/service/order_service.go @@ -16,6 +16,11 @@ import ( "github.com/google/uuid" ) +// orderUserRepository is a minimal interface to fetch users by organization for notification purposes. +type orderUserRepository interface { + GetActiveByOutletID(ctx context.Context, organizationID, outletID uuid.UUID) ([]*entities.User, error) +} + type OrderService interface { CreateOrder(ctx context.Context, req *models.CreateOrderRequest, organizationID uuid.UUID) (*models.OrderResponse, error) AddToOrder(ctx context.Context, orderID uuid.UUID, req *models.AddToOrderRequest) (*models.AddToOrderResponse, error) @@ -38,9 +43,11 @@ type OrderServiceImpl struct { productRecipeRepo repository.ProductRecipeRepository txManager *repository.TxManager sessionRepo repository.SessionRepository + notificationProcessor processor.NotificationProcessor + userRepo orderUserRepository } -func NewOrderServiceImpl(orderProcessor processor.OrderProcessor, tableRepo repository.TableRepositoryInterface, orderIngredientTransactionService *OrderIngredientTransactionService, orderIngredientTransactionProcessor processor.OrderIngredientTransactionProcessor, productRecipeRepo repository.ProductRecipeRepository, txManager *repository.TxManager, sessionRepo repository.SessionRepository) *OrderServiceImpl { +func NewOrderServiceImpl(orderProcessor processor.OrderProcessor, tableRepo repository.TableRepositoryInterface, orderIngredientTransactionService *OrderIngredientTransactionService, orderIngredientTransactionProcessor processor.OrderIngredientTransactionProcessor, productRecipeRepo repository.ProductRecipeRepository, txManager *repository.TxManager, sessionRepo repository.SessionRepository, notificationProcessor processor.NotificationProcessor, userRepo orderUserRepository) *OrderServiceImpl { return &OrderServiceImpl{ orderProcessor: orderProcessor, tableRepo: tableRepo, @@ -49,6 +56,8 @@ func NewOrderServiceImpl(orderProcessor processor.OrderProcessor, tableRepo repo productRecipeRepo: productRecipeRepo, txManager: txManager, sessionRepo: sessionRepo, + notificationProcessor: notificationProcessor, + userRepo: userRepo, } } @@ -104,10 +113,73 @@ func (s *OrderServiceImpl) CreateOrder(ctx context.Context, req *models.CreateOr return nil, err } + // Send notification to all org users if this is a self-order + if isSelfOrder(req.Metadata) { + go s.sendSelfOrderNotification(context.Background(), response, organizationID) + } + return response, nil } -// createIngredientTransactions creates ingredient transactions for order items efficiently +// isSelfOrder checks if the order metadata indicates a self-order. +func isSelfOrder(metadata map[string]interface{}) bool { + if metadata == nil { + return false + } + v, ok := metadata["self_order"] + if !ok { + return false + } + b, ok := v.(bool) + return ok && b +} + +// sendSelfOrderNotification sends a new-order notification to all active users +// that can access the outlet where the self-order was placed. +func (s *OrderServiceImpl) sendSelfOrderNotification(ctx context.Context, order *models.OrderResponse, organizationID uuid.UUID) { + if s.notificationProcessor == nil || s.userRepo == nil { + return + } + + users, err := s.userRepo.GetActiveByOutletID(ctx, organizationID, order.OutletID) + if err != nil || len(users) == 0 { + return + } + + receiverIDs := make([]uuid.UUID, 0, len(users)) + for _, u := range users { + receiverIDs = append(receiverIDs, u.ID) + } + + tableName := "" + if order.TableNumber != nil { + tableName = *order.TableNumber + } + + title := "Pesanan Baru Masuk" + body := fmt.Sprintf("Ada pesanan baru dari meja %s", tableName) + if tableName == "" { + body = "Ada pesanan baru masuk" + } + + orderID := order.ID + notifReq := &models.SendNotificationRequest{ + Title: title, + Body: body, + Type: "order", + Category: "self_order", + NotifiableType: "order", + NotifiableID: &orderID, + ReceiverIDs: receiverIDs, + Data: map[string]interface{}{ + "order_id": order.ID.String(), + "order_number": order.OrderNumber, + "table_name": tableName, + }, + } + + _, _ = s.notificationProcessor.Send(ctx, notifReq) +} func (s *OrderServiceImpl) createIngredientTransactions(ctx context.Context, orderID uuid.UUID, orderItems []models.OrderItemResponse) ([]*contract.CreateOrderIngredientTransactionRequest, error) { appCtx := appcontext.FromGinContext(ctx) organizationID := appCtx.OrganizationID