344 lines
13 KiB
Python
344 lines
13 KiB
Python
import frappe
|
|
from frappe.custom.doctype.custom_field.custom_field import create_custom_fields
|
|
|
|
MULTITENANT_DOCTYPES = [
|
|
"SB Organization", "SB Incident", "SB Idea", "SB Order", "SB Process",
|
|
"SB Cycle", "SB KPI Input", "Managements", "Department", "Branch"
|
|
]
|
|
|
|
def ensure_group_company_doctype():
|
|
"""Ensure SB Group Company doctype exists."""
|
|
if frappe.db.exists("DocType", "SB Group Company"):
|
|
return
|
|
|
|
doc = frappe.get_doc({
|
|
"doctype": "DocType",
|
|
"name": "SB Group Company",
|
|
"module": "Sidrah Business",
|
|
"custom": 1,
|
|
"is_submittable": 0,
|
|
"issingle": 0,
|
|
"istable": 0,
|
|
"quick_entry": 1,
|
|
"track_changes": 1,
|
|
"autoname": "field:company_name",
|
|
"fields": [
|
|
{
|
|
"fieldname": "company_name",
|
|
"fieldtype": "Data",
|
|
"label": "Company Name",
|
|
"reqd": 1,
|
|
"unique": 1,
|
|
"in_list_view": 1,
|
|
},
|
|
{
|
|
"fieldname": "acronym",
|
|
"fieldtype": "Data",
|
|
"label": "Acronym",
|
|
"in_list_view": 1,
|
|
},
|
|
{
|
|
"fieldname": "code",
|
|
"fieldtype": "Data",
|
|
"label": "Company Code",
|
|
"unique": 1,
|
|
},
|
|
{
|
|
"fieldname": "status",
|
|
"fieldtype": "Select",
|
|
"label": "Status",
|
|
"options": "Active\nInactive",
|
|
"default": "Active",
|
|
"in_list_view": 1,
|
|
},
|
|
{
|
|
"fieldname": "owner_user",
|
|
"fieldtype": "Link",
|
|
"label": "Group Co Owner / Admin",
|
|
"options": "User",
|
|
},
|
|
{
|
|
"fieldname": "notes",
|
|
"fieldtype": "Small Text",
|
|
"label": "Notes",
|
|
},
|
|
{
|
|
"fieldname": "custom_is_deleted",
|
|
"fieldtype": "Check",
|
|
"label": "Is Deleted",
|
|
"default": "0",
|
|
"read_only": 1,
|
|
"hidden": 1,
|
|
},
|
|
{
|
|
"fieldname": "custom_deleted_by",
|
|
"fieldtype": "Link",
|
|
"label": "Deleted By",
|
|
"options": "User",
|
|
"read_only": 1,
|
|
"hidden": 1,
|
|
},
|
|
{
|
|
"fieldname": "custom_deleted_on",
|
|
"fieldtype": "Datetime",
|
|
"label": "Deleted On",
|
|
"read_only": 1,
|
|
"hidden": 1,
|
|
},
|
|
{
|
|
"fieldname": "custom_deleted_reason",
|
|
"fieldtype": "Small Text",
|
|
"label": "Deleted Reason",
|
|
"read_only": 1,
|
|
"hidden": 1,
|
|
},
|
|
],
|
|
"permissions": [
|
|
{
|
|
"role": "System Manager",
|
|
"read": 1, "write": 1, "create": 1, "delete": 1
|
|
},
|
|
{
|
|
"role": "Administrator",
|
|
"read": 1, "write": 1, "create": 1, "delete": 1
|
|
}
|
|
]
|
|
})
|
|
doc.insert(ignore_permissions=True)
|
|
frappe.db.commit()
|
|
|
|
|
|
DOCTYPES_INSERT_AFTER = {
|
|
"SB Organization": "organization_name",
|
|
"SB Incident": "organization",
|
|
"SB Idea": "organization",
|
|
"SB Order": "organization",
|
|
"SB Process": "organization",
|
|
"SB Cycle": "organization",
|
|
"SB KPI Input": "organization",
|
|
"Managements": "organization",
|
|
"Department": "custom_organization",
|
|
"Branch": "organization",
|
|
}
|
|
|
|
|
|
def sync_user_group_company_permission(user_doc, method=None):
|
|
"""
|
|
Synchronizes SB Group Company User Permissions for a user.
|
|
Rule:
|
|
- Skip Administrator, Guest, Platform Admin, and CEO (unrestricted across system).
|
|
- For regular users (Auditors, Managers, Employees):
|
|
Ensure User Permission exists with for_value = custom_group_company.
|
|
Remove any permissions with a different for_value.
|
|
- For Admins / CEOs:
|
|
Remove any restricting SB Group Company user permissions so they have full access.
|
|
"""
|
|
user_name = user_doc.name if hasattr(user_doc, "name") else str(user_doc)
|
|
if user_name in ("Administrator", "Guest"):
|
|
return
|
|
|
|
from sidrah_business.soft_delete import is_platform_admin
|
|
is_admin = is_platform_admin(user_name)
|
|
|
|
company = getattr(user_doc, "custom_group_company", None)
|
|
if company is None and frappe.db.exists("User", user_name):
|
|
company = frappe.db.get_value("User", user_name, "custom_group_company")
|
|
|
|
# Fetch all existing User Permission records for allow = 'SB Group Company' for this user
|
|
existing_perms = frappe.get_all(
|
|
"User Permission",
|
|
filters={"user": user_name, "allow": "SB Group Company"},
|
|
fields=["name", "for_value"]
|
|
)
|
|
|
|
if is_admin:
|
|
# Admins and CEOs should not have restricting user permissions
|
|
for perm in existing_perms:
|
|
frappe.delete_doc("User Permission", perm.name, ignore_permissions=True)
|
|
return
|
|
|
|
# Regular users (Auditors, Managers, Employees): create user permission for their company
|
|
if company:
|
|
has_correct = False
|
|
for perm in existing_perms:
|
|
if perm.for_value == company:
|
|
has_correct = True
|
|
else:
|
|
frappe.delete_doc("User Permission", perm.name, ignore_permissions=True)
|
|
|
|
if not has_correct:
|
|
new_perm = frappe.get_doc({
|
|
"doctype": "User Permission",
|
|
"user": user_name,
|
|
"allow": "SB Group Company",
|
|
"for_value": company,
|
|
"is_default": 1,
|
|
"apply_to_all_doctypes": 1,
|
|
})
|
|
new_perm.insert(ignore_permissions=True)
|
|
else:
|
|
for perm in existing_perms:
|
|
frappe.delete_doc("User Permission", perm.name, ignore_permissions=True)
|
|
|
|
# Synchronize category to Frappe Has Role
|
|
category = getattr(user_doc, "custom_category", None)
|
|
if category is None and frappe.db.exists("User", user_name):
|
|
category = frappe.db.get_value("User", user_name, "custom_category")
|
|
|
|
if category:
|
|
category_map = {
|
|
'Admin': ['SB Admin', 'System Manager'],
|
|
'Business Owner': ['SB Business Owner', 'Business Owner', 'SB Owner'],
|
|
'Manager': ['SB Manager'],
|
|
'Auditor': ['SB Auditor'],
|
|
'X-Auditor': ['SB X-Auditor', 'SB Auditor'],
|
|
'Employee': ['SB Employee'],
|
|
'CEO': ['Ceo', 'SB Admin', 'System Manager'],
|
|
}
|
|
target_roles = category_map.get(category, [])
|
|
user_roles = frappe.get_roles(user_name)
|
|
for r in target_roles:
|
|
if not frappe.db.exists("Has Role", {"parent": user_name, "role": r}) and frappe.db.exists("Role", r):
|
|
frappe.get_doc({
|
|
"doctype": "Has Role",
|
|
"parent": user_name,
|
|
"parenttype": "User",
|
|
"parentfield": "roles",
|
|
"role": r
|
|
}).insert(ignore_permissions=True)
|
|
|
|
# Auto pick Group Co Owner / Admin User
|
|
if category in ('Admin', 'CEO', 'Business Owner') and company and frappe.db.exists("SB Group Company", company):
|
|
gc_owner = frappe.db.get_value("SB Group Company", company, "owner_user")
|
|
if not gc_owner:
|
|
frappe.db.set_value("SB Group Company", company, "owner_user", user_name, update_modified=False)
|
|
|
|
|
|
def sync_all_user_group_company_permissions():
|
|
"""Batch-sync SB Group Company User Permissions for all users."""
|
|
users = frappe.get_all("User", filters={"name": ["not in", ["Administrator", "Guest"]]}, fields=["name", "custom_group_company"])
|
|
for u in users:
|
|
sync_user_group_company_permission(u)
|
|
frappe.db.commit()
|
|
|
|
|
|
def setup_multitenancy_schema():
|
|
"""Creates SB Group Company doctype and adds group_company custom fields to target doctypes."""
|
|
ensure_group_company_doctype()
|
|
|
|
# Create default Group Company if none exists
|
|
default_co = "Default Group Company"
|
|
if not frappe.db.exists("SB Group Company", default_co):
|
|
co_doc = frappe.new_doc("SB Group Company")
|
|
co_doc.company_name = default_co
|
|
co_doc.acronym = "DGC"
|
|
co_doc.code = "DEFAULT"
|
|
co_doc.status = "Active"
|
|
co_doc.insert(ignore_permissions=True)
|
|
frappe.db.commit()
|
|
|
|
custom_fields = {}
|
|
|
|
for dt in MULTITENANT_DOCTYPES:
|
|
if frappe.db.exists("DocType", dt):
|
|
custom_fields[dt] = [{
|
|
"fieldname": "group_company",
|
|
"label": "Group Company",
|
|
"fieldtype": "Link",
|
|
"options": "SB Group Company",
|
|
"in_list_view": 0,
|
|
"in_standard_filter": 1,
|
|
"hidden": 1,
|
|
"insert_after": DOCTYPES_INSERT_AFTER.get(dt, "organization"),
|
|
}]
|
|
|
|
# Add custom_group_company and custom_manager fields on User doctype
|
|
custom_fields["User"] = [
|
|
{
|
|
"fieldname": "custom_group_company",
|
|
"label": "Group Company",
|
|
"fieldtype": "Link",
|
|
"options": "SB Group Company",
|
|
"in_list_view": 1,
|
|
"in_standard_filter": 1,
|
|
"insert_after": "custom_category",
|
|
},
|
|
{
|
|
"fieldname": "custom_manager",
|
|
"label": "Reports To / Manager",
|
|
"fieldtype": "Link",
|
|
"options": "User",
|
|
"in_list_view": 1,
|
|
"in_standard_filter": 1,
|
|
"insert_after": "custom_group_company",
|
|
}
|
|
]
|
|
|
|
create_custom_fields(custom_fields, ignore_validate=True)
|
|
|
|
# Ensure existing Custom Field records have hidden=1 and right insert_after
|
|
for dt, insert_after in DOCTYPES_INSERT_AFTER.items():
|
|
cf_name = frappe.db.get_value("Custom Field", {"dt": dt, "fieldname": "group_company"})
|
|
if cf_name:
|
|
frappe.db.set_value("Custom Field", cf_name, {
|
|
"insert_after": insert_after,
|
|
"hidden": 1,
|
|
"in_list_view": 0,
|
|
}, update_modified=False)
|
|
frappe.clear_cache(doctype=dt)
|
|
|
|
user_cf = frappe.db.get_value("Custom Field", {"dt": "User", "fieldname": "custom_group_company"})
|
|
if user_cf:
|
|
frappe.db.set_value("Custom Field", user_cf, "insert_after", "custom_category", update_modified=False)
|
|
|
|
user_mgr_cf = frappe.db.get_value("Custom Field", {"dt": "User", "fieldname": "custom_manager"})
|
|
if user_mgr_cf:
|
|
frappe.db.set_value("Custom Field", user_mgr_cf, "insert_after", "custom_group_company", update_modified=False)
|
|
|
|
cat_cf = frappe.db.get_value("Custom Field", {"dt": "User", "fieldname": "custom_category"})
|
|
if cat_cf:
|
|
frappe.db.set_value("Custom Field", cat_cf, "options", "\nAdmin\nBusiness Owner\nCEO\nChairman\nAuditor\nX-Auditor\nManager\nEmployee", update_modified=False)
|
|
frappe.clear_cache(doctype="User")
|
|
|
|
# Sync Group Company owner_users as SB Admin
|
|
companies = frappe.get_all("SB Group Company", filters={"owner_user": ["is", "set"], "custom_is_deleted": 0}, fields=["name", "owner_user"])
|
|
for comp in companies:
|
|
if comp.owner_user and frappe.db.exists("User", comp.owner_user):
|
|
owner_doc = frappe.get_doc("User", comp.owner_user)
|
|
roles = [r.role for r in owner_doc.roles]
|
|
if "SB Admin" not in roles:
|
|
owner_doc.add_roles("SB Admin")
|
|
if not owner_doc.custom_group_company:
|
|
frappe.db.set_value("User", comp.owner_user, "custom_group_company", comp.name, update_modified=False)
|
|
|
|
# Backfill existing records to default Group Company if empty
|
|
orgs = frappe.db.get_all("SB Organization", filters={"group_company": ["is", "not set"]})
|
|
for o in orgs:
|
|
frappe.db.set_value("SB Organization", o.name, "group_company", default_co, update_modified=False)
|
|
|
|
users = frappe.db.get_all("User", filters={"custom_group_company": ["is", "not set"]})
|
|
for u in users:
|
|
if u.name != "Administrator":
|
|
frappe.db.set_value("User", u.name, "custom_group_company", default_co, update_modified=False)
|
|
|
|
# Backfill documents from organization's group_company
|
|
for dt in ["SB Incident", "SB Idea", "SB Order", "SB Process", "SB Cycle", "SB KPI Input", "Managements", "Department", "Branch"]:
|
|
if frappe.db.exists("DocType", dt):
|
|
docs = frappe.db.get_all(dt, filters={"group_company": ["is", "not set"]})
|
|
for d in docs:
|
|
org = None
|
|
if frappe.db.has_column(dt, "organization"):
|
|
org = frappe.db.get_value(dt, d.name, "organization")
|
|
elif frappe.db.has_column(dt, "custom_organization"):
|
|
org = frappe.db.get_value(dt, d.name, "custom_organization")
|
|
|
|
co = frappe.db.get_value("SB Organization", org, "group_company") if org else default_co
|
|
frappe.db.set_value(dt, d.name, "group_company", co or default_co, update_modified=False)
|
|
|
|
# Synchronize User Permissions for all non-default group companies
|
|
sync_all_user_group_company_permissions()
|
|
|
|
frappe.db.commit()
|
|
print("Multi-tenancy schema, insert_after positioning, & user permissions sync completed successfully.")
|
|
|