mirror of https://github.com/zulip/zulip.git
billing: Highlight manual license management errors for admins.
In the billing portal UI for manual license management, limit decreasing the number of licenses for the next billing period to be less than the currently used licenses. If the customer is exempt from license number checks, then this limit is not applied. Also, visually highlight manual license management errors so that the billing admin is aware of potential issues. As of these changes, current licenses can be under the seat count when a guest user is changed to a non-guest user. And next billing period licenses can be under the seat cout when a user joins with a currently available, purchased license after a billing admin has decreased the number of licenses set for the next billing period.
This commit is contained in:
parent
b02cf53d5e
commit
137f4fccde
|
@ -2545,6 +2545,7 @@ class BillingSession(ABC):
|
|||
"licenses": licenses,
|
||||
"licenses_at_next_renewal": licenses_at_next_renewal,
|
||||
"seat_count": seat_count,
|
||||
"exempt_from_license_number_check": customer.exempt_from_license_number_check,
|
||||
"renewal_date": renewal_date,
|
||||
"renewal_amount": cents_to_dollar_string(renewal_cents) if renewal_cents != 0 else None,
|
||||
"payment_method": payment_method,
|
||||
|
|
|
@ -143,7 +143,7 @@
|
|||
</label>
|
||||
<div class="number-input-with-label">
|
||||
<form id="current-license-change-form">
|
||||
<input type="number" name="licenses" autocomplete="off" id="current-manual-license-count" class="short-width-number-input" data-original-value="{{ licenses }}" value="{{ licenses }}" required/>
|
||||
<input type="number" name="licenses" autocomplete="off" id="current-manual-license-count" class="short-width-number-input" data-original-value="{{ licenses }}" value="{{ licenses }}" {%if not exempt_from_license_number_check %}min="{{ seat_count }}"{% endif %} required/>
|
||||
</form>
|
||||
<span class="licence-count-in-use">licenses ({{ seat_count }} in use)</span>
|
||||
<button id="current-manual-license-count-update-button" class="license-count-update-button hide">
|
||||
|
@ -170,7 +170,7 @@
|
|||
</label>
|
||||
<div class="number-input-with-label">
|
||||
<form id="next-license-change-form">
|
||||
<input type="number" name="licenses_at_next_renewal" autocomplete="off" id="next-manual-license-count" class="short-width-number-input" data-original-value="{{ licenses_at_next_renewal }}" value="{{ licenses_at_next_renewal }}" required/>
|
||||
<input type="number" name="licenses_at_next_renewal" autocomplete="off" id="next-manual-license-count" class="short-width-number-input" data-original-value="{{ licenses_at_next_renewal }}" value="{{ licenses_at_next_renewal }}" {%if not exempt_from_license_number_check %}min="{{ seat_count }}"{% endif %} required/>
|
||||
</form>
|
||||
<span class="licence-count-in-use">licenses ({{ seat_count }} in use)</span>
|
||||
<button id="next-manual-license-count-update-button" class="license-count-update-button hide">
|
||||
|
|
|
@ -101,9 +101,10 @@ export function initialize(): void {
|
|||
e.preventDefault();
|
||||
});
|
||||
|
||||
function get_old_and_new_license_count_for_current_cycle(): {
|
||||
function get_license_counts_for_current_cycle(): {
|
||||
new_current_manual_license_count: number;
|
||||
old_current_manual_license_count: number;
|
||||
min_current_manual_license_count: number;
|
||||
} {
|
||||
const new_current_manual_license_count: number = Number.parseInt(
|
||||
$<HTMLInputElement>("input#current-manual-license-count").val()!,
|
||||
|
@ -113,15 +114,25 @@ export function initialize(): void {
|
|||
$<HTMLInputElement>("input#current-manual-license-count").attr("data-original-value")!,
|
||||
10,
|
||||
);
|
||||
let min_current_manual_license_count: number = Number.parseInt(
|
||||
$<HTMLInputElement>("input#current-manual-license-count").attr("min")!,
|
||||
10,
|
||||
);
|
||||
if (Number.isNaN(min_current_manual_license_count)) {
|
||||
// Customer is exempt from license number checks.
|
||||
min_current_manual_license_count = 0;
|
||||
}
|
||||
return {
|
||||
new_current_manual_license_count,
|
||||
old_current_manual_license_count,
|
||||
min_current_manual_license_count,
|
||||
};
|
||||
}
|
||||
|
||||
function get_old_and_new_license_count_for_next_cycle(): {
|
||||
function get_license_counts_for_next_cycle(): {
|
||||
new_next_manual_license_count: number;
|
||||
old_next_manual_license_count: number;
|
||||
min_next_manual_license_count: number;
|
||||
} {
|
||||
const new_next_manual_license_count: number = Number.parseInt(
|
||||
$<HTMLInputElement>("input#next-manual-license-count").val()!,
|
||||
|
@ -131,12 +142,43 @@ export function initialize(): void {
|
|||
$<HTMLInputElement>("input#next-manual-license-count").attr("data-original-value")!,
|
||||
10,
|
||||
);
|
||||
let min_next_manual_license_count: number = Number.parseInt(
|
||||
$<HTMLInputElement>("input#next-manual-license-count").attr("min")!,
|
||||
10,
|
||||
);
|
||||
if (Number.isNaN(min_next_manual_license_count)) {
|
||||
// Customer is exempt from license number checks.
|
||||
min_next_manual_license_count = 0;
|
||||
}
|
||||
return {
|
||||
new_next_manual_license_count,
|
||||
old_next_manual_license_count,
|
||||
min_next_manual_license_count,
|
||||
};
|
||||
}
|
||||
|
||||
function check_for_manual_billing_errors(): void {
|
||||
const {old_next_manual_license_count, min_next_manual_license_count} =
|
||||
get_license_counts_for_next_cycle();
|
||||
if (old_next_manual_license_count < min_next_manual_license_count) {
|
||||
$("#next-license-change-error").text(
|
||||
"Number of licenses for next billing period less than licenses in use.",
|
||||
);
|
||||
} else {
|
||||
$("#next-license-change-error").text("");
|
||||
}
|
||||
|
||||
const {old_current_manual_license_count, min_current_manual_license_count} =
|
||||
get_license_counts_for_current_cycle();
|
||||
if (old_current_manual_license_count < min_current_manual_license_count) {
|
||||
$("#current-license-change-error").text(
|
||||
"Number of licenses for current billing period less than licenses in use.",
|
||||
);
|
||||
} else {
|
||||
$("#current-license-change-error").text("");
|
||||
}
|
||||
}
|
||||
|
||||
$("#current-license-change-form, #next-license-change-form").on("submit", (e) => {
|
||||
// We don't want user to accidentally update the license count on pressing enter.
|
||||
e.preventDefault();
|
||||
|
@ -149,7 +191,7 @@ export function initialize(): void {
|
|||
}
|
||||
e.preventDefault();
|
||||
const {new_current_manual_license_count, old_current_manual_license_count} =
|
||||
get_old_and_new_license_count_for_current_cycle();
|
||||
get_license_counts_for_current_cycle();
|
||||
const $modal = $("#confirm-licenses-modal-increase");
|
||||
$modal.find(".new_license_count_holder").text(new_current_manual_license_count);
|
||||
$modal.find(".current_license_count_holder").text(old_current_manual_license_count);
|
||||
|
@ -166,7 +208,7 @@ export function initialize(): void {
|
|||
}
|
||||
e.preventDefault();
|
||||
const {new_next_manual_license_count, old_next_manual_license_count} =
|
||||
get_old_and_new_license_count_for_next_cycle();
|
||||
get_license_counts_for_next_cycle();
|
||||
let $modal;
|
||||
if (new_next_manual_license_count > old_next_manual_license_count) {
|
||||
$modal = $("#confirm-licenses-modal-increase");
|
||||
|
@ -290,15 +332,23 @@ export function initialize(): void {
|
|||
|
||||
let timeout: ReturnType<typeof setTimeout> | null = null;
|
||||
|
||||
check_for_manual_billing_errors();
|
||||
|
||||
$("#current-manual-license-count").on("keyup", () => {
|
||||
if (timeout !== null) {
|
||||
clearTimeout(timeout);
|
||||
}
|
||||
|
||||
timeout = setTimeout(() => {
|
||||
const {new_current_manual_license_count, old_current_manual_license_count} =
|
||||
get_old_and_new_license_count_for_current_cycle();
|
||||
if (new_current_manual_license_count > old_current_manual_license_count) {
|
||||
const {
|
||||
new_current_manual_license_count,
|
||||
old_current_manual_license_count,
|
||||
min_current_manual_license_count,
|
||||
} = get_license_counts_for_current_cycle();
|
||||
if (
|
||||
new_current_manual_license_count > old_current_manual_license_count &&
|
||||
new_current_manual_license_count > min_current_manual_license_count
|
||||
) {
|
||||
$("#current-manual-license-count-update-button").toggleClass("hide", false);
|
||||
$("#current-license-change-error").text("");
|
||||
} else if (new_current_manual_license_count < old_current_manual_license_count) {
|
||||
|
@ -308,7 +358,7 @@ export function initialize(): void {
|
|||
$("#current-manual-license-count-update-button").toggleClass("hide", true);
|
||||
} else {
|
||||
$("#current-manual-license-count-update-button").toggleClass("hide", true);
|
||||
$("#current-license-change-error").text("");
|
||||
check_for_manual_billing_errors();
|
||||
}
|
||||
}, 300); // Wait for 300ms after the user stops typing
|
||||
});
|
||||
|
@ -319,16 +369,26 @@ export function initialize(): void {
|
|||
}
|
||||
|
||||
timeout = setTimeout(() => {
|
||||
const {new_next_manual_license_count, old_next_manual_license_count} =
|
||||
get_old_and_new_license_count_for_next_cycle();
|
||||
const {
|
||||
new_next_manual_license_count,
|
||||
old_next_manual_license_count,
|
||||
min_next_manual_license_count,
|
||||
} = get_license_counts_for_next_cycle();
|
||||
if (
|
||||
!new_next_manual_license_count ||
|
||||
new_next_manual_license_count < 0 ||
|
||||
new_next_manual_license_count === old_next_manual_license_count
|
||||
) {
|
||||
$("#next-manual-license-count-update-button").toggleClass("hide", true);
|
||||
check_for_manual_billing_errors();
|
||||
} else if (new_next_manual_license_count < min_next_manual_license_count) {
|
||||
$("#next-manual-license-count-update-button").toggleClass("hide", true);
|
||||
$("#next-license-change-error").text(
|
||||
"Cannot be less than the number of licenses currently in use.",
|
||||
);
|
||||
} else {
|
||||
$("#next-manual-license-count-update-button").toggleClass("hide", false);
|
||||
$("#next-license-change-error").text("");
|
||||
}
|
||||
}, 300); // Wait for 300ms after the user stops typing
|
||||
});
|
||||
|
|
Loading…
Reference in New Issue