diff --git a/jey_wizard/__init__.py b/jey_wizard/__init__.py index f1380ee..9cb17e7 100644 --- a/jey_wizard/__init__.py +++ b/jey_wizard/__init__.py @@ -1 +1 @@ -__version__ = "0.1.7" +__version__ = "0.1.8" diff --git a/jey_wizard/etaxes.py b/jey_wizard/etaxes.py index b781592..b64d448 100644 --- a/jey_wizard/etaxes.py +++ b/jey_wizard/etaxes.py @@ -303,12 +303,15 @@ def _materialize_native_banks(company): created = 0 skipped_closed = 0 + created_gl_names = [] for acc in accounts: if (acc.get("status") or "").upper() == "C": skipped_closed += 1 continue try: - _materialize_one_bank(company, parent, acc) + gl = _materialize_one_bank(company, parent, acc) + if gl: + created_gl_names.append(gl) created += 1 except Exception as exc: frappe.log_error( @@ -316,6 +319,7 @@ def _materialize_native_banks(company): "Jey Wizard materialize (bank native)", ) + _repoint_company_default_bank(company, created_gl_names) _delete_bank_placeholders(company, parent) frappe.db.commit() # Success breadcrumb in Error Log so we can verify end-to-end run from the same @@ -352,13 +356,14 @@ def _find_bank_parent_account(company): def _materialize_one_bank(company, parent_account, row): # Accepts either the raw e-taxes payload shape (bankName/number/type…) or # the E-Taxes Bank Account doctype shape (bank_name/number/account_type). + # Returns the GL account name on success (used by the caller to repoint + # Company.default_bank_account before deleting placeholders), None otherwise. bank_name = (row.get("bankName") or row.get("bank_name") or "").strip() iban = (row.get("number") or "").strip() currency = (row.get("currency") or "").strip() or "AZN" - etx_account_type = (row.get("type") or row.get("account_type") or "").strip() if not bank_name or not iban: - return + return None # 1. Bank (global, unique on bank_name) if not frappe.db.exists("Bank", bank_name): @@ -393,6 +398,11 @@ def _materialize_one_bank(company, parent_account, row): {"bank": bank_name, "account_name": account_name, "company": company}, ) if not ba_exists: + # Bank Account.account_type is a Link to the Bank Account Type doctype + # (effectively empty on a fresh install). The e-taxes 'type' field is a + # numeric code (e.g. "2" for card) that has no Bank Account Type row to + # match — populating it raises "Hesab Növü: 2 tapılmadı" on validate. + # Field is optional, so just leave it unset. ba_doc = frappe.get_doc({ "doctype": "Bank Account", "account_name": account_name, @@ -402,10 +412,33 @@ def _materialize_one_bank(company, parent_account, row): "is_company_account": 1, "bank_account_no": iban, "iban": iban, - "account_type": etx_account_type or None, }) ba_doc.insert(ignore_permissions=True) + return gl_name + + +def _repoint_company_default_bank(company, created_gl_names): + """ERPNext's setup_company → set_default_accounts picks the first + account_type='Bank' Account it finds and writes it to + Company.default_bank_account. On a fresh AZ-CoA install that's a placeholder + (223.1 - AZN AZXX… - JS), and _delete_bank_placeholders then can't delete it + (LinkExistsError pointing back at Company). Repoint the default to the first + real bank GL we just created so the placeholder is free to remove. + + Only repoints when the current value is unset or matches the placeholder + pattern — never overwrites a value the user explicitly set. + """ + if not created_gl_names: + return + current = frappe.db.get_value("Company", company, "default_bank_account") + if current and "XXXX" not in current: + return + frappe.db.set_value( + "Company", company, "default_bank_account", + created_gl_names[0], update_modified=False, + ) + def _delete_bank_placeholders(company, parent_account): """Remove the stock template accounts from AZ CoA once real banks have been @@ -431,6 +464,22 @@ def _delete_bank_placeholders(company, parent_account): frappe.delete_doc( "Account", row.name, ignore_permissions=True, delete_permanently=True ) + except frappe.LinkExistsError: + # Some other doc still references this placeholder (e.g. a Company + # default field we didn't preempt). Frappe.throw queued a popup + # message before raising — clear it so the user doesn't see a + # scary error after a successful setup, then disable the account + # instead of leaving it active in the chart. + frappe.clear_last_message() + try: + frappe.db.set_value( + "Account", row.name, "disabled", 1, update_modified=False + ) + except Exception as exc: + frappe.log_error( + f"Placeholder disable fallback failed: {row.name}: {exc}", + "Jey Wizard materialize (bank placeholder)", + ) except Exception as exc: frappe.log_error( f"Placeholder delete failed: {row.name}: {exc}", diff --git a/jey_wizard/public/js/jey_setup.js b/jey_wizard/public/js/jey_setup.js index 8d5f1aa..c838cf0 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.1.7"; +const JEY_WIZARD_VERSION = "0.1.8"; // 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.