Files
jiu/backend/internal/service/migrate_test.go
T
wangjia 4fd0bb8b83 feat(backend): 出入库定价字段消歧 + 总利润落库 + 成本仅管理员可见
- 字段重命名(旧列弃用保留,启动幂等回填 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
2026-07-03 12:44:22 +08:00

90 lines
4.1 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package service
import (
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/wangjia/jiu/backend/internal/model"
"github.com/wangjia/jiu/backend/testutil"
)
// BackfillPricingColumns:旧列(unit_price/total_price/total_amount)拷入新列,幂等。
// 测试库 DDL 已是新列,这里手动补建旧列模拟升级前的生产库形态。
func TestBackfillPricingColumns_Idempotent(t *testing.T) {
db := testutil.SetupTestDB()
for _, ddl := range []string{
`ALTER TABLE stock_in_items ADD COLUMN unit_price REAL DEFAULT 0`,
`ALTER TABLE stock_in_items ADD COLUMN total_price REAL DEFAULT 0`,
`ALTER TABLE stock_in_orders ADD COLUMN total_amount REAL DEFAULT 0`,
`ALTER TABLE stock_out_items ADD COLUMN unit_price REAL DEFAULT 0`,
`ALTER TABLE stock_out_items ADD COLUMN total_price REAL DEFAULT 0`,
`ALTER TABLE stock_out_orders ADD COLUMN total_amount REAL DEFAULT 0`,
} {
require.NoError(t, db.Exec(ddl).Error)
}
shop := testutil.CreateTestShop(db, "MIG1")
// 升级前形态:只有旧列有值(新列 0)
inOrder := model.StockInOrder{TenantBase: model.TenantBase{ShopID: shop.ID}, OrderNo: "R1", WarehouseID: 1, OperatorID: 1, Status: "approved"}
require.NoError(t, db.Create(&inOrder).Error)
db.Exec(`UPDATE stock_in_orders SET total_amount = 500 WHERE id = ?`, inOrder.ID)
inItem := model.StockInItem{OrderID: inOrder.ID, ShopID: shop.ID, ProductID: 1, Quantity: 10}
require.NoError(t, db.Create(&inItem).Error)
db.Exec(`UPDATE stock_in_items SET unit_price = 50, total_price = 500 WHERE id = ?`, inItem.ID)
outOrder := model.StockOutOrder{TenantBase: model.TenantBase{ShopID: shop.ID}, OrderNo: "C1", WarehouseID: 1, OperatorID: 1, Status: "approved"}
require.NoError(t, db.Create(&outOrder).Error)
db.Exec(`UPDATE stock_out_orders SET total_amount = 800 WHERE id = ?`, outOrder.ID)
outItem := model.StockOutItem{OrderID: outOrder.ID, ShopID: shop.ID, ProductID: 1, Quantity: 2, SalePrice: 400}
require.NoError(t, db.Create(&outItem).Error)
db.Exec(`UPDATE stock_out_items SET unit_price = 355, total_price = 710, sale_amount = 0 WHERE id = ?`, outItem.ID)
// 待定价出库单:sale_price=0 → sale_amount/profit 均应保持 0
pendOrder := model.StockOutOrder{TenantBase: model.TenantBase{ShopID: shop.ID}, OrderNo: "C2", WarehouseID: 1, OperatorID: 1, Status: "approved"}
require.NoError(t, db.Create(&pendOrder).Error)
pendItem := model.StockOutItem{OrderID: pendOrder.ID, ShopID: shop.ID, ProductID: 1, Quantity: 3}
require.NoError(t, db.Create(&pendItem).Error)
db.Exec(`UPDATE stock_out_items SET unit_price = 100, total_price = 300 WHERE id = ?`, pendItem.ID)
BackfillPricingColumns(db)
var gotIn model.StockInItem
db.First(&gotIn, inItem.ID)
assert.Equal(t, float64(50), gotIn.CostPrice)
assert.Equal(t, float64(500), gotIn.CostAmount)
var gotInOrder model.StockInOrder
db.First(&gotInOrder, inOrder.ID)
assert.Equal(t, float64(500), gotInOrder.CostTotal)
var gotOut model.StockOutItem
db.First(&gotOut, outItem.ID)
assert.Equal(t, float64(355), gotOut.CostPrice)
assert.Equal(t, float64(710), gotOut.CostAmount)
assert.Equal(t, float64(800), gotOut.SaleAmount) // 400×2
var gotOutOrder model.StockOutOrder
db.First(&gotOutOrder, outOrder.ID)
assert.Equal(t, float64(800), gotOutOrder.SaleTotal)
assert.Equal(t, float64(90), gotOutOrder.ProfitTotal) // (400-355)×2
var gotPend model.StockOutItem
db.First(&gotPend, pendItem.ID)
assert.Equal(t, float64(100), gotPend.CostPrice)
assert.Equal(t, float64(0), gotPend.SaleAmount)
var gotPendOrder model.StockOutOrder
db.First(&gotPendOrder, pendOrder.ID)
assert.Equal(t, float64(0), gotPendOrder.ProfitTotal)
// 幂等:第二次执行不改变结果(人为改一个新列值验证不会被旧值覆盖——新列非 0 即跳过)
db.Exec(`UPDATE stock_out_items SET sale_price = 450, sale_amount = 900 WHERE id = ?`, outItem.ID)
BackfillPricingColumns(db)
var again model.StockOutItem
db.First(&again, outItem.ID)
assert.Equal(t, float64(900), again.SaleAmount) // 未被回填覆盖
var againOrder model.StockOutOrder
db.First(&againOrder, outOrder.ID)
assert.Equal(t, float64(800), againOrder.SaleTotal) // 已有值不重拷
}