populate_billing_realms: Add script to populate different realms.

These realms are based on different cases of realms which can
exist while billing them and hence are useful for testing.
This commit is contained in:
Aman Agrawal 2023-11-02 07:16:02 +00:00 committed by Tim Abbott
parent 0b3f7a5a6b
commit 9ebb3f06b3
2 changed files with 199 additions and 0 deletions

View File

@ -655,6 +655,15 @@ class Realm(models.Model): # type: ignore[django-manager-missing] # django-stub
PLAN_TYPE_STANDARD = 3
PLAN_TYPE_STANDARD_FREE = 4
PLAN_TYPE_PLUS = 10
# Used for creating realms with different plan types.
ALL_PLAN_TYPES = {
PLAN_TYPE_SELF_HOSTED: "self-hosted-plan",
PLAN_TYPE_LIMITED: "limited-plan",
PLAN_TYPE_STANDARD: "standard-plan",
PLAN_TYPE_STANDARD_FREE: "standard-free-plan",
PLAN_TYPE_PLUS: "plus-plan",
}
plan_type = models.PositiveSmallIntegerField(default=PLAN_TYPE_SELF_HOSTED)
# This value is also being used in web/src/settings_bots.bot_creation_policy_values.

View File

@ -0,0 +1,190 @@
import contextlib
from dataclasses import dataclass
from typing import Any, Optional
import stripe
from django.core.management.base import BaseCommand
from django.utils.timezone import now as timezone_now
from typing_extensions import override
from corporate.lib.stripe import update_or_create_stripe_customer
from corporate.models import Customer, CustomerPlan, LicenseLedger
from zerver.actions.create_realm import do_create_realm
from zerver.actions.create_user import do_create_user
from zerver.actions.streams import bulk_add_subscriptions
from zerver.apps import flush_cache
from zerver.lib.streams import create_stream_if_needed
from zerver.models import Realm, UserProfile, get_realm
from zproject.config import get_secret
@dataclass
class CustomerProfile:
unique_id: str
billing_schedule: int = CustomerPlan.ANNUAL
tier: Optional[int] = None
automanage_licenses: bool = False
status: int = CustomerPlan.ACTIVE
sponsorship_pending: bool = False
is_sponsored: bool = False
card: str = ""
class Command(BaseCommand):
help = "Populate database with different types of realms that can exist."
@override
def handle(self, *args: Any, **options: Any) -> None:
# Create a realm for each plan type
customer_profiles = [
# NOTE: The unique_id has to be less than 40 characters.
CustomerProfile(unique_id="sponsorship-pending", sponsorship_pending=True),
CustomerProfile(
unique_id="annual-standard",
billing_schedule=CustomerPlan.ANNUAL,
tier=CustomerPlan.STANDARD,
),
CustomerProfile(
unique_id="annual-plus",
billing_schedule=CustomerPlan.ANNUAL,
tier=CustomerPlan.PLUS,
),
CustomerProfile(
unique_id="monthly-standard",
billing_schedule=CustomerPlan.MONTHLY,
tier=CustomerPlan.STANDARD,
),
CustomerProfile(
unique_id="monthly-plus",
billing_schedule=CustomerPlan.MONTHLY,
tier=CustomerPlan.PLUS,
),
CustomerProfile(
unique_id="downgrade-end-of-cycle",
billing_schedule=CustomerPlan.MONTHLY,
tier=CustomerPlan.STANDARD,
status=CustomerPlan.DOWNGRADE_AT_END_OF_CYCLE,
),
CustomerProfile(
unique_id="standard-automanage-licenses",
billing_schedule=CustomerPlan.MONTHLY,
tier=CustomerPlan.STANDARD,
automanage_licenses=True,
),
CustomerProfile(
unique_id="standard-automatic-card",
billing_schedule=CustomerPlan.MONTHLY,
tier=CustomerPlan.STANDARD,
card="pm_card_visa",
),
CustomerProfile(
unique_id="sponsored",
is_sponsored=True,
billing_schedule=CustomerPlan.MONTHLY,
tier=CustomerPlan.STANDARD,
),
]
# Create a realm for each customer profile
for customer_profile in customer_profiles:
unique_id = customer_profile.unique_id
if customer_profile.tier is None:
plan_type = Realm.PLAN_TYPE_LIMITED
elif customer_profile.tier == CustomerPlan.STANDARD and customer_profile.is_sponsored:
plan_type = Realm.PLAN_TYPE_STANDARD_FREE
elif customer_profile.tier == CustomerPlan.STANDARD:
plan_type = Realm.PLAN_TYPE_STANDARD
elif customer_profile.tier == CustomerPlan.PLUS:
plan_type = Realm.PLAN_TYPE_PLUS
else:
raise AssertionError("Unexpected tier!")
plan_name = Realm.ALL_PLAN_TYPES[plan_type]
# Delete existing realm with this name
with contextlib.suppress(Realm.DoesNotExist):
get_realm(unique_id).delete()
# Because we just deleted a bunch of objects in the database
# directly (rather than deleting individual objects in Django,
# in which case our post_save hooks would have flushed the
# individual objects from memcached for us), we need to flush
# memcached in order to ensure deleted objects aren't still
# present in the memcached cache.
flush_cache(None)
realm = do_create_realm(
string_id=unique_id,
name=unique_id,
description=unique_id,
plan_type=plan_type,
)
# Create a user with billing access
full_name = f"{plan_name}-admin"
email = f"{full_name}@zulip.com"
user = do_create_user(
email,
full_name,
realm,
full_name,
role=UserProfile.ROLE_REALM_OWNER,
acting_user=None,
)
stream, _ = create_stream_if_needed(
realm,
"all",
)
bulk_add_subscriptions(realm, [stream], [user], acting_user=None)
if customer_profile.sponsorship_pending:
customer = Customer.objects.create(
realm=realm,
sponsorship_pending=customer_profile.sponsorship_pending,
)
continue
assert customer_profile.tier is not None
customer = update_or_create_stripe_customer(user)
assert customer.stripe_customer_id is not None
# Add a test card to the customer.
if customer_profile.card:
# Set the Stripe API key
stripe.api_key = get_secret("stripe_secret_key")
# Create a card payment method and attach it to the customer
payment_method = stripe.PaymentMethod.create(
type="card",
card={"token": "tok_visa"},
)
# Attach the payment method to the customer
stripe.PaymentMethod.attach(payment_method.id, customer=customer.stripe_customer_id)
# Set the default payment method for the customer
stripe.Customer.modify(
customer.stripe_customer_id,
invoice_settings={"default_payment_method": payment_method.id},
)
customer_plan = CustomerPlan.objects.create(
customer=customer,
billing_cycle_anchor=timezone_now(),
billing_schedule=customer_profile.billing_schedule,
tier=customer_profile.tier,
price_per_license=3,
automanage_licenses=customer_profile.automanage_licenses,
status=customer_profile.status,
charge_automatically=True,
)
LicenseLedger.objects.create(
licenses=10,
licenses_at_next_renewal=10,
event_time=timezone_now(),
is_renewal=True,
plan=customer_plan,
)