6
6
import frappe
7
7
from frappe import _
8
8
from frappe .model .document import Document
9
+ from frappe .utils import random_string
9
10
10
11
11
12
class CRMFieldsLayout (Document ):
12
13
pass
13
14
14
15
15
16
@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 ):
17
18
tabs = []
18
19
layout = None
19
20
@@ -29,38 +30,116 @@ def get_fields_layout(doctype: str, type: str):
29
30
has_tabs = tabs [0 ].get ("sections" ) if tabs and tabs [0 ] else False
30
31
31
32
if not has_tabs :
32
- tabs = [{"no_tabs " : True , "sections" : tabs }]
33
+ tabs = [{"name " : "first_tab" , "sections" : tabs }]
33
34
34
35
allowed_fields = []
35
36
for tab in tabs :
36
37
for section in tab .get ("sections" ):
37
- if not section . get ( "fields" ) :
38
+ if "columns" not in section :
38
39
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" ))
40
44
41
45
fields = frappe .get_meta (doctype ).fields
42
46
fields = [field for field in fields if field .fieldname in allowed_fields ]
43
47
44
48
for tab in tabs :
45
49
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
60
57
61
58
return tabs or []
62
59
63
60
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
+
64
143
@frappe .whitelist ()
65
144
def save_fields_layout (doctype : str , type : str , layout : str ):
66
145
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):
82
161
83
162
def get_default_layout (doctype : str ):
84
163
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 }]}]
92
164
165
+ tabs = []
93
166
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
0 commit comments