package order import ( "furtuna-be/internal/common/errors" "furtuna-be/internal/entity" "furtuna-be/internal/handlers/request" "furtuna-be/internal/handlers/response" "furtuna-be/internal/services" "net/http" "strconv" "time" "github.com/gin-gonic/gin" "github.com/go-playground/validator/v10" ) type Handler struct { service services.Order } func (h *Handler) Route(group *gin.RouterGroup, jwt gin.HandlerFunc) { route := group.Group("/order") route.POST("/", jwt, h.Create) route.GET("/list", jwt, h.GetAll) route.GET("/:id", jwt, h.GetByID) route.GET("/total-revenue", jwt, h.GetTotalRevenue) route.GET("/yearly-revenue/:year", jwt, h.GetYearlyRevenue) route.GET("/branch-revenue", jwt, h.GetBranchRevenue) route.PUT("/update-status/:id", jwt, h.UpdateStatus) } func NewHandler(service services.Order) *Handler { return &Handler{ service: service, } } // Create handles the creation of a new order. // @Summary Create a new order // @Description Create a new order with the provided details. // @Accept json // @Produce json // @Param Authorization header string true "JWT token" // @Param order body request.Order true "Order details" // @Success 200 {object} response.BaseResponse "Order created successfully" // @Failure 400 {object} response.BaseResponse{data=errors.Error} "Bad request" // @Failure 401 {object} response.BaseResponse{data=errors.Error} "Unauthorized" // @Router /api/v1/order [post] // @Tag Order APIs func (h *Handler) Create(c *gin.Context) { ctx := request.GetMyContext(c) var req request.Order if err := c.ShouldBindJSON(&req); err != nil { response.ErrorWrapper(c, errors.ErrorBadRequest) return } validate := validator.New() if err := validate.Struct(req); err != nil { response.ErrorWrapper(c, err) return } err := h.service.Create(ctx, req.ToEntity()) if err != nil { response.ErrorWrapper(c, err) return } c.JSON(http.StatusOK, response.BaseResponse{ Success: true, Status: http.StatusOK, }) } // UpdateStatus handles the update of the order status. // @Summary Update the status of an order // @Description Update the status of the specified order with the provided details. // @Accept json // @Produce json // @Param Authorization header string true "JWT token" // @Param id path string true "Order ID" // @Param status body request.UpdateStatus true "Status details" // @Success 200 {object} response.BaseResponse{data=response.Order} "Order status updated successfully" // @Failure 400 {object} response.BaseResponse{data=errors.Error} "Bad request" // @Failure 401 {object} response.BaseResponse{data=errors.Error} "Unauthorized" // @Failure 500 {object} response.BaseResponse "Internal server error" // @Router /api/v1/order/update-status/{id} [put] // @Tag Order APIs func (h *Handler) UpdateStatus(c *gin.Context) { ctx := request.GetMyContext(c) var req request.UpdateStatus if err := c.ShouldBindJSON(&req); err != nil { response.ErrorWrapper(c, errors.ErrorBadRequest) return } id := c.Param("id") // Parse the ID into a uint orderID, err := strconv.ParseInt(id, 10, 64) if err != nil { response.ErrorWrapper(c, errors.ErrorBadRequest) return } res, err := h.service.UpdateStatus(ctx, orderID, req.ToEntity()) if err != nil { c.JSON(http.StatusInternalServerError, response.BaseResponse{ Success: false, Status: http.StatusInternalServerError, Message: err.Error(), Data: nil, }) return } c.JSON(http.StatusOK, response.BaseResponse{ Success: true, Status: http.StatusOK, Data: h.toOrderResponse(res), }) } // GetByID retrieves the details of a specific order by ID. // @Summary Get details of an order by ID // @Description Retrieve the details of the specified order by ID. // @Accept json // @Produce json // @Param Authorization header string true "JWT token" // @Param id path string true "Order ID" // @Success 200 {object} response.BaseResponse{data=response.Order} "Order details retrieved successfully" // @Failure 400 {object} response.BaseResponse{data=errors.Error} "Bad request" // @Failure 401 {object} response.BaseResponse{data=errors.Error} "Unauthorized" // @Failure 500 {object} response.BaseResponse "Internal server error" // @Router /api/v1/order/{id} [get] // @Tag Order APIs func (h *Handler) GetByID(c *gin.Context) { id := c.Param("id") // Parse the ID into a uint orderID, err := strconv.ParseInt(id, 10, 64) if err != nil { response.ErrorWrapper(c, errors.ErrorBadRequest) return } res, err := h.service.GetByID(c.Request.Context(), orderID) if err != nil { c.JSON(http.StatusInternalServerError, response.BaseResponse{ Success: false, Status: http.StatusInternalServerError, Message: err.Error(), Data: nil, }) return } c.JSON(http.StatusOK, response.BaseResponse{ Success: true, Status: http.StatusOK, Data: h.toOrderResponse(res), }) } // GetAll retrieves a list of orders based on the specified parameters. // @Summary Get a list of orders // @Description Retrieve a list of orders based on the specified parameters. // @Accept json // @Produce json // @Param Authorization header string true "JWT token" // @Param limit query int false "Number of items to retrieve (default: 10)" // @Param offset query int false "Number of items to skip (default: 0)" // @Success 200 {object} response.BaseResponse{data=response.OrderList} "List of orders retrieved successfully" // @Failure 400 {object} response.BaseResponse{data=errors.Error} "Bad request" // @Failure 401 {object} response.BaseResponse{data=errors.Error} "Unauthorized" // @Failure 500 {object} response.BaseResponse "Internal server error" // @Router /api/v1/order/list [get] // @Tag Order APIs func (h *Handler) GetAll(c *gin.Context) { var req request.OrderParam if err := c.ShouldBindQuery(&req); err != nil { response.ErrorWrapper(c, errors.ErrorBadRequest) return } orders, total, err := h.service.GetAll(c.Request.Context(), req.ToEntity()) if err != nil { response.ErrorWrapper(c, err) return } c.JSON(http.StatusOK, response.BaseResponse{ Success: true, Status: http.StatusOK, Data: h.toOrderResponseList(orders, int64(total), req), }) } // GetTotalRevenue retrieves the total revenue and number of transactions for orders. // @Summary Get total revenue and number of transactions for orders // @Description Retrieve the total revenue and number of transactions for orders based on the specified parameters. // @Accept json // @Produce json // @Param Authorization header string true "JWT token" // @Param start_date query string false "Start date for filtering (format: 'YYYY-MM-DD')" // @Param end_date query string false "End date for filtering (format: 'YYYY-MM-DD')" // @Success 200 {object} response.BaseResponse{data=response.OrderMonthlyRevenue} "Total revenue and transactions retrieved successfully" // @Failure 400 {object} response.BaseResponse{data=errors.Error} "Bad request" // @Failure 401 {object} response.BaseResponse{data=errors.Error} "Unauthorized" // @Failure 500 {object} response.BaseResponse "Internal server error" // @Router /api/v1/order/total-revenue [get] // @Tag Order APIs func (h *Handler) GetTotalRevenue(c *gin.Context) { var req request.OrderTotalRevenueParam if err := c.ShouldBindQuery(&req); err != nil { response.ErrorWrapper(c, errors.ErrorBadRequest) return } rev, trans, err := h.service.GetTotalRevenue(c.Request.Context(), req.ToEntity()) if err != nil { c.JSON(http.StatusInternalServerError, response.BaseResponse{ Success: false, Status: http.StatusInternalServerError, Message: err.Error(), Data: nil, }) return } c.JSON(http.StatusOK, response.BaseResponse{ Success: true, Status: http.StatusOK, Data: h.toOrderTotalRevenueResponse(rev, trans), }) } // GetYearlyRevenue retrieves the yearly revenue for orders. // @Summary Get yearly revenue for orders // @Description Retrieve the yearly revenue for orders based on the specified year. // @Accept json // @Produce json // @Param Authorization header string true "JWT token" // @Param year path int true "Year for filtering" // @Success 200 {object} response.BaseResponse{data=map[int]map[string]float64} "Yearly revenue retrieved successfully" // @Failure 400 {object} response.BaseResponse{data=errors.Error} "Bad request" // @Failure 401 {object} response.BaseResponse{data=errors.Error} "Unauthorized" // @Failure 500 {object} response.BaseResponse "Internal server error" // @Router /api/v1/order/yearly-revenue/{year} [get] // @Tag Order APIs func (h *Handler) GetYearlyRevenue(c *gin.Context) { yearParam := c.Param("year") // Parse the ID into a uint year, err := strconv.Atoi(yearParam) if err != nil { response.ErrorWrapper(c, errors.ErrorBadRequest) return } rev, err := h.service.GetYearlyRevenue(c.Request.Context(), year) if err != nil { c.JSON(http.StatusInternalServerError, response.BaseResponse{ Success: false, Status: http.StatusInternalServerError, Message: err.Error(), Data: nil, }) return } c.JSON(http.StatusOK, response.BaseResponse{ Success: true, Status: http.StatusOK, Data: h.toOrderYearlyRevenueResponse(rev), }) } // GetBranchRevenue retrieves the branch-wise revenue for orders. // @Summary Get branch-wise revenue for orders // @Description Retrieve the branch-wise revenue for orders based on the specified parameters. // @Accept json // @Produce json // @Param Authorization header string true "JWT token" // @Param branch_id query int false "Branch ID for filtering" // @Param start_date query string false "Start date for filtering (format: 'YYYY-MM-DD')" // @Param end_date query string false "End date for filtering (format: 'YYYY-MM-DD')" // @Success 200 {object} response.BaseResponse{data=[]response.OrderBranchRevenue} "Branch-wise revenue retrieved successfully" // @Failure 400 {object} response.BaseResponse{data=errors.Error} "Bad request" // @Failure 401 {object} response.BaseResponse{data=errors.Error} "Unauthorized" // @Failure 500 {object} response.BaseResponse "Internal server error" // @Router /api/v1/order/branch-revenue [get] // @Tag Order APIs func (h *Handler) GetBranchRevenue(c *gin.Context) { var req request.OrderBranchRevenueParam if err := c.ShouldBindQuery(&req); err != nil { response.ErrorWrapper(c, errors.ErrorBadRequest) return } rev, err := h.service.GetBranchRevenue(c.Request.Context(), req.ToEntity()) if err != nil { c.JSON(http.StatusInternalServerError, response.BaseResponse{ Success: false, Status: http.StatusInternalServerError, Message: err.Error(), Data: nil, }) return } c.JSON(http.StatusOK, response.BaseResponse{ Success: true, Status: http.StatusOK, Data: h.toOrderBranchRevenueResponse(rev), }) } func (h *Handler) toOrderResponse(resp *entity.Order) response.Order { orderItems := []response.OrderItem{} for _, i := range resp.OrderItem { orderItems = append(orderItems, response.OrderItem{ OrderItemID: i.OrderItemID, ItemID: i.ItemID, ItemType: i.ItemType, ItemName: i.ItemName, Price: i.Price, Qty: i.Qty, CreatedAt: i.CreatedAt.Format(time.RFC3339), UpdatedAt: i.CreatedAt.Format(time.RFC3339), }) } return response.Order{ ID: resp.ID, BranchID: resp.BranchID, BranchName: resp.BranchName, Amount: resp.Amount, OrderItem: orderItems, Status: resp.Status, CustomerName: resp.CustomerName, CustomerPhone: resp.CustomerPhone, Pax: resp.Pax, PaymentMethod: resp.Transaction.PaymentMethod, CreatedAt: resp.CreatedAt.Format(time.RFC3339), UpdatedAt: resp.CreatedAt.Format(time.RFC3339), } } func (h *Handler) toOrderResponseList(resp []*entity.Order, total int64, req request.OrderParam) response.OrderList { var orders []response.Order for _, b := range resp { orders = append(orders, h.toOrderResponse(b)) } return response.OrderList{ Orders: orders, Total: total, Limit: req.Limit, Offset: req.Offset, } } func (h *Handler) toOrderTotalRevenueResponse(rev float64, trans int64) response.OrderMonthlyRevenue { return response.OrderMonthlyRevenue{ TotalRevenue: rev, TotalTransaction: trans, } } func (h *Handler) toOrderYearlyRevenueResponse(data entity.OrderYearlyRevenueList) map[int]map[string]float64 { result := make(map[int]map[string]float64) // Initialize result map with 0 values for all months and item types for i := 1; i <= 12; i++ { result[i] = map[string]float64{ "PRODUCT": 0, "STUDIO": 0, } } // Populate result map with actual data for _, v := range data { result[v.Month][v.ItemType] = v.Amount } return result } func (h *Handler) toOrderBranchRevenueResponse(data entity.OrderBranchRevenueList) []response.OrderBranchRevenue { var resp []response.OrderBranchRevenue for _, v := range data { resp = append(resp, response.OrderBranchRevenue{ BranchID: v.BranchID, BranchName: v.BranchName, BranchLocation: v.BranchLocation, TotalTransaction: v.TotalTransaction, TotalAmount: v.TotalAmount, }) } return resp }