Hierarchical User Permissions

applied parent territorry in user permission manager but not get child territorry.like my parent territory is “XYZ” and "XYZ " have two child territorry “X” AND “Y” but i can’t access this two child territory in my doctype.

Unfortunately, hierarchical user permissions are not implemented in ERPNext, you will have to set all the territories specifically

Okay…Thank you

        territory_name=employee.get("territory")
        frappe.permissions.add_user_permission("Territory",territory_name,user)    
        territory_list=frappe.db.sql("select territory_name from tabTerritory where parent_territory=%s",territory_name)
        territory_list1=json.dumps(territory_list)
        territory_list2=json.loads(territory_list1)
        for i,list in enumerate(territory_list2):
            frappe.permissions.add_user_permission("Territory",list[0],user)
            child_territory_list=frappe.db.sql("select territory_name from tabTerritory where parent_territory=%s",list[0])
            child_territory_list1=json.dumps(child_territory_list)
            child_territory_list2=json.loads(child_territory_list1)
            for j,list in enumerate(child_territory_list2):
                frappe.permissions.add_user_permission("Territory",list[0],user)
2 Likes

hierarchical user permissions work using this code

I want to share my take on this :slight_smile:

I mostly doing the same as you but using Redis as cache, improving permission performance by 100x more or less without too much hassle. I am managing a set of 14.000 territories with 4 different levels and it is working as of now

def generate_territory_cache():
    conn = redis.from_url(frappe.local.conf.redis_cache)
    prefix = frappe.conf.redis_territory + "_"
    ##Set a key in order to indicate that the cache has been initialized
    conn.set(prefix + "cache", 1)

    top = frappe.get_list("Territory", fields=["name"], user="Administrator")
    for item in top:
        conn.set(prefix + item.name, ";".join(get_all_children(item.name)))

def get_children_from_cache(territory):
    ##configure cache by site
    conn = redis.from_url(frappe.local.conf.redis_cache)
    prefix = frappe.conf.redis_territory + "_"

    ##If the cache is not initialized, we create it
    if not conn.exists(prefix + "cache"):
        generate_territory_cache()

    try:
        return conn.get(prefix + territory).decode("utf-8").split(";")
    except AttributeError:
        generate_territory_cache()
        return get_all_children(territory)


def territory_query_conditions(user):
    if user == "Administrator":
        return ""
    else:
        territory = frappe.db.get_value("User", user, ["territory"])
        ##with cache, improved by 100x
        child_territories = get_children_from_cache(territory)
        ##without cache
        ##child_territories = get_all_children(territory)

        return u"""(territory  in ({territories}))""".format(
            territories='"' + '", "'.join(child_territories) + '"')
5 Likes

Opportunity to make a contribution :wink:

1 Like

long time but is this integrated into core?

how can do it can you please elaborate… thanks

Already implemented: Check “Hide descendants” in user permissions.

Ref: https://docs.erpnext.com/docs/v13/user/manual/en/setting-up/users-and-permissions/user-permissions


hello @rmehta,
Give me just one example using “Hide descendants” in user permissions.
User Manager have the right to read/write/submit/edit/create a sales invoice.
As User A and B do not have the permission to edit/submit other sales invoice but only there sales invoice which they created.
Requirement: i want to allow user A and B to only view other sales invoices while they are able to read/create/write/submit there own invoices.
any solutions?

as far as I know, user permission does not distinguish between different operations(read/write/delete). so for your requirement, custom script needed.