From 5085d58bc711918f96215a5374f0ece5a5060fcf Mon Sep 17 00:00:00 2001 From: Prakhar Pratyush Date: Thu, 7 Mar 2024 16:12:15 +0530 Subject: [PATCH] corporate: Fix to check if the plan we are processing is paid or not. Earlier, the 'self.on_paid_plan()' check was verifying if the billing_session/customer is on paid plan and not the plan we are processing. This resulted in a bug. While processing a legacy plan, a customer switches from legacy plan to a paid plan resulting in the 'self.on_paid_plan()' check returning True. It leads to invoicing legacy plan which shouldn't happen. The fix is to check if the plan we are processing is paid or not instead of the remote_realm/remote_server plan_type. --- corporate/lib/stripe.py | 4 ++-- corporate/models.py | 11 +++++++++++ 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/corporate/lib/stripe.py b/corporate/lib/stripe.py index ce14289b66..0a42446106 100644 --- a/corporate/lib/stripe.py +++ b/corporate/lib/stripe.py @@ -2762,10 +2762,10 @@ class BillingSession(ABC): self.make_end_of_cycle_updates_if_needed(plan, event_time) # The primary way to not create an invoice for a plan is to not have - # any new ledger entry. The 'self.on_paid_plan()' check adds an extra + # any new ledger entry. The 'plan.is_paid()' check adds an extra # layer of defense to avoid creating any invoices for customers not on # paid plan. It saves a DB query too. - if self.on_paid_plan(): + if plan.is_paid(): if plan.invoicing_status == CustomerPlan.INVOICING_STATUS_INITIAL_INVOICE_TO_BE_SENT: invoiced_through_id = -1 licenses_base = None diff --git a/corporate/models.py b/corporate/models.py index 7d680d3509..5922528a0c 100644 --- a/corporate/models.py +++ b/corporate/models.py @@ -390,6 +390,14 @@ class CustomerPlan(AbstractCustomerPlan): TIER_SELF_HOSTED_ENTERPRISE = 105 tier = models.SmallIntegerField() + PAID_PLAN_TIERS = [ + TIER_CLOUD_STANDARD, + TIER_CLOUD_PLUS, + TIER_SELF_HOSTED_BASIC, + TIER_SELF_HOSTED_BUSINESS, + TIER_SELF_HOSTED_ENTERPRISE, + ] + ACTIVE = 1 DOWNGRADE_AT_END_OF_CYCLE = 2 FREE_TRIAL = 3 @@ -466,6 +474,9 @@ class CustomerPlan(AbstractCustomerPlan): def is_free_trial(self) -> bool: return self.status == CustomerPlan.FREE_TRIAL + def is_paid(self) -> bool: + return self.tier in self.PAID_PLAN_TIERS + def get_current_plan_by_customer(customer: Customer) -> Optional[CustomerPlan]: return CustomerPlan.objects.filter(