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.actions import do_change_plan_type
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
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 corporate.lib.stripe import StripeConnectionError, \
run_billing_processor_one_step
from zilencer.models import BillingProcessor
from corporate.models import BillingProcessor
class Command(BaseCommand):
help = """Run BillingProcessors, to sync billing-relevant updates into Stripe.

View File

@ -1,5 +1,5 @@
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 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_next_billing_log_entry, run_billing_processor_one_step, \
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 = 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, \
extract_current_subscription, process_initial_upgrade, sign_string, \
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')

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.
Nearly all the billing-relevant code lives in `zilencer/`.
Nearly all the billing-relevant code lives in `corporate/`.
## General architecture

View File

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

View File

@ -250,8 +250,8 @@ def home_real(request: HttpRequest) -> HttpResponse:
show_billing = False
show_plans = False
if settings.ZILENCER_ENABLED:
from zilencer.models import Customer
if settings.CORPORATE_ENABLED:
from corporate.models import Customer
if user_profile.is_billing_admin or user_profile.is_realm_admin:
customer = Customer.objects.filter(realm=user_profile.realm).first()
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 zerver.models import AbstractPushDeviceToken, Realm, UserProfile, \
RealmAuditLog
from zerver.models import AbstractPushDeviceToken
def get_remote_server_by_uuid(uuid: str) -> 'RemoteZulipServer':
return RemoteZulipServer.objects.get(uuid=uuid)
@ -35,44 +34,3 @@ class RemotePushDeviceToken(AbstractPushDeviceToken):
def __str__(self) -> str:
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
ZILENCER_ENABLED = 'zilencer' in INSTALLED_APPS
CORPORATE_ENABLED = 'corporate' in INSTALLED_APPS
# Base URL of the Tornado server
# We set it to None when running backend tests or populate_db.