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
90 lines
4.1 KiB
Go
90 lines
4.1 KiB
Go
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) // 已有值不重拷
|
||
}
|