Fetch e-taxes data after Asan auth, cache for wizard, materialize on finalize
Two-phase data loading so future wizard steps can consume tax-portal data before Company exists, while still ending up with properly-linked E-Taxes DocType records. Phase A (Asan step, after main_token): - jey_wizard.etaxes.fetch_all_etaxes hits 6 endpoints (objects, cash registers, POS terminals, bank accounts, obligation pacts, presented certs), using invoice_az's Asan Login for the main_token. Paginated endpoints follow hasMore. - Raw JSON per endpoint is stashed in the new single DocType "Jey Wizard Etaxes Cache". Per-endpoint errors are logged but don't abort. - jey_wizard.etaxes.get_cached(kind) is the read API for future wizard steps. - UI: new "fetching" sub-state between cert pick and done. "done" now shows a count summary of what was cached (or per-row error badge if a kind failed). Phase B (setup_wizard_complete hook, after Company is created): - jey_wizard.etaxes.materialize_after_setup receives setup args, reads args.company_name, and calls invoice_az.company_api.load_company_* for each of the six kinds. Those re-fetch and write into E-Taxes Object / E-Taxes Cash Register / etc with the real Company link. Each loader is best-effort; failures are logged, not raised. Why re-fetch instead of materialising from the cache: - invoice_az's loaders already own the ~500-line field mapping. Re-fetching once with a still-fresh main_token beats duplicating that code. Bump to 0.0.6. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
21981a1032
commit
2b9fba7dd7
|
|
@ -1 +1 @@
|
||||||
__version__ = "0.0.5"
|
__version__ = "0.0.6"
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,261 @@
|
||||||
|
"""Thin e-taxes fetchers for the wizard.
|
||||||
|
|
||||||
|
Unlike invoice_az's company_api which *fetches AND writes to E-Taxes DocTypes* (requires
|
||||||
|
an existing Company), these fetchers only do the HTTP part and stash raw JSON into the
|
||||||
|
`Jey Wizard Etaxes Cache` single doctype. They run during the Asan step — before Company
|
||||||
|
exists — so future wizard steps can query the data.
|
||||||
|
|
||||||
|
Final persistence into `E-Taxes Object`, `E-Taxes Cash Register`, etc. happens AFTER
|
||||||
|
setup_complete (see materialize_after_setup), where we delegate to invoice_az's loaders
|
||||||
|
with the freshly-created Company. That's a second fetch but avoids duplicating ~500 lines
|
||||||
|
of field-mapping code.
|
||||||
|
"""
|
||||||
|
|
||||||
|
import json
|
||||||
|
import traceback
|
||||||
|
|
||||||
|
import frappe
|
||||||
|
import requests
|
||||||
|
from frappe import _
|
||||||
|
|
||||||
|
from invoice_az.auth import DEFAULT_HEADERS, get_default_asan_login
|
||||||
|
|
||||||
|
|
||||||
|
BASE = "https://new.e-taxes.gov.az"
|
||||||
|
|
||||||
|
# (cache_field, http_method, url, json_payload_template, is_paginated)
|
||||||
|
# For paginated endpoints, `offset` is injected per page and `hasMore` drives the loop.
|
||||||
|
ENDPOINTS = [
|
||||||
|
(
|
||||||
|
"objects_json",
|
||||||
|
"GET",
|
||||||
|
f"{BASE}/api/po/profile/public/v1/object/list",
|
||||||
|
None,
|
||||||
|
False,
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"cash_registers_json",
|
||||||
|
"POST",
|
||||||
|
f"{BASE}/api/po/profile/public/v2/profile/cashRegister/list",
|
||||||
|
{"maxCount": 200, "offset": 0},
|
||||||
|
True,
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"pos_terminals_json",
|
||||||
|
"POST",
|
||||||
|
f"{BASE}/api/po/profile/public/v2/profile/posTerminal/list",
|
||||||
|
{"maxCount": 200, "offset": 0},
|
||||||
|
True,
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"bank_accounts_json",
|
||||||
|
"GET",
|
||||||
|
f"{BASE}/api/po/profile/public/v1/profile/bankAccount/list",
|
||||||
|
None,
|
||||||
|
False,
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"obligation_pacts_json",
|
||||||
|
"GET",
|
||||||
|
f"{BASE}/api/po/edi-legal/public/v1/application/sub-contractor/obl-pact-list",
|
||||||
|
None,
|
||||||
|
False,
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"presented_certs_json",
|
||||||
|
"GET",
|
||||||
|
f"{BASE}/api/po/edi-legal/public/v1/application/contractor/presented-accepted-e-certificates",
|
||||||
|
None,
|
||||||
|
False,
|
||||||
|
),
|
||||||
|
]
|
||||||
|
|
||||||
|
PAGE_SIZE = 200
|
||||||
|
MAX_PAGES = 50 # safety cap per paginated endpoint
|
||||||
|
|
||||||
|
|
||||||
|
KIND_TO_FIELD = {
|
||||||
|
"objects": "objects_json",
|
||||||
|
"cash_registers": "cash_registers_json",
|
||||||
|
"pos_terminals": "pos_terminals_json",
|
||||||
|
"bank_accounts": "bank_accounts_json",
|
||||||
|
"obligation_pacts": "obligation_pacts_json",
|
||||||
|
"presented_certs": "presented_certs_json",
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@frappe.whitelist()
|
||||||
|
def fetch_all_etaxes():
|
||||||
|
"""Fetch all six e-taxes lists and cache them. Best-effort: per-endpoint errors are
|
||||||
|
logged but don't abort the whole run.
|
||||||
|
"""
|
||||||
|
_only_admin()
|
||||||
|
|
||||||
|
login = get_default_asan_login()
|
||||||
|
if not login.get("found") or not login.get("main_token"):
|
||||||
|
return {
|
||||||
|
"ok": False,
|
||||||
|
"message": "No authenticated Asan Login with main_token — run Asan auth first.",
|
||||||
|
}
|
||||||
|
|
||||||
|
cache = frappe.get_single("Jey Wizard Etaxes Cache")
|
||||||
|
summary = {}
|
||||||
|
errors = {}
|
||||||
|
|
||||||
|
for field, method, url, payload, paginated in ENDPOINTS:
|
||||||
|
try:
|
||||||
|
data = _fetch_endpoint(method, url, payload, paginated)
|
||||||
|
cache.set(field, json.dumps(data, ensure_ascii=False))
|
||||||
|
summary[field] = _count_items(data, paginated)
|
||||||
|
except Exception as exc:
|
||||||
|
errors[field] = str(exc)
|
||||||
|
frappe.log_error(
|
||||||
|
f"{field} fetch failed: {exc}\n{traceback.format_exc()}",
|
||||||
|
"Jey Wizard etaxes fetch",
|
||||||
|
)
|
||||||
|
# Leave the previously cached value alone so partial success is preserved.
|
||||||
|
|
||||||
|
cache.fetched_at = frappe.utils.now()
|
||||||
|
cache.last_summary = json.dumps(summary, ensure_ascii=False)
|
||||||
|
cache.last_error = json.dumps(errors, ensure_ascii=False) if errors else ""
|
||||||
|
cache.flags.ignore_permissions = True
|
||||||
|
cache.save()
|
||||||
|
frappe.db.commit()
|
||||||
|
|
||||||
|
return {
|
||||||
|
"ok": True,
|
||||||
|
"summary": summary,
|
||||||
|
"errors": errors,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@frappe.whitelist()
|
||||||
|
def get_cached(kind):
|
||||||
|
"""Return parsed cached data for a given kind. Used by future wizard steps that
|
||||||
|
need to consume e-taxes data before Company exists. Returns None if nothing cached.
|
||||||
|
"""
|
||||||
|
_only_admin()
|
||||||
|
field = KIND_TO_FIELD.get(kind)
|
||||||
|
if not field:
|
||||||
|
frappe.throw(_("Unknown kind: {0}").format(kind))
|
||||||
|
cache = frappe.get_single("Jey Wizard Etaxes Cache")
|
||||||
|
raw = cache.get(field)
|
||||||
|
if not raw:
|
||||||
|
return None
|
||||||
|
try:
|
||||||
|
return json.loads(raw)
|
||||||
|
except Exception:
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
def materialize_after_setup(args):
|
||||||
|
"""setup_wizard_complete hook. After Company is created by the stock finalizer,
|
||||||
|
call invoice_az's loaders to persist everything into E-Taxes DocTypes with the
|
||||||
|
real Company link. Best-effort: per-loader failures are logged, not raised.
|
||||||
|
"""
|
||||||
|
company_name = (args or {}).get("company_name")
|
||||||
|
if not company_name:
|
||||||
|
frappe.log_error(
|
||||||
|
"materialize_after_setup: no company_name in args, skipping",
|
||||||
|
"Jey Wizard materialize",
|
||||||
|
)
|
||||||
|
return
|
||||||
|
|
||||||
|
if not frappe.db.exists("Company", company_name):
|
||||||
|
frappe.log_error(
|
||||||
|
f"materialize_after_setup: Company '{company_name}' not found, skipping",
|
||||||
|
"Jey Wizard materialize",
|
||||||
|
)
|
||||||
|
return
|
||||||
|
|
||||||
|
from invoice_az import company_api
|
||||||
|
|
||||||
|
loaders = [
|
||||||
|
("objects", company_api.load_company_objects),
|
||||||
|
("cash_registers", company_api.load_company_cash_registers),
|
||||||
|
("pos_terminals", company_api.load_company_pos_terminals),
|
||||||
|
("bank_accounts", company_api.load_company_bank_accounts),
|
||||||
|
("obligation_pacts", company_api.load_company_obligation_pacts),
|
||||||
|
("presented_certs", company_api.load_company_presented_certificates),
|
||||||
|
]
|
||||||
|
|
||||||
|
for name, loader in loaders:
|
||||||
|
try:
|
||||||
|
loader(company_name)
|
||||||
|
except Exception as exc:
|
||||||
|
frappe.log_error(
|
||||||
|
f"materialize_after_setup: {name} loader failed for {company_name}: {exc}\n"
|
||||||
|
f"{traceback.format_exc()}",
|
||||||
|
"Jey Wizard materialize",
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
# ---------- internals ----------
|
||||||
|
|
||||||
|
|
||||||
|
def _fetch_endpoint(method, url, payload, paginated):
|
||||||
|
if not paginated:
|
||||||
|
return _http_call(method, url, json_body=payload)
|
||||||
|
|
||||||
|
# Paginated: collect raw page responses in a list. Subsequent readers can iterate
|
||||||
|
# pages themselves; invoice_az will re-fetch anyway at finalize.
|
||||||
|
pages = []
|
||||||
|
offset = 0
|
||||||
|
for _ in range(MAX_PAGES):
|
||||||
|
page_payload = dict(payload)
|
||||||
|
page_payload["offset"] = offset
|
||||||
|
resp = _http_call(method, url, json_body=page_payload)
|
||||||
|
pages.append(resp)
|
||||||
|
if not (isinstance(resp, dict) and resp.get("hasMore")):
|
||||||
|
break
|
||||||
|
offset += PAGE_SIZE
|
||||||
|
return pages
|
||||||
|
|
||||||
|
|
||||||
|
def _http_call(method, url, json_body=None):
|
||||||
|
"""HTTP with invoice_az-compatible 401-retry-once: pulls fresh main_token on 401."""
|
||||||
|
headers = _auth_headers()
|
||||||
|
response = requests.request(method, url, headers=headers, json=json_body, timeout=60)
|
||||||
|
if response.status_code == 401:
|
||||||
|
fresh_headers = _auth_headers()
|
||||||
|
if fresh_headers.get("x-authorization") != headers.get("x-authorization"):
|
||||||
|
response = requests.request(
|
||||||
|
method, url, headers=fresh_headers, json=json_body, timeout=60
|
||||||
|
)
|
||||||
|
response.raise_for_status()
|
||||||
|
return response.json() or {}
|
||||||
|
|
||||||
|
|
||||||
|
def _auth_headers():
|
||||||
|
login = get_default_asan_login()
|
||||||
|
if not login.get("main_token"):
|
||||||
|
raise frappe.AuthenticationError("No main_token on default Asan Login")
|
||||||
|
headers = DEFAULT_HEADERS.copy()
|
||||||
|
headers["x-authorization"] = f"Bearer {login['main_token']}"
|
||||||
|
return headers
|
||||||
|
|
||||||
|
|
||||||
|
def _count_items(data, paginated):
|
||||||
|
"""Lightweight count for UI summary. Digs one level deep to find the list."""
|
||||||
|
if paginated and isinstance(data, list):
|
||||||
|
total = 0
|
||||||
|
for page in data:
|
||||||
|
if not isinstance(page, dict):
|
||||||
|
continue
|
||||||
|
for value in page.values():
|
||||||
|
if isinstance(value, list):
|
||||||
|
total += len(value)
|
||||||
|
break
|
||||||
|
return total
|
||||||
|
if isinstance(data, dict):
|
||||||
|
for value in data.values():
|
||||||
|
if isinstance(value, list):
|
||||||
|
return len(value)
|
||||||
|
if isinstance(data, list):
|
||||||
|
return len(data)
|
||||||
|
return 0
|
||||||
|
|
||||||
|
|
||||||
|
def _only_admin():
|
||||||
|
if frappe.session.user != "Administrator":
|
||||||
|
frappe.throw(_("Only Administrator may run setup"), frappe.PermissionError)
|
||||||
|
|
@ -35,6 +35,11 @@ required_apps = ["invoice_az"]
|
||||||
# created — so overriding frappe.setup.SetupWizard in it swaps the UI entirely.
|
# created — so overriding frappe.setup.SetupWizard in it swaps the UI entirely.
|
||||||
setup_wizard_requires = ["/assets/jey_wizard/js/jey_setup.js"]
|
setup_wizard_requires = ["/assets/jey_wizard/js/jey_setup.js"]
|
||||||
|
|
||||||
|
# Runs AFTER stock setup_complete stages (so Company exists). We use it to push the
|
||||||
|
# e-taxes data we already fetched+cached during the Asan step into the real
|
||||||
|
# E-Taxes Object / Cash Register / ... doctypes, now linked to the new Company.
|
||||||
|
setup_wizard_complete = "jey_wizard.etaxes.materialize_after_setup"
|
||||||
|
|
||||||
# include js, css files in header of web template
|
# include js, css files in header of web template
|
||||||
# web_include_css = "/assets/jey_wizard/css/jey_wizard.css"
|
# web_include_css = "/assets/jey_wizard/css/jey_wizard.css"
|
||||||
# web_include_js = "/assets/jey_wizard/js/jey_wizard.js"
|
# web_include_js = "/assets/jey_wizard/js/jey_wizard.js"
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,119 @@
|
||||||
|
{
|
||||||
|
"actions": [],
|
||||||
|
"allow_rename": 0,
|
||||||
|
"creation": "2026-04-22 19:00:00.000000",
|
||||||
|
"doctype": "DocType",
|
||||||
|
"engine": "InnoDB",
|
||||||
|
"field_order": [
|
||||||
|
"status_section",
|
||||||
|
"fetched_at",
|
||||||
|
"last_summary",
|
||||||
|
"last_error",
|
||||||
|
"data_section",
|
||||||
|
"objects_json",
|
||||||
|
"cash_registers_json",
|
||||||
|
"pos_terminals_json",
|
||||||
|
"bank_accounts_json",
|
||||||
|
"obligation_pacts_json",
|
||||||
|
"presented_certs_json"
|
||||||
|
],
|
||||||
|
"fields": [
|
||||||
|
{
|
||||||
|
"fieldname": "status_section",
|
||||||
|
"fieldtype": "Section Break",
|
||||||
|
"label": "Status"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "fetched_at",
|
||||||
|
"fieldtype": "Datetime",
|
||||||
|
"label": "Last Fetched",
|
||||||
|
"read_only": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "last_summary",
|
||||||
|
"fieldtype": "Code",
|
||||||
|
"label": "Last Summary",
|
||||||
|
"options": "JSON",
|
||||||
|
"read_only": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "last_error",
|
||||||
|
"fieldtype": "Small Text",
|
||||||
|
"label": "Last Error",
|
||||||
|
"read_only": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "data_section",
|
||||||
|
"fieldtype": "Section Break",
|
||||||
|
"label": "Cached Data"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "objects_json",
|
||||||
|
"fieldtype": "Code",
|
||||||
|
"label": "Objects",
|
||||||
|
"options": "JSON",
|
||||||
|
"read_only": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "cash_registers_json",
|
||||||
|
"fieldtype": "Code",
|
||||||
|
"label": "Cash Registers",
|
||||||
|
"options": "JSON",
|
||||||
|
"read_only": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "pos_terminals_json",
|
||||||
|
"fieldtype": "Code",
|
||||||
|
"label": "POS Terminals",
|
||||||
|
"options": "JSON",
|
||||||
|
"read_only": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "bank_accounts_json",
|
||||||
|
"fieldtype": "Code",
|
||||||
|
"label": "Bank Accounts",
|
||||||
|
"options": "JSON",
|
||||||
|
"read_only": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "obligation_pacts_json",
|
||||||
|
"fieldtype": "Code",
|
||||||
|
"label": "Obligation Pacts",
|
||||||
|
"options": "JSON",
|
||||||
|
"read_only": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "presented_certs_json",
|
||||||
|
"fieldtype": "Code",
|
||||||
|
"label": "Presented Certificates",
|
||||||
|
"options": "JSON",
|
||||||
|
"read_only": 1
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"index_web_pages_for_search": 0,
|
||||||
|
"issingle": 1,
|
||||||
|
"links": [],
|
||||||
|
"modified": "2026-04-22 19:00:00.000000",
|
||||||
|
"modified_by": "Administrator",
|
||||||
|
"module": "Jey Wizard",
|
||||||
|
"name": "Jey Wizard Etaxes Cache",
|
||||||
|
"owner": "Administrator",
|
||||||
|
"permissions": [
|
||||||
|
{
|
||||||
|
"create": 1,
|
||||||
|
"delete": 0,
|
||||||
|
"email": 0,
|
||||||
|
"export": 0,
|
||||||
|
"print": 0,
|
||||||
|
"read": 1,
|
||||||
|
"report": 0,
|
||||||
|
"role": "System Manager",
|
||||||
|
"share": 0,
|
||||||
|
"write": 1
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"row_format": "Dynamic",
|
||||||
|
"sort_field": "modified",
|
||||||
|
"sort_order": "DESC",
|
||||||
|
"states": []
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,5 @@
|
||||||
|
from frappe.model.document import Document
|
||||||
|
|
||||||
|
|
||||||
|
class JeyWizardEtaxesCache(Document):
|
||||||
|
pass
|
||||||
|
|
@ -10,7 +10,7 @@ frappe.provide("jey_wizard");
|
||||||
// Bump this string in every commit that changes wizard code. Displayed in the badge 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
|
// 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.
|
// sync with __version__ in jey_wizard/__init__.py.
|
||||||
const JEY_WIZARD_VERSION = "0.0.5";
|
const JEY_WIZARD_VERSION = "0.0.6";
|
||||||
|
|
||||||
// Wipe Frappe + ERPNext default slides so their `before_load`/`after_load` listeners
|
// 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.
|
// don't try to mutate a wizard that isn't slide-based anymore.
|
||||||
|
|
@ -172,15 +172,51 @@ frappe.setup.SetupWizard = class JeySetupWizard {
|
||||||
const idx = parseInt($(e.currentTarget).attr("data-idx"), 10);
|
const idx = parseInt($(e.currentTarget).attr("data-idx"), 10);
|
||||||
this.pick_certificate(idx);
|
this.pick_certificate(idx);
|
||||||
});
|
});
|
||||||
} else if (this.asan_state === "done") {
|
} else if (this.asan_state === "fetching") {
|
||||||
$body.html(`
|
$body.html(`
|
||||||
<h3 style="margin-bottom:16px;color:#080">✓ Authenticated</h3>
|
<h3 style="margin-bottom:16px">Loading data from tax portal...</h3>
|
||||||
|
<p style="color:#666">Pulling objects, cash registers, POS terminals, bank accounts, obligation pacts and presented certificates. This usually takes 10-30 seconds.</p>
|
||||||
|
<div style="display:flex;align-items:center;gap:12px;margin-top:20px">
|
||||||
|
<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 style="color:#666">Fetching...</div>
|
||||||
|
</div>
|
||||||
|
<style>@keyframes jey-spin { to { transform: rotate(360deg) } }</style>
|
||||||
|
`);
|
||||||
|
} else if (this.asan_state === "done") {
|
||||||
|
const summary = this.asan_fetch_summary || {};
|
||||||
|
const errors = this.asan_fetch_errors || {};
|
||||||
|
const labels = {
|
||||||
|
objects_json: "Objects",
|
||||||
|
cash_registers_json: "Cash registers",
|
||||||
|
pos_terminals_json: "POS terminals",
|
||||||
|
bank_accounts_json: "Bank accounts",
|
||||||
|
obligation_pacts_json: "Obligation pacts",
|
||||||
|
presented_certs_json: "Presented certificates",
|
||||||
|
};
|
||||||
|
const rows = Object.keys(labels).map((k) => {
|
||||||
|
const label = labels[k];
|
||||||
|
if (errors[k]) {
|
||||||
|
return `<li><b>${label}:</b> <span style="color:#c00">failed</span> <span style="color:#888;font-size:12px">(${frappe.utils.escape_html(errors[k])})</span></li>`;
|
||||||
|
}
|
||||||
|
const n = summary[k];
|
||||||
|
if (n === undefined) {
|
||||||
|
return `<li><b>${label}:</b> <span style="color:#888">—</span></li>`;
|
||||||
|
}
|
||||||
|
return `<li><b>${label}:</b> ${n}</li>`;
|
||||||
|
}).join("");
|
||||||
|
|
||||||
|
$body.html(`
|
||||||
|
<h3 style="margin-bottom:16px;color:#080">✓ Authenticated & data loaded</h3>
|
||||||
<p>Pulled from the tax portal:</p>
|
<p>Pulled from the tax portal:</p>
|
||||||
<ul style="line-height:1.8">
|
<ul style="line-height:1.8">
|
||||||
<li><b>Company:</b> ${frappe.utils.escape_html(this.data.company_name)}</li>
|
<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>
|
<li><b>VÖEN:</b> ${frappe.utils.escape_html(this.data.asan_voen)}</li>
|
||||||
</ul>
|
</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>
|
<div style="margin-top:16px;padding:12px;background:#f7f7f7;border-radius:6px">
|
||||||
|
<div style="font-weight:600;margin-bottom:6px">Cached datasets</div>
|
||||||
|
<ul style="line-height:1.6;margin:0;padding-left:20px">${rows}</ul>
|
||||||
|
</div>
|
||||||
|
<p style="color:#666;margin-top:12px">Everything above is now available to later wizard steps. After finalization, the data will be persisted into the E-Taxes DocTypes linked to your new Company.</p>
|
||||||
`);
|
`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -463,6 +499,31 @@ frappe.setup.SetupWizard = class JeySetupWizard {
|
||||||
const m = r.message || {};
|
const m = r.message || {};
|
||||||
this.data.company_name = m.company_name || this.data.company_name;
|
this.data.company_name = m.company_name || this.data.company_name;
|
||||||
this.data.asan_voen = m.voen || "";
|
this.data.asan_voen = m.voen || "";
|
||||||
|
// Move UI into the "fetching" sub-state so the user sees progress while
|
||||||
|
// we pull all six e-taxes lists into the cache doctype.
|
||||||
|
this.asan_state = "fetching";
|
||||||
|
this.show_current_step();
|
||||||
|
this.fetch_etaxes_data();
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
fetch_etaxes_data() {
|
||||||
|
frappe.call({
|
||||||
|
method: "jey_wizard.etaxes.fetch_all_etaxes",
|
||||||
|
// Deliberately no freeze_message — UI already shows a spinner.
|
||||||
|
callback: (r) => {
|
||||||
|
const m = r.message || {};
|
||||||
|
this.asan_fetch_summary = m.summary || {};
|
||||||
|
this.asan_fetch_errors = m.errors || {};
|
||||||
|
this.asan_state = "done";
|
||||||
|
this.show_current_step();
|
||||||
|
},
|
||||||
|
error: () => {
|
||||||
|
// The fetch is best-effort; we still proceed to done and let the user
|
||||||
|
// continue. The setup_wizard_complete hook will re-fetch + persist later.
|
||||||
|
this.asan_fetch_summary = {};
|
||||||
|
this.asan_fetch_errors = { all: "fetch call failed — see server logs" };
|
||||||
this.asan_state = "done";
|
this.asan_state = "done";
|
||||||
this.show_current_step();
|
this.show_current_step();
|
||||||
},
|
},
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue