mirror of https://github.com/zulip/zulip.git
support: Add admin support for updating end date of active plan.
This currently will only apply to tier.SELF_HOSTED_LEGACY plans.
This commit is contained in:
parent
71263ac2ab
commit
2994685399
|
@ -336,6 +336,44 @@ class TestRemoteServerSupportEndpoint(ZulipTestCase):
|
|||
check_no_sponsorship_request(result)
|
||||
check_legacy_plan_without_upgrade(result)
|
||||
|
||||
def test_extend_current_plan_end_date(self) -> None:
|
||||
remote_realm = RemoteRealm.objects.get(name="realm-name-5")
|
||||
customer = Customer.objects.get(remote_realm=remote_realm)
|
||||
plan = get_current_plan_by_customer(customer)
|
||||
assert plan is not None
|
||||
self.assertEqual(plan.status, CustomerPlan.ACTIVE)
|
||||
self.assertEqual(plan.end_date, datetime(2050, 2, 1, tzinfo=timezone.utc))
|
||||
|
||||
cordelia = self.example_user("cordelia")
|
||||
self.login_user(cordelia)
|
||||
result = self.client_post(
|
||||
"/activity/remote/support",
|
||||
{"realm_id": f"{remote_realm.id}", "end_date": "2040-01-01"},
|
||||
)
|
||||
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/remote/support",
|
||||
{"remote_realm_id": f"{remote_realm.id}", "plan_end_date": "2040-01-01"},
|
||||
)
|
||||
self.assert_in_success_response(
|
||||
["Current plan for realm-name-5 updated to end on 2040-01-01."], result
|
||||
)
|
||||
plan.refresh_from_db()
|
||||
self.assertEqual(plan.end_date, datetime(2040, 1, 1, tzinfo=timezone.utc))
|
||||
|
||||
result = self.client_post(
|
||||
"/activity/remote/support",
|
||||
{"remote_realm_id": f"{remote_realm.id}", "plan_end_date": "2020-01-01"},
|
||||
)
|
||||
self.assert_in_success_response(
|
||||
["Cannot update current plan for realm-name-5 to end on 2020-01-01."], result
|
||||
)
|
||||
|
||||
|
||||
class TestSupportEndpoint(ZulipTestCase):
|
||||
def create_customer_and_plan(self, realm: Realm, monthly: bool = False) -> Customer:
|
||||
|
|
|
@ -33,7 +33,13 @@ from zerver.lib.exceptions import JsonableError
|
|||
from zerver.lib.realm_icon import realm_icon_url
|
||||
from zerver.lib.request import REQ, has_request_variables
|
||||
from zerver.lib.subdomains import get_subdomain_from_hostname
|
||||
from zerver.lib.validator import check_bool, check_string_in, to_decimal, to_non_negative_int
|
||||
from zerver.lib.validator import (
|
||||
check_bool,
|
||||
check_date,
|
||||
check_string_in,
|
||||
to_decimal,
|
||||
to_non_negative_int,
|
||||
)
|
||||
from zerver.models import (
|
||||
MultiuseInvite,
|
||||
PreregistrationRealm,
|
||||
|
@ -416,6 +422,7 @@ def remote_servers_support(
|
|||
billing_modality: Optional[str] = REQ(
|
||||
default=None, str_validator=check_string_in(VALID_BILLING_MODALITY_VALUES)
|
||||
),
|
||||
plan_end_date: Optional[str] = REQ(default=None, str_validator=check_date),
|
||||
modify_plan: Optional[str] = REQ(
|
||||
default=None, str_validator=check_string_in(VALID_MODIFY_PLAN_METHODS)
|
||||
),
|
||||
|
@ -470,6 +477,11 @@ def remote_servers_support(
|
|||
support_type=SupportType.update_billing_modality,
|
||||
billing_modality=billing_modality,
|
||||
)
|
||||
elif plan_end_date is not None:
|
||||
support_view_request = SupportViewRequest(
|
||||
support_type=SupportType.update_plan_end_date,
|
||||
plan_end_date=plan_end_date,
|
||||
)
|
||||
elif modify_plan is not None:
|
||||
support_view_request = SupportViewRequest(
|
||||
support_type=SupportType.modify_plan,
|
||||
|
|
|
@ -4,7 +4,7 @@ import os
|
|||
import secrets
|
||||
from abc import ABC, abstractmethod
|
||||
from dataclasses import dataclass
|
||||
from datetime import datetime, timedelta
|
||||
from datetime import datetime, timedelta, timezone
|
||||
from decimal import Decimal
|
||||
from enum import Enum
|
||||
from functools import wraps
|
||||
|
@ -554,6 +554,7 @@ class SupportType(Enum):
|
|||
update_billing_modality = 4
|
||||
modify_plan = 5
|
||||
update_minimum_licenses = 6
|
||||
update_plan_end_date = 7
|
||||
|
||||
|
||||
class SupportViewRequest(TypedDict, total=False):
|
||||
|
@ -564,6 +565,7 @@ class SupportViewRequest(TypedDict, total=False):
|
|||
plan_modification: Optional[str]
|
||||
new_plan_tier: Optional[int]
|
||||
minimum_licenses: Optional[int]
|
||||
plan_end_date: Optional[str]
|
||||
|
||||
|
||||
class AuditLogEventType(Enum):
|
||||
|
@ -578,6 +580,7 @@ class AuditLogEventType(Enum):
|
|||
CUSTOMER_SWITCHED_FROM_ANNUAL_TO_MONTHLY_PLAN = 9
|
||||
BILLING_ENTITY_PLAN_TYPE_CHANGED = 10
|
||||
MINIMUM_LICENSES_CHANGED = 11
|
||||
CUSTOMER_PLAN_END_DATE_CHANGED = 12
|
||||
|
||||
|
||||
class PlanTierChangeType(Enum):
|
||||
|
@ -1166,6 +1169,31 @@ class BillingSession(ABC):
|
|||
success_message = f"Billing collection method of {self.billing_entity_display_name} updated to send invoice."
|
||||
return success_message
|
||||
|
||||
def update_end_date_of_current_plan(self, end_date_string: str) -> str:
|
||||
new_end_date = datetime.strptime(end_date_string, "%Y-%m-%d").replace(tzinfo=timezone.utc)
|
||||
if new_end_date.date() <= timezone_now().date():
|
||||
raise SupportRequestError(
|
||||
f"Cannot update current plan for {self.billing_entity_display_name} to end on {end_date_string}."
|
||||
)
|
||||
customer = self.get_customer()
|
||||
if customer is not None:
|
||||
plan = get_current_plan_by_customer(customer)
|
||||
if plan is not None:
|
||||
assert plan.end_date is not None
|
||||
assert plan.status == CustomerPlan.ACTIVE
|
||||
old_end_date = plan.end_date.strftime("%Y-%m-%d")
|
||||
plan.end_date = new_end_date
|
||||
plan.save(update_fields=["end_date"])
|
||||
self.write_to_audit_log(
|
||||
event_type=AuditLogEventType.CUSTOMER_PLAN_END_DATE_CHANGED,
|
||||
event_time=timezone_now(),
|
||||
extra_data={"old_end_date": old_end_date, "new_end_date": end_date_string},
|
||||
)
|
||||
return f"Current plan for {self.billing_entity_display_name} updated to end on {end_date_string}."
|
||||
raise SupportRequestError(
|
||||
f"No current plan for {self.billing_entity_display_name}."
|
||||
) # nocoverage
|
||||
|
||||
def setup_upgrade_payment_intent_and_charge(
|
||||
self,
|
||||
plan_tier: int,
|
||||
|
@ -2661,6 +2689,10 @@ class BillingSession(ABC):
|
|||
assert support_request["billing_modality"] in VALID_BILLING_MODALITY_VALUES
|
||||
charge_automatically = support_request["billing_modality"] == "charge_automatically"
|
||||
success_message = self.update_billing_modality_of_current_plan(charge_automatically)
|
||||
elif support_type == SupportType.update_plan_end_date:
|
||||
assert support_request["plan_end_date"] is not None
|
||||
new_plan_end_date = support_request["plan_end_date"]
|
||||
success_message = self.update_end_date_of_current_plan(new_plan_end_date)
|
||||
elif support_type == SupportType.modify_plan:
|
||||
assert support_request["plan_modification"] is not None
|
||||
plan_modification = support_request["plan_modification"]
|
||||
|
@ -2986,6 +3018,8 @@ class RealmBillingSession(BillingSession):
|
|||
return RealmAuditLog.REALM_SPONSORSHIP_PENDING_STATUS_CHANGED
|
||||
elif event_type is AuditLogEventType.BILLING_MODALITY_CHANGED:
|
||||
return RealmAuditLog.REALM_BILLING_MODALITY_CHANGED
|
||||
elif event_type is AuditLogEventType.CUSTOMER_PLAN_END_DATE_CHANGED:
|
||||
return RealmAuditLog.CUSTOMER_PLAN_END_DATE_CHANGED # nocoverage
|
||||
elif event_type is AuditLogEventType.CUSTOMER_SWITCHED_FROM_MONTHLY_TO_ANNUAL_PLAN:
|
||||
return RealmAuditLog.CUSTOMER_SWITCHED_FROM_MONTHLY_TO_ANNUAL_PLAN
|
||||
elif event_type is AuditLogEventType.CUSTOMER_SWITCHED_FROM_ANNUAL_TO_MONTHLY_PLAN:
|
||||
|
@ -3344,6 +3378,8 @@ class RemoteRealmBillingSession(BillingSession):
|
|||
return RemoteRealmAuditLog.REMOTE_SERVER_SPONSORSHIP_PENDING_STATUS_CHANGED
|
||||
elif event_type is AuditLogEventType.BILLING_MODALITY_CHANGED:
|
||||
return RemoteRealmAuditLog.REMOTE_SERVER_BILLING_MODALITY_CHANGED # nocoverage
|
||||
elif event_type is AuditLogEventType.CUSTOMER_PLAN_END_DATE_CHANGED:
|
||||
return RemoteRealmAuditLog.CUSTOMER_PLAN_END_DATE_CHANGED
|
||||
elif event_type is AuditLogEventType.BILLING_ENTITY_PLAN_TYPE_CHANGED:
|
||||
return RemoteRealmAuditLog.REMOTE_SERVER_PLAN_TYPE_CHANGED
|
||||
elif (
|
||||
|
@ -3756,6 +3792,8 @@ class RemoteServerBillingSession(BillingSession):
|
|||
return RemoteZulipServerAuditLog.REMOTE_SERVER_SPONSORSHIP_PENDING_STATUS_CHANGED
|
||||
elif event_type is AuditLogEventType.BILLING_MODALITY_CHANGED:
|
||||
return RemoteZulipServerAuditLog.REMOTE_SERVER_BILLING_MODALITY_CHANGED # nocoverage
|
||||
elif event_type is AuditLogEventType.CUSTOMER_PLAN_END_DATE_CHANGED:
|
||||
return RemoteZulipServerAuditLog.CUSTOMER_PLAN_END_DATE_CHANGED # nocoverage
|
||||
elif event_type is AuditLogEventType.BILLING_ENTITY_PLAN_TYPE_CHANGED:
|
||||
return RemoteZulipServerAuditLog.REMOTE_SERVER_PLAN_TYPE_CHANGED
|
||||
elif (
|
||||
|
|
|
@ -9,6 +9,16 @@
|
|||
<button type="submit" class="btn btn-default support-submit-button">Update</button>
|
||||
</form>
|
||||
|
||||
{% if current_plan.end_date and current_plan.status == current_plan.ACTIVE %}
|
||||
<form method="POST" class="remote-form">
|
||||
<b>Plan end date</b><br />
|
||||
{{ csrf_input }}
|
||||
<input type="hidden" name="{{ remote_type }}" value="{{ remote_id }}" />
|
||||
<input type="date" name="plan_end_date" value="{{ current_plan.end_date.strftime('%Y-%m-%d') }}" required />
|
||||
<button type="submit" class="btn btn-default support-submit-button">Update</button>
|
||||
</form>
|
||||
{% endif %}
|
||||
|
||||
<form method="POST" class="remote-form">
|
||||
<b>Modify current plan</b><br />
|
||||
{{ csrf_input }}
|
||||
|
|
|
@ -102,6 +102,7 @@ class AbstractRealmAuditLog(models.Model):
|
|||
CUSTOMER_SWITCHED_FROM_MONTHLY_TO_ANNUAL_PLAN = 503
|
||||
CUSTOMER_SWITCHED_FROM_ANNUAL_TO_MONTHLY_PLAN = 504
|
||||
CUSTOMER_MINIMUM_LICENSES_CHANGED = 505
|
||||
CUSTOMER_PLAN_END_DATE_CHANGED = 506
|
||||
|
||||
STREAM_CREATED = 601
|
||||
STREAM_DEACTIVATED = 602
|
||||
|
|
Loading…
Reference in New Issue