diff --git a/backend/database/db.go b/backend/database/db.go index 7452108..c49899f 100644 --- a/backend/database/db.go +++ b/backend/database/db.go @@ -26,5 +26,6 @@ func InitDB() { &models.CartItem{}, &models.Order{}, &models.OrderItem{}, + &models.StockMovement{}, ) } diff --git a/backend/handlers/stock_handler.go b/backend/handlers/stock_handler.go new file mode 100644 index 0000000..e761e34 --- /dev/null +++ b/backend/handlers/stock_handler.go @@ -0,0 +1,116 @@ +package handlers + +import ( + "fmt" + "net/http" + + "github.com/gin-gonic/gin" + "gorm.io/gorm" + + "fishfish/database" + "fishfish/models" +) + +func StockIn(c *gin.Context) { + var input struct { + ItemID uint `json:"item_id" binding:"required"` + Quantity float64 `json:"quantity" binding:"required"` + Comment string `json:"comment"` + } + + if err := c.ShouldBindJSON(&input); err != nil { + c.JSON(http.StatusBadRequest, gin.H{"error": "Неверные данные"}) + return + } + + if input.Quantity <= 0 { + c.JSON(http.StatusBadRequest, gin.H{"error": "Количество должно быть положительным"}) + return + } + + err := database.DB.Transaction(func(tx *gorm.DB) error { + result := tx.Model(&models.Item{}). + Where("id = ? ", input.ItemID). + Update("stock", gorm.Expr("stock + ?", input.Quantity)) + + if result.Error != nil { + return result.Error + } + + if result.RowsAffected == 0 { + return fmt.Errorf("товар не найден") + } + + movement := models.StockMovement{ + ItemID: input.ItemID, + Type: "in", + Quantity: input.Quantity, + Reason: "purchase", + Comment: input.Comment, + } + + if err := tx.Create(&movement).Error; err != nil { + return err + } + + return nil + }) + if err != nil { + c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) + return + } + + c.JSON(http.StatusOK, gin.H{"message": "Приход оформлен"}) +} + +func StockOut(c *gin.Context) { + var input struct { + ItemID uint `json:"item_id" binding:"required"` + Quantity float64 `json:"quantity" binding:"required"` + Comment string `json:"comment"` + } + + if err := c.ShouldBindJSON(&input); err != nil { + c.JSON(http.StatusBadRequest, gin.H{"error": "Неверные данные"}) + return + } + + if input.Quantity <= 0 { + c.JSON(http.StatusBadRequest, gin.H{"error": "Количество должно быть положительным"}) + return + } + + err := database.DB.Transaction(func(tx *gorm.DB) error { + result := tx.Model(&models.Item{}). + Where("id = ? AND stock >= ? ", input.ItemID, input.Quantity). + Update("stock", gorm.Expr("stock - ?", input.Quantity)) + + if result.Error != nil { + return result.Error + } + + if result.RowsAffected == 0 { + return fmt.Errorf("товар не найден или не достаточно товара") + } + + movement := models.StockMovement{ + ItemID: input.ItemID, + Type: "out", + Quantity: input.Quantity, + Reason: "sale", + Comment: input.Comment, + } + + if err := tx.Create(&movement).Error; err != nil { + return err + } + + return nil + }) + if err != nil { + c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) + return + } + + c.JSON(http.StatusOK, gin.H{"message": "Уход оформлен"}) +} diff --git a/backend/main.go b/backend/main.go index 3aefb7d..3aa41e8 100644 --- a/backend/main.go +++ b/backend/main.go @@ -67,6 +67,8 @@ func main() { admin.GET("/orders", handlers.GetAllOrders) admin.PATCH("/orders/:id", handlers.UpdateOrderStatus) admin.PATCH("/items/:id/avatar", handlers.UploadItemAvatar) + admin.POST("/stock/in", handlers.StockIn) + admin.POST("/stock/out", handlers.StockOut) } } diff --git a/backend/models/stock.go b/backend/models/stock.go new file mode 100644 index 0000000..e5fcc67 --- /dev/null +++ b/backend/models/stock.go @@ -0,0 +1,17 @@ +package models + +import ( + "time" +) + +type StockMovement struct { + ID uint `json:"id" gorm:"primaryKey"` + ItemID uint `json:"item_id"` + Item Item `json:"item" gorm:"foreignKey:ItemID"` + Type string `json:"type"` + Quantity float64 `json:"quantity"` + Reason string `json:"reason" gorm:"not null"` + OrderID *uint `json:"order_id"` + Comment string `json:"comment"` + CreatedAt time.Time +} diff --git a/frontend/src/api/index.js b/frontend/src/api/index.js index 6d79b20..9743829 100644 --- a/frontend/src/api/index.js +++ b/frontend/src/api/index.js @@ -64,3 +64,5 @@ export const updateOrderStatus = (id, data) => axios.patch(`${API_URL}/admin/orders/${id}`, data); export const registerAdmin = (data) => axios.post(`${API_URL}/admin/register`, data); + +export const stockIn = (data) => axios.post(`${API_URL}/admin/stock/in`, data) diff --git a/frontend/src/pages/ItemPage.jsx b/frontend/src/pages/ItemPage.jsx index 25f8e6a..62a5cab 100644 --- a/frontend/src/pages/ItemPage.jsx +++ b/frontend/src/pages/ItemPage.jsx @@ -64,7 +64,7 @@ export default function ItemPage() {
{/* ЛЕВО — фото */} -
+
{item.name} {/* ПРАВО — всё инфо в одной колонке */} -
+

{item.name}