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} } // GetDraft 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) } // GetDictionaries (бывший GetStores) 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) } // GetStores - устаревший метод для обратной совместимости // Возвращает массив складов func (h *DraftsHandler) GetStores(c *gin.Context) { userID := c.MustGet("userID").(uuid.UUID) // Используем логику из GetDictionaries, но возвращаем только stores 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 } // dict["stores"] уже содержит []catalog.Store c.JSON(http.StatusOK, dict["stores"]) } // UpdateItemDTO type UpdateItemDTO struct { ProductID *string `json:"product_id"` ContainerID *string `json:"container_id"` Quantity float64 `json:"quantity"` Price float64 `json:"price"` } // AddDraftItem - POST /api/drafts/:id/items 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) } // DeleteDraftItem - DELETE /api/drafts/:id/items/:itemId 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, }) } func (h *DraftsHandler) UpdateItem(c *gin.Context) { // userID := c.MustGet("userID").(uuid.UUID) // Пока не используется в UpdateItem, но можно добавить проверку владельца 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 && *req.ContainerID != "" { if uid, err := uuid.Parse(*req.ContainerID); err == nil { cID = &uid } } qty := decimal.NewFromFloat(req.Quantity) price := decimal.NewFromFloat(req.Price) if err := h.service.UpdateItem(draftID, itemID, pID, cID, qty, price); 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"` } 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); 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()}) } type DraftListItemDTO struct { ID string `json:"id"` DocumentNumber string `json:"document_number"` DateIncoming string `json:"date_incoming"` Status string `json:"status"` ItemsCount int `json:"items_count"` TotalSum float64 `json:"total_sum"` StoreName string `json:"store_name"` CreatedAt string `json:"created_at"` IsAppCreated bool `json:"is_app_created"` } func (h *DraftsHandler) GetDrafts(c *gin.Context) { userID := c.MustGet("userID").(uuid.UUID) // Читаем параметры периода из Query (default: 30 days) 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 = 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) { // 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 } 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()}) }