From 6422129c537c059b0203907b8a0ca37f30aade30 Mon Sep 17 00:00:00 2001
From: Ali <010109ali@gmail.com>
Date: Fri, 24 Apr 2026 11:59:53 +0000
Subject: [PATCH] Phone prefix picker, certificate-pick confirmation &
rollback, cleaner AMAS step
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
- Phone input split: hardcoded +994, 8-option prefix dropdown (050/051/010/
055/060/070/077/099 shown, 2-digit values submitted), 7-digit local number
with numeric-only input filter. Full E.164 assembled on submit.
- Certificate selection now prompts a confirmation dialog before the cascade
(select_taxpayer → fetch_etaxes_data) fires. Asan/done sub-state gets a
"Wrong company? Change" button that wipes the Asan Login tokens and every
cache field (via new jey_wizard.api.reset_company_selection), then returns
the user to the certs list without another phone tap.
- Employees intro no longer shows a dedicated Skip button — Next in the nav
acts as skip (and clears any stale AMAS cache when used that way).
- Employees done no longer shows "Reload from ƏMAS" — Back in the nav is
the single way to rewind. The rogue button was sending users back into a
stale intro state with leftover buttons.
---
jey_wizard/__init__.py | 2 +-
jey_wizard/api.py | 39 +++++++
jey_wizard/public/js/jey_setup.js | 162 +++++++++++++++++++++++-------
jey_wizard/translations/az.csv | 12 +++
jey_wizard/translations/ru.csv | 12 +++
5 files changed, 188 insertions(+), 39 deletions(-)
diff --git a/jey_wizard/__init__.py b/jey_wizard/__init__.py
index a73339b..00ec2dc 100644
--- a/jey_wizard/__init__.py
+++ b/jey_wizard/__init__.py
@@ -1 +1 @@
-__version__ = "0.0.8"
+__version__ = "0.0.9"
diff --git a/jey_wizard/api.py b/jey_wizard/api.py
index 9a8f17f..bfef8ba 100644
--- a/jey_wizard/api.py
+++ b/jey_wizard/api.py
@@ -61,6 +61,45 @@ def get_company_from_cert(asan_login_name):
}
+@frappe.whitelist()
+def reset_company_selection(asan_login_name):
+ """Roll back after a wrong certificate pick: wipe main_token / selected cert on
+ Asan Login and clear the wizard cache (both e-taxes and AMAS) so the user can
+ pick a different taxpayer and re-fetch from scratch. Certificates list itself
+ is not touched — it's still valid for the same Asan ID.
+ """
+ _only_admin()
+
+ doc = frappe.get_doc("Asan Login", asan_login_name)
+ doc.main_token = ""
+ doc.selected_certificate = ""
+ doc.selected_certificate_index = ""
+ doc.selected_certificate_json = ""
+ doc.choose_taxpayer_response = ""
+ doc.flags.ignore_permissions = True
+ doc.save()
+
+ cache = frappe.get_single("Jey Wizard Etaxes Cache")
+ for field in (
+ "objects_json",
+ "cash_registers_json",
+ "pos_terminals_json",
+ "bank_accounts_json",
+ "obligation_pacts_json",
+ "presented_certs_json",
+ "amas_selected_employees_json",
+ "last_summary",
+ "last_error",
+ ):
+ cache.set(field, "")
+ cache.fetched_at = None
+ cache.flags.ignore_permissions = True
+ cache.save()
+ frappe.db.commit()
+
+ return {"ok": True}
+
+
def _only_admin():
if frappe.session.user != "Administrator":
frappe.throw(_("Only Administrator may run setup"), frappe.PermissionError)
diff --git a/jey_wizard/public/js/jey_setup.js b/jey_wizard/public/js/jey_setup.js
index dcc64e2..1010f70 100644
--- a/jey_wizard/public/js/jey_setup.js
+++ b/jey_wizard/public/js/jey_setup.js
@@ -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.0.8";
+const JEY_WIZARD_VERSION = "0.0.9";
// 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.
@@ -41,6 +41,8 @@ frappe.setup.SetupWizard = class JeySetupWizard {
const year = new Date().getFullYear();
this.data = {
language: "az",
+ asan_prefix: "",
+ asan_local_number: "",
asan_phone: "",
asan_user_id: "",
asan_login_name: "",
@@ -143,11 +145,23 @@ frappe.setup.SetupWizard = class JeySetupWizard {
render_asan($body) {
if (this.asan_state === "input") {
+ // +994 is hardcoded (every Azerbaijani mobile uses it); the visible
+ // parts are the carrier prefix (dropdown) and the 7-digit local number.
+ const prefixOptions = ["50", "51", "10", "55", "60", "70", "77", "99"]
+ .map((p) => ``)
+ .join("");
$body.html(`
${__("Asan Imza authentication")}
-
+
+ +994
+
+
+
@@ -155,8 +169,14 @@ frappe.setup.SetupWizard = class JeySetupWizard {
${__("Heads up:")} ${__("You will receive a second confirmation request on your phone from ASAN Sign / MyGovID. This is separate from the one you just approved for the tax portal.")}
-
${__("This step is optional. Skip it if your company has no employees or is not registered with ƏMAS.")}
+
${__("This step is optional. Use Next to skip if your company has no employees or is not registered with ƏMAS.")}
${__("All employees will be imported into ƏMAS Employees and the standard Employee doctype after the wizard finishes.")}
-
-
-
`);
- $body.find(".jey-amas-back-intro").on("click", () => {
- this.amas_state = "intro";
- this.show_current_step();
- });
+ // No extra buttons here — Back/Next in nav cover everything.
}
}
@@ -437,9 +453,10 @@ frappe.setup.SetupWizard = class JeySetupWizard {
// surfaces a standard Next.
showStdNext = this.asan_state === "done";
} else if (step === "employees") {
- // On intro the user uses explicit Skip / Load buttons, not Next.
- // Only the "done" sub-state surfaces a standard Next to move on.
- showStdNext = this.amas_state === "done";
+ // Next is visible on intro (acts as "skip this step") and on done
+ // (move on with the loaded selection). During auth/loading/etc it
+ // stays hidden so the flow can't be short-circuited mid-request.
+ showStdNext = this.amas_state === "intro" || this.amas_state === "done";
} else {
showStdNext = true;
}
@@ -488,6 +505,24 @@ frappe.setup.SetupWizard = class JeySetupWizard {
next() {
if (!this.capture_current()) return;
+ const step = this.steps[this.current_step];
+ // Skip-by-Next on employees/intro with no prior selection: clear any stale
+ // cache so materialize_after_setup doesn't re-import a previous run's data.
+ if (
+ step === "employees" &&
+ this.amas_state === "intro" &&
+ (this.data.amas_selected_employees || []).length === 0
+ ) {
+ this.data.amas_skipped = true;
+ frappe.call({
+ method: "jey_wizard.amas.clear_amas_cache",
+ callback: () => {
+ this.current_step++;
+ this.show_current_step();
+ },
+ });
+ return;
+ }
this.current_step++;
this.show_current_step();
}
@@ -536,14 +571,26 @@ frappe.setup.SetupWizard = class JeySetupWizard {
// ---------- asan flow ----------
start_asan_auth() {
- const phone = (this.$wrap.find(".jey-asan-phone").val() || "").trim();
+ const prefix = (this.$wrap.find(".jey-asan-prefix").val() || "").trim();
+ const local = (this.$wrap.find(".jey-asan-phone").val() || "").replace(/\D/g, "");
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"));
+ if (!prefix) {
+ this.$wrap.find(".jey-status").text(__("Select a phone prefix"));
return;
}
- this.data.asan_phone = phone;
+ if (local.length !== 7) {
+ this.$wrap.find(".jey-status").text(__("Phone number must be 7 digits"));
+ return;
+ }
+ if (!userId) {
+ this.$wrap.find(".jey-status").text(__("User ID is required"));
+ return;
+ }
+ this.data.asan_prefix = prefix;
+ this.data.asan_local_number = local;
+ this.data.asan_phone = `+994${prefix}${local}`;
this.data.asan_user_id = userId;
+ const phone = this.data.asan_phone;
this.$wrap.find(".jey-status").empty();
frappe.call({
@@ -656,6 +703,21 @@ 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 || ""})`;
+ // Cert selection triggers a cascade (select_taxpayer → fetch_etaxes_data) that
+ // writes a lot of cached data. Picking the wrong cert is reversible but costs
+ // another phone tap, so confirm explicitly first.
+ frappe.confirm(
+ __("You selected {0}. Is this the right company? Switching later will require clearing loaded data and re-authenticating.", [frappe.utils.escape_html(certName)]),
+ () => this.pick_certificate(idx),
+ () => {}
+ );
+ }
+
pick_certificate(idx) {
const cert = this.asan_certificates[idx];
if (!cert) return;
@@ -714,6 +776,44 @@ frappe.setup.SetupWizard = class JeySetupWizard {
});
}
+ reset_company_selection() {
+ // Reached from asan/done when the user realizes they picked the wrong cert.
+ // Warn explicitly — we wipe both the e-taxes cache and the AMAS selection,
+ // and clear the certificate/main_token on Asan Login. Cert list stays in
+ // this.asan_certificates so the user can pick again without re-polling.
+ frappe.confirm(
+ __("This will delete all loaded tax portal data and any ƏMAS employee selection. You will need to pick a certificate again. Continue?"),
+ () => {
+ frappe.call({
+ method: "jey_wizard.api.reset_company_selection",
+ args: { asan_login_name: this.data.asan_login_name },
+ freeze: true,
+ freeze_message: __("Clearing..."),
+ callback: (r) => {
+ const m = r.message || {};
+ if (!m.ok) {
+ this.$wrap.find(".jey-status").text(__("Failed to clear data."));
+ return;
+ }
+ this.data.company_name = "";
+ this.data.asan_voen = "";
+ this.data.amas_selected_employees = [];
+ this.data.amas_skipped = false;
+ this.data.amas_organization_name = "";
+ this.amas_employees = [];
+ this.amas_existing_map = {};
+ this.amas_state = "intro";
+ this.asan_fetch_summary = {};
+ this.asan_fetch_errors = {};
+ this.asan_state = "certs";
+ this.show_current_step();
+ },
+ });
+ },
+ () => {}
+ );
+ }
+
fetch_etaxes_data() {
frappe.call({
method: "jey_wizard.etaxes.fetch_all_etaxes",
@@ -735,20 +835,6 @@ frappe.setup.SetupWizard = class JeySetupWizard {
// ---------- AMAS flow ----------
- amas_skip() {
- this.data.amas_skipped = true;
- this.data.amas_selected_employees = [];
- frappe.call({
- method: "jey_wizard.amas.clear_amas_cache",
- freeze: true,
- freeze_message: __("Skipping..."),
- callback: () => {
- this.current_step++;
- this.show_current_step();
- },
- });
- }
-
amas_start_auth() {
if (!this.data.asan_login_name) {
this.$wrap.find(".jey-status").text(__("Finish Asan Imza authentication first."));
diff --git a/jey_wizard/translations/az.csv b/jey_wizard/translations/az.csv
index 9a77170..4394ae2 100644
--- a/jey_wizard/translations/az.csv
+++ b/jey_wizard/translations/az.csv
@@ -4,6 +4,18 @@ Wizard & site language,Sihirbaz və sayt dili
Asan Imza authentication,Asan İmza ilə giriş
Phone number,Telefon nömrəsi
e.g. 0501234567,məsələn 0501234567
+Prefix,Prefiks
+7 digits,7 rəqəm
+Select a phone prefix,Telefon prefiksini seçin
+Phone number must be 7 digits,Telefon nömrəsi 7 rəqəmdən ibarət olmalıdır
+User ID is required,User ID tələb olunur
+"You selected {0}. Is this the right company? Switching later will require clearing loaded data and re-authenticating.","Siz {0} seçdiniz. Bu, düzgün şirkətdir? Sonradan dəyişmək üçün yüklənmiş məlumatları silmək və yenidən giriş etmək lazım olacaq."
+"This will delete all loaded tax portal data and any ƏMAS employee selection. You will need to pick a certificate again. Continue?","Vergi portalından yüklənmiş bütün məlumatlar və seçilmiş ƏMAS işçiləri silinəcək. Sertifikatı yenidən seçmək lazım gələcək. Davam edilsin?"
+Clearing...,Silinir...
+Failed to clear data.,Məlumatı silmək alınmadı.
+Wrong company? Change,Yanlış şirkət? Dəyiş
+Use Next to skip if your company has no employees or is not registered with ƏMAS.,"Şirkətinizdə işçi yoxdursa və ya ƏMAS-da qeydiyyatda deyilsə, «İrəli» düyməsi ilə ötürün."
+Already loaded:,Artıq yükləndi:
User ID,İstifadəçi ID
ASAN ID,ASAN ID
Send request to Asan Imza,Asan İmza-ya sorğu göndər
diff --git a/jey_wizard/translations/ru.csv b/jey_wizard/translations/ru.csv
index 97acbdc..b012aa4 100644
--- a/jey_wizard/translations/ru.csv
+++ b/jey_wizard/translations/ru.csv
@@ -4,6 +4,18 @@ Wizard & site language,Язык мастера и сайта
Asan Imza authentication,Аутентификация Asan İmza
Phone number,Номер телефона
e.g. 0501234567,например 0501234567
+Prefix,Префикс
+7 digits,7 цифр
+Select a phone prefix,Выберите префикс номера
+Phone number must be 7 digits,Номер телефона должен содержать 7 цифр
+User ID is required,User ID обязателен
+"You selected {0}. Is this the right company? Switching later will require clearing loaded data and re-authenticating.","Вы выбрали {0}. Это нужная компания? Смена выбора позже потребует удаления загруженных данных и повторной авторизации."
+"This will delete all loaded tax portal data and any ƏMAS employee selection. You will need to pick a certificate again. Continue?","Будут удалены все загруженные данные с налогового портала и выбранные сотрудники ƏMAS. Вам потребуется выбрать сертификат заново. Продолжить?"
+Clearing...,Очистка...
+Failed to clear data.,Не удалось очистить данные.
+Wrong company? Change,Не та компания? Сменить
+Use Next to skip if your company has no employees or is not registered with ƏMAS.,"Нажмите «Далее», чтобы пропустить, если у вашей компании нет сотрудников или она не зарегистрирована в ƏMAS."
+Already loaded:,Уже загружено:
User ID,User ID
ASAN ID,ASAN ID
Send request to Asan Imza,Отправить запрос в Asan İmza