billing: Adding invoicing fields to CustomerPlan.

This commit is contained in:
Rishi Gupta 2019-01-28 05:18:21 -08:00
parent fe280fc38c
commit 421cda0e34
4 changed files with 53 additions and 14 deletions

View File

@ -231,10 +231,10 @@ def compute_plan_parameters(
if discount is not None: if discount is not None:
# There are no fractional cents in Stripe, so round down to nearest integer. # There are no fractional cents in Stripe, so round down to nearest integer.
price_per_license = int(float(price_per_license * (1 - discount / 100)) + .00001) price_per_license = int(float(price_per_license * (1 - discount / 100)) + .00001)
next_billing_date = period_end next_invoice_date = period_end
if automanage_licenses: if automanage_licenses:
next_billing_date = add_months(billing_cycle_anchor, 1) next_invoice_date = add_months(billing_cycle_anchor, 1)
return billing_cycle_anchor, next_billing_date, period_end, price_per_license return billing_cycle_anchor, next_invoice_date, period_end, price_per_license
# Only used for cloud signups # Only used for cloud signups
@catch_stripe_errors @catch_stripe_errors
@ -251,7 +251,7 @@ def process_initial_upgrade(user: UserProfile, licenses: int, automanage_license
"Customer {} trying to upgrade, but has an active subscription".format(customer)) "Customer {} trying to upgrade, but has an active subscription".format(customer))
raise BillingError('subscribing with existing subscription', BillingError.TRY_RELOADING) raise BillingError('subscribing with existing subscription', BillingError.TRY_RELOADING)
billing_cycle_anchor, next_billing_date, period_end, price_per_license = compute_plan_parameters( billing_cycle_anchor, next_invoice_date, period_end, price_per_license = compute_plan_parameters(
automanage_licenses, billing_schedule, customer.default_discount) automanage_licenses, billing_schedule, customer.default_discount)
# The main design constraint in this function is that if you upgrade with a credit card, and the # The main design constraint in this function is that if you upgrade with a credit card, and the
# charge fails, everything should be rolled back as if nothing had happened. This is because we # charge fails, everything should be rolled back as if nothing had happened. This is because we
@ -294,15 +294,16 @@ def process_initial_upgrade(user: UserProfile, licenses: int, automanage_license
customer=customer, customer=customer,
# Deprecated, remove # Deprecated, remove
licenses=-1, licenses=-1,
billed_through=billing_cycle_anchor, next_invoice_date=next_invoice_date,
next_billing_date=next_billing_date,
**plan_params) **plan_params)
LicenseLedger.objects.create( ledger_entry = LicenseLedger.objects.create(
plan=plan, plan=plan,
is_renewal=True, is_renewal=True,
event_time=billing_cycle_anchor, event_time=billing_cycle_anchor,
licenses=billed_licenses, licenses=billed_licenses,
licenses_at_next_renewal=billed_licenses) licenses_at_next_renewal=billed_licenses)
plan.invoiced_through = ledger_entry
plan.save(update_fields=['invoiced_through'])
RealmAuditLog.objects.create( RealmAuditLog.objects.create(
realm=realm, acting_user=user, event_time=billing_cycle_anchor, realm=realm, acting_user=user, event_time=billing_cycle_anchor,
event_type=RealmAuditLog.CUSTOMER_PLAN_CREATED, event_type=RealmAuditLog.CUSTOMER_PLAN_CREATED,

View File

@ -0,0 +1,35 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11.18 on 2019-01-28 13:04
from __future__ import unicode_literals
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('corporate', '0004_licenseledger'),
]
operations = [
migrations.RenameField(
model_name='customerplan',
old_name='next_billing_date',
new_name='next_invoice_date',
),
migrations.RemoveField(
model_name='customerplan',
name='billed_through',
),
migrations.AddField(
model_name='customerplan',
name='invoiced_through',
field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, related_name='+', to='corporate.LicenseLedger'),
),
migrations.AddField(
model_name='customerplan',
name='invoicing_status',
field=models.SmallIntegerField(default=1),
),
]

View File

@ -39,9 +39,12 @@ class CustomerPlan(models.Model):
MONTHLY = 2 MONTHLY = 2
billing_schedule = models.SmallIntegerField() # type: int billing_schedule = models.SmallIntegerField() # type: int
# This is like analytic's FillState, but for billing next_invoice_date = models.DateTimeField(db_index=True) # type: datetime.datetime
billed_through = models.DateTimeField() # type: datetime.datetime invoiced_through = models.ForeignKey(
next_billing_date = models.DateTimeField(db_index=True) # type: datetime.datetime 'LicenseLedger', null=True, on_delete=CASCADE, related_name='+') # type: Optional[LicenseLedger]
DONE = 1
STARTED = 2
invoicing_status = models.SmallIntegerField(default=DONE) # type: int
STANDARD = 1 STANDARD = 1
PLUS = 2 # not available through self-serve signup PLUS = 2 # not available through self-serve signup

View File

@ -392,8 +392,8 @@ class StripeTest(StripeTestCase):
plan = CustomerPlan.objects.get( plan = CustomerPlan.objects.get(
customer=customer, automanage_licenses=True, customer=customer, automanage_licenses=True,
price_per_license=8000, fixed_price=None, discount=None, billing_cycle_anchor=self.now, price_per_license=8000, fixed_price=None, discount=None, billing_cycle_anchor=self.now,
billing_schedule=CustomerPlan.ANNUAL, billed_through=self.now, billing_schedule=CustomerPlan.ANNUAL, invoiced_through=LicenseLedger.objects.first(),
next_billing_date=self.next_month, tier=CustomerPlan.STANDARD, next_invoice_date=self.next_month, tier=CustomerPlan.STANDARD,
status=CustomerPlan.ACTIVE) status=CustomerPlan.ACTIVE)
LicenseLedger.objects.get( LicenseLedger.objects.get(
plan=plan, is_renewal=True, event_time=self.now, licenses=self.seat_count, plan=plan, is_renewal=True, event_time=self.now, licenses=self.seat_count,
@ -477,8 +477,8 @@ class StripeTest(StripeTestCase):
plan = CustomerPlan.objects.get( plan = CustomerPlan.objects.get(
customer=customer, automanage_licenses=False, charge_automatically=False, customer=customer, automanage_licenses=False, charge_automatically=False,
price_per_license=8000, fixed_price=None, discount=None, billing_cycle_anchor=self.now, price_per_license=8000, fixed_price=None, discount=None, billing_cycle_anchor=self.now,
billing_schedule=CustomerPlan.ANNUAL, billed_through=self.now, billing_schedule=CustomerPlan.ANNUAL, invoiced_through=LicenseLedger.objects.first(),
next_billing_date=self.next_year, tier=CustomerPlan.STANDARD, next_invoice_date=self.next_year, tier=CustomerPlan.STANDARD,
status=CustomerPlan.ACTIVE) status=CustomerPlan.ACTIVE)
LicenseLedger.objects.get( LicenseLedger.objects.get(
plan=plan, is_renewal=True, event_time=self.now, licenses=123, licenses_at_next_renewal=123) plan=plan, is_renewal=True, event_time=self.now, licenses=123, licenses_at_next_renewal=123)