Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Modify billing for Team plan (#637) #225

Merged
merged 26 commits into from
Nov 10, 2023
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
34093a3
feat: Modify billing for Team plan
JerrySentry Nov 2, 2023
73ca159
add unit test
JerrySentry Nov 2, 2023
8f0a5cf
fix: available plans for users while trialing
JerrySentry Nov 3, 2023
9e55518
linter fix
JerrySentry Nov 3, 2023
a876186
update unit tests
JerrySentry Nov 3, 2023
99034a8
plan_activated_users=None
JerrySentry Nov 3, 2023
825789b
feat: Add Sentry user to admin panel (#702)
JerrySentry Nov 6, 2023
9e5f09e
Revert "feat: Add Sentry user to admin panel (#702)"
JerrySentry Nov 6, 2023
f7f3863
add team plan stripe IDs
JerrySentry Nov 6, 2023
303a4d0
Merge branch 'api_731' into api_637
JerrySentry Nov 6, 2023
250fde4
update to use new product IDs
JerrySentry Nov 7, 2023
73c6d9c
Merge branch 'main' into api_637
JerrySentry Nov 7, 2023
3b5a442
Merge branch 'api_637' of github.com:codecov/codecov-api into api_637
JerrySentry Nov 7, 2023
0604356
delete /internal/plans endpoint and refactor plan validation
JerrySentry Nov 7, 2023
c95aba7
Merge branch 'main' into api_637
JerrySentry Nov 7, 2023
e9bba42
add a unit test
JerrySentry Nov 7, 2023
829e81b
Merge branch 'main' into api_637
JerrySentry Nov 7, 2023
e7f577b
s/repositories/uploads
JerrySentry Nov 7, 2023
4f5ea9c
unit test change
JerrySentry Nov 8, 2023
206841a
fix: Add isPublic filter to measurements filter
JerrySentry Nov 9, 2023
628052e
fix: s/Pro Team/Pro
JerrySentry Nov 9, 2023
9f31fc7
fix: rework logic for available plans from seats to activated users f…
JerrySentry Nov 9, 2023
4c0a519
Merge branch 'main' into api_637
JerrySentry Nov 9, 2023
7af18da
add propration logic for team plan
JerrySentry Nov 9, 2023
43b3214
address CR comments
JerrySentry Nov 9, 2023
dfbf514
Merge branch 'main' into api_637
JerrySentry Nov 10, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 15 additions & 3 deletions api/internal/owner/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,12 @@

from billing.helpers import available_plans
from codecov_auth.models import Owner
from plan.constants import PRO_PLANS, SENTRY_PAID_USER_PLAN_REPRESENTATIONS
from plan.constants import (
PAID_PLANS,
SENTRY_PAID_USER_PLAN_REPRESENTATIONS,
TEAM_PLAN_MAX_USERS,
TEAM_PLAN_REPRESENTATIONS,
)
from plan.service import PlanService
from services.billing import BillingService
from services.sentry import send_user_webhook as send_sentry_webhook
Expand Down Expand Up @@ -134,7 +139,7 @@
owner = self.context["view"].owner

# Validate quantity here because we need access to whole plan object
if plan["value"] in PRO_PLANS:
if plan["value"] in PAID_PLANS:
if "quantity" not in plan:
raise serializers.ValidationError(
f"Field 'quantity' required for updating to paid plans"
Expand All @@ -159,6 +164,13 @@
raise serializers.ValidationError(
f"Quantity or plan for paid plan must be different from the existing one"
)
if (
plan["value"] in TEAM_PLAN_REPRESENTATIONS
and plan["quantity"] > TEAM_PLAN_MAX_USERS
):
raise serializers.ValidationError(

Check warning on line 171 in api/internal/owner/serializers.py

View check run for this annotation

Codecov - Staging / codecov/patch

api/internal/owner/serializers.py#L171

Added line #L171 was not covered by tests

Check warning on line 171 in api/internal/owner/serializers.py

View check run for this annotation

Codecov - QA / codecov/patch

api/internal/owner/serializers.py#L171

Added line #L171 was not covered by tests

Check warning on line 171 in api/internal/owner/serializers.py

View check run for this annotation

Codecov Public QA / codecov/patch

api/internal/owner/serializers.py#L171

Added line #L171 was not covered by tests

Check warning on line 171 in api/internal/owner/serializers.py

View check run for this annotation

Codecov / codecov/patch

api/internal/owner/serializers.py#L171

Added line #L171 was not covered by tests
f"Quantity for Team plan cannot exceed {TEAM_PLAN_MAX_USERS}"
)
return plan


Expand All @@ -185,7 +197,7 @@
plan_name = list(stripe_plan_dict.keys())[
list(stripe_plan_dict.values()).index(plan_id)
]
marketing_plan_name = PRO_PLANS[plan_name].billing_rate
marketing_plan_name = PAID_PLANS[plan_name].billing_rate
return marketing_plan_name

def get_quantity(self, phase):
Expand Down
2 changes: 1 addition & 1 deletion api/internal/tests/views/test_account_viewset.py
Original file line number Diff line number Diff line change
Expand Up @@ -1109,7 +1109,7 @@ def test_update_sentry_plan_non_sentry_user(
assert res.json() == {
"plan": {
"value": [
"Invalid value for plan: users-sentrym; must be one of ['users-free', 'users-basic', 'users-pr-inappm', 'users-pr-inappy']"
"Invalid value for plan: users-sentrym; must be one of ['users-free', 'users-basic', 'users-pr-inappm', 'users-pr-inappy', 'users-teamm', 'users-teamy']"
]
}
}
Expand Down
90 changes: 90 additions & 0 deletions api/internal/tests/views/test_plans_viewset.py
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,36 @@ def test_list_plans_returns_200_and_plans(self):
"monthly_uploads_limit": None,
"trial_days": None,
},
{
"marketing_name": "Team",
"value": "users-teamm",
"billing_rate": "monthly",
"base_unit_price": 6,
"benefits": [
"Up to 10 users",
"Unlimited repositories",
"2500 repositories",
"Patch coverage analysis",
],
"tier_name": "team",
"monthly_uploads_limit": 2500,
"trial_days": None,
},
{
"marketing_name": "Team",
"value": "users-teamy",
"billing_rate": "annually",
"base_unit_price": 5,
"benefits": [
"Up to 10 users",
"Unlimited repositories",
"2500 repositories",
"Patch coverage analysis",
],
"tier_name": "team",
"monthly_uploads_limit": 2500,
"trial_days": None,
},
]

@patch("services.sentry.is_sentry_user")
Expand Down Expand Up @@ -144,6 +174,36 @@ def test_list_plans_sentry_user(self, is_sentry_user):
"monthly_uploads_limit": None,
"trial_days": None,
},
{
"marketing_name": "Team",
"value": "users-teamm",
"billing_rate": "monthly",
"base_unit_price": 6,
"benefits": [
"Up to 10 users",
"Unlimited repositories",
"2500 repositories",
"Patch coverage analysis",
],
"tier_name": "team",
"monthly_uploads_limit": 2500,
"trial_days": None,
},
{
"marketing_name": "Team",
"value": "users-teamy",
"billing_rate": "annually",
"base_unit_price": 5,
"benefits": [
"Up to 10 users",
"Unlimited repositories",
"2500 repositories",
"Patch coverage analysis",
],
"tier_name": "team",
"monthly_uploads_limit": 2500,
"trial_days": None,
},
{
"marketing_name": "Pro Team for Sentry",
"value": "users-sentrym",
Expand Down Expand Up @@ -243,4 +303,34 @@ def test_list_plans_anonymous_user(self):
"monthly_uploads_limit": None,
"trial_days": None,
},
{
"marketing_name": "Team",
"value": "users-teamm",
"billing_rate": "monthly",
"base_unit_price": 6,
"benefits": [
"Up to 10 users",
"Unlimited repositories",
"2500 repositories",
"Patch coverage analysis",
],
"tier_name": "team",
"monthly_uploads_limit": 2500,
"trial_days": None,
},
{
"marketing_name": "Team",
"value": "users-teamy",
"billing_rate": "annually",
"base_unit_price": 5,
"benefits": [
"Up to 10 users",
"Unlimited repositories",
"2500 repositories",
"Patch coverage analysis",
],
"tier_name": "team",
"monthly_uploads_limit": 2500,
"trial_days": None,
},
]
1 change: 1 addition & 0 deletions billing/helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ def available_plans(owner: Optional[Owner]) -> List[dict]:
plans: List[PlanData] = []
plans += list(FREE_PLAN_REPRESENTATIONS.values())
plans += list(PR_AUTHOR_PAID_USER_PLAN_REPRESENTATIONS.values())
plans += list(TEAM_PLAN_REPRESENTATIONS.values())
JerrySentry marked this conversation as resolved.
Show resolved Hide resolved

if owner and sentry.is_sentry_user(owner):
# these are only available to Sentry users
Expand Down
8 changes: 4 additions & 4 deletions billing/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
from rest_framework.views import APIView

from codecov_auth.models import Owner
from plan.constants import PRO_PLANS
from plan.constants import PAID_PLANS
from plan.service import PlanService
from services.billing import BillingService

Expand Down Expand Up @@ -147,7 +147,7 @@ def customer_subscription_created(self, subscription):
)
return

if subscription.plan.name not in PRO_PLANS:
if subscription.plan.name not in PAID_PLANS:
log.warning(
f"Subscription creation requested for invalid plan "
f"'{subscription.plan.name}' -- doing nothing",
Expand Down Expand Up @@ -210,8 +210,8 @@ def customer_subscription_updated(self, subscription):
# from the owner
owner.repository_set.update(active=False, activated=False)
return
# TODO: we can delete this if statement if we confirm there aren't any PRO_PLANS out there
if subscription.plan.name not in PRO_PLANS:
# TODO: we can delete this if statement if we confirm there aren't any PAID_PLANS out there
if subscription.plan.name not in PAID_PLANS:
log.warning(
f"Subscription update requested with invalid plan "
f"{subscription.plan.name} -- doing nothing",
Expand Down
2 changes: 2 additions & 0 deletions codecov/settings_dev.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@
"users-pr-inappy": "plan_H6P16wij3lUuxg",
"users-sentrym": "price_1Mj1kYGlVGuVgOrk7jucaZAa",
"users-sentryy": "price_1Mj1mMGlVGuVgOrkC0ORc6iW",
JerrySentry marked this conversation as resolved.
Show resolved Hide resolved
"users-teamm": "TBD",
"users-teamy": "TBD",
}

CORS_ALLOW_CREDENTIALS = True
Expand Down
2 changes: 2 additions & 0 deletions codecov/settings_prod.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@
"users-sentryy": "price_1MlYAYGlVGuVgOrke9SdbBUn",
"users-enterprisey": "price_1LmjzwGlVGuVgOrkIwlM46EU",
"users-enterprisem": "price_1LmjypGlVGuVgOrkzKtNqhwW",
"users-teamm": "TBD",
"users-teamy": "TBD",
}

CORS_ALLOW_HEADERS += ["sentry-trace", "baggage"]
Expand Down
2 changes: 2 additions & 0 deletions codecov/settings_staging.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@
"users-pr-inappy": "plan_H6P16wij3lUuxg",
"users-sentrym": "price_1Mj1kYGlVGuVgOrk7jucaZAa",
"users-sentryy": "price_1Mj1mMGlVGuVgOrkC0ORc6iW",
"users-teamm": "TBD",
"users-teamy": "TBD",
}

CORS_ALLOW_HEADERS += ["sentry-trace", "baggage"]
Expand Down
9 changes: 5 additions & 4 deletions plan/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -288,7 +288,7 @@ class PlanData:
],
tier_name=TierName.TEAM.value,
trial_days=None,
monthly_uploads_limit=1000,
monthly_uploads_limit=MonthlyUploadLimits.CODECOV_TEAM_PLAN.value,
),
PlanName.TEAM_YEARLY.value: PlanData(
marketing_name=PlanMarketingName.TEAM.value,
Expand All @@ -303,7 +303,7 @@ class PlanData:
],
tier_name=TierName.TEAM.value,
trial_days=None,
monthly_uploads_limit=1000,
monthly_uploads_limit=MonthlyUploadLimits.CODECOV_TEAM_PLAN.value,
JerrySentry marked this conversation as resolved.
Show resolved Hide resolved
),
}

Expand All @@ -325,10 +325,11 @@ class PlanData:
),
}

PRO_PLANS = {
PAID_PLANS = {
**PR_AUTHOR_PAID_USER_PLAN_REPRESENTATIONS,
**SENTRY_PAID_USER_PLAN_REPRESENTATIONS,
**ENTERPRISE_CLOUD_USER_PLAN_REPRESENTATIONS,
**TEAM_PLAN_REPRESENTATIONS,
}

TRIAL_PLANS = {**TRIAL_PLAN_REPRESENTATION}
Expand All @@ -340,7 +341,7 @@ class PlanData:
**FREE_PLAN_REPRESENTATIONS,
**NON_PR_AUTHOR_PAID_USER_PLAN_REPRESENTATIONS,
**GHM_PLAN_REPRESENTATION,
**PRO_PLANS,
**PAID_PLANS,
**TRIAL_PLANS,
**TEAM_PLANS,
}
Expand Down
4 changes: 2 additions & 2 deletions services/billing.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
from codecov_auth.models import Owner
from plan.constants import (
FREE_PLAN_REPRESENTATIONS,
PRO_PLANS,
PAID_PLANS,
USER_PLAN_REPRESENTATIONS,
PlanBillingRate,
)
Expand Down Expand Up @@ -514,7 +514,7 @@ def update_plan(self, owner, desired_plan):
else:
plan_service = PlanService(current_org=owner)
plan_service.set_default_plan_data()
elif desired_plan["value"] in PRO_PLANS:
elif desired_plan["value"] in PAID_PLANS:
if owner.stripe_subscription_id is not None:
self.payment_service.modify_subscription(owner, desired_plan)
else:
Expand Down
Loading