package pricing import ( "errors" "net/http" "github.com/gin-gonic/gin" "github.com/google/uuid" ) type Handler struct { service *Service } func NewHandler(service *Service) *Handler { return &Handler{service: service} } type tierResponse struct { ID uuid.UUID `json:"id"` ProductID uuid.UUID `json:"product_id"` UnitID uuid.UUID `json:"unit_id"` Quantity float64 `json:"quantity"` PriceCents int64 `json:"price_cents"` Position int `json:"position"` } func toResponse(t *PriceTier) tierResponse { return tierResponse{ ID: t.ID, ProductID: t.ProductID, UnitID: t.UnitID, Quantity: t.Quantity, PriceCents: t.PriceCents, Position: t.Position, } } func (h *Handler) ListByProduct(c *gin.Context) { productID, err := uuid.Parse(c.Param("id")) if err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": "invalid product id"}) return } list, err := h.service.ListByProduct(c.Request.Context(), productID) if err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to list price tiers"}) return } resp := make([]tierResponse, 0, len(list)) for _, t := range list { resp = append(resp, toResponse(t)) } c.JSON(http.StatusOK, gin.H{"price_tiers": resp}) } type upsertRequest struct { UnitID uuid.UUID `json:"unit_id" binding:"required"` Quantity float64 `json:"quantity" binding:"required,gt=0"` PriceCents int64 `json:"price_cents" binding:"gte=0"` Position int `json:"position"` } func (h *Handler) Create(c *gin.Context) { productID, err := uuid.Parse(c.Param("id")) if err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": "invalid product id"}) return } var req upsertRequest if err := c.ShouldBindJSON(&req); err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": "validation error", "details": err.Error()}) return } t, err := h.service.Create(c.Request.Context(), productID, req.UnitID, req.Quantity, req.PriceCents, req.Position) if err != nil { if errors.Is(err, ErrInvalidReference) { c.JSON(http.StatusBadRequest, gin.H{"error": "product or unit does not exist"}) return } c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to create price tier"}) return } c.JSON(http.StatusCreated, toResponse(t)) } func (h *Handler) Update(c *gin.Context) { id, err := uuid.Parse(c.Param("tierId")) if err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": "invalid id"}) return } var req upsertRequest if err := c.ShouldBindJSON(&req); err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": "validation error", "details": err.Error()}) return } t, err := h.service.Update(c.Request.Context(), id, req.UnitID, req.Quantity, req.PriceCents, req.Position) if err != nil { switch { case errors.Is(err, ErrNotFound): c.JSON(http.StatusNotFound, gin.H{"error": "price tier not found"}) case errors.Is(err, ErrInvalidReference): c.JSON(http.StatusBadRequest, gin.H{"error": "product or unit does not exist"}) default: c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to update price tier"}) } return } c.JSON(http.StatusOK, toResponse(t)) } func (h *Handler) Delete(c *gin.Context) { id, err := uuid.Parse(c.Param("tierId")) if err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": "invalid id"}) return } if err := h.service.Delete(c.Request.Context(), id); err != nil { if errors.Is(err, ErrNotFound) { c.JSON(http.StatusNotFound, gin.H{"error": "price tier not found"}) return } c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to delete price tier"}) return } c.Status(http.StatusNoContent) }