Browse Source

Jun 13: [FIX] Bug Fixed 'all_in_one_dynamic_custom_fields'

pull/320/head
RisvanaCybro 11 months ago
parent
commit
dc6ed16201
  1. 6
      all_in_one_dynamic_custom_fields/README.rst
  2. 6
      all_in_one_dynamic_custom_fields/__init__.py
  3. 17
      all_in_one_dynamic_custom_fields/__manifest__.py
  4. 16
      all_in_one_dynamic_custom_fields/data/widget_data.xml
  5. 5
      all_in_one_dynamic_custom_fields/doc/RELEASE_NOTES.md
  6. 8
      all_in_one_dynamic_custom_fields/models/__init__.py
  7. 19
      all_in_one_dynamic_custom_fields/models/dynamic_field_widgets.py
  8. 241
      all_in_one_dynamic_custom_fields/models/dynamic_fields.py
  9. 10
      all_in_one_dynamic_custom_fields/models/ir_model_fields.py
  10. 3
      all_in_one_dynamic_custom_fields/security/ir.model.access.csv
  11. 8
      all_in_one_dynamic_custom_fields/security/security.xml
  12. BIN
      all_in_one_dynamic_custom_fields/static/description/assets/screenshots/0.png
  13. BIN
      all_in_one_dynamic_custom_fields/static/description/assets/screenshots/1.png
  14. BIN
      all_in_one_dynamic_custom_fields/static/description/assets/screenshots/2.png
  15. BIN
      all_in_one_dynamic_custom_fields/static/description/assets/screenshots/3.png
  16. BIN
      all_in_one_dynamic_custom_fields/static/description/assets/screenshots/4.png
  17. 10
      all_in_one_dynamic_custom_fields/static/description/index.html
  18. 108
      all_in_one_dynamic_custom_fields/views/dynamic_fields.xml
  19. 116
      all_in_one_dynamic_custom_fields/views/dynamic_fields_views.xml

6
all_in_one_dynamic_custom_fields/README.rst

@ -1,11 +1,11 @@
.. image:: https://img.shields.io/badge/licence-AGPL--3-blue.svg .. image:: https://img.shields.io/badge/license-AGPL--3-blue.svg
:target: http://www.gnu.org/licenses/agpl-3.0-standalone.html :target: http://www.gnu.org/licenses/agpl-3.0-standalone.html
:alt: License: AGPL-3 :alt: License: AGPL-3
All in One Custom Dynamic Fields All in One Custom Dynamic Fields
================================ ================================
The features of module facilitates the addition of fields to any view within a model,
All in One Custom Dynamic Fields allowing users to specify attributes and properties at their preferred location.
Installation Installation
============ ============

6
all_in_one_dynamic_custom_fields/__init__.py

@ -1,9 +1,9 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
################################################################################### ################################################################################
# #
# Cybrosys Technologies Pvt. Ltd. # Cybrosys Technologies Pvt. Ltd.
# #
# Copyright (C) 2022-TODAY Cybrosys Technologies (<https://www.cybrosys.com>). # Copyright (C) 2022-TODAY Cybrosys Technologies (<https://www.cybrosys.com>)
# Author: Cybrosys Techno Solutions (<https://www.cybrosys.com>) # Author: Cybrosys Techno Solutions (<https://www.cybrosys.com>)
# #
# This program is free software: you can modify # This program is free software: you can modify
@ -19,5 +19,5 @@
# You should have received a copy of the GNU Affero General Public License # You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>. # along with this program. If not, see <https://www.gnu.org/licenses/>.
# #
################################################################################### ################################################################################
from . import models from . import models

17
all_in_one_dynamic_custom_fields/__manifest__.py

@ -1,9 +1,9 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
################################################################################### ################################################################################
# #
# Cybrosys Technologies Pvt. Ltd. # Cybrosys Technologies Pvt. Ltd.
# #
# Copyright (C) 2022-TODAY Cybrosys Technologies (<https://www.cybrosys.com>). # Copyright (C) 2022-TODAY Cybrosys Technologies (<https://www.cybrosys.com>)
# Author: Cybrosys Techno Solutions (<https://www.cybrosys.com>) # Author: Cybrosys Techno Solutions (<https://www.cybrosys.com>)
# #
# This program is free software: you can modify # This program is free software: you can modify
@ -19,26 +19,27 @@
# You should have received a copy of the GNU Affero General Public License # You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>. # along with this program. If not, see <https://www.gnu.org/licenses/>.
# #
################################################################################### ################################################################################
{ {
'name': 'All in One Dynamic Fields', 'name': 'All in One Dynamic Fields',
'version': '16.0.1.1.3', 'version': '16.0.1.2.4',
'summary': 'Create Custom Fields As Per Your Need Without Any Coding.',
'description': 'All in One Dynamic Fields, All in One Custom Fields, Dynamic Fields, Custom Fields, Create Fields Dynamically',
'category': 'Extra Tools', 'category': 'Extra Tools',
'summary': 'Create Custom Fields As Per Your Need Without Any Coding.',
'description': 'All in One Dynamic Fields, All in One Custom Fields, '
'Dynamic Fields, Custom Fields, Create Fields Dynamically',
'author': 'Cybrosys Techno Solutions', 'author': 'Cybrosys Techno Solutions',
'company': 'Cybrosys Techno Solutions', 'company': 'Cybrosys Techno Solutions',
'maintainer': 'Cybrosys Techno Solutions', 'maintainer': 'Cybrosys Techno Solutions',
'website': "https://www.cybrosys.com", 'website': "https://www.cybrosys.com",
'license': 'AGPL-3',
'depends': ['base'], 'depends': ['base'],
'data': [ 'data': [
'data/widget_data.xml', 'data/widget_data.xml',
'security/security.xml', 'security/security.xml',
'security/ir.model.access.csv', 'security/ir.model.access.csv',
'views/dynamic_fields.xml', 'views/dynamic_fields_views.xml',
], ],
'images': ['static/description/banner.png'], 'images': ['static/description/banner.png'],
'license': 'AGPL-3',
'installable': True, 'installable': True,
'auto_install': False, 'auto_install': False,
'application': False, 'application': False,

16
all_in_one_dynamic_custom_fields/data/widget_data.xml

@ -2,37 +2,37 @@
<odoo> <odoo>
<data noupdate="1"> <data noupdate="1">
<!-- Widget 'image' for 'binary' field --> <!-- Widget 'image' for 'binary' field -->
<record id="image_widget" model="dynamic.field.widgets"> <record id="dynamic_field_widgets_image" model="dynamic.field.widgets">
<field name="name">image</field> <field name="name">image</field>
<field name="description">Image</field> <field name="description">Image</field>
</record> </record>
<!-- Widget 'many2many_tags' for 'many2many' field --> <!-- Widget 'many2many_tags' for 'many2many' field -->
<record id="many2many_tag_widget" model="dynamic.field.widgets"> <record id="dynamic_field_widgets_many2many_tags" model="dynamic.field.widgets">
<field name="name">many2many_tags</field> <field name="name">many2many_tags</field>
<field name="description">Many2many Tags</field> <field name="description">Many2many Tags</field>
</record> </record>
<!-- Widget 'binary' for 'many2many' field --> <!-- Widget 'binary' for 'many2many' field -->
<record id="many2many_binary_widget" model="dynamic.field.widgets"> <record id="dynamic_field_widgets_binary" model="dynamic.field.widgets">
<field name="name">binary</field> <field name="name">binary</field>
<field name="description">Binary</field> <field name="description">Binary</field>
</record> </record>
<!-- Widget 'radio' for 'selection' field --> <!-- Widget 'radio' for 'selection' field -->
<record id="radio_widget" model="dynamic.field.widgets"> <record id="dynamic_field_widgets_radio" model="dynamic.field.widgets">
<field name="name">radio</field> <field name="name">radio</field>
<field name="description">Radio</field> <field name="description">Radio</field>
</record> </record>
<!-- Widget 'priority' for 'selection' field --> <!-- Widget 'priority' for 'selection' field -->
<record id="priority_widget" model="dynamic.field.widgets"> <record id="dynamic_field_widgets_priority" model="dynamic.field.widgets">
<field name="name">priority</field> <field name="name">priority</field>
<field name="description">Priority</field> <field name="description">Priority</field>
</record> </record>
<!-- Widget 'monetory' for 'float' field --> <!-- Widget 'monetary' for 'float' field -->
<record id="monetory_widget" model="dynamic.field.widgets"> <record id="dynamic_field_widgets_monetary" model="dynamic.field.widgets">
<field name="name">monetary</field> <field name="name">monetary</field>
<field name="description">Monetary</field> <field name="description">Monetary</field>
</record> </record>
<!-- Widget 'selection' for 'Many2one' field --> <!-- Widget 'selection' for 'Many2one' field -->
<record id="selection_widget" model="dynamic.field.widgets"> <record id="dynamic_field_widgets_selection" model="dynamic.field.widgets">
<field name="name">selection</field> <field name="name">selection</field>
<field name="description">Selection</field> <field name="description">Selection</field>
</record> </record>

5
all_in_one_dynamic_custom_fields/doc/RELEASE_NOTES.md

@ -4,3 +4,8 @@
#### Version 16.0.1.1.3 #### Version 16.0.1.1.3
##### ADD ##### ADD
- Initial commit for All in One Custom Dynamic Fields - Initial commit for All in One Custom Dynamic Fields
#### 12.06.2024
#### Version 16.0.1.2.4
##### UPDATE
- The latest module update includes enhancements to the list view configuration. You can now add the newly created field to the selected list view at the desired position, with the option to enable or disable its visibility by default.

8
all_in_one_dynamic_custom_fields/models/__init__.py

@ -1,9 +1,9 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
################################################################################### ################################################################################
# #
# Cybrosys Technologies Pvt. Ltd. # Cybrosys Technologies Pvt. Ltd.
# #
# Copyright (C) 2022-TODAY Cybrosys Technologies (<https://www.cybrosys.com>). # Copyright (C) 2024-TODAY Cybrosys Technologies (<https://www.cybrosys.com>)
# Author: Cybrosys Techno Solutions (<https://www.cybrosys.com>) # Author: Cybrosys Techno Solutions (<https://www.cybrosys.com>)
# #
# This program is free software: you can modify # This program is free software: you can modify
@ -19,7 +19,7 @@
# You should have received a copy of the GNU Affero General Public License # You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>. # along with this program. If not, see <https://www.gnu.org/licenses/>.
# #
################################################################################### ################################################################################
from . import ir_model_fields from . import ir_model_fields
from . import field_widgets from . import dynamic_field_widgets
from . import dynamic_fields from . import dynamic_fields

19
all_in_one_dynamic_custom_fields/models/field_widgets.py → all_in_one_dynamic_custom_fields/models/dynamic_field_widgets.py

@ -1,9 +1,9 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
################################################################################### ################################################################################
# #
# Cybrosys Technologies Pvt. Ltd. # Cybrosys Technologies Pvt. Ltd.
# #
# Copyright (C) 2022-TODAY Cybrosys Technologies (<https://www.cybrosys.com>). # Copyright (C) 2024-TODAY Cybrosys Technologies (<https://www.cybrosys.com>)
# Author: Cybrosys Techno Solutions (<https://www.cybrosys.com>) # Author: Cybrosys Techno Solutions (<https://www.cybrosys.com>)
# #
# This program is free software: you can modify # This program is free software: you can modify
@ -19,19 +19,20 @@
# You should have received a copy of the GNU Affero General Public License # You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>. # along with this program. If not, see <https://www.gnu.org/licenses/>.
# #
################################################################################### ################################################################################
from odoo import models, fields from odoo import models, fields
class FieldWidgets(models.Model): class DynamicFieldWidgets(models.Model):
"""We can't filter a selection field dynamically """We can't filter a selection field dynamically so when we select a
so when we select a field its widgets also need to change according to the selected field its widgets also need to change according to the selected field
field type, we can't do it by a 'selection' field, need a 'Many2one' field. type, we can't do it by a 'selection' field, need a 'Many2one' field.
""" """
_name = 'dynamic.field.widgets' _name = 'dynamic.field.widgets'
_rec_name = 'description' _rec_name = 'description'
_description = 'Field Widgets' _description = 'Field Widgets'
name = fields.Char(string="Name") name = fields.Char(string="Name", help='Name of the record')
description = fields.Char(string="Description") description = fields.Char(string="Description", help='Description given to '
'the record')

241
all_in_one_dynamic_custom_fields/models/dynamic_fields.py

@ -1,9 +1,9 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
################################################################################### ################################################################################
# #
# Cybrosys Technologies Pvt. Ltd. # Cybrosys Technologies Pvt. Ltd.
# #
# Copyright (C) 2022-TODAY Cybrosys Technologies (<https://www.cybrosys.com>). # Copyright (C) 2024-TODAY Cybrosys Technologies (<https://www.cybrosys.com>)
# Author: Cybrosys Techno Solutions (<https://www.cybrosys.com>) # Author: Cybrosys Techno Solutions (<https://www.cybrosys.com>)
# #
# This program is free software: you can modify # This program is free software: you can modify
@ -19,12 +19,15 @@
# You should have received a copy of the GNU Affero General Public License # You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>. # along with this program. If not, see <https://www.gnu.org/licenses/>.
# #
################################################################################### ################################################################################
from xlrd.xlsx import ET
from odoo import api, fields, models, _ from odoo import api, fields, models, _
from odoo.exceptions import ValidationError from odoo.exceptions import ValidationError
class DynamicFields(models.Model): class DynamicFields(models.Model):
"""Class DynamicFields to create fields dynamically to any model"""
_name = 'dynamic.fields' _name = 'dynamic.fields'
_rec_name = 'field_description' _rec_name = 'field_description'
_description = 'Custom Dynamic Fields' _description = 'Custom Dynamic Fields'
@ -39,49 +42,127 @@ class DynamicFields(models.Model):
field_list.remove(('reference', 'reference')) field_list.remove(('reference', 'reference'))
return field_list return field_list
position_field = fields.Many2one('ir.model.fields', string='Field Name', position_field_id = fields.Many2one('ir.model.fields',
required=True, string='Field Name',
ondelete='cascade') help='Position Field Name',
required=True,
ondelete='cascade')
position = fields.Selection([('before', 'Before'), position = fields.Selection([('before', 'Before'),
('after', 'After')], string='Position', ('after', 'After')], string='Position',
help='position of the newly created field',
required=True) required=True)
model_id = fields.Many2one('ir.model', string='Model', required=True, model_id = fields.Many2one('ir.model', string='Model',
required=True,
index=True, ondelete='cascade', index=True, ondelete='cascade',
help="The model this field belongs to") help="The model this field belongs to")
ref_model_id = fields.Many2one('ir.model', string='Model', index=True) ref_model_id = fields.Many2one('ir.model', string='Model',
selection_field = fields.Char(string="Selection Options") index=True, help='Reference model of the '
rel_field = fields.Many2one('ir.model.fields', string='Related Field') 'relational field')
selection_field = fields.Char(string="Selection Options",
help='Selection options for field')
rel_field_id = fields.Many2one('ir.model.fields',
string='Related Field', help='Related field')
field_type = fields.Selection(selection='get_possible_field_types', field_type = fields.Selection(selection='get_possible_field_types',
string='Field Type', required=True) string='Field Type',
ttype = fields.Selection(string="Field Type", related='field_type') help='Field type of newly created field',
widget = fields.Many2one('dynamic.field.widgets', string='Widget') required=True)
groups = fields.Many2many('res.groups', 'employee_dynamic_fields_group_rel', ttype = fields.Selection(string="Field Type", related='field_type',
'field_id', 'group_id') help='Field type')
extra_features = fields.Boolean(string="Show Extra Properties") widget_id = fields.Many2one('dynamic.field.widgets',
string='Widget',
help='Widget of the newly selected field')
groups = fields.Many2many('res.groups',
'employee_dynamic_fields_group_rel',
'field_id', 'group_id',
string='Groups', help='Groups')
is_extra_features = fields.Boolean(string="Show Extra Properties",
help='Show Extra Properties of newly '
'field')
created_from_id = fields.Many2one('ir.ui.view',
help='Created form view id for the field',
string='Dynamic created form view')
status = fields.Selection([ status = fields.Selection([
('draft', 'Draft'), ('draft', 'Draft'),
('form', 'Field Created'), ('form', 'Field Created'),
('tree', 'Added in Tree View'), ('tree', 'Added in Tree View'),
], string='Status', index=True, readonly=True, tracking=True, ], string='Status', index=True, readonly=True, tracking=True,
copy=False, default='draft', copy=False, default='draft',
required=True, help='Record Status') help='Record Status')
form_view_id = fields.Many2one('ir.ui.view', string="Form View ID", form_view_id = fields.Many2one('ir.ui.view',
string="Form View ID",
help='Form View ID',
required=True) required=True)
form_view_inherit_id = fields.Char(string="Form View Inherit Id", form_view_inherit_id = fields.Char(string="Form View Inherit Id",
help="Form View Inherit Id",
related='form_view_id.xml_id') related='form_view_id.xml_id')
add_field_in_tree = fields.Boolean(string="Add Field to the Tree View", is_add_field_in_tree = fields.Boolean(string="Add Field to the Tree View",
default=False) help="Add newly created Field to "
tree_view_id = fields.Many2one('ir.ui.view', string="Tree View ID") "the Tree View")
tree_view_id = fields.Many2one('ir.ui.view',
string="Tree View ID", help='Tree View ID')
tree_view_inherit_id = fields.Char(string="Tree View Inherit Id", tree_view_inherit_id = fields.Char(string="Tree View Inherit Id",
help='Tree View Inherit Id',
readonly=True,
related='tree_view_id.xml_id') related='tree_view_id.xml_id')
tree_field_position = fields.Selection([('before', 'Before'),
('after', 'After')],
string='Position',
help='Tree field position to the '
'selected tree field')
tree_field_ids = fields.Many2many('ir.model.fields',
string='Tree View Fields',
compute='_compute_tree_field_ids',
help='Domain field for tree view fields')
tree_view_field_id = fields.Many2one('ir.model.fields',
string='Tree View Field',
domain="[('id', 'in', "
"tree_field_ids)]",
help='Select a tree view field for '
'position of newly created '
'field in the tree view')
is_tree_view_toggle = fields.Boolean(string='Visible in tree view',
help='Enable to toggle view the newly '
'created view in selected tree '
'view')
created_tree_view_id = fields.Many2one('ir.ui.view',
help='Dynamic Created tree view id',
string='Dynamic Tree view id')
@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:
tree_fields = ET.fromstring(self.env.ref(
rec.tree_view_id.xml_id).arch).findall(".//field")
for field in tree_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:
tree_fields = ET.fromstring(self.env.ref(
inherit_id.xml_id).arch).findall(".//field")
for field in tree_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
def create_dynamic_fields(self): def action_create_dynamic_fields(self):
"""Function create Dynamic field"""
self.write({'status': 'form'}) self.write({'status': 'form'})
if self.field_type == 'monetary' and not self.env[ if self.field_type == 'monetary' and not self.env[
'ir.model.fields'].sudo().search([ 'ir.model.fields'].sudo().search([('model', '=', self.model_id.id),
('model', '=', self.model_id.id), ('name', '=', 'currency_id')]):
('name', '=', 'currency_id')]):
self.env['ir.model.fields'].sudo().create({ self.env['ir.model.fields'].sudo().create({
'name': 'x_currency_id', 'name': 'x_currency_id',
'field_description': 'Currency', 'field_description': 'Currency',
@ -105,9 +186,9 @@ class DynamicFields(models.Model):
'copied': self.copied, 'copied': self.copied,
'is_dynamic_field': True 'is_dynamic_field': True
}) })
inherit_form_view_name = str( inherit_form_view_name = (str(
self.form_view_id.name) + ".inherit.dynamic.custom." + \ self.form_view_id.name) + ".inherit.dynamic.custom." +
str(self.field_description) + ".field" str(self.field_description) + ".field")
xml_id = self.form_view_id.xml_id xml_id = self.form_view_id.xml_id
inherit_id = self.env.ref(xml_id) inherit_id = self.env.ref(xml_id)
arch_base = _('<?xml version="1.0"?>' arch_base = _('<?xml version="1.0"?>'
@ -115,18 +196,18 @@ class DynamicFields(models.Model):
'<field name="%s" position="%s">' '<field name="%s" position="%s">'
'<field name="%s"/>' '<field name="%s"/>'
'</field>' '</field>'
'</data>') % (self.position_field.name, '</data>') % (self.position_field_id.name,
self.position, self.name) self.position, self.name)
if self.widget: if self.widget_id:
arch_base = _('<?xml version="1.0"?>' arch_base = _('<?xml version="1.0"?>'
'<data>' '<data>'
'<field name="%s" position="%s">' '<field name="%s" position="%s">'
'<field name="%s" widget="%s"/>' '<field name="%s" widget="%s"/>'
'</field>' '</field>'
'</data>') % (self.position_field.name, '</data>') % (self.position_field_id.name,
self.position, self.name, self.position, self.name,
self.widget.name) self.widget_id.name)
self.form_view_id = self.env['ir.ui.view'].sudo().create({ self.created_from_id = self.env['ir.ui.view'].sudo().create({
'name': inherit_form_view_name, 'name': inherit_form_view_name,
'type': 'form', 'type': 'form',
'model': self.model_id.model, 'model': self.model_id.model,
@ -140,80 +221,82 @@ class DynamicFields(models.Model):
'tag': 'reload', 'tag': 'reload',
} }
def add_field_to_tree_view(self): def action_add_field_to_tree_view(self):
if self.add_field_in_tree: """Function to add created dynamic field to selected tree view"""
if self.add_field_in_tree: if self.is_add_field_in_tree:
inherit_tree_view_name = str( inherit_tree_view_name = str(
self.tree_view_id.name) + ".inherit.dynamic.custom" + \ self.tree_view_id.name) + ".inherit.dynamic.custom" + \
str(self.field_description) + ".field" str(self.field_description) + ".field"
tree_view_arch_base = _( optional = "show" if self.is_tree_view_toggle else "hide"
'<?xml version="1.0"?>' tree_view_arch_base = (_(f'''
'<data>' <data>
'''<xpath expr="//tree" position="inside">''' <xpath expr="//field[@name='{self.tree_view_field_id.name}']" position="{self.tree_field_position}">
'''<field name="%s" optional="show"/>''' <field name="{self.name}" optional="{optional}"/>
'''</xpath>''' </xpath>
'''</data>''') % self.name </data>'''))
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, 'name': inherit_tree_view_name,
'type': 'tree', 'type': 'tree',
'model': self.model_id.model, 'model': self.model_id.model,
'mode': 'extension', 'mode': 'extension',
'inherit_id': self.tree_view_id.id, 'inherit_id': self.tree_view_id.id,
'arch_base': tree_view_arch_base, 'arch_base': tree_view_arch_base,
'active': True}) 'active': True})
self.write({'status': 'tree'}) self.write({'status': 'tree'})
return { return {
'type': 'ir.actions.client', 'type': 'ir.actions.client',
'tag': 'reload', 'tag': 'reload',
} }
else: else:
raise ValidationError( raise ValidationError(
_('Error! Please select the boolean field Add Field to the Tree View.')) _('Error! Please select the boolean field Add Field to the '
'Tree View.'))
@api.depends('model_id','add_field_in_tree') @api.onchange('model_id', 'is_add_field_in_tree')
@api.onchange('model_id','add_field_in_tree')
def set_domain(self): def set_domain(self):
"""Return the fields that currently present in the form""" """Return the fields that currently present in the form"""
form_view_ids = self.model_id.view_ids.filtered( form_view_ids = self.model_id.view_ids.filtered(
lambda l: l.type == 'form' and l.mode == 'primary') lambda x: x.type == 'form' and x.mode == 'primary')
tree_view_ids = self.model_id.view_ids.filtered( tree_view_ids = self.model_id.view_ids.filtered(
lambda l: l.type == 'tree' and l.mode == 'primary') lambda x: x.type == 'tree' and x.mode == 'primary')
fields = self.env['ir.model.fields'].sudo().search([ fields_domain = self.env['ir.model.fields'].sudo().search([
('model', '=', self.model_id.model)]) ('model', '=', self.model_id.model)])
field_list = [] field_list = []
for rec in fields: for rec in fields_domain:
for field in rec: for field in rec:
field_list.append(field.id) field_list.append(field.id)
return {'domain': { return {'domain': {
'form_view_id': [('id', 'in', form_view_ids.ids)], 'form_view_id': [('id', 'in', form_view_ids.ids)],
'tree_view_id': [('id', 'in', tree_view_ids.ids)], 'tree_view_id': [('id', 'in', tree_view_ids.ids)],
'position_field': [('id', 'in', field_list)] 'position_field_id': [('id', 'in', field_list)]
}} }}
@api.depends('field_type')
@api.onchange('field_type') @api.onchange('field_type')
def onchange_field_type(self): def onchange_field_type(self):
"""Function to determine domain for field widget"""
if self.field_type: if self.field_type:
if self.field_type == 'binary': if self.field_type == 'binary':
return {'domain': {'widget': [('name', '=', 'image')]}} return {'domain': {'widget_id': [('name', '=', 'image')]}}
elif self.field_type == 'many2many': elif self.field_type == 'many2many':
return {'domain': {'widget': [ return {'domain': {'widget_id': [
('name', 'in', ['many2many_tags', 'binary'])]}} ('name', 'in', ['many2many_tags', 'binary'])]}}
elif self.field_type == 'selection': elif self.field_type == 'selection':
return {'domain': { return {'domain': {
'widget': [('name', 'in', ['radio', 'priority'])]}} 'widget_id': [('name', 'in', ['radio', 'priority'])]}}
elif self.field_type == 'float': elif self.field_type == 'float':
return {'domain': {'widget': [('name', '=', 'monetary')]}} return {'domain': {'widget_id': [('name', '=', 'monetary')]}}
elif self.field_type == 'many2one': elif self.field_type == 'many2one':
return {'domain': {'widget': [('name', '=', 'selection')]}} return {'domain': {'widget_id': [('name', '=', 'selection')]}}
else: else:
return {'domain': {'widget': [('id', '=', False)]}} return {'domain': {'widget_id': [('id', '=', False)]}}
return {'domain': {'widget': [('id', '=', False)]}} return {'domain': {'widget_id': [('id', '=', False)]}}
def unlink(self): def unlink(self):
if self.form_view_id: """Unlink function to make the created tree view and from view to
self.form_view_id.active = False inactive"""
if self.tree_view_id: if self.created_from_id:
self.tree_view_id.active = False self.created_from_id.active = False
if self.created_tree_view_id:
self.created_tree_view_id.active = False
res = super(DynamicFields, self).unlink() res = super(DynamicFields, self).unlink()
return res return res

10
all_in_one_dynamic_custom_fields/models/ir_model_fields.py

@ -1,9 +1,9 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
################################################################################### ################################################################################
# #
# Cybrosys Technologies Pvt. Ltd. # Cybrosys Technologies Pvt. Ltd.
# #
# Copyright (C) 2022-TODAY Cybrosys Technologies (<https://www.cybrosys.com>). # Copyright (C) 2024-TODAY Cybrosys Technologies (<https://www.cybrosys.com>)
# Author: Cybrosys Techno Solutions (<https://www.cybrosys.com>) # Author: Cybrosys Techno Solutions (<https://www.cybrosys.com>)
# #
# This program is free software: you can modify # This program is free software: you can modify
@ -19,7 +19,7 @@
# You should have received a copy of the GNU Affero General Public License # You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>. # along with this program. If not, see <https://www.gnu.org/licenses/>.
# #
################################################################################### ################################################################################
from odoo import models, fields from odoo import models, fields
@ -28,4 +28,6 @@ class IrModelFields(models.Model):
_inherit = 'ir.model.fields' _inherit = 'ir.model.fields'
is_dynamic_field = fields.Boolean(string="Dynamic Field") is_dynamic_field = fields.Boolean(string="Dynamic Field",
help="id created using All In One "
"Dynamic Custom Fields")

3
all_in_one_dynamic_custom_fields/security/ir.model.access.csv

@ -1,5 +1,4 @@
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
access_all_in_one_dynamic_custom_fields_administrator,dynamic.fields,model_dynamic_fields,all_in_one_dynamic_custom_fields.group_all_in_one_dynamic_custom_fields_administrator,1,1,1,1 access_all_in_one_dynamic_custom_fields_administrator,dynamic.fields,model_dynamic_fields,all_in_one_dynamic_custom_fields.group_all_in_one_dynamic_custom_fields_administrator,1,1,1,1
access_all_in_one_dynamic_custom_fields_user,dynamic.fields,model_dynamic_fields,all_in_one_dynamic_custom_fields.group_all_in_one_dynamic_custom_fields_user,1,0,0,0 access_all_in_one_dynamic_custom_fields_user,dynamic.fields,model_dynamic_fields,all_in_one_dynamic_custom_fields.group_all_in_one_dynamic_custom_fields_user,1,0,0,0
access_dynamic_field_widgets,"dynamic.field.widgets","model_dynamic_field_widgets",,1,1,1,1 access_dynamic_field_widgets,dynamic.field.widgets,model_dynamic_field_widgets,base.group_user,1,1,1,1

1 id name model_id:id group_id:id perm_read perm_write perm_create perm_unlink
2 access_all_in_one_dynamic_custom_fields_administrator dynamic.fields model_dynamic_fields all_in_one_dynamic_custom_fields.group_all_in_one_dynamic_custom_fields_administrator 1 1 1 1
3 access_all_in_one_dynamic_custom_fields_user dynamic.fields model_dynamic_fields all_in_one_dynamic_custom_fields.group_all_in_one_dynamic_custom_fields_user 1 0 0 0
4 access_dynamic_field_widgets dynamic.field.widgets model_dynamic_field_widgets base.group_user 1 1 1 1

8
all_in_one_dynamic_custom_fields/security/security.xml

@ -1,21 +1,21 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<odoo> <odoo>
<data> <data>
<!--Created a module category for all in one dynamic custom fields-->
<record id="module_category_all_in_one_dynamic_custom_fields" <record id="module_category_all_in_one_dynamic_custom_fields"
model="ir.module.category"> model="ir.module.category">
<field name="name">All in One Custom Dynamic Fields</field> <field name="name">All in One Custom Dynamic Fields</field>
<field name="description">Helps you handle your All-in-One Custom Dynamic Fields Permissions </field> <field name="description">Helps you handle your All-in-One Custom Dynamic Fields Permissions </field>
<field name="sequence">5</field> <field name="sequence">5</field>
</record> </record>
<!--Created a new group User in one dynamic custom fields-->
<record id="group_all_in_one_dynamic_custom_fields_user" <record id="group_all_in_one_dynamic_custom_fields_user"
model="res.groups"> model="res.groups">
<field name="name">User</field> <field name="name">User</field>
<field name="category_id" <field name="category_id"
ref="all_in_one_dynamic_custom_fields.module_category_all_in_one_dynamic_custom_fields"/> ref="all_in_one_dynamic_custom_fields.module_category_all_in_one_dynamic_custom_fields"/>
</record> </record>
<!--Created a new group Administrator in one dynamic custom fields-->
<record id="group_all_in_one_dynamic_custom_fields_administrator" <record id="group_all_in_one_dynamic_custom_fields_administrator"
model="res.groups"> model="res.groups">
<field name="name">Administrator</field> <field name="name">Administrator</field>
@ -26,7 +26,5 @@
<field name="users" <field name="users"
eval="[(4, ref('base.user_root')), (4, ref('base.user_admin'))]"/> eval="[(4, ref('base.user_root')), (4, ref('base.user_admin'))]"/>
</record> </record>
</data> </data>
</odoo> </odoo>

BIN
all_in_one_dynamic_custom_fields/static/description/assets/screenshots/0.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 116 KiB

BIN
all_in_one_dynamic_custom_fields/static/description/assets/screenshots/1.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 114 KiB

BIN
all_in_one_dynamic_custom_fields/static/description/assets/screenshots/2.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 114 KiB

BIN
all_in_one_dynamic_custom_fields/static/description/assets/screenshots/3.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 115 KiB

BIN
all_in_one_dynamic_custom_fields/static/description/assets/screenshots/4.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 116 KiB

10
all_in_one_dynamic_custom_fields/static/description/index.html

@ -171,14 +171,14 @@
<p style="font-weight: 400; font-family: 'Montserrat', sans-serif; font-size: 14px;">Open the module and Click Create Button to create new custom fields and fill the details.Then click the Create Field.</p> <p style="font-weight: 400; font-family: 'Montserrat', sans-serif; font-size: 14px;">Open the module and Click Create Button to create new custom fields and fill the details.Then click the Create Field.</p>
<img src="assets/screenshots/tree.png" class="img-thumbnail"> <img src="assets/screenshots/tree.png" class="img-thumbnail">
<img src="assets/screenshots/dynamic2.png" class="img-thumbnail"> <img src="assets/screenshots/dynamic2.png" class="img-thumbnail">
<img src="assets/screenshots/dynamic3.png" class="img-thumbnail"> <img src="assets/screenshots/0.png" class="img-thumbnail">
</div> </div>
<div style="display: block; margin: 30px auto;"> <div style="display: block; margin: 30px auto;">
<h3 style="font-family: 'Montserrat', sans-serif; font-size: 18px; font-weight: bold;">Model <h3 style="font-family: 'Montserrat', sans-serif; font-size: 18px; font-weight: bold;">Model
</h3> </h3>
<p style="font-weight: 400; font-family: 'Montserrat', sans-serif; font-size: 14px;">Select the model for which you want to create a custom field.</p> <p style="font-weight: 400; font-family: 'Montserrat', sans-serif; font-size: 14px;">Select the model for which you want to create a custom field.</p>
<img src="assets/screenshots/dynamic4.png" class="img-thumbnail"> <img src="assets/screenshots/1.png" class="img-thumbnail">
<img src="assets/screenshots/dynamic5.png" class="img-thumbnail"> <img src="assets/screenshots/dynamic5.png" class="img-thumbnail">
</div> </div>
@ -186,14 +186,14 @@
<h3 style="font-family: 'Montserrat', sans-serif; font-size: 18px; font-weight: bold;">Field Type <h3 style="font-family: 'Montserrat', sans-serif; font-size: 18px; font-weight: bold;">Field Type
</h3> </h3>
<p style="font-weight: 400; font-family: 'Montserrat', sans-serif; font-size: 14px;">Choose the field type</p> <p style="font-weight: 400; font-family: 'Montserrat', sans-serif; font-size: 14px;">Choose the field type</p>
<img src="assets/screenshots/dynamic6.png" class="img-thumbnail"> <img src="assets/screenshots/2.png" class="img-thumbnail">
</div> </div>
<div style="display: block; margin: 30px auto;"> <div style="display: block; margin: 30px auto;">
<h3 style="font-family: 'Montserrat', sans-serif; font-size: 18px; font-weight: bold;">List / Tree View <h3 style="font-family: 'Montserrat', sans-serif; font-size: 18px; font-weight: bold;">List / Tree View
</h3> </h3>
<p style="font-weight: 400; font-family: 'Montserrat', sans-serif; font-size: 14px;">The custom field also we can add to the module List/Tree view.</p> <p style="font-weight: 400; font-family: 'Montserrat', sans-serif; font-size: 14px;">The custom field also we can add to the module List/Tree view.</p>
<img src="assets/screenshots/dynamic7.png" class="img-thumbnail"> <img src="assets/screenshots/3.png" class="img-thumbnail">
<img src="assets/screenshots/dynamic8.png" class="img-thumbnail"> <img src="assets/screenshots/dynamic8.png" class="img-thumbnail">
<img src="assets/screenshots/dynamic9.png" class="img-thumbnail"> <img src="assets/screenshots/dynamic9.png" class="img-thumbnail">
</div> </div>
@ -209,7 +209,7 @@
<h3 style="font-family: 'Montserrat', sans-serif; font-size: 18px; font-weight: bold;"> Sale Order Tree/List View <h3 style="font-family: 'Montserrat', sans-serif; font-size: 18px; font-weight: bold;"> Sale Order Tree/List View
</h3> </h3>
<p style="font-weight: 400; font-family: 'Montserrat', sans-serif; font-size: 14px;"> A new custom field is created in the sale order tree view.</p> <p style="font-weight: 400; font-family: 'Montserrat', sans-serif; font-size: 14px;"> A new custom field is created in the sale order tree view.</p>
<img src="assets/screenshots/dynamic11.png" class="img-thumbnail"> <img src="assets/screenshots/4.png" class="img-thumbnail">
<img src="assets/screenshots/dynamic12.png" class="img-thumbnail"> <img src="assets/screenshots/dynamic12.png" class="img-thumbnail">
</div> </div>

108
all_in_one_dynamic_custom_fields/views/dynamic_fields.xml

@ -1,108 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<record id="custom_dynamic_fields_view_tree" model="ir.ui.view">
<field name="name">custom_dynamic_fields_view_tree</field>
<field name="model">dynamic.fields</field>
<field name="arch" type="xml">
<tree>
<field name="name"/>
<field name="field_description"/>
<field name="model_id"/>
<field name="status"/>
</tree>
</field>
</record>
<record id='custom_dynamic_fields_view_form' model='ir.ui.view'>
<field name="name">custom_dynamic_fields_view_form</field>
<field name="model">dynamic.fields</field>
<field name="arch" type="xml">
<form>
<header>
<button name="create_dynamic_fields" string="Create Field" type="object" class="oe_highlight"
attrs="{'invisible': [('status', 'in', ['form','tree'])]}" groups="all_in_one_dynamic_custom_fields.group_all_in_one_dynamic_custom_fields_administrator"/>
<button name="add_field_to_tree_view" string="Add to Tree View" type="object" class="oe_highlight"
attrs="{'invisible': [('status', 'in', ['draft','tree'])]}" groups="all_in_one_dynamic_custom_fields.group_all_in_one_dynamic_custom_fields_administrator"/>
<field name="status" widget="statusbar"
statusbar_visible="draft,form,tree"/>
</header>
<sheet>
<group>
<group string="Field Info">
<field name="name"/>
<field name="field_description"/>
<field name="state" readonly="1" groups="base.group_no_one"/>
<field name="model_id" options='{"no_open": True, "no_create": True}'/>
<field name="field_type"/>
<field name="selection_field" placeholder="[('blue', 'Blue'),('yellow', 'Yellow')]"
attrs="{'required': [('field_type','in',['selection','reference'])],
'readonly': [('field_type','not in',['selection','reference'])],
'invisible': [('field_type','not in',['selection','reference'])]}"/>
<field name="ref_model_id" options='{"no_open": True, "no_create": True}' attrs="{'required': [('field_type','in',['many2one','many2many'])],
'readonly': [('field_type','not in',['many2one','many2many'])],
'invisible': [('field_type','not in',['many2one','many2many'])]}"/>
<field name="widget" widget="selection"
attrs="{'invisible': [('field_type','not in',['binary', 'many2many', 'selection', 'float', 'many2one'])]}"/>
<field name="required"/>
<field name="form_view_id"/>
<field name="form_view_inherit_id"/>
</group>
<group string="Tree View">
<field name="add_field_in_tree"/>
<field name="tree_view_id" attrs="{'invisible': [('add_field_in_tree', '=', False)],
'required': [('add_field_in_tree', '=', True)]}"/>
<field name="tree_view_inherit_id" attrs="{'invisible': [('add_field_in_tree', '=', False)]}"/>
</group>
<group string="Position">
<field name="position_field" options='{"no_open": True, "no_create": True}'/>
<field name="position"/>
</group>
</group>
<group string="Extra Properties">
<group>
<field name="extra_features"/>
</group>
<group attrs="{'invisible': [('extra_features', '=', False)]}">
<field name="help"/>
</group>
<group attrs="{'invisible': [('extra_features', '=', False)]}">
<field name="readonly"/>
<field name="store"/>
<field name="index"/>
<field name="copied"/>
</group>
</group>
</sheet>
</form>
</field>
</record>
<record id='action_dynamic_custom_fields' model='ir.actions.act_window'>
<field name="name">Custom Dynamic Fields</field>
<field name="res_model">dynamic.fields</field>
<field name="view_mode">tree,form</field>
<field name="help" type="html">
<p class="o_view_nocontent_smiling_face">
Create New Custom Dynamic Field!
</p>
</field>
</record>
<menuitem id="menu_all_in_one_dynamic_custom_fields_root"
name="All in One Custom Dynamic Fields"
groups="group_all_in_one_dynamic_custom_fields_user,group_all_in_one_dynamic_custom_fields_administrator"
web_icon="all_in_one_dynamic_custom_fields,static/description/icon.png"
sequence="01"/>
<menuitem id="menu_create_custom_dynamic_fields"
name="Fields"
parent="menu_all_in_one_dynamic_custom_fields_root"
sequence="1"/>
<menuitem id="menu_create_fields"
name="Create Fields"
parent="menu_create_custom_dynamic_fields"
action="action_dynamic_custom_fields"
groups="group_all_in_one_dynamic_custom_fields_user,group_all_in_one_dynamic_custom_fields_administrator"
sequence="2"/>
</odoo>

116
all_in_one_dynamic_custom_fields/views/dynamic_fields_views.xml

@ -0,0 +1,116 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<!--Tree view for dynamic fields-->
<record id="dynamic_fields_view_tree" model="ir.ui.view">
<field name="name">dynamic.fields.view.tree</field>
<field name="model">dynamic.fields</field>
<field name="arch" type="xml">
<tree>
<field name="name"/>
<field name="field_description"/>
<field name="model_id"/>
<field name="status"/>
</tree>
</field>
</record>
<!--From view for dynamic fields-->
<record id='dynamic_fields_view_form' model='ir.ui.view'>
<field name="name">dynamic.fields.view.form</field>
<field name="model">dynamic.fields</field>
<field name="arch" type="xml">
<form>
<header>
<button name="action_create_dynamic_fields" string="Create Field" type="object" class="oe_highlight"
attrs="{'invisible': [('status', 'in', ['form','tree'])]}" groups="all_in_one_dynamic_custom_fields.group_all_in_one_dynamic_custom_fields_administrator"/>
<button name="action_add_field_to_tree_view" string="Add to Tree View" type="object" class="oe_highlight"
attrs="{'invisible': [('status', 'in', ['draft','tree'])]}" groups="all_in_one_dynamic_custom_fields.group_all_in_one_dynamic_custom_fields_administrator"/>
<field name="status" widget="statusbar"
statusbar_visible="draft,form,tree"/>
</header>
<sheet>
<group>
<group string="Field Info">
<field name="name" attrs="{'readonly': [('status', '!=', 'draft')]}"/>
<field name="field_description" attrs="{'readonly': [('status', '!=', 'draft')]}"/>/>
<field name="state" readonly="1" groups="base.group_no_one"/>
<field name="model_id" options='{"no_open": True, "no_create": True}' attrs="{'readonly': [('status', '!=', 'draft')]}"/>
<field name="field_type" attrs="{'readonly': [('status', '!=', 'draft')]}"/>
<field name="selection_field" placeholder="[('blue', 'Blue'),('yellow', 'Yellow')]"
attrs="{'required': [('field_type','in',['selection','reference'])],
'readonly': [('field_type','not in',['selection','reference'])],
'invisible': [('field_type','not in',['selection','reference'])]}"/>
<field name="ref_model_id" options='{"no_open": True, "no_create": True}' attrs="{'required': [('field_type','in',['many2one','many2many'])],
'readonly': ['|',('field_type','not in',['many2one','many2many']),('status', '!=', 'draft')],
'invisible': [('field_type','not in',['many2one','many2many'])]}"/>
<field name="widget_id" widget="selection"
attrs="{'invisible': [('field_type','not in',['binary', 'many2many', 'selection', 'float', 'many2one'])],
'readonly': [('status', '!=', 'draft')]}"/>
<field name="required" attrs="{'readonly': [('status', '!=', 'draft')]}"/>
<field name="form_view_id" options='{"no_open": True, "no_create": True}' attrs="{'readonly': [('status', '!=', 'draft')]}"/>
<field name="form_view_inherit_id"/>
</group>
<group string="Tree View">
<field name="is_add_field_in_tree" attrs="{'readonly': [('status', 'not in', ['draft', 'form'])]}"/>
<field name="tree_view_id" options='{"no_open": True, "no_create": True}' attrs="{'invisible': [('is_add_field_in_tree', '=', False)],
'required': [('is_add_field_in_tree', '=', True)], 'readonly': [('status', 'not in', ['draft', 'form'])]}"/>
<field name="tree_view_inherit_id" attrs="{'invisible': [('is_add_field_in_tree', '=', False)]}"/>
<field name="tree_view_field_id" options='{"no_open": True, "no_create": True}' attrs="{'invisible': [('is_add_field_in_tree', '=', False)],
'required': [('is_add_field_in_tree', '=', True)], 'readonly': [('status', 'not in', ['draft', 'form'])]}"/>
<field name="tree_field_position" attrs="{'invisible': [('is_add_field_in_tree', '=', False)],
'required': [('is_add_field_in_tree', '=', True)], 'readonly': [('status', 'not in', ['draft', 'form'])]}"/>
<field name="is_tree_view_toggle" attrs="{'invisible': [('is_add_field_in_tree', '=', False)],
'required': [('is_add_field_in_tree', '=', True)], 'readonly': [('status', 'not in', ['draft', 'form'])]}"/>
<field name="tree_field_ids" invisible="1"/>
</group>
<group string="Position">
<field name="position_field_id" options='{"no_open": True, "no_create": True}' attrs="{'readonly': [('status', '!=', 'draft')]}"/>
<field name="position" attrs="{'readonly': [('status', '!=', 'draft')]}"/>
</group>
</group>
<group string="Extra Properties">
<group>
<field name="is_extra_features" attrs="{'readonly': [('status', '!=', 'draft')]}"/>
</group>
<group attrs="{'invisible': [('is_extra_features', '=', False)], 'readonly': [('status', '!=', 'draft')]}">
<field name="help" attrs="{'readonly': [('status', '!=', 'draft')]}"/>
</group>
<group attrs="{'invisible': [('is_extra_features', '=', False)]}">
<field name="readonly" attrs="{'readonly': [('status', '!=', 'draft')]}"/>
<field name="store" attrs="{'readonly': [('status', '!=', 'draft')]}"/>
<field name="index" attrs="{'readonly': [('status', '!=', 'draft')]}"/>
<field name="copied" attrs="{'readonly': [('status', '!=', 'draft')]}"/>
</group>
</group>
</sheet>
</form>
</field>
</record>
<!--Action for dynamic fields-->
<record id='dynamic_fields_action' model='ir.actions.act_window'>
<field name="name">Custom Dynamic Fields</field>
<field name="res_model">dynamic.fields</field>
<field name="view_mode">tree,form</field>
<field name="help" type="html">
<p class="o_view_nocontent_smiling_face">
Create New Custom Dynamic Field!
</p>
</field>
</record>
<!--Menu item for dynamic fields-->
<menuitem id="menu_all_in_one_dynamic_custom_fields_root"
name="All in One Custom Dynamic Fields"
groups="group_all_in_one_dynamic_custom_fields_user,group_all_in_one_dynamic_custom_fields_administrator"
web_icon="all_in_one_dynamic_custom_fields,static/description/icon.png"
sequence="01"/>
<menuitem id="menu_create_custom_dynamic_fields"
name="Fields"
parent="menu_all_in_one_dynamic_custom_fields_root"
sequence="1"/>
<menuitem id="menu_create_fields"
name="Create Fields"
parent="menu_create_custom_dynamic_fields"
action="dynamic_fields_action"
groups="group_all_in_one_dynamic_custom_fields_user,group_all_in_one_dynamic_custom_fields_administrator"
sequence="2"/>
</odoo>
Loading…
Cancel
Save