@@ -140,10 +140,13 @@ def process(self):
140
140
self .init_field_map ()
141
141
return
142
142
143
- self .add_custom_fields ()
143
+ has_custom_fields = self .add_custom_fields ()
144
144
self .apply_property_setters ()
145
145
self .init_field_map ()
146
- self .sort_fields ()
146
+
147
+ if has_custom_fields :
148
+ self .sort_fields ()
149
+
147
150
self .get_valid_columns ()
148
151
self .set_custom_permissions ()
149
152
self .add_custom_links_and_actions ()
@@ -319,7 +322,7 @@ def get_list_fields(self):
319
322
return list_fields
320
323
321
324
def get_custom_fields (self ):
322
- return [d for d in self .fields if d . get ( "is_custom_field" )]
325
+ return [d for d in self .fields if getattr ( d , "is_custom_field" , False )]
323
326
324
327
def get_title_field (self ):
325
328
"""Return the title field of this doctype,
@@ -358,17 +361,20 @@ def add_custom_fields(self):
358
361
if not frappe .db .table_exists ("Custom Field" ):
359
362
return
360
363
361
- custom_fields = frappe .db .sql (
362
- """
363
- SELECT * FROM `tabCustom Field`
364
- WHERE dt = %s AND docstatus < 2
365
- """ ,
366
- (self .name ,),
367
- as_dict = 1 ,
364
+ custom_fields = frappe .db .get_values (
365
+ "Custom Field" ,
366
+ filters = {"dt" : self .name },
367
+ fieldname = "*" ,
368
+ as_dict = True ,
369
+ order_by = "idx" ,
368
370
update = {"is_custom_field" : 1 },
369
371
)
370
372
373
+ if not custom_fields :
374
+ return
375
+
371
376
self .extend ("fields" , custom_fields )
377
+ return True
372
378
373
379
def apply_property_setters (self ):
374
380
"""
@@ -452,43 +458,33 @@ def init_field_map(self):
452
458
self ._fields = {field .fieldname : field for field in self .fields }
453
459
454
460
def sort_fields (self ):
455
- """sort on basis of insert_after"""
456
- custom_fields = sorted (self .get_custom_fields (), key = lambda df : df .idx )
461
+ """Sort custom fields on the basis of insert_after"""
457
462
458
- if custom_fields :
459
- newlist = []
463
+ field_order = []
464
+ insert_after_map = {}
460
465
461
- # if custom field is at top
462
- # insert_after is false
463
- for c in list (custom_fields ):
464
- if not c .insert_after :
465
- newlist .append (c )
466
- custom_fields .pop (custom_fields .index (c ))
466
+ for field in self .fields :
467
+ if not getattr (field , "is_custom_field" , False ):
468
+ field_order .append (field .fieldname )
467
469
468
- # standard fields
469
- newlist += [ df for df in self . get ( "fields" ) if not df . get ( "is_custom_field" )]
470
+ elif insert_after := getattr ( field , "insert_after" , None ):
471
+ insert_after_map . setdefault ( insert_after , []). append ( field . fieldname )
470
472
471
- newlist_fieldnames = [df .fieldname for df in newlist ]
472
- for i in range (2 ):
473
- for df in list (custom_fields ):
474
- if df .insert_after in newlist_fieldnames :
475
- cf = custom_fields .pop (custom_fields .index (df ))
476
- idx = newlist_fieldnames .index (df .insert_after )
477
- newlist .insert (idx + 1 , cf )
478
- newlist_fieldnames .insert (idx + 1 , cf .fieldname )
473
+ else :
474
+ # if custom field is at the top, insert after is None
475
+ field_order .insert (0 , field .fieldname )
479
476
480
- if not custom_fields :
481
- break
477
+ if insert_after_map :
478
+ _update_field_order_based_on_insert_after ( field_order , insert_after_map )
482
479
483
- # worst case, add remaining custom fields to last
484
- if custom_fields :
485
- newlist += custom_fields
480
+ sorted_fields = []
486
481
487
- # renum idx
488
- for i , f in enumerate (newlist ):
489
- f .idx = i + 1
482
+ for idx , fieldname in enumerate (field_order , 1 ):
483
+ field = self ._fields [fieldname ]
484
+ field .idx = idx
485
+ sorted_fields .append (field )
490
486
491
- self .fields = newlist
487
+ self .fields = sorted_fields
492
488
493
489
def set_custom_permissions (self ):
494
490
"""Reset `permissions` with Custom DocPerm if exists"""
@@ -809,3 +805,28 @@ def is_internal(field):
809
805
frappe .db .sql_ddl (f"ALTER TABLE `tab{ doctype } ` { columns_to_remove } " )
810
806
811
807
return DROPPED_COLUMNS
808
+
809
+
810
+ def _update_field_order_based_on_insert_after (field_order , insert_after_map ):
811
+ """Update the field order based on insert_after_map"""
812
+
813
+ retry_field_insertion = True
814
+
815
+ while retry_field_insertion :
816
+ retry_field_insertion = False
817
+
818
+ for fieldname in list (insert_after_map ):
819
+ if fieldname not in field_order :
820
+ continue
821
+
822
+ custom_field_index = field_order .index (fieldname )
823
+ for custom_field_name in insert_after_map .pop (fieldname ):
824
+ custom_field_index += 1
825
+ field_order .insert (custom_field_index , custom_field_name )
826
+
827
+ retry_field_insertion = True
828
+
829
+ if insert_after_map :
830
+ # insert_after is an invalid fieldname, add these fields to the end
831
+ for fields in insert_after_map .values ():
832
+ field_order .extend (fields )
0 commit comments