@ -19,6 +19,8 @@
# If not, see <http://www.gnu.org/licenses/>.
#
################################################################################
from xlrd . xlsx import ET
from odoo import api , fields , models , _
@ -65,7 +67,7 @@ class DynamicFields(models.Model):
required = True , help = " Position of new field " )
model_id = fields . Many2one ( comodel_name = ' ir.model ' , string = ' Model ' ,
required = True ,
index = True , ondelete = ' cascade ' ,
index = True ,
help = " The model this field belongs to " )
ref_model_id = fields . Many2one ( comodel_name = ' ir.model ' , string = ' Relational '
' Model ' ,
@ -76,16 +78,20 @@ class DynamicFields(models.Model):
field_type = fields . Selection ( selection = ' get_possible_field_types ' ,
string = ' Field Type ' , required = True ,
help = " Data type of new field " )
tree_field_ids = fields . Many2many ( ' ir.model.fields ' ,
' tree_field_ids ' ,
compute = ' _compute_tree_field_ids ' )
ttype = fields . Selection ( string = " Field Type " , related = ' field_type ' ,
help = " Field type of field " )
widget = fields . Many2one ( comodel_name = ' dynamic.field.widgets ' ,
string = ' Widget ' , help = " Widgets for field " ,
domain = lambda self : " [( ' data_type ' , ' = ' , "
" field_type)] " )
widget_id = fields . Many2one ( comodel_name = ' dynamic.field.widgets ' ,
string = ' Widget ' , help = " Widgets for field " ,
domain = lambda self : " [( ' data_type ' , ' = ' , "
" field_type)] " )
groups = fields . Many2many ( ' res.groups ' ,
' employee_dynamic_fields_group_rel ' ,
' field_id ' , ' group_id ' ,
help = " Groups of field " )
' dynamic_fields_group_rel ' ,
' dynamic_field_id ' ,
' dynamic_group_id ' ,
help = " Groups of field " )
extra_features = fields . Boolean ( string = " Show Extra Properties " ,
help = " Enable to add extra features " )
status = fields . Selection ( selection = [ ( ' draft ' , ' Draft ' ) , ( ' form ' ,
@ -93,7 +99,7 @@ class DynamicFields(models.Model):
string = ' Status ' ,
index = True , readonly = True , tracking = True ,
copy = False , default = ' draft ' ,
required = True , help = ' State for record ' )
help = ' State for record ' )
form_view_ids = fields . Many2many ( comodel_name = ' ir.ui.view ' ,
string = " Form View IDs " ,
help = " Stores form view ids " )
@ -112,26 +118,78 @@ class DynamicFields(models.Model):
help = " Form view inherit id(adds "
" by selecting form view id) " )
add_field_in_tree = fields . Boolean ( string = " Add Field to the Tree View " ,
default = False ,
help = " Enable to add field in tree view " )
tree_view_id = fields . Many2one ( comodel_name = ' ir.ui.view ' ,
string = " Tree View ID " ,
help = " Tree view id of the model " ,
domain = lambda self : " [( ' id ' , ' in ' , "
" tree_view_ids)] " )
tree_view_inherit_id = fields . Char ( string = " Tree View Inherit Id" ,
tree_view_inherit_id = fields . Char ( string = " External Id" ,
related = ' tree_view_id.xml_id ' ,
help = " Tree view inherit id(adds "
" by selecting tree view id) " )
tree_field_id = fields . Many2one ( ' ir.model.fields ' ,
string = ' Tree Field ' ,
help = ' Position for new field ' ,
domain = " [( ' id ' , ' in ' , tree_field_ids)] " )
tree_field_position = fields . Selection ( selection = [ ( ' before ' , ' Before ' ) ,
( ' after ' , ' After ' ) ] ,
string = ' Tree Position ' ,
help = " Position of new field in "
" tree view " )
is_visible_in_tree_view = fields . Boolean ( string = ' Visible In List View ' ,
help = " Enable to make the field "
" visible in selected list "
" view of the model " )
created_tree_view_id = fields . Many2one ( ' ir.ui.view ' ,
string = ' Created Tree view ' ,
help = ' This is the currently '
' created tree view ' )
created_form_view_id = fields . Many2one ( ' ir.ui.view ' ,
string = ' Created form view ' ,
help = ' Created form view id for the '
' dynamic field ' )
@api . depends ( ' tree_view_id ' )
def _compute_tree_field_ids ( self ) :
""" Compute function to find the tree view fields of selected tree view
in field tree_view_id """
for rec in self :
if rec . tree_view_id :
field_list = [ ]
if rec . tree_view_id . xml_id :
fields = ET . fromstring ( self . env . ref (
rec . tree_view_id . xml_id ) . arch ) . findall ( " .//field " )
for field in fields :
field_list . append ( field . get ( ' name ' ) )
inherit_id = rec . tree_view_id . inherit_id if rec . tree_view_id . inherit_id else False
while inherit_id :
if inherit_id . xml_id :
fields = ET . fromstring ( self . env . ref (
inherit_id . xml_id ) . arch ) . findall ( " .//field " )
for field in fields :
field_list . append ( field . get ( ' name ' ) )
inherit_id = inherit_id . inherit_id if inherit_id . inherit_id else False
self . tree_field_ids = self . env [ ' ir.model.fields ' ] . search (
[ ( ' model_id ' , ' = ' , self . model_id . id ) ,
( ' name ' , ' in ' , field_list ) ] )
else :
rec . tree_field_ids = False
@api . onchange ( ' add_field_in_tree ' )
def _onchange_add_field_in_tree ( self ) :
""" Function to clear values of tree_view_id and tree_field_id """
if not self . add_field_in_tree :
self . tree_view_id = False
self . tree_field_id = False
def action_create_dynamic_field ( self ) :
""" Function to create dynamic field to a particular model, data type,
properties and etc """
self . write ( { ' status ' : ' form ' } )
if self . field_type == ' monetary ' and not self . env [
' ir.model.fields ' ] . sudo ( ) . search ( [
( ' model ' , ' = ' , self . model_id . id ) ,
( ' name ' , ' = ' , ' currency_id ' ) ] ) :
' ir.model.fields ' ] . sudo ( ) . search ( [ ( ' model ' , ' = ' , self . model_id . id ) ,
( ' name ' , ' = ' , ' currency_id ' ) ] ) :
self . env [ ' ir.model.fields ' ] . sudo ( ) . create ( {
' name ' : ' x_currency_id ' ,
' field_description ' : ' Currency ' ,
@ -139,6 +197,7 @@ class DynamicFields(models.Model):
' ttype ' : ' many2one ' ,
' relation ' : ' res.currency ' ,
' is_dynamic_field ' : True
} )
self . env [ ' ir.model.fields ' ] . sudo ( ) . create ( {
' name ' : self . name ,
@ -156,8 +215,8 @@ class DynamicFields(models.Model):
' is_dynamic_field ' : True
} )
inherit_form_view_name = str (
self . form_view_id . name ) + " .inherit.dynamic.custom. " + str ( self .
field_description ) + " .field "
self . form_view_id . name ) + " .inherit.dynamic.custom. " + str (
self . field_description ) + " .field "
xml_id = self . form_view_id . xml_id
inherit_id = self . env . ref ( xml_id )
arch_base = _ ( ' <?xml version= " 1.0 " ?> '
@ -167,7 +226,7 @@ class DynamicFields(models.Model):
' </field> '
' </data> ' ) % ( self . position_field_id . name ,
self . position , self . name )
if self . widget :
if self . widget_id :
arch_base = _ ( ' <?xml version= " 1.0 " ?> '
' <data> '
' <field name= " %s " position= " %s " > '
@ -175,8 +234,8 @@ class DynamicFields(models.Model):
' </field> '
' </data> ' ) % ( self . position_field_id . name ,
self . position , self . name ,
self . widget . name )
self . form_view_id = self . env [ ' ir.ui.view ' ] . sudo ( ) . create ( {
self . widget_id . name )
self . created_ form_view_id = self . env [ ' ir.ui.view ' ] . sudo ( ) . create ( {
' name ' : inherit_form_view_name ,
' type ' : ' form ' ,
' model ' : self . model_id . model ,
@ -194,22 +253,23 @@ class DynamicFields(models.Model):
def action_create_to_tree_view ( self ) :
""" Function to add field to tree view """
if self . add_field_in_tree :
optional = " show " if self . is_visible_in_tree_view else " hide "
tree_view_arch_base = ( _ ( f '''
< data >
< xpath expr = " //field[@name= ' {self.tree_field_id.name} ' ] " position = " {self.tree_field_position} " >
< field name = " {self.name} " optional = " {optional} " / >
< / xpath >
< / data > ''' ))
inherit_tree_view_name = str (
self . tree_view_id . name ) + " .inherit.dynamic.custom " + \
str ( self . field_description ) + " .field "
self . tree_view_id = self . env [ ' ir.ui.view ' ] . sudo ( ) . create ( {
self . created_ tree_view_id = self . env [ ' ir.ui.view ' ] . sudo ( ) . create ( {
' name ' : inherit_tree_view_name ,
' type ' : ' tree ' ,
' model ' : self . model_id . model ,
' mode ' : ' extension ' ,
' inherit_id ' : self . tree_view_id . id ,
' arch_base ' : _ (
' <?xml version= " 1.0 " ?> '
' <data> '
''' <xpath expr= " //tree " position= " inside " > '''
''' <field name= " %s " optional= " show " /> '''
''' </xpath> '''
''' </data> ''' ) % self . name ,
' arch_base ' : tree_view_arch_base ,
' active ' : True } )
return {
' type ' : ' ir.actions.client ' ,
@ -219,8 +279,8 @@ class DynamicFields(models.Model):
def unlink ( self ) :
""" Super unlink function """
if self . form_view_id :
self . form_view_id . active = False
self . created_ form_view_id. active = False
if self . tree_view_id :
self . tree_view_id . active = False
self . created_ tree_view_id. active = False
res = super ( DynamicFields , self ) . unlink ( )
return res