Skip to content

Commit 4330f01

Browse files
authored
ref: Plan query optimizations (#1131)
1 parent 7218333 commit 4330f01

File tree

3 files changed

+200
-82
lines changed

3 files changed

+200
-82
lines changed

api/internal/owner/serializers.py

+6-4
Original file line numberDiff line numberDiff line change
@@ -339,11 +339,13 @@ def update(self, instance: Owner, validated_data: Dict[str, Any]) -> object:
339339
instance, desired_plan
340340
)
341341

342-
sentry_plans = Plan.objects.filter(
343-
tier__tier_name=TierName.SENTRY.value, is_active=True
344-
).values_list("name", flat=True)
342+
plan = (
343+
Plan.objects.select_related("tier")
344+
.filter(name=desired_plan["value"])
345+
.first()
346+
)
345347

346-
if desired_plan["value"] in sentry_plans:
348+
if plan and plan.tier.tier_name == TierName.SENTRY.value:
347349
current_owner = self.context["view"].request.current_owner
348350
send_sentry_webhook(current_owner, instance)
349351

services/billing.py

+54-34
Original file line numberDiff line numberDiff line change
@@ -316,17 +316,33 @@ def get_schedule(self, owner: Owner):
316316
return stripe.SubscriptionSchedule.retrieve(subscription_schedule_id)
317317

318318
@_log_stripe_error
319-
def modify_subscription(self, owner, desired_plan):
319+
def modify_subscription(self, owner: Owner, desired_plan: dict):
320+
desired_plan_info = Plan.objects.filter(name=desired_plan["value"]).first()
321+
if not desired_plan_info:
322+
log.error(
323+
f"Plan {desired_plan['value']} not found",
324+
extra=dict(owner_id=owner.ownerid),
325+
)
326+
return
327+
320328
subscription = stripe.Subscription.retrieve(owner.stripe_subscription_id)
321-
proration_behavior = self._get_proration_params(owner, desired_plan)
329+
proration_behavior = self._get_proration_params(
330+
owner,
331+
desired_plan_info=desired_plan_info,
332+
desired_quantity=desired_plan["quantity"],
333+
)
322334
subscription_schedule_id = subscription.schedule
323335

324336
# proration_behavior indicates whether we immediately invoice a user or not. We only immediately
325337
# invoice a user if the user increases the number of seats or if the plan changes from monthly to yearly.
326338
# An increase in seats and/or plan implies the user is upgrading, hence 'is_upgrading' is a consequence
327339
# of proration_behavior providing an invoice, in this case, != "none"
328340
# TODO: change this to "self._is_upgrading_seats(owner, desired_plan) or self._is_extending_term(owner, desired_plan)"
329-
is_upgrading = True if proration_behavior != "none" else False
341+
is_upgrading = (
342+
True
343+
if proration_behavior != "none" and desired_plan_info.stripe_id
344+
else False
345+
)
330346

331347
# Divide logic bw immediate updates and scheduled updates
332348
# Immediate updates: when user upgrades seats or plan
@@ -335,15 +351,6 @@ def modify_subscription(self, owner, desired_plan):
335351
# Scheduled updates: when the user decreases seats or plan
336352
# If the user is not in a schedule, create a schedule
337353
# If the user is in a schedule, update the existing schedule
338-
339-
plan = Plan.objects.filter(name=desired_plan["value"]).first()
340-
if not plan or not plan.stripe_id:
341-
log.error(
342-
f"Plan {desired_plan['value']} not found",
343-
extra=dict(owner_id=owner.ownerid),
344-
)
345-
return
346-
347354
if is_upgrading:
348355
if subscription_schedule_id:
349356
log.info(
@@ -360,7 +367,7 @@ def modify_subscription(self, owner, desired_plan):
360367
items=[
361368
{
362369
"id": subscription["items"]["data"][0]["id"],
363-
"plan": plan.stripe_id,
370+
"plan": desired_plan_info.stripe_id,
364371
"quantity": desired_plan["quantity"],
365372
}
366373
],
@@ -404,7 +411,11 @@ def modify_subscription(self, owner, desired_plan):
404411
)
405412

406413
def _modify_subscription_schedule(
407-
self, owner: Owner, subscription, subscription_schedule_id, desired_plan
414+
self,
415+
owner: Owner,
416+
subscription: stripe.Subscription,
417+
subscription_schedule_id: str,
418+
desired_plan: dict,
408419
):
409420
current_subscription_start_date = subscription["current_period_start"]
410421
current_subscription_end_date = subscription["current_period_end"]
@@ -453,20 +464,18 @@ def _modify_subscription_schedule(
453464
metadata=self._get_checkout_session_and_subscription_metadata(owner),
454465
)
455466

456-
def _is_upgrading_seats(self, owner: Owner, desired_plan: dict) -> bool:
467+
def _is_upgrading_seats(self, owner: Owner, desired_quantity: int) -> bool:
457468
"""
458469
Returns `True` if purchasing more seats.
459470
"""
460-
return bool(
461-
owner.plan_user_count and owner.plan_user_count < desired_plan["quantity"]
462-
)
471+
return bool(owner.plan_user_count and owner.plan_user_count < desired_quantity)
463472

464-
def _is_extending_term(self, owner: Owner, desired_plan: dict) -> bool:
473+
def _is_extending_term(
474+
self, current_plan_info: Plan, desired_plan_info: Plan
475+
) -> bool:
465476
"""
466477
Returns `True` if switching from monthly to yearly plan.
467478
"""
468-
current_plan_info = Plan.objects.get(name=owner.plan)
469-
desired_plan_info = Plan.objects.get(name=desired_plan["value"])
470479

471480
return bool(
472481
current_plan_info
@@ -475,23 +484,24 @@ def _is_extending_term(self, owner: Owner, desired_plan: dict) -> bool:
475484
and desired_plan_info.billing_rate == PlanBillingRate.YEARLY.value
476485
)
477486

478-
def _is_similar_plan(self, owner: Owner, desired_plan: dict) -> bool:
487+
def _is_similar_plan(
488+
self,
489+
owner: Owner,
490+
current_plan_info: Plan,
491+
desired_plan_info: Plan,
492+
desired_quantity: int,
493+
) -> bool:
479494
"""
480495
Returns `True` if switching to a plan with similar term and seats.
481496
"""
482-
current_plan_info = Plan.objects.select_related("tier").get(name=owner.plan)
483-
desired_plan_info = Plan.objects.select_related("tier").get(
484-
name=desired_plan["value"]
485-
)
486-
487497
is_same_term = (
488498
current_plan_info
489499
and desired_plan_info
490500
and current_plan_info.billing_rate == desired_plan_info.billing_rate
491501
)
492502

493503
is_same_seats = (
494-
owner.plan_user_count and owner.plan_user_count == desired_plan["quantity"]
504+
owner.plan_user_count and owner.plan_user_count == desired_quantity
495505
)
496506
# If from PRO to TEAM, then not a similar plan
497507
if (
@@ -508,25 +518,35 @@ def _is_similar_plan(self, owner: Owner, desired_plan: dict) -> bool:
508518

509519
return bool(is_same_term and is_same_seats)
510520

511-
def _get_proration_params(self, owner: Owner, desired_plan: dict) -> str:
521+
def _get_proration_params(
522+
self, owner: Owner, desired_plan_info: Plan, desired_quantity: int
523+
) -> str:
524+
current_plan_info = Plan.objects.select_related("tier").get(name=owner.plan)
512525
if (
513-
self._is_upgrading_seats(owner, desired_plan)
514-
or self._is_extending_term(owner, desired_plan)
515-
or self._is_similar_plan(owner, desired_plan)
526+
self._is_upgrading_seats(owner=owner, desired_quantity=desired_quantity)
527+
or self._is_extending_term(
528+
current_plan_info=current_plan_info, desired_plan_info=desired_plan_info
529+
)
530+
or self._is_similar_plan(
531+
owner=owner,
532+
current_plan_info=current_plan_info,
533+
desired_plan_info=desired_plan_info,
534+
desired_quantity=desired_quantity,
535+
)
516536
):
517537
return "always_invoice"
518538
else:
519539
return "none"
520540

521-
def _get_success_and_cancel_url(self, owner):
541+
def _get_success_and_cancel_url(self, owner: Owner):
522542
short_services = {"github": "gh", "bitbucket": "bb", "gitlab": "gl"}
523543
base_path = f"/plan/{short_services[owner.service]}/{owner.username}"
524544
success_url = f"{settings.CODECOV_DASHBOARD_URL}{base_path}?success"
525545
cancel_url = f"{settings.CODECOV_DASHBOARD_URL}{base_path}?cancel"
526546
return success_url, cancel_url
527547

528548
@_log_stripe_error
529-
def create_checkout_session(self, owner: Owner, desired_plan):
549+
def create_checkout_session(self, owner: Owner, desired_plan: dict):
530550
success_url, cancel_url = self._get_success_and_cancel_url(owner)
531551
log.info(
532552
"Creating Stripe Checkout Session for owner",

0 commit comments

Comments
 (0)