diff --git a/corporate/tests/test_support_views.py b/corporate/tests/test_support_views.py index ca8db53b98..cdcc4e18af 100644 --- a/corporate/tests/test_support_views.py +++ b/corporate/tests/test_support_views.py @@ -573,6 +573,70 @@ class TestRemoteServerSupportEndpoint(ZulipTestCase): self.assertEqual(plan.tier, CustomerPlan.TIER_SELF_HOSTED_LEGACY) self.assertEqual(next_plan.tier, CustomerPlan.TIER_SELF_HOSTED_BASIC) + def test_support_deactivate_remote_server(self) -> None: + iago = self.example_user("iago") + self.login_user(iago) + + # A remote server cannot be deactivated when an upgrade is scheduled. + remote_server_with_upgrade = RemoteZulipServer.objects.get(hostname="zulip-4.example.com") + self.assertFalse(remote_server_with_upgrade.deactivated) + result = self.client_post( + "/activity/remote/support", + { + "remote_server_id": f"{remote_server_with_upgrade.id}", + "remote_server_status": "deactivated", + }, + ) + self.assert_in_success_response( + [ + f"Cannot deactivate remote server ({remote_server_with_upgrade.hostname}) that has active or scheduled plans." + ], + result, + ) + remote_server_with_upgrade.refresh_from_db() + self.assertFalse(remote_server_with_upgrade.deactivated) + + remote_server_no_upgrade = RemoteZulipServer.objects.get(hostname="zulip-5.example.com") + self.assertFalse(remote_server_no_upgrade.deactivated) + result = self.client_post( + "/activity/remote/support", + { + "remote_server_id": f"{remote_server_no_upgrade.id}", + "remote_server_status": "deactivated", + }, + ) + self.assert_in_success_response( + [f"Remote server ({remote_server_no_upgrade.hostname}) deactivated."], result + ) + remote_server_no_upgrade.refresh_from_db() + self.assertTrue(remote_server_no_upgrade.deactivated) + audit_log = RemoteZulipServerAuditLog.objects.filter( + event_type=RemoteZulipServerAuditLog.REMOTE_SERVER_DEACTIVATED + ).last() + assert audit_log is not None + self.assertEqual(audit_log.server, remote_server_no_upgrade) + + def test_support_reactivate_remote_server(self) -> None: + iago = self.example_user("iago") + self.login_user(iago) + + remote_server = RemoteZulipServer.objects.get(hostname="zulip-0.example.com") + self.assertTrue(remote_server.deactivated) + result = self.client_post( + "/activity/remote/support", + {"remote_server_id": f"{remote_server.id}", "remote_server_status": "active"}, + ) + self.assert_in_success_response( + [f"Remote server ({remote_server.hostname}) reactivated."], result + ) + remote_server.refresh_from_db() + self.assertFalse(remote_server.deactivated) + audit_log = RemoteZulipServerAuditLog.objects.filter( + event_type=RemoteZulipServerAuditLog.REMOTE_SERVER_REACTIVATED + ).last() + assert audit_log is not None + self.assertEqual(audit_log.server, remote_server) + class TestSupportEndpoint(ZulipTestCase): def create_customer_and_plan(self, realm: Realm, monthly: bool = False) -> Customer: diff --git a/corporate/views/support.py b/corporate/views/support.py index 20448f295a..a981574416 100644 --- a/corporate/views/support.py +++ b/corporate/views/support.py @@ -26,10 +26,13 @@ from corporate.lib.stripe import ( RealmBillingSession, RemoteRealmBillingSession, RemoteServerBillingSession, + ServerDeactivateWithExistingPlanError, SupportRequestError, SupportType, SupportViewRequest, cents_to_dollar_string, + do_deactivate_remote_server, + do_reactivate_remote_server, format_discount_percentage, ) from corporate.lib.support import ( @@ -534,6 +537,9 @@ def remote_servers_support( default=None, str_validator=check_string_in(VALID_MODIFY_PLAN_METHODS) ), delete_fixed_price_next_plan: bool = REQ(default=False, json_validator=check_bool), + remote_server_status: Optional[str] = REQ( + default=None, str_validator=check_string_in(VALID_STATUS_VALUES) + ), ) -> HttpResponse: context: Dict[str, Any] = {} @@ -608,6 +614,28 @@ def remote_servers_support( support_view_request = SupportViewRequest( support_type=SupportType.delete_fixed_price_next_plan, ) + elif remote_server_status: + assert remote_server is not None + remote_server_status_billing_session = RemoteServerBillingSession( + support_staff=acting_user, remote_server=remote_server + ) + if remote_server_status == "active": + do_reactivate_remote_server(remote_server) + context["success_message"] = ( + f"Remote server ({remote_server.hostname}) reactivated." + ) + else: + assert remote_server_status == "deactivated" + try: + do_deactivate_remote_server(remote_server, remote_server_status_billing_session) + context["success_message"] = ( + f"Remote server ({remote_server.hostname}) deactivated." + ) + except ServerDeactivateWithExistingPlanError: + context["error_message"] = ( + f"Cannot deactivate remote server ({remote_server.hostname}) that has active or scheduled plans." + ) + if support_view_request is not None: if remote_realm_support_request: try: diff --git a/templates/corporate/support/remote_server_support.html b/templates/corporate/support/remote_server_support.html index f8fee04d95..000799f2fb 100644 --- a/templates/corporate/support/remote_server_support.html +++ b/templates/corporate/support/remote_server_support.html @@ -146,6 +146,22 @@ {% endwith %} {% endif %} + + {% if remote_server.deactivated %} +
+ {% else %} + + {% endif %}