4fd0bb8b83
- 字段重命名(旧列弃用保留,启动幂等回填 BackfillPricingColumns): 入库 unit_price/total_price/total_amount → cost_price/cost_amount/cost_total; 出库同前缀 + 新增 sale_amount(售价小计)/ sale_total(应收)/ profit_total(总利润) - 建单落三值;确认售价联动重算 sale_amount/sale_total/profit_total; 确认进价回填成本后联动重算受影响出库单利润(recalcStockOutProfit) - 出库 List/Get 对 operator/readonly 抹零成本与利润(stripStockOutCost 服务端兜底) - 兼容一版:Create/Update 接受旧 key unit_price 回落(v1.0.87 及之前客户端) - SUM 聚合/价格趋势/导入/种子工具同步切新列;测试全量改名 + 6 个新回归 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01JJ1g8XV1YhhmHRzhwWEW7o
114 lines
3.8 KiB
Go
114 lines
3.8 KiB
Go
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))
|
||
}
|