package handler import ( "net/http" "strconv" "github.com/gin-gonic/gin" "gorm.io/gorm" "github.com/wangjia/jiu/backend/internal/middleware" "github.com/wangjia/jiu/backend/internal/model" "github.com/wangjia/jiu/backend/internal/service" ) type StockOutHandler struct { db *gorm.DB stockSvc *service.StockService } func NewStockOutHandler(db *gorm.DB, svc *service.StockService) *StockOutHandler { return &StockOutHandler{db: db, stockSvc: svc} } // List GET /api/v1/stock-out/orders func (h *StockOutHandler) List(c *gin.Context) { hotelID := middleware.GetHotelID(c) page, _ := strconv.Atoi(c.DefaultQuery("page", "1")) pageSize, _ := strconv.Atoi(c.DefaultQuery("page_size", "20")) query := h.db.Model(&model.StockOutOrder{}). Where("hotel_id = ? AND deleted_at IS NULL", hotelID) if status := c.Query("status"); status != "" { query = query.Where("status = ?", status) } if startDate := c.Query("start_date"); startDate != "" { query = query.Where("order_date >= ?", startDate) } if endDate := c.Query("end_date"); endDate != "" { query = query.Where("order_date <= ?", endDate) } var total int64 query.Count(&total) var orders []model.StockOutOrder query.Preload("Warehouse").Preload("Partner").Preload("Operator"). Offset((page - 1) * pageSize).Limit(pageSize). Order("id DESC").Find(&orders) c.JSON(http.StatusOK, gin.H{"data": orders, "total": total, "page": page, "page_size": pageSize}) } // Get GET /api/v1/stock-out/orders/:id func (h *StockOutHandler) Get(c *gin.Context) { hotelID := middleware.GetHotelID(c) var order model.StockOutOrder if err := h.db.Preload("Items.Product").Preload("Warehouse").Preload("Partner"). Where("id = ? AND hotel_id = ? AND deleted_at IS NULL", c.Param("id"), hotelID). First(&order).Error; err != nil { c.JSON(http.StatusNotFound, gin.H{"error": "not found"}) return } c.JSON(http.StatusOK, gin.H{"data": order}) } // Create POST /api/v1/stock-out/orders func (h *StockOutHandler) Create(c *gin.Context) { hotelID := middleware.GetHotelID(c) operatorID := middleware.GetUserID(c) var req model.StockOutOrder if err := c.ShouldBindJSON(&req); err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) return } req.HotelID = hotelID req.OperatorID = operatorID req.Status = "draft" orderNo, err := h.stockSvc.GenerateOrderNo(hotelID, "stock_out") if err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) return } req.OrderNo = orderNo var total float64 for i := range req.Items { req.Items[i].HotelID = hotelID req.Items[i].TotalPrice = req.Items[i].Quantity * req.Items[i].UnitPrice total += req.Items[i].TotalPrice } req.TotalAmount = total if err := h.db.Create(&req).Error; err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) return } c.JSON(http.StatusCreated, gin.H{"data": req}) } // Submit PUT /api/v1/stock-out/orders/:id/submit func (h *StockOutHandler) Submit(c *gin.Context) { hotelID := middleware.GetHotelID(c) result := h.db.Model(&model.StockOutOrder{}). Where("id = ? AND hotel_id = ? AND status = 'draft'", c.Param("id"), hotelID). Update("status", "pending") if result.RowsAffected == 0 { c.JSON(http.StatusBadRequest, gin.H{"error": "order not found or not in draft status"}) return } c.JSON(http.StatusOK, gin.H{"message": "submitted"}) } // Approve PUT /api/v1/stock-out/orders/:id/approve func (h *StockOutHandler) Approve(c *gin.Context) { hotelID := middleware.GetHotelID(c) reviewerID := middleware.GetUserID(c) id, _ := strconv.ParseUint(c.Param("id"), 10, 64) if err := h.stockSvc.ApproveStockOut(hotelID, id, reviewerID); err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) return } c.JSON(http.StatusOK, gin.H{"message": "approved"}) } // Reject PUT /api/v1/stock-out/orders/:id/reject func (h *StockOutHandler) Reject(c *gin.Context) { hotelID := middleware.GetHotelID(c) reviewerID := middleware.GetUserID(c) now := timeNow() result := h.db.Model(&model.StockOutOrder{}). Where("id = ? AND hotel_id = ? AND status = 'pending'", c.Param("id"), hotelID). Updates(map[string]interface{}{ "status": "rejected", "reviewer_id": reviewerID, "reviewed_at": now, }) if result.RowsAffected == 0 { c.JSON(http.StatusBadRequest, gin.H{"error": "order not found or not in pending status"}) return } c.JSON(http.StatusOK, gin.H{"message": "rejected"}) }