Skip to content

Commit bb55fbb

Browse files
authored
Merge pull request #507 from frappe/develop
2 parents 0ed61fe + 3dc7a25 commit bb55fbb

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

42 files changed

+2164
-1764
lines changed

crm/api/doc.py

+1-105
Original file line numberDiff line numberDiff line change
@@ -565,95 +565,6 @@ def get_fields_meta(doctype, restricted_fieldtypes=None, as_array=False):
565565
return fields_meta
566566

567567

568-
@frappe.whitelist()
569-
def get_sidebar_fields(doctype, name):
570-
if not frappe.db.exists("CRM Fields Layout", {"dt": doctype, "type": "Side Panel"}):
571-
return []
572-
layout = frappe.get_doc("CRM Fields Layout", {"dt": doctype, "type": "Side Panel"}).layout
573-
574-
if not layout:
575-
return []
576-
577-
layout = json.loads(layout)
578-
579-
not_allowed_fieldtypes = [
580-
"Tab Break",
581-
"Section Break",
582-
"Column Break",
583-
]
584-
585-
fields = frappe.get_meta(doctype).fields
586-
fields = [field for field in fields if field.fieldtype not in not_allowed_fieldtypes]
587-
588-
doc = frappe.get_cached_doc(doctype, name)
589-
has_high_permlevel_fields = any(df.permlevel > 0 for df in fields)
590-
if has_high_permlevel_fields:
591-
has_read_access_to_permlevels = doc.get_permlevel_access("read")
592-
has_write_access_to_permlevels = doc.get_permlevel_access("write")
593-
594-
for section in layout:
595-
section["name"] = section.get("name") or section.get("label")
596-
for field in section.get("fields") if section.get("fields") else []:
597-
field_obj = next((f for f in fields if f.fieldname == field), None)
598-
if field_obj:
599-
if field_obj.permlevel > 0:
600-
field_has_write_access = field_obj.permlevel in has_write_access_to_permlevels
601-
field_has_read_access = field_obj.permlevel in has_read_access_to_permlevels
602-
if not field_has_write_access and field_has_read_access:
603-
field_obj.read_only = 1
604-
if not field_has_read_access and not field_has_write_access:
605-
field_obj.hidden = 1
606-
section["fields"][section.get("fields").index(field)] = get_field_obj(field_obj)
607-
608-
fields_meta = {}
609-
for field in fields:
610-
fields_meta[field.fieldname] = field
611-
612-
return layout
613-
614-
615-
def get_field_obj(field):
616-
obj = {
617-
"label": field.label,
618-
"type": get_type(field),
619-
"name": field.fieldname,
620-
"hidden": field.hidden,
621-
"reqd": field.reqd,
622-
"read_only": field.read_only,
623-
"all_properties": field,
624-
}
625-
626-
obj["placeholder"] = field.get("placeholder") or "Add " + field.label + "..."
627-
628-
if field.fieldtype == "Link":
629-
obj["placeholder"] = field.get("placeholder") or "Select " + field.label + "..."
630-
obj["doctype"] = field.options
631-
elif field.fieldtype == "Select" and field.options:
632-
obj["placeholder"] = field.get("placeholder") or "Select " + field.label + "..."
633-
obj["options"] = [{"label": option, "value": option} for option in field.options.split("\n")]
634-
635-
if field.read_only:
636-
obj["tooltip"] = "This field is read only and cannot be edited."
637-
638-
return obj
639-
640-
641-
def get_type(field):
642-
if field.fieldtype == "Data" and field.options == "Phone":
643-
return "phone"
644-
elif field.fieldtype == "Data" and field.options == "Email":
645-
return "email"
646-
elif field.fieldtype == "Check":
647-
return "checkbox"
648-
elif field.fieldtype == "Int":
649-
return "number"
650-
elif field.fieldtype in ["Small Text", "Text", "Long Text"]:
651-
return "textarea"
652-
elif field.read_only:
653-
return "read_only"
654-
return field.fieldtype.lower()
655-
656-
657568
def get_assigned_users(doctype, name, default_assigned_to=None):
658569
assigned_users = frappe.get_all(
659570
"ToDo",
@@ -685,22 +596,7 @@ def get_fields(doctype: str, allow_all_fieldtypes: bool = False):
685596

686597
for field in fields:
687598
if field.fieldtype not in not_allowed_fieldtypes and field.fieldname:
688-
_fields.append(
689-
{
690-
"label": field.label,
691-
"type": field.fieldtype,
692-
"value": field.fieldname,
693-
"options": field.options,
694-
"mandatory": field.reqd,
695-
"read_only": field.read_only,
696-
"hidden": field.hidden,
697-
"depends_on": field.depends_on,
698-
"mandatory_depends_on": field.mandatory_depends_on,
699-
"read_only_depends_on": field.read_only_depends_on,
700-
"link_filters": field.get("link_filters"),
701-
"placeholder": field.get("placeholder"),
702-
}
703-
)
599+
_fields.append(field)
704600

705601
return _fields
706602

crm/fcrm/doctype/crm_deal/crm_deal.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,7 @@ def assign_agent(self, agent):
8080
# the agent is already set as an assignee
8181
return
8282

83-
assign({"assign_to": [agent], "doctype": "CRM Deal", "name": self.name})
83+
assign({"assign_to": [agent], "doctype": "CRM Deal", "name": self.name}, ignore_permissions=True)
8484

8585
def share_with_agent(self, agent):
8686
if not agent:

crm/fcrm/doctype/crm_fields_layout/crm_fields_layout.json

+11-2
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@
3737
"fieldname": "layout",
3838
"fieldtype": "Code",
3939
"label": "Layout",
40-
"options": "JS"
40+
"options": "JSON"
4141
},
4242
{
4343
"fieldname": "column_break_post",
@@ -46,7 +46,7 @@
4646
],
4747
"index_web_pages_for_search": 1,
4848
"links": [],
49-
"modified": "2024-12-29 12:58:54.280569",
49+
"modified": "2025-01-02 22:12:51.663011",
5050
"modified_by": "Administrator",
5151
"module": "FCRM",
5252
"name": "CRM Fields Layout",
@@ -64,6 +64,15 @@
6464
"role": "System Manager",
6565
"share": 1,
6666
"write": 1
67+
},
68+
{
69+
"email": 1,
70+
"export": 1,
71+
"print": 1,
72+
"read": 1,
73+
"report": 1,
74+
"role": "All",
75+
"share": 1
6776
}
6877
],
6978
"sort_field": "creation",

crm/fcrm/doctype/crm_fields_layout/crm_fields_layout.py

+137-31
Original file line numberDiff line numberDiff line change
@@ -6,14 +6,15 @@
66
import frappe
77
from frappe import _
88
from frappe.model.document import Document
9+
from frappe.utils import random_string
910

1011

1112
class CRMFieldsLayout(Document):
1213
pass
1314

1415

1516
@frappe.whitelist()
16-
def get_fields_layout(doctype: str, type: str):
17+
def get_fields_layout(doctype: str, type: str, parent_doctype: str | None = None):
1718
tabs = []
1819
layout = None
1920

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

3132
if not has_tabs:
32-
tabs = [{"no_tabs": True, "sections": tabs}]
33+
tabs = [{"name": "first_tab", "sections": tabs}]
3334

3435
allowed_fields = []
3536
for tab in tabs:
3637
for section in tab.get("sections"):
37-
if not section.get("fields"):
38+
if "columns" not in section:
3839
continue
39-
allowed_fields.extend(section.get("fields"))
40+
for column in section.get("columns"):
41+
if not column.get("fields"):
42+
continue
43+
allowed_fields.extend(column.get("fields"))
4044

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

4448
for tab in tabs:
4549
for section in tab.get("sections"):
46-
for field in section.get("fields") if section.get("fields") else []:
47-
field = next((f for f in fields if f.fieldname == field), None)
48-
if field:
49-
field = {
50-
"label": _(field.label),
51-
"name": field.fieldname,
52-
"type": field.fieldtype,
53-
"options": getOptions(field),
54-
"mandatory": field.reqd,
55-
"read_only": field.read_only,
56-
"placeholder": field.get("placeholder"),
57-
"filters": field.get("link_filters"),
58-
}
59-
section["fields"][section.get("fields").index(field["name"])] = field
50+
for column in section.get("columns") if section.get("columns") else []:
51+
for field in column.get("fields") if column.get("fields") else []:
52+
field = next((f for f in fields if f.fieldname == field), None)
53+
if field:
54+
field = field.as_dict()
55+
handle_perm_level_restrictions(field, doctype, parent_doctype)
56+
column["fields"][column.get("fields").index(field["fieldname"])] = field
6057

6158
return tabs or []
6259

6360

61+
@frappe.whitelist()
62+
def get_sidepanel_sections(doctype):
63+
if not frappe.db.exists("CRM Fields Layout", {"dt": doctype, "type": "Side Panel"}):
64+
return []
65+
layout = frappe.get_doc("CRM Fields Layout", {"dt": doctype, "type": "Side Panel"}).layout
66+
67+
if not layout:
68+
return []
69+
70+
layout = json.loads(layout)
71+
72+
not_allowed_fieldtypes = [
73+
"Tab Break",
74+
"Section Break",
75+
"Column Break",
76+
]
77+
78+
fields = frappe.get_meta(doctype).fields
79+
fields = [field for field in fields if field.fieldtype not in not_allowed_fieldtypes]
80+
81+
for section in layout:
82+
section["name"] = section.get("name") or section.get("label")
83+
for column in section.get("columns") if section.get("columns") else []:
84+
for field in column.get("fields") if column.get("fields") else []:
85+
field_obj = next((f for f in fields if f.fieldname == field), None)
86+
if field_obj:
87+
field_obj = field_obj.as_dict()
88+
handle_perm_level_restrictions(field_obj, doctype)
89+
column["fields"][column.get("fields").index(field)] = get_field_obj(field_obj)
90+
91+
fields_meta = {}
92+
for field in fields:
93+
fields_meta[field.fieldname] = field
94+
95+
return layout
96+
97+
98+
def handle_perm_level_restrictions(field, doctype, parent_doctype=None):
99+
if field.permlevel == 0:
100+
return
101+
field_has_write_access = field.permlevel in get_permlevel_access("write", doctype, parent_doctype)
102+
field_has_read_access = field.permlevel in get_permlevel_access("read", doctype, parent_doctype)
103+
104+
if not field_has_write_access and field_has_read_access:
105+
field.read_only = 1
106+
if not field_has_read_access and not field_has_write_access:
107+
field.hidden = 1
108+
109+
110+
def get_permlevel_access(permission_type="write", doctype=None, parent_doctype=None):
111+
allowed_permlevels = []
112+
roles = frappe.get_roles()
113+
114+
meta = frappe.get_meta(doctype)
115+
116+
if meta.istable and parent_doctype:
117+
meta = frappe.get_meta(parent_doctype)
118+
elif meta.istable and not parent_doctype:
119+
return [1, 0]
120+
121+
for perm in meta.permissions:
122+
if perm.role in roles and perm.get(permission_type) and perm.permlevel not in allowed_permlevels:
123+
allowed_permlevels.append(perm.permlevel)
124+
125+
return allowed_permlevels
126+
127+
128+
def get_field_obj(field):
129+
field["placeholder"] = field.get("placeholder") or "Add " + field.label + "..."
130+
131+
if field.fieldtype == "Link":
132+
field["placeholder"] = field.get("placeholder") or "Select " + field.label + "..."
133+
elif field.fieldtype == "Select" and field.options:
134+
field["placeholder"] = field.get("placeholder") or "Select " + field.label + "..."
135+
field["options"] = [{"label": option, "value": option} for option in field.options.split("\n")]
136+
137+
if field.read_only:
138+
field["tooltip"] = "This field is read only and cannot be edited."
139+
140+
return field
141+
142+
64143
@frappe.whitelist()
65144
def save_fields_layout(doctype: str, type: str, layout: str):
66145
if frappe.db.exists("CRM Fields Layout", {"dt": doctype, "type": type}):
@@ -82,18 +161,45 @@ def save_fields_layout(doctype: str, type: str, layout: str):
82161

83162
def get_default_layout(doctype: str):
84163
fields = frappe.get_meta(doctype).fields
85-
fields = [
86-
field.fieldname
87-
for field in fields
88-
if field.fieldtype not in ["Tab Break", "Section Break", "Column Break"]
89-
]
90-
91-
return [{"no_tabs": True, "sections": [{"hideLabel": True, "fields": fields}]}]
92164

165+
tabs = []
93166

94-
def getOptions(field):
95-
if field.fieldtype == "Select" and field.options:
96-
field.options = field.options.split("\n")
97-
field.options = [{"label": _(option), "value": option} for option in field.options]
98-
field.options.insert(0, {"label": "", "value": ""})
99-
return field.options
167+
if fields and fields[0].fieldtype != "Tab Break":
168+
sections = []
169+
if fields and fields[0].fieldtype != "Section Break":
170+
sections.append(
171+
{
172+
"name": "section_" + str(random_string(4)),
173+
"columns": [{"name": "column_" + str(random_string(4)), "fields": []}],
174+
}
175+
)
176+
tabs.append({"name": "tab_" + str(random_string(4)), "sections": sections})
177+
178+
for field in fields:
179+
if field.fieldtype == "Tab Break":
180+
tabs.append(
181+
{
182+
"name": "tab_" + str(random_string(4)),
183+
"sections": [
184+
{
185+
"name": "section_" + str(random_string(4)),
186+
"columns": [{"name": "column_" + str(random_string(4)), "fields": []}],
187+
}
188+
],
189+
}
190+
)
191+
elif field.fieldtype == "Section Break":
192+
tabs[-1]["sections"].append(
193+
{
194+
"name": "section_" + str(random_string(4)),
195+
"columns": [{"name": "column_" + str(random_string(4)), "fields": []}],
196+
}
197+
)
198+
elif field.fieldtype == "Column Break":
199+
tabs[-1]["sections"][-1]["columns"].append(
200+
{"name": "column_" + str(random_string(4)), "fields": []}
201+
)
202+
else:
203+
tabs[-1]["sections"][-1]["columns"][-1]["fields"].append(field.fieldname)
204+
205+
return tabs

crm/fcrm/doctype/crm_industry/crm_industry.json

+10-1
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@
2121
],
2222
"index_web_pages_for_search": 1,
2323
"links": [],
24-
"modified": "2024-01-19 21:57:02.025918",
24+
"modified": "2025-01-02 22:14:28.686821",
2525
"modified_by": "Administrator",
2626
"module": "FCRM",
2727
"name": "CRM Industry",
@@ -51,6 +51,15 @@
5151
"role": "Sales Manager",
5252
"share": 1,
5353
"write": 1
54+
},
55+
{
56+
"email": 1,
57+
"export": 1,
58+
"print": 1,
59+
"read": 1,
60+
"report": 1,
61+
"role": "All",
62+
"share": 1
5463
}
5564
],
5665
"quick_entry": 1,

0 commit comments

Comments
 (0)