Make Asan certificate selection step explicit

The cert picker has been a sub-state of the asan step since day one
(asan_state === "certs"), but in the wizard it presented as "Select
your company" with a single Select button per row, which read more
like a continuation of the company step than a discrete certificate
choice. Polished it so the step is unmistakable and one-click:

- Header changed to "Choose certificate" with a count line ("Found N
  certificate(s) linked to this Asan ID. Pick the one whose VÖEN
  matches the company you want to set up.").
- Each row now carries a Legal entity / Individual tag pill so the
  user sees what type they're picking.
- Click on Select commits immediately — dropped the second
  frappe.confirm dialog. Re-picking still requires an SMS via
  reset_company_selection, so the picker itself IS the confirmation;
  a noisy confirm right after wasn't adding safety.
- Save the chosen cert label (holder + VÖEN) on this.data so the
  Confirm screen surfaces it as "Asan certificate: NAME (VÖEN)" —
  proves to the user during review that the right cert is locked in.

confirm_and_pick_certificate removed (dead now). Background ƏMAS
import behaviour unchanged: still fire-and-forget via
import_bulk_employees.
This commit is contained in:
Ali 2026-05-01 15:12:56 +00:00
parent 10c07dfc0b
commit 8fe891d898
2 changed files with 40 additions and 26 deletions

View File

@ -1 +1 @@
__version__ = "0.1.19"
__version__ = "0.1.20"

View File

@ -10,7 +10,7 @@ frappe.provide("jey_wizard");
// 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.1.19";
const JEY_WIZARD_VERSION = "0.1.20";
// 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.
@ -47,6 +47,7 @@ frappe.setup.SetupWizard = class JeySetupWizard {
asan_user_id: "",
asan_login_name: "",
asan_voen: "",
asan_selected_cert_label: "",
company_name: "",
company_abbr: "",
company_abbr_user_edited: false,
@ -212,16 +213,28 @@ frappe.setup.SetupWizard = class JeySetupWizard {
</div>
`);
} else if (this.asan_state === "certs") {
const rows = (this.asan_certificates || []).map((c, i) => {
// Always-visible certificate picker. invoice_az's chooseTaxpayer call
// burns the bearer_token, so reselecting requires another SMS (handled
// via reset_company_selection). The picker is the moment to make sure
// the user lands on the right VÖEN.
const certs = this.asan_certificates || [];
const rows = certs.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>` : "";
const individual = c.individualInfo || {};
const isLegal = (c.taxpayerType === "legal") || !!legal.name;
const name = legal.name || individual.name || `${individual.firstName || ""} ${individual.lastName || ""}`.trim() || "—";
const voen = legal.voen || legal.tin || individual.fin || "";
const tag = isLegal
? `<span style="font-size:11px;color:#2563eb;background:#eff6ff;padding:2px 6px;border-radius:3px;margin-left:6px">${__("Legal entity")}</span>`
: `<span style="font-size:11px;color:#9333ea;background:#faf5ff;padding:2px 6px;border-radius:3px;margin-left:6px">${__("Individual")}</span>`;
const liquidated = c.liquidated
? ` <span style="color:#c00;font-style:italic">(${__("liquidated")})</span>`
: "";
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">
<td style="padding:10px 8px;border-bottom:1px solid #eee">${frappe.utils.escape_html(name)}${tag}${liquidated}</td>
<td style="padding:10px 8px;border-bottom:1px solid #eee;font-family:monospace">${frappe.utils.escape_html(voen)}</td>
<td style="padding:10px 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>
@ -229,21 +242,23 @@ frappe.setup.SetupWizard = class JeySetupWizard {
}).join("");
$body.html(`
<h3 style="margin-bottom:16px">${__("Select your company")}</h3>
<h3 style="margin-bottom:8px">${__("Choose certificate")}</h3>
<p style="color:#666;margin-bottom:16px">${__("Found {0} certificate(s) linked to this Asan ID. Pick the one whose VÖEN matches the company you want to set up.", [certs.length])}</p>
${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">${__("Holder")}</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>`
</table>
<div style="color:#888;font-size:12px;margin-top:10px">${__("Picking the wrong one means another phone tap to start over — choose carefully.")}</div>`
: `<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.confirm_and_pick_certificate(idx);
this.pick_certificate(idx);
});
} else if (this.asan_state === "fetching") {
$body.html(`
@ -639,6 +654,9 @@ frappe.setup.SetupWizard = class JeySetupWizard {
${this.data.asan_voen
? `<li><b>${__("VÖEN")}:</b> ${frappe.utils.escape_html(this.data.asan_voen)}</li>`
: ""}
${this.data.asan_selected_cert_label
? `<li><b>${__("Asan certificate")}:</b> ${frappe.utils.escape_html(this.data.asan_selected_cert_label)}</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>${__("Accounting method")}:</b> ${frappe.utils.escape_html(this.data.accounting_method || "Hesablama metodu")}</li>
@ -970,23 +988,19 @@ frappe.setup.SetupWizard = class JeySetupWizard {
});
}
confirm_and_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.confirm(
__("Confirm selection: {0}?", [frappe.utils.escape_html(certName)]),
() => this.pick_certificate(idx),
() => {}
);
}
pick_certificate(idx) {
// Single click commits — the picker IS the confirmation. Re-picking
// requires reset_company_selection (another SMS), so the user is
// already incentivised to choose carefully.
const cert = this.asan_certificates[idx];
if (!cert) return;
const legal = cert.legalInfo || {};
const certName = `${legal.name || ""} (${legal.voen || legal.tin || ""})`;
const individual = cert.individualInfo || {};
const holder = legal.name || individual.name || "";
const voen = legal.voen || legal.tin || individual.fin || "";
const certName = `${holder} (${voen})`;
// Persist label for the Confirm step's review block.
this.data.asan_selected_cert_label = certName;
frappe.call({
method: "invoice_az.auth.select_certificate",