models: Add plan_type to Realm.

This commit is contained in:
Vishnu Ks 2018-08-10 01:08:22 +05:30 committed by Tim Abbott
parent 6031ccff5c
commit 9bb338be11
8 changed files with 71 additions and 2 deletions

View File

@ -3018,6 +3018,15 @@ def do_change_icon_source(realm: Realm, icon_source: str, log: bool=True) -> Non
icon_url=realm_icon_url(realm))),
active_user_ids(realm.id))
def do_change_plan_type(user: UserProfile, plan_type: int) -> None:
realm = user.realm
old_value = realm.plan_type
realm.plan_type = plan_type
realm.save(update_fields=['plan_type'])
RealmAuditLog.objects.create(event_type=RealmAuditLog.REALM_PLAN_TYPE_CHANGED,
realm=realm, acting_user=user, event_time=timezone_now(),
extra_data={'old_value': old_value, 'new_value': plan_type})
def do_change_default_sending_stream(user_profile: UserProfile, stream: Optional[Stream],
log: bool=True) -> None:
user_profile.default_sending_stream = stream
@ -3194,6 +3203,8 @@ def do_create_realm(string_id: str, name: str,
kwargs = {} # type: Dict[str, Any]
if emails_restricted_to_domains is not None:
kwargs['emails_restricted_to_domains'] = emails_restricted_to_domains
if settings.BILLING_ENABLED:
kwargs['plan_type'] = Realm.LIMITED
realm = Realm(string_id=string_id, name=name, **kwargs)
realm.save()

View File

@ -0,0 +1,22 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11.14 on 2018-08-10 21:36
from __future__ import unicode_literals
from django.db import migrations, models
import zerver.models
class Migration(migrations.Migration):
dependencies = [
('zerver', '0184_rename_custom_field_types'),
]
operations = [
migrations.AddField(
model_name='realm',
name='plan_type',
# Realm.SELF_HOSTED
field=models.PositiveSmallIntegerField(default=1),
),
]

View File

@ -223,6 +223,15 @@ class Realm(models.Model):
COMMUNITY = 2
org_type = models.PositiveSmallIntegerField(default=CORPORATE) # type: int
# plan_type controls various features around resource/feature
# limitations for a Zulip organization on multi-tenant servers
# like zulipchat.com.
SELF_HOSTED = 1
LIMITED = 2
PREMIUM = 3
PREMIUM_FREE = 4
plan_type = models.PositiveSmallIntegerField(default=SELF_HOSTED) # type: int
# This value is also being used in static/js/settings_bots.bot_creation_policy_values.
# On updating it here, update it there as well.
BOT_CREATION_EVERYONE = 1
@ -2189,6 +2198,7 @@ class RealmAuditLog(models.Model):
REALM_DEACTIVATED = 'realm_deactivated'
REALM_REACTIVATED = 'realm_reactivated'
REALM_PLAN_TYPE_CHANGED = 'realm_plan_type_changed'
SUBSCRIPTION_CREATED = 'subscription_created'
SUBSCRIPTION_ACTIVATED = 'subscription_activated'

View File

@ -11,6 +11,7 @@ from zerver.lib.actions import (
do_set_realm_property,
do_deactivate_realm,
do_deactivate_stream,
do_create_realm,
)
from zerver.lib.send_email import send_future_email
@ -334,6 +335,12 @@ class RealmTest(ZulipTestCase):
self.assert_json_success(result)
self.assertEqual(get_realm('zulip').video_chat_provider, "Jitsi")
def test_initial_plan_type(self) -> None:
with self.settings(BILLING_ENABLED=True):
self.assertEqual(Realm.LIMITED, do_create_realm('hosted', 'hosted').plan_type)
with self.settings(BILLING_ENABLED=False):
self.assertEqual(Realm.SELF_HOSTED, do_create_realm('onpremise', 'onpremise').plan_type)
class RealmAPITest(ZulipTestCase):
def setUp(self) -> None:

View File

@ -16,6 +16,7 @@ from zerver.lib.exceptions import JsonableError
from zerver.lib.logging_util import log_to_file
from zerver.lib.timestamp import datetime_to_timestamp, timestamp_to_datetime
from zerver.lib.utils import generate_random_token
from zerver.lib.actions import do_change_plan_type
from zerver.models import Realm, UserProfile, RealmAuditLog
from zilencer.models import Customer, Plan, BillingProcessor
from zproject.settings import get_secret
@ -228,6 +229,7 @@ def process_initial_upgrade(user: UserProfile, plan: Plan, seat_count: int, stri
# TODO: billing address details are passed to us in the request;
# use that to calculate taxes.
tax_percent=0)
do_change_plan_type(user, Realm.PREMIUM)
## Process RealmAuditLog

View File

@ -50,6 +50,12 @@ def mock_customer_with_cancel_at_period_end_subscription(*args: Any, **kwargs: A
def mock_upcoming_invoice(*args: Any, **kwargs: Any) -> stripe.Invoice:
return stripe.util.convert_to_stripe_object(fixture_data["upcoming_invoice"])
# A Kandra is a fictional character that can become anything. Used as a
# wildcard when testing for equality.
class Kandra(object):
def __eq__(self, other: Any) -> bool:
return True
class StripeTest(ZulipTestCase):
def setUp(self) -> None:
self.token = 'token'
@ -103,6 +109,7 @@ class StripeTest(ZulipTestCase):
response = self.client_get("/upgrade/")
self.assert_in_success_response(['We can also bill by invoice'], response)
self.assertFalse(user.realm.has_seat_based_plan)
self.assertNotEqual(user.realm.plan_type, Realm.PREMIUM)
# Click "Make payment" in Stripe Checkout
self.client_post("/upgrade/", {
@ -135,10 +142,12 @@ class StripeTest(ZulipTestCase):
(RealmAuditLog.STRIPE_CUSTOMER_CREATED, timestamp_to_datetime(self.customer_created)),
(RealmAuditLog.STRIPE_CARD_ADDED, timestamp_to_datetime(self.customer_created)),
(RealmAuditLog.STRIPE_PLAN_CHANGED, timestamp_to_datetime(self.subscription_created)),
(RealmAuditLog.REALM_PLAN_TYPE_CHANGED, Kandra()),
])
# Check that we correctly updated Realm
realm = get_realm("zulip")
self.assertTrue(realm.has_seat_based_plan)
self.assertEqual(realm.plan_type, Realm.PREMIUM)
# Check that we can no longer access /upgrade
response = self.client_get("/upgrade/")
self.assertEqual(response.status_code, 302)
@ -202,12 +211,13 @@ class StripeTest(ZulipTestCase):
# correctly handled the requires_billing_update field
audit_log_entries = list(RealmAuditLog.objects.order_by('-id')
.values_list('event_type', 'event_time',
'requires_billing_update')[:4])[::-1]
'requires_billing_update')[:5])[::-1]
self.assertEqual(audit_log_entries, [
(RealmAuditLog.STRIPE_CUSTOMER_CREATED, timestamp_to_datetime(self.customer_created), False),
(RealmAuditLog.STRIPE_CARD_ADDED, timestamp_to_datetime(self.customer_created), False),
(RealmAuditLog.STRIPE_PLAN_CHANGED, timestamp_to_datetime(self.subscription_created), False),
(RealmAuditLog.STRIPE_PLAN_QUANTITY_RESET, timestamp_to_datetime(self.subscription_created), True),
(RealmAuditLog.REALM_PLAN_TYPE_CHANGED, Kandra(), False),
])
self.assertEqual(ujson.loads(RealmAuditLog.objects.filter(
event_type=RealmAuditLog.STRIPE_PLAN_QUANTITY_RESET).values_list('extra_data', flat=True).first()),
@ -261,7 +271,8 @@ class StripeTest(ZulipTestCase):
self.assertEqual(audit_log_entries, [RealmAuditLog.STRIPE_CUSTOMER_CREATED,
RealmAuditLog.STRIPE_CARD_ADDED,
RealmAuditLog.STRIPE_CARD_ADDED,
RealmAuditLog.STRIPE_PLAN_CHANGED])
RealmAuditLog.STRIPE_PLAN_CHANGED,
RealmAuditLog.REALM_PLAN_TYPE_CHANGED])
# Check that we correctly updated Realm
realm = get_realm("zulip")
self.assertTrue(realm.has_seat_based_plan)

View File

@ -123,3 +123,5 @@ if FAKE_LDAP_MODE:
THUMBOR_URL = 'http://127.0.0.1:9995'
SEARCH_PILLS_ENABLED = os.getenv('SEARCH_PILLS_ENABLED', False)
BILLING_ENABLED = True

View File

@ -437,6 +437,10 @@ DEFAULT_SETTINGS.update({
# DEFAULT_SETTINGS, since it likely isn't usefully user-configurable.
'OFFLINE_THRESHOLD_SECS': 5 * 60,
# Enables billing pages and plan-based feature gates. If False, all features
# are available to all realms.
'BILLING_ENABLED': False,
# Controls whether we run the worker that syncs billing-related updates
# into Stripe. Should be True on at most one machine.
'BILLING_PROCESSOR_ENABLED': False,