mirror of https://github.com/zulip/zulip.git
analytics: Create process_support_view_request BillingSession method.
Creates a process_support_view_request method for BillingSession to process the various support requests that relate to the billing system. Moves approve_realm_sponsorship, update_realm_sponsorship_status, and attach_discount_to_realm to this new BillingSession method. Adds a new abstract property to BillingSession to have a string value, billing_entity_display_name, to use for support messages sent when these requests are processed.
This commit is contained in:
parent
0679bc044a
commit
5d25cab42b
|
@ -1,4 +1,5 @@
|
||||||
from datetime import datetime, timedelta, timezone
|
from datetime import datetime, timedelta, timezone
|
||||||
|
from decimal import Decimal
|
||||||
from typing import TYPE_CHECKING, Any, Optional
|
from typing import TYPE_CHECKING, Any, Optional
|
||||||
from unittest import mock
|
from unittest import mock
|
||||||
|
|
||||||
|
@ -7,8 +8,7 @@ import time_machine
|
||||||
from django.utils.timezone import now as timezone_now
|
from django.utils.timezone import now as timezone_now
|
||||||
from typing_extensions import override
|
from typing_extensions import override
|
||||||
|
|
||||||
from corporate.lib.stripe import add_months
|
from corporate.lib.stripe import RealmBillingSession, add_months
|
||||||
from corporate.lib.support import update_realm_sponsorship_status
|
|
||||||
from corporate.models import Customer, CustomerPlan, LicenseLedger, get_customer_by_realm
|
from corporate.models import Customer, CustomerPlan, LicenseLedger, get_customer_by_realm
|
||||||
from zerver.actions.invites import do_create_multiuse_invite_link
|
from zerver.actions.invites import do_create_multiuse_invite_link
|
||||||
from zerver.actions.realm_settings import do_change_realm_org_type, do_send_realm_reactivation_email
|
from zerver.actions.realm_settings import do_change_realm_org_type, do_send_realm_reactivation_email
|
||||||
|
@ -533,14 +533,15 @@ class TestSupportEndpoint(ZulipTestCase):
|
||||||
self.assertEqual(result["Location"], "/login/")
|
self.assertEqual(result["Location"], "/login/")
|
||||||
|
|
||||||
iago = self.example_user("iago")
|
iago = self.example_user("iago")
|
||||||
self.login("iago")
|
self.login_user(iago)
|
||||||
|
|
||||||
with mock.patch("analytics.views.support.attach_discount_to_realm") as m:
|
|
||||||
result = self.client_post(
|
result = self.client_post(
|
||||||
"/activity/support", {"realm_id": f"{lear_realm.id}", "discount": "25"}
|
"/activity/support", {"realm_id": f"{lear_realm.id}", "discount": "25"}
|
||||||
)
|
)
|
||||||
m.assert_called_once_with(get_realm("lear"), 25, acting_user=iago)
|
self.assert_in_success_response(["Discount for lear changed to 25% from 0%"], result)
|
||||||
self.assert_in_success_response(["Discount of lear changed to 25% from 0%"], result)
|
customer = get_customer_by_realm(lear_realm)
|
||||||
|
assert customer is not None
|
||||||
|
self.assertEqual(customer.default_discount, Decimal(25))
|
||||||
|
|
||||||
def test_change_sponsorship_status(self) -> None:
|
def test_change_sponsorship_status(self) -> None:
|
||||||
lear_realm = get_realm("lear")
|
lear_realm = get_realm("lear")
|
||||||
|
@ -577,7 +578,10 @@ class TestSupportEndpoint(ZulipTestCase):
|
||||||
def test_approve_sponsorship(self) -> None:
|
def test_approve_sponsorship(self) -> None:
|
||||||
support_admin = self.example_user("iago")
|
support_admin = self.example_user("iago")
|
||||||
lear_realm = get_realm("lear")
|
lear_realm = get_realm("lear")
|
||||||
update_realm_sponsorship_status(lear_realm, True, acting_user=support_admin)
|
billing_session = RealmBillingSession(
|
||||||
|
user=support_admin, realm=lear_realm, support_session=True
|
||||||
|
)
|
||||||
|
billing_session.update_customer_sponsorship_status(True)
|
||||||
king_user = self.lear_user("king")
|
king_user = self.lear_user("king")
|
||||||
king_user.role = UserProfile.ROLE_REALM_OWNER
|
king_user.role = UserProfile.ROLE_REALM_OWNER
|
||||||
king_user.save()
|
king_user.save()
|
||||||
|
|
|
@ -55,16 +55,15 @@ if settings.ZILENCER_ENABLED:
|
||||||
if settings.BILLING_ENABLED:
|
if settings.BILLING_ENABLED:
|
||||||
from corporate.lib.stripe import (
|
from corporate.lib.stripe import (
|
||||||
RealmBillingSession,
|
RealmBillingSession,
|
||||||
|
SupportType,
|
||||||
|
SupportViewRequest,
|
||||||
get_latest_seat_count,
|
get_latest_seat_count,
|
||||||
void_all_open_invoices,
|
void_all_open_invoices,
|
||||||
)
|
)
|
||||||
from corporate.lib.support import (
|
from corporate.lib.support import (
|
||||||
approve_realm_sponsorship,
|
|
||||||
attach_discount_to_realm,
|
|
||||||
get_discount_for_realm,
|
get_discount_for_realm,
|
||||||
switch_realm_from_standard_to_plus_plan,
|
switch_realm_from_standard_to_plus_plan,
|
||||||
update_realm_billing_modality,
|
update_realm_billing_modality,
|
||||||
update_realm_sponsorship_status,
|
|
||||||
)
|
)
|
||||||
from corporate.models import (
|
from corporate.models import (
|
||||||
Customer,
|
Customer,
|
||||||
|
@ -197,7 +196,21 @@ def support(
|
||||||
assert realm_id is not None
|
assert realm_id is not None
|
||||||
realm = Realm.objects.get(id=realm_id)
|
realm = Realm.objects.get(id=realm_id)
|
||||||
|
|
||||||
if plan_type is not None:
|
support_view_request = None
|
||||||
|
|
||||||
|
if approve_sponsorship:
|
||||||
|
support_view_request = SupportViewRequest(support_type=SupportType.approve_sponsorship)
|
||||||
|
elif sponsorship_pending is not None:
|
||||||
|
support_view_request = SupportViewRequest(
|
||||||
|
support_type=SupportType.update_sponsorship_status,
|
||||||
|
sponsorship_status=sponsorship_pending,
|
||||||
|
)
|
||||||
|
elif discount is not None:
|
||||||
|
support_view_request = SupportViewRequest(
|
||||||
|
support_type=SupportType.attach_discount,
|
||||||
|
discount=discount,
|
||||||
|
)
|
||||||
|
elif plan_type is not None:
|
||||||
current_plan_type = realm.plan_type
|
current_plan_type = realm.plan_type
|
||||||
do_change_realm_plan_type(realm, plan_type, acting_user=acting_user)
|
do_change_realm_plan_type(realm, plan_type, acting_user=acting_user)
|
||||||
msg = f"Plan type of {realm.string_id} changed from {get_plan_name(current_plan_type)} to {get_plan_name(plan_type)} "
|
msg = f"Plan type of {realm.string_id} changed from {get_plan_name(current_plan_type)} to {get_plan_name(plan_type)} "
|
||||||
|
@ -207,12 +220,6 @@ def support(
|
||||||
do_change_realm_org_type(realm, org_type, acting_user=acting_user)
|
do_change_realm_org_type(realm, org_type, acting_user=acting_user)
|
||||||
msg = f"Org type of {realm.string_id} changed from {get_org_type_display_name(current_realm_type)} to {get_org_type_display_name(org_type)} "
|
msg = f"Org type of {realm.string_id} changed from {get_org_type_display_name(current_realm_type)} to {get_org_type_display_name(org_type)} "
|
||||||
context["success_message"] = msg
|
context["success_message"] = msg
|
||||||
elif discount is not None:
|
|
||||||
current_discount = get_discount_for_realm(realm) or 0
|
|
||||||
attach_discount_to_realm(realm, discount, acting_user=acting_user)
|
|
||||||
context[
|
|
||||||
"success_message"
|
|
||||||
] = f"Discount of {realm.string_id} changed to {discount}% from {current_discount}%."
|
|
||||||
elif new_subdomain is not None:
|
elif new_subdomain is not None:
|
||||||
old_subdomain = realm.string_id
|
old_subdomain = realm.string_id
|
||||||
try:
|
try:
|
||||||
|
@ -251,16 +258,6 @@ def support(
|
||||||
context[
|
context[
|
||||||
"success_message"
|
"success_message"
|
||||||
] = f"Billing collection method of {realm.string_id} updated to charge automatically."
|
] = f"Billing collection method of {realm.string_id} updated to charge automatically."
|
||||||
elif sponsorship_pending is not None:
|
|
||||||
if sponsorship_pending:
|
|
||||||
update_realm_sponsorship_status(realm, True, acting_user=acting_user)
|
|
||||||
context["success_message"] = f"{realm.string_id} marked as pending sponsorship."
|
|
||||||
else:
|
|
||||||
update_realm_sponsorship_status(realm, False, acting_user=acting_user)
|
|
||||||
context["success_message"] = f"{realm.string_id} is no longer pending sponsorship."
|
|
||||||
elif approve_sponsorship:
|
|
||||||
approve_realm_sponsorship(realm, acting_user=acting_user)
|
|
||||||
context["success_message"] = f"Sponsorship approved for {realm.string_id}"
|
|
||||||
elif modify_plan is not None:
|
elif modify_plan is not None:
|
||||||
billing_session = RealmBillingSession(
|
billing_session = RealmBillingSession(
|
||||||
user=acting_user, realm=realm, support_session=True
|
user=acting_user, realm=realm, support_session=True
|
||||||
|
@ -294,6 +291,13 @@ def support(
|
||||||
do_delete_user_preserving_messages(user_profile_for_deletion)
|
do_delete_user_preserving_messages(user_profile_for_deletion)
|
||||||
context["success_message"] = f"{user_email} in {realm.subdomain} deleted."
|
context["success_message"] = f"{user_email} in {realm.subdomain} deleted."
|
||||||
|
|
||||||
|
if support_view_request is not None:
|
||||||
|
billing_session = RealmBillingSession(
|
||||||
|
user=acting_user, realm=realm, support_session=True
|
||||||
|
)
|
||||||
|
success_message = billing_session.process_support_view_request(support_view_request)
|
||||||
|
context["success_message"] = success_message
|
||||||
|
|
||||||
if query:
|
if query:
|
||||||
key_words = get_invitee_emails_set(query)
|
key_words = get_invitee_emails_set(query)
|
||||||
|
|
||||||
|
|
|
@ -522,6 +522,18 @@ class EventStatusRequest:
|
||||||
stripe_payment_intent_id: Optional[str]
|
stripe_payment_intent_id: Optional[str]
|
||||||
|
|
||||||
|
|
||||||
|
class SupportType(Enum):
|
||||||
|
approve_sponsorship = 1
|
||||||
|
update_sponsorship_status = 2
|
||||||
|
attach_discount = 3
|
||||||
|
|
||||||
|
|
||||||
|
class SupportViewRequest(TypedDict, total=False):
|
||||||
|
support_type: SupportType
|
||||||
|
sponsorship_status: Optional[bool]
|
||||||
|
discount: Optional[Decimal]
|
||||||
|
|
||||||
|
|
||||||
class AuditLogEventType(Enum):
|
class AuditLogEventType(Enum):
|
||||||
STRIPE_CUSTOMER_CREATED = 1
|
STRIPE_CUSTOMER_CREATED = 1
|
||||||
STRIPE_CARD_CHANGED = 2
|
STRIPE_CARD_CHANGED = 2
|
||||||
|
@ -605,6 +617,11 @@ class SponsorshipRequestForm(forms.Form):
|
||||||
|
|
||||||
|
|
||||||
class BillingSession(ABC):
|
class BillingSession(ABC):
|
||||||
|
@property
|
||||||
|
@abstractmethod
|
||||||
|
def billing_entity_display_name(self) -> str:
|
||||||
|
pass
|
||||||
|
|
||||||
@property
|
@property
|
||||||
@abstractmethod
|
@abstractmethod
|
||||||
def billing_session_url(self) -> str:
|
def billing_session_url(self) -> str:
|
||||||
|
@ -672,7 +689,7 @@ class BillingSession(ABC):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
@abstractmethod
|
@abstractmethod
|
||||||
def approve_sponsorship(self) -> None:
|
def approve_sponsorship(self) -> str:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
@abstractmethod
|
@abstractmethod
|
||||||
|
@ -868,29 +885,32 @@ class BillingSession(ABC):
|
||||||
)
|
)
|
||||||
return stripe_session
|
return stripe_session
|
||||||
|
|
||||||
def attach_discount_to_customer(self, discount: Decimal) -> None:
|
def attach_discount_to_customer(self, new_discount: Decimal) -> str:
|
||||||
customer = self.get_customer()
|
customer = self.get_customer()
|
||||||
old_discount: Optional[Decimal] = None
|
old_discount = None
|
||||||
if customer is not None:
|
if customer is not None:
|
||||||
old_discount = customer.default_discount
|
old_discount = customer.default_discount
|
||||||
customer.default_discount = discount
|
customer.default_discount = new_discount
|
||||||
customer.save(update_fields=["default_discount"])
|
customer.save(update_fields=["default_discount"])
|
||||||
else:
|
else:
|
||||||
customer = self.update_or_create_customer(defaults={"default_discount": discount})
|
customer = self.update_or_create_customer(defaults={"default_discount": new_discount})
|
||||||
plan = get_current_plan_by_customer(customer)
|
plan = get_current_plan_by_customer(customer)
|
||||||
if plan is not None:
|
if plan is not None:
|
||||||
plan.price_per_license = get_price_per_license(
|
plan.price_per_license = get_price_per_license(
|
||||||
plan.tier, plan.billing_schedule, discount
|
plan.tier, plan.billing_schedule, new_discount
|
||||||
)
|
)
|
||||||
plan.discount = discount
|
plan.discount = new_discount
|
||||||
plan.save(update_fields=["price_per_license", "discount"])
|
plan.save(update_fields=["price_per_license", "discount"])
|
||||||
self.write_to_audit_log(
|
self.write_to_audit_log(
|
||||||
event_type=AuditLogEventType.DISCOUNT_CHANGED,
|
event_type=AuditLogEventType.DISCOUNT_CHANGED,
|
||||||
event_time=timezone_now(),
|
event_time=timezone_now(),
|
||||||
extra_data={"old_discount": old_discount, "new_discount": discount},
|
extra_data={"old_discount": old_discount, "new_discount": new_discount},
|
||||||
)
|
)
|
||||||
|
if old_discount is None:
|
||||||
|
old_discount = Decimal(0)
|
||||||
|
return f"Discount for {self.billing_entity_display_name} changed to {new_discount}% from {old_discount}%."
|
||||||
|
|
||||||
def update_customer_sponsorship_status(self, sponsorship_pending: bool) -> None:
|
def update_customer_sponsorship_status(self, sponsorship_pending: bool) -> str:
|
||||||
customer = self.get_customer()
|
customer = self.get_customer()
|
||||||
if customer is None:
|
if customer is None:
|
||||||
customer = self.update_or_create_customer()
|
customer = self.update_or_create_customer()
|
||||||
|
@ -902,6 +922,14 @@ class BillingSession(ABC):
|
||||||
extra_data={"sponsorship_pending": sponsorship_pending},
|
extra_data={"sponsorship_pending": sponsorship_pending},
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if sponsorship_pending:
|
||||||
|
success_message = f"{self.billing_entity_display_name} marked as pending sponsorship."
|
||||||
|
else:
|
||||||
|
success_message = (
|
||||||
|
f"{self.billing_entity_display_name} is no longer pending sponsorship."
|
||||||
|
)
|
||||||
|
return success_message
|
||||||
|
|
||||||
def update_billing_modality_of_current_plan(self, charge_automatically: bool) -> None:
|
def update_billing_modality_of_current_plan(self, charge_automatically: bool) -> None:
|
||||||
customer = self.get_customer()
|
customer = self.get_customer()
|
||||||
if customer is not None:
|
if customer is not None:
|
||||||
|
@ -1981,6 +2009,23 @@ class BillingSession(ABC):
|
||||||
context=context,
|
context=context,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def process_support_view_request(self, support_request: SupportViewRequest) -> str:
|
||||||
|
support_type = support_request["support_type"]
|
||||||
|
success_message = ""
|
||||||
|
|
||||||
|
if support_type == SupportType.approve_sponsorship:
|
||||||
|
success_message = self.approve_sponsorship()
|
||||||
|
elif support_type == SupportType.update_sponsorship_status:
|
||||||
|
assert support_request["sponsorship_status"] is not None
|
||||||
|
sponsorship_status = support_request["sponsorship_status"]
|
||||||
|
success_message = self.update_customer_sponsorship_status(sponsorship_status)
|
||||||
|
elif support_type == SupportType.attach_discount:
|
||||||
|
assert support_request["discount"] is not None
|
||||||
|
new_discount = support_request["discount"]
|
||||||
|
success_message = self.attach_discount_to_customer(new_discount)
|
||||||
|
|
||||||
|
return success_message
|
||||||
|
|
||||||
|
|
||||||
class RealmBillingSession(BillingSession):
|
class RealmBillingSession(BillingSession):
|
||||||
def __init__(
|
def __init__(
|
||||||
|
@ -2010,6 +2055,11 @@ class RealmBillingSession(BillingSession):
|
||||||
Realm.PLAN_TYPE_PLUS,
|
Realm.PLAN_TYPE_PLUS,
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@override
|
||||||
|
@property
|
||||||
|
def billing_entity_display_name(self) -> str:
|
||||||
|
return self.realm.string_id
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@property
|
@property
|
||||||
def billing_session_url(self) -> str:
|
def billing_session_url(self) -> str:
|
||||||
|
@ -2179,7 +2229,7 @@ class RealmBillingSession(BillingSession):
|
||||||
plan.save(update_fields=["status"])
|
plan.save(update_fields=["status"])
|
||||||
|
|
||||||
@override
|
@override
|
||||||
def approve_sponsorship(self) -> None:
|
def approve_sponsorship(self) -> str:
|
||||||
# Sponsorship approval is only a support admin action.
|
# Sponsorship approval is only a support admin action.
|
||||||
assert self.support_session
|
assert self.support_session
|
||||||
|
|
||||||
|
@ -2210,6 +2260,7 @@ class RealmBillingSession(BillingSession):
|
||||||
end_link="](/help/linking-to-zulip-website)",
|
end_link="](/help/linking-to-zulip-website)",
|
||||||
)
|
)
|
||||||
internal_send_private_message(notification_bot, user, message)
|
internal_send_private_message(notification_bot, user, message)
|
||||||
|
return f"Sponsorship approved for {self.billing_entity_display_name}"
|
||||||
|
|
||||||
@override
|
@override
|
||||||
def is_sponsored(self) -> bool:
|
def is_sponsored(self) -> bool:
|
||||||
|
@ -2322,6 +2373,11 @@ class RemoteRealmBillingSession(BillingSession): # nocoverage
|
||||||
else:
|
else:
|
||||||
self.support_session = False
|
self.support_session = False
|
||||||
|
|
||||||
|
@override
|
||||||
|
@property
|
||||||
|
def billing_entity_display_name(self) -> str:
|
||||||
|
return self.remote_realm.name
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@property
|
@property
|
||||||
def billing_session_url(self) -> str:
|
def billing_session_url(self) -> str:
|
||||||
|
@ -2476,9 +2532,9 @@ class RemoteRealmBillingSession(BillingSession): # nocoverage
|
||||||
self.remote_realm.save(update_fields=["plan_type"])
|
self.remote_realm.save(update_fields=["plan_type"])
|
||||||
|
|
||||||
@override
|
@override
|
||||||
def approve_sponsorship(self) -> None:
|
def approve_sponsorship(self) -> str:
|
||||||
# TBD
|
# TBD
|
||||||
pass
|
return ""
|
||||||
|
|
||||||
@override
|
@override
|
||||||
def is_sponsored(self) -> bool:
|
def is_sponsored(self) -> bool:
|
||||||
|
@ -2595,6 +2651,11 @@ class RemoteServerBillingSession(BillingSession): # nocoverage
|
||||||
else:
|
else:
|
||||||
self.support_session = False
|
self.support_session = False
|
||||||
|
|
||||||
|
@override
|
||||||
|
@property
|
||||||
|
def billing_entity_display_name(self) -> str:
|
||||||
|
return self.remote_server.hostname
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@property
|
@property
|
||||||
def billing_session_url(self) -> str:
|
def billing_session_url(self) -> str:
|
||||||
|
@ -2739,9 +2800,9 @@ class RemoteServerBillingSession(BillingSession): # nocoverage
|
||||||
self.remote_server.save(update_fields=["plan_type"])
|
self.remote_server.save(update_fields=["plan_type"])
|
||||||
|
|
||||||
@override
|
@override
|
||||||
def approve_sponsorship(self) -> None:
|
def approve_sponsorship(self) -> str:
|
||||||
# TBD
|
# TBD
|
||||||
pass
|
return ""
|
||||||
|
|
||||||
@override
|
@override
|
||||||
def process_downgrade(self, plan: CustomerPlan) -> None:
|
def process_downgrade(self, plan: CustomerPlan) -> None:
|
||||||
|
|
|
@ -26,23 +26,6 @@ def get_discount_for_realm(realm: Realm) -> Optional[Decimal]:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
def attach_discount_to_realm(realm: Realm, discount: Decimal, *, acting_user: UserProfile) -> None:
|
|
||||||
billing_session = RealmBillingSession(acting_user, realm, support_session=True)
|
|
||||||
billing_session.attach_discount_to_customer(discount)
|
|
||||||
|
|
||||||
|
|
||||||
def approve_realm_sponsorship(realm: Realm, *, acting_user: UserProfile) -> None:
|
|
||||||
billing_session = RealmBillingSession(acting_user, realm, support_session=True)
|
|
||||||
billing_session.approve_sponsorship()
|
|
||||||
|
|
||||||
|
|
||||||
def update_realm_sponsorship_status(
|
|
||||||
realm: Realm, sponsorship_pending: bool, *, acting_user: UserProfile
|
|
||||||
) -> None:
|
|
||||||
billing_session = RealmBillingSession(acting_user, realm, support_session=True)
|
|
||||||
billing_session.update_customer_sponsorship_status(sponsorship_pending)
|
|
||||||
|
|
||||||
|
|
||||||
def update_realm_billing_modality(
|
def update_realm_billing_modality(
|
||||||
realm: Realm, charge_automatically: bool, *, acting_user: UserProfile
|
realm: Realm, charge_automatically: bool, *, acting_user: UserProfile
|
||||||
) -> None:
|
) -> None:
|
||||||
|
|
|
@ -75,12 +75,9 @@ from corporate.lib.stripe import (
|
||||||
void_all_open_invoices,
|
void_all_open_invoices,
|
||||||
)
|
)
|
||||||
from corporate.lib.support import (
|
from corporate.lib.support import (
|
||||||
approve_realm_sponsorship,
|
|
||||||
attach_discount_to_realm,
|
|
||||||
get_discount_for_realm,
|
get_discount_for_realm,
|
||||||
switch_realm_from_standard_to_plus_plan,
|
switch_realm_from_standard_to_plus_plan,
|
||||||
update_realm_billing_modality,
|
update_realm_billing_modality,
|
||||||
update_realm_sponsorship_status,
|
|
||||||
)
|
)
|
||||||
from corporate.models import (
|
from corporate.models import (
|
||||||
Customer,
|
Customer,
|
||||||
|
@ -3756,7 +3753,10 @@ class StripeTest(StripeTestCase):
|
||||||
users_to_create=1, create_stripe_customer=False, create_plan=False
|
users_to_create=1, create_stripe_customer=False, create_plan=False
|
||||||
)
|
)
|
||||||
# To create local Customer object but no Stripe customer.
|
# To create local Customer object but no Stripe customer.
|
||||||
attach_discount_to_realm(realm, Decimal(20), acting_user=self.example_user("iago"))
|
billing_session = RealmBillingSession(
|
||||||
|
user=self.example_user("iago"), realm=realm, support_session=True
|
||||||
|
)
|
||||||
|
billing_session.attach_discount_to_customer(Decimal(20))
|
||||||
rows.append(Row(realm, Realm.PLAN_TYPE_SELF_HOSTED, None, None, False, False))
|
rows.append(Row(realm, Realm.PLAN_TYPE_SELF_HOSTED, None, None, False, False))
|
||||||
|
|
||||||
realm, _, _ = create_realm(
|
realm, _, _ = create_realm(
|
||||||
|
@ -5108,11 +5108,12 @@ class TestRemoteServerBillingSession(StripeTestCase):
|
||||||
|
|
||||||
class TestSupportBillingHelpers(StripeTestCase):
|
class TestSupportBillingHelpers(StripeTestCase):
|
||||||
def test_get_discount_for_realm(self) -> None:
|
def test_get_discount_for_realm(self) -> None:
|
||||||
iago = self.example_user("iago")
|
support_admin = self.example_user("iago")
|
||||||
user = self.example_user("hamlet")
|
user = self.example_user("hamlet")
|
||||||
self.assertEqual(get_discount_for_realm(user.realm), None)
|
self.assertEqual(get_discount_for_realm(user.realm), None)
|
||||||
|
|
||||||
attach_discount_to_realm(user.realm, Decimal(85), acting_user=iago)
|
billing_session = RealmBillingSession(support_admin, realm=user.realm, support_session=True)
|
||||||
|
billing_session.attach_discount_to_customer(Decimal(85))
|
||||||
self.assertEqual(get_discount_for_realm(user.realm), 85)
|
self.assertEqual(get_discount_for_realm(user.realm), 85)
|
||||||
|
|
||||||
@mock_stripe()
|
@mock_stripe()
|
||||||
|
@ -5120,7 +5121,8 @@ class TestSupportBillingHelpers(StripeTestCase):
|
||||||
# Attach discount before Stripe customer exists
|
# Attach discount before Stripe customer exists
|
||||||
support_admin = self.example_user("iago")
|
support_admin = self.example_user("iago")
|
||||||
user = self.example_user("hamlet")
|
user = self.example_user("hamlet")
|
||||||
attach_discount_to_realm(user.realm, Decimal(85), acting_user=support_admin)
|
billing_session = RealmBillingSession(support_admin, realm=user.realm, support_session=True)
|
||||||
|
billing_session.attach_discount_to_customer(Decimal(85))
|
||||||
realm_audit_log = RealmAuditLog.objects.filter(
|
realm_audit_log = RealmAuditLog.objects.filter(
|
||||||
event_type=RealmAuditLog.REALM_DISCOUNT_CHANGED
|
event_type=RealmAuditLog.REALM_DISCOUNT_CHANGED
|
||||||
).last()
|
).last()
|
||||||
|
@ -5149,7 +5151,8 @@ class TestSupportBillingHelpers(StripeTestCase):
|
||||||
# Attach discount to existing Stripe customer
|
# Attach discount to existing Stripe customer
|
||||||
plan.status = CustomerPlan.ENDED
|
plan.status = CustomerPlan.ENDED
|
||||||
plan.save(update_fields=["status"])
|
plan.save(update_fields=["status"])
|
||||||
attach_discount_to_realm(user.realm, Decimal(25), acting_user=support_admin)
|
billing_session = RealmBillingSession(support_admin, realm=user.realm, support_session=True)
|
||||||
|
billing_session.attach_discount_to_customer(Decimal(25))
|
||||||
with time_machine.travel(self.now, tick=False):
|
with time_machine.travel(self.now, tick=False):
|
||||||
self.add_card_and_upgrade(
|
self.add_card_and_upgrade(
|
||||||
user, license_management="automatic", billing_modality="charge_automatically"
|
user, license_management="automatic", billing_modality="charge_automatically"
|
||||||
|
@ -5165,7 +5168,8 @@ class TestSupportBillingHelpers(StripeTestCase):
|
||||||
)
|
)
|
||||||
plan = CustomerPlan.objects.get(price_per_license=6000, discount=Decimal(25))
|
plan = CustomerPlan.objects.get(price_per_license=6000, discount=Decimal(25))
|
||||||
|
|
||||||
attach_discount_to_realm(user.realm, Decimal(50), acting_user=support_admin)
|
billing_session = RealmBillingSession(support_admin, realm=user.realm, support_session=True)
|
||||||
|
billing_session.attach_discount_to_customer(Decimal(50))
|
||||||
plan.refresh_from_db()
|
plan.refresh_from_db()
|
||||||
self.assertEqual(plan.price_per_license, 4000)
|
self.assertEqual(plan.price_per_license, 4000)
|
||||||
self.assertEqual(plan.discount, 50)
|
self.assertEqual(plan.discount, 50)
|
||||||
|
@ -5192,7 +5196,8 @@ class TestSupportBillingHelpers(StripeTestCase):
|
||||||
self.assertNotEqual(realm.plan_type, Realm.PLAN_TYPE_STANDARD_FREE)
|
self.assertNotEqual(realm.plan_type, Realm.PLAN_TYPE_STANDARD_FREE)
|
||||||
|
|
||||||
support_admin = self.example_user("iago")
|
support_admin = self.example_user("iago")
|
||||||
approve_realm_sponsorship(realm, acting_user=support_admin)
|
billing_session = RealmBillingSession(user=support_admin, realm=realm, support_session=True)
|
||||||
|
billing_session.approve_sponsorship()
|
||||||
self.assertEqual(realm.plan_type, Realm.PLAN_TYPE_STANDARD_FREE)
|
self.assertEqual(realm.plan_type, Realm.PLAN_TYPE_STANDARD_FREE)
|
||||||
|
|
||||||
expected_message = (
|
expected_message = (
|
||||||
|
@ -5222,7 +5227,8 @@ class TestSupportBillingHelpers(StripeTestCase):
|
||||||
def test_update_realm_sponsorship_status(self) -> None:
|
def test_update_realm_sponsorship_status(self) -> None:
|
||||||
lear = get_realm("lear")
|
lear = get_realm("lear")
|
||||||
iago = self.example_user("iago")
|
iago = self.example_user("iago")
|
||||||
update_realm_sponsorship_status(lear, True, acting_user=iago)
|
billing_session = RealmBillingSession(user=iago, realm=lear, support_session=True)
|
||||||
|
billing_session.update_customer_sponsorship_status(True)
|
||||||
customer = get_customer_by_realm(realm=lear)
|
customer = get_customer_by_realm(realm=lear)
|
||||||
assert customer is not None
|
assert customer is not None
|
||||||
self.assertTrue(customer.sponsorship_pending)
|
self.assertTrue(customer.sponsorship_pending)
|
||||||
|
|
Loading…
Reference in New Issue