models: Move billing models from zilencer to corporate.

This commit is contained in:
Vishnu Ks
2018-09-25 17:32:43 +05:30
committed by Tim Abbott
parent d77a4c776d
commit d8c19cb003
14 changed files with 149 additions and 52 deletions

View File

@@ -18,7 +18,7 @@ from zerver.lib.timestamp import datetime_to_timestamp, timestamp_to_datetime
from zerver.lib.utils import generate_random_token from zerver.lib.utils import generate_random_token
from zerver.lib.actions import do_change_plan_type from zerver.lib.actions import do_change_plan_type
from zerver.models import Realm, UserProfile, RealmAuditLog from zerver.models import Realm, UserProfile, RealmAuditLog
from zilencer.models import Customer, Plan, Coupon, BillingProcessor from corporate.models import Customer, Plan, Coupon, BillingProcessor
from zproject.settings import get_secret from zproject.settings import get_secret
STRIPE_PUBLISHABLE_KEY = get_secret('stripe_publishable_key') STRIPE_PUBLISHABLE_KEY = get_secret('stripe_publishable_key')

View File

@@ -17,7 +17,7 @@ from zerver.lib.context_managers import lockfile
from zerver.lib.management import sleep_forever from zerver.lib.management import sleep_forever
from corporate.lib.stripe import StripeConnectionError, \ from corporate.lib.stripe import StripeConnectionError, \
run_billing_processor_one_step run_billing_processor_one_step
from zilencer.models import BillingProcessor from corporate.models import BillingProcessor
class Command(BaseCommand): class Command(BaseCommand):
help = """Run BillingProcessors, to sync billing-relevant updates into Stripe. help = """Run BillingProcessors, to sync billing-relevant updates into Stripe.

View File

@@ -1,5 +1,5 @@
from zerver.lib.management import ZulipBaseCommand from zerver.lib.management import ZulipBaseCommand
from zilencer.models import Plan, Coupon, Customer from corporate.models import Plan, Coupon, Customer
from zproject.settings import get_secret from zproject.settings import get_secret
from typing import Any from typing import Any

View File

@@ -0,0 +1,53 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11.14 on 2018-09-25 12:02
from __future__ import unicode_literals
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
initial = True
dependencies = [
('zerver', '0189_userprofile_add_some_emojisets'),
]
operations = [
migrations.CreateModel(
name='BillingProcessor',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('state', models.CharField(max_length=20)),
('last_modified', models.DateTimeField(auto_now=True)),
('log_row', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='zerver.RealmAuditLog')),
('realm', models.OneToOneField(null=True, on_delete=django.db.models.deletion.CASCADE, to='zerver.Realm')),
],
),
migrations.CreateModel(
name='Coupon',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('percent_off', models.SmallIntegerField(unique=True)),
('stripe_coupon_id', models.CharField(max_length=255, unique=True)),
],
),
migrations.CreateModel(
name='Customer',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('stripe_customer_id', models.CharField(max_length=255, unique=True)),
('has_billing_relationship', models.BooleanField(default=False)),
('realm', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, to='zerver.Realm')),
],
),
migrations.CreateModel(
name='Plan',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('nickname', models.CharField(max_length=40, unique=True)),
('stripe_plan_id', models.CharField(max_length=255, unique=True)),
],
),
]

View File

46
corporate/models.py Normal file
View File

@@ -0,0 +1,46 @@
import datetime
from django.db import models
from zerver.models import Realm, RealmAuditLog
class Customer(models.Model):
realm = models.OneToOneField(Realm, on_delete=models.CASCADE) # type: Realm
stripe_customer_id = models.CharField(max_length=255, unique=True) # type: str
# Becomes True the first time a payment successfully goes through, and never
# goes back to being False
has_billing_relationship = models.BooleanField(default=False) # type: bool
def __str__(self) -> str:
return "<Customer %s %s>" % (self.realm, self.stripe_customer_id)
class Plan(models.Model):
# The two possible values for nickname
CLOUD_MONTHLY = 'monthly'
CLOUD_ANNUAL = 'annual'
nickname = models.CharField(max_length=40, unique=True) # type: str
stripe_plan_id = models.CharField(max_length=255, unique=True) # type: str
class Coupon(models.Model):
percent_off = models.SmallIntegerField(unique=True) # type: int
stripe_coupon_id = models.CharField(max_length=255, unique=True) # type: str
def __str__(self) -> str:
return '<Coupon: %s %s %s>' % (self.percent_off, self.stripe_coupon_id, self.id)
class BillingProcessor(models.Model):
log_row = models.ForeignKey(RealmAuditLog, on_delete=models.CASCADE) # RealmAuditLog
# Exactly one processor, the global processor, has realm=None.
realm = models.OneToOneField(Realm, null=True, on_delete=models.CASCADE) # type: Realm
DONE = 'done'
STARTED = 'started'
SKIPPED = 'skipped' # global processor only
STALLED = 'stalled' # realm processors only
state = models.CharField(max_length=20) # type: str
last_modified = models.DateTimeField(auto_now=True) # type: datetime.datetime
def __str__(self) -> str:
return '<BillingProcessor: %s %s %s>' % (self.realm, self.log_row, self.id)

View File

@@ -21,7 +21,7 @@ from corporate.lib.stripe import catch_stripe_errors, \
get_seat_count, extract_current_subscription, sign_string, unsign_string, \ get_seat_count, extract_current_subscription, sign_string, unsign_string, \
get_next_billing_log_entry, run_billing_processor_one_step, \ get_next_billing_log_entry, run_billing_processor_one_step, \
BillingError, StripeCardError, StripeConnectionError BillingError, StripeCardError, StripeConnectionError
from zilencer.models import Customer, Plan, Coupon, BillingProcessor from corporate.models import Customer, Plan, Coupon, BillingProcessor
fixture_data_file = open(os.path.join(os.path.dirname(__file__), 'stripe_fixtures.json'), 'r') fixture_data_file = open(os.path.join(os.path.dirname(__file__), 'stripe_fixtures.json'), 'r')
fixture_data = ujson.load(fixture_data_file) fixture_data = ujson.load(fixture_data_file)

View File

@@ -19,7 +19,7 @@ from corporate.lib.stripe import STRIPE_PUBLISHABLE_KEY, \
stripe_get_customer, stripe_get_upcoming_invoice, get_seat_count, \ stripe_get_customer, stripe_get_upcoming_invoice, get_seat_count, \
extract_current_subscription, process_initial_upgrade, sign_string, \ extract_current_subscription, process_initial_upgrade, sign_string, \
unsign_string, BillingError, process_downgrade, do_replace_payment_source unsign_string, BillingError, process_downgrade, do_replace_payment_source
from zilencer.models import Customer, Plan from corporate.models import Customer, Plan
billing_logger = logging.getLogger('corporate.stripe') billing_logger = logging.getLogger('corporate.stripe')

View File

@@ -12,7 +12,7 @@ To set up the development environment to work on the billing code:
It is safe to run `manage.py setup_stripe` multiple times. It is safe to run `manage.py setup_stripe` multiple times.
Nearly all the billing-relevant code lives in `zilencer/`. Nearly all the billing-relevant code lives in `corporate/`.
## General architecture ## General architecture

View File

@@ -23,7 +23,7 @@ from zerver.models import (
flush_per_request_caches, DefaultStream, Realm, flush_per_request_caches, DefaultStream, Realm,
) )
from zerver.views.home import home, sent_time_in_epoch_seconds from zerver.views.home import home, sent_time_in_epoch_seconds
from zilencer.models import Customer from corporate.models import Customer
class HomeTest(ZulipTestCase): class HomeTest(ZulipTestCase):
def test_home(self) -> None: def test_home(self) -> None:

View File

@@ -250,8 +250,8 @@ def home_real(request: HttpRequest) -> HttpResponse:
show_billing = False show_billing = False
show_plans = False show_plans = False
if settings.ZILENCER_ENABLED: if settings.CORPORATE_ENABLED:
from zilencer.models import Customer from corporate.models import Customer
if user_profile.is_billing_admin or user_profile.is_realm_admin: if user_profile.is_billing_admin or user_profile.is_realm_admin:
customer = Customer.objects.filter(realm=user_profile.realm).first() customer = Customer.objects.filter(realm=user_profile.realm).first()
if customer is not None and customer.has_billing_relationship: if customer is not None and customer.has_billing_relationship:

View File

@@ -0,0 +1,39 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11.14 on 2018-09-25 12:01
from __future__ import unicode_literals
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('zilencer', '0014_cleanup_pushdevicetoken'),
]
operations = [
migrations.RemoveField(
model_name='billingprocessor',
name='log_row',
),
migrations.RemoveField(
model_name='billingprocessor',
name='realm',
),
migrations.DeleteModel(
name='Coupon',
),
migrations.RemoveField(
model_name='customer',
name='realm',
),
migrations.DeleteModel(
name='Plan',
),
migrations.DeleteModel(
name='BillingProcessor',
),
migrations.DeleteModel(
name='Customer',
),
]

View File

@@ -2,8 +2,7 @@ import datetime
from django.db import models from django.db import models
from zerver.models import AbstractPushDeviceToken, Realm, UserProfile, \ from zerver.models import AbstractPushDeviceToken
RealmAuditLog
def get_remote_server_by_uuid(uuid: str) -> 'RemoteZulipServer': def get_remote_server_by_uuid(uuid: str) -> 'RemoteZulipServer':
return RemoteZulipServer.objects.get(uuid=uuid) return RemoteZulipServer.objects.get(uuid=uuid)
@@ -35,44 +34,3 @@ class RemotePushDeviceToken(AbstractPushDeviceToken):
def __str__(self) -> str: def __str__(self) -> str:
return "<RemotePushDeviceToken %s %s>" % (self.server, self.user_id) return "<RemotePushDeviceToken %s %s>" % (self.server, self.user_id)
class Customer(models.Model):
realm = models.OneToOneField(Realm, on_delete=models.CASCADE) # type: Realm
stripe_customer_id = models.CharField(max_length=255, unique=True) # type: str
# Becomes True the first time a payment successfully goes through, and never
# goes back to being False
has_billing_relationship = models.BooleanField(default=False) # type: bool
def __str__(self) -> str:
return "<Customer %s %s>" % (self.realm, self.stripe_customer_id)
class Plan(models.Model):
# The two possible values for nickname
CLOUD_MONTHLY = 'monthly'
CLOUD_ANNUAL = 'annual'
nickname = models.CharField(max_length=40, unique=True) # type: str
stripe_plan_id = models.CharField(max_length=255, unique=True) # type: str
class Coupon(models.Model):
percent_off = models.SmallIntegerField(unique=True) # type: int
stripe_coupon_id = models.CharField(max_length=255, unique=True) # type: str
def __str__(self) -> str:
return '<Coupon: %s %s %s>' % (self.percent_off, self.stripe_coupon_id, self.id)
class BillingProcessor(models.Model):
log_row = models.ForeignKey(RealmAuditLog, on_delete=models.CASCADE) # RealmAuditLog
# Exactly one processor, the global processor, has realm=None.
realm = models.OneToOneField(Realm, null=True, on_delete=models.CASCADE) # type: Realm
DONE = 'done'
STARTED = 'started'
SKIPPED = 'skipped' # global processor only
STALLED = 'stalled' # realm processors only
state = models.CharField(max_length=20) # type: str
last_modified = models.DateTimeField(auto_now=True) # type: datetime.datetime
def __str__(self) -> str:
return '<BillingProcessor: %s %s %s>' % (self.realm, self.log_row, self.id)

View File

@@ -575,6 +575,7 @@ if USING_PGROONGA:
INSTALLED_APPS += EXTRA_INSTALLED_APPS INSTALLED_APPS += EXTRA_INSTALLED_APPS
ZILENCER_ENABLED = 'zilencer' in INSTALLED_APPS ZILENCER_ENABLED = 'zilencer' in INSTALLED_APPS
CORPORATE_ENABLED = 'corporate' in INSTALLED_APPS
# Base URL of the Tornado server # Base URL of the Tornado server
# We set it to None when running backend tests or populate_db. # We set it to None when running backend tests or populate_db.