Compare commits
3 Commits
3c103b7692
...
4cc563f6f1
| Author | SHA1 | Date | |
|---|---|---|---|
| 4cc563f6f1 | |||
| e7c4681102 | |||
| f957b07d23 |
@ -55,6 +55,7 @@ func (a *App) Initialize(cfg *config.Config) error {
|
|||||||
repos.outletRepo,
|
repos.outletRepo,
|
||||||
repos.userRepo,
|
repos.userRepo,
|
||||||
repos.sessionRepo,
|
repos.sessionRepo,
|
||||||
|
repos.orderRepo,
|
||||||
)
|
)
|
||||||
|
|
||||||
a.router = router.NewRouter(
|
a.router = router.NewRouter(
|
||||||
|
|||||||
@ -1,6 +1,8 @@
|
|||||||
package contract
|
package contract
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/google/uuid"
|
"github.com/google/uuid"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -15,7 +17,7 @@ type SelfOrderTableTokenResponse struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type SelfOrderMenuRequest struct {
|
type SelfOrderMenuRequest struct {
|
||||||
SessionID string `json:"session_id" validate:"required"`
|
SessionID string `form:"session_id" validate:"required"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type SelfOrderMenuResponse struct {
|
type SelfOrderMenuResponse struct {
|
||||||
@ -60,7 +62,8 @@ type SelfOrderCreateOrderItem struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type SelfOrderListCategoriesRequest struct {
|
type SelfOrderListCategoriesRequest struct {
|
||||||
SessionID string `form:"session_id" validate:"required"`
|
OrganizationID string `form:"organisasi_id" validate:"required"`
|
||||||
|
OutletID string `form:"outlet_id" validate:"required"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type SelfOrderCategoryItem struct {
|
type SelfOrderCategoryItem struct {
|
||||||
@ -73,3 +76,37 @@ type SelfOrderCategoryItem struct {
|
|||||||
type SelfOrderListCategoriesResponse struct {
|
type SelfOrderListCategoriesResponse struct {
|
||||||
Categories []SelfOrderCategoryItem `json:"categories"`
|
Categories []SelfOrderCategoryItem `json:"categories"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type SelfOrderListOrdersResponse struct {
|
||||||
|
Orders []SelfOrderOrderItem `json:"orders"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type SelfOrderOrderItem struct {
|
||||||
|
ID uuid.UUID `json:"id"`
|
||||||
|
OrderNumber string `json:"order_number"`
|
||||||
|
TableNumber *string `json:"table_number,omitempty"`
|
||||||
|
OrderType string `json:"order_type"`
|
||||||
|
Status string `json:"status"`
|
||||||
|
Subtotal float64 `json:"subtotal"`
|
||||||
|
TaxAmount float64 `json:"tax_amount"`
|
||||||
|
DiscountAmount float64 `json:"discount_amount"`
|
||||||
|
TotalAmount float64 `json:"total_amount"`
|
||||||
|
RemainingAmount float64 `json:"remaining_amount"`
|
||||||
|
PaymentStatus string `json:"payment_status"`
|
||||||
|
IsVoid bool `json:"is_void"`
|
||||||
|
IsRefund bool `json:"is_refund"`
|
||||||
|
Items []SelfOrderOrderLineItem `json:"items,omitempty"`
|
||||||
|
CreatedAt time.Time `json:"created_at"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type SelfOrderOrderLineItem struct {
|
||||||
|
ProductID uuid.UUID `json:"product_id"`
|
||||||
|
ProductName string `json:"product_name"`
|
||||||
|
ProductVariantID *uuid.UUID `json:"product_variant_id,omitempty"`
|
||||||
|
ProductVariantNam *string `json:"product_variant_name,omitempty"`
|
||||||
|
Quantity int `json:"quantity"`
|
||||||
|
UnitPrice float64 `json:"unit_price"`
|
||||||
|
TotalPrice float64 `json:"total_price"`
|
||||||
|
Notes *string `json:"notes,omitempty"`
|
||||||
|
Status string `json:"status"`
|
||||||
|
}
|
||||||
|
|||||||
@ -27,6 +27,7 @@ type SelfOrderHandler struct {
|
|||||||
outletRepo processor.OutletRepository
|
outletRepo processor.OutletRepository
|
||||||
userRepo processor.UserRepository
|
userRepo processor.UserRepository
|
||||||
sessionRepo repository.SessionRepository
|
sessionRepo repository.SessionRepository
|
||||||
|
orderRepo repository.OrderRepository
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewSelfOrderHandler(
|
func NewSelfOrderHandler(
|
||||||
@ -37,6 +38,7 @@ func NewSelfOrderHandler(
|
|||||||
outletRepo processor.OutletRepository,
|
outletRepo processor.OutletRepository,
|
||||||
userRepo processor.UserRepository,
|
userRepo processor.UserRepository,
|
||||||
sessionRepo repository.SessionRepository,
|
sessionRepo repository.SessionRepository,
|
||||||
|
orderRepo repository.OrderRepository,
|
||||||
) *SelfOrderHandler {
|
) *SelfOrderHandler {
|
||||||
return &SelfOrderHandler{
|
return &SelfOrderHandler{
|
||||||
orderService: orderService,
|
orderService: orderService,
|
||||||
@ -46,6 +48,7 @@ func NewSelfOrderHandler(
|
|||||||
outletRepo: outletRepo,
|
outletRepo: outletRepo,
|
||||||
userRepo: userRepo,
|
userRepo: userRepo,
|
||||||
sessionRepo: sessionRepo,
|
sessionRepo: sessionRepo,
|
||||||
|
orderRepo: orderRepo,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -150,8 +153,8 @@ func (h *SelfOrderHandler) GetMenu(c *gin.Context) {
|
|||||||
ctx := c.Request.Context()
|
ctx := c.Request.Context()
|
||||||
|
|
||||||
var req contract.SelfOrderMenuRequest
|
var req contract.SelfOrderMenuRequest
|
||||||
if err := c.ShouldBindJSON(&req); err != nil {
|
if err := c.ShouldBindQuery(&req); err != nil {
|
||||||
logger.FromContext(ctx).WithError(err).Error("SelfOrderHandler::GetMenu -> request binding failed")
|
logger.FromContext(ctx).WithError(err).Error("SelfOrderHandler::GetMenu -> query binding failed")
|
||||||
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{
|
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{
|
||||||
contract.NewResponseError(constants.MissingFieldErrorCode, constants.RequestEntity, err.Error()),
|
contract.NewResponseError(constants.MissingFieldErrorCode, constants.RequestEntity, err.Error()),
|
||||||
}), "SelfOrderHandler::GetMenu")
|
}), "SelfOrderHandler::GetMenu")
|
||||||
@ -352,6 +355,85 @@ func (h *SelfOrderHandler) CreateOrder(c *gin.Context) {
|
|||||||
util.HandleResponse(c.Writer, c.Request, contract.BuildSuccessResponse(contractResp), "SelfOrderHandler::CreateOrder")
|
util.HandleResponse(c.Writer, c.Request, contract.BuildSuccessResponse(contractResp), "SelfOrderHandler::CreateOrder")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (h *SelfOrderHandler) GetOrdersBySession(c *gin.Context) {
|
||||||
|
ctx := c.Request.Context()
|
||||||
|
sessionID := c.Param("sessionId")
|
||||||
|
|
||||||
|
if sessionID == "" {
|
||||||
|
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{
|
||||||
|
contract.NewResponseError(constants.MissingFieldErrorCode, constants.RequestEntity, "session_id is required"),
|
||||||
|
}), "SelfOrderHandler::GetOrdersBySession")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
session, err := h.sessionRepo.GetByID(ctx, sessionID)
|
||||||
|
if err != nil {
|
||||||
|
logger.FromContext(ctx).WithError(err).Error("SelfOrderHandler::GetOrdersBySession -> failed to get session")
|
||||||
|
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{
|
||||||
|
contract.NewResponseError(constants.NotFoundErrorCode, constants.RequestEntity, "session not found"),
|
||||||
|
}), "SelfOrderHandler::GetOrdersBySession")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if session == nil {
|
||||||
|
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{
|
||||||
|
contract.NewResponseError(constants.NotFoundErrorCode, constants.RequestEntity, "session not found"),
|
||||||
|
}), "SelfOrderHandler::GetOrdersBySession")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
orders, err := h.orderRepo.ListBySessionID(ctx, sessionID)
|
||||||
|
if err != nil {
|
||||||
|
logger.FromContext(ctx).WithError(err).Error("SelfOrderHandler::GetOrdersBySession -> failed to list orders")
|
||||||
|
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{
|
||||||
|
contract.NewResponseError(constants.InternalServerErrorCode, constants.OrderServiceEntity, "failed to list orders"),
|
||||||
|
}), "SelfOrderHandler::GetOrdersBySession")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
resp := &contract.SelfOrderListOrdersResponse{
|
||||||
|
Orders: make([]contract.SelfOrderOrderItem, 0, len(orders)),
|
||||||
|
}
|
||||||
|
for _, o := range orders {
|
||||||
|
item := contract.SelfOrderOrderItem{
|
||||||
|
ID: o.ID,
|
||||||
|
OrderNumber: o.OrderNumber,
|
||||||
|
TableNumber: o.TableNumber,
|
||||||
|
OrderType: string(o.OrderType),
|
||||||
|
Status: string(o.Status),
|
||||||
|
Subtotal: o.Subtotal,
|
||||||
|
TaxAmount: o.TaxAmount,
|
||||||
|
DiscountAmount: o.DiscountAmount,
|
||||||
|
TotalAmount: o.TotalAmount,
|
||||||
|
RemainingAmount: o.RemainingAmount,
|
||||||
|
PaymentStatus: string(o.PaymentStatus),
|
||||||
|
IsVoid: o.IsVoid,
|
||||||
|
IsRefund: o.IsRefund,
|
||||||
|
CreatedAt: o.CreatedAt,
|
||||||
|
}
|
||||||
|
for _, oi := range o.OrderItems {
|
||||||
|
lineItem := contract.SelfOrderOrderLineItem{
|
||||||
|
ProductID: oi.ProductID,
|
||||||
|
Quantity: oi.Quantity,
|
||||||
|
UnitPrice: oi.UnitPrice,
|
||||||
|
TotalPrice: oi.TotalPrice,
|
||||||
|
Notes: oi.Notes,
|
||||||
|
Status: string(oi.Status),
|
||||||
|
ProductVariantID: oi.ProductVariantID,
|
||||||
|
}
|
||||||
|
if oi.Product.ID != uuid.Nil {
|
||||||
|
lineItem.ProductName = oi.Product.Name
|
||||||
|
}
|
||||||
|
if oi.ProductVariant != nil {
|
||||||
|
lineItem.ProductVariantNam = &oi.ProductVariant.Name
|
||||||
|
}
|
||||||
|
item.Items = append(item.Items, lineItem)
|
||||||
|
}
|
||||||
|
resp.Orders = append(resp.Orders, item)
|
||||||
|
}
|
||||||
|
|
||||||
|
util.HandleResponse(c.Writer, c.Request, contract.BuildSuccessResponse(resp), "SelfOrderHandler::GetOrdersBySession")
|
||||||
|
}
|
||||||
|
|
||||||
func (h *SelfOrderHandler) validateCreateOrderRequest(req *contract.SelfOrderCreateOrderRequest) error {
|
func (h *SelfOrderHandler) validateCreateOrderRequest(req *contract.SelfOrderCreateOrderRequest) error {
|
||||||
if req.SessionID == "" {
|
if req.SessionID == "" {
|
||||||
return fmt.Errorf("session_id is required")
|
return fmt.Errorf("session_id is required")
|
||||||
@ -382,29 +464,53 @@ func (h *SelfOrderHandler) ListCategories(c *gin.Context) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if req.SessionID == "" {
|
if req.OrganizationID == "" {
|
||||||
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{
|
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{
|
||||||
contract.NewResponseError(constants.MissingFieldErrorCode, constants.RequestEntity, "session_id is required"),
|
contract.NewResponseError(constants.MissingFieldErrorCode, constants.RequestEntity, "organisasi_id is required"),
|
||||||
}), "SelfOrderHandler::ListCategories")
|
}), "SelfOrderHandler::ListCategories")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
session, table, _, err := h.resolveSession(ctx, req.SessionID)
|
if req.OutletID == "" {
|
||||||
if err != nil {
|
|
||||||
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{
|
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{
|
||||||
contract.NewResponseError(constants.ValidationErrorCode, constants.RequestEntity, err.Error()),
|
contract.NewResponseError(constants.MissingFieldErrorCode, constants.RequestEntity, "outlet_id is required"),
|
||||||
}), "SelfOrderHandler::ListCategories")
|
}), "SelfOrderHandler::ListCategories")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if session == nil {
|
|
||||||
|
orgID, err := uuid.Parse(req.OrganizationID)
|
||||||
|
if err != nil {
|
||||||
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{
|
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{
|
||||||
contract.NewResponseError(constants.NotFoundErrorCode, constants.RequestEntity, "session not found or expired"),
|
contract.NewResponseError(constants.ValidationErrorCode, constants.RequestEntity, "invalid organisasi_id format"),
|
||||||
|
}), "SelfOrderHandler::ListCategories")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
outletID, err := uuid.Parse(req.OutletID)
|
||||||
|
if err != nil {
|
||||||
|
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{
|
||||||
|
contract.NewResponseError(constants.ValidationErrorCode, constants.RequestEntity, "invalid outlet_id format"),
|
||||||
|
}), "SelfOrderHandler::ListCategories")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
outlet, err := h.outletRepo.GetByID(ctx, outletID)
|
||||||
|
if err != nil || outlet == nil {
|
||||||
|
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{
|
||||||
|
contract.NewResponseError(constants.NotFoundErrorCode, constants.RequestEntity, "outlet not found"),
|
||||||
|
}), "SelfOrderHandler::ListCategories")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if outlet.OrganizationID != orgID {
|
||||||
|
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{
|
||||||
|
contract.NewResponseError(constants.ValidationErrorCode, constants.RequestEntity, "outlet does not belong to the specified organization"),
|
||||||
}), "SelfOrderHandler::ListCategories")
|
}), "SelfOrderHandler::ListCategories")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
catResp := h.categoryService.ListCategories(ctx, &contract.ListCategoriesRequest{
|
catResp := h.categoryService.ListCategories(ctx, &contract.ListCategoriesRequest{
|
||||||
OrganizationID: &table.OrganizationID,
|
OrganizationID: &orgID,
|
||||||
Page: 1,
|
Page: 1,
|
||||||
Limit: 100,
|
Limit: 100,
|
||||||
})
|
})
|
||||||
|
|||||||
@ -18,6 +18,7 @@ type OrderRepository interface {
|
|||||||
Update(ctx context.Context, order *entities.Order) error
|
Update(ctx context.Context, order *entities.Order) error
|
||||||
Delete(ctx context.Context, id uuid.UUID) error
|
Delete(ctx context.Context, id uuid.UUID) error
|
||||||
List(ctx context.Context, filters map[string]interface{}, limit, offset int) ([]*entities.Order, int64, error)
|
List(ctx context.Context, filters map[string]interface{}, limit, offset int) ([]*entities.Order, int64, error)
|
||||||
|
ListBySessionID(ctx context.Context, sessionID string) ([]*entities.Order, error)
|
||||||
GetByOrderNumber(ctx context.Context, orderNumber string) (*entities.Order, error)
|
GetByOrderNumber(ctx context.Context, orderNumber string) (*entities.Order, error)
|
||||||
ExistsByOrderNumber(ctx context.Context, orderNumber string) (bool, error)
|
ExistsByOrderNumber(ctx context.Context, orderNumber string) (bool, error)
|
||||||
VoidOrder(ctx context.Context, id uuid.UUID, reason string, voidedBy uuid.UUID) error
|
VoidOrder(ctx context.Context, id uuid.UUID, reason string, voidedBy uuid.UUID) error
|
||||||
@ -130,6 +131,24 @@ func (r *OrderRepositoryImpl) List(ctx context.Context, filters map[string]inter
|
|||||||
return orders, total, err
|
return orders, total, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (r *OrderRepositoryImpl) ListBySessionID(ctx context.Context, sessionID string) ([]*entities.Order, error) {
|
||||||
|
var orders []*entities.Order
|
||||||
|
err := r.db.WithContext(ctx).Model(&entities.Order{}).
|
||||||
|
Preload("Organization").
|
||||||
|
Preload("Outlet").
|
||||||
|
Preload("User").
|
||||||
|
Preload("OrderItems").
|
||||||
|
Preload("OrderItems.Product").
|
||||||
|
Preload("OrderItems.ProductVariant").
|
||||||
|
Preload("Payments").
|
||||||
|
Preload("Payments.PaymentMethod").
|
||||||
|
Preload("Payments.PaymentOrderItems").
|
||||||
|
Where("metadata->>'session_id' = ?", sessionID).
|
||||||
|
Order("created_at ASC").
|
||||||
|
Find(&orders).Error
|
||||||
|
return orders, err
|
||||||
|
}
|
||||||
|
|
||||||
func (r *OrderRepositoryImpl) GetByOrderNumber(ctx context.Context, orderNumber string) (*entities.Order, error) {
|
func (r *OrderRepositoryImpl) GetByOrderNumber(ctx context.Context, orderNumber string) (*entities.Order, error) {
|
||||||
var order entities.Order
|
var order entities.Order
|
||||||
err := r.db.WithContext(ctx).First(&order, "order_number = ?", orderNumber).Error
|
err := r.db.WithContext(ctx).First(&order, "order_number = ?", orderNumber).Error
|
||||||
|
|||||||
@ -151,8 +151,9 @@ func (r *Router) addAppRoutes(rg *gin.Engine) {
|
|||||||
{
|
{
|
||||||
selfOrder.GET("/table/:token", r.selfOrderHandler.ValidateToken)
|
selfOrder.GET("/table/:token", r.selfOrderHandler.ValidateToken)
|
||||||
selfOrder.GET("/categories", r.selfOrderHandler.ListCategories)
|
selfOrder.GET("/categories", r.selfOrderHandler.ListCategories)
|
||||||
selfOrder.POST("/menu", r.selfOrderHandler.GetMenu)
|
selfOrder.GET("/menu", r.selfOrderHandler.GetMenu)
|
||||||
selfOrder.POST("/orders", r.selfOrderHandler.CreateOrder)
|
selfOrder.POST("/orders", r.selfOrderHandler.CreateOrder)
|
||||||
|
selfOrder.GET("/orders/:sessionId", r.selfOrderHandler.GetOrdersBySession)
|
||||||
}
|
}
|
||||||
|
|
||||||
organizations := v1.Group("/organizations")
|
organizations := v1.Group("/organizations")
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user