New 'user' wizard step + drop alert toasts after setup
Two fixes for the noisy post-finalize popup: 1. Dedicated admin-user step The wizard was passing Frappe's existing Administrator identity (full_name="Administrator", email="admin@example.com") into setup_complete, which made Frappe's user creation suggest a new username and warn that admin@example.com had no linked employee ("İşçinin Öz Xidməti rolu silindi"). Add a new "user" step between "company" and "confirm" collecting full name, email (= login), and password (with confirmation). build_setup_args sends those values, so Frappe creates a real user with no naming clash. Administrator stays untouched as the system-of-last-resort fallback. Validation: name required, basic email regex, password ≥8 chars, passwords must match. Email "admin@example.com" / "administrator" are blocked client-side as obviously-reserved. 2. Suppress alert-style toasts At the end of materialize_after_setup, drop every entry from frappe.local.message_log where alert=1. That kills the cluster of "Sənəd X yenidən adlandırıldı" toasts that pile up when the AZ translation pipeline renames default Item Group fixtures, without silencing the genuine modal warnings (e.g. HRMS Salary Component "accounts not assigned") which use plain msgprint.
This commit is contained in:
parent
b739f06c98
commit
d93a024ba8
|
|
@ -1 +1 @@
|
||||||
__version__ = "0.1.11"
|
__version__ = "0.1.12"
|
||||||
|
|
|
||||||
|
|
@ -260,6 +260,14 @@ def materialize_after_setup(args):
|
||||||
|
|
||||||
_materialize_amas_employees(company_name)
|
_materialize_amas_employees(company_name)
|
||||||
|
|
||||||
|
# Drop toast-style messages that piled up during the various install/translate
|
||||||
|
# stages — notably az_locale's "Sənəd X yenidən adlandırıldı" notifications
|
||||||
|
# fired when fixtures get renamed to Azerbaijani. These are cosmetic and would
|
||||||
|
# spam the post-setup screen. Modal warnings (non-alert msgprints — e.g. HRMS
|
||||||
|
# Salary Component "accounts not assigned") stay so genuine open items remain
|
||||||
|
# visible to the user.
|
||||||
|
_drop_alert_messages()
|
||||||
|
|
||||||
|
|
||||||
def _materialize_native_banks(company):
|
def _materialize_native_banks(company):
|
||||||
"""Create ERPNext-native Bank / GL Account / Bank Account for each non-closed
|
"""Create ERPNext-native Bank / GL Account / Bank Account for each non-closed
|
||||||
|
|
@ -758,6 +766,27 @@ def _find_default_warehouse(company):
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def _drop_alert_messages():
|
||||||
|
"""Remove every entry from frappe.local.message_log that was queued via
|
||||||
|
frappe.msgprint(alert=True) (a.k.a. toast). Keeps non-alert msgprints so
|
||||||
|
the user still sees real warnings.
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
log = list(getattr(frappe.local, "message_log", []) or [])
|
||||||
|
except Exception:
|
||||||
|
return
|
||||||
|
if not log:
|
||||||
|
return
|
||||||
|
def _is_alert(m):
|
||||||
|
try:
|
||||||
|
if isinstance(m, dict):
|
||||||
|
return bool(m.get("alert"))
|
||||||
|
return bool(getattr(m, "alert", 0))
|
||||||
|
except Exception:
|
||||||
|
return False
|
||||||
|
frappe.local.message_log = [m for m in log if not _is_alert(m)]
|
||||||
|
|
||||||
|
|
||||||
def _only_admin():
|
def _only_admin():
|
||||||
if frappe.session.user != "Administrator":
|
if frappe.session.user != "Administrator":
|
||||||
frappe.throw(_("Only Administrator may run setup"), frappe.PermissionError)
|
frappe.throw(_("Only Administrator may run setup"), frappe.PermissionError)
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,7 @@ frappe.provide("jey_wizard");
|
||||||
// Bump this string in every commit that changes wizard code. Displayed in the badge so
|
// Bump this string in every commit that changes wizard code. Displayed in the badge so
|
||||||
// we can tell at a glance which version is actually running on a given machine. Kept in
|
// we can tell at a glance which version is actually running on a given machine. Kept in
|
||||||
// sync with __version__ in jey_wizard/__init__.py.
|
// sync with __version__ in jey_wizard/__init__.py.
|
||||||
const JEY_WIZARD_VERSION = "0.1.11";
|
const JEY_WIZARD_VERSION = "0.1.12";
|
||||||
|
|
||||||
// Wipe Frappe + ERPNext default slides so their `before_load`/`after_load` listeners
|
// Wipe Frappe + ERPNext default slides so their `before_load`/`after_load` listeners
|
||||||
// don't try to mutate a wizard that isn't slide-based anymore.
|
// don't try to mutate a wizard that isn't slide-based anymore.
|
||||||
|
|
@ -53,6 +53,10 @@ frappe.setup.SetupWizard = class JeySetupWizard {
|
||||||
fy_start_date: `${year}-01-01`,
|
fy_start_date: `${year}-01-01`,
|
||||||
fy_end_date: `${year}-12-31`,
|
fy_end_date: `${year}-12-31`,
|
||||||
valuation_method: "FIFO",
|
valuation_method: "FIFO",
|
||||||
|
// Admin user (fresh user step — Administrator stays untouched)
|
||||||
|
user_full_name: "",
|
||||||
|
user_email: "",
|
||||||
|
user_password: "",
|
||||||
// AMAS
|
// AMAS
|
||||||
amas_skipped: false,
|
amas_skipped: false,
|
||||||
amas_selected_employees: [],
|
amas_selected_employees: [],
|
||||||
|
|
@ -75,7 +79,7 @@ frappe.setup.SetupWizard = class JeySetupWizard {
|
||||||
this.amas_verification_code = "";
|
this.amas_verification_code = "";
|
||||||
|
|
||||||
this.current_step = 0;
|
this.current_step = 0;
|
||||||
this.steps = ["language", "asan", "employees", "company", "confirm"];
|
this.steps = ["language", "asan", "employees", "company", "user", "confirm"];
|
||||||
this.render();
|
this.render();
|
||||||
|
|
||||||
// Session arrives in whatever language Frappe booted in (usually English for
|
// Session arrives in whatever language Frappe booted in (usually English for
|
||||||
|
|
@ -121,6 +125,7 @@ frappe.setup.SetupWizard = class JeySetupWizard {
|
||||||
else if (step === "asan") this.render_asan($body);
|
else if (step === "asan") this.render_asan($body);
|
||||||
else if (step === "employees") this.render_employees($body);
|
else if (step === "employees") this.render_employees($body);
|
||||||
else if (step === "company") this.render_company($body);
|
else if (step === "company") this.render_company($body);
|
||||||
|
else if (step === "user") this.render_user($body);
|
||||||
else if (step === "confirm") this.render_confirm($body);
|
else if (step === "confirm") this.render_confirm($body);
|
||||||
|
|
||||||
this.render_nav($nav, step);
|
this.render_nav($nav, step);
|
||||||
|
|
@ -514,6 +519,38 @@ frappe.setup.SetupWizard = class JeySetupWizard {
|
||||||
return initialsOf(clean, 4).slice(0, 10);
|
return initialsOf(clean, 4).slice(0, 10);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
render_user($body) {
|
||||||
|
// Collect a real admin user separate from Frappe's Administrator. We
|
||||||
|
// pass full_name/email/password into setup_complete; Frappe will create
|
||||||
|
// this user during run_setup_success. Administrator itself stays as
|
||||||
|
// the system-of-last-resort fallback login.
|
||||||
|
$body.html(`
|
||||||
|
<h3 style="margin-bottom:8px">${__("Administrator account")}</h3>
|
||||||
|
<p style="color:#888;font-size:13px;margin-bottom:16px">${__("Create the day-to-day admin account. The default Administrator login is preserved as a fallback.")}</p>
|
||||||
|
<div style="margin-bottom:12px;max-width:420px">
|
||||||
|
<label style="display:block;margin-bottom:4px">${__("Full name")}</label>
|
||||||
|
<input type="text" class="jey-user-name form-control" autocomplete="name" />
|
||||||
|
</div>
|
||||||
|
<div style="margin-bottom:12px;max-width:420px">
|
||||||
|
<label style="display:block;margin-bottom:4px">${__("Email (login)")}</label>
|
||||||
|
<input type="email" class="jey-user-email form-control" autocomplete="email" />
|
||||||
|
</div>
|
||||||
|
<div style="margin-bottom:12px;max-width:420px">
|
||||||
|
<label style="display:block;margin-bottom:4px">${__("Password")}</label>
|
||||||
|
<input type="password" class="jey-user-password form-control" autocomplete="new-password" />
|
||||||
|
<div style="color:#888;font-size:12px;margin-top:4px">${__("Minimum 8 characters.")}</div>
|
||||||
|
</div>
|
||||||
|
<div style="margin-bottom:12px;max-width:420px">
|
||||||
|
<label style="display:block;margin-bottom:4px">${__("Confirm password")}</label>
|
||||||
|
<input type="password" class="jey-user-password2 form-control" autocomplete="new-password" />
|
||||||
|
</div>
|
||||||
|
`);
|
||||||
|
$body.find(".jey-user-name").val(this.data.user_full_name);
|
||||||
|
$body.find(".jey-user-email").val(this.data.user_email);
|
||||||
|
$body.find(".jey-user-password").val(this.data.user_password);
|
||||||
|
$body.find(".jey-user-password2").val(this.data.user_password);
|
||||||
|
}
|
||||||
|
|
||||||
render_confirm($body) {
|
render_confirm($body) {
|
||||||
const empCount = (this.data.amas_selected_employees || []).length;
|
const empCount = (this.data.amas_selected_employees || []).length;
|
||||||
const empRow = this.data.amas_skipped
|
const empRow = this.data.amas_skipped
|
||||||
|
|
@ -529,6 +566,7 @@ frappe.setup.SetupWizard = class JeySetupWizard {
|
||||||
: ""}
|
: ""}
|
||||||
<li><b>${__("Fiscal Year")}:</b> ${frappe.utils.escape_html(this.data.fy_start_date)} — ${frappe.utils.escape_html(this.data.fy_end_date)}</li>
|
<li><b>${__("Fiscal Year")}:</b> ${frappe.utils.escape_html(this.data.fy_start_date)} — ${frappe.utils.escape_html(this.data.fy_end_date)}</li>
|
||||||
<li><b>${__("Valuation method")}:</b> ${this.data.valuation_method === "Moving Average" ? __("Moving Average") : "FIFO"}</li>
|
<li><b>${__("Valuation method")}:</b> ${this.data.valuation_method === "Moving Average" ? __("Moving Average") : "FIFO"}</li>
|
||||||
|
<li><b>${__("Admin user")}:</b> ${frappe.utils.escape_html(this.data.user_full_name)} <span style="color:#888">(${frappe.utils.escape_html(this.data.user_email)})</span></li>
|
||||||
<li><b>${__("ƏMAS employees")}:</b> ${empRow}</li>
|
<li><b>${__("ƏMAS employees")}:</b> ${empRow}</li>
|
||||||
</ul>
|
</ul>
|
||||||
`);
|
`);
|
||||||
|
|
@ -618,6 +656,35 @@ frappe.setup.SetupWizard = class JeySetupWizard {
|
||||||
this.data.fy_end_date = fyEnd;
|
this.data.fy_end_date = fyEnd;
|
||||||
const vm = (this.$wrap.find(".jey-valuation-method").val() || "FIFO").trim();
|
const vm = (this.$wrap.find(".jey-valuation-method").val() || "FIFO").trim();
|
||||||
this.data.valuation_method = vm === "Moving Average" ? "Moving Average" : "FIFO";
|
this.data.valuation_method = vm === "Moving Average" ? "Moving Average" : "FIFO";
|
||||||
|
} else if (step === "user") {
|
||||||
|
const fullName = (this.$wrap.find(".jey-user-name").val() || "").trim();
|
||||||
|
const email = (this.$wrap.find(".jey-user-email").val() || "").trim().toLowerCase();
|
||||||
|
const pw = this.$wrap.find(".jey-user-password").val() || "";
|
||||||
|
const pw2 = this.$wrap.find(".jey-user-password2").val() || "";
|
||||||
|
if (!fullName) {
|
||||||
|
this.$wrap.find(".jey-status").text(__("Full name is required"));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// Permissive check — Frappe revalidates on the server.
|
||||||
|
if (!email || !/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email)) {
|
||||||
|
this.$wrap.find(".jey-status").text(__("Enter a valid email"));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (email === "administrator" || email === "admin@example.com") {
|
||||||
|
this.$wrap.find(".jey-status").text(__("Pick a different email — that one is reserved"));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (pw.length < 8) {
|
||||||
|
this.$wrap.find(".jey-status").text(__("Password must be at least 8 characters"));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (pw !== pw2) {
|
||||||
|
this.$wrap.find(".jey-status").text(__("Passwords do not match"));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
this.data.user_full_name = fullName;
|
||||||
|
this.data.user_email = email;
|
||||||
|
this.data.user_password = pw;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
@ -1162,7 +1229,6 @@ frappe.setup.SetupWizard = class JeySetupWizard {
|
||||||
this.derive_abbr(company) ||
|
this.derive_abbr(company) ||
|
||||||
"CO"
|
"CO"
|
||||||
).slice(0, 10);
|
).slice(0, 10);
|
||||||
const admin = frappe.boot.user || {};
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
language: LANG_NAME[this.data.language] || "English",
|
language: LANG_NAME[this.data.language] || "English",
|
||||||
|
|
@ -1170,9 +1236,12 @@ frappe.setup.SetupWizard = class JeySetupWizard {
|
||||||
timezone: "Asia/Baku",
|
timezone: "Asia/Baku",
|
||||||
currency: "AZN",
|
currency: "AZN",
|
||||||
enable_telemetry: 0,
|
enable_telemetry: 0,
|
||||||
full_name: admin.full_name || "Administrator",
|
// Admin user fields come from the dedicated `user` step. We never
|
||||||
email: admin.email || "admin@example.com",
|
// reuse Administrator's identity here — that's why the previous
|
||||||
password: "",
|
// "username `administrator` already exists" warning showed up.
|
||||||
|
full_name: this.data.user_full_name,
|
||||||
|
email: this.data.user_email,
|
||||||
|
password: this.data.user_password,
|
||||||
company_name: company,
|
company_name: company,
|
||||||
company_abbr: abbr,
|
company_abbr: abbr,
|
||||||
chart_of_accounts: "Azerbaijan Republic",
|
chart_of_accounts: "Azerbaijan Republic",
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue