upodate system
This commit is contained in:
@@ -5,9 +5,10 @@ import (
|
||||
"enaklo-pos-be/internal/handlers/request"
|
||||
"enaklo-pos-be/internal/handlers/response"
|
||||
"enaklo-pos-be/internal/services/v2/cashier_session"
|
||||
"github.com/gin-gonic/gin"
|
||||
"net/http"
|
||||
"strconv"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
type CashierSessionHandler struct {
|
||||
@@ -15,7 +16,9 @@ type CashierSessionHandler struct {
|
||||
}
|
||||
|
||||
func NewCashierSession(service cashier_session.Service) *CashierSessionHandler {
|
||||
return &CashierSessionHandler{service: service}
|
||||
return &CashierSessionHandler{
|
||||
service: service,
|
||||
}
|
||||
}
|
||||
|
||||
func (h *CashierSessionHandler) Route(group *gin.RouterGroup, jwt gin.HandlerFunc) {
|
||||
@@ -26,6 +29,7 @@ func (h *CashierSessionHandler) Route(group *gin.RouterGroup, jwt gin.HandlerFun
|
||||
route.POST("/close/:id", h.CloseSession)
|
||||
route.GET("/open", h.GetOpenSession)
|
||||
route.GET("/report/:id", h.GetSessionReport)
|
||||
route.GET("/history", h.GetSessionHistory)
|
||||
}
|
||||
|
||||
func (h *CashierSessionHandler) OpenSession(c *gin.Context) {
|
||||
@@ -121,3 +125,45 @@ func (h *CashierSessionHandler) GetSessionReport(c *gin.Context) {
|
||||
Data: response.MapToCashierSessionReport(report),
|
||||
})
|
||||
}
|
||||
|
||||
func (h *CashierSessionHandler) GetSessionHistory(c *gin.Context) {
|
||||
ctx := request.GetMyContext(c)
|
||||
partnerID := ctx.GetPartnerID()
|
||||
|
||||
// Parse query parameters
|
||||
limitStr := c.DefaultQuery("limit", "10")
|
||||
offsetStr := c.DefaultQuery("offset", "0")
|
||||
|
||||
limit, err := strconv.Atoi(limitStr)
|
||||
if err != nil || limit < 0 {
|
||||
limit = 10
|
||||
}
|
||||
if limit > 50 {
|
||||
limit = 50
|
||||
}
|
||||
|
||||
offset, err := strconv.Atoi(offsetStr)
|
||||
if err != nil || offset < 0 {
|
||||
offset = 0
|
||||
}
|
||||
|
||||
sessions, total, err := h.service.GetSessionHistory(ctx, *partnerID, limit, offset)
|
||||
if err != nil {
|
||||
response.ErrorWrapper(c, err)
|
||||
return
|
||||
}
|
||||
|
||||
responseData := make([]*response.CashierSessionResponse, len(sessions))
|
||||
for i, session := range sessions {
|
||||
responseData[i] = response.MapToCashierSessionResponse(session)
|
||||
}
|
||||
|
||||
pagingMeta := response.NewPaginationHelper().BuildPagingMeta(offset, limit, total)
|
||||
|
||||
c.JSON(http.StatusOK, response.BaseResponse{
|
||||
Success: true,
|
||||
Status: http.StatusOK,
|
||||
Data: responseData,
|
||||
PagingMeta: pagingMeta,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -27,7 +27,6 @@ func (h *CustomerOrderHandler) Route(group *gin.RouterGroup, jwt gin.HandlerFunc
|
||||
|
||||
route.GET("/history", jwt, h.GetOrderHistory)
|
||||
route.GET("/detail/:id", jwt, h.GetOrderID)
|
||||
|
||||
}
|
||||
|
||||
func (h *CustomerOrderHandler) GetOrderHistory(c *gin.Context) {
|
||||
@@ -99,6 +98,7 @@ func (h *CustomerOrderHandler) GetOrderHistory(c *gin.Context) {
|
||||
Price: item.Price,
|
||||
Quantity: item.Quantity,
|
||||
Subtotal: item.Price * float64(item.Quantity),
|
||||
Status: item.Status,
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
+54
-119
@@ -7,20 +7,23 @@ import (
|
||||
"enaklo-pos-be/internal/handlers/request"
|
||||
"enaklo-pos-be/internal/handlers/response"
|
||||
"enaklo-pos-be/internal/services/v2/order"
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/go-playground/validator/v10"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/go-playground/validator/v10"
|
||||
)
|
||||
|
||||
type Handler struct {
|
||||
service order.Service
|
||||
service order.Service
|
||||
queryParser *request.QueryParser
|
||||
}
|
||||
|
||||
func NewOrderHandler(service order.Service) *Handler {
|
||||
return &Handler{
|
||||
service: service,
|
||||
service: service,
|
||||
queryParser: request.NewQueryParser(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -39,6 +42,7 @@ func (h *Handler) Route(group *gin.RouterGroup, jwt gin.HandlerFunc) {
|
||||
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 {
|
||||
@@ -104,12 +108,10 @@ type VoidItemRequest struct {
|
||||
}
|
||||
|
||||
type SplitBillRequest struct {
|
||||
OrderID int64 `json:"order_id" validate:"required"`
|
||||
Type string `json:"type" validate:"required,oneof=ITEM AMOUNT"`
|
||||
PaymentMethod string `json:"payment_method" validate:"required"`
|
||||
PaymentProvider string `json:"payment_provider"`
|
||||
Items []SplitBillItemRequest `json:"items,omitempty" validate:"required_if=Type ITEM,dive"`
|
||||
Amount float64 `json:"amount,omitempty" validate:"required_if=Type AMOUNT,min=0"`
|
||||
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 {
|
||||
@@ -318,7 +320,7 @@ func (h *Handler) Refund(c *gin.Context) {
|
||||
Reason: req.Reason,
|
||||
RefundedAt: order.UpdatedAt.Format("2006-01-02T15:04:05Z"),
|
||||
CustomerName: order.CustomerName,
|
||||
PaymentType: h.formatPayment(order.PaymentType, order.PaymentProvider),
|
||||
PaymentType: response.NewPaymentFormatter().Format(order.PaymentType, order.PaymentProvider),
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, response.BaseResponse{
|
||||
@@ -330,116 +332,30 @@ func (h *Handler) Refund(c *gin.Context) {
|
||||
|
||||
func (h *Handler) GetOrderHistory(c *gin.Context) {
|
||||
ctx := request.GetMyContext(c)
|
||||
partnerID := ctx.GetPartnerID()
|
||||
|
||||
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{}
|
||||
|
||||
if status != "" {
|
||||
searchReq.Status = status
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
if startDateStr != "" {
|
||||
startDate, err := time.Parse(time.RFC3339, startDateStr)
|
||||
if err == nil {
|
||||
searchReq.Start = startDate
|
||||
}
|
||||
}
|
||||
|
||||
// Parse end date if provided
|
||||
if endDateStr != "" {
|
||||
endDate, err := time.Parse(time.RFC3339, endDateStr)
|
||||
if err == nil {
|
||||
searchReq.End = endDate
|
||||
}
|
||||
}
|
||||
|
||||
orders, total, err := h.service.GetOrderHistory(ctx, *partnerID, searchReq)
|
||||
searchReq, err := h.queryParser.ParseSearchRequest(c)
|
||||
if err != nil {
|
||||
response.ErrorWrapper(c, err)
|
||||
return
|
||||
}
|
||||
|
||||
responseData := []response.OrderHistoryResponse{}
|
||||
for _, order := range orders {
|
||||
var orderItems []response.OrderItemResponse
|
||||
for _, item := range order.OrderItems {
|
||||
orderItems = append(orderItems, response.OrderItemResponse{
|
||||
ProductID: item.ItemID,
|
||||
ProductName: item.ItemName,
|
||||
Price: item.Price,
|
||||
Quantity: item.Quantity,
|
||||
Subtotal: item.Price * float64(item.Quantity),
|
||||
})
|
||||
}
|
||||
|
||||
responseData = append(responseData, response.OrderHistoryResponse{
|
||||
ID: order.ID,
|
||||
CustomerName: order.CustomerName,
|
||||
CustomerID: order.CustomerID,
|
||||
IsMember: order.IsMemberOrder(),
|
||||
Status: order.Status,
|
||||
Amount: order.Amount,
|
||||
Total: order.Total,
|
||||
PaymentType: h.formatPayment(order.PaymentType, order.PaymentProvider),
|
||||
TableNumber: order.TableNumber,
|
||||
OrderType: order.OrderType,
|
||||
OrderItems: orderItems,
|
||||
CreatedAt: order.CreatedAt.Format("2006-01-02T15:04:05Z"),
|
||||
Tax: order.Tax,
|
||||
})
|
||||
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: &response.PagingMeta{
|
||||
Page: offset + 1,
|
||||
Total: int64(total),
|
||||
Limit: limit,
|
||||
},
|
||||
Success: true,
|
||||
Status: http.StatusOK,
|
||||
Data: responseData,
|
||||
PagingMeta: pagingMeta,
|
||||
})
|
||||
}
|
||||
|
||||
func (h *Handler) formatPayment(payment, provider string) string {
|
||||
if payment == "CASH" {
|
||||
return payment
|
||||
}
|
||||
|
||||
return payment + " " + provider
|
||||
}
|
||||
|
||||
func (h *Handler) GetPaymentMethodAnalysis(c *gin.Context) {
|
||||
ctx := request.GetMyContext(c)
|
||||
partnerID := ctx.GetPartnerID()
|
||||
@@ -501,7 +417,7 @@ func (h *Handler) GetPaymentMethodAnalysis(c *gin.Context) {
|
||||
paymentBreakdown := make([]PaymentMethodBreakdown, len(paymentAnalysis.PaymentMethodBreakdown))
|
||||
for i, bd := range paymentAnalysis.PaymentMethodBreakdown {
|
||||
paymentBreakdown[i] = PaymentMethodBreakdown{
|
||||
PaymentMethod: h.formatPayment(bd.PaymentType, bd.PaymentProvider),
|
||||
PaymentMethod: response.NewPaymentFormatter().Format(bd.PaymentType, bd.PaymentProvider),
|
||||
TotalTransactions: bd.TotalTransactions,
|
||||
TotalAmount: bd.TotalAmount,
|
||||
}
|
||||
@@ -632,7 +548,6 @@ func (h *Handler) GetPopularProducts(c *gin.Context) {
|
||||
|
||||
func (h *Handler) GetRefundHistory(c *gin.Context) {
|
||||
ctx := request.GetMyContext(c)
|
||||
partnerID := ctx.GetPartnerID()
|
||||
|
||||
limitStr := c.Query("limit")
|
||||
offsetStr := c.Query("offset")
|
||||
@@ -664,8 +579,6 @@ func (h *Handler) GetRefundHistory(c *gin.Context) {
|
||||
}
|
||||
|
||||
searchReq.Offset = offset
|
||||
|
||||
// Set status to REFUNDED to get only refunded orders
|
||||
searchReq.Status = "REFUNDED"
|
||||
|
||||
if startDateStr != "" {
|
||||
@@ -682,7 +595,7 @@ func (h *Handler) GetRefundHistory(c *gin.Context) {
|
||||
}
|
||||
}
|
||||
|
||||
orders, total, err := h.service.GetOrderHistory(ctx, *partnerID, searchReq)
|
||||
orders, total, err := h.service.GetOrderHistory(ctx, searchReq)
|
||||
if err != nil {
|
||||
response.ErrorWrapper(c, err)
|
||||
return
|
||||
@@ -698,7 +611,7 @@ func (h *Handler) GetRefundHistory(c *gin.Context) {
|
||||
Status: order.Status,
|
||||
Amount: order.Amount,
|
||||
Total: order.Total,
|
||||
PaymentType: h.formatPayment(order.PaymentType, order.PaymentProvider),
|
||||
PaymentType: response.NewPaymentFormatter().Format(order.PaymentType, order.PaymentProvider),
|
||||
TableNumber: order.TableNumber,
|
||||
OrderType: order.OrderType,
|
||||
CreatedAt: order.CreatedAt.Format("2006-01-02T15:04:05Z"),
|
||||
@@ -759,7 +672,6 @@ func (h *Handler) PartialRefund(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
// Calculate refunded amount
|
||||
refundedAmount := 0.0
|
||||
var refundedItems []RefundedItemResponse
|
||||
|
||||
@@ -789,7 +701,7 @@ func (h *Handler) PartialRefund(c *gin.Context) {
|
||||
Reason: req.Reason,
|
||||
RefundedAt: order.UpdatedAt.Format("2006-01-02T15:04:05Z"),
|
||||
CustomerName: order.CustomerName,
|
||||
PaymentType: h.formatPayment(order.PaymentType, order.PaymentProvider),
|
||||
PaymentType: response.NewPaymentFormatter().Format(order.PaymentType, order.PaymentProvider),
|
||||
RefundedItems: refundedItems,
|
||||
}
|
||||
|
||||
@@ -815,7 +727,6 @@ func (h *Handler) VoidOrder(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
// Convert request items to entity items
|
||||
var items []entity.VoidItem
|
||||
if req.Type == "ITEM" {
|
||||
items = make([]entity.VoidItem, len(req.Items))
|
||||
@@ -906,7 +817,8 @@ func (h *Handler) SplitBill(c *gin.Context) {
|
||||
}
|
||||
}
|
||||
|
||||
splitOrder, err := h.service.SplitBillRequest(ctx, *ctx.GetPartnerID(), req.OrderID, req.Type, req.PaymentMethod, req.PaymentProvider, items, req.Amount)
|
||||
splitOrder, err := h.service.SplitBillRequest(ctx,
|
||||
*ctx.GetPartnerID(), req.OrderID, req.Type, items, req.Amount)
|
||||
if err != nil {
|
||||
response.ErrorWrapper(c, err)
|
||||
return
|
||||
@@ -918,3 +830,26 @@ func (h *Handler) SplitBill(c *gin.Context) {
|
||||
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}),
|
||||
})
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ package request
|
||||
import "enaklo-pos-be/internal/entity"
|
||||
|
||||
type OpenCashierSessionRequest struct {
|
||||
PartnerID int64 `json:"partner_id" validate:"required"`
|
||||
OpeningAmount float64 `json:"opening_amount" validate:"required,gt=0"`
|
||||
}
|
||||
|
||||
@@ -12,6 +13,7 @@ type CloseCashierSessionRequest struct {
|
||||
|
||||
func (o *OpenCashierSessionRequest) ToEntity(cashierID int64) *entity.CashierSession {
|
||||
return &entity.CashierSession{
|
||||
PartnerID: o.PartnerID,
|
||||
CashierID: cashierID,
|
||||
OpeningAmount: o.OpeningAmount,
|
||||
}
|
||||
|
||||
@@ -30,19 +30,17 @@ func (p *ProductParam) ToEntity(partnerID int64) entity.ProductSearch {
|
||||
}
|
||||
|
||||
type Product struct {
|
||||
ID int64 `json:"id,omitempty"`
|
||||
PartnerID int64 `json:"partner_id"`
|
||||
SiteID int64 `json:"site_id"`
|
||||
Name string `json:"name" validate:"required"`
|
||||
Type string `json:"type"`
|
||||
Price float64 `json:"price" validate:"required"`
|
||||
IsWeekendTicket bool `json:"is_weekend_ticket"`
|
||||
IsSeasonTicket bool `json:"is_season_ticket"`
|
||||
Status string `json:"status"`
|
||||
Description string `json:"description"`
|
||||
Stock int64 `json:"stock"`
|
||||
Image string `json:"image"`
|
||||
CategoryID int64 `json:"category_id"`
|
||||
ID int64 `json:"id,omitempty"`
|
||||
PartnerID int64 `json:"partner_id"`
|
||||
SiteID int64 `json:"site_id"`
|
||||
Name string `json:"name" validate:"required"`
|
||||
Type string `json:"type"`
|
||||
Price float64 `json:"price" validate:"required"`
|
||||
Status string `json:"status"`
|
||||
Description string `json:"description"`
|
||||
Stock int64 `json:"stock"`
|
||||
Image string `json:"image"`
|
||||
CategoryID int64 `json:"category_id"`
|
||||
}
|
||||
|
||||
func (e *Product) ToEntity() *entity.Product {
|
||||
|
||||
@@ -0,0 +1,126 @@
|
||||
package request
|
||||
|
||||
import (
|
||||
"enaklo-pos-be/internal/entity"
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/pkg/errors"
|
||||
"strconv"
|
||||
"time"
|
||||
)
|
||||
|
||||
type QueryParser struct {
|
||||
defaultLimit int
|
||||
maxLimit int
|
||||
}
|
||||
|
||||
func NewQueryParser() *QueryParser {
|
||||
return &QueryParser{
|
||||
defaultLimit: 20,
|
||||
maxLimit: 100,
|
||||
}
|
||||
}
|
||||
|
||||
func (p *QueryParser) ParseSearchRequest(c *gin.Context) (*entity.SearchRequest, error) {
|
||||
req := &entity.SearchRequest{}
|
||||
|
||||
if status := c.Query("status"); status != "" {
|
||||
req.Status = status
|
||||
}
|
||||
|
||||
limit, err := p.parseLimit(c.Query("limit"))
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "invalid limit parameter")
|
||||
}
|
||||
req.Limit = limit
|
||||
|
||||
offset, err := p.parseOffset(c.Query("offset"))
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "invalid offset parameter")
|
||||
}
|
||||
req.Offset = offset
|
||||
|
||||
if err := p.parseDateRange(c, req); err != nil {
|
||||
return nil, errors.Wrap(err, "invalid date parameters")
|
||||
}
|
||||
|
||||
return req, nil
|
||||
}
|
||||
|
||||
func (p *QueryParser) parseLimit(limitStr string) (int, error) {
|
||||
if limitStr == "" {
|
||||
return p.defaultLimit, nil
|
||||
}
|
||||
|
||||
limit, err := strconv.Atoi(limitStr)
|
||||
if err != nil {
|
||||
return 0, errors.New("limit must be a valid integer")
|
||||
}
|
||||
|
||||
if limit <= 0 {
|
||||
return p.defaultLimit, nil
|
||||
}
|
||||
|
||||
if limit > p.maxLimit {
|
||||
limit = p.maxLimit
|
||||
}
|
||||
|
||||
return limit, nil
|
||||
}
|
||||
|
||||
func (p *QueryParser) parseOffset(offsetStr string) (int, error) {
|
||||
if offsetStr == "" {
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
offset, err := strconv.Atoi(offsetStr)
|
||||
if err != nil {
|
||||
return 0, errors.New("offset must be a valid integer")
|
||||
}
|
||||
|
||||
if offset < 0 {
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
return offset, nil
|
||||
}
|
||||
|
||||
func (p *QueryParser) parseDateRange(c *gin.Context, req *entity.SearchRequest) error {
|
||||
if startDateStr := c.Query("start_date"); startDateStr != "" {
|
||||
startDate, err := p.parseDate(startDateStr)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "invalid start_date format")
|
||||
}
|
||||
req.Start = startDate
|
||||
}
|
||||
|
||||
if endDateStr := c.Query("end_date"); endDateStr != "" {
|
||||
endDate, err := p.parseDate(endDateStr)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "invalid end_date format")
|
||||
}
|
||||
req.End = endDate
|
||||
}
|
||||
|
||||
if !req.Start.IsZero() && !req.End.IsZero() && req.Start.After(req.End) {
|
||||
return errors.New("start_date cannot be after end_date")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *QueryParser) parseDate(dateStr string) (time.Time, error) {
|
||||
formats := []string{
|
||||
time.RFC3339,
|
||||
"2006-01-02T15:04:05",
|
||||
"2006-01-02",
|
||||
"2006-01-02 15:04:05",
|
||||
}
|
||||
|
||||
for _, format := range formats {
|
||||
if date, err := time.Parse(format, dateStr); err == nil {
|
||||
return date, nil
|
||||
}
|
||||
}
|
||||
|
||||
return time.Time{}, errors.New("unsupported date format")
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
package validator
|
||||
|
||||
import (
|
||||
"enaklo-pos-be/internal/entity"
|
||||
"github.com/pkg/errors"
|
||||
"time"
|
||||
)
|
||||
|
||||
type RequestValidator struct{}
|
||||
|
||||
func NewRequestValidator() *RequestValidator {
|
||||
return &RequestValidator{}
|
||||
}
|
||||
|
||||
func (v *RequestValidator) ValidateSearchRequest(req *entity.SearchRequest) error {
|
||||
if req.Status != "" {
|
||||
validStatuses := []string{"pending", "confirmed", "processing", "completed", "cancelled"}
|
||||
if !v.isValidStatus(req.Status, validStatuses) {
|
||||
return errors.New("invalid status value")
|
||||
}
|
||||
}
|
||||
|
||||
if !req.Start.IsZero() && !req.End.IsZero() {
|
||||
if req.Start.After(req.End) {
|
||||
return errors.New("start date cannot be after end date")
|
||||
}
|
||||
|
||||
if req.End.Sub(req.Start) > 365*24*time.Hour {
|
||||
return errors.New("date range cannot exceed 1 year")
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (v *RequestValidator) isValidStatus(status string, validStatuses []string) bool {
|
||||
for _, validStatus := range validStatuses {
|
||||
if status == validStatus {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
@@ -7,6 +7,7 @@ import (
|
||||
|
||||
type CashierSessionResponse struct {
|
||||
ID int64 `json:"id"`
|
||||
PartnerID int64 `json:"partner_id"`
|
||||
CashierID int64 `json:"cashier_id"`
|
||||
OpenedAt time.Time `json:"opened_at"`
|
||||
ClosedAt *time.Time `json:"closed_at,omitempty"`
|
||||
@@ -35,6 +36,7 @@ func MapToCashierSessionResponse(e *entity.CashierSession) *CashierSessionRespon
|
||||
|
||||
return &CashierSessionResponse{
|
||||
ID: e.ID,
|
||||
PartnerID: e.PartnerID,
|
||||
CashierID: e.CashierID,
|
||||
OpenedAt: e.OpenedAt,
|
||||
ClosedAt: e.ClosedAt,
|
||||
|
||||
@@ -3,6 +3,7 @@ package response
|
||||
import (
|
||||
"enaklo-pos-be/internal/constants/order"
|
||||
"enaklo-pos-be/internal/constants/transaction"
|
||||
"enaklo-pos-be/internal/entity"
|
||||
"time"
|
||||
)
|
||||
|
||||
@@ -204,3 +205,52 @@ type OrderHistoryResponse struct {
|
||||
Tax float64 `json:"tax"`
|
||||
RestaurantName string `json:"restaurant_name"`
|
||||
}
|
||||
|
||||
func MapOrderHistoryResponse(orders []*entity.Order) []OrderHistoryResponse {
|
||||
responseData := make([]OrderHistoryResponse, 0, len(orders))
|
||||
|
||||
for _, order := range orders {
|
||||
orderResponse := mapOrderToResponse(order)
|
||||
responseData = append(responseData, orderResponse)
|
||||
}
|
||||
|
||||
return responseData
|
||||
}
|
||||
|
||||
func mapOrderToResponse(order *entity.Order) OrderHistoryResponse {
|
||||
paymentFormatter := NewPaymentFormatter()
|
||||
return OrderHistoryResponse{
|
||||
ID: order.ID,
|
||||
CustomerName: order.CustomerName,
|
||||
CustomerID: order.CustomerID,
|
||||
IsMember: order.IsMemberOrder(),
|
||||
Status: order.Status,
|
||||
Amount: order.Amount,
|
||||
Total: order.Total,
|
||||
PaymentType: paymentFormatter.Format(order.PaymentType, order.PaymentProvider),
|
||||
TableNumber: order.TableNumber,
|
||||
OrderType: order.OrderType,
|
||||
OrderItems: mapOrderItems(order.OrderItems),
|
||||
CreatedAt: order.CreatedAt.Format(time.RFC3339),
|
||||
Tax: order.Tax,
|
||||
}
|
||||
}
|
||||
|
||||
func mapOrderItems(items []entity.OrderItem) []OrderItemResponse {
|
||||
orderItems := make([]OrderItemResponse, 0, len(items))
|
||||
|
||||
for _, item := range items {
|
||||
orderItems = append(orderItems, OrderItemResponse{
|
||||
OrderItemID: item.ID,
|
||||
ProductID: item.ItemID,
|
||||
ProductName: item.ItemName,
|
||||
Price: item.Price,
|
||||
Quantity: item.Quantity,
|
||||
Subtotal: item.Price * float64(item.Quantity),
|
||||
Notes: item.Notes,
|
||||
Status: item.Status,
|
||||
})
|
||||
}
|
||||
|
||||
return orderItems
|
||||
}
|
||||
|
||||
@@ -23,12 +23,14 @@ type OrderInquiryResponse struct {
|
||||
}
|
||||
|
||||
type OrderItemResponse struct {
|
||||
OrderItemID int64 `json:"order_item_id"`
|
||||
ProductID int64 `json:"product_id"`
|
||||
ProductName string `json:"product_name"`
|
||||
Price float64 `json:"price"`
|
||||
Quantity int `json:"quantity"`
|
||||
Subtotal float64 `json:"subtotal"`
|
||||
Notes string `json:"notes"`
|
||||
Status string `json:"status"`
|
||||
}
|
||||
|
||||
func mapToOrderItemResponses(items []entity.OrderItem) []OrderItemResponse {
|
||||
@@ -109,12 +111,14 @@ func MapToOrderItemResponses(items []entity.OrderItem) []OrderItemResponse {
|
||||
result := make([]OrderItemResponse, 0, len(items))
|
||||
for _, item := range items {
|
||||
result = append(result, OrderItemResponse{
|
||||
OrderItemID: item.ID,
|
||||
ProductID: item.ItemID,
|
||||
ProductName: item.ItemName,
|
||||
Price: item.Price,
|
||||
Quantity: item.Quantity,
|
||||
Subtotal: item.Price * float64(item.Quantity),
|
||||
Notes: item.Notes,
|
||||
Status: item.Status,
|
||||
})
|
||||
}
|
||||
return result
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
package response
|
||||
|
||||
type PaginationHelper struct{}
|
||||
|
||||
func NewPaginationHelper() *PaginationHelper {
|
||||
return &PaginationHelper{}
|
||||
}
|
||||
|
||||
func (p *PaginationHelper) BuildPagingMeta(offset, limit int, total int64) *PagingMeta {
|
||||
page := 1
|
||||
if limit > 0 {
|
||||
page = (offset / limit) + 1
|
||||
}
|
||||
|
||||
return &PagingMeta{
|
||||
Page: page,
|
||||
Total: total,
|
||||
Limit: limit,
|
||||
}
|
||||
}
|
||||
|
||||
func (p *PaginationHelper) calculateTotalPages(total int64, limit int) int {
|
||||
if limit <= 0 {
|
||||
return 1
|
||||
}
|
||||
return int((total + int64(limit) - 1) / int64(limit))
|
||||
}
|
||||
|
||||
func (p *PaginationHelper) hasNextPage(offset, limit int, total int64) bool {
|
||||
if limit <= 0 {
|
||||
return false
|
||||
}
|
||||
return int64(offset+limit) < total
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
package response
|
||||
|
||||
import "fmt"
|
||||
|
||||
type PaymentFormatter interface {
|
||||
Format(paymentType, paymentProvider string) string
|
||||
}
|
||||
|
||||
type paymentFormatter struct{}
|
||||
|
||||
func NewPaymentFormatter() PaymentFormatter {
|
||||
return &paymentFormatter{}
|
||||
}
|
||||
|
||||
func (f *paymentFormatter) Format(paymentType, paymentProvider string) string {
|
||||
if paymentProvider != "" {
|
||||
return fmt.Sprintf("%s (%s)", paymentType, paymentProvider)
|
||||
}
|
||||
return paymentType
|
||||
}
|
||||
Reference in New Issue
Block a user