package handlers import ( "net/http" "time" "github.com/gin-gonic/gin" "github.com/google/uuid" "github.com/shopspring/decimal" "go.uber.org/zap" "rmser/internal/services/drafts" "rmser/pkg/logger" ) type DraftsHandler struct { service *drafts.Service } func NewDraftsHandler(service *drafts.Service) *DraftsHandler { return &DraftsHandler{service: service} } func (h *DraftsHandler) GetDraft(c *gin.Context) { userID := c.MustGet("userID").(uuid.UUID) idStr := c.Param("id") id, err := uuid.Parse(idStr) if err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": "invalid id"}) return } draft, err := h.service.GetDraft(id, userID) if err != nil { c.JSON(http.StatusNotFound, gin.H{"error": "draft not found"}) return } c.JSON(http.StatusOK, draft) } func (h *DraftsHandler) GetDictionaries(c *gin.Context) { userID := c.MustGet("userID").(uuid.UUID) data, err := h.service.GetDictionaries(userID) if err != nil { logger.Log.Error("GetDictionaries error", zap.Error(err)) c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) return } c.JSON(http.StatusOK, data) } func (h *DraftsHandler) GetStores(c *gin.Context) { userID := c.MustGet("userID").(uuid.UUID) dict, err := h.service.GetDictionaries(userID) if err != nil { logger.Log.Error("GetStores error", zap.Error(err)) c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) return } c.JSON(http.StatusOK, dict["stores"]) } // UpdateItemDTO обновлен: float64 -> *float64, добавлен edited_field type UpdateItemDTO struct { ProductID *string `json:"product_id"` ContainerID *string `json:"container_id"` Quantity *float64 `json:"quantity"` Price *float64 `json:"price"` Sum *float64 `json:"sum"` EditedField string `json:"edited_field"` // "quantity", "price", "sum" } func (h *DraftsHandler) AddDraftItem(c *gin.Context) { draftID, err := uuid.Parse(c.Param("id")) if err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": "invalid draft id"}) return } item, err := h.service.AddItem(draftID) if err != nil { logger.Log.Error("Failed to add item", zap.Error(err)) c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) return } c.JSON(http.StatusOK, item) } func (h *DraftsHandler) DeleteDraftItem(c *gin.Context) { draftID, err := uuid.Parse(c.Param("id")) if err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": "invalid draft id"}) return } itemID, err := uuid.Parse(c.Param("itemId")) if err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": "invalid item id"}) return } newTotal, err := h.service.DeleteItem(draftID, itemID) if err != nil { logger.Log.Error("Failed to delete item", zap.Error(err)) c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) return } c.JSON(http.StatusOK, gin.H{ "status": "deleted", "id": itemID.String(), "total_sum": newTotal, }) } // UpdateItem обновлен func (h *DraftsHandler) UpdateItem(c *gin.Context) { draftID, _ := uuid.Parse(c.Param("id")) itemID, _ := uuid.Parse(c.Param("itemId")) var req UpdateItemDTO if err := c.ShouldBindJSON(&req); err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) return } var pID *uuid.UUID if req.ProductID != nil && *req.ProductID != "" { if uid, err := uuid.Parse(*req.ProductID); err == nil { pID = &uid } } var cID *uuid.UUID if req.ContainerID != nil { if *req.ContainerID == "" { // Сброс фасовки empty := uuid.Nil cID = &empty } else if uid, err := uuid.Parse(*req.ContainerID); err == nil { cID = &uid } } qty := decimal.Zero if req.Quantity != nil { qty = decimal.NewFromFloat(*req.Quantity) } price := decimal.Zero if req.Price != nil { price = decimal.NewFromFloat(*req.Price) } sum := decimal.Zero if req.Sum != nil { sum = decimal.NewFromFloat(*req.Sum) } // Дефолт, если фронт не прислал (для совместимости) editedField := req.EditedField if editedField == "" { if req.Sum != nil { editedField = "sum" } else if req.Price != nil { editedField = "price" } else { editedField = "quantity" } } if err := h.service.UpdateItem(draftID, itemID, pID, cID, qty, price, sum, editedField); err != nil { logger.Log.Error("Failed to update item", zap.Error(err)) c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) return } c.JSON(http.StatusOK, gin.H{"status": "updated"}) } type CommitRequestDTO struct { DateIncoming string `json:"date_incoming"` // YYYY-MM-DD StoreID string `json:"store_id"` SupplierID string `json:"supplier_id"` Comment string `json:"comment"` IncomingDocNum string `json:"incoming_doc_num"` } func (h *DraftsHandler) CommitDraft(c *gin.Context) { userID := c.MustGet("userID").(uuid.UUID) draftID, err := uuid.Parse(c.Param("id")) if err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": "invalid draft id"}) return } var req CommitRequestDTO if err := c.ShouldBindJSON(&req); err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) return } date, err := time.Parse("2006-01-02", req.DateIncoming) if err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": "invalid date format"}) return } storeID, err := uuid.Parse(req.StoreID) if err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": "invalid store id"}) return } supplierID, err := uuid.Parse(req.SupplierID) if err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": "invalid supplier id"}) return } if err := h.service.UpdateDraftHeader(draftID, &storeID, &supplierID, date, req.Comment, req.IncomingDocNum); err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to update header: " + err.Error()}) return } docNum, err := h.service.CommitDraft(draftID, userID) if err != nil { logger.Log.Error("Commit failed", zap.Error(err)) c.JSON(http.StatusBadGateway, gin.H{"error": "RMS error: " + err.Error()}) return } c.JSON(http.StatusOK, gin.H{"status": "completed", "document_number": docNum}) } type AddContainerRequestDTO struct { ProductID string `json:"product_id" binding:"required"` Name string `json:"name" binding:"required"` Count float64 `json:"count" binding:"required,gt=0"` } func (h *DraftsHandler) AddContainer(c *gin.Context) { userID := c.MustGet("userID").(uuid.UUID) var req AddContainerRequestDTO if err := c.ShouldBindJSON(&req); err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) return } pID, err := uuid.Parse(req.ProductID) if err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": "invalid product_id"}) return } countDec := decimal.NewFromFloat(req.Count) newID, err := h.service.CreateProductContainer(userID, pID, req.Name, countDec) if err != nil { logger.Log.Error("Failed to create container", zap.Error(err)) c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) return } c.JSON(http.StatusOK, gin.H{"status": "created", "container_id": newID.String()}) } func (h *DraftsHandler) GetDrafts(c *gin.Context) { userID := c.MustGet("userID").(uuid.UUID) fromStr := c.DefaultQuery("from", time.Now().AddDate(0, 0, -30).Format("2006-01-02")) toStr := c.DefaultQuery("to", time.Now().Format("2006-01-02")) from, _ := time.Parse("2006-01-02", fromStr) to, _ := time.Parse("2006-01-02", toStr) to = to.Add(23*time.Hour + 59*time.Minute + 59*time.Second) list, err := h.service.GetUnifiedList(userID, from, to) if err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) return } c.JSON(http.StatusOK, list) } func (h *DraftsHandler) DeleteDraft(c *gin.Context) { idStr := c.Param("id") id, err := uuid.Parse(idStr) if err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": "invalid id"}) return } newStatus, err := h.service.DeleteDraft(id) if err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) return } c.JSON(http.StatusOK, gin.H{"status": newStatus, "id": id.String()}) } // ReorderItems изменяет порядок позиции в черновике func (h *DraftsHandler) ReorderItems(c *gin.Context) { type ReorderRequest struct { ItemID uuid.UUID `json:"item_id" binding:"required"` NewOrder int `json:"new_order" binding:"required"` } var req ReorderRequest if err := c.ShouldBindJSON(&req); err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) return } // Получаем draftID из параметров пути draftID, err := uuid.Parse(c.Param("id")) if err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": "invalid draft id"}) return } if err := h.service.ReorderItem(draftID, req.ItemID, req.NewOrder); err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) return } c.JSON(http.StatusOK, gin.H{"success": true}) }