mirror of https://github.com/zulip/zulip.git
billing: Allow exempt_from_license_number_check any number of licenses.
exempt_from_license_number_check was initially added allowing organizations with it enabled to invite new users above their number of licenses. However, an organization with this permission enabled, cannot upgrade if they weren't on a plan already - because when choosing Manual license management, you cannot enter a number of licenses lower than the current seat count. However, an organization like that probably already has some users that they get free of charge - and thus they need to be able to enter a lower number of licenses in order to upgrade.
This commit is contained in:
parent
be208f73f7
commit
ef42065cec
|
@ -95,14 +95,19 @@ def unsign_string(signed_string: str, salt: str) -> str:
|
|||
return signer.unsign(signed_string)
|
||||
|
||||
|
||||
def validate_licenses(charge_automatically: bool, licenses: Optional[int], seat_count: int) -> None:
|
||||
def validate_licenses(
|
||||
charge_automatically: bool,
|
||||
licenses: Optional[int],
|
||||
seat_count: int,
|
||||
exempt_from_license_number_check: bool,
|
||||
) -> None:
|
||||
min_licenses = seat_count
|
||||
max_licenses = None
|
||||
if not charge_automatically:
|
||||
min_licenses = max(seat_count, MIN_INVOICED_LICENSES)
|
||||
max_licenses = MAX_INVOICED_LICENSES
|
||||
|
||||
if licenses is None or licenses < min_licenses:
|
||||
if licenses is None or (not exempt_from_license_number_check and licenses < min_licenses):
|
||||
raise BillingError(
|
||||
"not enough licenses", _("You must invoice for at least {} users.").format(min_licenses)
|
||||
)
|
||||
|
|
|
@ -2061,6 +2061,14 @@ class StripeTest(StripeTestCase):
|
|||
# Invoice
|
||||
check_success(True, MAX_INVOICED_LICENSES)
|
||||
|
||||
# By default, an organization on a "Pay by card" plan with Manual license
|
||||
# management cannot purchase less licenses than the current seat count.
|
||||
# If exempt_from_license_number_check is enabled, they should be able to though.
|
||||
customer = Customer.objects.get_or_create(realm=hamlet.realm)[0]
|
||||
customer.exempt_from_license_number_check = True
|
||||
customer.save()
|
||||
check_success(False, self.seat_count - 1, {"license_management": "manual"})
|
||||
|
||||
def test_upgrade_with_uncaught_exception(self) -> None:
|
||||
hamlet = self.example_user("hamlet")
|
||||
self.login_user(hamlet)
|
||||
|
|
|
@ -237,7 +237,12 @@ def update_plan(
|
|||
licenses=licenses
|
||||
)
|
||||
)
|
||||
validate_licenses(plan.charge_automatically, licenses, get_latest_seat_count(user.realm))
|
||||
validate_licenses(
|
||||
plan.charge_automatically,
|
||||
licenses,
|
||||
get_latest_seat_count(user.realm),
|
||||
plan.customer.exempt_from_license_number_check,
|
||||
)
|
||||
update_license_ledger_for_manual_plan(plan, timezone_now(), licenses=licenses)
|
||||
return json_success(request)
|
||||
|
||||
|
@ -258,6 +263,7 @@ def update_plan(
|
|||
plan.charge_automatically,
|
||||
licenses_at_next_renewal,
|
||||
get_latest_seat_count(user.realm),
|
||||
plan.customer.exempt_from_license_number_check,
|
||||
)
|
||||
update_license_ledger_for_manual_plan(
|
||||
plan, timezone_now(), licenses_at_next_renewal=licenses_at_next_renewal
|
||||
|
|
|
@ -65,6 +65,7 @@ def check_upgrade_parameters(
|
|||
license_management: Optional[str],
|
||||
licenses: Optional[int],
|
||||
seat_count: int,
|
||||
exempt_from_license_number_check: bool,
|
||||
) -> None:
|
||||
if billing_modality not in VALID_BILLING_MODALITY_VALUES: # nocoverage
|
||||
raise BillingError("unknown billing_modality", "")
|
||||
|
@ -72,7 +73,12 @@ def check_upgrade_parameters(
|
|||
raise BillingError("unknown schedule")
|
||||
if license_management not in VALID_LICENSE_MANAGEMENT_VALUES: # nocoverage
|
||||
raise BillingError("unknown license_management")
|
||||
validate_licenses(billing_modality == "charge_automatically", licenses, seat_count)
|
||||
validate_licenses(
|
||||
billing_modality == "charge_automatically",
|
||||
licenses,
|
||||
seat_count,
|
||||
exempt_from_license_number_check,
|
||||
)
|
||||
|
||||
|
||||
def setup_upgrade_checkout_session_and_payment_intent(
|
||||
|
@ -171,8 +177,18 @@ def upgrade(
|
|||
if billing_modality == "send_invoice":
|
||||
schedule = "annual"
|
||||
license_management = "manual"
|
||||
|
||||
customer = get_customer_by_realm(user.realm)
|
||||
exempt_from_license_number_check = (
|
||||
customer is not None and customer.exempt_from_license_number_check
|
||||
)
|
||||
check_upgrade_parameters(
|
||||
billing_modality, schedule, license_management, licenses, seat_count
|
||||
billing_modality,
|
||||
schedule,
|
||||
license_management,
|
||||
licenses,
|
||||
seat_count,
|
||||
exempt_from_license_number_check,
|
||||
)
|
||||
assert licenses is not None and license_management is not None
|
||||
automanage_licenses = license_management == "automatic"
|
||||
|
@ -258,6 +274,10 @@ def initial_upgrade(
|
|||
if customer is not None and customer.default_discount is not None:
|
||||
percent_off = customer.default_discount
|
||||
|
||||
exempt_from_license_number_check = (
|
||||
customer is not None and customer.exempt_from_license_number_check
|
||||
)
|
||||
|
||||
seat_count = get_latest_seat_count(user.realm)
|
||||
signed_seat_count, salt = sign_string(str(seat_count))
|
||||
context: Dict[str, Any] = {
|
||||
|
@ -268,6 +288,7 @@ def initial_upgrade(
|
|||
"salt": salt,
|
||||
"min_invoiced_licenses": max(seat_count, MIN_INVOICED_LICENSES),
|
||||
"default_invoice_days_until_due": DEFAULT_INVOICE_DAYS_UNTIL_DUE,
|
||||
"exempt_from_license_number_check": exempt_from_license_number_check,
|
||||
"plan": "Zulip Cloud Standard",
|
||||
"free_trial_days": settings.FREE_TRIAL_DAYS,
|
||||
"onboarding": onboarding,
|
||||
|
|
|
@ -151,8 +151,8 @@
|
|||
{% endif %}
|
||||
</p>
|
||||
|
||||
<h4>Number of licenses (minimum {{ seat_count }})</h4>
|
||||
<input type="number" name="licenses" min="{{ seat_count }}" autocomplete="off" id="manual_license_count" required/><br />
|
||||
<h4>Number of licenses {% if not exempt_from_license_number_check %}(minimum {{ seat_count }}){% endif %}</h4>
|
||||
<input type="number" name="licenses" {% if not exempt_from_license_number_check %}min="{{ seat_count }}"{% endif %} autocomplete="off" id="manual_license_count" required/><br />
|
||||
</div>
|
||||
<!-- Disabled buttons do not fire any events, so we need a container div that isn't disabled for tippyjs to work -->
|
||||
<div class="upgrade-button-container" {% if is_demo_organization %}data-tippy-content="{% trans %}Convert demo organization before upgrading.{% endtrans %}"{% endif %}>
|
||||
|
|
|
@ -187,10 +187,6 @@ run_test("autopay_form_fields", () => {
|
|||
document.querySelector("#autopay-form #automatic_license_count").value,
|
||||
"{{ seat_count }}",
|
||||
);
|
||||
assert.equal(
|
||||
document.querySelector("#autopay-form #manual_license_count").min,
|
||||
"{{ seat_count }}",
|
||||
);
|
||||
|
||||
const license_options = document.querySelectorAll(
|
||||
"#autopay-form input[type=radio][name=license_management]",
|
||||
|
|
Loading…
Reference in New Issue