billing: Standardize remote server plan type IDs.

This will likely save us at least one headache.
This commit is contained in:
Tim Abbott 2023-12-13 15:17:55 -08:00
parent 89545891f6
commit 6308e07e53
10 changed files with 99 additions and 39 deletions

View File

@ -75,7 +75,8 @@ def get_plan_type_string(plan_type: int) -> str:
Realm.PLAN_TYPE_STANDARD: "Standard",
Realm.PLAN_TYPE_STANDARD_FREE: "Standard free",
Realm.PLAN_TYPE_PLUS: "Plus",
RemoteZulipServer.PLAN_TYPE_SELF_HOSTED: "Self-hosted",
RemoteZulipServer.PLAN_TYPE_SELF_MANAGED: "Self-managed",
RemoteZulipServer.PLAN_TYPE_SELF_MANAGED_LEGACY: "Self-managed (legacy)",
RemoteZulipServer.PLAN_TYPE_COMMUNITY: "Community",
RemoteZulipServer.PLAN_TYPE_BUSINESS: "Business",
RemoteZulipServer.PLAN_TYPE_ENTERPRISE: "Enterprise",

View File

@ -3177,10 +3177,6 @@ class RemoteRealmBillingSession(BillingSession):
plan_type = RemoteRealm.PLAN_TYPE_COMMUNITY
elif tier == CustomerPlan.TIER_SELF_HOSTED_BUSINESS:
plan_type = RemoteRealm.PLAN_TYPE_BUSINESS
elif (
tier == CustomerPlan.TIER_SELF_HOSTED_PLUS
): # nocoverage # Plus plan doesn't use this code path yet.
plan_type = RemoteRealm.PLAN_TYPE_ENTERPRISE
else:
raise AssertionError("Unexpected tier")
@ -3259,7 +3255,7 @@ class RemoteRealmBillingSession(BillingSession):
def process_downgrade(self, plan: CustomerPlan) -> None: # nocoverage
with transaction.atomic():
old_plan_type = self.remote_realm.plan_type
new_plan_type = RemoteRealm.PLAN_TYPE_SELF_HOSTED
new_plan_type = RemoteRealm.PLAN_TYPE_SELF_MANAGED
self.remote_realm.plan_type = new_plan_type
self.remote_realm.save(update_fields=["plan_type"])
self.write_to_audit_log(
@ -3565,18 +3561,12 @@ class RemoteServerBillingSession(BillingSession):
def do_change_plan_type(
self, *, tier: Optional[int], is_sponsored: bool = False
) -> None: # nocoverage
# TODO: Create actual plan types.
# This function needs to translate between the different
# formats of CustomerPlan.tier and RealmZulipServer.plan_type.
if is_sponsored:
plan_type = RemoteZulipServer.PLAN_TYPE_COMMUNITY
elif tier == CustomerPlan.TIER_SELF_HOSTED_BUSINESS:
plan_type = RemoteZulipServer.PLAN_TYPE_BUSINESS
elif (
tier == CustomerPlan.TIER_SELF_HOSTED_PLUS
): # nocoverage # Plus plan doesn't use this code path yet.
plan_type = RemoteZulipServer.PLAN_TYPE_ENTERPRISE
else:
raise AssertionError("Unexpected tier")
@ -3628,7 +3618,7 @@ class RemoteServerBillingSession(BillingSession):
def process_downgrade(self, plan: CustomerPlan) -> None: # nocoverage
with transaction.atomic():
old_plan_type = self.remote_server.plan_type
new_plan_type = RemoteZulipServer.PLAN_TYPE_SELF_HOSTED
new_plan_type = RemoteZulipServer.PLAN_TYPE_SELF_MANAGED
self.remote_server.plan_type = new_plan_type
self.remote_server.save(update_fields=["plan_type"])
self.write_to_audit_log(

View File

@ -4578,7 +4578,7 @@ class BillingHelpersTest(ZulipTestCase):
hostname="demo.example.com",
contact_email="email@example.com",
)
self.assertEqual(remote_server.plan_type, RemoteZulipServer.PLAN_TYPE_SELF_HOSTED)
self.assertEqual(remote_server.plan_type, RemoteZulipServer.PLAN_TYPE_SELF_MANAGED)
do_change_remote_server_plan_type(remote_server, RemoteZulipServer.PLAN_TYPE_BUSINESS)
@ -4588,7 +4588,7 @@ class BillingHelpersTest(ZulipTestCase):
).last()
assert remote_realm_audit_log is not None
expected_extra_data = {
"old_value": RemoteZulipServer.PLAN_TYPE_SELF_HOSTED,
"old_value": RemoteZulipServer.PLAN_TYPE_SELF_MANAGED,
"new_value": RemoteZulipServer.PLAN_TYPE_BUSINESS,
}
self.assertEqual(remote_realm_audit_log.extra_data, expected_extra_data)

View File

@ -127,7 +127,11 @@ def remote_realm_billing_page(
or get_current_plan_by_customer(customer) is None
or (
billing_session.get_legacy_remote_server_next_plan_name(customer) is None
and billing_session.remote_realm.plan_type == RemoteRealm.PLAN_TYPE_SELF_HOSTED
and billing_session.remote_realm.plan_type
in [
RemoteRealm.PLAN_TYPE_SELF_MANAGED,
RemoteRealm.PLAN_TYPE_SELF_MANAGED_LEGACY,
]
)
):
return HttpResponseRedirect(reverse("remote_realm_plans_page", args=(realm_uuid,)))
@ -186,7 +190,11 @@ def remote_server_billing_page(
or get_current_plan_by_customer(customer) is None
or (
billing_session.get_legacy_remote_server_next_plan_name(customer) is None
and billing_session.remote_server.plan_type == RemoteZulipServer.PLAN_TYPE_SELF_HOSTED
and billing_session.remote_server.plan_type
in [
RemoteZulipServer.PLAN_TYPE_SELF_MANAGED,
RemoteZulipServer.PLAN_TYPE_SELF_MANAGED_LEGACY,
]
)
):
return HttpResponseRedirect(

View File

@ -268,7 +268,10 @@ def remote_realm_billing_finalize_login(
return HttpResponseRedirect(
reverse(f"remote_realm_{next_page}_page", args=(remote_realm_uuid,))
)
elif remote_realm.plan_type == RemoteRealm.PLAN_TYPE_SELF_HOSTED:
elif remote_realm.plan_type in [
RemoteRealm.PLAN_TYPE_SELF_MANAGED,
RemoteRealm.PLAN_TYPE_SELF_MANAGED_LEGACY,
]:
# If they have a scheduled upgrade, redirect to billing page.
billing_session = RemoteRealmBillingSession(remote_realm)
customer = billing_session.get_customer()
@ -657,7 +660,10 @@ def remote_billing_legacy_server_from_login_confirmation_link(
return HttpResponseRedirect(
reverse(f"remote_server_{next_page}_page", args=(remote_server_uuid,))
)
elif remote_server.plan_type == RemoteZulipServer.PLAN_TYPE_SELF_HOSTED:
elif remote_server.plan_type in [
RemoteZulipServer.PLAN_TYPE_SELF_MANAGED,
RemoteZulipServer.PLAN_TYPE_SELF_MANAGED_LEGACY,
]:
# If they have a scheduled upgrade, redirect to billing page.
billing_session = RemoteServerBillingSession(remote_server)
customer = billing_session.get_customer()

View File

@ -1171,7 +1171,7 @@ class AnalyticsBouncerTest(BouncerTestCase):
"realm_date_created": realm.date_created,
"registration_deactivated": False,
"realm_deactivated": False,
"plan_type": RemoteRealm.PLAN_TYPE_SELF_HOSTED,
"plan_type": RemoteRealm.PLAN_TYPE_SELF_MANAGED,
"is_system_bot_realm": realm.string_id == "zulipinternal",
}
for realm in Realm.objects.order_by("id")
@ -1613,7 +1613,7 @@ class AnalyticsBouncerTest(BouncerTestCase):
realm_date_created=realm.date_created,
registration_deactivated=False,
realm_deactivated=False,
plan_type=RemoteRealm.PLAN_TYPE_SELF_HOSTED,
plan_type=RemoteRealm.PLAN_TYPE_SELF_MANAGED,
)
with transaction.atomic(), self.assertLogs("zulip.analytics", level="WARNING") as m:
@ -1903,7 +1903,7 @@ class AnalyticsBouncerTest(BouncerTestCase):
"realm_date_created": realm.date_created,
"registration_deactivated": False,
"realm_deactivated": False,
"plan_type": RemoteRealm.PLAN_TYPE_SELF_HOSTED,
"plan_type": RemoteRealm.PLAN_TYPE_SELF_MANAGED,
}
for realm in Realm.objects.order_by("id")
],

View File

@ -394,11 +394,11 @@ def populate_remote_server(customer_profile: CustomerProfile) -> Dict[str, str]:
):
plan_type = RemoteZulipServer.PLAN_TYPE_COMMUNITY
elif customer_profile.tier == CustomerPlan.TIER_SELF_HOSTED_LEGACY:
plan_type = RemoteZulipServer.PLAN_TYPE_SELF_HOSTED
plan_type = RemoteZulipServer.PLAN_TYPE_SELF_MANAGED_LEGACY
elif customer_profile.tier == CustomerPlan.TIER_SELF_HOSTED_BUSINESS:
plan_type = RemoteZulipServer.PLAN_TYPE_BUSINESS
elif customer_profile.tier is CustomerPlan.TIER_SELF_HOSTED_BASE:
plan_type = RemoteZulipServer.PLAN_TYPE_SELF_HOSTED
plan_type = RemoteZulipServer.PLAN_TYPE_SELF_MANAGED
else:
raise AssertionError("Unexpected tier!")

View File

@ -0,0 +1,39 @@
# Generated by Django 4.2.8 on 2023-12-13 23:20
from django.db import migrations, models
from django.db.backends.base.schema import BaseDatabaseSchemaEditor
from django.db.migrations.state import StateApps
PLAN_TYPE_SELF_HOSTED = 1
PLAN_TYPE_SELF_MANAGED = 100
def renumber_plan_types(apps: StateApps, schema_editor: BaseDatabaseSchemaEditor) -> None:
RemoteZulipServer = apps.get_model("zilencer", "RemoteZulipServer")
RemoteRealm = apps.get_model("zilencer", "RemoteRealm")
RemoteRealm.objects.filter(plan_type=PLAN_TYPE_SELF_HOSTED).update(
plan_type=PLAN_TYPE_SELF_MANAGED
)
RemoteZulipServer.objects.filter(plan_type=PLAN_TYPE_SELF_HOSTED).update(
plan_type=PLAN_TYPE_SELF_MANAGED
)
class Migration(migrations.Migration):
dependencies = [
("zilencer", "0051_remoterealm_is_system_bot_realm"),
]
operations = [
migrations.AlterField(
model_name="remoterealm",
name="plan_type",
field=models.PositiveSmallIntegerField(db_index=True, default=PLAN_TYPE_SELF_MANAGED),
),
migrations.AlterField(
model_name="remotezulipserver",
name="plan_type",
field=models.PositiveSmallIntegerField(default=PLAN_TYPE_SELF_MANAGED),
),
migrations.RunPython(renumber_plan_types, reverse_code=migrations.RunPython.noop),
]

View File

@ -54,13 +54,22 @@ class RemoteZulipServer(models.Model):
deactivated = models.BooleanField(default=False)
# Plan types for self-hosted customers
#
# We reserve PLAN_TYPE_SELF_HOSTED=Realm.PLAN_TYPE_SELF_HOSTED for
# self-hosted installations that aren't using the notifications
# service.
#
# The other values align with, e.g., CustomerPlan.TIER_SELF_HOSTED_BASE
PLAN_TYPE_SELF_HOSTED = 1
PLAN_TYPE_COMMUNITY = 100
PLAN_TYPE_BUSINESS = 101
PLAN_TYPE_ENTERPRISE = 102
PLAN_TYPE_SELF_MANAGED = 100
PLAN_TYPE_SELF_MANAGED_LEGACY = 101
PLAN_TYPE_COMMUNITY = 102
PLAN_TYPE_BUSINESS = 103
PLAN_TYPE_PLUS = 104
PLAN_TYPE_ENTERPRISE = 105
# The current billing plan for the remote server, similar to Realm.plan_type.
plan_type = models.PositiveSmallIntegerField(default=PLAN_TYPE_SELF_HOSTED)
plan_type = models.PositiveSmallIntegerField(default=PLAN_TYPE_SELF_MANAGED)
# This is not synced with the remote server, but only filled for sponsorship requests.
org_type = models.PositiveSmallIntegerField(
@ -143,13 +152,20 @@ class RemoteRealm(models.Model):
realm_date_created = models.DateTimeField()
# Plan types for self-hosted customers
#
# We reserve PLAN_TYPE_SELF_HOSTED=Realm.PLAN_TYPE_SELF_HOSTED for
# self-hosted installations that aren't using the notifications
# service.
#
# The other values align with, e.g., CustomerPlan.TIER_SELF_HOSTED_BASE
PLAN_TYPE_SELF_HOSTED = 1
PLAN_TYPE_COMMUNITY = 100
PLAN_TYPE_BUSINESS = 101
PLAN_TYPE_ENTERPRISE = 102
# The current billing plan for the remote server, similar to Realm.plan_type.
plan_type = models.PositiveSmallIntegerField(default=PLAN_TYPE_SELF_HOSTED, db_index=True)
PLAN_TYPE_SELF_MANAGED = 100
PLAN_TYPE_SELF_MANAGED_LEGACY = 101
PLAN_TYPE_COMMUNITY = 102
PLAN_TYPE_BUSINESS = 103
PLAN_TYPE_PLUS = 104
PLAN_TYPE_ENTERPRISE = 105
plan_type = models.PositiveSmallIntegerField(default=PLAN_TYPE_SELF_MANAGED, db_index=True)
@override
def __str__(self) -> str:

View File

@ -768,10 +768,10 @@ def handle_customer_migration_from_server_to_realms(
and server_plan.tier == CustomerPlan.TIER_SELF_HOSTED_LEGACY
and server_plan.status == CustomerPlan.ACTIVE
):
assert server.plan_type == RemoteZulipServer.PLAN_TYPE_SELF_HOSTED
assert server.plan_type == RemoteZulipServer.PLAN_TYPE_SELF_MANAGED
assert server_plan.end_date is not None
remote_realms = RemoteRealm.objects.filter(
uuid__in=realm_uuids, server=server, plan_type=RemoteRealm.PLAN_TYPE_SELF_HOSTED
uuid__in=realm_uuids, server=server, plan_type=RemoteRealm.PLAN_TYPE_SELF_MANAGED
)
# Verify that all the realms are on self hosted plan.
@ -799,7 +799,7 @@ def handle_customer_migration_from_server_to_realms(
# We only do this migration if there is only one realm besides the system bot realm.
elif len(realm_uuids) == 1:
remote_realm = RemoteRealm.objects.get(
uuid=realm_uuids[0], plan_type=RemoteRealm.PLAN_TYPE_SELF_HOSTED
uuid=realm_uuids[0], plan_type=RemoteRealm.PLAN_TYPE_SELF_MANAGED
)
# Migrate customer from server to remote realm if there is only one realm.
server_customer.remote_realm = remote_realm
@ -809,7 +809,7 @@ def handle_customer_migration_from_server_to_realms(
# Might be better to call do_change_plan_type here.
remote_realm.plan_type = server.plan_type
remote_realm.save(update_fields=["plan_type"])
server.plan_type = RemoteZulipServer.PLAN_TYPE_SELF_HOSTED
server.plan_type = RemoteZulipServer.PLAN_TYPE_SELF_MANAGED
server.save(update_fields=["plan_type"])
remote_realm_audit_logs.append(
RemoteRealmAuditLog(
@ -819,7 +819,7 @@ def handle_customer_migration_from_server_to_realms(
event_time=event_time,
extra_data={
"attr_name": "plan_type",
"old_value": RemoteRealm.PLAN_TYPE_SELF_HOSTED,
"old_value": RemoteRealm.PLAN_TYPE_SELF_MANAGED,
"new_value": remote_realm.plan_type,
},
)