mirror of https://github.com/zulip/zulip.git
support: Add functionality to approve sponsorship requests.
This should make it much easier to process these requests.
This commit is contained in:
parent
d3834f8b9a
commit
5b0b1efb15
|
@ -10,13 +10,21 @@ from analytics.lib.counts import COUNT_STATS, CountStat
|
|||
from analytics.lib.time_utils import time_range
|
||||
from analytics.models import FillState, RealmCount, UserCount, last_successful_fill
|
||||
from analytics.views import rewrite_client_arrays, sort_by_totals, sort_client_labels
|
||||
from corporate.lib.stripe import add_months
|
||||
from corporate.lib.stripe import add_months, update_sponsorship_status
|
||||
from corporate.models import Customer, CustomerPlan, LicenseLedger, get_customer_by_realm
|
||||
from zerver.lib.actions import do_create_multiuse_invite_link, do_send_realm_reactivation_email
|
||||
from zerver.lib.test_classes import ZulipTestCase
|
||||
from zerver.lib.test_helpers import reset_emails_in_zulip_realm
|
||||
from zerver.lib.timestamp import ceiling_to_day, ceiling_to_hour, datetime_to_timestamp
|
||||
from zerver.models import Client, MultiuseInvite, PreregistrationUser, get_realm
|
||||
from zerver.models import (
|
||||
Client,
|
||||
MultiuseInvite,
|
||||
PreregistrationUser,
|
||||
Realm,
|
||||
UserMessage,
|
||||
UserProfile,
|
||||
get_realm,
|
||||
)
|
||||
|
||||
|
||||
class TestStatsEndpoint(ZulipTestCase):
|
||||
|
@ -622,6 +630,36 @@ class TestSupportEndpoint(ZulipTestCase):
|
|||
assert(customer is not None)
|
||||
self.assertFalse(customer.sponsorship_pending)
|
||||
|
||||
def test_approve_sponsorship(self) -> None:
|
||||
lear_realm = get_realm("lear")
|
||||
update_sponsorship_status(lear_realm, True)
|
||||
king_user = self.lear_user("king")
|
||||
king_user.role = UserProfile.ROLE_REALM_OWNER
|
||||
king_user.save()
|
||||
|
||||
cordelia = self.example_user('cordelia')
|
||||
self.login_user(cordelia)
|
||||
|
||||
result = self.client_post("/activity/support", {"realm_id": f"{lear_realm.id}",
|
||||
"approve_sponsorship": "approve_sponsorship"})
|
||||
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"{lear_realm.id}",
|
||||
"approve_sponsorship": "approve_sponsorship"})
|
||||
self.assert_in_success_response(["Sponsorship approved for Lear & Co."], result)
|
||||
lear_realm.refresh_from_db()
|
||||
self.assertEqual(lear_realm.plan_type, Realm.STANDARD_FREE)
|
||||
customer = get_customer_by_realm(lear_realm)
|
||||
assert(customer is not None)
|
||||
self.assertFalse(customer.sponsorship_pending)
|
||||
messages = UserMessage.objects.filter(user_profile=king_user)
|
||||
self.assertIn("request for sponsored hosting has been approved", messages[0].message.content)
|
||||
self.assertEqual(len(messages), 1)
|
||||
|
||||
def test_activate_or_deactivate_realm(self) -> None:
|
||||
cordelia = self.example_user('cordelia')
|
||||
lear_realm = get_realm('lear')
|
||||
|
|
|
@ -71,6 +71,7 @@ from zerver.views.invite import get_invitee_emails_set
|
|||
|
||||
if settings.BILLING_ENABLED:
|
||||
from corporate.lib.stripe import (
|
||||
approve_sponsorship,
|
||||
attach_discount_to_realm,
|
||||
get_current_plan_by_realm,
|
||||
get_customer_by_realm,
|
||||
|
@ -1157,6 +1158,10 @@ def support(request: HttpRequest) -> HttpResponse:
|
|||
elif sponsorship_pending == "false":
|
||||
update_sponsorship_status(realm, False)
|
||||
context["message"] = f"{realm.name} is no longer pending sponsorship."
|
||||
elif request.POST.get('approve_sponsorship') is not None:
|
||||
if request.POST.get('approve_sponsorship') == "approve_sponsorship":
|
||||
approve_sponsorship(realm)
|
||||
context["message"] = f"Sponsorship approved for {realm.name}"
|
||||
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)
|
||||
|
|
|
@ -12,6 +12,7 @@ from django.conf import settings
|
|||
from django.core.signing import Signer
|
||||
from django.db import transaction
|
||||
from django.utils.timezone import now as timezone_now
|
||||
from django.utils.translation import override as override_language
|
||||
from django.utils.translation import ugettext as _
|
||||
|
||||
from corporate.models import (
|
||||
|
@ -25,7 +26,7 @@ from corporate.models import (
|
|||
from zerver.lib.logging_util import log_to_file
|
||||
from zerver.lib.timestamp import datetime_to_timestamp, timestamp_to_datetime
|
||||
from zerver.lib.utils import generate_random_token
|
||||
from zerver.models import Realm, RealmAuditLog, UserProfile
|
||||
from zerver.models import Realm, RealmAuditLog, UserProfile, get_system_bot
|
||||
from zproject.config import get_secret
|
||||
|
||||
STRIPE_PUBLISHABLE_KEY = get_secret('stripe_publishable_key')
|
||||
|
@ -563,6 +564,24 @@ def update_sponsorship_status(realm: Realm, sponsorship_pending: bool) -> None:
|
|||
customer.sponsorship_pending = sponsorship_pending
|
||||
customer.save(update_fields=["sponsorship_pending"])
|
||||
|
||||
def approve_sponsorship(realm: Realm) -> None:
|
||||
from zerver.lib.actions import do_change_plan_type, internal_send_private_message
|
||||
do_change_plan_type(realm, Realm.STANDARD_FREE)
|
||||
customer = get_customer_by_realm(realm)
|
||||
if customer is not None and customer.sponsorship_pending:
|
||||
customer.sponsorship_pending = False
|
||||
customer.save(update_fields=["sponsorship_pending"])
|
||||
notification_bot = get_system_bot(settings.NOTIFICATION_BOT)
|
||||
for billing_admin in realm.get_human_billing_admin_users():
|
||||
with override_language(billing_admin.default_language):
|
||||
# Using variable to make life easier for translators if these details change.
|
||||
plan_name = "Zulip Cloud Standard"
|
||||
emoji = ":tada:"
|
||||
message = _(
|
||||
f"Your organization's request for sponsored hosting has been approved! {emoji}.\n"
|
||||
f"You have been upgraded to {plan_name}, free of charge.")
|
||||
internal_send_private_message(billing_admin.realm, notification_bot, billing_admin, message)
|
||||
|
||||
def get_discount_for_realm(realm: Realm) -> Optional[Decimal]:
|
||||
customer = get_customer_by_realm(realm)
|
||||
if customer is not None:
|
||||
|
|
|
@ -89,6 +89,11 @@ tr.admin td:first-child {
|
|||
top: -50px;
|
||||
}
|
||||
|
||||
.approve-sponsorship-form {
|
||||
position: relative;
|
||||
top: -40px;
|
||||
}
|
||||
|
||||
.scrub-realm-form {
|
||||
position: relative;
|
||||
top: -40px;
|
||||
|
|
|
@ -40,6 +40,19 @@
|
|||
</select>
|
||||
<button type="submit" class="button rounded small support-submit-button">Update</button>
|
||||
</form>
|
||||
|
||||
{% if realm.customer and realm.customer.sponsorship_pending %}
|
||||
<form method="POST" class="approve-sponsorship-form">
|
||||
{{ csrf_input }}
|
||||
<input type="hidden" name="realm_id" value="{{ realm.id }}" />
|
||||
<input type="hidden" name="approve_sponsorship" value="approve_sponsorship" />
|
||||
<button class="button rounded btn sea-green small approve-sponsorship-button">
|
||||
Approve full sponsorship
|
||||
</button>
|
||||
(will email organization owners).
|
||||
</form>
|
||||
{% endif %}
|
||||
|
||||
<form method="POST" class="support-discount-form">
|
||||
<b>Discount (use 85 for nonprofits)</b>:<br>
|
||||
{{ csrf_input }}
|
||||
|
|
|
@ -501,6 +501,10 @@ class Realm(models.Model):
|
|||
role__in=[UserProfile.ROLE_REALM_ADMINISTRATOR,
|
||||
UserProfile.ROLE_REALM_OWNER])
|
||||
|
||||
def get_human_billing_admin_users(self) -> Sequence['UserProfile']:
|
||||
return UserProfile.objects.filter(Q(role=UserProfile.ROLE_REALM_OWNER) | Q(is_billing_admin=True),
|
||||
realm=self, is_bot=False, is_active=True)
|
||||
|
||||
def get_active_users(self) -> Sequence['UserProfile']:
|
||||
# TODO: Change return type to QuerySet[UserProfile]
|
||||
return UserProfile.objects.filter(realm=self, is_active=True).select_related()
|
||||
|
|
Loading…
Reference in New Issue