package handler import ( "fmt" "net/http" "testing" "time" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/wangjia/jiu/backend/internal/model" "github.com/wangjia/jiu/backend/testutil" ) func TestStockOutHandler_FullFlow(t *testing.T) { db := testutil.SetupTestDB() hotel := testutil.CreateTestHotel(db, "SO001") user := testutil.CreateTestUser(db, hotel.ID, "admin", "pass", "admin") warehouse := testutil.CreateTestWarehouse(db, hotel.ID, "Main") product := testutil.CreateTestProduct(db, hotel.ID, "Whiskey") token := getAuthToken(user.ID, hotel.ID, "admin") r := setupProtectedRouter(db) // 先建立库存 db.Create(&model.Inventory{ HotelID: hotel.ID, WarehouseID: warehouse.ID, ProductID: product.ID, Quantity: 100, }) // 1. 创建出库单 w := makeRequest(r, "POST", "/api/v1/stock-out/orders", token, map[string]interface{}{ "warehouse_id": warehouse.ID, "order_date": time.Now().Format(time.RFC3339), "items": []map[string]interface{}{ { "product_id": product.ID, "quantity": 15.0, "unit_price": 20.0, }, }, }) require.Equal(t, http.StatusCreated, w.Code) orderID := extractID(w) assert.NotZero(t, orderID) data := parseResponse(w)["data"].(map[string]interface{}) assert.Equal(t, "draft", data["status"]) assert.NotEmpty(t, data["order_no"]) // 2. 提交 w = makeRequest(r, "PUT", fmt.Sprintf("/api/v1/stock-out/orders/%d/submit", orderID), token, nil) require.Equal(t, http.StatusOK, w.Code) // 3. 获取详情 w = makeRequest(r, "GET", fmt.Sprintf("/api/v1/stock-out/orders/%d", orderID), token, nil) require.Equal(t, http.StatusOK, w.Code) detailData := parseResponse(w)["data"].(map[string]interface{}) assert.Equal(t, "pending", detailData["status"]) // 4. 审核通过 w = makeRequest(r, "PUT", fmt.Sprintf("/api/v1/stock-out/orders/%d/approve", orderID), token, nil) require.Equal(t, http.StatusOK, w.Code) // 5. 验证库存减少 var inv model.Inventory db.Where("hotel_id = ? AND warehouse_id = ? AND product_id = ?", hotel.ID, warehouse.ID, product.ID).First(&inv) assert.Equal(t, float64(85), inv.Quantity) } func TestStockOutHandler_InsufficientStock(t *testing.T) { db := testutil.SetupTestDB() hotel := testutil.CreateTestHotel(db, "SO002") user := testutil.CreateTestUser(db, hotel.ID, "admin", "pass", "admin") warehouse := testutil.CreateTestWarehouse(db, hotel.ID, "Warehouse") product := testutil.CreateTestProduct(db, hotel.ID, "Vodka") token := getAuthToken(user.ID, hotel.ID, "admin") r := setupProtectedRouter(db) // 库存只有 5 db.Create(&model.Inventory{ HotelID: hotel.ID, WarehouseID: warehouse.ID, ProductID: product.ID, Quantity: 5, }) // 创建出库单要出 10 w := makeRequest(r, "POST", "/api/v1/stock-out/orders", token, map[string]interface{}{ "warehouse_id": warehouse.ID, "order_date": time.Now().Format(time.RFC3339), "items": []map[string]interface{}{ {"product_id": product.ID, "quantity": 10.0}, }, }) orderID := extractID(w) makeRequest(r, "PUT", fmt.Sprintf("/api/v1/stock-out/orders/%d/submit", orderID), token, nil) // 审核应该失败 w = makeRequest(r, "PUT", fmt.Sprintf("/api/v1/stock-out/orders/%d/approve", orderID), token, nil) assert.Equal(t, http.StatusBadRequest, w.Code) } func TestStockOutHandler_List(t *testing.T) { db := testutil.SetupTestDB() hotel := testutil.CreateTestHotel(db, "SO003") user := testutil.CreateTestUser(db, hotel.ID, "admin", "pass", "admin") warehouse := testutil.CreateTestWarehouse(db, hotel.ID, "Warehouse") product := testutil.CreateTestProduct(db, hotel.ID, "Beer") token := getAuthToken(user.ID, hotel.ID, "admin") r := setupProtectedRouter(db) // 先建立库存 db.Create(&model.Inventory{HotelID: hotel.ID, WarehouseID: warehouse.ID, ProductID: product.ID, Quantity: 100}) // 创建 2 个出库单 for i := 0; i < 2; i++ { makeRequest(r, "POST", "/api/v1/stock-out/orders", token, map[string]interface{}{ "warehouse_id": warehouse.ID, "order_date": time.Now().Format(time.RFC3339), "items": []map[string]interface{}{ {"product_id": product.ID, "quantity": 1.0}, }, }) } w := makeRequest(r, "GET", "/api/v1/stock-out/orders", token, nil) assert.Equal(t, http.StatusOK, w.Code) resp := parseResponse(w) assert.Equal(t, float64(2), resp["total"].(float64)) } func TestStockOutHandler_Reject(t *testing.T) { db := testutil.SetupTestDB() hotel := testutil.CreateTestHotel(db, "SO004") user := testutil.CreateTestUser(db, hotel.ID, "admin", "pass", "admin") warehouse := testutil.CreateTestWarehouse(db, hotel.ID, "Warehouse") product := testutil.CreateTestProduct(db, hotel.ID, "Rum") token := getAuthToken(user.ID, hotel.ID, "admin") r := setupProtectedRouter(db) db.Create(&model.Inventory{HotelID: hotel.ID, WarehouseID: warehouse.ID, ProductID: product.ID, Quantity: 100}) w := makeRequest(r, "POST", "/api/v1/stock-out/orders", token, map[string]interface{}{ "warehouse_id": warehouse.ID, "order_date": time.Now().Format(time.RFC3339), "items": []map[string]interface{}{ {"product_id": product.ID, "quantity": 5.0}, }, }) orderID := extractID(w) makeRequest(r, "PUT", fmt.Sprintf("/api/v1/stock-out/orders/%d/submit", orderID), token, nil) // 驳回 w = makeRequest(r, "PUT", fmt.Sprintf("/api/v1/stock-out/orders/%d/reject", orderID), token, nil) assert.Equal(t, http.StatusOK, w.Code) // 验证状态 w = makeRequest(r, "GET", fmt.Sprintf("/api/v1/stock-out/orders/%d", orderID), token, nil) detailData := parseResponse(w)["data"].(map[string]interface{}) assert.Equal(t, "rejected", detailData["status"]) } func TestStockOutHandler_GetNotFound(t *testing.T) { db := testutil.SetupTestDB() hotel := testutil.CreateTestHotel(db, "SO005") user := testutil.CreateTestUser(db, hotel.ID, "admin", "pass", "admin") token := getAuthToken(user.ID, hotel.ID, "admin") r := setupProtectedRouter(db) w := makeRequest(r, "GET", "/api/v1/stock-out/orders/99999", token, nil) assert.Equal(t, http.StatusNotFound, w.Code) }