From 3e438538b4e43a5dc39bc95c57a94103d5b92b57 Mon Sep 17 00:00:00 2001 From: Vishnu KS Date: Thu, 13 Aug 2020 16:50:18 +0530 Subject: [PATCH] support: Add support for downgrading realm. --- analytics/tests/test_views.py | 30 ++++++++++++++++++++++++++ analytics/views.py | 15 +++++++++++++ static/styles/portico/activity.css | 9 ++++++++ templates/analytics/realm_details.html | 15 +++++++++++++ 4 files changed, 69 insertions(+) diff --git a/analytics/tests/test_views.py b/analytics/tests/test_views.py index 0e3a293566..c77766ecd7 100644 --- a/analytics/tests/test_views.py +++ b/analytics/tests/test_views.py @@ -684,6 +684,36 @@ class TestSupportEndpoint(ZulipTestCase): m.assert_called_once_with(lear_realm) self.assert_in_success_response(["Realm reactivation email sent to admins of Lear"], result) + def test_downgrade_realm(self) -> 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) + + with mock.patch("analytics.views.downgrade_at_the_end_of_billing_cycle") as m: + result = self.client_post("/activity/support", {"realm_id": f"{iago.realm_id}", + "downgrade_method": "downgrade_at_billing_cycle_end"}) + m.assert_called_once_with(get_realm("zulip")) + self.assert_in_success_response(["Zulip Dev marked for downgrade at the end of billing cycle"], result) + + with mock.patch("analytics.views.downgrade_now_without_creating_additional_invoices") as m: + result = self.client_post("/activity/support", {"realm_id": f"{iago.realm_id}", + "downgrade_method": "downgrade_now_without_additional_licenses"}) + m.assert_called_once_with(get_realm("zulip")) + self.assert_in_success_response(["Zulip Dev downgraded without creating additional invoices"], result) + + with mock.patch("analytics.views.downgrade_now_without_creating_additional_invoices") as m1: + with mock.patch("analytics.views.void_all_open_invoices", return_value=1) as m2: + result = self.client_post("/activity/support", {"realm_id": f"{iago.realm_id}", + "downgrade_method": "downgrade_now_void_open_invoices"}) + m1.assert_called_once_with(get_realm("zulip")) + m2.assert_called_once_with(get_realm("zulip")) + self.assert_in_success_response(["Zulip Dev downgraded and voided 1 open invoices"], result) + def test_scrub_realm(self) -> None: cordelia = self.example_user('cordelia') lear_realm = get_realm('lear') diff --git a/analytics/views.py b/analytics/views.py index 660207674a..aed9f3047c 100644 --- a/analytics/views.py +++ b/analytics/views.py @@ -75,12 +75,15 @@ if settings.BILLING_ENABLED: from corporate.lib.stripe import ( approve_sponsorship, attach_discount_to_realm, + downgrade_at_the_end_of_billing_cycle, + downgrade_now_without_creating_additional_invoices, get_current_plan_by_realm, get_customer_by_realm, get_discount_for_realm, get_latest_seat_count, make_end_of_cycle_updates_if_needed, update_sponsorship_status, + void_all_open_invoices, ) if settings.ZILENCER_ENABLED: @@ -1183,6 +1186,18 @@ def support(request: HttpRequest) -> HttpResponse: if request.POST.get('approve_sponsorship') == "approve_sponsorship": approve_sponsorship(realm) context["message"] = f"Sponsorship approved for {realm.name}" + elif request.POST.get('downgrade_method', None) is not None: + downgrade_method = request.POST.get('downgrade_method') + if downgrade_method == "downgrade_at_billing_cycle_end": + downgrade_at_the_end_of_billing_cycle(realm) + context["message"] = f"{realm.name} marked for downgrade at the end of billing cycle" + elif downgrade_method == "downgrade_now_without_additional_licenses": + downgrade_now_without_creating_additional_invoices(realm) + context["message"] = f"{realm.name} downgraded without creating additional invoices" + elif downgrade_method == "downgrade_now_void_open_invoices": + downgrade_now_without_creating_additional_invoices(realm) + voided_invoices_count = void_all_open_invoices(realm) + context["message"] = f"{realm.name} downgraded and voided {voided_invoices_count} open invoices" elif request.POST.get("scrub_realm", None) is not None: if request.POST.get("scrub_realm") == "scrub_realm": do_scrub_realm(realm, acting_user=request.user) diff --git a/static/styles/portico/activity.css b/static/styles/portico/activity.css index 18260a7a0c..af58809a09 100644 --- a/static/styles/portico/activity.css +++ b/static/styles/portico/activity.css @@ -94,6 +94,15 @@ tr.admin td:first-child { top: -40px; } +.downgrade-plan-form { + position: relative; + top: -70px; +} + +.downgrade-plan-method-select { + width: auto; +} + .scrub-realm-form { position: relative; top: -40px; diff --git a/templates/analytics/realm_details.html b/templates/analytics/realm_details.html index de40620b7b..119e413a65 100644 --- a/templates/analytics/realm_details.html +++ b/templates/analytics/realm_details.html @@ -75,6 +75,21 @@ Payment method: {% if realm.current_plan.charge_automatically %}Card{% else %}Send invoice{% endif %}
Next invoice date: {{ realm.current_plan.next_invoice_date.strftime('%d %B %Y') }}
+ +
+
+ Downgrade plan
+ {{ csrf_input }} + + + +
+ {% endif %}