self-order+notification #5
@ -409,7 +409,7 @@ func (a *App) initServices(processors *processors, repos *repositories, cfg *con
|
|||||||
productService := service.NewProductService(processors.productProcessor)
|
productService := service.NewProductService(processors.productProcessor)
|
||||||
productVariantService := service.NewProductVariantService(processors.productVariantProcessor)
|
productVariantService := service.NewProductVariantService(processors.productVariantProcessor)
|
||||||
inventoryService := service.NewInventoryService(processors.inventoryProcessor)
|
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)
|
paymentMethodService := service.NewPaymentMethodService(processors.paymentMethodProcessor)
|
||||||
fileService := service.NewFileServiceImpl(processors.fileProcessor)
|
fileService := service.NewFileServiceImpl(processors.fileProcessor)
|
||||||
var customerService service.CustomerService = service.NewCustomerService(processors.customerProcessor)
|
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)
|
notificationService := service.NewNotificationService(processors.notificationProcessor)
|
||||||
|
|
||||||
// Update order service with order ingredient transaction service
|
// 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{
|
return &services{
|
||||||
userService: service.NewUserService(processors.userProcessor),
|
userService: service.NewUserService(processors.userProcessor),
|
||||||
|
|||||||
@ -61,6 +61,17 @@ func (r *UserRepositoryImpl) GetActiveUsers(ctx context.Context, organizationID
|
|||||||
return users, err
|
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 {
|
func (r *UserRepositoryImpl) Update(ctx context.Context, user *entities.User) error {
|
||||||
return r.db.WithContext(ctx).Save(user).Error
|
return r.db.WithContext(ctx).Save(user).Error
|
||||||
}
|
}
|
||||||
|
|||||||
@ -16,6 +16,11 @@ import (
|
|||||||
"github.com/google/uuid"
|
"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 {
|
type OrderService interface {
|
||||||
CreateOrder(ctx context.Context, req *models.CreateOrderRequest, organizationID uuid.UUID) (*models.OrderResponse, error)
|
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)
|
AddToOrder(ctx context.Context, orderID uuid.UUID, req *models.AddToOrderRequest) (*models.AddToOrderResponse, error)
|
||||||
@ -38,9 +43,11 @@ type OrderServiceImpl struct {
|
|||||||
productRecipeRepo repository.ProductRecipeRepository
|
productRecipeRepo repository.ProductRecipeRepository
|
||||||
txManager *repository.TxManager
|
txManager *repository.TxManager
|
||||||
sessionRepo repository.SessionRepository
|
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{
|
return &OrderServiceImpl{
|
||||||
orderProcessor: orderProcessor,
|
orderProcessor: orderProcessor,
|
||||||
tableRepo: tableRepo,
|
tableRepo: tableRepo,
|
||||||
@ -49,6 +56,8 @@ func NewOrderServiceImpl(orderProcessor processor.OrderProcessor, tableRepo repo
|
|||||||
productRecipeRepo: productRecipeRepo,
|
productRecipeRepo: productRecipeRepo,
|
||||||
txManager: txManager,
|
txManager: txManager,
|
||||||
sessionRepo: sessionRepo,
|
sessionRepo: sessionRepo,
|
||||||
|
notificationProcessor: notificationProcessor,
|
||||||
|
userRepo: userRepo,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -104,10 +113,73 @@ func (s *OrderServiceImpl) CreateOrder(ctx context.Context, req *models.CreateOr
|
|||||||
return nil, err
|
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
|
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) {
|
func (s *OrderServiceImpl) createIngredientTransactions(ctx context.Context, orderID uuid.UUID, orderItems []models.OrderItemResponse) ([]*contract.CreateOrderIngredientTransactionRequest, error) {
|
||||||
appCtx := appcontext.FromGinContext(ctx)
|
appCtx := appcontext.FromGinContext(ctx)
|
||||||
organizationID := appCtx.OrganizationID
|
organizationID := appCtx.OrganizationID
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user