diff --git a/jey_wizard/api.py b/jey_wizard/api.py index e90367b..60b6dac 100644 --- a/jey_wizard/api.py +++ b/jey_wizard/api.py @@ -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. diff --git a/jey_wizard/public/js/jey_setup.js b/jey_wizard/public/js/jey_setup.js index 0feac78..3eed93b 100644 --- a/jey_wizard/public/js/jey_setup.js +++ b/jey_wizard/public/js/jey_setup.js @@ -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( `
Setting up your system. This can take a minute...
` @@ -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)" ); }, });