Skip to content

Commit

Permalink
Merge pull request #504 from shariquerik/section-column-layout
Browse files Browse the repository at this point in the history
  • Loading branch information
shariquerik authored Jan 2, 2025
2 parents 4b51801 + ee39b66 commit 76616cb
Show file tree
Hide file tree
Showing 40 changed files with 2,160 additions and 1,759 deletions.
106 changes: 1 addition & 105 deletions crm/api/doc.py
Original file line number Diff line number Diff line change
Expand Up @@ -565,95 +565,6 @@ def get_fields_meta(doctype, restricted_fieldtypes=None, as_array=False):
return fields_meta


@frappe.whitelist()
def get_sidebar_fields(doctype, name):
if not frappe.db.exists("CRM Fields Layout", {"dt": doctype, "type": "Side Panel"}):
return []
layout = frappe.get_doc("CRM Fields Layout", {"dt": doctype, "type": "Side Panel"}).layout

if not layout:
return []

layout = json.loads(layout)

not_allowed_fieldtypes = [
"Tab Break",
"Section Break",
"Column Break",
]

fields = frappe.get_meta(doctype).fields
fields = [field for field in fields if field.fieldtype not in not_allowed_fieldtypes]

doc = frappe.get_cached_doc(doctype, name)
has_high_permlevel_fields = any(df.permlevel > 0 for df in fields)
if has_high_permlevel_fields:
has_read_access_to_permlevels = doc.get_permlevel_access("read")
has_write_access_to_permlevels = doc.get_permlevel_access("write")

for section in layout:
section["name"] = section.get("name") or section.get("label")
for field in section.get("fields") if section.get("fields") else []:
field_obj = next((f for f in fields if f.fieldname == field), None)
if field_obj:
if field_obj.permlevel > 0:
field_has_write_access = field_obj.permlevel in has_write_access_to_permlevels
field_has_read_access = field_obj.permlevel in has_read_access_to_permlevels
if not field_has_write_access and field_has_read_access:
field_obj.read_only = 1
if not field_has_read_access and not field_has_write_access:
field_obj.hidden = 1
section["fields"][section.get("fields").index(field)] = get_field_obj(field_obj)

fields_meta = {}
for field in fields:
fields_meta[field.fieldname] = field

return layout


def get_field_obj(field):
obj = {
"label": field.label,
"type": get_type(field),
"name": field.fieldname,
"hidden": field.hidden,
"reqd": field.reqd,
"read_only": field.read_only,
"all_properties": field,
}

obj["placeholder"] = field.get("placeholder") or "Add " + field.label + "..."

if field.fieldtype == "Link":
obj["placeholder"] = field.get("placeholder") or "Select " + field.label + "..."
obj["doctype"] = field.options
elif field.fieldtype == "Select" and field.options:
obj["placeholder"] = field.get("placeholder") or "Select " + field.label + "..."
obj["options"] = [{"label": option, "value": option} for option in field.options.split("\n")]

if field.read_only:
obj["tooltip"] = "This field is read only and cannot be edited."

return obj


def get_type(field):
if field.fieldtype == "Data" and field.options == "Phone":
return "phone"
elif field.fieldtype == "Data" and field.options == "Email":
return "email"
elif field.fieldtype == "Check":
return "checkbox"
elif field.fieldtype == "Int":
return "number"
elif field.fieldtype in ["Small Text", "Text", "Long Text"]:
return "textarea"
elif field.read_only:
return "read_only"
return field.fieldtype.lower()


def get_assigned_users(doctype, name, default_assigned_to=None):
assigned_users = frappe.get_all(
"ToDo",
Expand Down Expand Up @@ -685,22 +596,7 @@ def get_fields(doctype: str, allow_all_fieldtypes: bool = False):

for field in fields:
if field.fieldtype not in not_allowed_fieldtypes and field.fieldname:
_fields.append(
{
"label": field.label,
"type": field.fieldtype,
"value": field.fieldname,
"options": field.options,
"mandatory": field.reqd,
"read_only": field.read_only,
"hidden": field.hidden,
"depends_on": field.depends_on,
"mandatory_depends_on": field.mandatory_depends_on,
"read_only_depends_on": field.read_only_depends_on,
"link_filters": field.get("link_filters"),
"placeholder": field.get("placeholder"),
}
)
_fields.append(field)

return _fields

Expand Down
13 changes: 11 additions & 2 deletions crm/fcrm/doctype/crm_fields_layout/crm_fields_layout.json
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@
"fieldname": "layout",
"fieldtype": "Code",
"label": "Layout",
"options": "JS"
"options": "JSON"
},
{
"fieldname": "column_break_post",
Expand All @@ -46,7 +46,7 @@
],
"index_web_pages_for_search": 1,
"links": [],
"modified": "2024-12-29 12:58:54.280569",
"modified": "2025-01-02 22:12:51.663011",
"modified_by": "Administrator",
"module": "FCRM",
"name": "CRM Fields Layout",
Expand All @@ -64,6 +64,15 @@
"role": "System Manager",
"share": 1,
"write": 1
},
{
"email": 1,
"export": 1,
"print": 1,
"read": 1,
"report": 1,
"role": "All",
"share": 1
}
],
"sort_field": "creation",
Expand Down
168 changes: 137 additions & 31 deletions crm/fcrm/doctype/crm_fields_layout/crm_fields_layout.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,15 @@
import frappe
from frappe import _
from frappe.model.document import Document
from frappe.utils import random_string


class CRMFieldsLayout(Document):
pass


@frappe.whitelist()
def get_fields_layout(doctype: str, type: str):
def get_fields_layout(doctype: str, type: str, parent_doctype: str | None = None):
tabs = []
layout = None

Expand All @@ -29,38 +30,116 @@ def get_fields_layout(doctype: str, type: str):
has_tabs = tabs[0].get("sections") if tabs and tabs[0] else False

if not has_tabs:
tabs = [{"no_tabs": True, "sections": tabs}]
tabs = [{"name": "first_tab", "sections": tabs}]

allowed_fields = []
for tab in tabs:
for section in tab.get("sections"):
if not section.get("fields"):
if "columns" not in section:
continue
allowed_fields.extend(section.get("fields"))
for column in section.get("columns"):
if not column.get("fields"):
continue
allowed_fields.extend(column.get("fields"))

fields = frappe.get_meta(doctype).fields
fields = [field for field in fields if field.fieldname in allowed_fields]

for tab in tabs:
for section in tab.get("sections"):
for field in section.get("fields") if section.get("fields") else []:
field = next((f for f in fields if f.fieldname == field), None)
if field:
field = {
"label": _(field.label),
"name": field.fieldname,
"type": field.fieldtype,
"options": getOptions(field),
"mandatory": field.reqd,
"read_only": field.read_only,
"placeholder": field.get("placeholder"),
"filters": field.get("link_filters"),
}
section["fields"][section.get("fields").index(field["name"])] = field
for column in section.get("columns") if section.get("columns") else []:
for field in column.get("fields") if column.get("fields") else []:
field = next((f for f in fields if f.fieldname == field), None)
if field:
field = field.as_dict()
handle_perm_level_restrictions(field, doctype, parent_doctype)
column["fields"][column.get("fields").index(field["fieldname"])] = field

return tabs or []


@frappe.whitelist()
def get_sidepanel_sections(doctype):
if not frappe.db.exists("CRM Fields Layout", {"dt": doctype, "type": "Side Panel"}):
return []
layout = frappe.get_doc("CRM Fields Layout", {"dt": doctype, "type": "Side Panel"}).layout

if not layout:
return []

layout = json.loads(layout)

not_allowed_fieldtypes = [
"Tab Break",
"Section Break",
"Column Break",
]

fields = frappe.get_meta(doctype).fields
fields = [field for field in fields if field.fieldtype not in not_allowed_fieldtypes]

for section in layout:
section["name"] = section.get("name") or section.get("label")
for column in section.get("columns") if section.get("columns") else []:
for field in column.get("fields") if column.get("fields") else []:
field_obj = next((f for f in fields if f.fieldname == field), None)
if field_obj:
field_obj = field_obj.as_dict()
handle_perm_level_restrictions(field_obj, doctype)
column["fields"][column.get("fields").index(field)] = get_field_obj(field_obj)

fields_meta = {}
for field in fields:
fields_meta[field.fieldname] = field

return layout


def handle_perm_level_restrictions(field, doctype, parent_doctype=None):
if field.permlevel == 0:
return
field_has_write_access = field.permlevel in get_permlevel_access("write", doctype, parent_doctype)
field_has_read_access = field.permlevel in get_permlevel_access("read", doctype, parent_doctype)

if not field_has_write_access and field_has_read_access:
field.read_only = 1
if not field_has_read_access and not field_has_write_access:
field.hidden = 1


def get_permlevel_access(permission_type="write", doctype=None, parent_doctype=None):
allowed_permlevels = []
roles = frappe.get_roles()

meta = frappe.get_meta(doctype)

if meta.istable and parent_doctype:
meta = frappe.get_meta(parent_doctype)
elif meta.istable and not parent_doctype:
return [1, 0]

for perm in meta.permissions:
if perm.role in roles and perm.get(permission_type) and perm.permlevel not in allowed_permlevels:
allowed_permlevels.append(perm.permlevel)

return allowed_permlevels


def get_field_obj(field):
field["placeholder"] = field.get("placeholder") or "Add " + field.label + "..."

if field.fieldtype == "Link":
field["placeholder"] = field.get("placeholder") or "Select " + field.label + "..."
elif field.fieldtype == "Select" and field.options:
field["placeholder"] = field.get("placeholder") or "Select " + field.label + "..."
field["options"] = [{"label": option, "value": option} for option in field.options.split("\n")]

if field.read_only:
field["tooltip"] = "This field is read only and cannot be edited."

return field


@frappe.whitelist()
def save_fields_layout(doctype: str, type: str, layout: str):
if frappe.db.exists("CRM Fields Layout", {"dt": doctype, "type": type}):
Expand All @@ -82,18 +161,45 @@ def save_fields_layout(doctype: str, type: str, layout: str):

def get_default_layout(doctype: str):
fields = frappe.get_meta(doctype).fields
fields = [
field.fieldname
for field in fields
if field.fieldtype not in ["Tab Break", "Section Break", "Column Break"]
]

return [{"no_tabs": True, "sections": [{"hideLabel": True, "fields": fields}]}]

tabs = []

def getOptions(field):
if field.fieldtype == "Select" and field.options:
field.options = field.options.split("\n")
field.options = [{"label": _(option), "value": option} for option in field.options]
field.options.insert(0, {"label": "", "value": ""})
return field.options
if fields and fields[0].fieldtype != "Tab Break":
sections = []
if fields and fields[0].fieldtype != "Section Break":
sections.append(
{
"name": "section_" + str(random_string(4)),
"columns": [{"name": "column_" + str(random_string(4)), "fields": []}],
}
)
tabs.append({"name": "tab_" + str(random_string(4)), "sections": sections})

for field in fields:
if field.fieldtype == "Tab Break":
tabs.append(
{
"name": "tab_" + str(random_string(4)),
"sections": [
{
"name": "section_" + str(random_string(4)),
"columns": [{"name": "column_" + str(random_string(4)), "fields": []}],
}
],
}
)
elif field.fieldtype == "Section Break":
tabs[-1]["sections"].append(
{
"name": "section_" + str(random_string(4)),
"columns": [{"name": "column_" + str(random_string(4)), "fields": []}],
}
)
elif field.fieldtype == "Column Break":
tabs[-1]["sections"][-1]["columns"].append(
{"name": "column_" + str(random_string(4)), "fields": []}
)
else:
tabs[-1]["sections"][-1]["columns"][-1]["fields"].append(field.fieldname)

return tabs
11 changes: 10 additions & 1 deletion crm/fcrm/doctype/crm_industry/crm_industry.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
],
"index_web_pages_for_search": 1,
"links": [],
"modified": "2024-01-19 21:57:02.025918",
"modified": "2025-01-02 22:14:28.686821",
"modified_by": "Administrator",
"module": "FCRM",
"name": "CRM Industry",
Expand Down Expand Up @@ -51,6 +51,15 @@
"role": "Sales Manager",
"share": 1,
"write": 1
},
{
"email": 1,
"export": 1,
"print": 1,
"read": 1,
"report": 1,
"role": "All",
"share": 1
}
],
"quick_entry": 1,
Expand Down
Loading

0 comments on commit 76616cb

Please sign in to comment.