Files
hfb_sys/backend/internal/modules/distribution/refund_checkout_status_test.go
T
yml2213 d3d571357a refactor(order): 结账单状态常量化并锁定仲裁结单统计口径
仲裁结案会把结账单置为终态 settled,但该状态一直是裸字符串;同时 adminfinance 按日结算聚合只按
accepted 过滤,容易被误读成"仲裁单掉出统计"。本次只做卫生与口径固化,不改任何行为:
- order 新增 CheckoutStatus{Submitted,Countered,Accepted,Disputed,Settled},包内沿用非导出别名
- dispute 的仲裁结案与争议取消分支改用常量;distribution 不能反向依赖 order,本地声明同名常量
- adminfinance 聚合里的 oc.status = 'accepted' 改为常量绑定,并注释说明仲裁单不计入是有意口径
- 新增 distribution 结账单退款重算用例(该分支原先无覆盖):accepted/settled 按结账单实际租金,
  submitted 不被采用;dashboard 用例补一张 settled 结账单,钉住"不计入已结算订单数"
2026-09-16 12:49:21 +08:00

104 lines
3.5 KiB
Go

package distribution
import (
"testing"
"time"
"hfb_sys/backend/internal/model"
)
// TestApplyRefundTxUsesAgreedCheckoutActualRent 覆盖 ApplyRefundTx 的结账单分支:
// 只有已达成一致的结账单(accepted / settled)才按其实际消费租金与号主实得重算,
// 仲裁结案的 settled 属于同一口径;未达成一致的 submitted 结账单不得被采用。
func TestApplyRefundTxUsesAgreedCheckoutActualRent(t *testing.T) {
for _, tc := range []struct {
name string
checkoutStatus string
wantRentRefund int64
}{
{name: "accepted 结账单按实际租金重算", checkoutStatus: checkoutStatusAccepted, wantRentRefund: 2000},
{name: "settled 结账单(仲裁结案)同口径", checkoutStatus: checkoutStatusSettled, wantRentRefund: 2000},
{name: "submitted 结账单不被采用", checkoutStatus: "submitted", wantRentRefund: 1000},
} {
t.Run(tc.name, func(t *testing.T) {
financial := snapshotAndRefundWithCheckout(t, tc.checkoutStatus)
if financial.RefundAmountCent != tc.wantRentRefund {
t.Fatalf("refund_amount_cent = %d, want %d", financial.RefundAmountCent, tc.wantRentRefund)
}
})
}
}
// snapshotAndRefundWithCheckout 构造一笔分销订单(租金 10000、号主底价 6000、无押金)、
// 挂一张租金 8000 / 号主实得 5000 的结账单,快照后按 1000 退款,返回重算后的财务快照。
func snapshotAndRefundWithCheckout(t *testing.T, checkoutStatus string) model.DistributionOrderFinancial {
t.Helper()
db := distributionTestDB(t)
now := time.Now()
site := model.DistributionSite{
SiteCode: "site_refund_" + checkoutStatus,
SiteName: "退款口径测试站",
Domain: "refund-" + checkoutStatus + ".dist.com",
Status: "active",
DisplayAccountEnabled: true,
}
if err := db.Create(&site).Error; err != nil {
t.Fatal(err)
}
rule := model.DistributionRuleVersion{
SiteID: &site.ID,
Mode: ModeTwoPartyDistributorReceive,
Version: 1,
Status: "active",
DistributorReceiveRatio: 35,
PlatformSellRatio: 40,
ServiceFeeValue: 25,
ServiceFeeUnit: ServiceFeeUnitPercent,
PaymentFeeBPS: 50,
EffectiveFrom: now.Add(-time.Hour),
}
if err := db.Create(&rule).Error; err != nil {
t.Fatal(err)
}
order := model.RentalOrder{
OrderNo: "ORD-REFUND-" + checkoutStatus,
ListingID: 9101,
AccountID: 9102,
OwnerID: 11,
RenterID: 12,
Status: "renting",
RentAmountCent: 10000,
OwnerRentAmountCent: 6000,
PlatformFeeCent: 4000,
DepositAmountCent: 0,
DistributionSiteID: &site.ID,
DistributionRuleVersionID: &rule.ID,
}
if err := db.Create(&order).Error; err != nil {
t.Fatal(err)
}
checkout := model.OrderCheckout{
OrderID: order.ID,
InitiatedBy: 12,
Status: checkoutStatus,
RentAmountCent: 8000,
OwnerRentAmountCent: 5000,
}
if err := db.Create(&checkout).Error; err != nil {
t.Fatal(err)
}
if err := SnapshotPaidOrderTx(db, order.ID, 10000); err != nil {
t.Fatalf("SnapshotPaidOrderTx failed: %v", err)
}
if err := ApplyRefundTx(db, order.ID, 1000); err != nil {
t.Fatalf("ApplyRefundTx failed: %v", err)
}
var financial model.DistributionOrderFinancial
if err := db.Where("order_id = ?", order.ID).First(&financial).Error; err != nil {
t.Fatal(err)
}
return financial
}