feat(bank-integration): fall back to first leaf node when no group is configured + filter group fields
Creating customers/suppliers no longer requires a default Customer/ Supplier Group or Territory on the Bank Integration — when none is set, the first leaf (non-group) node of the tree is used. A configured value is still validated (must exist and be a leaf), with a clear message. Also: the default-group / default-territory Link fields (and the matching mapping child-table fields) now filter to is_group=0 so a group node can't be selected in the first place. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
94ce72e4fb
commit
1abdedce20
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue