From 32d45d228bba307cdb2d4b4568ab17c46fccd32e Mon Sep 17 00:00:00 2001 From: Alexis de Lattre Date: Tue, 26 May 2020 17:09:53 +0200 Subject: [PATCH] Add new module base_dynamic_list --- base_dynamic_list/__init__.py | 1 + base_dynamic_list/__manifest__.py | 63 +++++ base_dynamic_list/models/__init__.py | 1 + base_dynamic_list/models/dynamic_list.py | 116 +++++++++ .../security/ir.model.access.csv | 9 + base_dynamic_list/views/dynamic_list.xml | 232 ++++++++++++++++++ 6 files changed, 422 insertions(+) create mode 100644 base_dynamic_list/__init__.py create mode 100644 base_dynamic_list/__manifest__.py create mode 100644 base_dynamic_list/models/__init__.py create mode 100644 base_dynamic_list/models/dynamic_list.py create mode 100644 base_dynamic_list/security/ir.model.access.csv create mode 100644 base_dynamic_list/views/dynamic_list.xml diff --git a/base_dynamic_list/__init__.py b/base_dynamic_list/__init__.py new file mode 100644 index 0000000..0650744 --- /dev/null +++ b/base_dynamic_list/__init__.py @@ -0,0 +1 @@ +from . import models diff --git a/base_dynamic_list/__manifest__.py b/base_dynamic_list/__manifest__.py new file mode 100644 index 0000000..62f60af --- /dev/null +++ b/base_dynamic_list/__manifest__.py @@ -0,0 +1,63 @@ +# -*- coding: utf-8 -*- +# Copyright 2020 Akretion France (http://www.akretion.com) +# @author Alexis de Lattre +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +{ + 'name': 'Base Dynamic List', + 'version': '10.0.1.0.0', + 'category': 'Tools', + 'license': 'AGPL-3', + 'summary': 'Dynamic lists', + 'description': """ +Base Dynamic List +================= + +Very often during an Odoo implementation, we need to add selection fields on a native objet, and we don't want to have a hard-coded selection list (fields.Selection), but a selection list that can be changed by users (Many2one field). For that, the developper needs to add a new object (with just a 'name' and 'sequence' field) with a form/tree view. The goal of this module is to speed-up this process by defining a dynamic list object that already has all the required views. + +This module provides several ready-to-go objects: + +* simple list : fields *name*, *sequence* and *active* +* translatable list : fields *name* with translate=True, *sequence* and *active* +* code list : fields *code* (unique), *name*, *sequence* and *active* +* translatable code list : fields *code* (unique), *name* with translate=True, *sequence* and *active* + +These objects are readable by the employee group. The system group has full rights on it. + +To use it, you need to do 2 or 3 things : + +1) Add an entry in the domain field and the object you selected: + +domain = fields.Selection(selection_add=[('risk.type', "Risk Type")]) + +2) Add the many2one field on your object: + +risk_type_id = fields.Many2one( + 'dynamic.list', string="Risk Type", + ondelete='restrict', domain=[('domain', '=', 'risk.type')]) + + +3) Optionally, you can add a dedicated action and a menu entry (otherwize, you can use the generic menu entry under *Settings > Technical > Dynamic Lists*: + + + Risk Type + dynamic.list + tree,form + [('domain', '=', 'risk.type')] + {'default_domain': 'risk.type'} + + + + +Limitation: when you want to have different access rights on these lists depending on the source object, you should prefer to use dedicated objects. +""", + 'author': 'Akretion', + 'website': 'http://www.akretion.com', + 'depends': ['base'], + 'data': [ + 'security/ir.model.access.csv', + 'views/dynamic_list.xml', + ], + 'installable': True, +} diff --git a/base_dynamic_list/models/__init__.py b/base_dynamic_list/models/__init__.py new file mode 100644 index 0000000..ab91119 --- /dev/null +++ b/base_dynamic_list/models/__init__.py @@ -0,0 +1 @@ +from . import dynamic_list diff --git a/base_dynamic_list/models/dynamic_list.py b/base_dynamic_list/models/dynamic_list.py new file mode 100644 index 0000000..21fa368 --- /dev/null +++ b/base_dynamic_list/models/dynamic_list.py @@ -0,0 +1,116 @@ +# -*- coding: utf-8 -*- +# Copyright 2020 Akretion France (http://www.akretion.com) +# @author Alexis de Lattre +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from odoo import api, fields, models + + +class DynamicList(models.Model): + _name = 'dynamic.list' + _description = 'Dynamic List (non translatable)' + _order = 'sequence, id' + + name = fields.Char(required=True) + sequence = fields.Integer() + active = fields.Boolean(default=True) + domain = fields.Selection([], string='Domain', required=True) + + _sql_constraint = [( + 'domain_name_uniq', + 'unique(domain, name)', + 'This entry already exists!' + )] + + +class DynamicListTranslate(models.Model): + _name = 'dynamic.list.translate' + _description = 'Translatable Dynamic List' + _order = 'sequence, id' + + name = fields.Char(translate=True, required=True) + sequence = fields.Integer() + active = fields.Boolean(default=True) + domain = fields.Selection([], string='Domain', required=True) + + _sql_constraint = [( + 'domain_name_uniq', + 'unique(domain, name)', + 'This entry already exists!' + )] + + +class DynamicListCode(models.Model): + _name = 'dynamic.list.code' + _description = 'Dynamic list with code' + _order = 'sequence, id' + + code = fields.Char(required=True) + name = fields.Char(translate=True, required=True) + sequence = fields.Integer() + active = fields.Boolean(default=True) + domain = fields.Selection([], string='Domain', required=True) + + _sql_constraint = [( + 'domain_code_uniq', + 'unique(domain, code)', + 'This code already exists!' + )] + + @api.depends('code', 'name') + def name_get(self): + res = [] + for rec in self: + res.append((rec.id, u'[%s] %s' % (rec.code, rec.name))) + return res + + @api.model + def name_search( + self, name='', args=None, operator='ilike', limit=80): + if args is None: + args = [] + if name and operator == 'ilike': + recs = self.search( + [('code', '=', name)] + args, limit=limit) + if recs: + return recs.name_get() + return super(DynamicListCode, self).name_search( + name=name, args=args, operator=operator, limit=limit) + + +class DynamicListCodeTranslate(models.Model): + _name = 'dynamic.list.code.translate' + _description = 'Translatable dynamic list with code' + _order = 'sequence, id' + + code = fields.Char(required=True) + name = fields.Char(translate=True, required=True) + sequence = fields.Integer() + active = fields.Boolean(default=True) + domain = fields.Selection([], string='Domain', required=True) + + _sql_constraint = [( + 'domain_code_uniq', + 'unique(domain, code)', + 'This code already exists!' + )] + + @api.depends('code', 'name') + def name_get(self): + res = [] + for rec in self: + res.append((rec.id, u'[%s] %s' % (rec.code, rec.name))) + return res + + @api.model + def name_search( + self, name='', args=None, operator='ilike', limit=80): + if args is None: + args = [] + if name and operator == 'ilike': + recs = self.search( + [('code', '=', name)] + args, limit=limit) + if recs: + return recs.name_get() + return super(DynamicListCodeTranslate, self).name_search( + name=name, args=args, operator=operator, limit=limit) diff --git a/base_dynamic_list/security/ir.model.access.csv b/base_dynamic_list/security/ir.model.access.csv new file mode 100644 index 0000000..b7a626a --- /dev/null +++ b/base_dynamic_list/security/ir.model.access.csv @@ -0,0 +1,9 @@ +id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink +access_dynamic_list_read,Read access on dynamic.list to employees,model_dynamic_list,base.group_user,1,0,0,0 +access_dynamic_list_full,Full access to dynamic.list to System group,model_dynamic_list,base.group_system,1,1,1,1 +access_dynamic_list_translate_read,Read access on dynamic.list.translate to employees,model_dynamic_list_translate,base.group_user,1,0,0,0 +access_dynamic_list_translate_full,Full access to dynamic.list.translate to System group,model_dynamic_list_translate,base.group_system,1,1,1,1 +access_dynamic_list_code_read,Read access on dynamic.list.code to employees,model_dynamic_list_code,base.group_user,1,0,0,0 +access_dynamic_list_code_full,Full access to dynamic.list.code to System group,model_dynamic_list_code,base.group_system,1,1,1,1 +access_dynamic_list_code_translate_read,Read access on dynamic.list.code.translate to employees,model_dynamic_list_code_translate,base.group_user,1,0,0,0 +access_dynamic_list_code_translate_full,Full access to dynamic.list.code.translate to System group,model_dynamic_list_code_translate,base.group_system,1,1,1,1 diff --git a/base_dynamic_list/views/dynamic_list.xml b/base_dynamic_list/views/dynamic_list.xml new file mode 100644 index 0000000..92f1883 --- /dev/null +++ b/base_dynamic_list/views/dynamic_list.xml @@ -0,0 +1,232 @@ + + + + + + + + + + dynamic.list + +
+ +
+ +
+ + + + +
+
+
+
+ + + dynamic.list + + + + + + + + + + + dynamic.list + + + + + + + + + + + + Simple List + dynamic.list + tree,form + {'dynamic_list_main_view': True, 'search_default_domain_groupby': True} + + + + + + dynamic.list.translate + +
+ +
+ +
+ + + + +
+
+
+
+ + + dynamic.list.translate + + + + + + + + + + + dynamic.list.translate + + + + + + + + + + + + Translatable Simple List + dynamic.list.translate + tree,form + {'dynamic_list_translate_main_view': True, 'search_default_domain_groupby': True} + + + + + + dynamic.list.code + +
+ +
+ +
+ + + + + +
+
+
+
+ + + dynamic.list.code + + + + + + + + + + + + dynamic.list.code + + + + + + + + + + + + + Code List + dynamic.list.code + tree,form + {'dynamic_list_code_main_view': True, 'search_default_domain_groupby': True} + + + + + + dynamic.list.code.translate + +
+ +
+ +
+ + + + + +
+
+
+
+ + + dynamic.list.code.translate + + + + + + + + + + + + dynamic.list.code.translate + + + + + + + + + + + + + Translatable Code List + dynamic.list.code.translate + tree,form + {'dynamic_list_code_translate_main_view': True, 'search_default_domain_groupby': True} + + + + + +