mirror of
https://github.com/zulip/zulip.git
synced 2025-10-24 00:23:49 +00:00
support: Add option to change billing method.
This commit is contained in:
@@ -461,8 +461,9 @@ class TestSupportEndpoint(ZulipTestCase):
|
|||||||
'<b>Billing schedule</b>: Annual',
|
'<b>Billing schedule</b>: Annual',
|
||||||
'<b>Licenses</b>: 2/10 (Manual)',
|
'<b>Licenses</b>: 2/10 (Manual)',
|
||||||
'<b>Price per license</b>: $80.0',
|
'<b>Price per license</b>: $80.0',
|
||||||
'<b>Payment method</b>: Send invoice',
|
|
||||||
'<b>Next invoice date</b>: 02 January 2017',
|
'<b>Next invoice date</b>: 02 January 2017',
|
||||||
|
'<option value="send_invoice" selected>',
|
||||||
|
'<option value="charge_automatically" >'
|
||||||
], result)
|
], result)
|
||||||
|
|
||||||
def check_preregistration_user_query_result(result: HttpResponse, email: str, invite: bool=False) -> None:
|
def check_preregistration_user_query_result(result: HttpResponse, email: str, invite: bool=False) -> None:
|
||||||
@@ -572,6 +573,28 @@ class TestSupportEndpoint(ZulipTestCase):
|
|||||||
check_realm_reactivation_link_query_result(result)
|
check_realm_reactivation_link_query_result(result)
|
||||||
check_zulip_realm_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:
|
def test_change_plan_type(self) -> None:
|
||||||
cordelia = self.example_user('cordelia')
|
cordelia = self.example_user('cordelia')
|
||||||
self.login_user(cordelia)
|
self.login_user(cordelia)
|
||||||
|
|||||||
@@ -82,6 +82,7 @@ if settings.BILLING_ENABLED:
|
|||||||
get_discount_for_realm,
|
get_discount_for_realm,
|
||||||
get_latest_seat_count,
|
get_latest_seat_count,
|
||||||
make_end_of_cycle_updates_if_needed,
|
make_end_of_cycle_updates_if_needed,
|
||||||
|
update_billing_method_of_current_plan,
|
||||||
update_sponsorship_status,
|
update_sponsorship_status,
|
||||||
void_all_open_invoices,
|
void_all_open_invoices,
|
||||||
)
|
)
|
||||||
@@ -1174,6 +1175,14 @@ def support(request: HttpRequest) -> HttpResponse:
|
|||||||
elif status == "deactivated":
|
elif status == "deactivated":
|
||||||
do_deactivate_realm(realm, request.user)
|
do_deactivate_realm(realm, request.user)
|
||||||
context["message"] = f"{realm.name} deactivated."
|
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:
|
elif request.POST.get("sponsorship_pending", None) is not None:
|
||||||
sponsorship_pending = request.POST.get("sponsorship_pending")
|
sponsorship_pending = request.POST.get("sponsorship_pending")
|
||||||
if sponsorship_pending == "true":
|
if sponsorship_pending == "true":
|
||||||
|
|||||||
@@ -655,3 +655,9 @@ def void_all_open_invoices(realm: Realm) -> int:
|
|||||||
)
|
)
|
||||||
voided_invoices_count += 1
|
voided_invoices_count += 1
|
||||||
return voided_invoices_count
|
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"])
|
||||||
|
|||||||
@@ -37,6 +37,7 @@ from corporate.lib.stripe import (
|
|||||||
sign_string,
|
sign_string,
|
||||||
stripe_get_customer,
|
stripe_get_customer,
|
||||||
unsign_string,
|
unsign_string,
|
||||||
|
update_billing_method_of_current_plan,
|
||||||
update_license_ledger_for_automanaged_plan,
|
update_license_ledger_for_automanaged_plan,
|
||||||
update_license_ledger_if_needed,
|
update_license_ledger_if_needed,
|
||||||
update_or_create_stripe_customer,
|
update_or_create_stripe_customer,
|
||||||
@@ -1846,6 +1847,23 @@ class StripeTest(StripeTestCase):
|
|||||||
for invoice in invoices:
|
for invoice in invoices:
|
||||||
self.assertEqual(invoice.status, "void")
|
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):
|
class RequiresBillingAccessTest(ZulipTestCase):
|
||||||
def setUp(self) -> None:
|
def setUp(self) -> None:
|
||||||
super().setUp()
|
super().setUp()
|
||||||
|
|||||||
@@ -94,18 +94,23 @@ tr.admin td:first-child {
|
|||||||
top: -40px;
|
top: -40px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.downgrade-plan-form {
|
.billing-method-form {
|
||||||
position: relative;
|
position: relative;
|
||||||
top: -70px;
|
top: -70px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.downgrade-plan-form {
|
||||||
|
position: relative;
|
||||||
|
top: -115px;
|
||||||
|
}
|
||||||
|
|
||||||
.downgrade-plan-method-select {
|
.downgrade-plan-method-select {
|
||||||
width: auto;
|
width: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
.scrub-realm-form {
|
.scrub-realm-form {
|
||||||
position: relative;
|
position: relative;
|
||||||
top: -40px;
|
top: -70px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.support-search-button {
|
.support-search-button {
|
||||||
|
|||||||
@@ -72,10 +72,21 @@
|
|||||||
{% else %}
|
{% else %}
|
||||||
<b>Fixed price</b>: ${{ realm.current_plan.fixed_price/100 }}<br>
|
<b>Fixed price</b>: ${{ realm.current_plan.fixed_price/100 }}<br>
|
||||||
{% endif %}
|
{% 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>
|
<b>Next invoice date</b>: {{ realm.current_plan.next_invoice_date.strftime('%d %B %Y') }}<br>
|
||||||
</div>
|
</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">
|
<form method="POST" class="downgrade-plan-form">
|
||||||
<br>
|
<br>
|
||||||
<b>Downgrade plan</b><br>
|
<b>Downgrade plan</b><br>
|
||||||
|
|||||||
Reference in New Issue
Block a user