Add Asan Imza step that prefills company name from cert
New wizard step between language and company: 1. User enters phone + Asan user_id, hits "Send SMS" 2. ensure_asan_login (jey_wizard.api) creates/resets the single Asan Login record 3. invoice_az.auth.handle_authentication kicks off SMS, JS shows verification code 4. Polling loop calls poll_auth_status every 5s up to 20 attempts (~100s total) 5. On confirmation, get_auth_certificates fetches certs; UI filters to legal-only 6. User picks one; select_certificate + select_taxpayer run back-to-back 7. get_company_from_cert (jey_wizard.api) parses selected_certificate_json and pulls legalInfo.name + voen — no extra API call to the tax portal needed 8. Company step is then prefilled with that name (still editable) Test scope: legal entities only, single Asan Login reused/reset on phone change, no rollback machinery, no own renew_token scheduler (piggybacks on invoice_az's). required_apps now includes invoice_az since we call its auth methods directly. Bump to 0.0.5. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
f4e7d0a3b6
commit
21981a1032
|
|
@ -1 +1 @@
|
|||
__version__ = "0.0.4"
|
||||
__version__ = "0.0.5"
|
||||
|
|
|
|||
|
|
@ -1,4 +1,66 @@
|
|||
# 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.
|
||||
import json
|
||||
|
||||
import frappe
|
||||
from frappe import _
|
||||
|
||||
|
||||
@frappe.whitelist()
|
||||
def ensure_asan_login(phone, user_id):
|
||||
"""Reuse the single Asan Login record (or create one) and reset its auth state.
|
||||
|
||||
The wizard is one-shot, so we keep at most one record. If the user goes back to the
|
||||
credentials step and changes phone/user_id, we wipe the previous tokens so a fresh
|
||||
auth round can run cleanly.
|
||||
"""
|
||||
_only_admin()
|
||||
|
||||
existing = frappe.get_all("Asan Login", limit=1, fields=["name"])
|
||||
if existing:
|
||||
doc = frappe.get_doc("Asan Login", existing[0].name)
|
||||
else:
|
||||
doc = frappe.new_doc("Asan Login")
|
||||
|
||||
doc.phone = phone
|
||||
doc.user_id = user_id
|
||||
doc.auth_status = "Not Authenticated"
|
||||
doc.bearer_token = ""
|
||||
doc.main_token = ""
|
||||
doc.certificates_json = ""
|
||||
doc.selected_certificate = ""
|
||||
doc.selected_certificate_index = ""
|
||||
doc.selected_certificate_json = ""
|
||||
doc.choose_taxpayer_response = ""
|
||||
doc.verification_code = ""
|
||||
doc.is_default = 1
|
||||
doc.flags.ignore_permissions = True
|
||||
doc.save() if existing else doc.insert(ignore_permissions=True)
|
||||
|
||||
return {"name": doc.name}
|
||||
|
||||
|
||||
@frappe.whitelist()
|
||||
def get_company_from_cert(asan_login_name):
|
||||
"""After select_taxpayer ran, pull the company name + VÖEN from the chosen
|
||||
certificate. The cert payload from the tax portal already carries everything we need
|
||||
for the wizard's company step — no extra HTTP call required.
|
||||
"""
|
||||
_only_admin()
|
||||
|
||||
doc = frappe.get_doc("Asan Login", asan_login_name)
|
||||
cert = json.loads(doc.selected_certificate_json or "{}")
|
||||
|
||||
legal = cert.get("legalInfo") or {}
|
||||
# Wizard is legal-only by user requirement; we still defensively fall back to
|
||||
# individualInfo so the JS never crashes if a stray individual cert sneaks in.
|
||||
individual = cert.get("individualInfo") or {}
|
||||
|
||||
return {
|
||||
"taxpayer_type": cert.get("taxpayerType") or "",
|
||||
"company_name": legal.get("name") or individual.get("name") or "",
|
||||
"voen": legal.get("voen") or legal.get("tin") or individual.get("fin") or "",
|
||||
}
|
||||
|
||||
|
||||
def _only_admin():
|
||||
if frappe.session.user != "Administrator":
|
||||
frappe.throw(_("Only Administrator may run setup"), frappe.PermissionError)
|
||||
|
|
|
|||
|
|
@ -8,7 +8,9 @@ app_license = "unlicense"
|
|||
# Apps
|
||||
# ------------------
|
||||
|
||||
# required_apps = []
|
||||
# Asan Imza authentication in the wizard reuses invoice_az's Asan Login DocType and
|
||||
# its auth.* whitelisted methods directly. The wizard cannot run without invoice_az.
|
||||
required_apps = ["invoice_az"]
|
||||
|
||||
# Each item in the list will be shown as an app in the apps page
|
||||
# add_to_apps_screen = [
|
||||
|
|
|
|||
|
|
@ -7,10 +7,10 @@
|
|||
|
||||
frappe.provide("jey_wizard");
|
||||
|
||||
// Bump this string in every commit that changes wizard code. Displayed in the footer 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
|
||||
// sync with __version__ in jey_wizard/__init__.py.
|
||||
const JEY_WIZARD_VERSION = "0.0.4";
|
||||
const JEY_WIZARD_VERSION = "0.0.5";
|
||||
|
||||
// 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.
|
||||
|
|
@ -18,16 +18,32 @@ frappe.setup.slides = [];
|
|||
frappe.setup.slides_settings = [];
|
||||
frappe.setup.events = {};
|
||||
|
||||
// Asan polling: 20 attempts × 5s = ~100s window for the user to confirm on phone.
|
||||
// Mirrors invoice_az/asan_login.js timing.
|
||||
const ASAN_POLL_INTERVAL_MS = 5000;
|
||||
const ASAN_POLL_MAX_ATTEMPTS = 20;
|
||||
|
||||
frappe.setup.SetupWizard = class JeySetupWizard {
|
||||
constructor(settings = {}) {
|
||||
this.parent = settings.parent;
|
||||
this.values = {};
|
||||
this.data = {
|
||||
language: "az",
|
||||
company_name: "Test Company AZ",
|
||||
asan_phone: "",
|
||||
asan_user_id: "",
|
||||
asan_login_name: "",
|
||||
asan_voen: "",
|
||||
company_name: "",
|
||||
};
|
||||
// Sub-state for the asan step: input -> polling -> certs -> done
|
||||
this.asan_state = "input";
|
||||
this.asan_certificates = [];
|
||||
this.asan_bearer_token = "";
|
||||
this.asan_poll_attempts = 0;
|
||||
this.asan_poll_timer = null;
|
||||
|
||||
this.current_step = 0;
|
||||
this.steps = ["language", "company", "confirm"];
|
||||
this.steps = ["language", "asan", "company", "confirm"];
|
||||
this.render();
|
||||
}
|
||||
|
||||
|
|
@ -37,7 +53,7 @@ frappe.setup.SetupWizard = class JeySetupWizard {
|
|||
render() {
|
||||
const $parent = $(this.parent).empty();
|
||||
this.$wrap = $(`
|
||||
<div class="jey-wizard" style="max-width:560px;margin:80px auto;padding:32px;font-family:system-ui,sans-serif;position:relative">
|
||||
<div class="jey-wizard" style="max-width:640px;margin:80px auto;padding:32px;font-family:system-ui,sans-serif;position:relative">
|
||||
<div class="jey-version" style="position:absolute;top:8px;right:8px;background:#222;color:#fff;padding:4px 10px;border-radius:4px;font-size:12px;font-weight:600">v${JEY_WIZARD_VERSION}</div>
|
||||
<h1 style="margin-bottom:8px">Jey Setup</h1>
|
||||
<div class="jey-progress" style="color:#888;margin-bottom:24px"></div>
|
||||
|
|
@ -50,6 +66,7 @@ frappe.setup.SetupWizard = class JeySetupWizard {
|
|||
}
|
||||
|
||||
show_current_step() {
|
||||
this.stop_polling();
|
||||
const step = this.steps[this.current_step];
|
||||
const $body = this.$wrap.find(".jey-body").empty();
|
||||
const $nav = this.$wrap.find(".jey-nav").empty();
|
||||
|
|
@ -59,6 +76,21 @@ frappe.setup.SetupWizard = class JeySetupWizard {
|
|||
this.$wrap.find(".jey-status").empty();
|
||||
|
||||
if (step === "language") {
|
||||
this.render_language(($body));
|
||||
} else if (step === "asan") {
|
||||
this.render_asan($body);
|
||||
} else if (step === "company") {
|
||||
this.render_company($body);
|
||||
} else if (step === "confirm") {
|
||||
this.render_confirm($body);
|
||||
}
|
||||
|
||||
this.render_nav($nav, step);
|
||||
}
|
||||
|
||||
// ---------- step renderers ----------
|
||||
|
||||
render_language($body) {
|
||||
$body.html(`
|
||||
<label style="display:block;margin-bottom:8px">Wizard & site language</label>
|
||||
<select class="jey-language form-control" style="max-width:280px">
|
||||
|
|
@ -68,45 +100,147 @@ frappe.setup.SetupWizard = class JeySetupWizard {
|
|||
</select>
|
||||
`);
|
||||
$body.find(".jey-language").val(this.data.language);
|
||||
} else if (step === "company") {
|
||||
}
|
||||
|
||||
render_asan($body) {
|
||||
// Sub-state machine: input -> polling -> certs -> done.
|
||||
// All four sub-states render into the same $body slot.
|
||||
if (this.asan_state === "input") {
|
||||
$body.html(`
|
||||
<h3 style="margin-bottom:16px">Asan Imza authentication</h3>
|
||||
<div style="margin-bottom:12px">
|
||||
<label style="display:block;margin-bottom:4px">Phone number</label>
|
||||
<input type="text" class="jey-asan-phone form-control" style="max-width:280px" placeholder="e.g. 0501234567" />
|
||||
</div>
|
||||
<div style="margin-bottom:12px">
|
||||
<label style="display:block;margin-bottom:4px">User ID</label>
|
||||
<input type="text" class="jey-asan-userid form-control" style="max-width:280px" placeholder="ASAN ID" />
|
||||
</div>
|
||||
<button class="btn btn-primary jey-asan-auth" style="margin-top:8px">Send SMS to Asan Imza</button>
|
||||
`);
|
||||
$body.find(".jey-asan-phone").val(this.data.asan_phone);
|
||||
$body.find(".jey-asan-userid").val(this.data.asan_user_id);
|
||||
$body.find(".jey-asan-auth").on("click", () => this.start_asan_auth());
|
||||
} else if (this.asan_state === "polling") {
|
||||
const code = this.asan_verification_code || "";
|
||||
$body.html(`
|
||||
<h3 style="margin-bottom:16px">Confirm on your phone</h3>
|
||||
<p>Open the Asan Imza app on your phone and confirm the request.</p>
|
||||
${code
|
||||
? `<div style="margin:16px 0;padding:14px;background:#f7f7f7;border-radius:6px;text-align:center">
|
||||
<div style="color:#666;font-size:12px;margin-bottom:6px">Verification code</div>
|
||||
<div style="font-size:28px;letter-spacing:4px;font-weight:600">${frappe.utils.escape_html(String(code))}</div>
|
||||
</div>`
|
||||
: ""}
|
||||
<div style="display:flex;align-items:center;gap:12px;margin-top:16px">
|
||||
<div class="jey-spinner" style="width:18px;height:18px;border:3px solid #ddd;border-top-color:#444;border-radius:50%;animation:jey-spin 0.8s linear infinite"></div>
|
||||
<div class="jey-asan-poll-status" style="color:#666">Waiting for confirmation...</div>
|
||||
</div>
|
||||
<style>@keyframes jey-spin { to { transform: rotate(360deg) } }</style>
|
||||
`);
|
||||
} else if (this.asan_state === "certs") {
|
||||
const rows = (this.asan_certificates || []).map((c, i) => {
|
||||
const legal = c.legalInfo || {};
|
||||
const name = legal.name || "";
|
||||
const voen = legal.voen || legal.tin || "";
|
||||
const liquidated = c.liquidated ? " <i style='color:#c00'>(liquidated)</i>" : "";
|
||||
return `
|
||||
<tr>
|
||||
<td style="padding:8px;border-bottom:1px solid #eee">${frappe.utils.escape_html(name)}${liquidated}</td>
|
||||
<td style="padding:8px;border-bottom:1px solid #eee;font-family:monospace">${frappe.utils.escape_html(voen)}</td>
|
||||
<td style="padding:8px;border-bottom:1px solid #eee;text-align:right">
|
||||
<button class="btn btn-sm btn-primary jey-cert-pick" data-idx="${i}">Select</button>
|
||||
</td>
|
||||
</tr>
|
||||
`;
|
||||
}).join("");
|
||||
|
||||
$body.html(`
|
||||
<h3 style="margin-bottom:16px">Select your company</h3>
|
||||
${rows
|
||||
? `<table style="width:100%;border-collapse:collapse">
|
||||
<thead><tr>
|
||||
<th style="text-align:left;padding:8px;border-bottom:2px solid #ccc">Company</th>
|
||||
<th style="text-align:left;padding:8px;border-bottom:2px solid #ccc">VÖEN</th>
|
||||
<th style="padding:8px;border-bottom:2px solid #ccc"></th>
|
||||
</tr></thead>
|
||||
<tbody>${rows}</tbody>
|
||||
</table>`
|
||||
: `<p style="color:#c00">No legal-entity certificates available on this Asan ID. Use a phone/ID linked to a company.</p>`}
|
||||
`);
|
||||
$body.find(".jey-cert-pick").on("click", (e) => {
|
||||
const idx = parseInt($(e.currentTarget).attr("data-idx"), 10);
|
||||
this.pick_certificate(idx);
|
||||
});
|
||||
} else if (this.asan_state === "done") {
|
||||
$body.html(`
|
||||
<h3 style="margin-bottom:16px;color:#080">✓ Authenticated</h3>
|
||||
<p>Pulled from the tax portal:</p>
|
||||
<ul style="line-height:1.8">
|
||||
<li><b>Company:</b> ${frappe.utils.escape_html(this.data.company_name)}</li>
|
||||
<li><b>VÖEN:</b> ${frappe.utils.escape_html(this.data.asan_voen)}</li>
|
||||
</ul>
|
||||
<p style="color:#666;margin-top:12px">Click Next to continue — you'll be able to edit the company name on the next step.</p>
|
||||
`);
|
||||
}
|
||||
}
|
||||
|
||||
render_company($body) {
|
||||
$body.html(`
|
||||
<label style="display:block;margin-bottom:8px">Company name</label>
|
||||
<input type="text" class="jey-company-name form-control" style="max-width:380px" />
|
||||
${this.data.asan_voen
|
||||
? `<div style="color:#888;font-size:12px;margin-top:6px">VÖEN ${frappe.utils.escape_html(this.data.asan_voen)} (from Asan)</div>`
|
||||
: ""}
|
||||
`);
|
||||
$body.find(".jey-company-name").val(this.data.company_name);
|
||||
} else if (step === "confirm") {
|
||||
}
|
||||
|
||||
render_confirm($body) {
|
||||
$body.html(`
|
||||
<h3>Confirm</h3>
|
||||
<ul style="line-height:1.8">
|
||||
<li><b>Language:</b> ${this.data.language}</li>
|
||||
<li><b>Company:</b> ${frappe.utils.escape_html(this.data.company_name)}</li>
|
||||
${this.data.asan_voen
|
||||
? `<li><b>VÖEN:</b> ${frappe.utils.escape_html(this.data.asan_voen)}</li>`
|
||||
: ""}
|
||||
<li><b>Country:</b> Azerbaijan <i>(hardcoded)</i></li>
|
||||
<li><b>Currency:</b> AZN <i>(hardcoded)</i></li>
|
||||
<li><b>Timezone:</b> Asia/Baku <i>(hardcoded)</i></li>
|
||||
<li><b>Chart of Accounts:</b> az_chart_of_accounts <i>(hardcoded)</i></li>
|
||||
<li><b>Chart of Accounts:</b> Azerbaijan Republic <i>(hardcoded)</i></li>
|
||||
<li><b>Fiscal Year:</b> current year <i>(hardcoded)</i></li>
|
||||
<li><b>Telemetry:</b> off <i>(hardcoded)</i></li>
|
||||
<li><b>Demo data:</b> off <i>(hardcoded)</i></li>
|
||||
</ul>
|
||||
`);
|
||||
}
|
||||
|
||||
render_nav($nav, step) {
|
||||
// On the asan step we don't show standard Next — auth flow drives navigation
|
||||
// internally and only enables Next when the user reaches the "done" sub-state.
|
||||
const showStdNext =
|
||||
this.current_step < this.steps.length - 1 &&
|
||||
!(step === "asan" && this.asan_state !== "done");
|
||||
const showFinish = this.current_step === this.steps.length - 1;
|
||||
|
||||
if (this.current_step > 0) {
|
||||
$(`<button class="btn btn-default jey-back">Back</button>`)
|
||||
.appendTo($nav)
|
||||
.on("click", () => this.prev());
|
||||
}
|
||||
if (this.current_step < this.steps.length - 1) {
|
||||
if (showStdNext) {
|
||||
$(`<button class="btn btn-primary jey-next">Next</button>`)
|
||||
.appendTo($nav)
|
||||
.on("click", () => this.next());
|
||||
} else {
|
||||
}
|
||||
if (showFinish) {
|
||||
$(`<button class="btn btn-primary jey-finish">Finish</button>`)
|
||||
.appendTo($nav)
|
||||
.on("click", () => this.finalize());
|
||||
}
|
||||
}
|
||||
|
||||
// ---------- step navigation ----------
|
||||
|
||||
capture_current() {
|
||||
const step = this.steps[this.current_step];
|
||||
if (step === "language") {
|
||||
|
|
@ -129,10 +263,214 @@ frappe.setup.SetupWizard = class JeySetupWizard {
|
|||
}
|
||||
|
||||
prev() {
|
||||
// Going back to/from asan resets its sub-state so the user can re-enter cleanly.
|
||||
const step = this.steps[this.current_step];
|
||||
if (step === "asan") {
|
||||
this.asan_state = "input";
|
||||
}
|
||||
this.current_step--;
|
||||
// Same when stepping back ONTO asan from the company step — keep it simple,
|
||||
// don't try to resume polling/cert state mid-flow.
|
||||
if (this.steps[this.current_step] === "asan") {
|
||||
this.asan_state = "input";
|
||||
}
|
||||
this.show_current_step();
|
||||
}
|
||||
|
||||
// ---------- asan flow ----------
|
||||
|
||||
start_asan_auth() {
|
||||
const phone = (this.$wrap.find(".jey-asan-phone").val() || "").trim();
|
||||
const userId = (this.$wrap.find(".jey-asan-userid").val() || "").trim();
|
||||
if (!phone || !userId) {
|
||||
this.$wrap.find(".jey-status").text("Phone and User ID are required");
|
||||
return;
|
||||
}
|
||||
this.data.asan_phone = phone;
|
||||
this.data.asan_user_id = userId;
|
||||
this.$wrap.find(".jey-status").empty();
|
||||
|
||||
// Step 1: ensure Asan Login record exists & is reset, then trigger SMS.
|
||||
frappe.call({
|
||||
method: "jey_wizard.api.ensure_asan_login",
|
||||
args: { phone, user_id: userId },
|
||||
freeze: true,
|
||||
freeze_message: "Preparing...",
|
||||
callback: (r) => {
|
||||
const m = r.message || {};
|
||||
if (!m.name) {
|
||||
this.$wrap.find(".jey-status").text("Failed to create Asan Login record");
|
||||
return;
|
||||
}
|
||||
this.data.asan_login_name = m.name;
|
||||
this.trigger_asan_authentication();
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
trigger_asan_authentication() {
|
||||
frappe.call({
|
||||
method: "invoice_az.auth.handle_authentication",
|
||||
args: { asan_login_name: this.data.asan_login_name },
|
||||
freeze: true,
|
||||
freeze_message: "Sending request to Asan Imza...",
|
||||
callback: (r) => {
|
||||
const m = r.message || {};
|
||||
if (!m.success) {
|
||||
this.$wrap.find(".jey-status").text(
|
||||
m.message || "Failed to start authentication"
|
||||
);
|
||||
return;
|
||||
}
|
||||
this.asan_bearer_token = m.bearer_token || "";
|
||||
this.asan_verification_code = m.verification_code || "";
|
||||
this.asan_state = "polling";
|
||||
this.asan_poll_attempts = 0;
|
||||
this.show_current_step();
|
||||
this.poll_asan();
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
poll_asan() {
|
||||
if (this.asan_poll_attempts >= ASAN_POLL_MAX_ATTEMPTS) {
|
||||
this.$wrap.find(".jey-asan-poll-status").html(
|
||||
`<span style="color:#c00">Timeout — confirmation not received within ~100 seconds.</span>`
|
||||
);
|
||||
this.$wrap.find(".jey-spinner").remove();
|
||||
this.$wrap.find(".jey-status").empty();
|
||||
this.$wrap.find(".jey-nav")
|
||||
.empty()
|
||||
.html(
|
||||
`<button class="btn btn-default jey-back">Back</button>` +
|
||||
`<button class="btn btn-primary jey-asan-restart">Try again</button>`
|
||||
)
|
||||
.find(".jey-back").on("click", () => this.prev()).end()
|
||||
.find(".jey-asan-restart").on("click", () => {
|
||||
this.asan_state = "input";
|
||||
this.show_current_step();
|
||||
});
|
||||
return;
|
||||
}
|
||||
this.asan_poll_attempts += 1;
|
||||
|
||||
frappe.call({
|
||||
method: "invoice_az.auth.poll_auth_status",
|
||||
args: {
|
||||
asan_login_name: this.data.asan_login_name,
|
||||
bearer_token: this.asan_bearer_token,
|
||||
},
|
||||
callback: (r) => {
|
||||
if (this.asan_state !== "polling") return; // user navigated away
|
||||
const m = r.message || {};
|
||||
if (m.authenticated) {
|
||||
this.fetch_certificates();
|
||||
} else {
|
||||
this.asan_poll_timer = setTimeout(
|
||||
() => this.poll_asan(),
|
||||
ASAN_POLL_INTERVAL_MS
|
||||
);
|
||||
}
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
stop_polling() {
|
||||
if (this.asan_poll_timer) {
|
||||
clearTimeout(this.asan_poll_timer);
|
||||
this.asan_poll_timer = null;
|
||||
}
|
||||
}
|
||||
|
||||
fetch_certificates() {
|
||||
this.stop_polling();
|
||||
this.$wrap.find(".jey-asan-poll-status").text("Loading certificates...");
|
||||
frappe.call({
|
||||
method: "invoice_az.auth.get_auth_certificates",
|
||||
args: { asan_login_name: this.data.asan_login_name },
|
||||
callback: (r) => {
|
||||
const m = r.message || {};
|
||||
if (!m.success) {
|
||||
this.$wrap.find(".jey-status").text(
|
||||
m.message || "Failed to load certificates"
|
||||
);
|
||||
return;
|
||||
}
|
||||
const all = m.certificates || [];
|
||||
// Wizard scope: legal entities only (per requirement). Filter and warn
|
||||
// if nothing remains so the user knows why the list is empty.
|
||||
this.asan_certificates = all.filter(
|
||||
(c) => c.taxpayerType === "legal"
|
||||
);
|
||||
this.asan_state = "certs";
|
||||
this.show_current_step();
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
pick_certificate(idx) {
|
||||
const cert = this.asan_certificates[idx];
|
||||
if (!cert) return;
|
||||
const legal = cert.legalInfo || {};
|
||||
const certName = `${legal.name || ""} (${legal.voen || legal.tin || ""})`;
|
||||
|
||||
frappe.call({
|
||||
method: "invoice_az.auth.select_certificate",
|
||||
args: {
|
||||
asan_login_name: this.data.asan_login_name,
|
||||
certificate_data: cert,
|
||||
certificate_name: certName,
|
||||
},
|
||||
freeze: true,
|
||||
freeze_message: "Selecting certificate...",
|
||||
callback: (r) => {
|
||||
const m = r.message || {};
|
||||
if (!m.success) {
|
||||
this.$wrap.find(".jey-status").text(
|
||||
m.message || "Failed to select certificate"
|
||||
);
|
||||
return;
|
||||
}
|
||||
this.run_select_taxpayer();
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
run_select_taxpayer() {
|
||||
frappe.call({
|
||||
method: "invoice_az.auth.select_taxpayer",
|
||||
args: { asan_login_name: this.data.asan_login_name },
|
||||
freeze: true,
|
||||
freeze_message: "Choosing taxpayer & getting main token...",
|
||||
callback: (r) => {
|
||||
const m = r.message || {};
|
||||
if (!m.success) {
|
||||
this.$wrap.find(".jey-status").text(
|
||||
m.message || "Failed to choose taxpayer"
|
||||
);
|
||||
return;
|
||||
}
|
||||
this.fetch_company_from_cert();
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
fetch_company_from_cert() {
|
||||
frappe.call({
|
||||
method: "jey_wizard.api.get_company_from_cert",
|
||||
args: { asan_login_name: this.data.asan_login_name },
|
||||
callback: (r) => {
|
||||
const m = r.message || {};
|
||||
this.data.company_name = m.company_name || this.data.company_name;
|
||||
this.data.asan_voen = m.voen || "";
|
||||
this.asan_state = "done";
|
||||
this.show_current_step();
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
// ---------- finalization ----------
|
||||
|
||||
build_setup_args() {
|
||||
const LANG_CODE_TO_NAME = {
|
||||
en: "English",
|
||||
|
|
@ -198,7 +536,6 @@ frappe.setup.SetupWizard = class JeySetupWizard {
|
|||
window.location.href = "/app";
|
||||
}, 1500);
|
||||
} 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)
|
||||
|
|
|
|||
Loading…
Reference in New Issue