package handler import ( "apskel-pos-be/internal/constants" "apskel-pos-be/internal/contract" "apskel-pos-be/internal/logger" "apskel-pos-be/internal/models" "apskel-pos-be/internal/repository" "apskel-pos-be/internal/service" "apskel-pos-be/internal/util" "github.com/gin-gonic/gin" "github.com/google/uuid" ) type SelfOrderHandler struct { orderService service.OrderService productService service.ProductService customerRepo *repository.CustomerRepository userRepo *repository.UserRepositoryImpl outletRepo *repository.OutletRepositoryImpl } func NewSelfOrderHandler( orderService service.OrderService, productService service.ProductService, customerRepo *repository.CustomerRepository, userRepo *repository.UserRepositoryImpl, outletRepo *repository.OutletRepositoryImpl, ) *SelfOrderHandler { return &SelfOrderHandler{ orderService: orderService, productService: productService, customerRepo: customerRepo, userRepo: userRepo, outletRepo: outletRepo, } } 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 { util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{ contract.NewResponseError(constants.InternalServerErrorCode, constants.SelfOrderEntity, "invalid organization ID"), }), "SelfOrderHandler::GetMenu") return } outletID, err := uuid.Parse(outletIDStr.(string)) if err != nil { util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{ contract.NewResponseError(constants.InternalServerErrorCode, constants.SelfOrderEntity, "invalid outlet ID"), }), "SelfOrderHandler::GetMenu") return } tableID, _ := uuid.Parse(tableIDStr.(string)) isActive := true req := &contract.ListProductsRequest{ OrganizationID: &organizationID, IsActive: &isActive, Page: 1, Limit: 100, } productsResponse := h.productService.ListProducts(ctx, req) outlet, outletErr := h.outletRepo.GetByID(ctx, outletID) outletName := "" if outletErr == nil && outlet != nil { outletName = outlet.Name } menuResponse := &contract.SelfOrderMenuResponse{ OutletID: outletID, OutletName: outletName, TableID: tableID, TableName: tableName.(string), Organization: contract.OrganizationMenuInfo{ ID: organizationID, }, } if productsResponse != nil { if data, ok := productsResponse.Data.(*contract.ListProductsResponse); ok { menuResponse.Products = *data } } util.HandleResponse(c.Writer, c.Request, contract.BuildSuccessResponse(menuResponse), "SelfOrderHandler::GetMenu") } 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 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{ contract.NewResponseError(constants.MissingFieldErrorCode, constants.RequestEntity, err.Error()), }), "SelfOrderHandler::CreateOrder") return } if req.CustomerName == "" { util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{ contract.NewResponseError(constants.ValidationErrorCode, constants.SelfOrderEntity, "customer_name is required"), }), "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) if err != nil { logger.FromContext(ctx).WithError(err).Error("SelfOrderHandler::CreateOrder -> failed to get admin user") util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{ contract.NewResponseError(constants.InternalServerErrorCode, constants.SelfOrderEntity, "failed to resolve system user"), }), "SelfOrderHandler::CreateOrder") return } var customerID *uuid.UUID if req.PhoneNumber != nil && *req.PhoneNumber != "" { customer, err := h.customerRepo.GetByPhoneNumber(ctx, *req.PhoneNumber) if err != nil { logger.FromContext(ctx).WithError(err).Error("SelfOrderHandler::CreateOrder -> failed to lookup customer by phone") } if customer != nil { customerID = &customer.ID } } 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{ ProductID: item.ProductID, ProductVariantID: item.ProductVariantID, Quantity: item.Quantity, Notes: item.Notes, } } tableNameStr := tableName.(string) modelReq := &models.CreateOrderRequest{ OutletID: outletID, UserID: adminUser.ID, CustomerID: customerID, TableID: &tableID, TableNumber: &tableNameStr, OrderType: constants.OrderTypeDineIn, OrderItems: orderItems, Notes: req.Notes, CustomerName: &req.CustomerName, Metadata: metadata, } response, err := h.orderService.CreateOrder(ctx, modelReq, 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()), }), "SelfOrderHandler::CreateOrder") return } outlet, _ := h.outletRepo.GetByID(ctx, outletID) outletName := "" if outlet != nil { outletName = outlet.Name } 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, } } 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, } util.HandleResponse(c.Writer, c.Request, contract.BuildSuccessResponse(selfOrderResp), "SelfOrderHandler::CreateOrder") }