From 6308e07e532ac278b3a61686a0b8035eb5a577d7 Mon Sep 17 00:00:00 2001 From: Tim Abbott Date: Wed, 13 Dec 2023 15:17:55 -0800 Subject: [PATCH] billing: Standardize remote server plan type IDs. This will likely save us at least one headache. --- analytics/views/support.py | 3 +- corporate/lib/stripe.py | 14 +------ corporate/tests/test_stripe.py | 4 +- corporate/views/billing_page.py | 12 +++++- corporate/views/remote_billing_page.py | 10 ++++- zerver/tests/test_push_notifications.py | 6 +-- .../commands/populate_billing_realms.py | 4 +- ...52_alter_remoterealm_plan_type_and_more.py | 39 +++++++++++++++++++ zilencer/models.py | 36 ++++++++++++----- zilencer/views.py | 10 ++--- 10 files changed, 99 insertions(+), 39 deletions(-) create mode 100644 zilencer/migrations/0052_alter_remoterealm_plan_type_and_more.py diff --git a/analytics/views/support.py b/analytics/views/support.py index 20f2bab4dc..cfcad36e67 100644 --- a/analytics/views/support.py +++ b/analytics/views/support.py @@ -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", diff --git a/corporate/lib/stripe.py b/corporate/lib/stripe.py index 2f9a9f6dd6..14acdfadcb 100644 --- a/corporate/lib/stripe.py +++ b/corporate/lib/stripe.py @@ -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( diff --git a/corporate/tests/test_stripe.py b/corporate/tests/test_stripe.py index 20834b99ee..91c9edeb0b 100644 --- a/corporate/tests/test_stripe.py +++ b/corporate/tests/test_stripe.py @@ -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) diff --git a/corporate/views/billing_page.py b/corporate/views/billing_page.py index f95504c790..ca9b498330 100644 --- a/corporate/views/billing_page.py +++ b/corporate/views/billing_page.py @@ -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( diff --git a/corporate/views/remote_billing_page.py b/corporate/views/remote_billing_page.py index 4ce160b55c..78739ab318 100644 --- a/corporate/views/remote_billing_page.py +++ b/corporate/views/remote_billing_page.py @@ -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() diff --git a/zerver/tests/test_push_notifications.py b/zerver/tests/test_push_notifications.py index b422f122d5..55da83203f 100644 --- a/zerver/tests/test_push_notifications.py +++ b/zerver/tests/test_push_notifications.py @@ -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") ], diff --git a/zilencer/management/commands/populate_billing_realms.py b/zilencer/management/commands/populate_billing_realms.py index 693abe127c..752e826b75 100644 --- a/zilencer/management/commands/populate_billing_realms.py +++ b/zilencer/management/commands/populate_billing_realms.py @@ -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!") diff --git a/zilencer/migrations/0052_alter_remoterealm_plan_type_and_more.py b/zilencer/migrations/0052_alter_remoterealm_plan_type_and_more.py new file mode 100644 index 0000000000..209e24cfdb --- /dev/null +++ b/zilencer/migrations/0052_alter_remoterealm_plan_type_and_more.py @@ -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), + ] diff --git a/zilencer/models.py b/zilencer/models.py index bb95174dce..0b32347131 100644 --- a/zilencer/models.py +++ b/zilencer/models.py @@ -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: diff --git a/zilencer/views.py b/zilencer/views.py index 7e504405e1..36c9e91d93 100644 --- a/zilencer/views.py +++ b/zilencer/views.py @@ -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, }, )