package delivery import ( "net/http" "time" sqlc "github.com/chedius/delivery-tracker/internal/db/sqlc" "github.com/gin-gonic/gin" "github.com/google/uuid" "github.com/jackc/pgx/v5/pgtype" ) type Handler struct { queries *sqlc.Queries } // DeliveryRequest represents the request body for creating or updating a delivery type DeliveryRequest struct { Date string `json:"date" binding:"required"` // DD-MM-YYYY PickupLocation string `json:"pickup_location" binding:"required,oneof=warehouse symbat nursaya galaktika"` PickupLocation2 *string `json:"pickup_location_2" binding:"omitempty,oneof=warehouse symbat nursaya galaktika"` ProductName string `json:"product_name" binding:"required"` ProductName2 *string `json:"product_name_2"` CustomerName string `json:"customer_name" binding:"required"` Address string `json:"address" binding:"required"` Street string `json:"street" binding:"required"` House string `json:"house" binding:"required"` Apartment *string `json:"apartment"` Entrance *string `json:"entrance"` Floor *string `json:"floor"` Phone string `json:"phone" binding:"required"` AdditionalPhone *string `json:"additional_phone"` HasElevator bool `json:"has_elevator"` ServiceInfo *string `json:"service_info"` Comment string `json:"comment"` } func NewHandler(queries *sqlc.Queries) *Handler { return &Handler{queries: queries} } // GET /api/deliveries/:id func (h *Handler) GetDeliveryByID(c *gin.Context) { id := c.Param("id") if id == "" { c.JSON(http.StatusBadRequest, gin.H{"error": "ID is required"}) return } parsedID, err := uuid.Parse(id) if err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid UUID format", "details": err.Error()}) return } delivery, err := h.queries.GetDeliveryByID(c.Request.Context(), pgtype.UUID{Bytes: parsedID, Valid: true}) if err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to get delivery", "details": err.Error(), "id": id, "bytes": [16]byte([]byte(id))}) return } c.JSON(http.StatusOK, gin.H{"delivery": delivery}) } // GET /api/deliveries?date=DD-MM-YYYY func (h *Handler) GetDeliveries(c *gin.Context) { t, err := parseDate(c.Query("date")) if err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid date format", "details": err.Error()}) return } date := pgtype.Date{Time: t, Valid: true} deliveries, err := h.queries.GetDeliveriesByDate(c.Request.Context(), date) if err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to get deliveries", "details": err.Error()}) return } c.JSON(http.StatusOK, gin.H{"deliveries": deliveries}) } // GET /api/deliveries/count func (h *Handler) GetDeliveryCount(c *gin.Context) { counts, err := h.queries.GetDeliveryCount(c.Request.Context()) if err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to get delivery count", "details": err.Error()}) return } c.JSON(http.StatusOK, gin.H{"counts": counts}) } // POST /api/deliveries func (h *Handler) CreateDelivery(c *gin.Context) { var req DeliveryRequest = DeliveryRequest{} if err := c.ShouldBindJSON(&req); err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid request body", "details": err.Error()}) return } // Parse date from DD-MM-YYYY t, err := parseDate(req.Date) if err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid date format", "details": err.Error()}) return } params := sqlc.CreateDeliveryParams{ Date: pgtype.Date{Time: t, Valid: true}, PickupLocation: req.PickupLocation, PickupLocation2: pgtype.Text{String: derefString(req.PickupLocation2), Valid: req.PickupLocation2 != nil}, ProductName: req.ProductName, ProductName2: pgtype.Text{String: derefString(req.ProductName2), Valid: req.ProductName2 != nil}, CustomerName: req.CustomerName, Address: req.Address, Street: req.Street, House: req.House, Apartment: pgtype.Text{String: derefString(req.Apartment), Valid: req.Apartment != nil}, Entrance: pgtype.Text{String: derefString(req.Entrance), Valid: req.Entrance != nil}, Floor: pgtype.Text{String: derefString(req.Floor), Valid: req.Floor != nil}, Phone: req.Phone, AdditionalPhone: pgtype.Text{String: derefString(req.AdditionalPhone), Valid: req.AdditionalPhone != nil}, HasElevator: req.HasElevator, ServiceInfo: pgtype.Text{String: derefString(req.ServiceInfo), Valid: req.ServiceInfo != nil}, Comment: pgtype.Text{String: req.Comment, Valid: true}, } res, err := h.queries.CreateDelivery(c.Request.Context(), params) if err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to create delivery", "details": err.Error()}) return } c.JSON(http.StatusOK, gin.H{"message": "Delivery created", "id": res.ID.String()}) } // PATCH /api/deliveries/:id func (h *Handler) UpdateDelivery(c *gin.Context) { var req DeliveryRequest = DeliveryRequest{} if err := c.ShouldBindJSON(&req); err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid request body", "details": err.Error()}) return } id := c.Param("id") if id == "" { c.JSON(http.StatusBadRequest, gin.H{"error": "ID is required"}) return } parsedID, err := uuid.Parse(id) if err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid UUID format", "details": err.Error()}) return } t, err := parseDate(req.Date) if err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid date format", "details": err.Error()}) return } if err := h.queries.UpdateDelivery(c.Request.Context(), sqlc.UpdateDeliveryParams{ ID: pgtype.UUID{Bytes: parsedID, Valid: true}, Date: pgtype.Date{Time: t, Valid: true}, PickupLocation: req.PickupLocation, PickupLocation2: pgtype.Text{String: derefString(req.PickupLocation2), Valid: req.PickupLocation2 != nil}, ProductName: req.ProductName, ProductName2: pgtype.Text{String: derefString(req.ProductName2), Valid: req.ProductName2 != nil}, CustomerName: req.CustomerName, Address: req.Address, Street: req.Street, House: req.House, Apartment: pgtype.Text{String: derefString(req.Apartment), Valid: req.Apartment != nil}, Entrance: pgtype.Text{String: derefString(req.Entrance), Valid: req.Entrance != nil}, Floor: pgtype.Text{String: derefString(req.Floor), Valid: req.Floor != nil}, Phone: req.Phone, AdditionalPhone: pgtype.Text{String: derefString(req.AdditionalPhone), Valid: req.AdditionalPhone != nil}, HasElevator: req.HasElevator, ServiceInfo: pgtype.Text{String: derefString(req.ServiceInfo), Valid: req.ServiceInfo != nil}, Comment: pgtype.Text{String: req.Comment, Valid: true}, }); err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to update delivery", "details": err.Error()}) return } c.JSON(http.StatusOK, gin.H{"message": "Delivery updated"}) } // PATCH /api/deliveries/:id/status func (h *Handler) UpdateDeliveryStatus(c *gin.Context) { var req struct { Status string `json:"status"` } if err := c.ShouldBindJSON(&req); err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid request body", "details": err.Error()}) return } id := c.Param("id") if id == "" { c.JSON(http.StatusBadRequest, gin.H{"error": "ID is required"}) return } parsedID, err := uuid.Parse(id) if err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid UUID format", "details": err.Error()}) return } status := req.Status if status == "" { c.JSON(http.StatusBadRequest, gin.H{"error": "Status is required"}) return } if err := h.queries.UpdateDeliveryStatus(c.Request.Context(), sqlc.UpdateDeliveryStatusParams{ ID: pgtype.UUID{Bytes: parsedID, Valid: true}, Status: status, }); err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to update delivery status", "details": err.Error()}) return } c.JSON(http.StatusOK, gin.H{"message": "Delivery status updated"}) } // DELETE /api/deliveries/:id func (h *Handler) DeleteDelivery(c *gin.Context) { id := c.Param("id") if id == "" { c.JSON(http.StatusBadRequest, gin.H{"error": "ID is required"}) return } parsedID, err := uuid.Parse(id) if err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid UUID format", "details": err.Error()}) return } if err := h.queries.DeleteDelivery(c.Request.Context(), pgtype.UUID{Bytes: parsedID, Valid: true}); err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to delete delivery", "details": err.Error()}) return } c.JSON(http.StatusOK, gin.H{"message": "Delivery deleted"}) } func parseDate(dateStr string) (time.Time, error) { t, err := time.Parse("02-01-2006", dateStr) if err != nil { return time.Time{}, err } return t, nil } // derefString safely dereferences a string pointer func derefString(s *string) string { if s == nil { return "" } return *s }