From 830d621a8781868e20adc3b31a44753e930524c3 Mon Sep 17 00:00:00 2001 From: Ali <010109ali@gmail.com> Date: Tue, 28 Apr 2026 12:00:20 +0000 Subject: [PATCH] Use ERPNext's update_account_number for placeholder rename + per-bank trace MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The 0.1.9 placeholder repurposing didn't take effect on the test machine (account tree still showed XXXX rows even though E-Taxes Bank Account got populated). The most likely culprit was the manual doc.save() + frappe.rename_doc combo: doc.save runs Account.validate, which fires validate_account_currency and validate_account_number — either can throw on an in-place placeholder edit, leaving the account_name updated but the doc name stale, which dodges the XXXX-pattern delete sweep too. Switch to ERPNext's canonical update_account_number helper. It does the rename atomically, handles the tree-doctype reparenting, and is the exact path the in-app rename UI uses. Currency is set with a direct db.set_value beforehand so we never go through the full validate stack. Also expanded "Jey Wizard trace: bank native done" to a JSON dump that records, for every bank in the cache: currency, whether a same-currency placeholder was available, the resulting GL name, and the action taken (reused_placeholder / created_fresh / skipped_closed / error). Plus the placeholder_index keys/counts as seen at the start of the loop. Next run will show exactly which step is failing if anything still goes wrong. --- jey_wizard/__init__.py | 2 +- jey_wizard/etaxes.py | 80 ++++++++++++++++++++----------- jey_wizard/public/js/jey_setup.js | 2 +- 3 files changed, 54 insertions(+), 30 deletions(-) diff --git a/jey_wizard/__init__.py b/jey_wizard/__init__.py index c11f861..569b121 100644 --- a/jey_wizard/__init__.py +++ b/jey_wizard/__init__.py @@ -1 +1 @@ -__version__ = "0.1.9" +__version__ = "0.1.10" diff --git a/jey_wizard/etaxes.py b/jey_wizard/etaxes.py index 4dbc295..a1617fb 100644 --- a/jey_wizard/etaxes.py +++ b/jey_wizard/etaxes.py @@ -317,18 +317,35 @@ def _materialize_native_banks(company): skipped_closed = 0 created_by_currency = {} created_gl_names = [] + bank_trace = [] # per-bank: action taken + result, dumped to Error Log at end for acc in accounts: if (acc.get("status") or "").upper() == "C": skipped_closed += 1 + bank_trace.append({"iban": acc.get("number"), "action": "skipped_closed"}) continue + cur_for_log = (acc.get("currency") or "AZN").strip() or "AZN" + ph_available_before = bool(_peek_placeholder(placeholder_index, cur_for_log)) try: gl = _materialize_one_bank(company, parent, acc, placeholder_index) if gl: created_gl_names.append(gl) - cur = (acc.get("currency") or "AZN").strip() or "AZN" - created_by_currency.setdefault(cur, []).append(gl) + created_by_currency.setdefault(cur_for_log, []).append(gl) created += 1 + bank_trace.append({ + "iban": acc.get("number"), + "currency": cur_for_log, + "placeholder_was_available": ph_available_before, + "resulting_gl": gl, + "action": "reused_placeholder" if (ph_available_before and gl) else "created_fresh", + }) except Exception as exc: + bank_trace.append({ + "iban": acc.get("number"), + "currency": cur_for_log, + "placeholder_was_available": ph_available_before, + "action": "error", + "error": str(exc)[:300], + }) frappe.log_error( f"{acc.get('number')}: {exc}\n{traceback.format_exc()}", "Jey Wizard materialize (bank native)", @@ -340,8 +357,16 @@ def _materialize_native_banks(company): # Success breadcrumb in Error Log so we can verify end-to-end run from the same # place where loader/cache traces live. frappe.log_error( - f"company={company} materialised={created} skipped_closed={skipped_closed} " - f"input_rows={len(accounts)} parent={parent}", + json.dumps({ + "company": company, + "materialised": created, + "skipped_closed": skipped_closed, + "input_rows": len(accounts), + "parent": parent, + "placeholder_index_keys": sorted(list(placeholder_index.keys())), + "placeholder_index_counts": {cur: len(items) for cur, items in placeholder_index.items()}, + "banks": bank_trace, + }, ensure_ascii=False, indent=2), "Jey Wizard trace: bank native done", ) @@ -711,33 +736,32 @@ def _claim_placeholder(placeholder_index, currency): return None +def _peek_placeholder(placeholder_index, currency): + """Like _claim_placeholder but doesn't mutate. Used by trace logging.""" + if not placeholder_index: + return None + for ph in placeholder_index.get(currency, []): + if not ph["consumed"]: + return ph + return None + + def _convert_placeholder_to_real(old_name, account_number, company, new_account_name, currency): - """Repurpose an existing placeholder Account in place: update its - account_name and account_currency, then rename to the canonical - " - - " form. frappe.rename_doc - updates Link references in other docs (notably Company.default_bank_account - and any GL entries) to the new name automatically. + """Repurpose an existing placeholder Account in place. Delegates the rename + to ERPNext's canonical update_account_number helper — that routine handles + the tree-doctype rename, child-company sync, and final frappe.rename_doc + with force=True. Currency is updated separately via direct db write so we + don't go through Account.validate (which on a doc.save would re-fire all + hooks and historically choked on the AZ-CoA placeholder shape). """ - abbr = frappe.db.get_value("Company", company, "abbr") or "" - doc = frappe.get_doc("Account", old_name) - doc.account_name = new_account_name - doc.account_currency = currency - doc.flags.ignore_permissions = True - doc.flags.ignore_validate_update_after_submit = True - doc.save(ignore_permissions=True) - - if account_number: - new_name = f"{account_number} - {new_account_name} - {abbr}" - else: - new_name = f"{new_account_name} - {abbr}" - - if old_name != new_name and not frappe.db.exists("Account", new_name): - frappe.rename_doc( - "Account", old_name, new_name, - force=True, ignore_permissions=True, show_alert=False, + if currency: + frappe.db.set_value( + "Account", old_name, "account_currency", currency, update_modified=False ) - return new_name - return old_name + from erpnext.accounts.doctype.account.account import update_account_number + + new_name = update_account_number(old_name, new_account_name, account_number) + return new_name or old_name def _find_default_warehouse(company): diff --git a/jey_wizard/public/js/jey_setup.js b/jey_wizard/public/js/jey_setup.js index 19e4632..d16679f 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.9"; +const JEY_WIZARD_VERSION = "0.1.10"; // 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.