[REM] base: remove model ir.values
This commit is contained in:
@@ -1,22 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from odoo import api, models, tools
|
||||
from odoo.addons.calendar.models.calendar import calendar_id2real_id
|
||||
|
||||
class IrValues(models.Model):
|
||||
_inherit = 'ir.values'
|
||||
|
||||
@api.model
|
||||
@api.returns('self', lambda value: value.id)
|
||||
def set_action(self, name, action_slot, model, action, res_id=False):
|
||||
if res_id:
|
||||
res_id = calendar_id2real_id(res_id)
|
||||
return super(IrValues, self).set_action(name, action_slot, model, action, res_id=res_id)
|
||||
|
||||
@api.model
|
||||
@tools.ormcache_context('self._uid', 'action_slot', 'model', 'res_id', keys=('lang',))
|
||||
def get_actions(self, action_slot, model, res_id=False):
|
||||
if res_id:
|
||||
res_id = calendar_id2real_id(res_id)
|
||||
return super(IrValues, self).get_actions(action_slot, model, res_id=res_id)
|
||||
@@ -299,10 +299,6 @@ def set_cookie_and_redirect(redirect_url):
|
||||
redirect.autocorrect_location_header = False
|
||||
return redirect
|
||||
|
||||
def load_actions_from_ir_values(action_slot, model, res_id):
|
||||
actions = request.env['ir.values'].get_actions(action_slot, model, res_id)
|
||||
return [(id, name, clean_action(action)) for id, name, action in actions]
|
||||
|
||||
def clean_action(action):
|
||||
action.setdefault('flags', {})
|
||||
action_type = action.setdefault('type', 'ir.actions.act_window_close')
|
||||
|
||||
@@ -32,7 +32,6 @@ The kernel of Odoo, needed for all installation.
|
||||
'ir/ir_translation_view.xml',
|
||||
'ir/ir_ui_menu_view.xml',
|
||||
'ir/ir_ui_view_view.xml',
|
||||
'ir/ir_values_view.xml',
|
||||
'ir/ir_default_view.xml',
|
||||
'ir/ir_autovacuum.xml',
|
||||
'ir/ir_model_report.xml',
|
||||
|
||||
@@ -10,7 +10,6 @@ from . import ir_actions_report
|
||||
from . import ir_attachment
|
||||
from . import ir_cron
|
||||
from . import ir_filters
|
||||
from . import ir_values
|
||||
from . import ir_default
|
||||
from . import ir_translation
|
||||
from . import ir_exports
|
||||
|
||||
@@ -46,8 +46,6 @@ class IrActions(models.Model):
|
||||
res = super(IrActions, self).create(vals)
|
||||
# self.get_bindings() depends on action records
|
||||
self.clear_caches()
|
||||
# ir_values.get_actions() depends on action records
|
||||
self.env['ir.values'].clear_caches()
|
||||
return res
|
||||
|
||||
@api.multi
|
||||
@@ -55,8 +53,6 @@ class IrActions(models.Model):
|
||||
res = super(IrActions, self).write(vals)
|
||||
# self.get_bindings() depends on action records
|
||||
self.clear_caches()
|
||||
# ir_values.get_actions() depends on action records
|
||||
self.env['ir.values'].clear_caches()
|
||||
return res
|
||||
|
||||
@api.multi
|
||||
@@ -68,8 +64,6 @@ class IrActions(models.Model):
|
||||
res = super(IrActions, self).unlink()
|
||||
# self.get_bindings() depends on action records
|
||||
self.clear_caches()
|
||||
# ir_values.get_actions() depends on action records
|
||||
self.env['ir.values'].clear_caches()
|
||||
return res
|
||||
|
||||
@api.model
|
||||
|
||||
@@ -1,431 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from ast import literal_eval
|
||||
|
||||
from odoo import api, fields, models, tools, _
|
||||
from odoo.exceptions import AccessError, MissingError, ValidationError
|
||||
from odoo.tools import pickle, pycompat
|
||||
|
||||
import logging
|
||||
_logger = logging.getLogger(__name__)
|
||||
|
||||
#: Possible slots to bind an action to with :meth:`~.set_action`
|
||||
ACTION_SLOTS = [
|
||||
"client_action_multi", # sidebar wizard action
|
||||
"client_print_multi", # sidebar report printing button
|
||||
"client_action_relate", # sidebar related link
|
||||
"tree_but_open", # double-click on item in tree view
|
||||
"tree_but_action", # deprecated: same as tree_but_open
|
||||
]
|
||||
|
||||
|
||||
class IrValues(models.Model):
|
||||
"""Holds internal model-specific action bindings and user-defined default
|
||||
field values. definitions. This is a legacy internal model, mixing
|
||||
two different concepts, and will likely be updated or replaced in a
|
||||
future version by cleaner, separate models. You should not depend
|
||||
explicitly on it.
|
||||
|
||||
The purpose of each ``ir.values`` entry depends on its type, defined
|
||||
by the ``key`` column:
|
||||
|
||||
* 'default': user-defined default values, used when creating new
|
||||
records of this model:
|
||||
* 'action': binding of an action to a particular *action slot* of
|
||||
this model, making the action easily available in the user
|
||||
interface for this model.
|
||||
|
||||
The ``key2`` column acts as a qualifier, further refining the type
|
||||
of the entry. The possible values are:
|
||||
|
||||
* for 'default' entries: an optional condition restricting the
|
||||
cases where this particular default value will be applicable,
|
||||
or ``False`` for no condition
|
||||
* for 'action' entries: the ``key2`` qualifier is one of the available
|
||||
action slots, defining how this action can be invoked:
|
||||
|
||||
* ``'client_print_multi'`` for report printing actions that will
|
||||
be available on views displaying items from this model
|
||||
* ``'client_action_multi'`` for assistants (wizards) actions
|
||||
that will be available in views displaying objects of this model
|
||||
* ``'client_action_relate'`` for links towards related documents
|
||||
that should be available in views displaying objects of this model
|
||||
* ``'tree_but_open'`` for actions that will be triggered when
|
||||
double-clicking an item from this model in a hierarchical tree view
|
||||
|
||||
Each entry is specific to a model (``model`` column), and for ``'actions'``
|
||||
type, may even be made specific to a given record of that model when the
|
||||
``res_id`` column contains a record ID (``False`` means it's global for
|
||||
all records).
|
||||
|
||||
The content of the entry is defined by the ``value`` column, which may either
|
||||
contain an arbitrary value, or a reference string defining the action that
|
||||
should be executed.
|
||||
|
||||
.. rubric:: Usage: default values
|
||||
|
||||
The ``'default'`` entries are usually defined manually by the
|
||||
users, and set by their UI clients calling :meth:`~.set_default`.
|
||||
These default values are then automatically used by the
|
||||
ORM every time a new record is about to be created, i.e. when
|
||||
:meth:`~odoo.models.Model.default_get`
|
||||
or :meth:`~odoo.models.Model.create` are called.
|
||||
|
||||
.. rubric:: Usage: action bindings
|
||||
|
||||
Business applications will usually bind their actions during
|
||||
installation, and Odoo UI clients will apply them as defined,
|
||||
based on the list of actions included in the result of
|
||||
:meth:`~odoo.models.Model.fields_view_get`,
|
||||
or directly returned by explicit calls to :meth:`~.get_actions`.
|
||||
"""
|
||||
_name = 'ir.values'
|
||||
|
||||
name = fields.Char(required=True)
|
||||
model = fields.Char(string='Model Name', index=True, required=True,
|
||||
help="Model to which this entry applies")
|
||||
|
||||
# TODO: model_id and action_id should be read-write function fields
|
||||
model_id = fields.Many2one('ir.model', string='Model (change only)',
|
||||
help="Model to which this entry applies - "
|
||||
"helper field for setting a model, will "
|
||||
"automatically set the correct model name")
|
||||
action_id = fields.Many2one('ir.actions.actions', string='Action (change only)',
|
||||
help="Action bound to this entry - "
|
||||
"helper field for binding an action, will "
|
||||
"automatically set the correct reference")
|
||||
|
||||
value = fields.Binary(help="Default value (pickled) or reference to an action")
|
||||
value_unpickle = fields.Binary(string='Default value or action reference',
|
||||
compute='_value_unpickle', inverse='_value_pickle')
|
||||
key = fields.Selection([('action', 'Action'), ('default', 'Default')],
|
||||
string='Type', index=True, required=True, default='action',
|
||||
help="- Action: an action attached to one slot of the given model\n"
|
||||
"- Default: a default value for a model field")
|
||||
key2 = fields.Char(string='Qualifier', index=True, default='tree_but_open',
|
||||
help="For actions, one of the possible action slots: \n"
|
||||
" - client_action_multi\n"
|
||||
" - client_print_multi\n"
|
||||
" - client_action_relate\n"
|
||||
" - tree_but_open\n"
|
||||
"For defaults, an optional condition")
|
||||
res_id = fields.Integer(string='Record ID', index=True,
|
||||
help="Database identifier of the record to which this applies. "
|
||||
"0 = for all records")
|
||||
user_id = fields.Many2one('res.users', string='User', ondelete='cascade', index=True,
|
||||
help="If set, action binding only applies for this user.")
|
||||
company_id = fields.Many2one('res.company', string='Company', ondelete='cascade', index=True,
|
||||
help="If set, action binding only applies for this company")
|
||||
|
||||
@api.depends('key', 'value')
|
||||
def _value_unpickle(self):
|
||||
for record in self:
|
||||
value = record.value
|
||||
if record.key == 'default' and value:
|
||||
# default values are pickled on the fly
|
||||
with tools.ignore(Exception):
|
||||
value = str(pickle.loads(value))
|
||||
record.value_unpickle = value
|
||||
|
||||
def _value_pickle(self):
|
||||
context = dict(self._context)
|
||||
context.pop(self.CONCURRENCY_CHECK_FIELD, None)
|
||||
for record in self.with_context(context):
|
||||
value = record.value_unpickle
|
||||
# Only char-like fields should be written directly. Other types should be converted to
|
||||
# their appropriate type first.
|
||||
if record.model in self.env and record.name in self.env[record.model]._fields:
|
||||
field = self.env[record.model]._fields[record.name]
|
||||
if field.type not in ['char', 'text', 'html', 'selection']:
|
||||
value = literal_eval(value)
|
||||
if record.key == 'default':
|
||||
value = pickle.dumps(value)
|
||||
record.value = value
|
||||
|
||||
@api.onchange('model_id')
|
||||
def onchange_object_id(self):
|
||||
if self.model_id:
|
||||
self.model = self.model_id.model
|
||||
|
||||
@api.onchange('action_id')
|
||||
def onchange_action_id(self):
|
||||
if self.action_id:
|
||||
self.value_unpickle = self.action_id
|
||||
|
||||
@api.model_cr_context
|
||||
def _auto_init(self):
|
||||
res = super(IrValues, self)._auto_init()
|
||||
tools.create_index(self._cr, 'ir_values_key_model_key2_res_id_user_id_idx',
|
||||
self._table, ['key', 'model', 'key2', 'res_id', 'user_id'])
|
||||
return res
|
||||
|
||||
@api.model
|
||||
def create(self, vals):
|
||||
self.clear_caches()
|
||||
return super(IrValues, self).create(vals)
|
||||
|
||||
@api.multi
|
||||
def write(self, vals):
|
||||
self.clear_caches()
|
||||
return super(IrValues, self).write(vals)
|
||||
|
||||
@api.multi
|
||||
def unlink(self):
|
||||
self.clear_caches()
|
||||
return super(IrValues, self).unlink()
|
||||
|
||||
@api.model
|
||||
@api.returns('self', lambda value: value.id)
|
||||
def set_default(self, model, field_name, value, for_all_users=True, company_id=False, condition=False):
|
||||
"""Defines a default value for the given model and field_name. Any previous
|
||||
default for the same scope (model, field_name, value, for_all_users, company_id, condition)
|
||||
will be replaced and lost in the process.
|
||||
|
||||
Defaults can be later retrieved via :meth:`~.get_defaults`, which will return
|
||||
the highest priority default for any given field. Defaults that are more specific
|
||||
have a higher priority, in the following order (highest to lowest):
|
||||
|
||||
* specific to user and company
|
||||
* specific to user only
|
||||
* specific to company only
|
||||
* global to everyone
|
||||
|
||||
:param string model: model name
|
||||
:param string field_name: field name to which the default applies
|
||||
:param value: the default field value to set
|
||||
:type value: any serializable Python value
|
||||
:param bool for_all_users: whether the default should apply to everybody or only
|
||||
the user calling the method
|
||||
:param int company_id: optional ID of the company to which the default should
|
||||
apply. If omitted, the default will be global. If True
|
||||
is passed, the current user's company will be used.
|
||||
:param string condition: optional condition specification that can be used to
|
||||
restrict the applicability of the default values
|
||||
(e.g. based on another field's value). This is an
|
||||
opaque string as far as the API is concerned, but client
|
||||
stacks typically use single-field conditions in the
|
||||
form ``'key=stringified_value'``.
|
||||
(Currently, the condition is trimmed to 200 characters,
|
||||
so values that share the same first 200 characters always
|
||||
match)
|
||||
:return: the newly created ir.values entry
|
||||
"""
|
||||
if isinstance(value, pycompat.string_types):
|
||||
value = pycompat.to_native(value)
|
||||
if company_id is True:
|
||||
# should be company-specific, need to get company id
|
||||
company_id = self.env.user.company_id.id
|
||||
|
||||
# check consistency of model, field_name and value
|
||||
try:
|
||||
field = self.env[model]._fields[field_name]
|
||||
field.convert_to_cache(value, self.browse())
|
||||
except KeyError:
|
||||
_logger.warning("Invalid field %s.%s", model, field_name)
|
||||
except Exception:
|
||||
raise ValidationError(_("Invalid value for %s.%s: %s") % (model, field_name, value))
|
||||
|
||||
# remove existing defaults for the same scope
|
||||
search_criteria = [
|
||||
('key', '=', 'default'),
|
||||
('key2', '=', condition and condition[:200]),
|
||||
('model', '=', model),
|
||||
('name', '=', field_name),
|
||||
('user_id', '=', False if for_all_users else self._uid),
|
||||
('company_id', '=', company_id)
|
||||
]
|
||||
self.search(search_criteria).unlink()
|
||||
|
||||
return self.create({
|
||||
'name': field_name,
|
||||
'value': pickle.dumps(value),
|
||||
'model': model,
|
||||
'key': 'default',
|
||||
'key2': condition and condition[:200],
|
||||
'user_id': False if for_all_users else self._uid,
|
||||
'company_id': company_id,
|
||||
})
|
||||
|
||||
@api.model
|
||||
def get_default(self, model, field_name, for_all_users=True, company_id=False, condition=False):
|
||||
""" Return the default value defined for model, field_name, users, company and condition.
|
||||
Return ``None`` if no such default exists.
|
||||
"""
|
||||
search_criteria = [
|
||||
('key', '=', 'default'),
|
||||
('key2', '=', condition and condition[:200]),
|
||||
('model', '=', model),
|
||||
('name', '=', field_name),
|
||||
('user_id', '=', False if for_all_users else self._uid),
|
||||
('company_id', '=', company_id)
|
||||
]
|
||||
defaults = self.search(search_criteria)
|
||||
return pickle.loads(defaults.value) if defaults else None
|
||||
|
||||
@api.model
|
||||
def get_defaults(self, model, condition=False):
|
||||
"""Returns any default values that are defined for the current model and user,
|
||||
(and match ``condition``, if specified), previously registered via
|
||||
:meth:`~.set_default`.
|
||||
|
||||
Defaults are global to a model, not field-specific, but an optional
|
||||
``condition`` can be provided to restrict matching default values
|
||||
to those that were defined for the same condition (usually based
|
||||
on another field's value).
|
||||
|
||||
Default values also have priorities depending on whom they apply
|
||||
to: only the highest priority value will be returned for any
|
||||
field. See :meth:`~.set_default` for more details.
|
||||
|
||||
:param string model: model name
|
||||
:param string condition: optional condition specification that can be used to
|
||||
restrict the applicability of the default values
|
||||
(e.g. based on another field's value). This is an
|
||||
opaque string as far as the API is concerned, but client
|
||||
stacks typically use single-field conditions in the
|
||||
form ``'key=stringified_value'``.
|
||||
(Currently, the condition is trimmed to 200 characters,
|
||||
so values that share the same first 200 characters always
|
||||
match)
|
||||
:return: list of default values tuples of the form ``(id, field_name, value)``
|
||||
(``id`` is the ID of the default entry, usually irrelevant)
|
||||
"""
|
||||
# use a direct SQL query for performance reasons,
|
||||
# this is called very often
|
||||
query = """ SELECT v.id, v.name, v.value FROM ir_values v
|
||||
LEFT JOIN res_users u ON (v.user_id = u.id)
|
||||
WHERE v.key = %%s AND v.model = %%s
|
||||
AND (v.user_id = %%s OR v.user_id IS NULL)
|
||||
AND (v.company_id IS NULL OR
|
||||
v.company_id = (SELECT company_id FROM res_users WHERE id = %%s)
|
||||
)
|
||||
%s
|
||||
ORDER BY v.user_id, v.company_id, v.id"""
|
||||
params = ('default', model, self._uid, self._uid)
|
||||
if condition:
|
||||
query = query % 'AND v.key2 = %s'
|
||||
params += (condition[:200],)
|
||||
else:
|
||||
query = query % 'AND v.key2 IS NULL'
|
||||
self._cr.execute(query, params)
|
||||
|
||||
# keep only the highest priority default for each field
|
||||
defaults = {}
|
||||
for row in self._cr.dictfetchall():
|
||||
value = pickle.loads(row['value'])
|
||||
defaults.setdefault(row['name'], (row['id'], row['name'], value))
|
||||
return list(defaults.values())
|
||||
|
||||
# use ormcache: this is called a lot by BaseModel.default_get()!
|
||||
@api.model
|
||||
@tools.ormcache('self._uid', 'model', 'condition')
|
||||
def get_defaults_dict(self, model, condition=False):
|
||||
""" Returns a dictionary mapping field names with their corresponding
|
||||
default value. This method simply improves the returned value of
|
||||
:meth:`~.get_defaults`.
|
||||
"""
|
||||
return dict((f, v) for i, f, v in self.get_defaults(model, condition))
|
||||
|
||||
@api.model
|
||||
@api.returns('self', lambda value: value.id)
|
||||
def set_action(self, name, action_slot, model, action, res_id=False):
|
||||
"""Binds an the given action to the given model's action slot - for later
|
||||
retrieval via :meth:`~.get_actions`. Any existing binding of the same action
|
||||
to the same slot is first removed, allowing an update of the action's name.
|
||||
See the class description for more details about the various action
|
||||
slots: :class:`~ir_values`.
|
||||
|
||||
:param string name: action label, usually displayed by UI client
|
||||
:param string action_slot: the action slot to which the action should be
|
||||
bound to - one of ``client_action_multi``,
|
||||
``client_print_multi``, ``client_action_relate``,
|
||||
``tree_but_open``.
|
||||
:param string model: model name
|
||||
:param string action: action reference, in the form ``'model,id'``
|
||||
:param int res_id: optional record id - will bind the action only to a
|
||||
specific record of the model, not all records.
|
||||
:return: the newly created ir.values entry
|
||||
"""
|
||||
assert isinstance(action, pycompat.string_types) and ',' in action, \
|
||||
'Action definition must be an action reference, e.g. "ir.actions.act_window,42"'
|
||||
assert action_slot in ACTION_SLOTS, \
|
||||
'Action slot (%s) must be one of: %r' % (action_slot, ACTION_SLOTS)
|
||||
|
||||
action = action.encode('utf-8')
|
||||
|
||||
# remove existing action definition of same slot and value
|
||||
search_criteria = [
|
||||
('key', '=', 'action'),
|
||||
('key2', '=', action_slot),
|
||||
('model', '=', model),
|
||||
('res_id', '=', res_id or 0), # int field -> NULL == 0
|
||||
('value', '=', action),
|
||||
]
|
||||
self.search(search_criteria).unlink()
|
||||
|
||||
return self.create({
|
||||
'key': 'action',
|
||||
'key2': action_slot,
|
||||
'model': model,
|
||||
'res_id': res_id,
|
||||
'name': name,
|
||||
'value': action,
|
||||
})
|
||||
|
||||
@api.model
|
||||
@tools.ormcache_context('self._uid', 'action_slot', 'model', 'res_id', keys=('lang',))
|
||||
def get_actions(self, action_slot, model, res_id=False):
|
||||
"""Retrieves the list of actions bound to the given model's action slot.
|
||||
See the class description for more details about the various action
|
||||
slots: :class:`~.ir_values`.
|
||||
|
||||
:param string action_slot: the action slot to which the actions should be
|
||||
bound to - one of ``client_action_multi``,
|
||||
``client_print_multi``, ``client_action_relate``,
|
||||
``tree_but_open``.
|
||||
:param string model: model name
|
||||
:param int res_id: optional record id - will bind the action only to a
|
||||
specific record of the model, not all records.
|
||||
:return: list of action tuples of the form ``(id, name, action_def)``,
|
||||
where ``id`` is the ID of the default entry, ``name`` is the
|
||||
action label, and ``action_def`` is a dict containing the
|
||||
action definition as obtained by calling
|
||||
:meth:`~odoo.models.Model.read` on the action record.
|
||||
"""
|
||||
assert action_slot in ACTION_SLOTS, 'Illegal action slot value: %s' % action_slot
|
||||
# use a direct SQL query for performance reasons,
|
||||
# this is called very often
|
||||
query = """ SELECT v.id, v.name, v.value FROM ir_values v
|
||||
WHERE v.key = %s AND v.key2 = %s AND v.model = %s
|
||||
AND (v.res_id = %s OR v.res_id IS NULL OR v.res_id = 0)
|
||||
ORDER BY v.id """
|
||||
self._cr.execute(query, ('action', action_slot, model, res_id or None))
|
||||
|
||||
# map values to their corresponding action record
|
||||
actions = []
|
||||
for id, name, value in self._cr.fetchall():
|
||||
if not value:
|
||||
continue # skip if undefined
|
||||
action_model, action_id = bytes(value).decode('utf-8').split(',')
|
||||
if action_model not in self.env:
|
||||
continue # unknown model? skip it!
|
||||
action = self.env[action_model].browse(int(action_id))
|
||||
actions.append((id, name, action))
|
||||
|
||||
# process values and their action
|
||||
results = {}
|
||||
for id, name, action in actions:
|
||||
# FIXME: needs cleanup
|
||||
try:
|
||||
action_def = dict([(k, v.convert_to_read(action[k], action)) for k, v in action._fields.items()])
|
||||
if action._name in ('ir.actions.report', 'ir.actions.act_window'):
|
||||
if action.groups_id and not action.groups_id & self.env.user.groups_id:
|
||||
if name == 'Menuitem':
|
||||
raise AccessError(_('You do not have the permission to perform this operation!!!'))
|
||||
continue
|
||||
# keep only the last action registered for each action name
|
||||
results[name] = (id, name, action_def)
|
||||
except (AccessError, MissingError):
|
||||
continue
|
||||
return sorted(results.values())
|
||||
@@ -1,126 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
<!-- Values -->
|
||||
<record id="values_view_form_action" model="ir.ui.view">
|
||||
<field name="name">ir.values.form.action</field>
|
||||
<field name="model">ir.values</field>
|
||||
<field name="arch" type="xml">
|
||||
<form string="Action Bindings">
|
||||
<group>
|
||||
<group>
|
||||
<field name="name"/>
|
||||
<field name="model_id"/>
|
||||
<field name="model"/>
|
||||
<field name="res_id"/>
|
||||
<field name="key2"/>
|
||||
</group>
|
||||
<group>
|
||||
<field name="action_id"/>
|
||||
<field name="value_unpickle" colspan="4" string="Action Reference"/>
|
||||
</group>
|
||||
</group>
|
||||
</form>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="values_view_form_defaults" model="ir.ui.view">
|
||||
<field name="name">ir.values.form.defaults</field>
|
||||
<field name="model">ir.values</field>
|
||||
<field name="arch" type="xml">
|
||||
<form string="User-defined Defaults">
|
||||
<group>
|
||||
<group>
|
||||
<field name="name"/>
|
||||
<field name="model"/>
|
||||
<field name="model_id"/>
|
||||
</group>
|
||||
<group>
|
||||
<field name="key2" string="Condition"/>
|
||||
<field name="user_id"/>
|
||||
<field name="company_id" groups="base.group_multi_company"/>
|
||||
</group>
|
||||
<separator string="Default value or action reference"/>
|
||||
<field name="value_unpickle" nolabel="1" colspan="2"/>
|
||||
</group>
|
||||
</form>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="values_view_tree_action" model="ir.ui.view">
|
||||
<field name="name">ir.values.tree.action</field>
|
||||
<field name="model">ir.values</field>
|
||||
<field name="arch" type="xml">
|
||||
<tree string="Action Bindings/Defaults">
|
||||
<field name="name"/>
|
||||
<field name="model"/>
|
||||
<field name="value_unpickle"/>
|
||||
<field name="key2"/>
|
||||
</tree>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="values_view_search_action" model="ir.ui.view">
|
||||
<field name="name">ir.values.search.action</field>
|
||||
<field name="model">ir.values</field>
|
||||
<field name="arch" type="xml">
|
||||
<search string="Client Actions">
|
||||
<field name="name"
|
||||
filter_domain="['|', '|', ('name','ilike',self), ('model','ilike',self), ('key2','ilike',self)]"
|
||||
string="Client Action"/>
|
||||
<group expand="0" string="Group By">
|
||||
<filter string="Model" domain="[]" context="{'group_by':'model'}"/>
|
||||
<filter string="Type" domain="[]" context="{'group_by':'key2'}"/>
|
||||
</group>
|
||||
</search>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="act_values_form_action" model="ir.actions.act_window">
|
||||
<field name="name">Action Bindings</field>
|
||||
<field name="type">ir.actions.act_window</field>
|
||||
<field name="res_model">ir.values</field>
|
||||
<field name="view_type">form</field>
|
||||
<field name="view_mode">tree,form</field>
|
||||
<field name="search_view_id" ref="values_view_search_action"/>
|
||||
<field name="domain">[('key','=','action')]</field>
|
||||
<field name="context">{'default_key':'action'}</field>
|
||||
</record>
|
||||
<record model="ir.actions.act_window.view" id="action_values_tree_view">
|
||||
<field name="sequence" eval="1"/>
|
||||
<field name="view_mode">tree</field>
|
||||
<field name="view_id" ref="values_view_tree_action"/>
|
||||
<field name="act_window_id" ref="act_values_form_action"/>
|
||||
</record>
|
||||
<record model="ir.actions.act_window.view" id="action_values_form_view">
|
||||
<field name="sequence" eval="2"/>
|
||||
<field name="view_mode">form</field>
|
||||
<field name="view_id" ref="values_view_form_action"/>
|
||||
<field name="act_window_id" ref="act_values_form_action"/>
|
||||
</record>
|
||||
<menuitem action="act_values_form_action" id="menu_values_form_action" parent="next_id_6"/>
|
||||
|
||||
<record id="act_values_form_defaults" model="ir.actions.act_window">
|
||||
<field name="name">User-defined Defaults</field>
|
||||
<field name="type">ir.actions.act_window</field>
|
||||
<field name="res_model">ir.values</field>
|
||||
<field name="view_type">form</field>
|
||||
<field name="view_mode">tree,form</field>
|
||||
<field name="search_view_id" ref="values_view_search_action"/>
|
||||
<field name="domain">[('key','=','default')]</field>
|
||||
<field name="context">{'default_key':'default','default_key2':''}</field>
|
||||
</record>
|
||||
<record model="ir.actions.act_window.view" id="action_values_defaults_tree_view">
|
||||
<field name="sequence" eval="1"/>
|
||||
<field name="view_mode">tree</field>
|
||||
<field name="view_id" ref="values_view_tree_action"/>
|
||||
<field name="act_window_id" ref="act_values_form_defaults"/>
|
||||
</record>
|
||||
<record model="ir.actions.act_window.view" id="action_values_defaults_form_view">
|
||||
<field name="sequence" eval="2"/>
|
||||
<field name="view_mode">form</field>
|
||||
<field name="view_id" ref="values_view_form_defaults"/>
|
||||
<field name="act_window_id" ref="act_values_form_defaults"/>
|
||||
</record>
|
||||
<menuitem action="act_values_form_defaults" id="menu_values_form_defaults" parent="next_id_6"/>
|
||||
|
||||
</odoo>
|
||||
@@ -683,8 +683,6 @@ class GroupsView(models.Model):
|
||||
self._update_user_groups_view()
|
||||
# actions.get_bindings() depends on action records
|
||||
self.env['ir.actions.actions'].clear_caches()
|
||||
# ir_values.get_actions() depends on action records
|
||||
self.env['ir.values'].clear_caches()
|
||||
return user
|
||||
|
||||
@api.multi
|
||||
@@ -693,8 +691,6 @@ class GroupsView(models.Model):
|
||||
self._update_user_groups_view()
|
||||
# actions.get_bindings() depends on action records
|
||||
self.env['ir.actions.actions'].clear_caches()
|
||||
# ir_values.get_actions() depends on action records
|
||||
self.env['ir.values'].clear_caches()
|
||||
return res
|
||||
|
||||
@api.multi
|
||||
@@ -703,8 +699,6 @@ class GroupsView(models.Model):
|
||||
self._update_user_groups_view()
|
||||
# actions.get_bindings() depends on action records
|
||||
self.env['ir.actions.actions'].clear_caches()
|
||||
# ir_values.get_actions() depends on action records
|
||||
self.env['ir.values'].clear_caches()
|
||||
return res
|
||||
|
||||
@api.model
|
||||
|
||||
@@ -104,13 +104,6 @@
|
||||
<field name="perm_write" eval="False"/>
|
||||
</record>
|
||||
|
||||
<record model="ir.rule" id="ir_values_default_rule">
|
||||
<field name="name">Defaults: alter personal values only</field>
|
||||
<field name="model_id" ref="model_ir_values"/>
|
||||
<field name="domain_force">[('key','=','default'),('user_id','=',user.id)]</field>
|
||||
<field name="perm_read" eval="False"/>
|
||||
</record>
|
||||
|
||||
<record model="ir.rule" id="ir_default_user_rule">
|
||||
<field name="name">Defaults: alter personal defaults</field>
|
||||
<field name="model_id" ref="model_ir_default"/>
|
||||
|
||||
@@ -32,7 +32,6 @@
|
||||
"access_ir_ui_view_group_user","ir_ui_view group_user","model_ir_ui_view",,1,0,0,0
|
||||
"access_ir_ui_view_group_system","ir_ui_view group_system","model_ir_ui_view","group_system",1,1,1,1
|
||||
"access_ir_ui_view_custom_group_user","ir_ui_view_custom_group_user","model_ir_ui_view_custom",,1,1,1,1
|
||||
"access_ir_values_group_all","ir_values group_all","model_ir_values",,1,1,1,1
|
||||
"access_ir_default_all","ir_default all","model_ir_default",,1,0,0,0
|
||||
"access_ir_default_group_user","ir_default group_user","model_ir_default","group_user",1,1,1,1
|
||||
"access_ir_default_group_system","ir_default group_system","model_ir_default","group_system",1,1,1,1
|
||||
|
||||
|
@@ -12,7 +12,6 @@ from . import test_ir_http
|
||||
from . import test_ir_filters
|
||||
from . import test_ir_sequence
|
||||
from . import test_ir_sequence_date_range
|
||||
from . import test_ir_values
|
||||
from . import test_ir_default
|
||||
from . import test_mail
|
||||
from . import test_menu
|
||||
|
||||
@@ -1,129 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from odoo import tools
|
||||
from odoo.exceptions import ValidationError
|
||||
from odoo.tests.common import TransactionCase
|
||||
|
||||
|
||||
class TestIrValues(TransactionCase):
|
||||
|
||||
def test_defaults(self):
|
||||
# Create some default value for some model, for all users.
|
||||
ir_values = self.env['ir.values']
|
||||
ir_values.set_default('res.partner', 'ref', 'X11')
|
||||
ir_values.set_default('res.partner.title', 'shortcut', 'Mr', condition='name=Mister')
|
||||
|
||||
# Retrieve them: ds is a list of triplets (id, name, value)
|
||||
ds = ir_values.get_defaults('res.partner')
|
||||
d = next((d for d in ds if d[1] == 'ref'), None)
|
||||
self.assertTrue(d, "At least one value should be retrieved for this model.")
|
||||
self.assertEqual(d[2], 'X11', "Can't retrieve the created default value.")
|
||||
|
||||
ds = ir_values.get_defaults('res.partner.title')
|
||||
d = next((d for d in ds if d[1] == 'shortcut'), None)
|
||||
self.assertFalse(d, "No value should be retrieved, the condition is not met.")
|
||||
|
||||
ds = ir_values.get_defaults('res.partner.title', condition="name=Miss")
|
||||
d = next((d for d in ds if d[1] == 'shortcut'), None)
|
||||
self.assertFalse(d, "No value should be retrieved, the condition is not met.")
|
||||
|
||||
ds = ir_values.get_defaults('res.partner.title', condition="name=Mister")
|
||||
d = next((d for d in ds if d[1] == 'shortcut'), None)
|
||||
self.assertTrue(d, "At least one value should be retrieved.")
|
||||
self.assertEqual(d[2], 'Mr', "Can't retrieve the created default value.")
|
||||
|
||||
# Do it again but for a specific user.
|
||||
ir_values.set_default('res.partner', 'ref', '007', for_all_users=False)
|
||||
|
||||
# Retrieve it and check it is the one for the current user.
|
||||
ds = ir_values.get_defaults('res.partner')
|
||||
d = next((d for d in ds if d[1] == 'ref'), None)
|
||||
self.assertTrue(d, "At least one value should be retrieved for this model.")
|
||||
self.assertEqual(d[2], '007', "Can't retrieve the created default value.")
|
||||
|
||||
# create valid but unusable defaults, a ValidationError should not be thrown
|
||||
with tools.mute_logger('odoo.addons.base.ir.ir_values'):
|
||||
ir_values.set_default('unknown_model', 'unknown_field', 42)
|
||||
ir_values.set_default('res.partner', 'unknown_field', 42)
|
||||
|
||||
# create invalid defaults
|
||||
with self.assertRaises(ValidationError):
|
||||
ir_values.set_default('res.partner', 'lang', 'some_LANG')
|
||||
with self.assertRaises(ValidationError):
|
||||
ir_values.set_default('res.partner', 'credit_limit', 'foo')
|
||||
|
||||
def test_actions(self):
|
||||
# Create some action bindings for a model.
|
||||
act_id_1 = self.ref('base.act_values_form_action')
|
||||
act_id_2 = self.ref('base.act_values_form_defaults')
|
||||
act_id_3 = self.ref('base.action_res_company_form')
|
||||
ir_values = self.env['ir.values']
|
||||
ir_values.set_action('OnDblClick Action', action_slot='tree_but_open', model='res.partner', action='ir.actions.act_window,%d' % act_id_1, res_id=False)
|
||||
ir_values.set_action('OnDblClick Action 2', action_slot='tree_but_open', model='res.partner', action='ir.actions.act_window,%d' % act_id_2, res_id=False)
|
||||
ir_values.set_action('Side Wizard', action_slot='client_action_multi', model='res.partner', action='ir.actions.act_window,%d' % act_id_3, res_id=False)
|
||||
|
||||
reports = self.env['ir.actions.report'].search([])
|
||||
report_id = next(report.id for report in reports if not report.groups_id)
|
||||
ir_values.set_action('Nice Report', action_slot='client_print_multi', model='res.partner', action='ir.actions.report,%d' % report_id, res_id=False)
|
||||
|
||||
# Replace one action binding to set a new name.
|
||||
ir_values.set_action('OnDblClick Action New', action_slot='tree_but_open', model='res.partner', action='ir.actions.act_window,%d' % act_id_1, res_id=False)
|
||||
|
||||
# Retrieve the action bindings and check they're correct
|
||||
actions = ir_values.get_actions(action_slot='tree_but_open', model='res.partner', res_id=False)
|
||||
self.assertEqual(len(actions), 2, "Mismatching number of bound actions")
|
||||
# first action
|
||||
self.assertEqual(len(actions[0]), 3, "Malformed action definition")
|
||||
self.assertEqual(actions[0][1], 'OnDblClick Action 2', 'Bound action does not match definition')
|
||||
self.assertTrue(isinstance(actions[0][2], dict) and actions[0][2]['id'] == act_id_2,
|
||||
'Bound action does not match definition')
|
||||
# second action - this ones comes last because it was re-created with a different name
|
||||
self.assertEqual(len(actions[1]), 3, "Malformed action definition")
|
||||
self.assertEqual(actions[1][1], 'OnDblClick Action New', 'Re-Registering an action should replace it')
|
||||
self.assertTrue(isinstance(actions[1][2], dict) and actions[1][2]['id'] == act_id_1,
|
||||
'Bound action does not match definition')
|
||||
|
||||
actions = ir_values.get_actions(action_slot='client_action_multi', model='res.partner', res_id=False)
|
||||
self.assertEqual(len(actions), 1, "Mismatching number of bound actions")
|
||||
self.assertEqual(len(actions[0]), 3, "Malformed action definition")
|
||||
self.assertEqual(actions[0][1], 'Side Wizard', 'Bound action does not match definition')
|
||||
self.assertTrue(isinstance(actions[0][2], dict) and actions[0][2]['id'] == act_id_3,
|
||||
'Bound action does not match definition')
|
||||
|
||||
actions = ir_values.get_actions(action_slot='client_print_multi', model='res.partner', res_id=False)
|
||||
self.assertEqual(len(actions), 1, "Mismatching number of bound actions")
|
||||
self.assertEqual(len(actions[0]), 3, "Malformed action definition")
|
||||
self.assertEqual(actions[0][1], 'Nice Report', 'Bound action does not match definition')
|
||||
self.assertTrue(isinstance(actions[0][2], dict) and actions[0][2]['id'] == report_id,
|
||||
'Bound action does not match definition')
|
||||
|
||||
def test_orders(self):
|
||||
ir_values = self.env['ir.values']
|
||||
|
||||
# create a global rule for all
|
||||
ir_values.set_default(
|
||||
'res.partner', 'ref', 'value_global',
|
||||
for_all_users=True, company_id=False, condition=False)
|
||||
self.assertEqual(
|
||||
ir_values.get_defaults_dict('res.partner')['ref'],
|
||||
'value_global',
|
||||
"Can't retrieve the created default value for all.")
|
||||
|
||||
# set a default value for current company (behavior of 'set default' from debug mode)
|
||||
ir_values.set_default(
|
||||
'res.partner', 'ref', 'value_company',
|
||||
for_all_users=True, company_id=True, condition=False)
|
||||
self.assertEqual(
|
||||
ir_values.get_defaults_dict('res.partner')['ref'],
|
||||
'value_company',
|
||||
"Can't retrieve the created default value for company.")
|
||||
|
||||
# set a default value for current user (behavior of 'set default' from debug mode)
|
||||
ir_values.set_default(
|
||||
'res.partner', 'ref', 'value_user',
|
||||
for_all_users=False, company_id=True, condition=False)
|
||||
self.assertEqual(
|
||||
ir_values.get_defaults_dict('res.partner')['ref'],
|
||||
'value_user',
|
||||
"Can't retrieve the created default value for user.")
|
||||
@@ -2832,7 +2832,6 @@ class BaseModel(MetaModel('DummyModel', (object,), {'_register': False})):
|
||||
|
||||
cr = self._cr
|
||||
Data = self.env['ir.model.data'].sudo().with_context({})
|
||||
Values = self.env['ir.values']
|
||||
Defaults = self.env['ir.default'].sudo()
|
||||
Attachment = self.env['ir.attachment']
|
||||
|
||||
@@ -2851,14 +2850,6 @@ class BaseModel(MetaModel('DummyModel', (object,), {'_register': False})):
|
||||
if data:
|
||||
data.unlink()
|
||||
|
||||
# For the same reason, remove the relevant records in ir_values
|
||||
refs = ['%s,%s' % (self._name, i) for i in sub_ids]
|
||||
values = Values.search(['|', ('value', 'in', refs),
|
||||
'&', ('model', '=', self._name),
|
||||
('res_id', 'in', sub_ids)])
|
||||
if values:
|
||||
values.unlink()
|
||||
|
||||
# For the same reason, remove the defaults having some of the
|
||||
# records as value
|
||||
Defaults.discard_records(self.browse(sub_ids))
|
||||
|
||||
Reference in New Issue
Block a user