@ -0,0 +1,47 @@ |
|||||
|
.. image:: https://img.shields.io/badge/license-AGPL--3-blue.svg |
||||
|
:target: https://www.gnu.org/licenses/agpl-3.0-standalone.html |
||||
|
:alt: License: AGPL-3 |
||||
|
|
||||
|
All in One Custom Dynamic Fields |
||||
|
================================ |
||||
|
The features of module facilitates the addition of fields to any view within a model, |
||||
|
allowing users to specify attributes and properties at their preferred location. |
||||
|
|
||||
|
Configuration |
||||
|
============= |
||||
|
- www.odoo.com/documentation/17.0/setup/install.html |
||||
|
- Install our custom addon |
||||
|
|
||||
|
Company |
||||
|
------- |
||||
|
* `Cybrosys Techno Solutions <https://cybrosys.com/>`__ |
||||
|
|
||||
|
License |
||||
|
------- |
||||
|
AFFERO General Public License, Version 3 (AGPL v3). |
||||
|
(http://www.gnu.org/licenses/agpl-3.0-standalone.html) |
||||
|
|
||||
|
Credits |
||||
|
------- |
||||
|
Developer: (V18) NIHALA KP, Contact: odoo@cybrosys.com |
||||
|
|
||||
|
Contacts |
||||
|
-------- |
||||
|
* Mail Contact : odoo@cybrosys.com |
||||
|
|
||||
|
Bug Tracker |
||||
|
----------- |
||||
|
Bugs are tracked on GitHub Issues. In case of trouble, |
||||
|
please check there if your issue has already been reported. |
||||
|
|
||||
|
Maintainer |
||||
|
========== |
||||
|
.. image:: https://cybrosys.com/images/logo.png |
||||
|
:target: https://cybrosys.com |
||||
|
|
||||
|
This module is maintained by Cybrosys Technologies. |
||||
|
For support and more information, please visit `Our Website <https://cybrosys.com/>`__ |
||||
|
|
||||
|
Further information |
||||
|
=================== |
||||
|
HTML Description: `<static/description/index.html>`__ |
@ -0,0 +1,22 @@ |
|||||
|
# -*- coding: utf-8 -*- |
||||
|
############################################################################### |
||||
|
# |
||||
|
# Cybrosys Technologies Pvt. Ltd. |
||||
|
# |
||||
|
# Copyright (C) 2024-TODAY Cybrosys Technologies(<https://www.cybrosys.com>) |
||||
|
# Author: Nihala KP (odoo@cybrosys.com) |
||||
|
# |
||||
|
# You can modify it under the terms of the GNU AFFERO |
||||
|
# GENERAL PUBLIC LICENSE (AGPL v3), Version 3. |
||||
|
# |
||||
|
# This program is distributed in the hope that it will be useful, |
||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||
|
# GNU AFFERO GENERAL PUBLIC LICENSE (AGPL v3) for more details. |
||||
|
# |
||||
|
# You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE |
||||
|
# (AGPL v3) along with this program. |
||||
|
# If not, see <http://www.gnu.org/licenses/>. |
||||
|
# |
||||
|
############################################################################### |
||||
|
from . import models |
@ -0,0 +1,47 @@ |
|||||
|
# -*- coding: utf-8 -*- |
||||
|
############################################################################### |
||||
|
# |
||||
|
# Cybrosys Technologies Pvt. Ltd. |
||||
|
# |
||||
|
# Copyright (C) 2024-TODAY Cybrosys Technologies(<https://www.cybrosys.com>) |
||||
|
# Author: Nihala KP (odoo@cybrosys.com) |
||||
|
# |
||||
|
# You can modify it under the terms of the GNU AFFERO |
||||
|
# GENERAL PUBLIC LICENSE (AGPL v3), Version 3. |
||||
|
# |
||||
|
# This program is distributed in the hope that it will be useful, |
||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||
|
# GNU AFFERO GENERAL PUBLIC LICENSE (AGPL v3) for more details. |
||||
|
# |
||||
|
# You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE |
||||
|
# (AGPL v3) along with this program. |
||||
|
# If not, see <http://www.gnu.org/licenses/>. |
||||
|
# |
||||
|
############################################################################### |
||||
|
{ |
||||
|
'name': 'All in One Dynamic Fields', |
||||
|
'version': '18.0.1.0.0', |
||||
|
'category': 'Extra Tools', |
||||
|
'summary': 'Create Custom Fields As Per Your Need Without Any Coding.', |
||||
|
'description': "The features of module facilitates the addition of fields" |
||||
|
"to any view within a model, allowing users to specify" |
||||
|
"attributes and properties at their preferred " |
||||
|
"location.", |
||||
|
'author': 'Cybrosys Techno Solutions', |
||||
|
'company': 'Cybrosys Techno Solutions', |
||||
|
'maintainer': 'Cybrosys Techno Solutions', |
||||
|
'website': "https://www.cybrosys.com", |
||||
|
'depends': ['base'], |
||||
|
'data': [ |
||||
|
'data/dynamic_field_widgets_data.xml', |
||||
|
'security/all_in_one_dynamic_custom_fields_security.xml', |
||||
|
'security/ir.model.access.csv', |
||||
|
'views/dynamic_fields_views.xml', |
||||
|
], |
||||
|
'images': ['static/description/banner.png'], |
||||
|
'license': 'AGPL-3', |
||||
|
'installable': True, |
||||
|
'auto_install': False, |
||||
|
'application': False, |
||||
|
} |
@ -0,0 +1,47 @@ |
|||||
|
<?xml version="1.0" encoding="utf-8"?> |
||||
|
<odoo> |
||||
|
<data noupdate="1"> |
||||
|
<!-- Widget 'image' for 'binary' field --> |
||||
|
<record id="dynamic_field_widget_image" model="dynamic.field.widgets"> |
||||
|
<field name="name">image</field> |
||||
|
<field name="data_type">binary</field> |
||||
|
<field name="description">Image</field> |
||||
|
</record> |
||||
|
<!-- Widget 'many2many_tags' for 'many 2 many' field --> |
||||
|
<record id="dynamic_field_widget_many2many_tags" model="dynamic.field.widgets"> |
||||
|
<field name="name">many2many_tags</field> |
||||
|
<field name="data_type">many2many</field> |
||||
|
<field name="description">Many2many Tags</field> |
||||
|
</record> |
||||
|
<!-- Widget 'binary' for 'many 2 many' field --> |
||||
|
<record id="dynamic_field_widget_binary" model="dynamic.field.widgets"> |
||||
|
<field name="name">binary</field> |
||||
|
<field name="data_type">many2many</field> |
||||
|
<field name="description">Binary</field> |
||||
|
</record> |
||||
|
<!-- Widget 'radio' for 'selection' field --> |
||||
|
<record id="dynamic_field_widget_radio" model="dynamic.field.widgets"> |
||||
|
<field name="name">radio</field> |
||||
|
<field name="data_type">selection</field> |
||||
|
<field name="description">Radio</field> |
||||
|
</record> |
||||
|
<!-- Widget 'priority' for 'selection' field --> |
||||
|
<record id="dynamic_field_widget_priority" model="dynamic.field.widgets"> |
||||
|
<field name="name">priority</field> |
||||
|
<field name="data_type">selection</field> |
||||
|
<field name="description">Priority</field> |
||||
|
</record> |
||||
|
<!-- Widget 'monetary' for 'float' field --> |
||||
|
<record id="dynamic_field_widget_monetary" model="dynamic.field.widgets"> |
||||
|
<field name="name">monetary</field> |
||||
|
<field name="data_type">float</field> |
||||
|
<field name="description">Monetary</field> |
||||
|
</record> |
||||
|
<!-- Widget 'selection' for 'Many_2_one' field --> |
||||
|
<record id="dynamic_field_widget_selection" model="dynamic.field.widgets"> |
||||
|
<field name="name">selection</field> |
||||
|
<field name="data_type">many2one</field> |
||||
|
<field name="description">Selection</field> |
||||
|
</record> |
||||
|
</data> |
||||
|
</odoo> |
@ -0,0 +1,7 @@ |
|||||
|
## Module <all_in_one_dynamic_custom_fields> |
||||
|
|
||||
|
#### 2.11.2024 |
||||
|
#### Version 18.0.1.0.0 |
||||
|
##### ADD |
||||
|
|
||||
|
- Initial commit for All in One Dynamic Fields |
@ -0,0 +1,24 @@ |
|||||
|
# -*- coding: utf-8 -*- |
||||
|
############################################################################### |
||||
|
# |
||||
|
# Cybrosys Technologies Pvt. Ltd. |
||||
|
# |
||||
|
# Copyright (C) 2024-TODAY Cybrosys Technologies(<https://www.cybrosys.com>) |
||||
|
# Author: Nihala KP (odoo@cybrosys.com) |
||||
|
# |
||||
|
# You can modify it under the terms of the GNU AFFERO |
||||
|
# GENERAL PUBLIC LICENSE (AGPL v3), Version 3. |
||||
|
# |
||||
|
# This program is distributed in the hope that it will be useful, |
||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||
|
# GNU AFFERO GENERAL PUBLIC LICENSE (AGPL v3) for more details. |
||||
|
# |
||||
|
# You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE |
||||
|
# (AGPL v3) along with this program. |
||||
|
# If not, see <http://www.gnu.org/licenses/>. |
||||
|
# |
||||
|
############################################################################### |
||||
|
from . import dynamic_field_widgets |
||||
|
from . import dynamic_fields |
||||
|
from . import ir_model_fields |
@ -0,0 +1,39 @@ |
|||||
|
# -*- coding: utf-8 -*- |
||||
|
############################################################################### |
||||
|
# |
||||
|
# Cybrosys Technologies Pvt. Ltd. |
||||
|
# |
||||
|
# Copyright (C) 2024-TODAY Cybrosys Technologies(<https://www.cybrosys.com>) |
||||
|
# Author: Nihala KP (odoo@cybrosys.com) |
||||
|
# |
||||
|
# You can modify it under the terms of the GNU AFFERO |
||||
|
# GENERAL PUBLIC LICENSE (AGPL v3), Version 3. |
||||
|
# |
||||
|
# This program is distributed in the hope that it will be useful, |
||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||
|
# GNU AFFERO GENERAL PUBLIC LICENSE (AGPL v3) for more details. |
||||
|
# |
||||
|
# You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE |
||||
|
# (AGPL v3) along with this program. |
||||
|
# If not, see <http://www.gnu.org/licenses/>. |
||||
|
# |
||||
|
############################################################################### |
||||
|
from odoo import fields, models |
||||
|
|
||||
|
|
||||
|
class DynamicFieldWidgets(models.Model): |
||||
|
"""We can't filter a selection field dynamically |
||||
|
so when we select a field its widgets also need to change according to |
||||
|
the selected field type, we can't do it by a 'selection' field, |
||||
|
need a 'Many 2 one' field.""" |
||||
|
|
||||
|
_name = 'dynamic.field.widgets' |
||||
|
_rec_name = 'description' |
||||
|
_description = 'Field Widgets' |
||||
|
|
||||
|
name = fields.Char(string="Name", help="Technical name of the widget") |
||||
|
data_type = fields.Char(string="Data Type", help="Datatype suitable for" |
||||
|
" the widget") |
||||
|
description = fields.Char(string="Description", help="Description of" |
||||
|
" the widget") |
@ -0,0 +1,293 @@ |
|||||
|
# -*- coding: utf-8 -*- |
||||
|
############################################################################### |
||||
|
# |
||||
|
# Cybrosys Technologies Pvt. Ltd. |
||||
|
# |
||||
|
# Copyright (C) 2024-TODAY Cybrosys Technologies(<https://www.cybrosys.com>) |
||||
|
# Author: Nihala KP (odoo@cybrosys.com) |
||||
|
# |
||||
|
# You can modify it under the terms of the GNU AFFERO |
||||
|
# GENERAL PUBLIC LICENSE (AGPL v3), Version 3. |
||||
|
# |
||||
|
# This program is distributed in the hope that it will be useful, |
||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||
|
# GNU AFFERO GENERAL PUBLIC LICENSE (AGPL v3) for more details. |
||||
|
# |
||||
|
# You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE |
||||
|
# (AGPL v3) along with this program. |
||||
|
# If not, see <http://www.gnu.org/licenses/>. |
||||
|
# |
||||
|
############################################################################### |
||||
|
import xml.etree.ElementTree as ET |
||||
|
from odoo import api, fields, models, _ |
||||
|
|
||||
|
|
||||
|
class DynamicFields(models.Model): |
||||
|
"""Creates dynamic fields model to create and manage new fields""" |
||||
|
_name = 'dynamic.fields' |
||||
|
_rec_name = 'field_description' |
||||
|
_description = 'Custom Dynamic Fields' |
||||
|
_inherit = 'ir.model.fields' |
||||
|
|
||||
|
@api.model |
||||
|
def get_possible_field_types(self): |
||||
|
"""Return all available field types other than 'o2m' and |
||||
|
'reference' fields.""" |
||||
|
field_list = sorted((key, key) for key in fields.MetaField.by_type) |
||||
|
field_list.remove(('one2many', 'one2many')) |
||||
|
field_list.remove(('reference', 'reference')) |
||||
|
return field_list |
||||
|
|
||||
|
@api.onchange('model_id') |
||||
|
def _onchange_model_id(self): |
||||
|
"""Pass selected model into model field to filter position fields, |
||||
|
set values to form_view_ids to filter form view ids and pass |
||||
|
values to tree_view_ids to filter tree view ids""" |
||||
|
for rec in self: |
||||
|
rec.model = rec.model_id.model |
||||
|
rec.write({'form_view_ids': [(6, 0, rec.model_id.view_ids.filtered( |
||||
|
lambda view: view.type == 'form' and view.mode == 'primary') |
||||
|
.ids)]}) |
||||
|
rec.write({'tree_view_ids': |
||||
|
[(6, 0, self.model_id.view_ids.filtered( |
||||
|
lambda |
||||
|
view: view.type == 'list' and view.mode == 'primary') |
||||
|
.ids)]}) |
||||
|
|
||||
|
model = fields.Char(string='Model', help="To store selected model name") |
||||
|
position_field_id = fields.Many2one(comodel_name='ir.model.fields', |
||||
|
string='Field Name', |
||||
|
required=True, ondelete='cascade', |
||||
|
help="Position field for new field", |
||||
|
domain=lambda |
||||
|
self: "[('model', '=', model)]") |
||||
|
position = fields.Selection(selection=[('before', 'Before'), |
||||
|
('after', 'After')], |
||||
|
string='Position', |
||||
|
required=True, help="Position of new field") |
||||
|
model_id = fields.Many2one(comodel_name='ir.model', string='Model', |
||||
|
required=True, |
||||
|
index=True, |
||||
|
help="The model this field belongs to") |
||||
|
ref_model_id = fields.Many2one(comodel_name='ir.model', string='Rezlational' |
||||
|
'Model', |
||||
|
index=True, help="Relational model" |
||||
|
" for relational fields") |
||||
|
selection_field = fields.Char(string="Selection Options", |
||||
|
help="The model this field belongs to") |
||||
|
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_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', |
||||
|
'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', 'Field Created')], |
||||
|
string='Status', |
||||
|
index=True, readonly=True, tracking=True, |
||||
|
copy=False, default='draft', |
||||
|
help='State for record') |
||||
|
form_view_ids = fields.Many2many(comodel_name='ir.ui.view', |
||||
|
string="Form View IDs", |
||||
|
help="Stores form view ids") |
||||
|
tree_view_ids = fields.Many2many(comodel_name='ir.ui.view', |
||||
|
relation="rel_tree_view", |
||||
|
string="Tree View IDs", |
||||
|
help="Stores tree view ids") |
||||
|
form_view_id = fields.Many2one(comodel_name='ir.ui.view', |
||||
|
string="Form View ID", |
||||
|
required=True, |
||||
|
help="Form view id of the model", |
||||
|
domain=lambda self: "[('id', 'in', " |
||||
|
"form_view_ids)]") |
||||
|
form_view_inherit_id = fields.Char(string="Form View Inherit Id", |
||||
|
related='form_view_id.xml_id', |
||||
|
help="Form view inherit id(adds" |
||||
|
" by selecting form view id)") |
||||
|
add_field_in_tree = fields.Boolean(string="Add Field to the Tree View", |
||||
|
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="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')]): |
||||
|
self.env['ir.model.fields'].sudo().create({ |
||||
|
'name': 'x_currency_id', |
||||
|
'field_description': 'Currency', |
||||
|
'model_id': self.model_id.id, |
||||
|
'ttype': 'many2one', |
||||
|
'relation': 'res.currency', |
||||
|
'is_dynamic_field': True |
||||
|
}) |
||||
|
self.env['ir.model.fields'].sudo().create({ |
||||
|
'name': self.name, |
||||
|
'field_description': self.field_description, |
||||
|
'model_id': self.model_id.id, |
||||
|
'ttype': self.field_type, |
||||
|
'relation': self.ref_model_id.model, |
||||
|
'required': self.required, |
||||
|
'index': self.index, |
||||
|
'store': self.store, |
||||
|
'help': self.help, |
||||
|
'readonly': self.readonly, |
||||
|
'selection': self.selection_field, |
||||
|
'copied': self.copied, |
||||
|
'is_dynamic_field': True |
||||
|
}) |
||||
|
inherit_form_view_name = str( |
||||
|
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"?>' |
||||
|
'<data>' |
||||
|
'<field name="%s" position="%s">' |
||||
|
'<field name="%s"/>' |
||||
|
'</field>' |
||||
|
'</data>') % (self.position_field_id.name, |
||||
|
self.position, self.name) |
||||
|
if self.widget_id: |
||||
|
arch_base = _('<?xml version="1.0"?>' |
||||
|
'<data>' |
||||
|
'<field name="%s" position="%s">' |
||||
|
'<field name="%s" widget="%s"/>' |
||||
|
'</field>' |
||||
|
'</data>') % (self.position_field_id.name, |
||||
|
self.position, self.name, |
||||
|
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, |
||||
|
'mode': 'extension', |
||||
|
'inherit_id': inherit_id.id, |
||||
|
'arch_base': arch_base, |
||||
|
'active': True |
||||
|
}) |
||||
|
self.action_create_to_tree_view() |
||||
|
return { |
||||
|
'type': 'ir.actions.client', |
||||
|
'tag': 'reload', |
||||
|
} |
||||
|
|
||||
|
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.created_tree_view_id = self.env['ir.ui.view'].sudo().create( |
||||
|
{ |
||||
|
'name': inherit_tree_view_name, |
||||
|
'type': 'list', |
||||
|
'model': self.model_id.model, |
||||
|
'mode': 'extension', |
||||
|
'inherit_id': self.tree_view_id.id, |
||||
|
'arch_base': tree_view_arch_base, |
||||
|
'active': True |
||||
|
}) |
||||
|
return { |
||||
|
'type': 'ir.actions.client', |
||||
|
'tag': 'reload', |
||||
|
} |
||||
|
|
||||
|
def unlink(self): |
||||
|
"""Custom unlink method to handle linked records.""" |
||||
|
# Deactivate related form and tree views for the dynamic field |
||||
|
if self.created_form_view_id: |
||||
|
self.created_form_view_id.active = False |
||||
|
if self.created_tree_view_id: |
||||
|
self.created_tree_view_id.active = False |
||||
|
model = self.env[self.model_id.model] |
||||
|
if self.name in model._fields: |
||||
|
model._pop_field(self.name) |
||||
|
return super(DynamicFields, self).unlink() |
@ -0,0 +1,32 @@ |
|||||
|
# -*- coding: utf-8 -*- |
||||
|
############################################################################### |
||||
|
# |
||||
|
# Cybrosys Technologies Pvt. Ltd. |
||||
|
# |
||||
|
# Copyright (C) 2024-TODAY Cybrosys Technologies(<https://www.cybrosys.com>) |
||||
|
# Author: Nihala KP(odoo@cybrosys.com) |
||||
|
# |
||||
|
# You can modify it under the terms of the GNU AFFERO |
||||
|
# GENERAL PUBLIC LICENSE (AGPL v3), Version 3. |
||||
|
# |
||||
|
# This program is distributed in the hope that it will be useful, |
||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||
|
# GNU AFFERO GENERAL PUBLIC LICENSE (AGPL v3) for more details. |
||||
|
# |
||||
|
# You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE |
||||
|
# (AGPL v3) along with this program. |
||||
|
# If not, see <http://www.gnu.org/licenses/>. |
||||
|
# |
||||
|
############################################################################### |
||||
|
from odoo import models, fields |
||||
|
|
||||
|
|
||||
|
class IrModelFields(models.Model): |
||||
|
"""Adding a new field to understand the dynamically created fields.""" |
||||
|
|
||||
|
_inherit = 'ir.model.fields' |
||||
|
|
||||
|
is_dynamic_field = fields.Boolean(string="Dynamic Field", |
||||
|
help="To filter dynamically" |
||||
|
" created fields") |
@ -0,0 +1,30 @@ |
|||||
|
<?xml version="1.0" encoding="UTF-8"?> |
||||
|
<odoo> |
||||
|
<!-- All in one dynamic custom fields module category--> |
||||
|
<record id="module_category_all_in_one_dynamic_custom_fields" |
||||
|
model="ir.module.category"> |
||||
|
<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="sequence">5</field> |
||||
|
</record> |
||||
|
<!-- All in one dynamic custom fields user group--> |
||||
|
<record id="group_all_in_one_dynamic_custom_fields_user" |
||||
|
model="res.groups"> |
||||
|
<field name="name">User</field> |
||||
|
<field name="category_id" |
||||
|
ref="all_in_one_dynamic_custom_fields.module_category_all_in_one_dynamic_custom_fields"/> |
||||
|
</record> |
||||
|
<!-- All in one dynamic custom fields administrator group--> |
||||
|
<record id="group_all_in_one_dynamic_custom_fields_administrator" |
||||
|
model="res.groups"> |
||||
|
<field name="name">Administrator</field> |
||||
|
<field name="category_id" |
||||
|
ref="all_in_one_dynamic_custom_fields.module_category_all_in_one_dynamic_custom_fields"/> |
||||
|
<field name="implied_ids" |
||||
|
eval="[(4, ref('all_in_one_dynamic_custom_fields.group_all_in_one_dynamic_custom_fields_user'))]"/> |
||||
|
<field name="users" |
||||
|
eval="[(4, ref('base.user_root')), (4, ref('base.user_admin'))]"/> |
||||
|
</record> |
||||
|
</odoo> |
|
After Width: | Height: | Size: 2.2 KiB |
After Width: | Height: | Size: 28 KiB |
After Width: | Height: | Size: 628 KiB |
After Width: | Height: | Size: 1.1 KiB |
After Width: | Height: | Size: 210 KiB |
After Width: | Height: | Size: 209 KiB |
After Width: | Height: | Size: 109 KiB |
After Width: | Height: | Size: 495 B |
After Width: | Height: | Size: 1.0 KiB |
After Width: | Height: | Size: 624 B |
After Width: | Height: | Size: 136 KiB |
After Width: | Height: | Size: 214 KiB |
After Width: | Height: | Size: 36 KiB |
After Width: | Height: | Size: 3.6 KiB |
After Width: | Height: | Size: 310 B |
After Width: | Height: | Size: 929 B |
After Width: | Height: | Size: 1.3 KiB |
After Width: | Height: | Size: 3.3 KiB |
After Width: | Height: | Size: 1.4 KiB |
After Width: | Height: | Size: 17 KiB |
After Width: | Height: | Size: 542 B |
After Width: | Height: | Size: 576 B |
After Width: | Height: | Size: 733 B |
After Width: | Height: | Size: 4.3 KiB |
After Width: | Height: | Size: 1.2 KiB |
After Width: | Height: | Size: 4.0 KiB |
After Width: | Height: | Size: 1.7 KiB |
After Width: | Height: | Size: 166 KiB |
After Width: | Height: | Size: 2.2 KiB |
After Width: | Height: | Size: 911 B |
After Width: | Height: | Size: 1.1 KiB |
After Width: | Height: | Size: 1.2 KiB |
After Width: | Height: | Size: 1.2 KiB |
After Width: | Height: | Size: 600 B |
After Width: | Height: | Size: 673 B |
After Width: | Height: | Size: 2.0 KiB |
After Width: | Height: | Size: 462 B |
After Width: | Height: | Size: 2.1 KiB |
After Width: | Height: | Size: 926 B |
After Width: | Height: | Size: 9.0 KiB |
After Width: | Height: | Size: 23 KiB |
After Width: | Height: | Size: 7.0 KiB |
After Width: | Height: | Size: 878 B |
After Width: | Height: | Size: 1.2 KiB |
After Width: | Height: | Size: 653 B |
After Width: | Height: | Size: 800 B |
After Width: | Height: | Size: 905 B |
After Width: | Height: | Size: 189 KiB |
After Width: | Height: | Size: 4.3 KiB |
After Width: | Height: | Size: 839 B |
After Width: | Height: | Size: 1.7 KiB |
After Width: | Height: | Size: 5.9 KiB |
After Width: | Height: | Size: 1.6 KiB |
After Width: | Height: | Size: 34 KiB |
After Width: | Height: | Size: 26 KiB |
After Width: | Height: | Size: 3.8 KiB |
After Width: | Height: | Size: 23 KiB |
After Width: | Height: | Size: 1.9 KiB |
After Width: | Height: | Size: 2.3 KiB |
After Width: | Height: | Size: 427 B |
After Width: | Height: | Size: 627 B |
After Width: | Height: | Size: 1.1 KiB |
After Width: | Height: | Size: 1.2 KiB |
After Width: | Height: | Size: 988 B |
After Width: | Height: | Size: 3.7 KiB |
After Width: | Height: | Size: 5.0 KiB |
After Width: | Height: | Size: 875 B |
After Width: | Height: | Size: 1.2 KiB |
After Width: | Height: | Size: 144 KiB |
After Width: | Height: | Size: 149 KiB |
After Width: | Height: | Size: 127 KiB |
After Width: | Height: | Size: 95 KiB |
After Width: | Height: | Size: 126 KiB |
After Width: | Height: | Size: 146 KiB |
After Width: | Height: | Size: 113 KiB |
After Width: | Height: | Size: 82 KiB |
After Width: | Height: | Size: 79 KiB |
After Width: | Height: | Size: 176 KiB |
After Width: | Height: | Size: 88 KiB |
After Width: | Height: | Size: 76 KiB |
After Width: | Height: | Size: 880 KiB |
After Width: | Height: | Size: 89 KiB |
After Width: | Height: | Size: 9.8 KiB |
@ -0,0 +1,137 @@ |
|||||
|
<?xml version="1.0" encoding="utf-8"?> |
||||
|
<odoo> |
||||
|
<!-- Dynamic fields view tree --> |
||||
|
<record id="dynamic_fields_view_list" model="ir.ui.view"> |
||||
|
<field name="name">dynamic.fields.view.list</field> |
||||
|
<field name="model">dynamic.fields</field> |
||||
|
<field name="arch" type="xml"> |
||||
|
<list> |
||||
|
<field name="name"/> |
||||
|
<field name="field_description"/> |
||||
|
<field name="model_id"/> |
||||
|
<field name="status"/> |
||||
|
</list> |
||||
|
</field> |
||||
|
</record> |
||||
|
<!-- Dynamic fields view form --> |
||||
|
<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_field" |
||||
|
string="Create Field" type="object" |
||||
|
class="oe_highlight" |
||||
|
invisible="status in ('form','list')" |
||||
|
groups="all_in_one_dynamic_custom_fields.group_all_in_one_dynamic_custom_fields_administrator"/> |
||||
|
<field name="status" widget="statusbar" |
||||
|
statusbar_visible="draft,form"/> |
||||
|
</header> |
||||
|
<sheet> |
||||
|
<group> |
||||
|
<group string="Field Info"> |
||||
|
<field name="name" readonly="status != 'draft'"/> |
||||
|
<field name="field_description" |
||||
|
readonly="status != 'draft'"/> |
||||
|
<field name="state" readonly="1" |
||||
|
groups="base.group_no_one"/> |
||||
|
<field name="model_id" |
||||
|
options='{"no_open": True, "no_create": True}' |
||||
|
readonly="status != 'draft'"/> |
||||
|
<field name="field_type" |
||||
|
readonly="status != 'draft'"/> |
||||
|
<field name="selection_field" |
||||
|
placeholder="Eg: [('blue', 'Blue'),('yellow', 'Yellow')]" |
||||
|
required="field_type in ('selection','reference')" |
||||
|
readonly="field_type not in ('selection','reference') and status != 'draft'" |
||||
|
invisible="field_type not in ('selection','reference')"/> |
||||
|
<field name="ref_model_id" |
||||
|
options='{"no_open": True, "no_create": True}' |
||||
|
required="field_type in ('many2one','many2many')" |
||||
|
readonly="field_type not in ('many2one','many2many') or status != 'draft'" |
||||
|
invisible="field_type not in ('many2one','many2many')"/> |
||||
|
<field name="widget_id" widget="selection" |
||||
|
invisible="field_type not in ('binary','many2many', 'selection', 'float', 'many2one')" |
||||
|
readonly="status != 'draft'"/> |
||||
|
<field name="required" |
||||
|
readonly="status != 'draft'"/> |
||||
|
<field name="model" invisible="1"/> |
||||
|
<field name="form_view_ids" invisible="1"/> |
||||
|
<field name="tree_view_ids" invisible="1"/> |
||||
|
<field name="form_view_id" |
||||
|
options='{"no_open": True, "no_create": True}' |
||||
|
readonly="status != 'draft'"/> |
||||
|
<field name="form_view_inherit_id"/> |
||||
|
</group> |
||||
|
<group string="Tree View"> |
||||
|
<field name="add_field_in_tree" |
||||
|
readonly="status != 'draft'"/> |
||||
|
<field name="tree_view_id" |
||||
|
options='{"no_open": True, "no_create": True}' |
||||
|
invisible="not add_field_in_tree" |
||||
|
required="add_field_in_tree == 'True'" |
||||
|
readonly="status != 'draft'"/> |
||||
|
<field name="tree_view_inherit_id" |
||||
|
invisible="not add_field_in_tree"/> |
||||
|
<field name="tree_field_ids" invisible="1"/> |
||||
|
<field name="tree_field_id" required="add_field_in_tree" invisible="not add_field_in_tree"/> |
||||
|
<field name="tree_field_position" required="add_field_in_tree" invisible="not add_field_in_tree"/> |
||||
|
<field name="is_visible_in_tree_view" required="add_field_in_tree" invisible="not add_field_in_tree"/> |
||||
|
</group> |
||||
|
<group string="Position"> |
||||
|
<field name="position_field_id" |
||||
|
options='{"no_open": True, "no_create": True}' |
||||
|
readonly="status != 'draft'"/> |
||||
|
<field name="position" |
||||
|
readonly="status != 'draft'"/> |
||||
|
</group> |
||||
|
</group> |
||||
|
<group string="Extra Properties"> |
||||
|
<group> |
||||
|
<field name="extra_features" |
||||
|
readonly="status != 'draft'"/> |
||||
|
</group> |
||||
|
<group invisible="not extra_features"> |
||||
|
<field name="help" readonly="status != 'draft'"/> |
||||
|
</group> |
||||
|
<group invisible="not extra_features"> |
||||
|
<field name="readonly" |
||||
|
readonly="status != 'draft'"/> |
||||
|
<field name="store" readonly="status != 'draft'"/> |
||||
|
<field name="index" readonly="status != 'draft'"/> |
||||
|
<field name="copied" readonly="status != 'draft'"/> |
||||
|
</group> |
||||
|
</group> |
||||
|
</sheet> |
||||
|
</form> |
||||
|
</field> |
||||
|
</record> |
||||
|
|
||||
|
<!-- Dynamic fields action--> |
||||
|
<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">list,form</field> |
||||
|
<field name="help" type="html"> |
||||
|
<p class="o_view_nocontent_smiling_face"> |
||||
|
Create New Custom Dynamic Field! |
||||
|
</p> |
||||
|
</field> |
||||
|
</record> |
||||
|
<!-- All in One Custom Dynamic Fields root menu--> |
||||
|
<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"/> |
||||
|
<!-- Create custom dynamic fields menu--> |
||||
|
<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_all_in_one_dynamic_custom_fields_root" |
||||
|
action="dynamic_fields_action" |
||||
|
groups="group_all_in_one_dynamic_custom_fields_user,group_all_in_one_dynamic_custom_fields_administrator" |
||||
|
sequence="2"/> |
||||
|
</odoo> |