Self Order - Feature
This commit is contained in:
parent
b993da898f
commit
d2296e5b13
@ -42,8 +42,16 @@ func (a *App) Initialize(cfg *config.Config) error {
|
||||
processors := a.initProcessors(cfg, repos)
|
||||
services := a.initServices(processors, repos, cfg)
|
||||
validators := a.initValidators()
|
||||
middleware := a.initMiddleware(services, cfg, repos)
|
||||
middleware := a.initMiddleware(services, cfg)
|
||||
healthHandler := handler.NewHealthHandler()
|
||||
selfOrderHandler := handler.NewSelfOrderHandler(
|
||||
services.orderService,
|
||||
services.categoryService,
|
||||
services.productService,
|
||||
repos.tableRepo,
|
||||
repos.outletRepo,
|
||||
repos.userRepo,
|
||||
)
|
||||
|
||||
a.router = router.NewRouter(
|
||||
cfg,
|
||||
@ -105,14 +113,7 @@ func (a *App) Initialize(cfg *config.Config) error {
|
||||
services.customerPointsService,
|
||||
services.spinGameService,
|
||||
middleware.customerAuthMiddleware,
|
||||
handler.NewSelfOrderHandler(
|
||||
services.orderService,
|
||||
services.productService,
|
||||
repos.customerRepo,
|
||||
repos.userRepo,
|
||||
repos.outletRepo,
|
||||
),
|
||||
middleware.selfOrderMiddleware,
|
||||
selfOrderHandler,
|
||||
)
|
||||
|
||||
return nil
|
||||
@ -449,14 +450,12 @@ func (a *App) initServices(processors *processors, repos *repositories, cfg *con
|
||||
type middlewares struct {
|
||||
authMiddleware *middleware.AuthMiddleware
|
||||
customerAuthMiddleware *middleware.CustomerAuthMiddleware
|
||||
selfOrderMiddleware *middleware.SelfOrderMiddleware
|
||||
}
|
||||
|
||||
func (a *App) initMiddleware(services *services, cfg *config.Config, repos *repositories) *middlewares {
|
||||
func (a *App) initMiddleware(services *services, cfg *config.Config) *middlewares {
|
||||
return &middlewares{
|
||||
authMiddleware: middleware.NewAuthMiddleware(services.authService),
|
||||
customerAuthMiddleware: middleware.NewCustomerAuthMiddleware(cfg.GetCustomerJWTSecret()),
|
||||
selfOrderMiddleware: middleware.NewSelfOrderMiddleware(repos.tableRepo),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -1,51 +1,54 @@
|
||||
package contract
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/google/uuid"
|
||||
)
|
||||
|
||||
type CreateSelfOrderRequest struct {
|
||||
CustomerName string `json:"customer_name" validate:"required,min=1,max=255"`
|
||||
PhoneNumber *string `json:"phone_number,omitempty" validate:"omitempty"`
|
||||
OrderItems []SelfOrderItemRequest `json:"order_items" validate:"required,min=1,dive"`
|
||||
Notes *string `json:"notes,omitempty" validate:"omitempty,max=1000"`
|
||||
}
|
||||
|
||||
type SelfOrderItemRequest struct {
|
||||
ProductID uuid.UUID `json:"product_id" validate:"required"`
|
||||
ProductVariantID *uuid.UUID `json:"product_variant_id,omitempty"`
|
||||
Quantity int `json:"quantity" validate:"required,min=1"`
|
||||
Notes *string `json:"notes,omitempty" validate:"omitempty,max=500"`
|
||||
}
|
||||
|
||||
type SelfOrderResponse struct {
|
||||
OrderID uuid.UUID `json:"order_id"`
|
||||
OrderNumber string `json:"order_number"`
|
||||
TableID uuid.UUID `json:"table_id"`
|
||||
TableName string `json:"table_name"`
|
||||
OutletID uuid.UUID `json:"outlet_id"`
|
||||
OutletName string `json:"outlet_name"`
|
||||
CustomerName string `json:"customer_name"`
|
||||
OrderItems []OrderItemResponse `json:"order_items"`
|
||||
Subtotal float64 `json:"subtotal"`
|
||||
TaxAmount float64 `json:"tax_amount"`
|
||||
TotalAmount float64 `json:"total_amount"`
|
||||
Status string `json:"status"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
type SelfOrderMenuRequest struct {
|
||||
TableID uuid.UUID `json:"table_id" validate:"required"`
|
||||
CustomerName string `json:"customer_name" validate:"required"`
|
||||
Phone *string `json:"phone,omitempty"`
|
||||
}
|
||||
|
||||
type SelfOrderMenuResponse struct {
|
||||
OutletID uuid.UUID `json:"outlet_id"`
|
||||
OutletName string `json:"outlet_name"`
|
||||
TableID uuid.UUID `json:"table_id"`
|
||||
TableName string `json:"table_name"`
|
||||
Organization OrganizationMenuInfo `json:"organization"`
|
||||
Products ListProductsResponse `json:"products"`
|
||||
Categories []SelfOrderMenuCategory `json:"categories"`
|
||||
}
|
||||
|
||||
type OrganizationMenuInfo struct {
|
||||
type SelfOrderMenuCategory struct {
|
||||
ID uuid.UUID `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Description *string `json:"description,omitempty"`
|
||||
Order int `json:"order"`
|
||||
Products []SelfOrderMenuItem `json:"products"`
|
||||
}
|
||||
|
||||
type SelfOrderMenuItem struct {
|
||||
ID uuid.UUID `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Description *string `json:"description,omitempty"`
|
||||
Price float64 `json:"price"`
|
||||
ImageURL *string `json:"image_url,omitempty"`
|
||||
Variants []SelfOrderMenuVariant `json:"variants,omitempty"`
|
||||
}
|
||||
|
||||
type SelfOrderMenuVariant struct {
|
||||
ID uuid.UUID `json:"id"`
|
||||
Name string `json:"name"`
|
||||
PriceModifier float64 `json:"price_modifier"`
|
||||
}
|
||||
|
||||
type SelfOrderCreateOrderRequest struct {
|
||||
TableID uuid.UUID `json:"table_id" validate:"required"`
|
||||
CustomerName string `json:"customer_name" validate:"required"`
|
||||
Phone *string `json:"phone,omitempty"`
|
||||
OrderItems []SelfOrderCreateOrderItem `json:"order_items" validate:"required,min=1,dive"`
|
||||
}
|
||||
|
||||
type SelfOrderCreateOrderItem struct {
|
||||
ProductID uuid.UUID `json:"product_id" validate:"required"`
|
||||
ProductVariantID *uuid.UUID `json:"product_variant_id,omitempty"`
|
||||
Quantity int `json:"quantity" validate:"required,min=1"`
|
||||
Notes *string `json:"notes,omitempty"`
|
||||
}
|
||||
|
||||
@ -3,11 +3,16 @@ package handler
|
||||
import (
|
||||
"apskel-pos-be/internal/constants"
|
||||
"apskel-pos-be/internal/contract"
|
||||
"apskel-pos-be/internal/entities"
|
||||
"apskel-pos-be/internal/logger"
|
||||
"apskel-pos-be/internal/models"
|
||||
"apskel-pos-be/internal/processor"
|
||||
"apskel-pos-be/internal/repository"
|
||||
"apskel-pos-be/internal/service"
|
||||
"apskel-pos-be/internal/transformer"
|
||||
"apskel-pos-be/internal/util"
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/google/uuid"
|
||||
@ -15,102 +20,179 @@ import (
|
||||
|
||||
type SelfOrderHandler struct {
|
||||
orderService service.OrderService
|
||||
categoryService service.CategoryService
|
||||
productService service.ProductService
|
||||
customerRepo *repository.CustomerRepository
|
||||
userRepo *repository.UserRepositoryImpl
|
||||
outletRepo *repository.OutletRepositoryImpl
|
||||
tableRepo repository.TableRepositoryInterface
|
||||
outletRepo processor.OutletRepository
|
||||
userRepo processor.UserRepository
|
||||
}
|
||||
|
||||
func NewSelfOrderHandler(
|
||||
orderService service.OrderService,
|
||||
categoryService service.CategoryService,
|
||||
productService service.ProductService,
|
||||
customerRepo *repository.CustomerRepository,
|
||||
userRepo *repository.UserRepositoryImpl,
|
||||
outletRepo *repository.OutletRepositoryImpl,
|
||||
tableRepo repository.TableRepositoryInterface,
|
||||
outletRepo processor.OutletRepository,
|
||||
userRepo processor.UserRepository,
|
||||
) *SelfOrderHandler {
|
||||
return &SelfOrderHandler{
|
||||
orderService: orderService,
|
||||
categoryService: categoryService,
|
||||
productService: productService,
|
||||
customerRepo: customerRepo,
|
||||
userRepo: userRepo,
|
||||
tableRepo: tableRepo,
|
||||
outletRepo: outletRepo,
|
||||
userRepo: userRepo,
|
||||
}
|
||||
}
|
||||
|
||||
func (h *SelfOrderHandler) GetMenu(c *gin.Context) {
|
||||
ctx := c.Request.Context()
|
||||
|
||||
organizationIDStr, _ := c.Get("self_order_organization_id")
|
||||
outletIDStr, _ := c.Get("self_order_outlet_id")
|
||||
tableIDStr, _ := c.Get("self_order_table_id")
|
||||
tableName, _ := c.Get("self_order_table_name")
|
||||
|
||||
organizationID, err := uuid.Parse(organizationIDStr.(string))
|
||||
if err != nil {
|
||||
var req contract.SelfOrderMenuRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
logger.FromContext(ctx).WithError(err).Error("SelfOrderHandler::GetMenu -> request binding failed")
|
||||
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{
|
||||
contract.NewResponseError(constants.InternalServerErrorCode, constants.SelfOrderEntity, "invalid organization ID"),
|
||||
contract.NewResponseError(constants.MissingFieldErrorCode, constants.RequestEntity, err.Error()),
|
||||
}), "SelfOrderHandler::GetMenu")
|
||||
return
|
||||
}
|
||||
|
||||
outletID, err := uuid.Parse(outletIDStr.(string))
|
||||
if err != nil {
|
||||
if req.TableID == uuid.Nil {
|
||||
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{
|
||||
contract.NewResponseError(constants.InternalServerErrorCode, constants.SelfOrderEntity, "invalid outlet ID"),
|
||||
contract.NewResponseError(constants.MissingFieldErrorCode, constants.RequestEntity, "table_id is required"),
|
||||
}), "SelfOrderHandler::GetMenu")
|
||||
return
|
||||
}
|
||||
|
||||
tableID, _ := uuid.Parse(tableIDStr.(string))
|
||||
if req.CustomerName == "" {
|
||||
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{
|
||||
contract.NewResponseError(constants.MissingFieldErrorCode, constants.RequestEntity, "customer_name is required"),
|
||||
}), "SelfOrderHandler::GetMenu")
|
||||
return
|
||||
}
|
||||
|
||||
table, err := h.tableRepo.GetByID(ctx, req.TableID)
|
||||
if err != nil {
|
||||
logger.FromContext(ctx).WithError(err).Error("SelfOrderHandler::GetMenu -> table not found")
|
||||
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{
|
||||
contract.NewResponseError(constants.NotFoundErrorCode, constants.TableEntity, "table not found"),
|
||||
}), "SelfOrderHandler::GetMenu")
|
||||
return
|
||||
}
|
||||
|
||||
if !table.IsActive {
|
||||
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{
|
||||
contract.NewResponseError(constants.ValidationErrorCode, constants.TableEntity, "table is not active"),
|
||||
}), "SelfOrderHandler::GetMenu")
|
||||
return
|
||||
}
|
||||
|
||||
outlet, err := h.outletRepo.GetByID(ctx, table.OutletID)
|
||||
if err != nil {
|
||||
logger.FromContext(ctx).WithError(err).Error("SelfOrderHandler::GetMenu -> outlet not found")
|
||||
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{
|
||||
contract.NewResponseError(constants.NotFoundErrorCode, constants.OrderServiceEntity, "outlet not found"),
|
||||
}), "SelfOrderHandler::GetMenu")
|
||||
return
|
||||
}
|
||||
|
||||
isActive := true
|
||||
req := &contract.ListProductsRequest{
|
||||
OrganizationID: &organizationID,
|
||||
IsActive: &isActive,
|
||||
catResp := h.categoryService.ListCategories(ctx, &contract.ListCategoriesRequest{
|
||||
OrganizationID: &table.OrganizationID,
|
||||
Page: 1,
|
||||
Limit: 100,
|
||||
})
|
||||
if catResp.HasErrors() {
|
||||
logger.FromContext(ctx).WithError(catResp.GetErrors()[0]).Error("SelfOrderHandler::GetMenu -> failed to list categories")
|
||||
util.HandleResponse(c.Writer, c.Request, catResp, "SelfOrderHandler::GetMenu")
|
||||
return
|
||||
}
|
||||
|
||||
productsResponse := h.productService.ListProducts(ctx, req)
|
||||
|
||||
outlet, outletErr := h.outletRepo.GetByID(ctx, outletID)
|
||||
outletName := ""
|
||||
if outletErr == nil && outlet != nil {
|
||||
outletName = outlet.Name
|
||||
prodResp := h.productService.ListProducts(ctx, &contract.ListProductsRequest{
|
||||
OrganizationID: &table.OrganizationID,
|
||||
IsActive: &isActive,
|
||||
Page: 1,
|
||||
Limit: 1000,
|
||||
})
|
||||
if prodResp.HasErrors() {
|
||||
logger.FromContext(ctx).WithError(prodResp.GetErrors()[0]).Error("SelfOrderHandler::GetMenu -> failed to list products")
|
||||
util.HandleResponse(c.Writer, c.Request, prodResp, "SelfOrderHandler::GetMenu")
|
||||
return
|
||||
}
|
||||
|
||||
menuResponse := &contract.SelfOrderMenuResponse{
|
||||
OutletID: outletID,
|
||||
OutletName: outletName,
|
||||
TableID: tableID,
|
||||
TableName: tableName.(string),
|
||||
Organization: contract.OrganizationMenuInfo{
|
||||
ID: organizationID,
|
||||
},
|
||||
catList, ok := catResp.Data.(*contract.ListCategoriesResponse)
|
||||
if !ok {
|
||||
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{
|
||||
contract.NewResponseError(constants.InternalServerErrorCode, constants.CategoryServiceEntity, "unexpected categories response type"),
|
||||
}), "SelfOrderHandler::GetMenu")
|
||||
return
|
||||
}
|
||||
|
||||
if productsResponse != nil {
|
||||
if data, ok := productsResponse.Data.(*contract.ListProductsResponse); ok {
|
||||
menuResponse.Products = *data
|
||||
}
|
||||
prodList, ok := prodResp.Data.(*contract.ListProductsResponse)
|
||||
if !ok {
|
||||
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{
|
||||
contract.NewResponseError(constants.InternalServerErrorCode, constants.ProductServiceEntity, "unexpected products response type"),
|
||||
}), "SelfOrderHandler::GetMenu")
|
||||
return
|
||||
}
|
||||
|
||||
util.HandleResponse(c.Writer, c.Request, contract.BuildSuccessResponse(menuResponse), "SelfOrderHandler::GetMenu")
|
||||
menu := h.buildMenuResponse(outlet, table, catList.Categories, prodList.Products)
|
||||
util.HandleResponse(c.Writer, c.Request, contract.BuildSuccessResponse(menu), "SelfOrderHandler::GetMenu")
|
||||
}
|
||||
|
||||
func (h *SelfOrderHandler) buildMenuResponse(
|
||||
outlet *entities.Outlet,
|
||||
table *entities.Table,
|
||||
categories []contract.CategoryResponse,
|
||||
products []contract.ProductResponse,
|
||||
) *contract.SelfOrderMenuResponse {
|
||||
productMap := make(map[uuid.UUID][]contract.ProductResponse)
|
||||
for _, p := range products {
|
||||
productMap[p.CategoryID] = append(productMap[p.CategoryID], p)
|
||||
}
|
||||
|
||||
menuCategories := make([]contract.SelfOrderMenuCategory, 0, len(categories))
|
||||
for _, cat := range categories {
|
||||
menuItems := make([]contract.SelfOrderMenuItem, 0)
|
||||
if prods, ok := productMap[cat.ID]; ok {
|
||||
for _, p := range prods {
|
||||
item := contract.SelfOrderMenuItem{
|
||||
ID: p.ID,
|
||||
Name: p.Name,
|
||||
Description: p.Description,
|
||||
Price: p.Price,
|
||||
ImageURL: p.ImageURL,
|
||||
}
|
||||
for _, v := range p.Variants {
|
||||
item.Variants = append(item.Variants, contract.SelfOrderMenuVariant{
|
||||
ID: v.ID,
|
||||
Name: v.Name,
|
||||
PriceModifier: v.PriceModifier,
|
||||
})
|
||||
}
|
||||
menuItems = append(menuItems, item)
|
||||
}
|
||||
}
|
||||
menuCategories = append(menuCategories, contract.SelfOrderMenuCategory{
|
||||
ID: cat.ID,
|
||||
Name: cat.Name,
|
||||
Description: cat.Description,
|
||||
Order: cat.Order,
|
||||
Products: menuItems,
|
||||
})
|
||||
}
|
||||
|
||||
return &contract.SelfOrderMenuResponse{
|
||||
OutletName: outlet.Name,
|
||||
TableName: table.TableName,
|
||||
Categories: menuCategories,
|
||||
}
|
||||
}
|
||||
|
||||
func (h *SelfOrderHandler) CreateOrder(c *gin.Context) {
|
||||
ctx := c.Request.Context()
|
||||
|
||||
organizationIDStr, _ := c.Get("self_order_organization_id")
|
||||
outletIDStr, _ := c.Get("self_order_outlet_id")
|
||||
tableIDStr, _ := c.Get("self_order_table_id")
|
||||
tableName, _ := c.Get("self_order_table_name")
|
||||
|
||||
organizationID, _ := uuid.Parse(organizationIDStr.(string))
|
||||
outletID, _ := uuid.Parse(outletIDStr.(string))
|
||||
tableID, _ := uuid.Parse(tableIDStr.(string))
|
||||
|
||||
var req contract.CreateSelfOrderRequest
|
||||
var req contract.SelfOrderCreateOrderRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
logger.FromContext(ctx).WithError(err).Error("SelfOrderHandler::CreateOrder -> request binding failed")
|
||||
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{
|
||||
@ -119,121 +201,108 @@ func (h *SelfOrderHandler) CreateOrder(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
if req.CustomerName == "" {
|
||||
if err := h.validateCreateOrderRequest(&req); err != nil {
|
||||
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{
|
||||
contract.NewResponseError(constants.ValidationErrorCode, constants.SelfOrderEntity, "customer_name is required"),
|
||||
contract.NewResponseError(constants.ValidationErrorCode, constants.RequestEntity, err.Error()),
|
||||
}), "SelfOrderHandler::CreateOrder")
|
||||
return
|
||||
}
|
||||
|
||||
if len(req.OrderItems) == 0 {
|
||||
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{
|
||||
contract.NewResponseError(constants.ValidationErrorCode, constants.SelfOrderEntity, "at least one order item is required"),
|
||||
}), "SelfOrderHandler::CreateOrder")
|
||||
return
|
||||
}
|
||||
|
||||
adminUser, err := h.userRepo.GetAdminByOrganizationID(ctx, organizationID)
|
||||
table, err := h.tableRepo.GetByID(ctx, req.TableID)
|
||||
if err != nil {
|
||||
logger.FromContext(ctx).WithError(err).Error("SelfOrderHandler::CreateOrder -> failed to get admin user")
|
||||
logger.FromContext(ctx).WithError(err).Error("SelfOrderHandler::CreateOrder -> table not found")
|
||||
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{
|
||||
contract.NewResponseError(constants.InternalServerErrorCode, constants.SelfOrderEntity, "failed to resolve system user"),
|
||||
contract.NewResponseError(constants.NotFoundErrorCode, constants.TableEntity, "table not found"),
|
||||
}), "SelfOrderHandler::CreateOrder")
|
||||
return
|
||||
}
|
||||
|
||||
var customerID *uuid.UUID
|
||||
if req.PhoneNumber != nil && *req.PhoneNumber != "" {
|
||||
customer, err := h.customerRepo.GetByPhoneNumber(ctx, *req.PhoneNumber)
|
||||
if !table.IsActive || !table.IsAvailable() {
|
||||
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{
|
||||
contract.NewResponseError(constants.ValidationErrorCode, constants.TableEntity, "table is not available for ordering"),
|
||||
}), "SelfOrderHandler::CreateOrder")
|
||||
return
|
||||
}
|
||||
|
||||
userID, err := h.resolveOrgUser(ctx, table.OrganizationID)
|
||||
if err != nil {
|
||||
logger.FromContext(ctx).WithError(err).Error("SelfOrderHandler::CreateOrder -> failed to lookup customer by phone")
|
||||
}
|
||||
if customer != nil {
|
||||
customerID = &customer.ID
|
||||
}
|
||||
logger.FromContext(ctx).WithError(err).Error("SelfOrderHandler::CreateOrder -> failed to resolve org user")
|
||||
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{
|
||||
contract.NewResponseError(constants.InternalServerErrorCode, constants.OrderServiceEntity, "failed to create self-order"),
|
||||
}), "SelfOrderHandler::CreateOrder")
|
||||
return
|
||||
}
|
||||
|
||||
metadata := map[string]interface{}{
|
||||
"source": string(constants.OrderSourceSelfOrder),
|
||||
"customer_phone": "",
|
||||
}
|
||||
if req.PhoneNumber != nil {
|
||||
metadata["customer_phone"] = *req.PhoneNumber
|
||||
}
|
||||
|
||||
orderItems := make([]models.CreateOrderItemRequest, len(req.OrderItems))
|
||||
for i, item := range req.OrderItems {
|
||||
orderItems[i] = models.CreateOrderItemRequest{
|
||||
orderItems := make([]models.CreateOrderItemRequest, 0, len(req.OrderItems))
|
||||
for _, item := range req.OrderItems {
|
||||
orderItems = append(orderItems, models.CreateOrderItemRequest{
|
||||
ProductID: item.ProductID,
|
||||
ProductVariantID: item.ProductVariantID,
|
||||
Quantity: item.Quantity,
|
||||
Notes: item.Notes,
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
tableNameStr := tableName.(string)
|
||||
metadata := make(map[string]interface{})
|
||||
metadata["self_order"] = true
|
||||
metadata["customer_name"] = req.CustomerName
|
||||
if req.Phone != nil {
|
||||
metadata["customer_phone"] = *req.Phone
|
||||
}
|
||||
|
||||
tableID := req.TableID
|
||||
modelReq := &models.CreateOrderRequest{
|
||||
OutletID: outletID,
|
||||
UserID: adminUser.ID,
|
||||
CustomerID: customerID,
|
||||
OutletID: table.OutletID,
|
||||
UserID: userID,
|
||||
TableID: &tableID,
|
||||
TableNumber: &tableNameStr,
|
||||
TableNumber: &table.TableName,
|
||||
OrderType: constants.OrderTypeDineIn,
|
||||
OrderItems: orderItems,
|
||||
Notes: req.Notes,
|
||||
CustomerName: &req.CustomerName,
|
||||
Metadata: metadata,
|
||||
}
|
||||
|
||||
response, err := h.orderService.CreateOrder(ctx, modelReq, organizationID)
|
||||
response, err := h.orderService.CreateOrder(ctx, modelReq, table.OrganizationID)
|
||||
if err != nil {
|
||||
logger.FromContext(ctx).WithError(err).Error("SelfOrderHandler::CreateOrder -> failed to create order")
|
||||
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{
|
||||
contract.NewResponseError(constants.InternalServerErrorCode, constants.SelfOrderEntity, err.Error()),
|
||||
contract.NewResponseError(constants.InternalServerErrorCode, constants.OrderServiceEntity, err.Error()),
|
||||
}), "SelfOrderHandler::CreateOrder")
|
||||
return
|
||||
}
|
||||
|
||||
outlet, _ := h.outletRepo.GetByID(ctx, outletID)
|
||||
outletName := ""
|
||||
if outlet != nil {
|
||||
outletName = outlet.Name
|
||||
contractResp := transformer.OrderModelToContract(response)
|
||||
util.HandleResponse(c.Writer, c.Request, contract.BuildSuccessResponse(contractResp), "SelfOrderHandler::CreateOrder")
|
||||
}
|
||||
|
||||
orderItemsResp := make([]contract.OrderItemResponse, len(response.OrderItems))
|
||||
for i, item := range response.OrderItems {
|
||||
orderItemsResp[i] = contract.OrderItemResponse{
|
||||
ID: item.ID,
|
||||
OrderID: item.OrderID,
|
||||
ProductID: item.ProductID,
|
||||
ProductName: item.ProductName,
|
||||
ProductVariantID: item.ProductVariantID,
|
||||
ProductVariantName: item.ProductVariantName,
|
||||
Quantity: item.Quantity,
|
||||
UnitPrice: item.UnitPrice,
|
||||
TotalPrice: item.TotalPrice,
|
||||
Notes: item.Notes,
|
||||
Status: string(item.Status),
|
||||
CreatedAt: item.CreatedAt,
|
||||
UpdatedAt: item.UpdatedAt,
|
||||
func (h *SelfOrderHandler) validateCreateOrderRequest(req *contract.SelfOrderCreateOrderRequest) error {
|
||||
if req.TableID == uuid.Nil {
|
||||
return fmt.Errorf("table_id is required")
|
||||
}
|
||||
if req.CustomerName == "" {
|
||||
return fmt.Errorf("customer_name is required")
|
||||
}
|
||||
if len(req.OrderItems) == 0 {
|
||||
return fmt.Errorf("at least one order item is required")
|
||||
}
|
||||
for i, item := range req.OrderItems {
|
||||
if item.ProductID == uuid.Nil {
|
||||
return fmt.Errorf("product_id is required for item %d", i+1)
|
||||
}
|
||||
if item.Quantity <= 0 {
|
||||
return fmt.Errorf("quantity must be greater than zero for item %d", i+1)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
selfOrderResp := &contract.SelfOrderResponse{
|
||||
OrderID: response.ID,
|
||||
OrderNumber: response.OrderNumber,
|
||||
TableID: tableID,
|
||||
TableName: tableNameStr,
|
||||
OutletID: outletID,
|
||||
OutletName: outletName,
|
||||
CustomerName: req.CustomerName,
|
||||
OrderItems: orderItemsResp,
|
||||
Subtotal: response.Subtotal,
|
||||
TaxAmount: response.TaxAmount,
|
||||
TotalAmount: response.TotalAmount,
|
||||
Status: string(response.Status),
|
||||
CreatedAt: response.CreatedAt,
|
||||
func (h *SelfOrderHandler) resolveOrgUser(ctx context.Context, organizationID uuid.UUID) (uuid.UUID, error) {
|
||||
users, err := h.userRepo.GetByOrganizationID(ctx, organizationID)
|
||||
if err != nil {
|
||||
return uuid.Nil, fmt.Errorf("failed to get users for organization: %w", err)
|
||||
}
|
||||
|
||||
util.HandleResponse(c.Writer, c.Request, contract.BuildSuccessResponse(selfOrderResp), "SelfOrderHandler::CreateOrder")
|
||||
if len(users) == 0 {
|
||||
return uuid.Nil, fmt.Errorf("no users found for organization")
|
||||
}
|
||||
return users[0].ID, nil
|
||||
}
|
||||
|
||||
@ -49,10 +49,9 @@ type Router struct {
|
||||
selfOrderHandler *handler.SelfOrderHandler
|
||||
authMiddleware *middleware.AuthMiddleware
|
||||
customerAuthMiddleware *middleware.CustomerAuthMiddleware
|
||||
selfOrderMiddleware *middleware.SelfOrderMiddleware
|
||||
}
|
||||
|
||||
func NewRouter(cfg *config.Config, healthHandler *handler.HealthHandler, authService service.AuthService, authMiddleware *middleware.AuthMiddleware, userService *service.UserServiceImpl, userValidator *validator.UserValidatorImpl, organizationService service.OrganizationService, organizationValidator validator.OrganizationValidator, outletService service.OutletService, outletValidator validator.OutletValidator, outletSettingService service.OutletSettingService, categoryService service.CategoryService, categoryValidator validator.CategoryValidator, productService service.ProductService, productValidator validator.ProductValidator, productVariantService service.ProductVariantService, productVariantValidator validator.ProductVariantValidator, inventoryService service.InventoryService, inventoryValidator validator.InventoryValidator, orderService service.OrderService, orderValidator validator.OrderValidator, fileService service.FileService, fileValidator validator.FileValidator, customerService service.CustomerService, customerValidator validator.CustomerValidator, paymentMethodService service.PaymentMethodService, paymentMethodValidator validator.PaymentMethodValidator, analyticsService *service.AnalyticsServiceImpl, reportService service.ReportService, tableService *service.TableServiceImpl, tableValidator *validator.TableValidator, unitService handler.UnitService, ingredientService handler.IngredientService, productRecipeService service.ProductRecipeService, vendorService service.VendorService, vendorValidator validator.VendorValidator, purchaseOrderService service.PurchaseOrderService, purchaseOrderValidator validator.PurchaseOrderValidator, unitConverterService service.IngredientUnitConverterService, unitConverterValidator validator.IngredientUnitConverterValidator, chartOfAccountTypeService service.ChartOfAccountTypeService, chartOfAccountTypeValidator validator.ChartOfAccountTypeValidator, chartOfAccountService service.ChartOfAccountService, chartOfAccountValidator validator.ChartOfAccountValidator, accountService service.AccountService, accountValidator validator.AccountValidator, orderIngredientTransactionService service.OrderIngredientTransactionService, orderIngredientTransactionValidator validator.OrderIngredientTransactionValidator, gamificationService service.GamificationService, gamificationValidator validator.GamificationValidator, rewardService service.RewardService, rewardValidator validator.RewardValidator, campaignService service.CampaignService, campaignValidator validator.CampaignValidator, customerAuthService service.CustomerAuthService, customerAuthValidator validator.CustomerAuthValidator, customerPointsService service.CustomerPointsService, spinGameService service.SpinGameService, customerAuthMiddleware *middleware.CustomerAuthMiddleware, selfOrderHandler *handler.SelfOrderHandler, selfOrderMiddleware *middleware.SelfOrderMiddleware) *Router {
|
||||
func NewRouter(cfg *config.Config, healthHandler *handler.HealthHandler, authService service.AuthService, authMiddleware *middleware.AuthMiddleware, userService *service.UserServiceImpl, userValidator *validator.UserValidatorImpl, organizationService service.OrganizationService, organizationValidator validator.OrganizationValidator, outletService service.OutletService, outletValidator validator.OutletValidator, outletSettingService service.OutletSettingService, categoryService service.CategoryService, categoryValidator validator.CategoryValidator, productService service.ProductService, productValidator validator.ProductValidator, productVariantService service.ProductVariantService, productVariantValidator validator.ProductVariantValidator, inventoryService service.InventoryService, inventoryValidator validator.InventoryValidator, orderService service.OrderService, orderValidator validator.OrderValidator, fileService service.FileService, fileValidator validator.FileValidator, customerService service.CustomerService, customerValidator validator.CustomerValidator, paymentMethodService service.PaymentMethodService, paymentMethodValidator validator.PaymentMethodValidator, analyticsService *service.AnalyticsServiceImpl, reportService service.ReportService, tableService *service.TableServiceImpl, tableValidator *validator.TableValidator, unitService handler.UnitService, ingredientService handler.IngredientService, productRecipeService service.ProductRecipeService, vendorService service.VendorService, vendorValidator validator.VendorValidator, purchaseOrderService service.PurchaseOrderService, purchaseOrderValidator validator.PurchaseOrderValidator, unitConverterService service.IngredientUnitConverterService, unitConverterValidator validator.IngredientUnitConverterValidator, chartOfAccountTypeService service.ChartOfAccountTypeService, chartOfAccountTypeValidator validator.ChartOfAccountTypeValidator, chartOfAccountService service.ChartOfAccountService, chartOfAccountValidator validator.ChartOfAccountValidator, accountService service.AccountService, accountValidator validator.AccountValidator, orderIngredientTransactionService service.OrderIngredientTransactionService, orderIngredientTransactionValidator validator.OrderIngredientTransactionValidator, gamificationService service.GamificationService, gamificationValidator validator.GamificationValidator, rewardService service.RewardService, rewardValidator validator.RewardValidator, campaignService service.CampaignService, campaignValidator validator.CampaignValidator, customerAuthService service.CustomerAuthService, customerAuthValidator validator.CustomerAuthValidator, customerPointsService service.CustomerPointsService, spinGameService service.SpinGameService, customerAuthMiddleware *middleware.CustomerAuthMiddleware, selfOrderHandler *handler.SelfOrderHandler) *Router {
|
||||
|
||||
return &Router{
|
||||
config: cfg,
|
||||
@ -90,9 +89,8 @@ func NewRouter(cfg *config.Config, healthHandler *handler.HealthHandler, authSer
|
||||
spinGameHandler: handler.NewSpinGameHandler(spinGameService),
|
||||
authMiddleware: authMiddleware,
|
||||
customerAuthMiddleware: customerAuthMiddleware,
|
||||
selfOrderHandler: selfOrderHandler,
|
||||
selfOrderMiddleware: selfOrderMiddleware,
|
||||
productVariantHandler: handler.NewProductVariantHandler(productVariantService, productVariantValidator),
|
||||
selfOrderHandler: selfOrderHandler,
|
||||
}
|
||||
}
|
||||
|
||||
@ -149,11 +147,9 @@ func (r *Router) addAppRoutes(rg *gin.Engine) {
|
||||
customer.POST("/spin", r.spinGameHandler.PlaySpinGame)
|
||||
}
|
||||
|
||||
// Self-order routes (public, token-based table identification)
|
||||
selfOrder := v1.Group("/self-order")
|
||||
selfOrder.Use(r.selfOrderMiddleware.ResolveToken())
|
||||
{
|
||||
selfOrder.GET("/menu", r.selfOrderHandler.GetMenu)
|
||||
selfOrder.POST("/menu", r.selfOrderHandler.GetMenu)
|
||||
selfOrder.POST("/order", r.selfOrderHandler.CreateOrder)
|
||||
}
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user