We take advantage of SQL objects to discard the use of domain operators
'inselect and 'not inselect'. Indeed, as we now support SQL objects at
the right-hand side of a condition, one can replace conditions like
(lhs, 'inselect', (query, params)) by (lhs, 'in', SQL(query, *params)).
This commit also adds tests that ensure the correct behavior of the
adaptation of the implementation.
Part-of: odoo/odoo#138019
Update (some) query counters according to runbot state.
Also make some tests deterministic when involving company name.
Task-36879 (Mail: Support MultiCompany Aliases)
closesodoo/odoo#135288
Related: odoo/enterprise#47345
Signed-off-by: Thibault Delavallee (tde) <tde@openerp.com>
We explicitly discourage a compute method used for both stored and
non-stored fields, because it may be unexpectedly update the database.
Indeed, we don't expect the computation of a non-stored field to update
the database. Allowing this kind of compute method makes reasoning
about readonly code much more difficult, and would prevent readonly
transactions with such fields.
For instance, consider a compute method for both fields F (non-stored)
and G (stored), and assume that none of those fields are in cache.
Whenever F is accessed, the compute method is invoked, and both fields
are assigned, which potentially generates an SQL update for field G.
This is problematic if we require the current transaction to be
readonly.
Part-of: odoo/odoo#98565
The 'sale_ebay' module adds the `product_variant_ids` one2many field on
the `product.template` form view. The `product_variant_ids` view
contains `virtual_available` (depending on `uom_id`). When the user
changes the `uom_id` of the `product.template`, onchange is triggered,
it takes a snapshot of the previous data, and it will computes the
previous value of `virtual_available`. But the associated compute
method will fail with a traceback:
File "/data/build/odoo/addons/stock/models/product.py", line 199, in _compute_quantities_dict
res[product_id]['qty_available'] = float_round(qty_available, precision_rounding=rounding)
File "/data/build/odoo/odoo/tools/float_utils.py", line 54, in float_round
rounding_factor = _float_check_precision(precision_digits=precision_digits,
File "/data/build/odoo/odoo/tools/float_utils.py", line 29, in _float_check_precision
assert precision_rounding is None or precision_rounding > 0,\
AssertionError: precision_rounding must be positive, got 0.0
The `precision_rounding` is `0.0` because the `uom_id` of the product is
empty. It is is empty because we force the `uom_id` of the
`product.template` to be `False` in `initial_values` (before the
snapshot), and then the `uom_id` takes the value of its
`product.template` (`False`). But actually, the cache of the product
should be full with its previous values before doing the snapshot.
This was not the case because we only copy data from store fields
(see `fnames`). Then compute fields was computed after setting field
change to `False`.
opw-3334822
opw-3419392
X-original-commit: 5021e77ba52fac465a5d842ac04c0a3a22aea2dd
Part-of: odoo/odoo#135635
Co-authored-by: William Henrotin (whe) <whe@odoo.com>
before this commit:
translations updated by `update_field_translation` api cannot be detected by
t-cache and some cached data whose model overrides `write` with an extra
'clear_caches()'
Step to reproduce:
- Create a mega menu, select any template, `Odoo Menu` for the example
- Install another language on the website
- Go to the translated version of your website and enter translate mode
- Change "Camera" in the mega menu to something else
- Save
The change won't be replicated, looking like it did nothing.
From there, removing or adding `edit_translations=1` in the URL will
use different cache version of the page's views and you will see the
outdated value on one and the correct on the other one.
after this commit:
`update_field_translation` will call `write`
it does the following 4 important things
1. mark field as modified
2. execute logics in the override `write` method
3. update write_date if needed to support t-cache
opw-3305117
X-original-commit: 2beb466668e4eb80d7c3ca3947445fb1cb141cff
Part-of: odoo/odoo#135277
Purpose
=======
Allow to group records by their property values,
like we can do with normal fields.
Technical
=========
Because there's no foreign key, when we group by a relational property
we need to check the existence of the ids in the query (and same for
selection and tags, because we might have value of a deleted
option / tag in database).
Task-3032464
Part-of: odoo/odoo#103510
Purpose
=======
Currently, it is possible to use some bad characters in a property name
by writing directly on the parent (not via the child).
It's possible only with RPC call and it wasn't an issue because we
recheck the name anyway before using it in SQL query (but it should be
cleaned).
We also restrict the maximum length (there's nor reason to use
huge property names).
Task-3032464
Part-of: odoo/odoo#103510
Purpose
=======
Allow to use `default_properties.xxxxx` as a context key, to set the
default value on a property. We need that because when we group by
a property in a Kanban view, we can create new record in a given
column, and this process will set this context key.
Task-3032464
Part-of: odoo/odoo#103510
This commit, adds a new python method (`web_save`) to save a record, and
optionally read-it again in one rpc call. This optimizes the current
behavior that is to save a record in one rpc, and read-it in a second
rpc.
web_save, will receive the list of IDs of the records to save (if this
list is empty it will create the records, if not, it will write on the
existing records), the list of changed fields, and the unity
specification as optional argument to read the created/modified records
(if the specification is not set, the function will return a list of IDs
of the created/modified records).
closesodoo/odoo#133021
Task-id: 3453184
Related: odoo/enterprise#46559
Signed-off-by: Aaron Bohy (aab) <aab@odoo.com>
When _create() is invoked, it inserts rows into the database, and sets
the cache of the corresponding records to the values that are inserted.
If a field is not passed to _create(), we assume that its database value
will be NULL (or falsy, at least) and put None in cache, in order to
avoid fetching that value. However, this only makes sense for stored
fields.
Part-of: odoo/odoo#133021
When we changed the display of the form in v16, we forgot to adapt the
python framework default form view layout (used when the form view of
the model isn't defined). This provided a weird layout in these case on
Odoo 16.+.
To fix this bad behavior, we adapt the algorithm used to build the
default view form.
Steps to reproduce:
Create a model without a form view and edit it (like we do when you
follow the rd training).
closesodoo/odoo#133837
X-original-commit: 93e95274939abce5f3facde6c8cd29b922d15251
Signed-off-by: Pierre Paridans (app) <app@odoo.com>
Signed-off-by: Romain Estievenart (res) <res@odoo.com>
Purpose
=======
Add the "separator" properties type, to be able to group properties.
Specification
=============
The separator creates a group until the next separator, we can fold and
unfold the properties in a group.
Technical
=========
The separator is only stored on the definition record, so it does not
take more space than needed.
The "fold" information is stored in the local storage, and is therefore
per user, but shared for all records of the same parent.
We use a properties type for it, to be able to use the exact same code
as other properties (so we can easily move the separator, etc). So
the order of the properties in the definition, and the position of the
separators will create the groups.
Task-3188915
Part-of: odoo/odoo#113974
Bug
===
Since 7d2baaa0c7 , we read the field
with `web_read`. We don't load the display name when calling read,
but instead we load them after.
But, since this change, the display names of the relational
properties are not loaded.
To fix that, we always load the display name of the relational
properties because it's a purely frontend field.
Task-3460126
Part-of: odoo/odoo#131394
These changes are made as a result of simplifying attrs and 'states' in
views. However, they should have remained in a separate commit. When
applying the script making the xml changes (used later for the migration
script), the script checked the definition of the python fields in order
to convert the information into a python expression. Therefore, this
commit is not applied when the script is applied to xml changes.
During this attribute deletion pre-existing errors were found. Part of
the code was using the boolean values of 'states' and another part of
the code was not. The behavior could therefore be different (in cases
where readonly on the field had the same value as the ballan in
'states').
Following the deletion of 'states' and without the application of the
view migration, the js tests (tower) were no longer functional. Tests
using the Form view suffered the same effect. There are few tests that
had to be adapted, including two tests in business accounting (updated
by the accounting team). A test for column_invisible did not work. Test
checking if the test system triggers an error if we try to write on an
invisible field. It turns out that Form was testing on the value of
invisible but not taking into account if the column was invisible. The
test system fix is applied separately because there were a lot of tests
that were incorrect.
Part-of: odoo/odoo#104741
We prevent to merge partner linked to more than one user (by raising an
exception) to avoid having multiple user pointing to the same partner as a
consequence of a merge.
We want to avoid this situation because it generates weird behaviors. As
res.user inherits from res.partner, having multiple user pointing to the same
partner makes the fields of that partner shared with all those users. This was
decided following the tentative to improve partner merge in website_slides
(odoo/odoo#114840), where we also noticed strange behavior like completing a
lesson with one user were adding karma to all the users linked to a same
partner.
We have also adapted WebsiteVisitorTests tests in website that were failing
because they were merging partners with more than one user: simply by merging
partners with only one user.
Task-3167160
closesodoo/odoo#125322
Signed-off-by: Stéphane Debauche (std) <std@odoo.com>
This completes 96a98f4f4c in the case
where a many2one field has a new record as value, even when that
many2one field is not a delegate field.
Part-of: odoo/odoo#114024
The tests did not cover the cases where:
- the main record has no company;
- the linked record has no company.
closesodoo/odoo#129044
Signed-off-by: Raphael Collet <rco@odoo.com>
This commit partly reverts 59afbf6686 and
provides an alternative solution. In this solution, records.web_read()
returns a list of dicts where the key 'id' corresponds to each record.id
in records, which enables to reliably relate each record to its values.
This also fixes the implementation of web_read() on new records where
the fields contain a reference field with some context.
closesodoo/odoo#128878
Signed-off-by: Raphael Collet <rco@odoo.com>
One of the main issue with ormcache is that the invalidation clears
everything, meaning that some value, slow to compute but with a long
lifetime, can be removed from the cache because an easy to invalidate
value is cleared, like after writting or creating a product has an
example.
Most example in the code will try to invalidate the cache of the models
doing something like `env['ir.qweb'].clear_caches()` but it is
finally equivalent to `env.registry.clear_cache()`, and cross worker.
The idea is to have multiple cache, maybe with specific sizes for a
specific purpose.
Having one per model is maybe a bad idea because it will be difficult
to size the LRU correcly, and it is too dynamic. Checking invalidation
may be expensive.
The proposed solution is closed allow a limited number of named caches,
using onse sequence per cache. This is actually close to the
cache_longterm.
We want to discourage using a specific cache for one use case in
the buisness code. Adding a cache shouldn't be something easy, doable
in stable.
Note that we could also change the invalisation mecanism using an
insert only table. We an check the sequence of this table, but also
fetch all invalidation messages.
Another possible improvement, especially if we have more than x cache is
to have a global sequence, checking signaling would mean to check the
main sequence, and only the other ones if the main one changed.
Note that this poc is inspired from the long term cache but not all
use case where applie yet.
Part-of: odoo/odoo#119813
Install POS, go in the settings, add a new cash routing method.
Traceback, the field as no comodel.
The ir.property `search_multi` method wasn't compatible with the
'any'/'not any' operator.
opw-3375624
closesodoo/odoo#127750
X-original-commit: 7dd14eb0b0edb57150288361e72e407e05e67a73
Signed-off-by: Raphael Collet <rco@odoo.com>
Signed-off-by: Rémy Voet (ryv) <ryv@odoo.com>
Co-authored-by: Julien00859 <juc@odoo.com>
Since 5a998694a6,
`[('<many2one>', 'not any', [<domain>])]` matches rows where the
<many2one> is set and the corresponding many2one row matches the
<domain>. This is incorrect. 'not any' should be the inverse of
the 'any' operator and domains such as
`['!', ('partner_id.name', '=', 'System')]` are incorrectly converted
to "Return every record with a partner name != 'System'"
when it should be "Return every record with a partner name != 'System'
OR without partner at all".
Fix semantic and add tests to avoid any future regressions.
X-original-commit: c8c1ef45f24482e380529daf2de55b9091338a83
Part-of: odoo/odoo#127750
- Improve performances, as the ir.rule restricting private partners
visibility is also applied on res.users by inheritance, on each
prefetch.
- Solve the issue of partners set as followers on records (eg: application
form) and then made private, making them impossible to contact via the
chatter.
- Solve the multiple access issues when trying to access the bank
account, or the private address for non HR people like the accountants
forcing the usage of sudo in the business code.
TaskID: 3101400
Rationale
=========
Since v8, the `display_name` field is present on all models. By default,
`display_name` uses `name_get` which has pretty much the same purpose
(return record name used by the web client). Gradually, many (backend)
developers (and the ORM: https://github.com/odoo/odoo/commit/6da1c3ac4c036eac289597602976538e243cb939)
started using `display_name` (more convenient than
`record.name_get()[0][1]`) but it still had the `name_get` override.
It becomes more complex than necessary and poeple start to misunderstand
the two (and sometimes override both, leading to inconstiencies between
`display_name`/`name_get`).
To simplify the ORM and the API, we decided to keep only one of them,
the `display_name` field:
- It is much more convenient from a backend point of view
(`record.name_get()[0][1]` vs `record.display_name`)
- It is cached during the same transaction (and invalidated if
its dependencies change)
- It can be overridden like any other compute field (override
`_compute_display_name` with any extra dependencies)
- `name_get` is replaced by `read(['display_name'])`
(API perceptive), which can actually be more efficient
(if `display_name`'s depends are correct, the ORM will only fetch the
fields it needs instead of every prefetchable field)
Changes
=======
- Deprecates `name_get` for the v17 and based the method on
`display_name` (the opposite of before)
- Converts all usage of `name_get`
- Overrides of `name_get` are now overrides of `_compute_display_name`
- For `res.partner`, rename the field store `display_name` into
`complete_name` because `display_name` context-dependent and it makes
no sense to have a compute store that is context-dependent.
- Previously, it was possible to return multiple names for the same
record with `name_get`, but it was tricky and most of the usage of
this `name_get` didn't take this into account. The only example of
this is the `name_get` of `product.product`
(now use `", ".join(<names>)`).
Part-of: odoo/odoo#122085
The LINK command must include the corresponding record data in the
web_read() format instead of the diff() format. The real difference
shows up in x2many fields: web_read() returns them as lists of dicts (or
list of ids), while diff() returns them as lists of commands.
Part-of: odoo/odoo#124612
onchange2() reads the origin record from database, and copies its field
values in cache on the corresponding new records. The copied x2many
fields must use NewId instead of their real id.
Part-of: odoo/odoo#124612
When doing an onchange on some existing record, the x2many commands must
be applied on the current x2many value of the record. When doing an
onchange on a new record, the x2many commands must be applied on empty
x2many values.
Part-of: odoo/odoo#124612
Bug
===
If we create a selection properties with some options, change the type
to char, and then again to selection, the options are restored.
But, if we just change the type to char, the options of the old
property are stored in the database, and we don't want that.
Task-3346103
closesodoo/odoo#124129
X-original-commit: 35c99a9110cec3317f36d4ddffae5196b58939e5
Signed-off-by: Thibault Delavallee (tde) <tde@openerp.com>
Bug
===
The web client expects the selection key to be at least an empty array.
(never false), like for a normal selection field, and so if the selection
has been created without an option, it crashes in the list view.
For consistency, we also set an array when there are no tags.
Task-3340671
closesodoo/odoo#123844
X-original-commit: 2e38397c2cfe0e4ed501d4ce7b98f0c47fe6c344
Signed-off-by: Rémy Voet <ryv@odoo.com>
Signed-off-by: Stéphane Debauche (std) <std@odoo.com>
> The bug is not present in 16.2+, but we keep the test
To Reproduce:
=============
- on a contact add a one2many field using studio
- in related field choose a field that is not stored and doesn't have a search function implemented
- close studio and notice all the lines of the selected field are listed on the contact even if are not linked to it
Problem:
========
- when searching a field that is not stored and doesn't have a search function, all the lines are returned
Solution:
=========
in this usecase filter the returned lines and only keep the ones linked to the record
opw-3265982
closesodoo/odoo#123563
X-original-commit: 7b29e9dff1d5ef97c5fb2a2ae0709bb21fd3fa3a
Signed-off-by: Rémy Voet <ryv@odoo.com>
Signed-off-by: abla001 <abla@odoo.com>
Issue:
Html fields cannot add company_dependent
Cause:
Neven have html type for company property
Solution:
Add html type inside ir.property
closesodoo/odoo#121243
Signed-off-by: Rémy Voet <ryv@odoo.com>
1.
Infinite loop may happen on using `parent_of`\`child_of` when there is a
recursion in the tree (e.g. a record is marked as a parent of itself). Fix it by
excluding seen records from the next iteration.
2.
Another problem with `child_of` is `parent_id` that references to another model.
For example, the `parent_id` may come from inherited model. It's the case with
`res.users` and `res.partner` models. It may lead to a random search results.
Avoid that by raising exception in case of wrong usage of the `child_of`
operator.
STEPS:
In demo data, there is a partner called "Wood Corner" that is `res.partner(9,)`
that has 3 sub-contacts. If we give Portal access to two of them, we end up with
a database, where we have a `res.users(9,)` record that has a partner, which has a
`parent_id` to "Wood corner". So this way, the user id is the same as the user's
partner's parent contact id.
After that open a shell and type:
```
env['res.partner'].search([["user_ids", "child_of", 9]])
```
BEFORE: infinite loop (without change n.1) or random search results (when change
n.1 is applied)
AFTER: ValueError exception
---
opw-2729740
closesodoo/odoo#123353
X-original-commit: 2e1adc0c3e33fcf7989d27bb4d1c2e3c019faf2b
Signed-off-by: Ivan Elizaryev (iel) <iel@odoo.com>
Signed-off-by: Raphael Collet <rco@odoo.com>
In specific situation, unlink can lead to raise a `RecursionError`:
- The model `A` has a many2one `b_id` field toward a model `B`.
This field is set with `ondelete='cascade'`.
- The model `A` has one **store** related field **no-sudo** named
`a_related` (`related='b_id.b_other_field`).
- With `ir.rule` on model `A` with a domain containing `a_related`
You have one record B `b_1` with 20 records A linked to it
(`a_1, ..., a_20`). When you try to unlink `b_1`:
Stack:
File "...", line 543, in ...
b_1.unlink()
File "/home/odoo/Documents/dev/odoo/odoo/models.py", line 3594, in unlink
self.env.flush_all()
=> At this point, `a_1, ..., a_20` have already been deleted from the
database because of the 'cascade' deletion. But the ORM doesn't have
any information about this, and `a_related` (for `a_1, ..., a_20`) are
flagged to be recomputed (because it depends on `b_id.b_other_field`)
File "/home/odoo/Documents/dev/odoo/odoo/api.py", line 732, in flush_all
self._recompute_all()
File "/home/odoo/Documents/dev/odoo/odoo/api.py", line 728, in _recompute_all
self[field.model_name]._recompute_field(field)
File "/home/odoo/Documents/dev/odoo/odoo/models.py", line 6165, in _recompute_field
field.recompute(records)
File "/home/odoo/Documents/dev/odoo/odoo/fields.py", line 1348, in recompute
self.compute_value(record)
=> `self.compute_value(recs)` raised a `MissingError` before recalling
`compute_value` with only the first `record` (but others are still in
the prefetch)
File "/home/odoo/Documents/dev/odoo/odoo/fields.py", line 1368, in compute_value
records._compute_field_value(self)
=> `a_related` of `record` is removed from to_compute, but only the
first record, not the rest of the records present in the prefetch set.
File "/home/odoo/Documents/dev/odoo/odoo/models.py", line 4194, in _compute_field_value
fields.determine(field.compute, self)
File "/home/odoo/Documents/dev/odoo/odoo/fields.py", line 100, in determine
return needle(records, *args)
File "/home/odoo/Documents/dev/odoo/odoo/fields.py", line 689, in _compute_related
values = [first(value[name]) for value in values]
File "/home/odoo/Documents/dev/odoo/odoo/fields.py", line 689, in <listcomp>
values = [first(value[name]) for value in values]
File "/home/odoo/Documents/dev/odoo/odoo/models.py", line 5860, in __getitem__
return self._fields[key].__get__(self, type(self))
File "/home/odoo/Documents/dev/odoo/odoo/fields.py", line 2772, in __get__
return super().__get__(records, owner)
File "/home/odoo/Documents/dev/odoo/odoo/fields.py", line 1186, in __get__
recs._fetch_field(self)
File "/home/odoo/Documents/dev/odoo/odoo/models.py", line 3162, in _fetch_field
self._read(fnames)
=> `_read` tries to read the first record + others from the prefetch set
File "/home/odoo/Documents/dev/odoo/odoo/models.py", line 3215, in _read
self.with_context(active_test=False)._flush_search([], order='id')
File "/home/odoo/Documents/dev/odoo/odoo/models.py", line 4607, in _flush_search
self.env[model_name].flush_model(field_names)
File "/home/odoo/Documents/dev/odoo/odoo/models.py", line 5560, in flush_model
self._recompute_model(fnames)
File "/home/odoo/Documents/dev/odoo/odoo/models.py", line 6134, in _recompute_model
self._recompute_field(field)
=> This is where the recursion starts, record compute will move forward
one by one. But sadly, the stack grows very fast, and with only a few
(already deleted) records to recompute, the issue will be generated.
File "/home/odoo/Documents/dev/odoo/odoo/models.py", line 6165, in _recompute_field
field.recompute(records)
File "/home/odoo/Documents/dev/odoo/odoo/fields.py", line 1348, in recompute
self.compute_value(record)
File "/home/odoo/Documents/dev/odoo/odoo/fields.py", line 1368, in compute_value
records._compute_field_value(self)
File "/home/odoo/Documents/dev/odoo/odoo/models.py", line 4194, in _compute_field_value
fields.determine(field.compute, self)
File "/home/odoo/Documents/dev/odoo/odoo/fields.py", line 100, in determine
return needle(records, *args)
File "/home/odoo/Documents/dev/odoo/odoo/fields.py", line 689, in _compute_related
values = [first(value[name]) for value in values]
File "/home/odoo/Documents/dev/odoo/odoo/fields.py", line 689, in <listcomp>
values = [first(value[name]) for value in values]
File "/home/odoo/Documents/dev/odoo/odoo/models.py", line 5860, in __getitem__
return self._fields[key].__get__(self, type(self))
File "/home/odoo/Documents/dev/odoo/odoo/fields.py", line 2772, in __get__
return super().__get__(records, owner)
File "/home/odoo/Documents/dev/odoo/odoo/fields.py", line 1186, in __get__
recs._fetch_field(self)
File "/home/odoo/Documents/dev/odoo/odoo/models.py", line 3162, in _fetch_field
self._read(fnames)
File "/home/odoo/Documents/dev/odoo/odoo/models.py", line 3215, in _read
self.with_context(active_test=False)._flush_search([], order='id')
File "/home/odoo/Documents/dev/odoo/odoo/models.py", line 4607, in _flush_search
self.env[model_name].flush_model(field_names)
File "/home/odoo/Documents/dev/odoo/odoo/models.py", line 5560, in flush_model
self._recompute_model(fnames)
File "/home/odoo/Documents/dev/odoo/odoo/models.py", line 6134, in _recompute_model
self._recompute_field(field)
How to fix it:
Move the logic of the MissingError of `_recompute_field` inside the
`recompute` directly.
X-original-commit: c2aac02ac4f8c5cc4a9324134535393bd97338ce
Part-of: odoo/odoo#122147
The issue with properties fields is that the value in the record
snapshot is not correct. This is caused by convert_to_record()
combining the values with the definition, and in the case of onchange(),
the values don't match the definition, which causes the method to return
the empty list [].
We fix the root cause by changing convert_to_record() to return the dict
itself. The combination of the values with the definition is now only
done in convert_to_read(). Method convert_to_onchange() has only one
hack to retrieve the current definition record from the record snapshot,
as because of cache invalidation, its value is no longer available.
closesodoo/odoo#120457
Signed-off-by: Raphael Collet <rco@odoo.com>
Purpose
=======
When we create a new record, we can change the definition on the definition
record. If the parent had no definition, in `_add_default_values`,
we just return the value. But in some weird cases, if we have
invalid properties name in the value, the will be written in database.
Now, in that particular case, we propagate the value only
if we try to change the definition.
Part-of: odoo/odoo#120457
The purpose of onchange2() is to adress two shortcomings of onchange():
- reduce the payload of the RPC call by minimizing the diff
- use the "unity" format for returning the data
Because of the dependency of onchange2() on web_read(), the new method
has been introduced in module web.
closesodoo/odoo#119510
Signed-off-by: Raphael Collet <rco@odoo.com>
Co-authored-by: Julien Castiaux <juc@odoo.com>
Co-authored-by: VincentSchippefilt <VincentSchippefilt@users.noreply.github.com>
Rationale
---------
Given the following domain:
['|',
('company_id.name', 'like', 'BE'),
('company_id.city', 'like', 'Brussels')]
The ORM would generate the following SQL query:
SELECT "res_partner"."id"
FROM "res_partner"
WHERE "res_partner"."company_id" IN (
SELECT "res_company"."id"
FROM "res_company"
WHERE "res_company"."name" like 'BE'
) OR "res_partner"."company_id" IN (
SELECT "res_company"."id"
FROM "res_company"
WHERE "res_company"."email" like 'help@odoo.com'
);
Which is sub-optiomal as the WHERE clause of the two subqueries could be
regrouped inside of a single query like so:
SELECT "res_partner"."id"
FROM "res_partner"
WHERE "res_partner"."company_id" IN (
SELECT "res_company"."id"
FROM "res_company" WHERE (
"res_company"."name" like 'BE'
OR "res_company"."email" like 'help@odoo.com'
)
);
Postgres-wise, it is faster to execute the latter query than the former
one. This commit is about optimizing the ORM so that it generates
queries where the WHERE clause of compatible subqueries are grouped
together.
Technical solution
------------------
The solution explored by this work is to replace the regular relational
field accesses (over a dotted path) by a new `any` operator that look as
follow:
(relational_field, 'any', domain)
For instance, the above `('company_id.name', 'like', 'BE')` leaf becomes
`('company_id', 'any', [('name', 'like', 'BE')]` using `any`.
Having a domain as right-hand-side allow for the combination of the
domains of same compatible relations:
[('company_id', 'any', ['|',
('name', 'like', 'BE'),
('city', 'like', 'Brussels')])
Having combined domains allows for generating better SQL queries with
minimal changes to the expression parsing algorithm, which can only
translate a single leaf at a time to SQL. With the new `any` operator,
it is still a single leaf but the right-hand-side contains the merged
domain, thus the combined SQL WHERE clause is immediately generated.
task-id-3234671
Part-of: odoo/odoo#118067
Co-authored-by: Raphaël Collet <rco@odoo.com>
This commit hads a few tests that report the existing behavior (before
merging subqueries WHERE clauses).
task-id-3234671
Part-of: odoo/odoo#118067
Co-authored-by: Raphaël Collet <rco@odoo.com>
A mistake introduced in 234db70d86, the
`groupby` of `_read_group` should be list/tuple of `str`, not a `str`.
closesodoo/odoo#119400
Signed-off-by: Raphael Collet <rco@odoo.com>
Introduce an optimised way of reading a graph of data from the webclient.
Before this commit:
When reading data from the webclient it could at most read multiple ids of the same model in one RPC.
This mean that when reading x2many or specific information on many2one, that could only be done after the initial read (when the client knows the ids of the comodels) and model by model.
After this commit:
Introduce methods web_read and web_search_read_unity. Both method receive a specificiation for the fields instead of a list of fields. The specification can request fields from the model, as well as follow relations and request fields for each relations, recursively.
Example of web_read specification for account_move
```python
{'name': {}},
{'date': {}},
{'journal_id': {'fields': {'display_name':{}}}
},
...
{'invoice_line_ids' :
{
'fields': {
'journal_id' : {'fields': {'display_name:{}}},
'move_name' : {},
...
'tax_ids' : {
fields: {
'display_name':{},
...
}
}
}
}
}
```
Result for this example with 2 invoice lines
```python
{
'id': 1234,
'name' : 'invoice name ABC',
'journal_id: {
'id': 999,
'display_name': 'Customer Invoices'
},
...
'invoice_line_ids': [
{
'id': 666,
'journal_id': {
'id': 999,
'display_name': 'Customer Invoices'
},
'move_name': 'a move name',
'tax_ids': [
{
'id': 333,
'display_name: "15% tax",
...
}
]
},
{
'id': 667,
'journal_id': {
'id': 999,
'display_name': 'Customer Invoices'
},
'move_name': 'another move name',
'tax_ids': [
{
'id': 334,
'display_name: "21% customer tax",
...
}
]
}
]
}
```
closesodoo/odoo#119034
Signed-off-by: Vincent Schippefilt (vsc) <vsc@odoo.com>