Call stock setup_complete endpoint directly from client
The Python finalize() shim imported frappe.desk.page.setup_wizard.setup_wizard.setup_complete and called it as a plain function, which bypassed the HTTP/whitelist code path the default wizard uses. This made the setup_company stage silently drop the Company insert via make_records' blanket except, so downstream setup_wizard_complete hooks blew up with "Company Test Company AZ not found". Build all hardcoded AZ defaults on the client and call the stock endpoint over HTTP so we go through typing_validations, sanitize_input and parse_args exactly like the default wizard. api.py is left as a placeholder for future integrations. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
34f0ab2d15
commit
7662e784b2
|
|
@ -1,83 +1,4 @@
|
|||
from datetime import date
|
||||
|
||||
import frappe
|
||||
from frappe import _
|
||||
|
||||
|
||||
LANG_CODE_TO_NAME = {
|
||||
"en": "English",
|
||||
"az": "Azərbaycan dili",
|
||||
"ru": "Русский",
|
||||
}
|
||||
|
||||
|
||||
@frappe.whitelist()
|
||||
def finalize(language, company_name):
|
||||
"""Run setup_complete with hardcoded AZ defaults plus two fields from the UI.
|
||||
|
||||
For the skeleton test only. Once the real multi-step wizard is in place, this will
|
||||
grow into a Session-backed finalizer with rollback.
|
||||
"""
|
||||
if frappe.session.user != "Administrator":
|
||||
frappe.throw(_("Only Administrator may run setup"), frappe.PermissionError)
|
||||
|
||||
if frappe.is_setup_complete():
|
||||
return {"status": "ok", "already_complete": True}
|
||||
|
||||
language_name = LANG_CODE_TO_NAME.get(language, "English")
|
||||
company_name = (company_name or "").strip() or "Test Company AZ"
|
||||
year = date.today().year
|
||||
|
||||
# Emulate what the default wizard passes: the email of the current session user.
|
||||
# Frappe's create_or_update_user uses this to either update the existing user (if the
|
||||
# row already exists) or create one. Passing blanks here caused downstream hooks that
|
||||
# assume a real user-record to misbehave.
|
||||
session_user = frappe.db.get_value(
|
||||
"User", frappe.session.user, ["full_name", "email"], as_dict=True
|
||||
) or frappe._dict()
|
||||
|
||||
args = {
|
||||
"language": language_name,
|
||||
"country": "Azerbaijan",
|
||||
"timezone": "Asia/Baku",
|
||||
"currency": "AZN",
|
||||
"enable_telemetry": 0,
|
||||
"full_name": session_user.full_name or "Administrator",
|
||||
"email": session_user.email or "",
|
||||
"password": "",
|
||||
"company_name": company_name,
|
||||
"company_abbr": _make_abbr(company_name),
|
||||
"chart_of_accounts": "az_chart_of_accounts",
|
||||
"fy_start_date": f"{year}-01-01",
|
||||
"fy_end_date": f"{year}-12-31",
|
||||
"setup_demo": 0,
|
||||
}
|
||||
|
||||
from frappe.desk.page.setup_wizard.setup_wizard import setup_complete
|
||||
|
||||
try:
|
||||
result = setup_complete(args)
|
||||
except Exception:
|
||||
# Surface the traceback to the client so we can see exactly where setup fell over,
|
||||
# instead of the generic "Setup failed" wrapper.
|
||||
tb = frappe.get_traceback(with_context=True)
|
||||
frappe.log_error(title="Jey Wizard finalize failed", message=tb)
|
||||
return {
|
||||
"status": "error",
|
||||
"failure_message": frappe.response.get("setup_wizard_failure_message")
|
||||
or "Setup failed — see traceback",
|
||||
"traceback": tb,
|
||||
}
|
||||
|
||||
return result
|
||||
|
||||
|
||||
def _make_abbr(name: str) -> str:
|
||||
parts = [p for p in name.split() if p]
|
||||
if len(parts) >= 2:
|
||||
abbr = "".join(p[0] for p in parts[:3])
|
||||
elif parts:
|
||||
abbr = parts[0][:3]
|
||||
else:
|
||||
abbr = "CO"
|
||||
return abbr.upper()[:10]
|
||||
# The skeleton wizard now calls frappe.desk.page.setup_wizard.setup_wizard.setup_complete
|
||||
# directly from the client, so no server-side finalize shim is needed here.
|
||||
# Real integrations (tax API, session-backed steps, rollback) will live in this module
|
||||
# once the skeleton is verified.
|
||||
|
|
|
|||
|
|
@ -127,6 +127,42 @@ frappe.setup.SetupWizard = class JeySetupWizard {
|
|||
this.show_current_step();
|
||||
}
|
||||
|
||||
build_setup_args() {
|
||||
const LANG_CODE_TO_NAME = {
|
||||
en: "English",
|
||||
az: "Azərbaycan dili",
|
||||
ru: "Русский",
|
||||
};
|
||||
const company = (this.data.company_name || "").trim() || "Test Company AZ";
|
||||
const parts = company.split(/\s+/).filter(Boolean);
|
||||
const abbr = (
|
||||
parts.length >= 2
|
||||
? parts.slice(0, 3).map((p) => p[0]).join("")
|
||||
: (parts[0] || "CO").slice(0, 3)
|
||||
).toUpperCase().slice(0, 10);
|
||||
const year = new Date().getFullYear();
|
||||
const admin = frappe.boot.user || {};
|
||||
|
||||
// Mirror exactly what Frappe's default slides would submit. All AZ-specific
|
||||
// values are hardcoded; only language and company_name come from the UI.
|
||||
return {
|
||||
language: LANG_CODE_TO_NAME[this.data.language] || "English",
|
||||
country: "Azerbaijan",
|
||||
timezone: "Asia/Baku",
|
||||
currency: "AZN",
|
||||
enable_telemetry: 0,
|
||||
full_name: admin.full_name || "Administrator",
|
||||
email: admin.email || "admin@example.com",
|
||||
password: "",
|
||||
company_name: company,
|
||||
company_abbr: abbr,
|
||||
chart_of_accounts: "az_chart_of_accounts",
|
||||
fy_start_date: `${year}-01-01`,
|
||||
fy_end_date: `${year}-12-31`,
|
||||
setup_demo: 0,
|
||||
};
|
||||
}
|
||||
|
||||
finalize() {
|
||||
this.$wrap.find(".jey-body").html(
|
||||
`<p>Setting up your system. This can take a minute...</p>`
|
||||
|
|
@ -134,12 +170,14 @@ frappe.setup.SetupWizard = class JeySetupWizard {
|
|||
this.$wrap.find(".jey-nav").empty();
|
||||
this.$wrap.find(".jey-status").empty();
|
||||
|
||||
const values = this.build_setup_args();
|
||||
|
||||
frappe.call({
|
||||
method: "jey_wizard.api.finalize",
|
||||
args: {
|
||||
language: this.data.language,
|
||||
company_name: this.data.company_name,
|
||||
},
|
||||
// Call the stock Frappe endpoint directly — same entrypoint the default
|
||||
// wizard uses when the user hits Finish. This keeps typing_validations,
|
||||
// sanitize_input and parse_args in the normal HTTP/whitelist code path.
|
||||
method: "frappe.desk.page.setup_wizard.setup_wizard.setup_complete",
|
||||
args: { args: values },
|
||||
freeze: true,
|
||||
callback: (r) => {
|
||||
const m = r.message || {};
|
||||
|
|
@ -150,8 +188,14 @@ frappe.setup.SetupWizard = class JeySetupWizard {
|
|||
setTimeout(() => {
|
||||
window.location.href = "/app";
|
||||
}, 1500);
|
||||
} else if (m.status === "error") {
|
||||
this.show_error(m.failure_message || "Setup failed", m.traceback);
|
||||
} else if (m.status === "registered") {
|
||||
// Background-task mode: wait for socket events (we don't support that yet).
|
||||
this.show_error(
|
||||
"Setup is running in background mode, not supported by skeleton",
|
||||
JSON.stringify(m, null, 2)
|
||||
);
|
||||
} else if (m.fail !== undefined) {
|
||||
this.show_error(m.fail, JSON.stringify(m, null, 2));
|
||||
} else {
|
||||
this.show_error(
|
||||
"Setup returned unexpected response",
|
||||
|
|
@ -163,7 +207,7 @@ frappe.setup.SetupWizard = class JeySetupWizard {
|
|||
const resp = frappe.last_response || {};
|
||||
this.show_error(
|
||||
resp.setup_wizard_failure_message || "Setup failed",
|
||||
resp.exc || resp.exception || ""
|
||||
resp.exc || resp.exception || "(check server logs)"
|
||||
);
|
||||
},
|
||||
});
|
||||
|
|
|
|||
Loading…
Reference in New Issue