diff --git a/webnotes/tests/test_nested_set.py b/webnotes/tests/test_nested_set.py deleted file mode 100644 index 766c00f526..0000000000 --- a/webnotes/tests/test_nested_set.py +++ /dev/null @@ -1,154 +0,0 @@ -import unittest, webnotes - -# TODO - rewrite this in Item Group - -# class TestNSM(unittest.TestCase): -# def setUp(self): -# webnotes.conn.sql("""delete from `tabItem Group` -# where (name like 'c%') or (name like 'gc%')""") -# -# -# self.data = [ -# ["All Item Groups", None, 1, 20], -# ["c0", "All Item Groups", 2, 3], -# ["c1", "All Item Groups", 4, 11], -# ["gc1", "c1", 5, 6], -# ["gc2", "c1", 7, 8], -# ["gc3", "c1", 9, 10], -# ["c2", "All Item Groups", 12, 17], -# ["gc4", "c2", 13, 14], -# ["gc5", "c2", 15, 16], -# ["c3", "All Item Groups", 18, 19] -# ] -# -# for d in self.data: -# b = webnotes.bean([{ -# "doctype": "Item Group", "item_group_name": d[0], "parent_item_group": d[1], -# "__islocal": 1, "is_group": "Yes" -# }]) -# b.insert() -# self.__dict__[d[0]] = b -# -# -# self.reload_all() -# -# def reload_all(self, data=None): -# for d in data or self.data: -# self.__dict__[d[0]].load_from_db() -# -# def test_basic_tree(self, data=None): -# for d in data or self.data: -# self.assertEquals(self.__dict__[d[0]].doc.lft, d[2]) -# self.assertEquals(self.__dict__[d[0]].doc.rgt, d[3]) -# -# def test_validate_loop_move(self): -# self.c1.doc.parent_item_group = 'gc3' -# self.assertRaises(webnotes.ValidationError, self.c1.save) -# -# def test_rebuild_tree(self): -# from webnotes.utils.nestedset import rebuild_tree -# rebuild_tree("Item Group", "parent_item_group") -# self.test_basic_tree(self.data) -# -# def test_move_group(self): -# self.c1.doc.parent_item_group = 'c2' -# self.c1.save() -# self.reload_all() -# -# new_tree = [ -# ["All Item Groups", None, 1, 20], -# ["c0", "All Item Groups", 2, 3], -# ["c2", "All Item Groups", 4, 17], -# ["gc4", "c2", 5, 6], -# ["gc5", "c2", 7, 8], -# ["c1", "All Item Groups", 9, 16], -# ["gc1", "c1", 10, 11], -# ["gc2", "c1", 12, 13], -# ["gc3", "c1", 14, 15], -# ["c3", "All Item Groups", 18, 19] -# ] -# self.test_basic_tree(new_tree) -# -# # Move back -# -# self.c1.doc.parent_item_group = 'gc4' -# self.c1.save() -# self.reload_all() -# -# new_tree = [ -# ["All Item Groups", None, 1, 20], -# ["c0", "All Item Groups", 2, 3], -# ["c2", "All Item Groups", 4, 17], -# ["gc4", "c2", 5, 14], -# ["c1", "All Item Groups", 6, 13], -# ["gc1", "c1", 7, 8], -# ["gc2", "c1", 9, 10], -# ["gc3", "c1", 11, 12], -# ["gc5", "c2", 15, 16], -# ["c3", "All Item Groups", 18, 19] -# ] -# self.test_basic_tree(new_tree) -# -# # Move to root -# -# # self.c1.doc.parent_item_group = '' -# # self.c1.save() -# # self.reload_all() -# # -# # new_tree = [ -# # ["All Item Groups", None, 1, 12], -# # ["c0", "All Item Groups", 2, 3], -# # ["c2", "All Item Groups", 4, 9], -# # ["gc4", "c2", 5, 6], -# # ["gc5", "c2", 7, 8], -# # ["c3", "All Item Groups", 10, 11], -# # ["c1", "All Item Groups", 13, 20], -# # ["gc1", "c1", 14, 15], -# # ["gc2", "c1", 16, 17], -# # ["gc3", "c1", 18, 19], -# # ] -# # self.test_basic_tree(new_tree) -# -# # move leaf -# self.gc3.doc.parent_item_group = 'c2' -# self.gc3.save() -# self.reload_all() -# -# new_tree = [ -# ["All Item Groups", None, 1, 20], -# ["c0", "All Item Groups", 2, 3], -# ["c2", "All Item Groups", 4, 17], -# ["gc4", "c2", 5, 12], -# ["c1", "All Item Groups", 6, 11], -# ["gc1", "c1", 7, 8], -# ["gc2", "c1", 9, 10], -# ["gc5", "c2", 13, 14], -# ["gc3", "c2", 15, 16], -# ["c3", "All Item Groups", 18, 19] -# ] -# self.test_basic_tree(new_tree) -# -# # delete leaf -# from webnotes.model import delete_doc -# delete_doc(self.gc2.doc.doctype, self.gc2.doc.name) -# -# new_tree = [ -# ["All Item Groups", None, 1, 18], -# ["c0", "All Item Groups", 2, 3], -# ["c2", "All Item Groups", 4, 15], -# ["gc4", "c2", 5, 10], -# ["c1", "All Item Groups", 6, 9], -# ["gc1", "c1", 7, 8], -# ["gc5", "c2", 11, 12], -# ["gc3", "c2", 13, 14], -# ["c3", "All Item Groups", 16, 17] -# ] -# -# del self.__dict__["gc2"] -# self.reload_all(new_tree) -# self.test_basic_tree(new_tree) -# -# if __name__=="__main__": -# import webnotes -# webnotes.connect() -# unittest.main() diff --git a/webnotes/utils/nestedset.py b/webnotes/utils/nestedset.py index 6224b6bb27..ab654ba97f 100644 --- a/webnotes/utils/nestedset.py +++ b/webnotes/utils/nestedset.py @@ -15,6 +15,9 @@ from __future__ import unicode_literals import webnotes from webnotes import msgprint, _ +class NestedSetRecursionError(webnotes.ValidationError): pass +class NestedSetMultipleRootsError(webnotes.ValidationError): pass + # called in the on_update method def update_nsm(doc_obj): # get fields, data from the DocType @@ -175,7 +178,7 @@ def validate_loop(doctype, name, lft, rgt): """check if item not an ancestor (loop)""" if name in webnotes.conn.sql_list("""select name from `tab%s` where lft <= %s and rgt >= %s""" % (doctype, "%s", "%s"), (lft, rgt)): - webnotes.throw("""Item cannot be added to its own descendents.""") + webnotes.throw("""Item cannot be added to its own descendents.""", NestedSetRecursionError) class DocTypeNestedSet(object): def on_update(self): @@ -208,7 +211,7 @@ class DocTypeNestedSet(object): if not self.doc.fields[self.nsm_parent_field]: if webnotes.conn.sql("""select count(*) from `tab%s` where ifnull(%s, '')=''""" % (self.doc.doctype, self.nsm_parent_field))[0][0] > 1: - webnotes.throw(_("""Multiple root nodes not allowed.""")) + webnotes.throw(_("""Multiple root nodes not allowed."""), NestedSetMultipleRootsError) def validate_ledger(self, group_identifier="is_group"): if self.doc.fields.get(group_identifier) == "No": @@ -216,3 +219,17 @@ class DocTypeNestedSet(object): (self.doc.doctype, self.nsm_parent_field, '%s'), (self.doc.name)): webnotes.throw(self.doc.doctype + ": " + self.doc.name + _(" can not be marked as a ledger as it has existing child")) + +def get_root_of(doctype): + """Get root element of a DocType with a tree structure""" + result = webnotes.conn.sql_list("""select name from `tab%s` + where lft=1 and rgt=(select max(rgt) from `tab%s` where docstatus < 2)""" % + (doctype, doctype)) + return result[0] if result else None + +def get_ancestors_of(doctype, name): + """Get ancestor elements of a DocType with a tree structure""" + lft, rgt = webnotes.conn.get_value(doctype, name, ["lft", "rgt"]) + result = webnotes.conn.sql_list("""select name from `tab%s` + where lft<%s and rgt>%s order by lft desc""" % (doctype, "%s", "%s"), (lft, rgt)) + return result or []