Files
web/web_view_searchpanel/models/base.py
Dmytro Katyukha 2f5ddbfd08 [FIX] search_panel view: Do not apply model domain to comodel
Description
-----------

This commit fixes incorrect bechavior (error thrown) in case when
additional `domain` provided to action (`ir.actions.act_window`)
that displays view with search panel enabled.

Before this commit
------------------

For example we have following models:
- My Category
- My Model

And in category view, we have stat-button that display number of records
of records in this category. On click, it have to open view for My
Model, with domain like `[('category_id', '=', 42)]`.
In this case, following error will be raised:

```tracaback
Error:
Odoo Server Error

Traceback (most recent call last):
  ...
  File "/opt/odoo/custom_addons/web_view_searchpanel/models/base.py", line 60, in search_panel_select_range
    hierarchical_naming=False).search_read(model_domain, fields),
  File "/opt/odoo/odoo/odoo/models.py", line 4615, in search_read
    records = self.search(domain or [], offset=offset, limit=limit, order=order)
  File "/opt/odoo/odoo/odoo/models.py", line 1581, in search
    res = self._search(args, offset=offset, limit=limit, order=order, count=count)
  File "/opt/odoo/odoo/odoo/models.py", line 4147, in _search
    query = self._where_calc(args)
  File "/opt/odoo/odoo/odoo/models.py", line 3939, in _where_calc
    e = expression.expression(domain, self)
  File "/opt/odoo/odoo/odoo/osv/expression.py", line 673, in __init__
    self.parse()
  File "/opt/odoo/odoo/odoo/osv/expression.py", line 854, in parse
    raise ValueError("Invalid field %r in leaf %r" % (left, str(leaf)))
ValueError: Invalid field 'category_id' in leaf "<osv.ExtendedLeaf: ('category_id', '=', 26) on bureaucrat_knowledge_category (ctx: )>"
```

Diagnostics
-----------

It seems that model domain was passed to comodel, thus system cannot
find field related to model in comodel. See code
(web_view_searchpanel/models/base.py", line 60, in
search_panel_select_range)

As tested, the `search_domain` causes this bug.

But if we look at implementation of same method
(`search_panel_select_range`) in Odoo 13.0 (see
[code](https://github.com/odoo/odoo/blob/13.0/addons/web/models/models.py#L214))
then we can see, that there is only empty domain applied for search.

Solution
--------

It seems, that variable `model_domain` could be simply removed, and
we could do the search in comodel without any extra domain in this case.

So, this commit, only makes imlementation of this method look same as in
Odoo 13.0

After this commit
-----------------

Everything is working fine.
2021-02-02 13:48:05 +02:00

193 lines
7.3 KiB
Python

# Copyright 2017-2019 MuK IT GmbH.
# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl.html).
import logging
from odoo import _, api, models
from odoo.exceptions import UserError
from odoo.osv import expression
_logger = logging.getLogger(__name__)
class Base(models.AbstractModel):
_inherit = 'base'
@api.model
def search_panel_select_range(self, field_name, **kwargs):
"""
Return possible values of the field field_name (case select="one")
and the parent field (if any) used to hierarchize them.
:param field_name: the name of a many2one category field
:return: {
'parent_field': parent field on the comodel of field, or False
'values': array of dictionaries containing some info on the
records available on the comodel of the field 'field_name'.
The display name (and possibly parent_field) are fetched.
}
"""
field = self._fields[field_name]
supported_types = ['many2one']
if field.type not in supported_types:
raise UserError(_(
'Only types %(supported_types)s are supported for category'
'(found type %(field_type)s)'
) % ({
'supported_types': supported_types,
'field_type': field.type
}))
Comodel = self.env[field.comodel_name]
fields = ['display_name']
parent_name = (
Comodel._parent_name if Comodel._parent_name
in Comodel._fields else False
)
if parent_name:
fields.append(parent_name)
return {
'parent_field': parent_name,
'values': Comodel.with_context(
hierarchical_naming=False).search_read([], fields),
}
@api.model
def search_panel_select_multi_range(self, field_name, **kwargs):
"""
Return possible values of the field field_name (case select="multi"),
possibly with counters and groups.
:param field_name: the name of a filter field;
possible types are many2one, many2many, selection.
:param search_domain: base domain of search
:param category_domain: domain generated by categories
:param filter_domain: domain generated by filters
:param comodel_domain: domain of field values (if relational)
:param group_by: extra field to read on comodel, to group comodel
records
:param disable_counters: whether to count records by value
:return: a list of possible values, each being a dict with keys
'id' (value),
'name' (value label),
'count' (how many records with that value),
'group_id' (value of group),
'group_name' (label of group).
"""
field = self._fields[field_name]
supported_types = ['many2one', 'many2many', 'selection']
if field.type not in supported_types:
raise UserError(_(
'Only types %(supported_types)s are supported for '
'filter (found type %(field_type)s)'
) % ({
'supported_types': supported_types, 'field_type': field.type}))
Comodel = self.env.get(field.comodel_name)
model_domain = expression.AND([
kwargs.get('search_domain', []),
kwargs.get('category_domain', []),
kwargs.get('filter_domain', []),
[(field_name, '!=', False)],
])
comodel_domain = kwargs.get('comodel_domain', [])
disable_counters = kwargs.get('disable_counters', False)
group_by = kwargs.get('group_by', False)
if group_by:
# determine the labeling of values returned by the group_by field
group_by_field = Comodel._fields[group_by]
if group_by_field.type == 'many2one':
def group_id_name(value):
return value or (False, _("Not Set"))
elif group_by_field.type == 'selection':
desc = Comodel.fields_get([group_by])[group_by]
group_by_selection = dict(desc['selection'])
group_by_selection[False] = _("Not Set")
def group_id_name(value):
return value, group_by_selection[value]
else:
def group_id_name(value):
return (value, value) if value else (False, _("Not Set"))
# get filter_values
filter_values = []
if field.type == 'many2one':
counters = {}
if not disable_counters:
groups = self.read_group(
model_domain, [field_name], [field_name])
counters = {
group[field_name][0]: group[field_name + '_count']
for group in groups
}
# retrieve all possible values, and return them with their label
# and counter
field_names = ['display_name']
if group_by:
field_names.append(group_by)
records = Comodel.search_read(comodel_domain, field_names)
for record in records:
record_id = record['id']
values = {
'id': record_id,
'name': record['display_name'],
'count': counters.get(record_id, 0),
}
if group_by:
values['group_id'], values['group_name'] = group_id_name(
record[group_by])
filter_values.append(values)
elif field.type == 'many2many':
# retrieve all possible values, and return them with their label
# and counter
field_names = ['display_name']
if group_by:
field_names.append(group_by)
records = Comodel.search_read(comodel_domain, field_names)
for record in records:
record_id = record['id']
values = {
'id': record_id,
'name': record['display_name'],
'count': 0,
}
if not disable_counters:
count_domain = expression.AND([
model_domain, [(field_name, 'in', record_id)]])
values['count'] = self.search_count(count_domain)
if group_by:
values['group_id'], values['group_name'] = group_id_name(
record[group_by])
filter_values.append(values)
elif field.type == 'selection':
counters = {}
if not disable_counters:
groups = self.read_group(
model_domain, [field_name], [field_name])
counters = {
group[field_name]: group[field_name + '_count']
for group in groups
}
# retrieve all possible values, and return them with their label
# and counter
selection = self.fields_get([field_name])[field_name]['selection']
for value, label in selection:
filter_values.append({
'id': value,
'name': label,
'count': counters.get(value, 0),
})
return filter_values