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" ) // Trend:近 N 月按 order_date 聚合出库(in)/入库(out)金额,多租户隔离 func TestFinanceHandler_Trend(t *testing.T) { db := testutil.SetupTestDB() shop := testutil.CreateTestShop(db, "FIN001") other := testutil.CreateTestShop(db, "FIN001B") user := testutil.CreateTestUser(db, shop.ID, "admin", "pass", "admin") warehouse := testutil.CreateTestWarehouse(db, shop.ID, "Main") token := getAuthToken(user.ID, shop.ID, "admin") r := setupProtectedRouter(db) now := time.Now() thisMonth := time.Date(now.Year(), now.Month(), 5, 0, 0, 0, 0, time.UTC) lastMonth := thisMonth.AddDate(0, -1, 0) mkOut := func(shopID uint64, date time.Time, amount float64) { require.NoError(t, db.Create(&model.StockOutOrder{ TenantBase: model.TenantBase{ShopID: shopID}, OrderNo: fmt.Sprintf("CK-%d-%d", shopID, time.Now().UnixNano()), WarehouseID: warehouse.ID, OperatorID: user.ID, Status: "approved", OrderDate: model.Date{Time: date}, SaleTotal: amount, }).Error) } mkIn := func(shopID uint64, date time.Time, amount float64) { require.NoError(t, db.Create(&model.StockInOrder{ TenantBase: model.TenantBase{ShopID: shopID}, OrderNo: fmt.Sprintf("RK-%d-%d", shopID, time.Now().UnixNano()), WarehouseID: warehouse.ID, OperatorID: user.ID, Status: "approved", OrderDate: model.Date{Time: date}, CostTotal: amount, }).Error) } mkOut(shop.ID, thisMonth, 1000) mkOut(shop.ID, thisMonth, 860) mkOut(shop.ID, lastMonth, 500) mkIn(shop.ID, thisMonth, 700) mkOut(other.ID, thisMonth, 99999) // 别店数据不得串店 w := makeRequest(r, "GET", "/api/v1/finance/trend?months=2", token, nil) require.Equal(t, http.StatusOK, w.Code) data := parseResponse(w)["data"].([]interface{}) require.Len(t, data, 2) prev := data[0].(map[string]interface{}) cur := data[1].(map[string]interface{}) assert.Equal(t, lastMonth.Format("2006-01"), prev["month"]) assert.Equal(t, float64(500), prev["in"]) assert.Equal(t, float64(0), prev["out"]) assert.Equal(t, thisMonth.Format("2006-01"), cur["month"]) assert.Equal(t, float64(1860), cur["in"]) assert.Equal(t, float64(700), cur["out"]) } // ListRecords 日期区间:start_date/end_date 过滤 record_date(区间优先于 month) func TestFinanceHandler_ListRecords_DateRange(t *testing.T) { db := testutil.SetupTestDB() shop := testutil.CreateTestShop(db, "FIN002") user := testutil.CreateTestUser(db, shop.ID, "admin", "pass", "admin") token := getAuthToken(user.ID, shop.ID, "admin") r := setupProtectedRouter(db) mk := func(date string, amount float64) { d, err := time.Parse("2006-01-02", date) require.NoError(t, err) require.NoError(t, db.Create(&model.FinanceRecord{ ShopID: shop.ID, Type: "receipt", Amount: amount, Status: "closed", OperatorID: user.ID, RecordDate: d, }).Error) } mk("2026-04-10", 100) mk("2026-05-15", 200) mk("2026-06-20", 300) // 区间 [2026-05-01, 2026-06-30] → 命中 2 条 w := makeRequest(r, "GET", "/api/v1/finance/records?start_date=2026-05-01&end_date=2026-06-30", token, nil) require.Equal(t, http.StatusOK, w.Code) assert.Equal(t, float64(2), parseResponse(w)["total"].(float64)) // 只给 start → 下界过滤 w = makeRequest(r, "GET", "/api/v1/finance/records?start_date=2026-06-01", token, nil) assert.Equal(t, float64(1), parseResponse(w)["total"].(float64)) // 区间优先于 month(month 会被忽略) w = makeRequest(r, "GET", "/api/v1/finance/records?start_date=2026-04-01&end_date=2026-04-30&month=2026-06", token, nil) assert.Equal(t, float64(1), parseResponse(w)["total"].(float64)) }