add warehouse_request_source and warehouse_request_source_2 fields to deliveries table with validation and normalization logic
Some checks failed
Build and Push Docker Images / build-backend (push) Has been cancelled
Build and Push Docker Images / build-frontend (push) Has been cancelled

This commit is contained in:
Egor Pozharov
2026-05-04 17:32:03 +06:00
parent f9c54b5172
commit c39bde0b10
13 changed files with 305 additions and 165 deletions

View File

@@ -1,6 +1,7 @@
package delivery
import (
"errors"
"net/http"
"time"
@@ -16,23 +17,25 @@ type Handler struct {
// 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"`
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"`
WarehouseRequestSource *string `json:"warehouse_request_source" binding:"omitempty,oneof=symbat nursaya galaktika"`
WarehouseRequestSource2 *string `json:"warehouse_request_source_2" binding:"omitempty,oneof=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 {
@@ -97,6 +100,10 @@ func (h *Handler) CreateDelivery(c *gin.Context) {
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid request body", "details": err.Error()})
return
}
if err := normalizeWarehouseRequestSources(&req); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
// Parse date from DD-MM-YYYY
t, err := parseDate(req.Date)
@@ -106,23 +113,25 @@ func (h *Handler) CreateDelivery(c *gin.Context) {
}
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},
Date: pgtype.Date{Time: t, Valid: true},
PickupLocation: req.PickupLocation,
PickupLocation2: pgtype.Text{String: derefString(req.PickupLocation2), Valid: req.PickupLocation2 != nil},
WarehouseRequestSource: pgtype.Text{String: derefString(req.WarehouseRequestSource), Valid: req.WarehouseRequestSource != nil},
WarehouseRequestSource2: pgtype.Text{String: derefString(req.WarehouseRequestSource2), Valid: req.WarehouseRequestSource2 != 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 {
@@ -141,6 +150,10 @@ func (h *Handler) UpdateDelivery(c *gin.Context) {
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid request body", "details": err.Error()})
return
}
if err := normalizeWarehouseRequestSources(&req); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
id := c.Param("id")
@@ -161,24 +174,26 @@ func (h *Handler) UpdateDelivery(c *gin.Context) {
}
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},
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},
WarehouseRequestSource: pgtype.Text{String: derefString(req.WarehouseRequestSource), Valid: req.WarehouseRequestSource != nil},
WarehouseRequestSource2: pgtype.Text{String: derefString(req.WarehouseRequestSource2), Valid: req.WarehouseRequestSource2 != 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
@@ -266,3 +281,19 @@ func derefString(s *string) string {
}
return *s
}
func normalizeWarehouseRequestSources(req *DeliveryRequest) error {
if req.PickupLocation == "warehouse" && req.WarehouseRequestSource == nil {
return errors.New("warehouse_request_source is required when pickup_location is warehouse")
}
if req.PickupLocation != "warehouse" {
req.WarehouseRequestSource = nil
}
if req.PickupLocation2 != nil && *req.PickupLocation2 == "warehouse" && req.WarehouseRequestSource2 == nil {
return errors.New("warehouse_request_source_2 is required when pickup_location_2 is warehouse")
}
if req.PickupLocation2 == nil || *req.PickupLocation2 != "warehouse" {
req.WarehouseRequestSource2 = nil
}
return nil
}