def update_or_create_stripe_customer(user: UserProfile, stripe_token: Optional[str]=None) -> Customer: realm = user.realm customer = get_customer_by_realm(realm) if customer is None or customer.stripe_customer_id is None: return do_create_stripe_customer(user, stripe_token=stripe_token) if stripe_token is not None: do_replace_payment_source(user, stripe_token) return customer
def billing_home(request: HttpRequest) -> HttpResponse: user = request.user customer = get_customer_by_realm(user.realm) if customer is None: return HttpResponseRedirect(reverse('corporate.views.initial_upgrade')) if not CustomerPlan.objects.filter(customer=customer).exists(): return HttpResponseRedirect(reverse('corporate.views.initial_upgrade')) if not user.is_realm_admin and not user.is_billing_admin: context: Dict[str, Any] = {'admin_access': False} return render(request, 'corporate/billing.html', context=context) context = { 'admin_access': True, 'has_active_plan': False, } plan = get_current_plan_by_customer(customer) if plan is not None: now = timezone_now() last_ledger_entry = make_end_of_cycle_updates_if_needed(plan, now) if last_ledger_entry is not None: plan_name = { CustomerPlan.STANDARD: 'Zulip Standard', CustomerPlan.PLUS: 'Zulip Plus', }[plan.tier] free_trial = plan.status == CustomerPlan.FREE_TRIAL downgrade_at_end_of_cycle = plan.status == CustomerPlan.DOWNGRADE_AT_END_OF_CYCLE licenses = last_ledger_entry.licenses licenses_used = get_latest_seat_count(user.realm) # Should do this in javascript, using the user's timezone renewal_date = '{dt:%B} {dt.day}, {dt.year}'.format(dt=start_of_next_billing_cycle(plan, now)) renewal_cents = renewal_amount(plan, now) charge_automatically = plan.charge_automatically stripe_customer = stripe_get_customer(customer.stripe_customer_id) if charge_automatically: payment_method = payment_method_string(stripe_customer) else: payment_method = 'Billed by invoice' context.update({ 'plan_name': plan_name, 'has_active_plan': True, 'free_trial': free_trial, 'downgrade_at_end_of_cycle': downgrade_at_end_of_cycle, 'licenses': licenses, 'licenses_used': licenses_used, 'renewal_date': renewal_date, 'renewal_amount': '{:,.2f}'.format(renewal_cents / 100.), 'payment_method': payment_method, 'charge_automatically': charge_automatically, 'publishable_key': STRIPE_PUBLISHABLE_KEY, 'stripe_email': stripe_customer.email, 'CustomerPlan': CustomerPlan, 'onboarding': request.GET.get("onboarding") is not None, }) return render(request, 'corporate/billing.html', context=context)
def initial_upgrade( request: HttpRequest, onboarding: bool = REQ(default=False, json_validator=check_bool) ) -> HttpResponse: user = request.user assert user.is_authenticated if not settings.BILLING_ENABLED or user.is_guest: return render(request, "404.html", status=404) billing_page_url = reverse(billing_home) customer = get_customer_by_realm(user.realm) if customer is not None and ( get_current_plan_by_customer(customer) is not None or customer.sponsorship_pending ): if onboarding: billing_page_url = f"{billing_page_url}?onboarding=true" return HttpResponseRedirect(billing_page_url) if is_sponsored_realm(user.realm): return HttpResponseRedirect(billing_page_url) percent_off = Decimal(0) if customer is not None and customer.default_discount is not None: percent_off = customer.default_discount seat_count = get_latest_seat_count(user.realm) signed_seat_count, salt = sign_string(str(seat_count)) context: Dict[str, Any] = { "realm": user.realm, "email": user.delivery_email, "seat_count": seat_count, "signed_seat_count": signed_seat_count, "salt": salt, "min_invoiced_licenses": max(seat_count, MIN_INVOICED_LICENSES), "default_invoice_days_until_due": DEFAULT_INVOICE_DAYS_UNTIL_DUE, "plan": "Zulip Standard", "free_trial_days": settings.FREE_TRIAL_DAYS, "onboarding": onboarding, "page_params": { "seat_count": seat_count, "annual_price": 8000, "monthly_price": 800, "percent_off": float(percent_off), }, "realm_org_type": user.realm.org_type, "sorted_org_types": sorted( ( [org_type_name, org_type] for (org_type_name, org_type) in Realm.ORG_TYPES.items() if not org_type.get("hidden") ), key=lambda d: d[1]["display_order"], ), } response = render(request, "corporate/upgrade.html", context=context) return response
def update_or_create_stripe_customer( user: UserProfile, payment_method: Optional[str] = None ) -> Customer: realm = user.realm customer = get_customer_by_realm(realm) if customer is None or customer.stripe_customer_id is None: return do_create_stripe_customer(user, payment_method=payment_method) if payment_method is not None: do_replace_payment_method(user, payment_method, True) return customer
def start_retry_payment_intent_session( request: HttpRequest, user: UserProfile, stripe_payment_intent_id: str = REQ()) -> HttpResponse: customer = get_customer_by_realm(user.realm) if customer is None: raise JsonableError(_("Please create a customer first.")) try: payment_intent = PaymentIntent.objects.get( stripe_payment_intent_id=stripe_payment_intent_id, customer=customer) except PaymentIntent.DoesNotExist: raise JsonableError(_("Invalid payment intent id.")) stripe_payment_intent = stripe.PaymentIntent.retrieve( stripe_payment_intent_id) if stripe_payment_intent.status == "succeeded": raise JsonableError(_("Payment already succeeded.")) if (stripe_payment_intent.status == "processing" ): # nocoverage: Hard to arrive at this state using card raise JsonableError(_("Payment processing.")) metadata: Dict[str, Any] = { "user_id": user.id, } metadata.update(stripe_payment_intent.metadata) stripe_session = stripe.checkout.Session.create( cancel_url=f"{user.realm.uri}/upgrade/", customer=customer.stripe_customer_id, metadata=metadata, setup_intent_data={"metadata": metadata}, mode="setup", payment_method_types=["card"], success_url= f"{user.realm.uri}/billing/event_status?stripe_session_id={{CHECKOUT_SESSION_ID}}", ) session = Session.objects.create( stripe_session_id=stripe_session.id, customer=customer, type=Session.RETRY_UPGRADE_WITH_ANOTHER_PAYMENT_METHOD, ) session.payment_intent = payment_intent session.save(update_fields=["payment_intent"]) session.save() return json_success( request, data={ "stripe_session_id": stripe_session.id, "stripe_session_url": stripe_session.url, }, )
def void_all_open_invoices(realm: Realm) -> int: customer = get_customer_by_realm(realm) if customer is None: return 0 invoices = get_all_invoices_for_customer(customer) voided_invoices_count = 0 for invoice in invoices: if invoice.status == "open": stripe.Invoice.void_invoice(invoice.id) voided_invoices_count += 1 return voided_invoices_count
def test_change_sponsorship_status(self) -> None: lear_realm = get_realm("lear") self.assertIsNone(get_customer_by_realm(lear_realm)) cordelia = self.example_user("cordelia") self.login_user(cordelia) result = self.client_post("/activity/support", { "realm_id": f"{lear_realm.id}", "sponsorship_pending": "true" }) 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}", "sponsorship_pending": "true" }) self.assert_in_success_response( ["lear marked as pending sponsorship."], result) customer = get_customer_by_realm(lear_realm) assert customer is not None self.assertTrue(customer.sponsorship_pending) result = self.client_post("/activity/support", { "realm_id": f"{lear_realm.id}", "sponsorship_pending": "false" }) self.assert_in_success_response( ["lear is no longer pending sponsorship."], result) customer = get_customer_by_realm(lear_realm) assert customer is not None self.assertFalse(customer.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 initial_upgrade(request: HttpRequest) -> HttpResponse: user = request.user if not settings.BILLING_ENABLED or user.is_guest: return render(request, "404.html", status=404) billing_page_url = reverse(billing_home) customer = get_customer_by_realm(user.realm) if customer is not None and ( get_current_plan_by_customer(customer) is not None or customer.sponsorship_pending ): if request.GET.get("onboarding") is not None: billing_page_url = f"{billing_page_url}?onboarding=true" return HttpResponseRedirect(billing_page_url) if user.realm.plan_type == user.realm.STANDARD_FREE: return HttpResponseRedirect(billing_page_url) percent_off = Decimal(0) if customer is not None and customer.default_discount is not None: percent_off = customer.default_discount seat_count = get_latest_seat_count(user.realm) signed_seat_count, salt = sign_string(str(seat_count)) context: Dict[str, Any] = { "realm": user.realm, "publishable_key": STRIPE_PUBLISHABLE_KEY, "email": user.delivery_email, "seat_count": seat_count, "signed_seat_count": signed_seat_count, "salt": salt, "min_invoiced_licenses": max(seat_count, MIN_INVOICED_LICENSES), "default_invoice_days_until_due": DEFAULT_INVOICE_DAYS_UNTIL_DUE, "plan": "Zulip Standard", "free_trial_days": settings.FREE_TRIAL_DAYS, "onboarding": request.GET.get("onboarding") is not None, "page_params": { "seat_count": seat_count, "annual_price": 8000, "monthly_price": 800, "percent_off": float(percent_off), }, } response = render(request, "corporate/upgrade.html", context=context) return response
def handle(self, *args: Any, **options: str) -> None: realm = self.get_realm(options) assert realm is not None # Should be ensured by parser user_count = UserProfile.objects.filter( realm_id=realm.id, is_active=True, is_bot=False, ).count() message_count = Message.objects.filter(sender__realm=realm).count() print( f"This realm has {user_count} users and {message_count} messages.\n" ) if settings.BILLING_ENABLED: # Deleting a Realm object also deletes associating billing # metadata in an invariant-violating way, so we should # never use this tool for a realm with billing setup. from corporate.models import CustomerPlan, get_customer_by_realm customer = get_customer_by_realm(realm) if customer: if (customer.stripe_customer_id or CustomerPlan.objects.filter( customer=customer).count() > 0): raise CommandError( "This realm has had a billing relationship associated with it!" ) print( "This command will \033[91mPERMANENTLY DELETE\033[0m all data for this realm. " "Most use cases will be better served by scrub_realm and/or deactivate_realm." ) confirmation = input("Type the name of the realm to confirm: ") if confirmation != realm.string_id: raise CommandError("Aborting!") # TODO: This approach leaks Recipient and Huddle objects, # because those don't have a foreign key to the Realm or any # other model it cascades to (Realm/Stream/UserProfile/etc.). realm.delete() print("Realm has been successfully permanently deleted.")
def get_billing_info(user_profile: UserProfile) -> BillingInfo: show_billing = False show_plans = False if settings.CORPORATE_ENABLED and user_profile is not None: if user_profile.has_billing_access: from corporate.models import CustomerPlan, get_customer_by_realm customer = get_customer_by_realm(user_profile.realm) if customer is not None: if customer.sponsorship_pending: show_billing = True elif CustomerPlan.objects.filter(customer=customer).exists(): show_billing = True if not user_profile.is_guest and user_profile.realm.plan_type == Realm.LIMITED: show_plans = True return BillingInfo(show_billing=show_billing, show_plans=show_plans)
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"], 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 initial_upgrade(request: HttpRequest) -> HttpResponse: user = request.user if not settings.BILLING_ENABLED or user.is_guest: return render(request, "404.html", status=404) customer = get_customer_by_realm(user.realm) if customer is not None and (get_current_plan_by_customer(customer) is not None or customer.sponsorship_pending): billing_page_url = reverse('corporate.views.billing_home') if request.GET.get("onboarding") is not None: billing_page_url = f"{billing_page_url}?onboarding=true" return HttpResponseRedirect(billing_page_url) percent_off = Decimal(0) if customer is not None and customer.default_discount is not None: percent_off = customer.default_discount seat_count = get_latest_seat_count(user.realm) signed_seat_count, salt = sign_string(str(seat_count)) context: Dict[str, Any] = { 'realm': user.realm, 'publishable_key': STRIPE_PUBLISHABLE_KEY, 'email': user.delivery_email, 'seat_count': seat_count, 'signed_seat_count': signed_seat_count, 'salt': salt, 'min_invoiced_licenses': max(seat_count, MIN_INVOICED_LICENSES), 'default_invoice_days_until_due': DEFAULT_INVOICE_DAYS_UNTIL_DUE, 'plan': "Zulip Standard", "free_trial_days": settings.FREE_TRIAL_DAYS, "onboarding": request.GET.get("onboarding") is not None, 'page_params': { 'seat_count': seat_count, 'annual_price': 8000, 'monthly_price': 800, 'percent_off': float(percent_off), }, } response = render(request, 'corporate/upgrade.html', context=context) return response
def do_replace_payment_source(user: UserProfile, stripe_token: str, pay_invoices: bool=False) -> stripe.Customer: customer = get_customer_by_realm(user.realm) assert(customer is not None) # for mypy stripe_customer = stripe_get_customer(customer.stripe_customer_id) stripe_customer.source = stripe_token # Deletes existing card: https://stripe.com/docs/api#update_customer-source updated_stripe_customer = stripe.Customer.save(stripe_customer) RealmAuditLog.objects.create( realm=user.realm, acting_user=user, event_type=RealmAuditLog.STRIPE_CARD_CHANGED, event_time=timezone_now()) if pay_invoices: for stripe_invoice in stripe.Invoice.list( billing='charge_automatically', customer=stripe_customer.id, status='open'): # The user will get either a receipt or a "failed payment" email, but the in-app # messaging could be clearer here (e.g. it could explicitly tell the user that there # were payment(s) and that they succeeded or failed). # Worth fixing if we notice that a lot of cards end up failing at this step. stripe.Invoice.pay(stripe_invoice) return updated_stripe_customer
def attach_discount_to_realm( realm: Realm, discount: Decimal, *, acting_user: Optional[UserProfile] ) -> None: customer = get_customer_by_realm(realm) old_discount: Optional[Decimal] = None if customer is not None: old_discount = customer.default_discount customer.default_discount = discount customer.save(update_fields=["default_discount"]) else: Customer.objects.create(realm=realm, default_discount=discount) plan = get_current_plan_by_realm(realm) if plan is not None: plan.price_per_license = get_price_per_license(plan.tier, plan.billing_schedule, discount) plan.discount = discount plan.save(update_fields=["price_per_license", "discount"]) RealmAuditLog.objects.create( realm=realm, acting_user=acting_user, event_type=RealmAuditLog.REALM_DISCOUNT_CHANGED, event_time=timezone_now(), extra_data={"old_discount": old_discount, "new_discount": discount}, )
def plans_view(request: HttpRequest) -> HttpResponse: realm = get_realm_from_request(request) free_trial_days = settings.FREE_TRIAL_DAYS sponsorship_pending = False sponsorship_url = "/upgrade#sponsorship" if is_subdomain_root_or_alias(request): # If we're on the root domain, we make this link first ask you which organization. sponsorship_url = f"/accounts/go/?next={urllib.parse.quote(sponsorship_url)}" realm_on_free_trial = False if realm is not None: if realm.plan_type == Realm.PLAN_TYPE_SELF_HOSTED and settings.PRODUCTION: return HttpResponseRedirect("https://zulip.com/plans") if not request.user.is_authenticated: return redirect_to_login(next="/plans") if request.user.is_guest: return TemplateResponse(request, "404.html", status=404) if settings.CORPORATE_ENABLED: from corporate.lib.stripe import is_realm_on_free_trial from corporate.models import get_customer_by_realm customer = get_customer_by_realm(realm) if customer is not None: sponsorship_pending = customer.sponsorship_pending realm_on_free_trial = is_realm_on_free_trial(realm) return TemplateResponse( request, "zerver/plans.html", context={ "realm": realm, "free_trial_days": free_trial_days, "realm_on_free_trial": realm_on_free_trial, "sponsorship_pending": sponsorship_pending, "sponsorship_url": sponsorship_url, }, )
def get_discount_for_realm(realm: Realm) -> Optional[Decimal]: customer = get_customer_by_realm(realm) if customer is not None: return customer.default_discount return None
def billing_home(request: HttpRequest) -> HttpResponse: user = request.user assert user.is_authenticated customer = get_customer_by_realm(user.realm) context: Dict[str, Any] = { "admin_access": user.has_billing_access, "has_active_plan": False, } if user.realm.plan_type == user.realm.STANDARD_FREE: context["is_sponsored"] = True return render(request, "corporate/billing.html", context=context) if customer is None: from corporate.views.upgrade import initial_upgrade return HttpResponseRedirect(reverse(initial_upgrade)) if customer.sponsorship_pending: context["sponsorship_pending"] = True return render(request, "corporate/billing.html", context=context) if not CustomerPlan.objects.filter(customer=customer).exists(): from corporate.views.upgrade import initial_upgrade return HttpResponseRedirect(reverse(initial_upgrade)) if not user.has_billing_access: return render(request, "corporate/billing.html", context=context) plan = get_current_plan_by_customer(customer) if plan is not None: now = timezone_now() new_plan, last_ledger_entry = make_end_of_cycle_updates_if_needed( plan, now) if last_ledger_entry is not None: if new_plan is not None: # nocoverage plan = new_plan assert plan is not None # for mypy downgrade_at_end_of_cycle = plan.status == CustomerPlan.DOWNGRADE_AT_END_OF_CYCLE switch_to_annual_at_end_of_cycle = ( plan.status == CustomerPlan.SWITCH_TO_ANNUAL_AT_END_OF_CYCLE) licenses = last_ledger_entry.licenses licenses_at_next_renewal = last_ledger_entry.licenses_at_next_renewal seat_count = get_latest_seat_count(user.realm) # Should do this in javascript, using the user's timezone renewal_date = "{dt:%B} {dt.day}, {dt.year}".format( dt=start_of_next_billing_cycle(plan, now)) renewal_cents = renewal_amount(plan, now) charge_automatically = plan.charge_automatically assert customer.stripe_customer_id is not None # for mypy stripe_customer = stripe_get_customer(customer.stripe_customer_id) if charge_automatically: payment_method = payment_method_string(stripe_customer) else: payment_method = "Billed by invoice" context.update( plan_name=plan.name, has_active_plan=True, free_trial=plan.is_free_trial(), downgrade_at_end_of_cycle=downgrade_at_end_of_cycle, automanage_licenses=plan.automanage_licenses, switch_to_annual_at_end_of_cycle= switch_to_annual_at_end_of_cycle, licenses=licenses, licenses_at_next_renewal=licenses_at_next_renewal, seat_count=seat_count, renewal_date=renewal_date, renewal_amount=cents_to_dollar_string(renewal_cents), payment_method=payment_method, charge_automatically=charge_automatically, publishable_key=STRIPE_PUBLISHABLE_KEY, stripe_email=stripe_customer.email, CustomerPlan=CustomerPlan, onboarding=request.GET.get("onboarding") is not None, ) return render(request, "corporate/billing.html", context=context)
def support( request: HttpRequest, realm_id: Optional[int] = REQ(default=None, converter=to_non_negative_int), plan_type: Optional[int] = REQ(default=None, converter=to_non_negative_int), discount: Optional[Decimal] = REQ(default=None, converter=to_decimal), new_subdomain: Optional[str] = REQ(default=None), status: Optional[str] = REQ( default=None, str_validator=check_string_in(VALID_STATUS_VALUES)), billing_method: Optional[str] = REQ( default=None, str_validator=check_string_in(VALID_BILLING_METHODS)), sponsorship_pending: Optional[bool] = REQ(default=None, json_validator=check_bool), approve_sponsorship: Optional[bool] = REQ(default=None, json_validator=check_bool), downgrade_method: Optional[str] = REQ( default=None, str_validator=check_string_in(VALID_DOWNGRADE_METHODS)), scrub_realm: Optional[bool] = REQ(default=None, json_validator=check_bool), query: Optional[str] = REQ("q", default=None), org_type: Optional[int] = REQ(default=None, converter=to_non_negative_int), ) -> HttpResponse: context: Dict[str, Any] = {} if "success_message" in request.session: context["success_message"] = request.session["success_message"] del request.session["success_message"] if settings.BILLING_ENABLED and request.method == "POST": # We check that request.POST only has two keys in it: The # realm_id and a field to change. keys = set(request.POST.keys()) if "csrfmiddlewaretoken" in keys: keys.remove("csrfmiddlewaretoken") if len(keys) != 2: raise JsonableError(_("Invalid parameters")) realm = Realm.objects.get(id=realm_id) acting_user = request.user assert isinstance(acting_user, UserProfile) if plan_type is not None: current_plan_type = realm.plan_type do_change_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)} " context["success_message"] = msg elif org_type is not None: current_realm_type = realm.org_type 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)} " 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: old_subdomain = realm.string_id try: check_subdomain_available(new_subdomain) except ValidationError as error: context["error_message"] = error.message else: do_change_realm_subdomain(realm, new_subdomain, acting_user=acting_user) request.session[ "success_message"] = f"Subdomain changed from {old_subdomain} to {new_subdomain}" return HttpResponseRedirect( reverse("support") + "?" + urlencode({"q": new_subdomain})) elif status is not None: if status == "active": do_send_realm_reactivation_email(realm, acting_user=acting_user) context[ "success_message"] = f"Realm reactivation email sent to admins of {realm.string_id}." elif status == "deactivated": do_deactivate_realm(realm, acting_user=acting_user) context["success_message"] = f"{realm.string_id} deactivated." elif billing_method is not None: if billing_method == "send_invoice": update_billing_method_of_current_plan( realm, charge_automatically=False, acting_user=acting_user) context[ "success_message"] = f"Billing method of {realm.string_id} updated to pay by invoice." elif billing_method == "charge_automatically": update_billing_method_of_current_plan( realm, charge_automatically=True, acting_user=acting_user) context[ "success_message"] = f"Billing method of {realm.string_id} updated to charge automatically." elif sponsorship_pending is not None: if sponsorship_pending: update_sponsorship_status(realm, True, acting_user=acting_user) context[ "success_message"] = f"{realm.string_id} marked as pending sponsorship." else: update_sponsorship_status(realm, False, acting_user=acting_user) context[ "success_message"] = f"{realm.string_id} is no longer pending sponsorship." elif approve_sponsorship: do_approve_sponsorship(realm, acting_user=acting_user) context[ "success_message"] = f"Sponsorship approved for {realm.string_id}" elif downgrade_method is not None: if downgrade_method == "downgrade_at_billing_cycle_end": downgrade_at_the_end_of_billing_cycle(realm) context[ "success_message"] = f"{realm.string_id} 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[ "success_message"] = f"{realm.string_id} 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[ "success_message"] = f"{realm.string_id} downgraded and voided {voided_invoices_count} open invoices" elif scrub_realm: do_scrub_realm(realm, acting_user=acting_user) context["success_message"] = f"{realm.string_id} scrubbed." if query: key_words = get_invitee_emails_set(query) users = set(UserProfile.objects.filter(delivery_email__in=key_words)) realms = set(Realm.objects.filter(string_id__in=key_words)) for key_word in key_words: try: URLValidator()(key_word) parse_result = urllib.parse.urlparse(key_word) hostname = parse_result.hostname assert hostname is not None if parse_result.port: hostname = f"{hostname}:{parse_result.port}" subdomain = get_subdomain_from_hostname(hostname) try: realms.add(get_realm(subdomain)) except Realm.DoesNotExist: pass except ValidationError: users.update( UserProfile.objects.filter(full_name__iexact=key_word)) for realm in realms: realm.customer = get_customer_by_realm(realm) current_plan = get_current_plan_by_realm(realm) if current_plan is not None: new_plan, last_ledger_entry = make_end_of_cycle_updates_if_needed( current_plan, timezone_now()) if last_ledger_entry is not None: if new_plan is not None: realm.current_plan = new_plan else: realm.current_plan = current_plan realm.current_plan.licenses = last_ledger_entry.licenses realm.current_plan.licenses_used = get_latest_seat_count( realm) # full_names can have , in them users.update(UserProfile.objects.filter(full_name__iexact=query)) context["users"] = users context["realms"] = realms confirmations: List[Dict[str, Any]] = [] preregistration_users = PreregistrationUser.objects.filter( email__in=key_words) confirmations += get_confirmations( [ Confirmation.USER_REGISTRATION, Confirmation.INVITATION, Confirmation.REALM_CREATION ], preregistration_users, hostname=request.get_host(), ) multiuse_invites = MultiuseInvite.objects.filter(realm__in=realms) confirmations += get_confirmations([Confirmation.MULTIUSE_INVITE], multiuse_invites) confirmations += get_confirmations([Confirmation.REALM_REACTIVATION], [realm.id for realm in realms]) context["confirmations"] = confirmations def get_realm_owner_emails_as_string(realm: Realm) -> str: return ", ".join(realm.get_human_owner_users().order_by( "delivery_email").values_list("delivery_email", flat=True)) def get_realm_admin_emails_as_string(realm: Realm) -> str: return ", ".join( realm.get_human_admin_users(include_realm_owners=False).order_by( "delivery_email").values_list("delivery_email", flat=True)) context[ "get_realm_owner_emails_as_string"] = get_realm_owner_emails_as_string context[ "get_realm_admin_emails_as_string"] = get_realm_admin_emails_as_string context["get_discount_for_realm"] = get_discount_for_realm context["get_org_type_display_name"] = get_org_type_display_name context["realm_icon_url"] = realm_icon_url context["Confirmation"] = Confirmation context["sorted_realm_types"] = sorted(Realm.ORG_TYPES.values(), key=lambda d: d["display_order"]) return render(request, "analytics/support.html", context=context)
def billing_home(request: HttpRequest) -> HttpResponse: user = request.user customer = get_customer_by_realm(user.realm) context: Dict[str, Any] = { "admin_access": user.has_billing_access, 'has_active_plan': False, } if user.realm.plan_type == user.realm.STANDARD_FREE: context["is_sponsored"] = True return render(request, 'corporate/billing.html', context=context) if customer is None: return HttpResponseRedirect(reverse('corporate.views.initial_upgrade')) if customer.sponsorship_pending: context["sponsorship_pending"] = True return render(request, 'corporate/billing.html', context=context) if not CustomerPlan.objects.filter(customer=customer).exists(): return HttpResponseRedirect(reverse('corporate.views.initial_upgrade')) if not user.has_billing_access: return render(request, 'corporate/billing.html', context=context) plan = get_current_plan_by_customer(customer) if plan is not None: now = timezone_now() new_plan, last_ledger_entry = make_end_of_cycle_updates_if_needed( plan, now) if last_ledger_entry is not None: if new_plan is not None: # nocoverage plan = new_plan assert (plan is not None) # for mypy free_trial = plan.status == CustomerPlan.FREE_TRIAL downgrade_at_end_of_cycle = plan.status == CustomerPlan.DOWNGRADE_AT_END_OF_CYCLE switch_to_annual_at_end_of_cycle = plan.status == CustomerPlan.SWITCH_TO_ANNUAL_AT_END_OF_CYCLE licenses = last_ledger_entry.licenses licenses_used = get_latest_seat_count(user.realm) # Should do this in javascript, using the user's timezone renewal_date = '{dt:%B} {dt.day}, {dt.year}'.format( dt=start_of_next_billing_cycle(plan, now)) renewal_cents = renewal_amount(plan, now) charge_automatically = plan.charge_automatically stripe_customer = stripe_get_customer(customer.stripe_customer_id) if charge_automatically: payment_method = payment_method_string(stripe_customer) else: payment_method = 'Billed by invoice' context.update({ 'plan_name': plan.name, 'has_active_plan': True, 'free_trial': free_trial, 'downgrade_at_end_of_cycle': downgrade_at_end_of_cycle, 'automanage_licenses': plan.automanage_licenses, 'switch_to_annual_at_end_of_cycle': switch_to_annual_at_end_of_cycle, 'licenses': licenses, 'licenses_used': licenses_used, 'renewal_date': renewal_date, 'renewal_amount': f'{renewal_cents / 100.:,.2f}', 'payment_method': payment_method, 'charge_automatically': charge_automatically, 'publishable_key': STRIPE_PUBLISHABLE_KEY, 'stripe_email': stripe_customer.email, 'CustomerPlan': CustomerPlan, 'onboarding': request.GET.get("onboarding") is not None, }) return render(request, 'corporate/billing.html', context=context)
def home_real(request: HttpRequest) -> HttpResponse: # Before we do any real work, check if the app is banned. client_user_agent = request.META.get("HTTP_USER_AGENT", "") (insecure_desktop_app, banned_desktop_app, auto_update_broken) = is_outdated_desktop_app( client_user_agent) if banned_desktop_app: return render( request, 'zerver/insecure_desktop_app.html', context={ "auto_update_broken": auto_update_broken, }, ) (unsupported_browser, browser_name) = is_unsupported_browser(client_user_agent) if unsupported_browser: return render( request, 'zerver/unsupported_browser.html', context={ "browser_name": browser_name, }, ) # We need to modify the session object every two weeks or it will expire. # This line makes reloading the page a sufficient action to keep the # session alive. request.session.modified = True if request.user.is_authenticated: user_profile = request.user else: # nocoverage # This code path should not be reachable because of zulip_login_required above. user_profile = None # If a user hasn't signed the current Terms of Service, send them there if need_accept_tos(user_profile): return accounts_accept_terms(request) narrow, narrow_stream, narrow_topic = detect_narrowed_window(request, user_profile) client_capabilities = { 'notification_settings_null': True, 'bulk_message_deletion': True, 'user_avatar_url_field_optional': True, } register_ret = do_events_register(user_profile, request.client, apply_markdown=True, client_gravatar=True, slim_presence=True, client_capabilities=client_capabilities, narrow=narrow) update_last_reminder(user_profile) if user_profile is not None: first_in_realm = realm_user_count(user_profile.realm) == 1 # If you are the only person in the realm and you didn't invite # anyone, we'll continue to encourage you to do so on the frontend. prompt_for_invites = ( first_in_realm and not PreregistrationUser.objects.filter(referred_by=user_profile).count() ) needs_tutorial = user_profile.tutorial_status == UserProfile.TUTORIAL_WAITING else: # nocoverage first_in_realm = False prompt_for_invites = False # The current tutorial doesn't super make sense for logged-out users. needs_tutorial = False furthest_read_time = get_furthest_read_time(user_profile) # We pick a language for the user as follows: # * First priority is the language in the URL, for debugging. # * If not in the URL, we use the language from the user's settings. request_language = translation.get_language_from_path(request.path_info) if request_language is None: request_language = register_ret['default_language'] translation.activate(request_language) # We also save the language to the user's session, so that # something reasonable will happen in logged-in portico pages. request.session[translation.LANGUAGE_SESSION_KEY] = translation.get_language() two_fa_enabled = settings.TWO_FACTOR_AUTHENTICATION_ENABLED and user_profile is not None # Pass parameters to the client-side JavaScript code. # These end up in a global JavaScript Object named 'page_params'. page_params = dict( # Server settings. debug_mode = settings.DEBUG, test_suite = settings.TEST_SUITE, poll_timeout = settings.POLL_TIMEOUT, insecure_desktop_app = insecure_desktop_app, login_page = settings.HOME_NOT_LOGGED_IN, root_domain_uri = settings.ROOT_DOMAIN_URI, save_stacktraces = settings.SAVE_FRONTEND_STACKTRACES, warn_no_email = settings.WARN_NO_EMAIL, search_pills_enabled = settings.SEARCH_PILLS_ENABLED, # Misc. extra data. initial_servertime = time.time(), # Used for calculating relative presence age default_language_name = get_language_name(register_ret['default_language']), language_list_dbl_col = get_language_list_for_templates(register_ret['default_language']), language_list = get_language_list(), needs_tutorial = needs_tutorial, first_in_realm = first_in_realm, prompt_for_invites = prompt_for_invites, furthest_read_time = furthest_read_time, has_mobile_devices = user_profile is not None and num_push_devices_for_user(user_profile) > 0, bot_types = get_bot_types(user_profile), two_fa_enabled = two_fa_enabled, # Adding two_fa_enabled as condition saves us 3 queries when # 2FA is not enabled. two_fa_enabled_user = two_fa_enabled and bool(default_device(user_profile)), ) undesired_register_ret_fields = [ 'streams', ] for field_name in set(register_ret.keys()) - set(undesired_register_ret_fields): page_params[field_name] = register_ret[field_name] if narrow_stream is not None: # In narrow_stream context, initial pointer is just latest message recipient = narrow_stream.recipient try: max_message_id = Message.objects.filter(recipient=recipient).order_by('id').reverse()[0].id except IndexError: max_message_id = -1 page_params["narrow_stream"] = narrow_stream.name if narrow_topic is not None: page_params["narrow_topic"] = narrow_topic page_params["narrow"] = [dict(operator=term[0], operand=term[1]) for term in narrow] page_params["max_message_id"] = max_message_id page_params["enable_desktop_notifications"] = False statsd.incr('views.home') show_invites, show_add_streams = compute_show_invites_and_add_streams(user_profile) show_billing = False show_plans = False if settings.CORPORATE_ENABLED and user_profile is not None: from corporate.models import CustomerPlan, get_customer_by_realm if user_profile.has_billing_access: customer = get_customer_by_realm(user_profile.realm) if customer is not None: if customer.sponsorship_pending: show_billing = True elif CustomerPlan.objects.filter(customer=customer).exists(): show_billing = True if user_profile.realm.plan_type == Realm.LIMITED: show_plans = True request._log_data['extra'] = "[{}]".format(register_ret["queue_id"]) page_params['translation_data'] = {} if request_language != 'en': page_params['translation_data'] = get_language_translation_data(request_language) csp_nonce = generate_random_token(48) if user_profile is not None: color_scheme = user_profile.color_scheme is_guest = user_profile.is_guest is_realm_owner = user_profile.is_realm_owner is_realm_admin = user_profile.is_realm_admin show_webathena = user_profile.realm.webathena_enabled else: # nocoverage color_scheme = UserProfile.COLOR_SCHEME_AUTOMATIC is_guest = False is_realm_admin = False is_realm_owner = False show_webathena = False navbar_logo_url = compute_navbar_logo_url(page_params) # print("----------------->") # new_realm_users = [] # user_initial = user_profile.full_name[0] # pattern = '^{}'.format(user_initial) # for _ in range(len(page_params['realm_users'])): # current_user_fullname = page_params['realm_users'][_]['full_name'] # initials_matched_user = re.match(pattern,current_user_fullname) # if initials_matched_user: # new_realm_users.append(page_params['realm_users'][_]) # page_params['realm_users'] = new_realm_users # print(page_params['realm_users']) # print('\n') # print(page_params.keys()) # print("-----------------> UserProfile objects") # print(UserProfile.objects.all()) response = render(request, 'zerver/app/index.html', context={'user_profile': user_profile, 'page_params': page_params, 'csp_nonce': csp_nonce, 'search_pills_enabled': settings.SEARCH_PILLS_ENABLED, 'show_invites': show_invites, 'show_add_streams': show_add_streams, 'show_billing': show_billing, 'corporate_enabled': settings.CORPORATE_ENABLED, 'show_plans': show_plans, 'is_owner': is_realm_owner, 'is_admin': is_realm_admin, 'is_guest': is_guest, 'color_scheme': color_scheme, 'navbar_logo_url': navbar_logo_url, 'show_webathena': show_webathena, 'embedded': narrow_stream is not None, 'invite_as': PreregistrationUser.INVITE_AS, 'max_file_upload_size_mib': settings.MAX_FILE_UPLOAD_SIZE, }) patch_cache_control(response, no_cache=True, no_store=True, must_revalidate=True) # print("########################### | These are page_params keys | ##############") # print(page_params.keys()) # print("########################### | Realm Users | #############################") # print(page_params['realm_users']) # print("############################ | Presences |############################") # print("\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/") # print(page_params['presences']) return response