diff --git a/jey_erp/bank_integration/creation.py b/jey_erp/bank_integration/creation.py index eef270a..2a5a440 100644 --- a/jey_erp/bank_integration/creation.py +++ b/jey_erp/bank_integration/creation.py @@ -4,6 +4,27 @@ import frappe from frappe import _ +def _pick_leaf(doctype, configured, label): + """Resolve `configured` to a valid leaf node of an NSM tree doctype + (Customer Group / Supplier Group / Territory). + + - If configured: validate it exists and is NOT a group node. + - If not configured: fall back to the first leaf node (so creation works + without forcing a default on the Bank Integration). + Raises with a clear message if it can't. + """ + if configured: + if not frappe.db.exists(doctype, configured): + frappe.throw(_("{0} '{1}' does not exist.").format(label, configured)) + if frappe.db.get_value(doctype, configured, "is_group"): + frappe.throw(_("{0} '{1}' is a group node — pick a leaf (non-group) {0}.").format(label, configured)) + return configured + leaf = frappe.db.get_value(doctype, {"is_group": 0}, "name", order_by="lft asc") + if not leaf: + frappe.throw(_("No non-group {0} exists. Create one first.").format(label)) + return leaf + + @frappe.whitelist() def create_unmapped_customers(bank_integration): """For each Bank Integration Customer Mapping row without erp_customer, create an ERPNext Customer.""" @@ -19,32 +40,19 @@ def create_unmapped_customers(bank_integration): if not unmapped: return {"success": True, "created_count": 0, "message": "No unmapped customers in table"} - # Validate config upfront — clearer than ERPNext's errors thrown deep inside - # party creation ("Could not find Customer Group" / "Cannot select a Group type"). - for mapping, _bi_cust in unmapped: - group = mapping.customer_group or bi.default_customer_group - if not group or not frappe.db.exists("Customer Group", group): - frappe.throw(_( - "Set a Default Customer Group on the Bank Integration " - "(or a Customer Group on each customer mapping row) before creating customers." - )) - if frappe.db.get_value("Customer Group", group, "is_group"): - frappe.throw(_( - "Customer Group '{0}' is a group node — pick a leaf (non-group) Customer Group." - ).format(group)) - territory = mapping.territory or bi.default_territory - if not territory or not frappe.db.exists("Territory", territory): - frappe.throw(_( - "Set a Default Territory on the Bank Integration " - "(or a Territory on each customer mapping row) before creating customers." - )) - if frappe.db.get_value("Territory", territory, "is_group"): - frappe.throw(_( - "Territory '{0}' is a group node — pick a leaf (non-group) Territory." - ).format(territory)) + # Resolve group / territory per row upfront (raises a clear message on bad + # config). If nothing is configured, _pick_leaf falls back to the first leaf + # node — so creation works without a default set on the Bank Integration. + resolved = [ + ( + _pick_leaf("Customer Group", mapping.customer_group or bi.default_customer_group, "Customer Group"), + _pick_leaf("Territory", mapping.territory or bi.default_territory, "Territory"), + ) + for mapping, _bi_cust in unmapped + ] created = 0 - for mapping, bi_cust in unmapped: + for (mapping, bi_cust), (customer_group, territory) in zip(unmapped, resolved): try: customer_name = bi_cust.customer_name if frappe.db.exists("Customer", {"customer_name": customer_name}): @@ -53,8 +61,8 @@ def create_unmapped_customers(bank_integration): party = frappe.new_doc("Customer") party.customer_name = customer_name party.customer_type = "Company" - party.customer_group = mapping.customer_group or bi.default_customer_group - party.territory = mapping.territory or bi.default_territory + party.customer_group = customer_group + party.territory = territory if bi_cust.tax_id: party.tax_id = bi_cust.tax_id @@ -103,20 +111,13 @@ def create_unmapped_suppliers(bank_integration): if not unmapped: return {"success": True, "created_count": 0, "message": "No unmapped suppliers in table"} - for mapping, _bi_supp in unmapped: - group = mapping.supplier_group or bi.default_supplier_group - if not group or not frappe.db.exists("Supplier Group", group): - frappe.throw(_( - "Set a Default Supplier Group on the Bank Integration " - "(or a Supplier Group on each supplier mapping row) before creating suppliers." - )) - if frappe.db.get_value("Supplier Group", group, "is_group"): - frappe.throw(_( - "Supplier Group '{0}' is a group node — pick a leaf (non-group) Supplier Group." - ).format(group)) + resolved = [ + _pick_leaf("Supplier Group", mapping.supplier_group or bi.default_supplier_group, "Supplier Group") + for mapping, _bi_supp in unmapped + ] created = 0 - for mapping, bi_supp in unmapped: + for (mapping, bi_supp), supplier_group in zip(unmapped, resolved): try: supplier_name = bi_supp.supplier_name if frappe.db.exists("Supplier", {"supplier_name": supplier_name}): @@ -125,7 +126,7 @@ def create_unmapped_suppliers(bank_integration): party = frappe.new_doc("Supplier") party.supplier_name = supplier_name party.supplier_type = "Company" - party.supplier_group = mapping.supplier_group or bi.default_supplier_group + party.supplier_group = supplier_group if bi_supp.tax_id: party.tax_id = bi_supp.tax_id diff --git a/jey_erp/jey_erp/doctype/bank_integration/bank_integration.js b/jey_erp/jey_erp/doctype/bank_integration/bank_integration.js index f726ca1..2601382 100644 --- a/jey_erp/jey_erp/doctype/bank_integration/bank_integration.js +++ b/jey_erp/jey_erp/doctype/bank_integration/bank_integration.js @@ -2,6 +2,18 @@ // Mirrors the kapital_bank_settings.js layout for familiarity. frappe.ui.form.on('Bank Integration', { + setup(frm) { + // Customer/Supplier Group and Territory must be leaf (non-group) nodes — + // don't let group nodes be picked here or in the mapping child tables. + const leaf = () => ({ filters: { is_group: 0 } }); + frm.set_query('default_customer_group', leaf); + frm.set_query('default_supplier_group', leaf); + frm.set_query('default_territory', leaf); + frm.set_query('customer_group', 'customer_mappings', leaf); + frm.set_query('territory', 'customer_mappings', leaf); + frm.set_query('supplier_group', 'supplier_mappings', leaf); + }, + refresh(frm) { if (frm.is_new()) return;