package distribution import "testing" func TestDefaultRules(t *testing.T) { cases := []struct { mode string check func(Rule) bool }{ {ModeTwoPartyDistributorReceive, func(r Rule) bool { return r.DistributorReceiveRatio == 35 && r.PlatformSellRatio == 40 }}, {ModeTwoPartyPlatformReceive, func(r Rule) bool { return r.PlatformReceiveRatio == 35 && r.DistributorSellRatio == 40 }}, {ModeThreeParty, func(r Rule) bool { return r.DistributorARatio == 35 && r.DistributorBRatio == 40 }}, } for _, item := range cases { rule, err := DefaultRule(item.mode) if err != nil || !item.check(rule) || rule.ServiceFeeValue != 25 || rule.ServiceFeeUnit != ServiceFeeUnitPercent || rule.PaymentFeeBPS != 50 { t.Fatalf("default rule %s = %#v, err=%v", item.mode, rule, err) } } if _, err := DefaultRule(ModeSiteSelfSell); err != ErrUnsupportedMode { t.Fatalf("site self-sell must not have a default split, err=%v", err) } } func TestNormalizeSplitPercent(t *testing.T) { if got := NormalizeSplitPercent(3.5); got != 35 { t.Fatalf("3.5 -> %v, want 35", got) } if got := NormalizeSplitPercent(4); got != 40 { t.Fatalf("4 -> %v, want 40", got) } if got := NormalizeSplitPercent(2.5); got != 25 { t.Fatalf("2.5 -> %v, want 25", got) } if got := NormalizeSplitPercent(35); got != 35 { t.Fatalf("35 stays %v", got) } } func TestCalculateSettlementSplitsProfitAfterOwnerAndFee(t *testing.T) { rule := Rule{ServiceFeeValue: 25, ServiceFeeUnit: ServiceFeeUnitPercent, PaymentFeeBPS: 50} got, err := CalculateSettlement(rule, SettlementInput{GrossPaidAmountCent: 10000, OwnerRentAmountCent: 6000}) if err != nil { t.Fatal(err) } // 通道费 50,利润 10000-6000-50=3950,服务费 25%=988 if got.NetPaidAmountCent != 10000 || got.PaymentFeeCent != 50 || got.SplitBaseAmountCent != 3950 || got.ServiceFeeCent != 988 { t.Fatalf("unexpected settlement: %#v", got) } } func TestCalculateSettlementTaxesEachPositivePartyProfit(t *testing.T) { rule := Rule{ServiceFeeUnit: ServiceFeeUnitFixedAmount, PaymentFeeBPS: 0, TaxEnabled: true, TaxRateBPS: 100} got, err := CalculateSettlement(rule, SettlementInput{ GrossPaidAmountCent: 10000, GrossProfitByParty: []PartyProfitInput{ {PartyRole: "distributor_a", IncomeAmountCent: 1000, CostAmountCent: 400}, {PartyRole: "distributor_b", IncomeAmountCent: 500, CostAmountCent: 700}, }, }) if err != nil { t.Fatal(err) } if got.TaxAmountCent != 6 || len(got.TaxByParty) != 2 || got.TaxByParty[0].TaxAmountCent != 6 || got.TaxByParty[1].TaxAmountCent != 0 { t.Fatalf("unexpected tax result: %#v", got) } } func TestCalculateSettlementRejectsRefundOverPayment(t *testing.T) { _, err := CalculateSettlement(Rule{ServiceFeeUnit: ServiceFeeUnitFixedAmount}, SettlementInput{GrossPaidAmountCent: 1, RefundAmountCent: 2}) if err != ErrInvalidAmount { t.Fatalf("err = %v", err) } } func TestSplitSettlementTwoPartyDistributorReceive(t *testing.T) { rule := Rule{Mode: ModeTwoPartyDistributorReceive, DistributorReceiveRatio: 35, PlatformSellRatio: 40} splits := SplitSettlement(rule, 3950, 0) if len(splits) != 2 { t.Fatalf("splits = %#v", splits) } if splits[0].PartyRole != "distributor_receive" || splits[0].AmountCent != 1383 { t.Fatalf("distributor split = %#v", splits[0]) } if splits[1].PartyRole != "platform_sell" || splits[1].AmountCent != 1580 { t.Fatalf("platform split = %#v", splits[1]) } if got := SplitRoundingCent(3950, 988, 0, splits); got != -1 { t.Fatalf("rounding = %d, want -1", got) } } func TestSplitSettlementAddsShelfMarkupDirectlyToSeller(t *testing.T) { rule := Rule{Mode: ModeTwoPartyPlatformReceive, DistributorSellRatio: 40, PlatformReceiveRatio: 35} // 提价利润 10000 分,分站自主加价 2000 分 splits := SplitSettlement(rule, 10000, 2000) // 卖方分站获得 40% (4000) + 2000 (加价) = 6000 if splits[0].PartyRole != "distributor_sell" || splits[0].AmountCent != 6000 { t.Fatalf("distributor sell = %#v, want 6000", splits[0]) } // 收号方获得 35% = 3500 if splits[1].AmountCent != 3500 { t.Fatalf("platform receive = %#v, want 3500", splits[1]) } // 尾差校验:平台服务费 2500,提价利润 10000 - (3500 + 4000) - 2500 = 0 if got := SplitRoundingCent(10000, 2500, 2000, splits); got != 0 { t.Fatalf("rounding = %d, want 0", got) } } func TestSplitSettlementThreeParty(t *testing.T) { rule := Rule{Mode: ModeThreeParty, DistributorARatio: 35, DistributorBRatio: 40} // 提价利润 10000,分站B自主加价 1500 splits := SplitSettlement(rule, 10000, 1500) if len(splits) != 2 || splits[0].AmountCent != 3500 || splits[1].AmountCent != 5500 { t.Fatalf("splits = %#v", splits) } }