Skip to content

Commit

Permalink
support: Add option to change billing method.
Browse files Browse the repository at this point in the history
  • Loading branch information
hackerkid authored and timabbott committed Sep 28, 2020
1 parent 367c792 commit 510efbc
Show file tree
Hide file tree
Showing 6 changed files with 76 additions and 4 deletions.
25 changes: 24 additions & 1 deletion analytics/tests/test_views.py
Original file line number Diff line number Diff line change
Expand Up @@ -461,8 +461,9 @@ def check_lear_realm_query_result(result: HttpResponse) -> None:
'<b>Billing schedule</b>: Annual',
'<b>Licenses</b>: 2/10 (Manual)',
'<b>Price per license</b>: $80.0',
'<b>Payment method</b>: Send invoice',
'<b>Next invoice date</b>: 02 January 2017',
'<option value="send_invoice" selected>',
'<option value="charge_automatically" >'
], result)

def check_preregistration_user_query_result(result: HttpResponse, email: str, invite: bool=False) -> None:
Expand Down Expand Up @@ -572,6 +573,28 @@ def check_realm_reactivation_link_query_result(result: HttpResponse) -> None:
check_realm_reactivation_link_query_result(result)
check_zulip_realm_query_result(result)

@mock.patch("analytics.views.update_billing_method_of_current_plan")
def test_change_billing_method(self, m: mock.Mock) -> None:
cordelia = self.example_user('cordelia')
self.login_user(cordelia)

result = self.client_post("/activity/support", {"realm_id": f"{cordelia.realm_id}", "plan_type": "2"})
self.assertEqual(result.status_code, 302)
self.assertEqual(result["Location"], "/login/")

iago = self.example_user("iago")
self.login_user(iago)

result = self.client_post("/activity/support", {"realm_id": f"{iago.realm_id}", "billing_method": "charge_automatically"})
m.assert_called_once_with(get_realm("zulip"), charge_automatically=True)
self.assert_in_success_response(["Billing method of Zulip Dev updated to charge automatically"], result)

m.reset_mock()

result = self.client_post("/activity/support", {"realm_id": f"{iago.realm_id}", "billing_method": "send_invoice"})
m.assert_called_once_with(get_realm("zulip"), charge_automatically=False)
self.assert_in_success_response(["Billing method of Zulip Dev updated to pay by invoice"], result)

def test_change_plan_type(self) -> None:
cordelia = self.example_user('cordelia')
self.login_user(cordelia)
Expand Down
9 changes: 9 additions & 0 deletions analytics/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@
get_discount_for_realm,
get_latest_seat_count,
make_end_of_cycle_updates_if_needed,
update_billing_method_of_current_plan,
update_sponsorship_status,
void_all_open_invoices,
)
Expand Down Expand Up @@ -1174,6 +1175,14 @@ def support(request: HttpRequest) -> HttpResponse:
elif status == "deactivated":
do_deactivate_realm(realm, request.user)
context["message"] = f"{realm.name} deactivated."
elif request.POST.get("billing_method", None) is not None:
billing_method = request.POST.get("billing_method")
if billing_method == "send_invoice":
update_billing_method_of_current_plan(realm, charge_automatically=False)
context["message"] = f"Billing method of {realm.name} updated to pay by invoice."
elif billing_method == "charge_automatically":
update_billing_method_of_current_plan(realm, charge_automatically=True)
context["message"] = f"Billing method of {realm.name} updated to charge automatically."
elif request.POST.get("sponsorship_pending", None) is not None:
sponsorship_pending = request.POST.get("sponsorship_pending")
if sponsorship_pending == "true":
Expand Down
6 changes: 6 additions & 0 deletions corporate/lib/stripe.py
Original file line number Diff line number Diff line change
Expand Up @@ -655,3 +655,9 @@ def void_all_open_invoices(realm: Realm) -> int:
)
voided_invoices_count += 1
return voided_invoices_count

def update_billing_method_of_current_plan(realm: Realm, charge_automatically: bool) -> None:
plan = get_current_plan_by_realm(realm)
if plan is not None:
plan.charge_automatically = charge_automatically
plan.save(update_fields=["charge_automatically"])
18 changes: 18 additions & 0 deletions corporate/tests/test_stripe.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
sign_string,
stripe_get_customer,
unsign_string,
update_billing_method_of_current_plan,
update_license_ledger_for_automanaged_plan,
update_license_ledger_if_needed,
update_or_create_stripe_customer,
Expand Down Expand Up @@ -1846,6 +1847,23 @@ def test_void_all_open_invoices(self, *mock: Mock) -> None:
for invoice in invoices:
self.assertEqual(invoice.status, "void")

def test_update_billing_method_of_current_plan(self) -> None:
realm = get_realm("zulip")
customer = Customer.objects.create(realm=realm, stripe_customer_id='cus_12345')
plan = CustomerPlan.objects.create(customer=customer, status=CustomerPlan.ACTIVE,
billing_cycle_anchor=timezone_now(),
billing_schedule=CustomerPlan.ANNUAL,
tier=CustomerPlan.STANDARD)
self.assertEqual(plan.charge_automatically, False)

update_billing_method_of_current_plan(realm, True)
plan.refresh_from_db()
self.assertEqual(plan.charge_automatically, True)

update_billing_method_of_current_plan(realm, False)
plan.refresh_from_db()
self.assertEqual(plan.charge_automatically, False)

class RequiresBillingAccessTest(ZulipTestCase):
def setUp(self) -> None:
super().setUp()
Expand Down
9 changes: 7 additions & 2 deletions static/styles/portico/activity.css
Original file line number Diff line number Diff line change
Expand Up @@ -94,18 +94,23 @@ tr.admin td:first-child {
top: -40px;
}

.downgrade-plan-form {
.billing-method-form {
position: relative;
top: -70px;
}

.downgrade-plan-form {
position: relative;
top: -115px;
}

.downgrade-plan-method-select {
width: auto;
}

.scrub-realm-form {
position: relative;
top: -40px;
top: -70px;
}

.support-search-button {
Expand Down
13 changes: 12 additions & 1 deletion templates/analytics/realm_details.html
Original file line number Diff line number Diff line change
Expand Up @@ -72,10 +72,21 @@ <h3>📅 Current plan</h3>
{% else %}
<b>Fixed price</b>: ${{ realm.current_plan.fixed_price/100 }}<br>
{% endif %}
<b>Payment method</b>: {% if realm.current_plan.charge_automatically %}Card{% else %}Send invoice{% endif %}<br>
<b>Next invoice date</b>: {{ realm.current_plan.next_invoice_date.strftime('%d %B %Y') }}<br>
</div>

<form method="POST" class="billing-method-form">
<br>
<b>Billing method</b><br>
{{ csrf_input }}
<input type="hidden" name="realm_id" value="{{ realm.id }}" />
<select name="billing_method" class="billing-method-select" required>
<option value="charge_automatically" {% if realm.current_plan.charge_automatically %}selected{% endif %}>Charge automatically</option>
<option value="send_invoice" {% if not realm.current_plan.charge_automatically %}selected{% endif %}>Pay by invoice</option>
</select>
<button type="submit" class="button rounded small support-submit-button">Update</button>
</form>

<form method="POST" class="downgrade-plan-form">
<br>
<b>Downgrade plan</b><br>
Expand Down

0 comments on commit 510efbc

Please sign in to comment.