corporate: Add a Basic plan.

This commit is contained in:
Karl Stolley 2023-12-18 16:57:32 -06:00 committed by Tim Abbott
parent 3a0be097f4
commit a37354f92a
13 changed files with 216 additions and 92 deletions

View File

@ -81,6 +81,7 @@ def get_plan_type_string(plan_type: int) -> str:
CustomerPlan.TIER_SELF_HOSTED_LEGACY
),
RemoteZulipServer.PLAN_TYPE_COMMUNITY: "Community",
RemoteZulipServer.PLAN_TYPE_BASIC: "Basic",
RemoteZulipServer.PLAN_TYPE_BUSINESS: "Business",
RemoteZulipServer.PLAN_TYPE_ENTERPRISE: "Enterprise",
}[plan_type]

View File

@ -1833,10 +1833,10 @@ class BillingSession(ABC):
customer = self.get_customer()
# Allow users to upgrade to business regardless of current sponsorship status.
if (
self.is_sponsored_or_pending(customer)
and initial_upgrade_request.tier != CustomerPlan.TIER_SELF_HOSTED_BUSINESS
):
if self.is_sponsored_or_pending(customer) and initial_upgrade_request.tier not in [
CustomerPlan.TIER_SELF_HOSTED_BASIC,
CustomerPlan.TIER_SELF_HOSTED_BUSINESS,
]:
return f"{self.billing_session_url}/sponsorship", None
remote_server_legacy_plan_end_date = self.get_formatted_remote_server_legacy_plan_end_date(
@ -1935,8 +1935,10 @@ class BillingSession(ABC):
return None, context
def min_licenses_for_plan(self, tier: int) -> int:
if tier == CustomerPlan.TIER_SELF_HOSTED_BUSINESS:
if tier == CustomerPlan.TIER_SELF_HOSTED_BASIC:
return 10
if tier == CustomerPlan.TIER_SELF_HOSTED_BUSINESS:
return 25
return 1
def downgrade_at_the_end_of_billing_cycle(self, plan: Optional[CustomerPlan] = None) -> None:
@ -3258,6 +3260,8 @@ class RemoteRealmBillingSession(BillingSession):
if is_sponsored:
plan_type = RemoteRealm.PLAN_TYPE_COMMUNITY
self.add_customer_to_community_plan()
elif tier == CustomerPlan.TIER_SELF_HOSTED_BASIC:
plan_type = RemoteRealm.PLAN_TYPE_BASIC
elif tier == CustomerPlan.TIER_SELF_HOSTED_BUSINESS:
plan_type = RemoteRealm.PLAN_TYPE_BUSINESS
elif tier == CustomerPlan.TIER_SELF_HOSTED_LEGACY:
@ -3367,8 +3371,8 @@ class RemoteRealmBillingSession(BillingSession):
) -> PlanTierChangeType: # nocoverage
valid_plan_tiers = [
CustomerPlan.TIER_SELF_HOSTED_LEGACY,
CustomerPlan.TIER_SELF_HOSTED_BASIC,
CustomerPlan.TIER_SELF_HOSTED_BUSINESS,
CustomerPlan.TIER_SELF_HOSTED_PLUS,
]
if (
current_plan_tier not in valid_plan_tiers
@ -3377,18 +3381,18 @@ class RemoteRealmBillingSession(BillingSession):
):
return PlanTierChangeType.INVALID
if (
current_plan_tier == CustomerPlan.TIER_SELF_HOSTED_BUSINESS
and new_plan_tier == CustomerPlan.TIER_SELF_HOSTED_PLUS
current_plan_tier == CustomerPlan.TIER_SELF_HOSTED_BASIC
and new_plan_tier == CustomerPlan.TIER_SELF_HOSTED_BUSINESS
):
return PlanTierChangeType.UPGRADE
elif current_plan_tier == CustomerPlan.TIER_SELF_HOSTED_LEGACY and new_plan_tier in (
CustomerPlan.TIER_SELF_HOSTED_BASIC,
CustomerPlan.TIER_SELF_HOSTED_BUSINESS,
CustomerPlan.TIER_SELF_HOSTED_PLUS,
):
return PlanTierChangeType.UPGRADE
else:
assert current_plan_tier == CustomerPlan.TIER_SELF_HOSTED_PLUS
assert new_plan_tier == CustomerPlan.TIER_SELF_HOSTED_BUSINESS
assert current_plan_tier == CustomerPlan.TIER_SELF_HOSTED_BUSINESS
assert new_plan_tier == CustomerPlan.TIER_SELF_HOSTED_BASIC
return PlanTierChangeType.DOWNGRADE
@override
@ -3398,6 +3402,7 @@ class RemoteRealmBillingSession(BillingSession):
return True
PAID_PLANS = [
RemoteRealm.PLAN_TYPE_BASIC,
RemoteRealm.PLAN_TYPE_BUSINESS,
RemoteRealm.PLAN_TYPE_ENTERPRISE,
]
@ -3644,6 +3649,8 @@ class RemoteServerBillingSession(BillingSession):
if is_sponsored:
plan_type = RemoteZulipServer.PLAN_TYPE_COMMUNITY
self.add_customer_to_community_plan()
elif tier == CustomerPlan.TIER_SELF_HOSTED_BASIC:
plan_type = RemoteZulipServer.PLAN_TYPE_BASIC
elif tier == CustomerPlan.TIER_SELF_HOSTED_BUSINESS:
plan_type = RemoteZulipServer.PLAN_TYPE_BUSINESS
elif tier == CustomerPlan.TIER_SELF_HOSTED_LEGACY:
@ -3747,8 +3754,8 @@ class RemoteServerBillingSession(BillingSession):
) -> PlanTierChangeType: # nocoverage
valid_plan_tiers = [
CustomerPlan.TIER_SELF_HOSTED_LEGACY,
CustomerPlan.TIER_SELF_HOSTED_BASIC,
CustomerPlan.TIER_SELF_HOSTED_BUSINESS,
CustomerPlan.TIER_SELF_HOSTED_PLUS,
]
if (
current_plan_tier not in valid_plan_tiers
@ -3758,23 +3765,28 @@ class RemoteServerBillingSession(BillingSession):
return PlanTierChangeType.INVALID
if current_plan_tier == CustomerPlan.TIER_SELF_HOSTED_LEGACY and new_plan_tier in (
CustomerPlan.TIER_SELF_HOSTED_BASIC,
CustomerPlan.TIER_SELF_HOSTED_BUSINESS,
CustomerPlan.TIER_SELF_HOSTED_PLUS,
):
return PlanTierChangeType.UPGRADE
elif (
current_plan_tier == CustomerPlan.TIER_SELF_HOSTED_BUSINESS
and new_plan_tier == CustomerPlan.TIER_SELF_HOSTED_PLUS
current_plan_tier == CustomerPlan.TIER_SELF_HOSTED_BASIC
and new_plan_tier == CustomerPlan.TIER_SELF_HOSTED_BUSINESS
):
return PlanTierChangeType.UPGRADE
elif (
current_plan_tier == CustomerPlan.TIER_SELF_HOSTED_BASIC
and new_plan_tier == CustomerPlan.TIER_SELF_HOSTED_LEGACY
):
return PlanTierChangeType.DOWNGRADE
elif (
current_plan_tier == CustomerPlan.TIER_SELF_HOSTED_BUSINESS
and new_plan_tier == CustomerPlan.TIER_SELF_HOSTED_LEGACY
):
return PlanTierChangeType.DOWNGRADE
else:
assert current_plan_tier == CustomerPlan.TIER_SELF_HOSTED_PLUS
assert new_plan_tier == CustomerPlan.TIER_SELF_HOSTED_BUSINESS
assert current_plan_tier == CustomerPlan.TIER_SELF_HOSTED_BUSINESS
assert new_plan_tier == CustomerPlan.TIER_SELF_HOSTED_BASIC
return PlanTierChangeType.DOWNGRADE
@override
@ -3784,6 +3796,7 @@ class RemoteServerBillingSession(BillingSession):
return True
PAID_PLANS = [
RemoteZulipServer.PLAN_TYPE_BASIC,
RemoteZulipServer.PLAN_TYPE_BUSINESS,
RemoteZulipServer.PLAN_TYPE_ENTERPRISE,
]
@ -3887,7 +3900,7 @@ def get_price_per_license(
price_map: Dict[int, Dict[str, int]] = {
CustomerPlan.TIER_CLOUD_STANDARD: {"Annual": 8000, "Monthly": 800},
CustomerPlan.TIER_CLOUD_PLUS: {"Annual": 16000, "Monthly": 1600},
# Placeholder self-hosted plan for development.
CustomerPlan.TIER_SELF_HOSTED_BASIC: {"Annual": 4200, "Monthly": 350},
CustomerPlan.TIER_SELF_HOSTED_BUSINESS: {"Annual": 8000, "Monthly": 800},
# To help with processing discount request on support page.
CustomerPlan.TIER_SELF_HOSTED_LEGACY: {"Annual": 0, "Monthly": 0},

View File

@ -0,0 +1,26 @@
# Generated by Django 4.2.8 on 2023-12-19 11:38
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("corporate", "0029_session_tier"),
]
operations = [
migrations.AlterField(
model_name="zulipsponsorshiprequest",
name="requested_plan",
field=models.CharField(
choices=[
("", "UNSPECIFIED"),
("Community", "COMMUNITY"),
("Basic", "BASIC"),
("Business", "BUSINESS"),
],
default="",
max_length=50,
),
),
]

View File

@ -270,8 +270,8 @@ class CustomerPlan(models.Model):
TIER_SELF_HOSTED_BASE = 100
TIER_SELF_HOSTED_LEGACY = 101
TIER_SELF_HOSTED_COMMUNITY = 102
TIER_SELF_HOSTED_BUSINESS = 103
TIER_SELF_HOSTED_PLUS = 104
TIER_SELF_HOSTED_BASIC = 103
TIER_SELF_HOSTED_BUSINESS = 104
TIER_SELF_HOSTED_ENTERPRISE = 105
tier = models.SmallIntegerField()
@ -307,6 +307,7 @@ class CustomerPlan(models.Model):
CustomerPlan.TIER_CLOUD_PLUS: "Zulip Cloud Plus",
CustomerPlan.TIER_CLOUD_ENTERPRISE: "Zulip Enterprise",
CustomerPlan.TIER_SELF_HOSTED_LEGACY: "Self-managed (legacy plan)",
CustomerPlan.TIER_SELF_HOSTED_BASIC: "Zulip Basic",
CustomerPlan.TIER_SELF_HOSTED_BUSINESS: "Zulip Business",
CustomerPlan.TIER_SELF_HOSTED_COMMUNITY: "Community",
}[tier]
@ -398,6 +399,7 @@ class SponsoredPlanTypes(Enum):
# unspecified used for cloud sponsorship requests
UNSPECIFIED = ""
COMMUNITY = "Community"
BASIC = "Basic"
BUSINESS = "Business"

View File

@ -84,8 +84,8 @@ class PlansPageContext:
billing_base_url: str = ""
tier_self_hosted_basic: int = CustomerPlan.TIER_SELF_HOSTED_BASIC
tier_self_hosted_business: int = CustomerPlan.TIER_SELF_HOSTED_BUSINESS
tier_cloud_standard: int = CustomerPlan.TIER_CLOUD_STANDARD

View File

@ -6,25 +6,25 @@ questions about plans and billing for self-hosted organizations. Please refer to
details. If you have any questions not answered here, please don't hesitate to
reach out at [sales@zulip.com](mailto:sales@zulip.com).
## Business plan details and upgrades
The Business plan is appropriate for most business organizations. It includes
unlimited access to the Mobile Push Notification Service and commercial support
for dozens of features and integrations that help businesses take full advantage
of their Zulip implementation.
## Paid plan details and upgrades
For businesses with up to 10 Zulip users, the Self-managed plan is a good
option, and includes free access to the Mobile Push Notification service. For
commercial support with your installation, sign up for the Business plan, with a
minimum purchase of 10 licenses.
option, and includes free access to the Mobile Push Notification Service.
If you organization requires hands-on support, such as real-time support during
For businesses with more than 10 Zulip users, both the Basic and Business plans
include unlimited access to the Mobile Push Notification Service.
The Business plan also includes commercial support for dozens of features and
integrations that help businesses take full advantage of their Zulip
implementation. The minimum purchase is 25 licenses.
If your organization requires hands-on support, such as real-time support during
installation and upgrades, support for advanced deployment options, custom
feature development or integrations, etc., should contact
[sales@zulip.com](mailto:sales@zulip.com) to discuss pricing.
Business plan discounts are available in a variety of situations; see
[below](#business-plan-discounts) for details.
Paid plan discounts are available in a variety of situations; see
[below](#paid-plan-discounts) for details.
### Upgrades for legacy customers
@ -289,7 +289,7 @@ eligibility prior to setting up a server, contact
{end_tabs}
## Business plan discounts
## Paid plan discounts
The following types of organizations are generally eligible for significant
discounts on the Zulip Business plan. You can also contact

View File

@ -17,7 +17,7 @@
<tr>
<th class="comparison-table-feature">Features</th>
<th>Self-<wbr />managed</th>
<th>Comm<wbr />unity</th>
<th>Basic</th>
<th>Busi<wbr />ness</th>
<th>Enter<wbr />prise</th>
</tr>

View File

@ -138,7 +138,7 @@
</li>
</ul>
<div class="discounted-business-plan">
<h3>Business plan discounts <a href="/help/self-hosted-billing#business-plan-discounts">available</a></h3>
<h3>Paid plan discounts <a href="/help/self-hosted-billing#business-plan-discounts">available</a></h3>
<ul>
<li>
<span><a href="/help/self-hosted-billing#business-plan-discounts">Special</a> education and non-profit pricing (100+ users)</span>
@ -179,7 +179,8 @@
<span><a href="/help/zulip-cloud-or-self-hosting">How do I choose between Zulip Cloud and self-hosting?</a></span>
</li>
<li>
<span><a href="/help/self-hosted-billing#business-plan-details-and-upgrades">How do I upgrade to the Business plan?</a></span>
<span><a href="/help/self-hosted-billing#business-plan-details-and-upgrades">How
do I upgrade to a paid plan?</a></span>
</li>
<li>
<span><a
@ -190,8 +191,8 @@
</li>
</ul>
<p>
Please reach out to <a href="mailto:sales@zulip.com">sales@zulip.com</a> if you
have any further questions or would like to discuss Enterprise pricing.
Contact <a href="mailto:sales@zulip.com">sales@zulip.com</a>
with further questions or to discuss Enterprise pricing.
</p>
</div>
</div>

View File

@ -84,7 +84,7 @@
</div>
</div>
{% if is_cloud_realm and on_free_tier and not sponsorship_pending %}
<a href="/upgrade/?tier%3D{{ tier_cloud_standard }}" class="button upgrade-button">
<a href="/upgrade/?tier={{ tier_cloud_standard }}" class="button upgrade-button">
{% if free_trial_days %}
Start {{ free_trial_days }}-day free trial
{% else %}
@ -160,22 +160,17 @@
<div class="self-hosted-plan-pricing pricing-pane">
<div class="price-box" tabindex="-1">
<div class="text-content">
<h2>Self-managed</h2>
<h2>Free</h2>
<ul class="feature-list">
<li><span><a href="https://zulip.readthedocs.io/en/stable/production/mobile-push-notifications.html">Mobile push notifications</a> for organizations with up to 10&nbsp;users</span></li>
<li class="support-note"><span>Friendly <a href="/development-community/">community</a> support for:</span></li>
<li><span>Tools for <a href="/why-zulip/">efficient communication</a></span></li>
<li><span>Unlimited <a href="/help/search-for-messages">search</a> history</span></li>
<li><span>Unlimited <a href="/help/start-a-call">voice and video calls</a></span></li>
<li><span>Unlimited <a href="/integrations/">integrations</a></span></li>
<li><span>Easy <a href="https://zulip.readthedocs.io/en/stable/production/install.html">installation</a> and <a href="https://zulip.readthedocs.io/en/stable/production/upgrade.html">maintenance</a></span></li>
<li class="comparison-table-pointer"><span>And <a href="{{ billing_base_url }}/plans/#self-hosted-plan-comparison">much more</a>!</span></li>
<li><span>Complete team chat solution, with <a href="{{ billing_base_url }}/plans/#self-hosted-plan-comparison">all Zulip features</a> included</span></li>
<li><span><a href="https://zulip.readthedocs.io/en/stable/production/mobile-push-notifications.html">Mobile notifications</a> for organizations with up to 10&nbsp;users</span></li>
<li><span>Many organizations are eligible for unlimited mobile notifications on the free Community plan (see below)</span></li>
</ul>
</div>
<div class="bottom">
<div class="text-content">
<div class="standard-price-box">
<div class="price">Free</div>
<div class="price no-discount">Free</div>
</div>
{% if is_self_hosted_realm and on_free_tier %}
<span class="button current-plan-descriptor" type="button">
@ -195,38 +190,73 @@
<div class="price-box" tabindex="-1">
<div class="text-content">
<h2>Community</h2>
<h2>Basic</h2>
<ul class="feature-list">
<li class="unlimited-push-notifications"><span>Unlimited <a href="https://zulip.readthedocs.io/en/stable/production/mobile-push-notifications.html">mobile push notifications</a></span></li>
<li class="support-note"><span>Friendly <a href="/development-community/">community</a> support for:</span></li>
<li><span><a href="/help/configure-authentication-methods">OAuth social login</a> (e.g., Google, GitHub)</span></li>
<li><span><a href="/help/public-access-option">Public access option</a></span></li>
<li><span><a href="/help/moderating-open-organizations">Advanced moderation tools</a></span></li>
<li><span><a href="/help/format-your-message-using-markdown">Expressive formatting</a> (code, LaTeX)</span></li>
<li class="comparison-table-pointer"><span>And <a href="{{ billing_base_url }}/plans/#self-hosted-plan-comparison">much more</a>!</span></li>
<li><span>Complete team chat solution, with <a href="{{ billing_base_url }}/plans/#self-hosted-plan-comparison">all Zulip features</a> included</span></li>
<li><span>Unlimited <a href="https://zulip.readthedocs.io/en/stable/production/mobile-push-notifications.html">mobile notifications</a></span></li>
<li><span>Support Zulip's open-source development</span></li>
</ul>
</div>
<div class="bottom">
<div class="text-content">
<div class="standard-price-box">
<div class="price">Free</div>
{% if (is_legacy_server_with_scheduled_upgrade and legacy_server_new_plan.tier == legacy_server_new_plan.TIER_SELF_HOSTED_BASIC)
or (is_self_hosted_realm and sponsorship_pending and requested_sponsorship_plan == "Basic")
or (is_self_hosted_realm and customer_plan and customer_plan.tier == customer_plan.TIER_SELF_HOSTED_BASIC)%}
<div class="price no-discount"><span class="currency-symbol">$</span>3.50</div>
<div class="details">
<p>
/user/month billed monthly
</p>
</div>
{% else %}
<div class="price"><span class="currency-symbol">$</span>3.50</div>
<div class="details">
<p>
/user/month billed monthly
</p>
</div>
<div class="discount">
<b>$20/month off</b> for the first year!
</div>
{% endif %}
</div>
{% if is_self_hosted_realm and is_sponsored %}
<a href='{{ billing_base_url }}/billing' class="button current-plan-button" type="button">
<i class="icon current-plan-icon"></i>
Current plan
{% if is_legacy_server_with_scheduled_upgrade and legacy_server_new_plan.tier == legacy_server_new_plan.TIER_SELF_HOSTED_BASIC %}
<a href="{{ billing_base_url }}/billing/" class="button current-plan-button">
Upgrade is scheduled
</a>
{% elif is_self_hosted_realm and sponsorship_pending and requested_sponsorship_plan == "Community" %}
<a href="{{ billing_base_url }}/billing/" class="button current-plan-button" type="button">
{% elif is_self_hosted_realm and sponsorship_pending and requested_sponsorship_plan == "Business" %}
<a href="{{ billing_base_url }}/upgrade/?tier={{ tier_self_hosted_basic }}" class="button current-plan-button" type="button">
Sponsorship requested
</a>
{% elif is_self_hosted_realm and on_free_tier and not sponsorship_pending %}
<a href="{{ billing_base_url }}/upgrade/?tier={{ tier_self_hosted_basic }}" class="button upgrade-button">
{% if free_trial_days %}
Start {{ free_trial_days }}-day free trial
{% else %}
Upgrade to Basic
{% endif %}
</a>
{% elif is_self_hosted_realm and customer_plan and customer_plan.tier == customer_plan.TIER_SELF_HOSTED_BASIC %}
<a href='{{ billing_base_url }}/billing' class="button current-plan-button" type="button">
<i class="icon current-plan-icon"></i>
{% if on_free_trial %}
Current plan (free trial)
{% else %}
Current plan
{% endif %}
</a>
{% elif is_self_hosted_realm %}
<a href="{{ billing_base_url }}/sponsorship/" class="button upgrade-button">
Request sponsorship
<a href="{{ billing_base_url }}/upgrade/?tier={{ tier_self_hosted_basic }}" class="button upgrade-button">
{% if free_trial_days %}
Start {{ free_trial_days }}-day free trial
{% else %}
Upgrade to Basic
{% endif %}
</a>
{% else %}
<a href="/help/self-hosted-billing#free-community-plan" target="_blank" rel="noopener noreferrer" class="button upgrade-button">
Log in to apply
<a href="/help/self-hosted-billing" target="_blank" rel="noopener noreferrer" class="button upgrade-button">
Log in to upgrade
</a>
{% endif %}
</div>
@ -235,9 +265,10 @@
<div class="price-box" tabindex="-1">
<div class="text-content">
<h2 class="with-fine-print">Business <small>10 users minimum</small></h2>
<h2 class="with-fine-print">Business <small>25 users minimum</small></h2>
<ul class="feature-list">
<li class="unlimited-push-notifications"><span>Unlimited <a href="https://zulip.readthedocs.io/en/stable/production/mobile-push-notifications.html">mobile push notifications</a></span></li>
<li><span>Complete team chat solution, with <a href="{{ billing_base_url }}/plans/#self-hosted-plan-comparison">all Zulip features</a> included</span></li>
<li><span>Unlimited <a href="https://zulip.readthedocs.io/en/stable/production/mobile-push-notifications.html">mobile notifications</a></span></li>
<li class="support-note"><span>Email and chat support for:</span></li>
<li><span><a href="/help/saml-authentication">SSO with SAML, Azure AD</a></span></li>
<li><span><a href="https://zulip.readthedocs.io/en/stable/production/authentication-methods.html">AD/LDAP user sync </a></span></li>
@ -250,12 +281,29 @@
<div class="bottom">
<div class="text-content">
<div class="standard-price-box">
<div class="price"><span class="currency-symbol">$</span>6.67</div>
<div class="details">
<p>
/user/month billed annually
or&nbsp;<b>$8</b>&nbsp;billed monthly
</p>
<div class="standard-price-box">
{% if (is_legacy_server_with_scheduled_upgrade and legacy_server_new_plan.tier == legacy_server_new_plan.TIER_SELF_HOSTED_BUSINESS)
or (is_self_hosted_realm and sponsorship_pending and requested_sponsorship_plan == "Business")
or (is_self_hosted_realm and customer_plan and customer_plan.tier == customer_plan.TIER_SELF_HOSTED_BUSINESS)%}
<div class="price no-discount"><span class="currency-symbol">$</span>6.67</div>
<div class="details">
<p>
/user/month billed annually
or&nbsp;<b>$8</b>&nbsp;billed monthly
</p>
</div>
{% else %}
<div class="price"><span class="currency-symbol">$</span>6.67</div>
<div class="details">
<p>
/user/month billed annually
or&nbsp;<b>$8</b>&nbsp;billed monthly
</p>
</div>
<div class="discount">
<b>$20/month off</b> for the first year!
</div>
{% endif %}
</div>
</div>
{% if is_legacy_server_with_scheduled_upgrade and legacy_server_new_plan.tier == legacy_server_new_plan.TIER_SELF_HOSTED_BUSINESS %}
@ -263,11 +311,11 @@
Upgrade is scheduled
</a>
{% elif is_self_hosted_realm and sponsorship_pending and requested_sponsorship_plan == "Business" %}
<a href="{{ billing_base_url }}/upgrade/?tier%3D{{ tier_self_hosted_business }}" class="button current-plan-button" type="button">
<a href="{{ billing_base_url }}/upgrade/?tier={{ tier_self_hosted_business }}" class="button current-plan-button" type="button">
Sponsorship requested
</a>
{% elif is_self_hosted_realm and on_free_tier and not sponsorship_pending %}
<a href="{{ billing_base_url }}/upgrade/?tier%3D{{ tier_self_hosted_business }}" class="button upgrade-button">
<a href="{{ billing_base_url }}/upgrade/?tier={{ tier_self_hosted_business }}" class="button upgrade-button">
{% if free_trial_days %}
Start {{ free_trial_days }}-day free trial
{% else %}
@ -284,7 +332,7 @@
{% endif %}
</a>
{% elif is_self_hosted_realm %}
<a href="{{ billing_base_url }}/upgrade/?tier%3D{{ tier_self_hosted_business }}" class="button upgrade-button">
<a href="{{ billing_base_url }}/upgrade/?tier={{ tier_self_hosted_business }}" class="button upgrade-button">
{% if free_trial_days %}
Start {{ free_trial_days }}-day free trial
{% else %}
@ -304,7 +352,8 @@
<div class="text-content">
<h2>Enterprise</h2>
<ul class="feature-list">
<li class="unlimited-push-notifications"><span>Unlimited <a href="https://zulip.readthedocs.io/en/stable/production/mobile-push-notifications.html">mobile push notifications</a></span></li>
<li><span>Complete team chat solution, with <a href="{{ billing_base_url }}/plans/#self-hosted-plan-comparison">all Zulip features</a> included</span></li>
<li><span>Unlimited <a href="https://zulip.readthedocs.io/en/stable/production/mobile-push-notifications.html">mobile notifications</a></span></li>
<li class="support-note"><span>Email, chat and phone support for:</span></li>
<li><span><a href="https://zulip.readthedocs.io/en/stable/production/authentication-methods.html#openid-connect">SSO with OpenID Connect</a></span></li>
<li><span><a href="https://zulip.readthedocs.io/en/stable/production/authentication-methods.html">AD/LDAP group sync</a></span></li>

View File

@ -573,7 +573,11 @@ html_rules: List["Rule"] = [
"good_lines": ['<a href="{{variable}}">'],
"bad_lines": ["<a href={{variable}}>"],
# Exclude the use of URL templates from this check.
"exclude_pattern": "={code}",
# Exclude the use GET parameters in URLs from this check.
"exclude_pattern": "={code}|\\?[a-z]+={|\\&[a-z]+={",
"exclude": {
"templates/corporate/pricing_model.html",
},
},
{
"pattern": " '}}",

View File

@ -351,6 +351,9 @@
.standard-price-box {
display: flex;
/* Handle a discount line, when
needed. */
flex-wrap: wrap;
align-items: flex-start;
gap: 4px;
min-height: 56px;
@ -360,16 +363,38 @@
font-size: 38px;
line-height: 1;
letter-spacing: -1px;
&.no-discount {
/* Pad prices that do not have
a discount (e.g., Free, or a
customer's current plan). */
padding-bottom: 25px;
}
}
.currency-symbol {
opacity: 0.5;
}
.details p {
margin: 2px 0 0;
font-size: 15px;
line-height: 17px;
.details {
flex: 1 0 0;
p {
margin: 2px 0 0;
font-size: 15px;
line-height: 17px;
}
}
.discount {
/* Keep the discount to its own
line in the wrapping flexbox. */
flex: 0 0 100%;
text-align: center;
padding: 1px 0 6px;
margin-bottom: -5px;
border-radius: 4px 4px 0 0;
background-color: hsl(0deg 0% 85%);
}
}
}
@ -504,6 +529,9 @@
letter-spacing: 0.06ch;
padding: 8px 10px;
border-radius: 4px;
/* Position relatively for discount tab */
position: relative;
z-index: 5;
}
.button-placeholder {

View File

@ -164,7 +164,7 @@ class Command(BaseCommand):
unique_id="legacy-server-upgrade-scheduled",
tier=CustomerPlan.TIER_SELF_HOSTED_LEGACY,
status=CustomerPlan.SWITCH_PLAN_TIER_AT_PLAN_END,
new_plan_tier=CustomerPlan.TIER_SELF_HOSTED_PLUS,
new_plan_tier=CustomerPlan.TIER_SELF_HOSTED_BASIC,
is_remote_server=True,
),
CustomerProfile(
@ -441,7 +441,7 @@ def populate_remote_server(customer_profile: CustomerProfile) -> Dict[str, str]:
# This attaches stripe_customer_id to customer.
customer = billing_session.update_or_create_stripe_customer()
add_card_to_customer(customer)
seat_count = 10
seat_count = 30
signed_seat_count, salt = sign_string(str(seat_count))
upgrade_request = UpgradeRequest(
billing_modality="charge_automatically",

View File

@ -64,8 +64,8 @@ class RemoteZulipServer(models.Model):
PLAN_TYPE_SELF_MANAGED = 100
PLAN_TYPE_SELF_MANAGED_LEGACY = 101
PLAN_TYPE_COMMUNITY = 102
PLAN_TYPE_BUSINESS = 103
PLAN_TYPE_PLUS = 104
PLAN_TYPE_BASIC = 103
PLAN_TYPE_BUSINESS = 104
PLAN_TYPE_ENTERPRISE = 105
# The current billing plan for the remote server, similar to Realm.plan_type.
@ -165,8 +165,8 @@ class RemoteRealm(models.Model):
PLAN_TYPE_SELF_MANAGED = 100
PLAN_TYPE_SELF_MANAGED_LEGACY = 101
PLAN_TYPE_COMMUNITY = 102
PLAN_TYPE_BUSINESS = 103
PLAN_TYPE_PLUS = 104
PLAN_TYPE_BASIC = 103
PLAN_TYPE_BUSINESS = 104
PLAN_TYPE_ENTERPRISE = 105
plan_type = models.PositiveSmallIntegerField(default=PLAN_TYPE_SELF_MANAGED, db_index=True)