856 lines
23 KiB
Go
856 lines
23 KiB
Go
package http
|
|
|
|
import (
|
|
"enaklo-pos-be/internal/common/errors"
|
|
order2 "enaklo-pos-be/internal/constants/order"
|
|
"enaklo-pos-be/internal/entity"
|
|
"enaklo-pos-be/internal/handlers/request"
|
|
"enaklo-pos-be/internal/handlers/response"
|
|
"enaklo-pos-be/internal/services/v2/order"
|
|
"net/http"
|
|
"strconv"
|
|
"time"
|
|
|
|
"github.com/gin-gonic/gin"
|
|
"github.com/go-playground/validator/v10"
|
|
)
|
|
|
|
type Handler struct {
|
|
service order.Service
|
|
queryParser *request.QueryParser
|
|
}
|
|
|
|
func NewOrderHandler(service order.Service) *Handler {
|
|
return &Handler{
|
|
service: service,
|
|
queryParser: request.NewQueryParser(),
|
|
}
|
|
}
|
|
|
|
func (h *Handler) Route(group *gin.RouterGroup, jwt gin.HandlerFunc) {
|
|
route := group.Group("/order")
|
|
|
|
route.POST("/inquiry", jwt, h.Inquiry)
|
|
route.POST("/execute", jwt, h.Execute)
|
|
route.POST("/refund", jwt, h.Refund)
|
|
route.POST("/partial-refund", jwt, h.PartialRefund)
|
|
route.POST("/void", jwt, h.VoidOrder)
|
|
route.POST("/split-bill", jwt, h.SplitBill)
|
|
route.GET("/history", jwt, h.GetOrderHistory)
|
|
route.GET("/refund-history", jwt, h.GetRefundHistory)
|
|
route.GET("/payment-analysis", jwt, h.GetPaymentMethodAnalysis)
|
|
route.GET("/revenue-overview", jwt, h.GetRevenueOverview)
|
|
route.GET("/sales-by-category", jwt, h.GetSalesByCategory)
|
|
route.GET("/popular-products", jwt, h.GetPopularProducts)
|
|
route.GET("/detail/:id", jwt, h.GetByID)
|
|
}
|
|
|
|
type InquiryRequest struct {
|
|
CustomerID *int64 `json:"customer_id"`
|
|
CustomerName string `json:"customer_name" validate:"required_without=CustomerID"`
|
|
CustomerEmail string `json:"customer_email"`
|
|
CustomerPhoneNumber string `json:"customer_phone_number"`
|
|
PaymentMethod string `json:"payment_method" validate:"required"`
|
|
OrderItems []OrderItemRequest `json:"order_items" validate:"required,min=1,dive"`
|
|
OrderType string `json:"order_type"`
|
|
PaymentProvider string `json:"payment_provider"`
|
|
TableNumber string `json:"table_number"`
|
|
CashierSessionID int64 `json:"cashier_session_id"`
|
|
}
|
|
|
|
func (o *InquiryRequest) GetPaymentProvider() string {
|
|
if o.PaymentMethod == "CASH" {
|
|
return "CASH"
|
|
}
|
|
|
|
return o.PaymentProvider
|
|
}
|
|
|
|
type OrderItemRequest struct {
|
|
ProductID int64 `json:"product_id" validate:"required"`
|
|
Quantity int `json:"quantity"`
|
|
Notes string `json:"notes"`
|
|
}
|
|
|
|
type ExecuteRequest struct {
|
|
PaymentMethod string `json:"payment_method" validate:"required"`
|
|
PaymentProvider string `json:"payment_provider"`
|
|
InProgressOrderID int64 `json:"in_progress_order_id"`
|
|
Token string `json:"token"`
|
|
}
|
|
|
|
type RefundRequest struct {
|
|
OrderID int64 `json:"order_id" validate:"required"`
|
|
Reason string `json:"reason" validate:"required"`
|
|
}
|
|
|
|
type PartialRefundRequest struct {
|
|
OrderID int64 `json:"order_id" validate:"required"`
|
|
Reason string `json:"reason" validate:"required"`
|
|
Items []PartialRefundItemRequest `json:"items" validate:"required,min=1,dive"`
|
|
}
|
|
|
|
type PartialRefundItemRequest struct {
|
|
OrderItemID int64 `json:"order_item_id" validate:"required"`
|
|
Quantity int `json:"quantity" validate:"required,min=1"`
|
|
}
|
|
|
|
type VoidOrderRequest struct {
|
|
OrderID int64 `json:"order_id" validate:"required"`
|
|
Reason string `json:"reason" validate:"required"`
|
|
Type string `json:"type" validate:"required,oneof=ALL ITEM"`
|
|
Items []VoidItemRequest `json:"items,omitempty" validate:"required_if=Type ITEM,dive"`
|
|
}
|
|
|
|
type VoidItemRequest struct {
|
|
OrderItemID int64 `json:"order_item_id" validate:"required"`
|
|
Quantity int `json:"quantity" validate:"required,min=1"`
|
|
}
|
|
|
|
type SplitBillRequest struct {
|
|
OrderID int64 `json:"order_id" validate:"required"`
|
|
Type string `json:"type" validate:"required,oneof=ITEM AMOUNT"`
|
|
Items []SplitBillItemRequest `json:"items,omitempty" validate:"required_if=Type ITEM,dive"`
|
|
Amount float64 `json:"amount,omitempty" validate:"required_if=Type AMOUNT,min=0"`
|
|
}
|
|
|
|
type SplitBillItemRequest struct {
|
|
OrderItemID int64 `json:"order_item_id" validate:"required"`
|
|
Quantity int `json:"quantity" validate:"required,min=1"`
|
|
}
|
|
|
|
type RefundResponse struct {
|
|
OrderID int64 `json:"order_id"`
|
|
Status string `json:"status"`
|
|
RefundAmount float64 `json:"refund_amount"`
|
|
Reason string `json:"reason"`
|
|
RefundedAt string `json:"refunded_at"`
|
|
CustomerName string `json:"customer_name"`
|
|
PaymentType string `json:"payment_type"`
|
|
}
|
|
|
|
type RefundHistoryResponse struct {
|
|
OrderID int64 `json:"order_id"`
|
|
CustomerName string `json:"customer_name"`
|
|
CustomerID *int64 `json:"customer_id"`
|
|
IsMember bool `json:"is_member"`
|
|
Status string `json:"status"`
|
|
Amount float64 `json:"amount"`
|
|
Total float64 `json:"total"`
|
|
PaymentType string `json:"payment_type"`
|
|
TableNumber string `json:"table_number"`
|
|
OrderType string `json:"order_type"`
|
|
CreatedAt string `json:"created_at"`
|
|
RefundedAt string `json:"refunded_at"`
|
|
Tax float64 `json:"tax"`
|
|
}
|
|
|
|
type PartialRefundResponse struct {
|
|
OrderID int64 `json:"order_id"`
|
|
Status string `json:"status"`
|
|
RefundedAmount float64 `json:"refunded_amount"`
|
|
RemainingAmount float64 `json:"remaining_amount"`
|
|
Reason string `json:"reason"`
|
|
RefundedAt string `json:"refunded_at"`
|
|
CustomerName string `json:"customer_name"`
|
|
PaymentType string `json:"payment_type"`
|
|
RefundedItems []RefundedItemResponse `json:"refunded_items"`
|
|
}
|
|
|
|
type RefundedItemResponse struct {
|
|
OrderItemID int64 `json:"order_item_id"`
|
|
ItemName string `json:"item_name"`
|
|
Quantity int `json:"quantity"`
|
|
UnitPrice float64 `json:"unit_price"`
|
|
TotalPrice float64 `json:"total_price"`
|
|
}
|
|
|
|
type VoidOrderResponse struct {
|
|
OrderID int64 `json:"order_id"`
|
|
Status string `json:"status"`
|
|
Reason string `json:"reason"`
|
|
VoidedAt string `json:"voided_at"`
|
|
CustomerName string `json:"customer_name"`
|
|
VoidedItems []VoidedItemResponse `json:"voided_items,omitempty"`
|
|
}
|
|
|
|
type VoidedItemResponse struct {
|
|
OrderItemID int64 `json:"order_item_id"`
|
|
ItemName string `json:"item_name"`
|
|
Quantity int `json:"quantity"`
|
|
UnitPrice float64 `json:"unit_price"`
|
|
TotalPrice float64 `json:"total_price"`
|
|
}
|
|
|
|
type SplitBillResponse struct {
|
|
OriginalOrderID int64 `json:"original_order_id"`
|
|
SplitOrders []SplitOrderResponse `json:"split_orders"`
|
|
SplitAt string `json:"split_at"`
|
|
}
|
|
|
|
type SplitOrderResponse struct {
|
|
OrderID int64 `json:"order_id"`
|
|
CustomerName string `json:"customer_name"`
|
|
CustomerID *int64 `json:"customer_id"`
|
|
Amount float64 `json:"amount"`
|
|
Total float64 `json:"total"`
|
|
Tax float64 `json:"tax"`
|
|
Status string `json:"status"`
|
|
Items []response.OrderItemResponse `json:"items"`
|
|
}
|
|
|
|
func (h *Handler) Inquiry(c *gin.Context) {
|
|
ctx := request.GetMyContext(c)
|
|
userID := ctx.RequestedBy()
|
|
partnerID := ctx.GetPartnerID()
|
|
|
|
var req InquiryRequest
|
|
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
|
|
}
|
|
|
|
orderItems := make([]entity.OrderItemRequest, len(req.OrderItems))
|
|
for i, item := range req.OrderItems {
|
|
orderItems[i] = entity.OrderItemRequest{
|
|
ProductID: item.ProductID,
|
|
Quantity: item.Quantity,
|
|
Notes: item.Notes,
|
|
}
|
|
}
|
|
|
|
orderReq := &entity.OrderRequest{
|
|
Source: "POS",
|
|
CreatedBy: userID,
|
|
PartnerID: *partnerID,
|
|
PaymentMethod: req.PaymentMethod,
|
|
OrderItems: orderItems,
|
|
CustomerID: req.CustomerID,
|
|
CustomerName: req.CustomerName,
|
|
CustomerEmail: req.CustomerEmail,
|
|
CustomerPhoneNumber: req.CustomerPhoneNumber,
|
|
OrderType: req.OrderType,
|
|
PaymentProvider: req.GetPaymentProvider(),
|
|
TableNumber: req.TableNumber,
|
|
CashierSessionID: req.CashierSessionID,
|
|
}
|
|
|
|
result, err := h.service.CreateOrderInquiry(ctx, orderReq)
|
|
if err != nil {
|
|
response.ErrorWrapper(c, err)
|
|
return
|
|
}
|
|
|
|
c.JSON(http.StatusOK, response.BaseResponse{
|
|
Success: true,
|
|
Status: http.StatusOK,
|
|
Data: response.MapToInquiryResponse(result),
|
|
})
|
|
}
|
|
|
|
func (h *Handler) Execute(c *gin.Context) {
|
|
ctx := request.GetMyContext(c)
|
|
|
|
var req ExecuteRequest
|
|
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
|
|
}
|
|
|
|
result, err := h.service.ExecuteOrderInquiry(ctx, req.Token, req.PaymentMethod, req.PaymentProvider, req.InProgressOrderID)
|
|
if err != nil {
|
|
response.ErrorWrapper(c, err)
|
|
return
|
|
}
|
|
|
|
c.JSON(http.StatusOK, response.BaseResponse{
|
|
Success: true,
|
|
Status: http.StatusOK,
|
|
Data: response.MapToOrderResponse(result),
|
|
})
|
|
}
|
|
|
|
func (h *Handler) Refund(c *gin.Context) {
|
|
ctx := request.GetMyContext(c)
|
|
|
|
var req RefundRequest
|
|
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.RefundRequest(ctx, *ctx.GetPartnerID(), req.OrderID, req.Reason)
|
|
if err != nil {
|
|
response.ErrorWrapper(c, err)
|
|
return
|
|
}
|
|
|
|
order, err := h.service.GetOrderByID(ctx, req.OrderID)
|
|
if err != nil {
|
|
c.JSON(http.StatusOK, response.BaseResponse{
|
|
Success: true,
|
|
Status: http.StatusOK,
|
|
Message: "Refund processed successfully",
|
|
})
|
|
return
|
|
}
|
|
|
|
refundResponse := RefundResponse{
|
|
OrderID: order.ID,
|
|
Status: order.Status,
|
|
RefundAmount: order.Total,
|
|
Reason: req.Reason,
|
|
RefundedAt: order.UpdatedAt.Format("2006-01-02T15:04:05Z"),
|
|
CustomerName: order.CustomerName,
|
|
PaymentType: response.NewPaymentFormatter().Format(order.PaymentType, order.PaymentProvider),
|
|
}
|
|
|
|
c.JSON(http.StatusOK, response.BaseResponse{
|
|
Success: true,
|
|
Status: http.StatusOK,
|
|
Data: refundResponse,
|
|
})
|
|
}
|
|
|
|
func (h *Handler) GetOrderHistory(c *gin.Context) {
|
|
ctx := request.GetMyContext(c)
|
|
|
|
searchReq, err := h.queryParser.ParseSearchRequest(c)
|
|
if err != nil {
|
|
response.ErrorWrapper(c, err)
|
|
return
|
|
}
|
|
|
|
orders, total, err := h.service.GetOrderHistory(ctx, *searchReq)
|
|
if err != nil {
|
|
response.ErrorWrapper(c, err)
|
|
return
|
|
}
|
|
|
|
responseData := response.MapOrderHistoryResponse(orders)
|
|
pagingMeta := response.NewPaginationHelper().BuildPagingMeta(searchReq.Offset, searchReq.Limit, total)
|
|
|
|
c.JSON(http.StatusOK, response.BaseResponse{
|
|
Success: true,
|
|
Status: http.StatusOK,
|
|
Data: responseData,
|
|
PagingMeta: pagingMeta,
|
|
})
|
|
}
|
|
|
|
func (h *Handler) GetPaymentMethodAnalysis(c *gin.Context) {
|
|
ctx := request.GetMyContext(c)
|
|
partnerID := ctx.GetPartnerID()
|
|
|
|
// Parse query parameters
|
|
limitStr := c.Query("limit")
|
|
offsetStr := c.Query("offset")
|
|
status := c.Query("status")
|
|
startDateStr := c.Query("start_date")
|
|
endDateStr := c.Query("end_date")
|
|
|
|
searchReq := entity.SearchRequest{}
|
|
|
|
limit := 10
|
|
if limitStr != "" {
|
|
parsedLimit, err := strconv.Atoi(limitStr)
|
|
if err == nil && parsedLimit > 0 {
|
|
limit = parsedLimit
|
|
}
|
|
}
|
|
if limit > 20 {
|
|
limit = 20
|
|
}
|
|
searchReq.Limit = limit
|
|
|
|
offset := 0
|
|
if offsetStr != "" {
|
|
parsedOffset, err := strconv.Atoi(offsetStr)
|
|
if err == nil && parsedOffset >= 0 {
|
|
offset = parsedOffset
|
|
}
|
|
}
|
|
searchReq.Offset = offset
|
|
|
|
if status != "" {
|
|
searchReq.Status = status
|
|
}
|
|
|
|
if startDateStr != "" {
|
|
startDate, err := time.Parse(time.RFC3339, startDateStr)
|
|
if err == nil {
|
|
searchReq.Start = startDate
|
|
}
|
|
}
|
|
|
|
if endDateStr != "" {
|
|
endDate, err := time.Parse(time.RFC3339, endDateStr)
|
|
if err == nil {
|
|
searchReq.End = endDate
|
|
}
|
|
}
|
|
|
|
paymentAnalysis, err := h.service.GetOrderPaymentAnalysis(ctx, *partnerID, searchReq)
|
|
if err != nil {
|
|
response.ErrorWrapper(c, err)
|
|
return
|
|
}
|
|
|
|
paymentBreakdown := make([]PaymentMethodBreakdown, len(paymentAnalysis.PaymentMethodBreakdown))
|
|
for i, bd := range paymentAnalysis.PaymentMethodBreakdown {
|
|
paymentBreakdown[i] = PaymentMethodBreakdown{
|
|
PaymentMethod: response.NewPaymentFormatter().Format(bd.PaymentType, bd.PaymentProvider),
|
|
TotalTransactions: bd.TotalTransactions,
|
|
TotalAmount: bd.TotalAmount,
|
|
}
|
|
}
|
|
|
|
c.JSON(http.StatusOK, response.BaseResponse{
|
|
Success: true,
|
|
Status: http.StatusOK,
|
|
Data: PaymentMethodAnalysisResponse{
|
|
PaymentMethodBreakdown: paymentBreakdown,
|
|
TotalAmount: paymentAnalysis.TotalAmount,
|
|
TotalTransactions: paymentAnalysis.TotalTransactions,
|
|
},
|
|
})
|
|
}
|
|
|
|
type PaymentMethodBreakdown struct {
|
|
PaymentMethod string `json:"payment_method"`
|
|
TotalTransactions int64 `json:"total_transactions"`
|
|
TotalAmount float64 `json:"total_amount"`
|
|
AverageTransactionAmount float64 `json:"average_transaction_amount"`
|
|
Percentage float64 `json:"percentage"`
|
|
}
|
|
|
|
type PaymentMethodAnalysisResponse struct {
|
|
PaymentMethodBreakdown []PaymentMethodBreakdown `json:"payment_method_breakdown"`
|
|
TotalAmount float64 `json:"total_amount"`
|
|
TotalTransactions int64 `json:"total_transactions"`
|
|
|
|
MostUsedPaymentMethod string `json:"most_used_payment_method"`
|
|
HighestRevenueMethod string `json:"highest_revenue_method"`
|
|
}
|
|
|
|
func (h *Handler) GetRevenueOverview(c *gin.Context) {
|
|
ctx := request.GetMyContext(c)
|
|
partnerID := ctx.GetPartnerID()
|
|
|
|
granularity := c.Query("period")
|
|
|
|
year := time.Now().Year()
|
|
|
|
if granularity != "m" && granularity != "w" && granularity != "d" {
|
|
granularity = "m"
|
|
}
|
|
|
|
revenueOverview, err := h.service.GetRevenueOverview(
|
|
ctx,
|
|
*partnerID,
|
|
year,
|
|
granularity,
|
|
order2.Paid.String(),
|
|
)
|
|
|
|
if err != nil {
|
|
response.ErrorWrapper(c, err)
|
|
return
|
|
}
|
|
|
|
c.JSON(http.StatusOK, response.BaseResponse{
|
|
Success: true,
|
|
Status: http.StatusOK,
|
|
Data: revenueOverview,
|
|
})
|
|
}
|
|
|
|
func (h *Handler) GetSalesByCategory(c *gin.Context) {
|
|
ctx := request.GetMyContext(c)
|
|
partnerID := ctx.GetPartnerID()
|
|
|
|
period := c.Query("period")
|
|
status := order2.Paid.String()
|
|
|
|
if period != "d" && period != "w" && period != "m" {
|
|
period = "d"
|
|
}
|
|
|
|
salesByCategory, err := h.service.GetSalesByCategory(
|
|
ctx,
|
|
*partnerID,
|
|
period,
|
|
status,
|
|
)
|
|
if err != nil {
|
|
response.ErrorWrapper(c, err)
|
|
return
|
|
}
|
|
|
|
c.JSON(http.StatusOK, response.BaseResponse{
|
|
Success: true,
|
|
Status: http.StatusOK,
|
|
Data: salesByCategory,
|
|
})
|
|
}
|
|
|
|
func (h *Handler) GetPopularProducts(c *gin.Context) {
|
|
ctx := request.GetMyContext(c)
|
|
partnerID := ctx.GetPartnerID()
|
|
|
|
period := c.Query("period")
|
|
status := order2.Paid.String()
|
|
sortBy := c.Query("sort_by")
|
|
|
|
limit := 1000
|
|
|
|
if period != "d" && period != "w" && period != "m" {
|
|
period = "d"
|
|
}
|
|
|
|
popularProducts, err := h.service.GetPopularProducts(
|
|
ctx,
|
|
*partnerID,
|
|
period,
|
|
status,
|
|
limit,
|
|
sortBy,
|
|
)
|
|
if err != nil {
|
|
response.ErrorWrapper(c, err)
|
|
return
|
|
}
|
|
|
|
c.JSON(http.StatusOK, response.BaseResponse{
|
|
Success: true,
|
|
Status: http.StatusOK,
|
|
Data: popularProducts,
|
|
})
|
|
}
|
|
|
|
func (h *Handler) GetRefundHistory(c *gin.Context) {
|
|
ctx := request.GetMyContext(c)
|
|
|
|
limitStr := c.Query("limit")
|
|
offsetStr := c.Query("offset")
|
|
startDateStr := c.Query("start_date")
|
|
endDateStr := c.Query("end_date")
|
|
|
|
searchReq := entity.SearchRequest{}
|
|
|
|
limit := 20
|
|
if limitStr != "" {
|
|
parsedLimit, err := strconv.Atoi(limitStr)
|
|
if err == nil && parsedLimit > 0 {
|
|
limit = parsedLimit
|
|
}
|
|
}
|
|
|
|
if limit > 100 {
|
|
limit = 100
|
|
}
|
|
|
|
searchReq.Limit = limit
|
|
|
|
offset := 0
|
|
if offsetStr != "" {
|
|
parsedOffset, err := strconv.Atoi(offsetStr)
|
|
if err == nil && parsedOffset >= 0 {
|
|
offset = parsedOffset
|
|
}
|
|
}
|
|
|
|
searchReq.Offset = offset
|
|
searchReq.Status = "REFUNDED"
|
|
|
|
if startDateStr != "" {
|
|
startDate, err := time.Parse(time.RFC3339, startDateStr)
|
|
if err == nil {
|
|
searchReq.Start = startDate
|
|
}
|
|
}
|
|
|
|
if endDateStr != "" {
|
|
endDate, err := time.Parse(time.RFC3339, endDateStr)
|
|
if err == nil {
|
|
searchReq.End = endDate
|
|
}
|
|
}
|
|
|
|
orders, total, err := h.service.GetOrderHistory(ctx, searchReq)
|
|
if err != nil {
|
|
response.ErrorWrapper(c, err)
|
|
return
|
|
}
|
|
|
|
responseData := []RefundHistoryResponse{}
|
|
for _, order := range orders {
|
|
responseData = append(responseData, RefundHistoryResponse{
|
|
OrderID: order.ID,
|
|
CustomerName: order.CustomerName,
|
|
CustomerID: order.CustomerID,
|
|
IsMember: order.IsMemberOrder(),
|
|
Status: order.Status,
|
|
Amount: order.Amount,
|
|
Total: order.Total,
|
|
PaymentType: response.NewPaymentFormatter().Format(order.PaymentType, order.PaymentProvider),
|
|
TableNumber: order.TableNumber,
|
|
OrderType: order.OrderType,
|
|
CreatedAt: order.CreatedAt.Format("2006-01-02T15:04:05Z"),
|
|
RefundedAt: order.UpdatedAt.Format("2006-01-02T15:04:05Z"),
|
|
Tax: order.Tax,
|
|
})
|
|
}
|
|
|
|
c.JSON(http.StatusOK, response.BaseResponse{
|
|
Success: true,
|
|
Status: http.StatusOK,
|
|
Data: responseData,
|
|
PagingMeta: &response.PagingMeta{
|
|
Page: offset + 1,
|
|
Total: int64(total),
|
|
Limit: limit,
|
|
},
|
|
})
|
|
}
|
|
|
|
func (h *Handler) PartialRefund(c *gin.Context) {
|
|
ctx := request.GetMyContext(c)
|
|
|
|
var req PartialRefundRequest
|
|
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
|
|
}
|
|
|
|
items := make([]entity.PartialRefundItem, len(req.Items))
|
|
for i, item := range req.Items {
|
|
items[i] = entity.PartialRefundItem{
|
|
OrderItemID: item.OrderItemID,
|
|
Quantity: item.Quantity,
|
|
}
|
|
}
|
|
|
|
err := h.service.PartialRefundRequest(ctx, *ctx.GetPartnerID(), req.OrderID, req.Reason, items)
|
|
if err != nil {
|
|
response.ErrorWrapper(c, err)
|
|
return
|
|
}
|
|
|
|
// Get updated order to return details
|
|
order, err := h.service.GetOrderByID(ctx, req.OrderID)
|
|
if err != nil {
|
|
c.JSON(http.StatusOK, response.BaseResponse{
|
|
Success: true,
|
|
Status: http.StatusOK,
|
|
Message: "Partial refund processed successfully",
|
|
})
|
|
return
|
|
}
|
|
|
|
refundedAmount := 0.0
|
|
var refundedItems []RefundedItemResponse
|
|
|
|
for _, reqItem := range req.Items {
|
|
for _, orderItem := range order.OrderItems {
|
|
if orderItem.ID == reqItem.OrderItemID {
|
|
itemTotal := orderItem.Price * float64(reqItem.Quantity)
|
|
refundedAmount += itemTotal
|
|
|
|
refundedItems = append(refundedItems, RefundedItemResponse{
|
|
OrderItemID: orderItem.ID,
|
|
ItemName: orderItem.ItemName,
|
|
Quantity: reqItem.Quantity,
|
|
UnitPrice: orderItem.Price,
|
|
TotalPrice: itemTotal,
|
|
})
|
|
break
|
|
}
|
|
}
|
|
}
|
|
|
|
partialRefundResponse := PartialRefundResponse{
|
|
OrderID: order.ID,
|
|
Status: order.Status,
|
|
RefundedAmount: refundedAmount,
|
|
RemainingAmount: order.Total,
|
|
Reason: req.Reason,
|
|
RefundedAt: order.UpdatedAt.Format("2006-01-02T15:04:05Z"),
|
|
CustomerName: order.CustomerName,
|
|
PaymentType: response.NewPaymentFormatter().Format(order.PaymentType, order.PaymentProvider),
|
|
RefundedItems: refundedItems,
|
|
}
|
|
|
|
c.JSON(http.StatusOK, response.BaseResponse{
|
|
Success: true,
|
|
Status: http.StatusOK,
|
|
Data: partialRefundResponse,
|
|
})
|
|
}
|
|
|
|
func (h *Handler) VoidOrder(c *gin.Context) {
|
|
ctx := request.GetMyContext(c)
|
|
|
|
var req VoidOrderRequest
|
|
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
|
|
}
|
|
|
|
var items []entity.VoidItem
|
|
if req.Type == "ITEM" {
|
|
items = make([]entity.VoidItem, len(req.Items))
|
|
for i, item := range req.Items {
|
|
items[i] = entity.VoidItem{
|
|
OrderItemID: item.OrderItemID,
|
|
Quantity: item.Quantity,
|
|
}
|
|
}
|
|
}
|
|
|
|
err := h.service.VoidOrderRequest(ctx, *ctx.GetPartnerID(), req.OrderID, req.Reason, req.Type, items)
|
|
if err != nil {
|
|
response.ErrorWrapper(c, err)
|
|
return
|
|
}
|
|
|
|
// Get updated order to return details
|
|
order, err := h.service.GetOrderByID(ctx, req.OrderID)
|
|
if err != nil {
|
|
c.JSON(http.StatusOK, response.BaseResponse{
|
|
Success: true,
|
|
Status: http.StatusOK,
|
|
Message: "Order voided successfully",
|
|
})
|
|
return
|
|
}
|
|
|
|
var voidedItems []VoidedItemResponse
|
|
if req.Type == "ITEM" {
|
|
for _, reqItem := range req.Items {
|
|
for _, orderItem := range order.OrderItems {
|
|
if orderItem.ID == reqItem.OrderItemID {
|
|
itemTotal := orderItem.Price * float64(reqItem.Quantity)
|
|
|
|
voidedItems = append(voidedItems, VoidedItemResponse{
|
|
OrderItemID: orderItem.ID,
|
|
ItemName: orderItem.ItemName,
|
|
Quantity: reqItem.Quantity,
|
|
UnitPrice: orderItem.Price,
|
|
TotalPrice: itemTotal,
|
|
})
|
|
break
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
voidOrderResponse := VoidOrderResponse{
|
|
OrderID: order.ID,
|
|
Status: order.Status,
|
|
Reason: req.Reason,
|
|
VoidedAt: order.UpdatedAt.Format("2006-01-02T15:04:05Z"),
|
|
CustomerName: order.CustomerName,
|
|
VoidedItems: voidedItems,
|
|
}
|
|
|
|
c.JSON(http.StatusOK, response.BaseResponse{
|
|
Success: true,
|
|
Status: http.StatusOK,
|
|
Data: voidOrderResponse,
|
|
})
|
|
}
|
|
|
|
func (h *Handler) SplitBill(c *gin.Context) {
|
|
ctx := request.GetMyContext(c)
|
|
|
|
var req SplitBillRequest
|
|
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
|
|
}
|
|
|
|
var items []entity.SplitBillItem
|
|
if req.Type == "ITEM" {
|
|
items = make([]entity.SplitBillItem, len(req.Items))
|
|
for i, item := range req.Items {
|
|
items[i] = entity.SplitBillItem{
|
|
OrderItemID: item.OrderItemID,
|
|
Quantity: item.Quantity,
|
|
}
|
|
}
|
|
}
|
|
|
|
splitOrder, err := h.service.SplitBillRequest(ctx,
|
|
*ctx.GetPartnerID(), req.OrderID, req.Type, items, req.Amount)
|
|
if err != nil {
|
|
response.ErrorWrapper(c, err)
|
|
return
|
|
}
|
|
|
|
c.JSON(http.StatusOK, response.BaseResponse{
|
|
Success: true,
|
|
Status: http.StatusOK,
|
|
Data: response.MapToOrderResponse(&entity.OrderResponse{Order: splitOrder}),
|
|
})
|
|
}
|
|
|
|
func (h *Handler) GetByID(c *gin.Context) {
|
|
ctx := request.GetMyContext(c)
|
|
partnerID := ctx.GetPartnerID()
|
|
|
|
orderID, err := strconv.ParseInt(c.Param("id"), 10, 64)
|
|
if err != nil {
|
|
response.ErrorWrapper(c, errors.ErrorBadRequest)
|
|
return
|
|
}
|
|
|
|
order, err := h.service.GetOrderByIDAndPartnerID(ctx, orderID, *partnerID)
|
|
if err != nil {
|
|
response.ErrorWrapper(c, err)
|
|
return
|
|
}
|
|
|
|
c.JSON(http.StatusOK, response.BaseResponse{
|
|
Success: true,
|
|
Status: http.StatusOK,
|
|
Data: response.MapToOrderResponse(&entity.OrderResponse{Order: order}),
|
|
})
|
|
}
|