Browse Source

Pricing updates.

pull/163/head
Pēteris Caune 7 years ago
parent
commit
01fef3d054
10 changed files with 124 additions and 106 deletions
  1. +20
    -0
      hc/payments/migrations/0005_subscription_plan_name.py
  2. +10
    -24
      hc/payments/models.py
  3. +5
    -4
      hc/payments/tests/test_pricing.py
  4. +37
    -7
      hc/payments/tests/test_set_plan.py
  5. +4
    -4
      hc/payments/views.py
  6. +9
    -0
      static/css/profile.css
  7. +4
    -4
      static/js/pricing.js
  8. +20
    -40
      templates/accounts/billing.html
  9. +1
    -1
      templates/front/my_checks.html
  10. +14
    -22
      templates/payments/pricing.html

+ 20
- 0
hc/payments/migrations/0005_subscription_plan_name.py View File

@ -0,0 +1,20 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11.6 on 2018-03-04 17:28
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('payments', '0004_subscription_send_invoices'),
]
operations = [
migrations.AddField(
model_name='subscription',
name='plan_name',
field=models.CharField(blank=True, max_length=50),
),
]

+ 10
- 24
hc/payments/models.py View File

@ -42,35 +42,12 @@ class Subscription(models.Model):
payment_method_token = models.CharField(max_length=35, blank=True)
subscription_id = models.CharField(max_length=10, blank=True)
plan_id = models.CharField(max_length=10, blank=True)
plan_name = models.CharField(max_length=50, blank=True)
address_id = models.CharField(max_length=2, blank=True)
send_invoices = models.BooleanField(default=True)
objects = SubscriptionManager()
def price(self):
if self.plan_id == "P5":
return 5
elif self.plan_id == "P50":
return 50
elif self.plan_id == "Y48":
return 48
elif self.plan_id == "Y480":
return 480
elif self.plan_id == "T144":
return 144
return 0
def period(self):
if self.plan_id.startswith("P"):
return "month"
elif self.plan_id.startswith("Y"):
return "year"
elif self.plan_id.startswith("T"):
return "3 years"
raise NotImplementedError("Unexpected plan: %s" % self.plan_id)
@property
def payment_method(self):
if not self.payment_method_token:
@ -160,6 +137,15 @@ class Subscription(models.Model):
if result.is_success:
self.subscription_id = result.subscription.id
self.plan_id = plan_id
if plan_id == "P20":
self.plan_name = "Standard ($20 / month)"
elif plan_id == "Y192":
self.plan_name = "Standard ($192 / year)"
elif plan_id == "P80":
self.plan_name = "Plus ($80 / month)"
elif plan_id == "Y768":
self.plan_name = "Plus ($768 / year)"
self.save()
return result


+ 5
- 4
hc/payments/tests/test_pricing.py View File

@ -8,7 +8,7 @@ class PricingTestCase(BaseTestCase):
def test_anonymous(self):
r = self.client.get("/pricing/")
self.assertContains(r, "Unlimited Checks", status_code=200)
self.assertContains(r, "Unlimited Team Members", status_code=200)
# A subscription object should have NOT been created
assert Subscription.objects.count() == 0
@ -17,7 +17,7 @@ class PricingTestCase(BaseTestCase):
self.client.login(username="[email protected]", password="password")
r = self.client.get("/pricing/")
self.assertContains(r, "Unlimited Checks", status_code=200)
self.assertContains(r, "Unlimited Team Members", status_code=200)
# A subscription object still should have NOT been created
assert Subscription.objects.count() == 0
@ -39,10 +39,11 @@ class PricingTestCase(BaseTestCase):
def test_it_shows_active_plan(self):
self.sub = Subscription(user=self.alice)
self.sub.subscription_id = "test-id"
self.sub.plan_id = "P5"
self.sub.plan_id = "P20"
self.sub.plan_name = "Standard ($20 / month)"
self.sub.save()
self.client.login(username="[email protected]", password="password")
r = self.client.get("/pricing/")
self.assertContains(r, "Standard (monthly)", status_code=200)
self.assertContains(r, "Standard ($20 / month)", status_code=200)

+ 37
- 7
hc/payments/tests/test_set_plan.py View File

@ -12,7 +12,7 @@ class SetPlanTestCase(BaseTestCase):
mock.Subscription.create.return_value.is_success = True
mock.Subscription.create.return_value.subscription.id = "t-sub-id"
def run_set_plan(self, plan_id="P5"):
def run_set_plan(self, plan_id="P20"):
form = {"plan_id": plan_id}
self.client.login(username="[email protected]", password="password")
return self.client.post("/pricing/set_plan/", form, follow=True)
@ -31,12 +31,13 @@ class SetPlanTestCase(BaseTestCase):
# Subscription should be filled out:
sub = Subscription.objects.get(user=self.alice)
self.assertEqual(sub.subscription_id, "t-sub-id")
self.assertEqual(sub.plan_id, "P5")
self.assertEqual(sub.plan_id, "P20")
self.assertEqual(sub.plan_name, "Standard ($20 / month)")
# User's profile should have a higher limits
self.profile.refresh_from_db()
self.assertEqual(self.profile.ping_log_limit, 1000)
self.assertEqual(self.profile.check_limit, 500)
self.assertEqual(self.profile.check_limit, 50)
self.assertEqual(self.profile.team_limit, 9)
self.assertEqual(self.profile.sms_limit, 50)
self.assertEqual(self.profile.sms_sent, 0)
@ -52,18 +53,19 @@ class SetPlanTestCase(BaseTestCase):
self.profile.sms_sent = 1
self.profile.save()
r = self.run_set_plan("Y48")
r = self.run_set_plan("Y192")
self.assertRedirects(r, "/accounts/profile/billing/")
# Subscription should be filled out:
sub = Subscription.objects.get(user=self.alice)
self.assertEqual(sub.subscription_id, "t-sub-id")
self.assertEqual(sub.plan_id, "Y48")
self.assertEqual(sub.plan_id, "Y192")
self.assertEqual(sub.plan_name, "Standard ($192 / year)")
# User's profile should have a higher limits
self.profile.refresh_from_db()
self.assertEqual(self.profile.ping_log_limit, 1000)
self.assertEqual(self.profile.check_limit, 500)
self.assertEqual(self.profile.check_limit, 50)
self.assertEqual(self.profile.team_limit, 9)
self.assertEqual(self.profile.sms_limit, 50)
self.assertEqual(self.profile.sms_sent, 0)
@ -71,13 +73,41 @@ class SetPlanTestCase(BaseTestCase):
# braintree.Subscription.cancel should have not been called
assert not mock.Subscription.cancel.called
@patch("hc.payments.models.braintree")
def test_plus_works(self, mock):
self._setup_mock(mock)
self.profile.sms_limit = 0
self.profile.sms_sent = 1
self.profile.save()
r = self.run_set_plan("P80")
self.assertRedirects(r, "/accounts/profile/billing/")
# Subscription should be filled out:
sub = Subscription.objects.get(user=self.alice)
self.assertEqual(sub.subscription_id, "t-sub-id")
self.assertEqual(sub.plan_id, "P80")
self.assertEqual(sub.plan_name, "Plus ($80 / month)")
# User's profile should have a higher limits
self.profile.refresh_from_db()
self.assertEqual(self.profile.ping_log_limit, 1000)
self.assertEqual(self.profile.check_limit, 500)
self.assertEqual(self.profile.team_limit, 500)
self.assertEqual(self.profile.sms_limit, 500)
self.assertEqual(self.profile.sms_sent, 0)
# braintree.Subscription.cancel should have not been called
assert not mock.Subscription.cancel.called
@patch("hc.payments.models.braintree")
def test_it_cancels(self, mock):
self._setup_mock(mock)
self.sub = Subscription(user=self.alice)
self.sub.subscription_id = "test-id"
self.sub.plan_id = "P5"
self.sub.plan_id = "P20"
self.sub.save()
self.profile.sms_limit = 1


+ 4
- 4
hc/payments/views.py View File

@ -93,7 +93,7 @@ def log_and_bail(request, result):
@require_POST
def set_plan(request):
plan_id = request.POST["plan_id"]
if plan_id not in ("", "P5", "P50", "Y48", "Y480", "T144"):
if plan_id not in ("", "P20", "P80", "Y192", "Y768"):
return HttpResponseBadRequest()
sub = Subscription.objects.for_user(request.user)
@ -117,14 +117,14 @@ def set_plan(request):
# Update user's profile
profile = request.user.profile
if plan_id in ("P5", "Y48", "T144"):
if plan_id in ("P20", "Y192"):
profile.ping_log_limit = 1000
profile.check_limit = 500
profile.check_limit = 50
profile.team_limit = 9
profile.sms_limit = 50
profile.sms_sent = 0
profile.save()
elif plan_id in ("P50", "Y480"):
elif plan_id in ("P80", "Y768"):
profile.ping_log_limit = 1000
profile.check_limit = 500
profile.team_limit = 500


+ 9
- 0
static/css/profile.css View File

@ -36,4 +36,13 @@ span.loading {
.billing-empty {
color: #888;
}
.at-limit span {
display: inline-block;
background-color: #FFD54F;
font-weight: bold;
font-size: 12px;
border-radius: 2px;
padding: 2px 6px;
}

+ 4
- 4
static/js/pricing.js View File

@ -1,13 +1,13 @@
$(function () {
$("#period-controls :input").change(function() {
if (this.value == "monthly") {
$("#s-price").text("$5");
$("#p-price").text("$50");
$("#s-price").text("$20");
$("#p-price").text("$80");
}
if (this.value == "annual") {
$("#s-price").text("$4");
$("#p-price").text("$40");
$("#s-price").text("$16");
$("#p-price").text("$64");
}
});
});

+ 20
- 40
templates/accounts/billing.html View File

@ -46,13 +46,7 @@
{% if sub is None or sub.plan_id == "" %}
Free
{% else %}
{% if sub.plan_id == "P5" or sub.plan_id == "Y48" or sub.plan_id == "T144" %}
Standard
{% elif sub.plan_id == "P50" or sub.plan_id == "Y480" %}
Plus
{% endif %}
(${{ sub.price }} per {{ sub.period }})
{{ sub.plan_name }}
{% endif %}
</td>
</tr>
@ -66,24 +60,21 @@
{% endif %}
<tr>
<td>Checks Used</td>
<td>
{{ num_checks }} of
{% if sub.plan_id %}
unlimited
{% else %}
{{ profile.check_limit }}
{% endif %}
<td {% if num_checks >= profile.check_limit %} class="at-limit" {% endif %}>
<span>{{ num_checks }} of {{ profile.check_limit }}</span>
</td>
</tr>
<tr>
<td>Team Size</td>
<td>
<td {% if team_size >= profile.team_limit %} class="at-limit" {% endif %}>
<span>
{{ team_size }} of
{% if profile.team_limit == 500 %}
unlimited
{% else %}
{{ team_max }}
{% endif %}
</span>
</td>
</tr>
</table>
@ -219,57 +210,46 @@
Enjoy free service.
</label>
<h2>Standard <small>Unlimited checks, 20 team members</small></h2>
<h2>Standard <small>50 checks, 10 team members</small></h2>
<label class="radio-container">
<input
type="radio"
name="plan_id"
value="P5"
{% if sub.plan_id == "P5" %} checked {% endif %}>
value="P20"
{% if sub.plan_id == "P20" %} checked {% endif %}>
<span class="radiomark"></span>
Monthly, $5/month
Monthly, $20 / month
</label>
<label class="radio-container">
<input
type="radio"
name="plan_id"
value="Y48"
{% if sub.plan_id == "Y48" %} checked {% endif %}>
value="Y192"
{% if sub.plan_id == "Y192" %} checked {% endif %}>
<span class="radiomark"></span>
Annual, $48/year (20% off monthly)
Annual, $192 / year (20% off monthly)
</label>
<h2>Plus <small>500 checks, unlimited team members</small></h2>
<label class="radio-container">
<input
type="radio"
name="plan_id"
value="T144"
{% if sub.plan_id == "T144" %} checked {% endif %}>
<span class="radiomark"></span>
3-year, $144 per 3 years (20% off monthly)
</label>
<h2>Plus <small>Unlimited checks, unlimited team members</small></h2>
<label class="radio-container">
<input
type="radio"
name="plan_id"
value="P50"
{% if sub.plan_id == "P50" %} checked {% endif %}>
value="P80"
{% if sub.plan_id == "P80" %} checked {% endif %}>
<span class="radiomark"></span>
Monthly, $50/month
Monthly, $80/month
</label>
<label class="radio-container">
<input
type="radio"
name="plan_id"
value="Y480"
{% if sub.plan_id == "Y480" %} checked {% endif %}>
value="Y768"
{% if sub.plan_id == "Y768" %} checked {% endif %}>
<span class="radiomark"></span>
Annual, $480/year (20% off monthly)
Annual, $768/year (20% off monthly)
</label>
<div class="alert alert-warning">


+ 1
- 1
templates/front/my_checks.html View File

@ -45,7 +45,7 @@
<div class="alert alert-info">
<strong>Check limit reached.</strong>
To add more checks, please
<a href="{% url 'hc-pricing' %}">upgrade your account!</a>
<a href="{% url 'hc-billing' %}">upgrade your account!</a>
</div>
{% endif %}
</div>


+ 14
- 22
templates/payments/pricing.html View File

@ -13,29 +13,21 @@
<div class="col-md-12">
<div id="subscription-status" class="jumbotron">
<p>
Your account is currently on the
{% if sub.plan_id == "P5" %}
<strong>Standard (monthly)</strong>
{% elif sub.plan_id == "Y48" %}
<strong>Standard (annual)</strong>
{% elif sub.plan_id == "T144" %}
<strong>Standard (3 years)</strong>
{% elif sub.plan_id == "Y480" %}
<strong>Plus (annual)</strong>
{% elif sub.plan_id == "P50" %}
<strong>Plus (monthly)</strong>
{% if sub.plan_id %}
Your account is currently on the
<strong>{{ sub.plan_name }}</strong>
plan. Thank you for supporting {% site_name %}!
{% else %}
<strong>Free</strong>
Your account is currently on the
<strong>Free</strong> plan.
{% endif %}
plan.
{% if sub.plan_id %}
You are paying
<strong>${{ sub.price }}</strong> / {{ sub.period }}.
{% endif %}
</p>
<p>
{% if sub.plan_id %}
<a class="btn btn-default" href="{% url 'hc-billing' %}">Billing Details</a>
{% else %}
<a class="btn btn-default" href="{% url 'hc-billing' %}">Billing Details and Plan Upgrades</a>
{% endif %}
</p>
</div>
</div>
@ -100,12 +92,12 @@
<div class="panel-body text-center">
<h1>Standard</h1>
<h2>
<span id="s-price">$5</span><span class="mo">/mo</span>
<span id="s-price">$20</span><span class="mo">/mo</span>
</h2>
</div>
<ul class="list-group text-center">
<li class="list-group-item">Unlimited Checks</li>
<li class="list-group-item">50 Checks</li>
<li class="list-group-item">10 Team Members</li>
<li class="list-group-item">1000 log entries per check</li>
<li class="list-group-item">50 SMS alerts per month</li>
@ -128,12 +120,12 @@
<div class="panel-body text-center">
<h1>Plus</h1>
<h2>
<span id="p-price">$50</span><span class="mo">/mo</span>
<span id="p-price">$80</span><span class="mo">/mo</span>
</h2>
</div>
<ul class="list-group text-center">
<li class="list-group-item">Unlimited Checks</li>
<li class="list-group-item">500 Checks</li>
<li class="list-group-item">Unlimited Team Members</li>
<li class="list-group-item">1000 log entries per check</li>
<li class="list-group-item">500 SMS alerts per month</li>


Loading…
Cancel
Save