diff --git a/internal/logic/public/order/promoPricing.go b/internal/logic/public/order/promoPricing.go index 431451d..cfaf6a1 100644 --- a/internal/logic/public/order/promoPricing.go +++ b/internal/logic/public/order/promoPricing.go @@ -58,3 +58,26 @@ func calculatePurchasePrice( result.DiscountAmount = originalPrice - result.PayableBase return result, nil } + +func calculateRenewalPrice( + ctx context.Context, + svcCtx *svc.ServiceContext, + userID int64, + subscribeID int64, + unitPrice int64, + quantity int64, + discounts []types.SubscribeDiscount, + eligibleForDiscount bool, +) (*orderPriceResult, error) { + return calculatePurchasePrice( + ctx, + svcCtx, + userID, + subscribeID, + unitPrice, + quantity, + discounts, + eligibleForDiscount, + false, + ) +} diff --git a/internal/logic/public/order/promoPricing_test.go b/internal/logic/public/order/promoPricing_test.go index 1f84f37..63874ee 100644 --- a/internal/logic/public/order/promoPricing_test.go +++ b/internal/logic/public/order/promoPricing_test.go @@ -297,3 +297,78 @@ func TestCalculatePurchasePriceNewUserGatedByFirstPurchase(t *testing.T) { t.Fatalf("PayableBase = %d, want 900 (regular 90%% discount)", result.PayableBase) } } + +func TestCalculateRenewalPriceCampaignAppliesToReturningUsers(t *testing.T) { + model := &fakePromoModel{rules: []*promo.RuleWithPrice{ + { + Rule: promo.Rule{ + Id: 21, + Name: "renewal campaign", + Type: promo.RuleTypeCampaign, + Enabled: true, + }, + PromoPrice: 700, + }, + }} + svcCtx := &svc.ServiceContext{DB: &gorm.DB{}, PromoModel: model} + + result, err := calculateRenewalPrice( + context.Background(), + svcCtx, + 42, + 2, + 500, + 3, + []types.SubscribeDiscount{{Quantity: 3, Discount: 80}}, + true, + ) + if err != nil { + t.Fatalf("calculateRenewalPrice returned error: %v", err) + } + if result.OriginalPrice != 1500 { + t.Fatalf("OriginalPrice = %d, want 1500", result.OriginalPrice) + } + if result.PayableBase != 700 { + t.Fatalf("PayableBase = %d, want 700", result.PayableBase) + } + if result.DiscountAmount != 0 { + t.Fatalf("DiscountAmount = %d, want 0 when promo applies", result.DiscountAmount) + } + if result.PromoRuleId != 21 { + t.Fatalf("PromoRuleId = %d, want 21", result.PromoRuleId) + } + if result.PromoDiscount != 800 { + t.Fatalf("PromoDiscount = %d, want 800", result.PromoDiscount) + } +} + +func TestCalculateRenewalPriceFallsBackToRegularDiscountWhenPromoMisses(t *testing.T) { + model := &fakePromoModel{rules: nil} + svcCtx := &svc.ServiceContext{DB: &gorm.DB{}, PromoModel: model} + + result, err := calculateRenewalPrice( + context.Background(), + svcCtx, + 42, + 2, + 500, + 3, + []types.SubscribeDiscount{{Quantity: 3, Discount: 80}}, + true, + ) + if err != nil { + t.Fatalf("calculateRenewalPrice returned error: %v", err) + } + if result.OriginalPrice != 1500 { + t.Fatalf("OriginalPrice = %d, want 1500", result.OriginalPrice) + } + if result.PayableBase != 1200 { + t.Fatalf("PayableBase = %d, want 1200", result.PayableBase) + } + if result.DiscountAmount != 300 { + t.Fatalf("DiscountAmount = %d, want 300", result.DiscountAmount) + } + if result.PromoRuleId != 0 || result.PromoDiscount != 0 { + t.Fatalf("promo fields = (%d, %d), want (0, 0)", result.PromoRuleId, result.PromoDiscount) + } +} diff --git a/internal/logic/public/order/renewalLogic.go b/internal/logic/public/order/renewalLogic.go index 269d01a..a8b0ea6 100644 --- a/internal/logic/public/order/renewalLogic.go +++ b/internal/logic/public/order/renewalLogic.go @@ -3,7 +3,6 @@ package order import ( "context" "encoding/json" - "math" "time" "github.com/google/uuid" @@ -165,13 +164,28 @@ func (l *RenewalLogic) Renewal(req *types.RenewalOrderRequest) (resp *types.Rene ) return nil, err } - var discount float64 = 1 - if len(newUserDiscount.Discounts) > 0 { - discount = getDiscount(newUserDiscount.Discounts, req.Quantity, newUserDiscount.EligibleForDiscount) + priceResult, err := calculateRenewalPrice( + l.ctx, + l.svcCtx, + entitlement.EffectiveUserID, + userSubscribe.SubscribeId, + sub.UnitPrice, + req.Quantity, + newUserDiscount.Discounts, + newUserDiscount.EligibleForDiscount, + ) + if err != nil { + l.Errorw("[Renewal] Promo price calculation error", + logger.Field("error", err.Error()), + logger.Field("user_id", u.Id), + logger.Field("effective_user_id", entitlement.EffectiveUserID), + logger.Field("subscribe_id", userSubscribe.SubscribeId), + ) + return nil, err } - price := sub.UnitPrice * req.Quantity - amount := int64(math.Round(float64(price) * discount)) - discountAmount := price - amount + price := priceResult.OriginalPrice + amount := priceResult.PayableBase + discountAmount := priceResult.DiscountAmount // Validate amount to prevent overflow if amount > MaxOrderAmount { @@ -269,6 +283,8 @@ func (l *RenewalLogic) Renewal(req *types.RenewalOrderRequest) (resp *types.Rene Amount: amount, GiftAmount: deductionAmount, Discount: discountAmount, + PromoRuleId: priceResult.PromoRuleId, + PromoDiscount: priceResult.PromoDiscount, Coupon: req.Coupon, CouponDiscount: coupon, PaymentId: payment.Id,