Non-attachment binary fields need to be flushed before reading their
size, since the latter relies on the database's binary size function.
Part-of: odoo/odoo#160708
After writing or creating on a related Image field, its cache contains
the full-size image instead of the resized one (according to its
attributes max_width and max_height). Fix the cache with the resized
image at the end of the inverse method.
Part-of: odoo/odoo#160708
When invoking create() or write() with a binary field, the cache of the
field was incorrect if bin_size=True was in context. Force context with
bin_size=False when putting a binary value in cache. It is particularly
important to have coherent values in the cache for `web_save`.
Also, because an environment with bin_size=False won't return the same
context cache key as one with bin_size=None, it leads to have a cache
inconstistency when we write with bin_size=False. Change Environment
method cache_key() to return the same cache key when bin_size is absent,
bin_size=None or bin_size=False.
Tests on binary fields have been updated to not rely on flush and
invalidate. We also created specific tests for write() on binary
fields.
Part-of: odoo/odoo#160708
Changing the environment in method create() to force bin_size=False
looks harmless, but it actually breaks many tests, in particular in
module account. The reason is that company_dependent fields are read at
the wrong place in the cache. And this is because `env._cache_key` can
be polluted with old data.
Make sure that `_cache_key` is cleared when resetting all the lazy
properties on the environment. Only the change in res_user.py makes it
work, but let's not tempt the devil.
Side note: I hate caches.
Part-of: odoo/odoo#160708
_read_group doesn't raise an error when we have an aggregate
specification like `order_id.create_date:min`, instead it silently
ignores the `.create_date` part.
We only add a warning in the stable version to avoid breaking any
change.
closesodoo/odoo#158777
Signed-off-by: Rémy Voet (ryv) <ryv@odoo.com>
BaseModel's sorted() has two problems:
- It breaks the prefetch of self for no reason
- When it is called without an argument, it filters out new records
because the search() used in sorted() doesn't return new records.
Keep the same prefetch as self to fix the first problem.
We partially fix/support the second issue, we just avoid filtering out
new records (but we don't actually sort them)
closesodoo/odoo#157145
X-original-commit: 0551c3b7e8e1469dabdb19d6420c544a1654fec5
Signed-off-by: Rémy Voet (ryv) <ryv@odoo.com>
__getitem__() of BaseModel, reset the prefetch set of the recordset.
Fix _compute_activity_date_deadline, to batched the reading of
activity's deadline.
closesodoo/odoo#154860
X-original-commit: ef7226aaa2f0ad135133fe27bdf1414b7ebf2a92
Signed-off-by: Rémy Voet (ryv) <ryv@odoo.com>
Use read_group with groupby=['id'] raise a Exception:
```
File "/home/odoo/Documents/dev/odoo/odoo/models.py", line 2386, in _read_group_format_result
m2x_records = self.env[field.comodel_name].browse(ids).union()
File "/home/odoo/Documents/dev/odoo/odoo/api.py", line 521, in __getitem__
return self.registry[model_name](self, (), ())
File "/home/odoo/Documents/dev/odoo/odoo/modules/registry.py", line 190, in __getitem__
return self.models[model_name]
KeyError: None
```
Even if it doesn't make lot of sense to do that (mostly equivalent to
search), it is preferable to manage the case correctly.
closesodoo/odoo#154799
X-original-commit: 75a259365989d469972c0616dba22a56bf218bb5
Signed-off-by: Rémy Voet (ryv) <ryv@odoo.com>
Current behaviour:
When planning more than 88 work orders,
there is a recursion error.
Steps to reproduce:
1. Go to Manufacturing
2. Operations > Manufacturing Orders
3. Create a manufacturing order
4. Add 90 work orders
5. Click on Confirm
6. Click on Plan
7. Recursion error
Cause of the issue:
Maximum depth of the Python interpreter stack
The recursion limit being set at 1000
by default (with getrecursionlimit)
Fix:
Upped the limit to ~320 work orders
opw-3651494
closesodoo/odoo#152198
X-original-commit: 25081646ef0b679356ad46f62fe737fb44269baf
Signed-off-by: William Henrotin (whe) <whe@odoo.com>
Co-authored-by: Rémy Voet <ryv@odoo.com>
In e0297bdac4, the creation of a new
record always patches the inverse fields of relational fields in order
to make the cache of those inverse fields consistent.
For instance, when creating a new record like
user = model.new({'group_ids': [Command.link(group.id)]})
The inverse of field 'group_ids' on the new record having 'group' as
origin is patched so that its value includes record. A side effect of
this mechanism is that it fetches group.user_ids in order to patch the
value of new_group.user_ids, where 'new_group' is the new record having
'group' as origin.
The side effect described above is problematic when that inverse field
has huge cardinality, like hundreds of thousands of records, and this
performance overhead is unacceptable when the inverse field is actually
not used at all.
We address this performance issue by patching the value of x2many fields
only when they are used. If the value of the field is not in cache yet,
the patch is applied once a value is put in cache. If the field is not
used, the patch is simply never applied.
Part-of: odoo/odoo#149624
In e0297bd, we fixed the inverse field values of the new record during
the onchange. But we actually filter out inactive records by doing
record[self.name] in _update(). And since the XtoMany field cache
values should always contain inactive records, we need to add
with_context(active_test=False) on records.
Also remove the useless 'if value', value is always truly because it
is always a record.
Note that this solves a performance issue in our production, because
in order to filter out inactive records, we need to fetch the active
field next to every prefechable field.
closesodoo/odoo#148675
Signed-off-by: Raphael Collet <rco@odoo.com>
When we add a new record N to an one2many tree view from an existing
record X form, during the onchange() on the one2many comodel, the cache
of the N.one2many contains only the new record X (the siblings aren't in
it). Because of this, the result of compute methods may be incorrect
and the form won't be updated accordingly. See
https://github.com/odoo/enterprise/pull/52957 for a concrete example.
Technically, this is due to _update_cache() forcing the inverse field
value to the single value of the new record
("not cache.contains(inv_rec, invf)" is True), instead of also
considering the original values (which is properly done by
Field._update()).
closesodoo/odoo#146778
Related: odoo/enterprise#53298
Signed-off-by: Raphael Collet <rco@odoo.com>
For a compute store field with new records (e.g. during an 'onchange'),
the compute method can be called multiple times on the same records
without changing the dependencies. Moreover, it can lead to have N² / 2
complexity for a trivial compute on N records.
With partial (where we don't always change the value) compute method:
```
@api.depends('reward')
def _compute_has_been_rewarded(self):
for rec in self:
if rec.reward:
rec.has_been_rewarded = 'Yes'
```
If every `reward` of `self` (N records) is `False`, when the ORM needs
to recompute `has_been_rewarded` of `self`: the compute will be batched,
but only the first record in the batch will be set (to `False`) each
time (due to the current fallback - "fallback to null value if compute
gives nothing"). This means that we will call the compute method N
times, and the compute itself will loop on an average of N/2 records
(the prefetch set decreasing at each step).
Fix this quadratic behavior by setting the cache to `False` for every
record not set during the compute method (instead of just the current
record).
closesodoo/odoo#142162
X-original-commit: 1604ee983aadc0cbee0cd50cbea2b09572905b04
Signed-off-by: Raphael Collet <rco@odoo.com>
Signed-off-by: Rémy Voet (ryv) <ryv@odoo.com>
As of https://github.com/odoo/odoo/pull/114024, `display_name` is
implicit in every form view (see the use of addFieldDependencies in the
Form controller). Therefore, when you create a new record for this
model, it calls the first `onchange`, which will compute display_name
for a new record (id without origin). Some `_compute_display_name` don't
handle new records correctly and raise a traceback. These models are
sometimes directly accessible:
- Accounting > Account Group > New => Traceback
- Contact > Contact Tags > New => Traceback
Other models are inaccessible by default (no view to access or create a
new record), but if someone creates a view for them with studio (or
modifies an existing one to allow creation):
- `crm.iap.lead.role`
- `crm.iap.lead.seniority`
- `chatbot.script.answer`
- `payment.token`
Change the code of `_compute_display_name` on these models to be more
defensive and avoid (potential) tracebacks. Similarly, change the
`convert_to_display_name` of `fields.Datetime` to take into account
`None` value.
closesodoo/odoo#139592
Related: odoo/enterprise#49721
Signed-off-by: Rémy Voet (ryv) <ryv@odoo.com>
Since #104741, `Field.states` is no longer supported, remove the
documentation associated. We cannot remove the attribute
from Field because it is used in the migration script.
closesodoo/odoo#140694
Signed-off-by: Raphael Collet <rco@odoo.com>
Since https://github.com/odoo/odoo/pull/137098, `display_name`
can be false (from the default behavior in BaseModel). It isn't
handle correctly in domain_selector Component, which trigger a
traceback trying to `split` false.
closesodoo/odoo#139450
X-original-commit: 06f3cbb95b2e4cfb271039a19660991c743c2ba8
Signed-off-by: Mathieu Duckerts-Antoine (dam) <dam@odoo.com>
Signed-off-by: Rémy Voet (ryv) <ryv@odoo.com>
Co-authored-by: Polymorphe57 <dam@odoo.com>
Since https://github.com/odoo/odoo/pull/137098, we add a fallback for
false display_name in the form view, but we didn't add it for the list
view which doesn't instantiate the Many2one component. It uses
`formatMany2one` instead. Fix it to be consistent with the Many2one
closesodoo/odoo#139215
Signed-off-by: Aaron Bohy (aab) <aab@odoo.com>
Signed-off-by: Rémy Voet (ryv) <ryv@odoo.com>
Since https://github.com/odoo/odoo/pull/137098, in
`Accounting > Bank > Tree View`, you get a traceback if one of the bank
statements in the view has a false as `display_name` (name is only
required in the view not in the model). The traceback comes from the
`extraLines` accessor in the Many2one component. The fix is the same
as in https://github.com/odoo/odoo/pull/138061 (FW of the first PR).
Part-of: odoo/odoo#139215
The previous commit reverts the ORM fallback of `display_name`. Then the
`display_name` can legitimately be `False` again. But the `Many2one` and
`Many2XAutocomplete` components don't handle this case (and generate a
traceback see task-3424154).
This commit adds the same fallback ('Unnamed record') for these two
problematic components; one when the name_search returns `False` as
`display_name` (autocomplete for Many2Xfields) and another when we read
the many2one `display_name`. This name is not user friendly, but for
the business model it shouldn't happen anyway. It is also better than
the empty string which is confusing with no value. Note that there is
already a fallback for `display_name` that is used in the broadcrumb
(https://github.com/odoo/odoo/blob/50b2a24f22cb470bcc1a9befe677cf794211d7b2/addons/web/static/src/views/form/form_controller.js#L282).
Part-of: odoo/odoo#138061
This reverts commit 4573ca0c83eb63785016f4389a5157efb21fa9a4.
Because now, `display_name` is implicitly on every form (last breadcrumb
item). Then it will be queried by `onchange` calls. When we create a
new record, `_rec_name` can be `False` and the display_name will be a
technical one: '<model_name>,<NewId0x...>' which is uglier than the
previous situation showing 'New'.
Part-of: odoo/odoo#138061
Since https://github.com/odoo/odoo/pull/110737, it is better to use
`_read_group` instead of `read_group` in the backend. In fact, the
public method is less efficient (it computes display_name of relational
groupby, extra order, ...) and more verbose.
This commit replaces these new uses of `read_group` with `_read_group`.
closesodoo/odoo#136381
Related: odoo/enterprise#47826
Signed-off-by: Raphael Collet <rco@odoo.com>
The 'like'/'ilike' operators automatically add the wildcard character
(`%`) at the beginning and at the end of the value. This commit fixes
the incorrect usage.
closesodoo/odoo#136007
Related: odoo/enterprise#47886
Signed-off-by: Raphael Collet <rco@odoo.com>
The `=like`/`like` domain operators are accent-insensitive, but still
case-sensitive. This is not really coherent, since `unaccent` is a best
effort to find records from the client, and it is the same idea behind
being case-insensitive. Also, `=like`/`like` cannot be used from the web
client, and we use them in domain to search from the Python side.
Moreover, adding `unaccent` to `=like` can be very inefficient when
searching for a prefix ('prefix%'). In fact, PostgreSQL can use btree
index to find prefix matches, but because we create dy default btree
index without unaccent (when we put
`index=True/'btree'/'btree_not_null'` on the field), PostgreSQL cannot
use this index.
Part-of: odoo/odoo#136007
`_set_dates` didn't work properly with multiple records.
In fact, it only used the `date_start` and `date_finished` of the
first record. The assumption was that `date_start`/`date_finished`
is always the same for each record in self, because it usually
comes from a write call (writing the same values to a batch of
records). But inverse methods are also called by the create
method, and then the previous assumption isn't true anymore.
This bug leads to several inconsistencies between the `date_start`
and the `leave_id`. The `test_replan_mo_without_bom` was fixed in
the previous version, but with the new onchange, it breaks again
because of these inconsistencies.
closesodoo/odoo#135635
Signed-off-by: Raphael Collet <rco@odoo.com>
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>
Since https://github.com/odoo/odoo/pull/122085, on the sale
order form (with website_sale installed), changing the "Customer"
changes the "Invoice Address" & "Delivery Address". But the latter
are displayed with the partner name, but also with the full
partner address. This last information makes the form uglier than
before and are useless.
The old `display_name` of `res.partner` was stored in the DB and
did not depend on the context. Also, onchange calls add the
context of the source field (the one being changed). In this case,
`'show_address': 1` is added, and then `display_name` of
`partner_invoice_id`/`partner_shipping_id` is also read with this
context.
We cannot easily fix this in saas16-4 because it was not possible
to change the read context of a particular field. With the new
specification of onchange, we can override the read context on a
particular field.
closesodoo/odoo#134304
Signed-off-by: Raphael Collet <rco@odoo.com>
Since 3c62ca1eb9, if the `_rec_name` value
is False, `name_search` and `name_create` will return a tuple of
(<id>, False). This is an invalid response for the web client,
which triggers a JS traceback.
Instead of using the old behavior (returning an empty string, resulting
in a partially invisible row in the Many2one selection),
use the same fallback as when the `_rec_name` doesn't exist.
Since `display_name` should never be Falsy anymore, remove part of the
test_mail_message_values_fromto_long_name that covers the
Falsy `display_name` case.
task-3424154
closesodoo/odoo#133691
X-original-commit: 0cb9e66edd9b7142a6e56bc6ee6491e6d6047e51
Signed-off-by: Raphael Collet <rco@odoo.com>
Signed-off-by: Rémy Voet (ryv) <ryv@odoo.com>
Issue
=====
The previous commit forced consistency between `check_access_rule` and
`_apply_ir_rule`. Now the parent model `ir.rule` (via inherited) is also
checked before the unlinking (`check_access_rule("unlink")`). The
`website.page` unlink override sometimes calls `unlink` on its parent
view and because view_id has `ondelete="cascade"`, it will actually
deletes the `website.page` itself. Then calling to `super().unlink()`
with self will raise a MissingError.
Fix
===
Batch the old logic and remove already unlinked record from `self`
before calling `super`.
closesodoo/odoo#125916
Signed-off-by: Raphael Collet <rco@odoo.com>
Issues
======
- `_apply_ir_rule` applies `ir.rule` of the current model and also
`ir.rule` from the inherited model (via inherits). But
`check_access_rule` doesn't check the later one.
- `_flush_search` doesn't flush fields coming from the `ir.rule` of
the inherited model (via inherits). Then the filtering done by
`_apply_ir_rule` may be inconsistent with cached values.
Changes
=======
Because of https://github.com/odoo/odoo/blob/6ddcb448612f5d784c8e9ebb90f19077e65be3e1/odoo/osv/expression.py#L1073-L1073,
and https://github.com/odoo/odoo/blob/00e86b1552d1e5541a8dbf9411de5cfdb8990cc4/odoo/fields.py#L2895
leaf like `('<many2one_delegate>', 'any', [<sub-domain>])`,
will be translated in the same way as `_inherits_join_add` does.
We can remove `_inherits_join_add` and its usage in `_apply_ir_rule`
and change `ir.rule._compute_domain` to also return the inherited
(via inherits) `ir.rule` domain (with the new 'any' operator).
Since `_compute_domain` is used by `_apply_ir_rule` and
`_filter_access_rules_python`, everything is consistent.
Also fix `BaseModel._flush_search` to take in account 'any'/'not any'
operators (compulsory in order to flush correctly new domain
from `ir.rule._compute_domain` generated).
Part-of: odoo/odoo#125916
Add test to ensure that `_compute_display_name` is called once with
the correct recordset during `read_group`. Also
fix and small typo in the documentation of `read_group`.
closesodoo/odoo#132261
X-original-commit: 60477586f11ca7eb698240a6bd3f565b5a9e3279
Signed-off-by: Julien Castiaux (juc) <juc@odoo.com>
Signed-off-by: Raphael Collet <rco@odoo.com>
Signed-off-by: Rémy Voet (ryv) <ryv@odoo.com>
In https://github.com/odoo/odoo/pull/110737, I didn't consider that
compute could be called on NewId record with `_origin`. Some compute
methods are badly refactored with the new signature of `_read_group`.
We use recordsets returning from `_read_group` to assign field value to
`self`. But if `self` contains `NewId` with `origin`, these records
don't represent `self`, it contains real record instead of the one with
NewId + origin. Then the assignations are done on records not in `self`
which may lead to generate traceback or write to other records during
an onchange.
Fix multiple compute to work correctly with NewId (origin set) record.
X-original-commit: bd22d0a5c479a72cdaf799309387e41ce692bb29
Part-of: odoo/odoo#132261
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>
`filtered_domain` didn't throw an exception when it is called with a
domain containing the new 'any' and 'not any' operators. Fix it.
X-original-commit: 05713a270bfad406daa9dbf739b80cf8c4d065c0
Part-of: odoo/odoo#127750
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
The previous commit introduced an optimization to reduce the number of
queries and fields fetched when we call `name_search`. But it works much
better when the dependencies of `display_name` contain field names used
in the calculation (on the same record/model).
Then, to improve the performance and the cache coherency, add `depends`
and `depends_context` depending on the custom `_compute_display_name`.
Add only the first level of dependencies (never traverse relational
field) because only these have a positive impact on the previous
optimization and the cost is very low (see `modified`).
About `depends_context`, we don't include `lang` because (when `_` is
used by example) it is unlikely to get the same display_name in the same
request with two different lang.
closesodoo/odoo#122085
Related: odoo/documentation#4639
Related: odoo/enterprise#42599
Related: odoo/upgrade#4780
Signed-off-by: Raphael Collet <rco@odoo.com>
The `name_search` makes at least 2 SQL requests, one to find the records
and one to read fields needed to compute the `display_name`. With the
default `_compute_display_name`, it only needs to fetch the `_rec_name`
field (if there is one), but the ORM perfetch field mechanism will also
fetch every prefetchable field (see `_fetch_field`).
Then, to avoid running two queries and fetching too many fields, we add
the dependency fields (with `_determine_fields_to_fetch`) to the select
clause of the `Query` returned by `_name_search`.
Part-of: odoo/odoo#122085
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
Since https://github.com/odoo/odoo/pull/121355,
add_to_compute on no-store compute field, lead to recompute the field
at the end of request and it can lead to some non deterministic bug
(compute with a bad context by example).
Avoid to add_to_compute no-store or no-compute fields.
closesodoo/odoo#122973
X-original-commit: 533193106e6c9460d741e504babc7591cb935939
Signed-off-by: Raphael Collet <rco@odoo.com>
Signed-off-by: Christophe Simonis (chs) <chs@odoo.com>
Signed-off-by: Christophe Monniez (moc) <moc@odoo.com>
`tocompute` in the `Transaction` contains store field on records to
be recomputed. No-store compute fields are directly invalidated from
the cache when a dependency changes (see `BaseModel.modified`).
In fact, `_recompute_field` was actually doing too much for nothing.
Also, it may invalidate caches of compute no-store fields for no reason
(e.g., if they are searchable). Remove the part for field compute
no-store field. And prevent `_recompute_field` callers from calling it
with no-store fields.
closesodoo/odoo#122147
X-original-commit: ba9ccb07fb12558667db97b866df492fd0f5ba4d
Signed-off-by: Raphael Collet <rco@odoo.com>
Signed-off-by: Rémy Voet <ryv@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
This reverts commit 9e71094582ec4c9b719431e77538da8f91ffa9e3.
Why?
- It is useless in 16.0, the bug it claims to fix should not exist. In the
commit explanation the sentence 'this calls `_read`, which flushes the
field we're trying to read' is wrong, since
https://github.com/odoo/odoo/pull/66938 (merged in 15.5). (It is still true
for fields that are in an ir.rule, but it sounds very unlikely to have
a recursive field in ir.rule that causes trigger the problem)
- It creates worst errors (infinite loop for recursive field computation on
missing record - Next commit).
- Also, it looks like a dangerous fix that can hide or trigger new
issues.
X-original-commit: 4a46c1049a4cdc15f0e211d228fb976ba7a0173d
Part-of: odoo/odoo#122147
On a model X, where there is a field related x_related
(related= 'y_id.y_translate') towards a translate field y_translate
on Model Y.
When you unlink at least 1001 records of X
(r1, r2, ... , r1000, r1001) (cr.MAX_IN + 1), you get a traceback
(`RecursionError: maximum recursion depth exceeded in comparison`).
The stack looks like:
File "/home/odoo/Documents/dev/odoo/odoo/models.py", line 3609, in unlink
self.env.flush_all()
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 6179, 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 4209, 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 5874, in __getitem__
return self._fields[key].__get__(self, type(self))
File "/home/odoo/Documents/dev/odoo/odoo/fields.py", line 2771, 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 3214, in _read
self.flush_recordset(translated_field_names)
...
-> Extra info:
translated_field_names = ['x_related']
`self = X(r1001, r1, r2, ..., r999)`
...
File "/home/odoo/Documents/dev/odoo/odoo/models.py", line 5585, in flush_recordset
self._recompute_recordset(fnames)
File "/home/odoo/Documents/dev/odoo/odoo/models.py", line 6162, in _recompute_recordset
self._recompute_field(field, self._ids)
...
-> Long recursion starts here but first records to be recomputed will be r1, then r2, then r3, ...
But the maximum recursion depth error will be triggered earlier at the
end of the recursion, because the stack limit in Python is 1000
by default.
...
File "/home/odoo/Documents/dev/odoo/odoo/models.py", line 6179, 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 4209, 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 5874, in __getitem__
return self._fields[key].__get__(self, type(self))
File "/home/odoo/Documents/dev/odoo/odoo/fields.py", line 2771, 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 3214, in _read
self.flush_recordset(translated_field_names)
...
------ Extra info:
translated_field_names = ['x_related']
`self = X(r1, r2, ... , r999)`
...
Since 9c3b9a4926, the `x_related` is
flagged to be recomputed at the end of the delete
loop of `unlink`, but it shouldn't be, because records are deleted now.
The problem is in these lines:
```python
with self.env.protecting(self._fields.values(), records):
self.modified(self._fields, before=True)
```
The `records` are only a part of `self` (batch of 1000), so the
`protecting` call only protects the current batch and not `self`.
Then, the second batch (here with only one record), will flag to recompute
`x_related` of the first batch records. Then, later on, the `flush_all`
will generate the recursion error trying to resolve
these `to_recompute`.
To fix it, only move the modified call (+ protecting) before
the batch loop and executes it on `self`.
This issue shouldn't exist in master, because having a
related translate field triggers a warning (`Translated stored related
field (<field_name>) will not be computed correctly in all languages`).
Also `https://github.com/odoo/odoo/pull/100472` fixes the issue in
master, but it generates one SQL request by record, which isn't great.
Then in master, we should forward this commit (but test will be remove
because it generates the warning message).
closesodoo/odoo#119471
X-original-commit: 78450cae5900e267de439e4988b314aa644e76bd
Signed-off-by: Raphael Collet <rco@odoo.com>
Signed-off-by: Rémy Voet <ryv@odoo.com>
The `read_group` of `calendar.event` sends a danger notification when
the user groups by any field in the list view. It is because the
security reenforcement done in 2c0b3ab670168a155b3974d5a5b39aa1f1df3452
is too strict. It checks all `fields`, even the ones that are filtered
out by the `read_group` (`fields` without aggregation specification
nor `group_operator`).
closesodoo/odoo#119459
Signed-off-by: Raphael Collet <rco@odoo.com>
Before 916c9c46f58f27c68c5558a2a57fdca43a89fe89, we could
groupby several times on the same relational field with `read_group`
Example: `read_group(..., groupby=['product_id', 'product_id'], ...)`.
Now, it produces a traceback:
File "/data/build/odoo/odoo/models.py", line 2583, in read_group
self._read_group_format_result(rows_dict, lazy_groupby)
File "/data/build/odoo/odoo/models.py", line 2396, in _read_group_format_result
ids = [row[group].id for row in rows_dict if row[group]]
File "/data/build/odoo/odoo/models.py", line 2396, in <listcomp>
ids = [row[group].id for row in rows_dict if row[group]]
AttributeError: 'tuple' object has no attribute 'id'
It is because `_read_group_format_result` try to convert the record
into tuple (id, display_name) twice (one for each groupby).
Fix this issue introduced by the refactor of `_read_group`.
Part-of: odoo/odoo#119459
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>
Calling `fetch` with 'id' in the `fields_name`, will always generate
SQL query even if all requested field values are in the cache.
This is because we also look for values in the 'id' field cache,
but we don't ever fill the cache for `Id` fields.
closesodoo/odoo#119107
X-original-commit: 3ba8d0e5e57e1358cc8958abf511d514515eccb2
Signed-off-by: Vincent Schippefilt (vsc) <vsc@odoo.com>
Make the public `read_group` depends of its private method `_read_group`
refactored to match the backend usage.
We try to keep the public API similar for this first part of the
rafactor, but there are still some API change:
- We cannot order by `id` anymore.
- The display_name of many2x group values are not lazy anymore.
Part-of: odoo/odoo#110737
Lot of override of read_group reimplement partially custom security
rule of `_search` method. In order to simplify these security check
and have a consistent behavior between the search and read_group method,
_read_group now use _search to create the from and where clause.
Part-of: odoo/odoo#110737
The new backend version of read_group can simplify the
current overrides of read_group. Do it for each of them and
avoid making extra search (done with __domain) when it is possible.
Part-of: odoo/odoo#110737
The `_read_group` was designed to be used by the web client to
efficiently compute aggregations grouped by one or more fields.
However, more and more developers have been using it from the backend
to make computations more efficient (avoid doing the aggregation
in Python). Unfortunately, the API was designed for the web client,
which added a lot of boilerplate when used in the Python (list of
dict with misleading key name choices).
`_read_group` was created to improve the performance of read_group
for backend use (4ef0c00b4b), but didn't
change the API and based the implementation on read_group itself.
Rewrite `_read_group` from scratch with a new API to make it easier
to use from the backend (see the method documentation). Also, split
the method to make it easy to override and add custom behavior.
Part-of: odoo/odoo#110737
`read_group` cannot order on the fields that are not
aggregates in the same transaction. We get a warning for it:
`<model>: read_group order by '<field_name> ASC' ignored, cannot sort on empty columns (not grouped/aggregated)`
Fix it.
Part-of: odoo/odoo#110737
The default `group_operator` of `Many2oneReference` is 'sum' (inherited
from the parent class `Integer`), it doesn't make any functional sense
to sum ids.
Also set group_operator to None on `co2` instead of override read_group
for the same result.
Part-of: odoo/odoo#110737
When we create a new record X and its values contains an one2many field
with either a recordset Y or a Command.set(ids), the ORM generates extra
SQL queries to search and remove the existing lines of the new record.
The call chain is as follows:
Model._create
-> _RelationalMulti.create
-> _RelationalMulti.write_batch
-> One2many.write_real
But because record X is brand new, it has no lines to begin with. The
fix consists in skipping the search and removal in that case.
Also remove any nondeterministic behavior of method write_real() using
an OrderedSet instead of a builtin set.
closesodoo/odoo#114726
X-original-commit: 428827e5213ee002a5b50d864f8ce1f24d1842b3
Signed-off-by: Raphael Collet <rco@odoo.com>
The TriggerTree class does not have a specific __repr__(). It thus
falls back on dict's __repr__(), which does not show the root of the
tree. This makes debugging hard and confusing.
closesodoo/odoo#113521
Signed-off-by: Raphael Collet <rco@odoo.com>
According to the method documentation, `Field.write` should return
the subset of record actually write.
But it is not respected at every return, it is not used at all and it generated extra completixy for nothing.
Then remove every return values.
closesodoo/odoo#111108
Signed-off-by: Raphael Collet <rco@odoo.com>
The `_update` of `_RelationalMulti` return a bool but the
others `_update` methods doesn't return anything.
It is actually not used. Also, the `_update` is always called with
a recordset as `value`, then the first part of the method is useless.
Part-of: odoo/odoo#111108
`convert_to_cache` of the `Selection` field type, check that the
column_type is 'int4'. But nowadays (since
a0e05e2ab9), a `Selection` field can
only be a Varchar type.
Part-of: odoo/odoo#111108
There is only one legitimate usage of `_patch_method` (base_automation)
and none of `_revert_method`. The other uses are in tests and they are
all wrong: if the test crashes between the `_patch_method` and the
`_revert_method`, the method will not be reverted.
For the only proper usage of `_patch_method`, move the code to this
place. Correct tests using `_patch_method`/`_revert_method` by calling
the `patch` method of `BaseCase`.
Also, remove the `api.returns` from the `create` method of `BaseModel`
as it is useless and confusing. In fact, we never use it because we
have a special treatment at the RPC level for the `create` method
(see `_call_kw_model_create`).
closesodoo/odoo#110370
Signed-off-by: Raphael Collet <rco@odoo.com>
The `ormcache` decorator fails to create the key method
(`determine_key`) when the method signature contains any annotation.
Fix it by removing annotation of the signature.
closesodoo/odoo#109777
Signed-off-by: Raphael Collet <rco@odoo.com>
The tree view `view_module_category_tree` was unused, remove it.
The form view (`view_module_category_form`) of `ir.module.category` is
still used in the `view_groups_form` view.
Part-of: odoo/odoo#109420
Since https://github.com/odoo/odoo/pull/87522 (landed in v16),
`fields_view_get`, `_fields_view_get` and `load_views` are deprecated.
Remove it for maintenance (and it is the last usage of
the useless field `field_parent`).
Part-of: odoo/odoo#109420
This is a rare case where method _read() raises a MissingError instead
of just ignoring it.
The issue is triggered by several conditions on a model M:
- at least one ir.rule on M with a domain using a column field on M;
- one deleted record Y which is in the prefetch set of a record X;
- one reads a non-column field on record X.
Fix method _read() to manage that case. It adds an extra call to
exists() in that case, but adds no overhead in the general case.
closesodoo/odoo#108050
X-original-commit: 1876dc87e5c88c711c9b3882c39be704ffc46532
Signed-off-by: Raphael Collet <rco@odoo.com>
Signed-off-by: Rémy Voet <ryv@odoo.com>
Field indexed by trigram cannot become translated because the
`convert_column_translatable` is based on the old index naming convention
(changed in https://github.com/odoo/odoo/pull/100736).
Then it generates a PostgreSQL error:
`psycopg2.errors.DatatypeMismatch: operator class "gin_trgm_ops" does not accept data type jsonb`
closesodoo/odoo#105295
Signed-off-by: Raphael Collet <rco@odoo.com>
There are two issues with the index naming convention used by the ORM:
Problem 1: it is possible to have naming conflict for indexes. For
instance, the name 'slide_channel_tag_group_sequence_index' is used for
both fields slide.channel.tag.group.sequence and
slide.channel.tag.group_sequence. Only the first index will be created.
Solution 1: we separate the model and field names with a double
underscore instead of a single one, which is the same strategy as with
LEFT JOIN aliases. This is correct because model names don't contain
such double underscores or underscores as prefix or suffix (it is not
forbiden but model name should follow the 'dot notation'.)
Problem 2: index names can be longer than 63 chars, but PostgreSQL
silently truncates it. This doesn't actually break anything (PostgreSQL
also truncates values when we check the existence of indexes) but it can
lead to using the same name twice. There is hopefully not any example
in our code.
Solution 2: if the name is too large, we truncate it and pad it with a
hash of the complete name to match 63 characters, which is also the
strategy used for LEFT JOIN aliases.
task-2984730
closesodoo/odoo#100736
Related: odoo/upgrade#3957
Signed-off-by: Raphael Collet <rco@odoo.com>
`action_view_pos_orders` is unused and it is incorrect
because it generates a wrong domain (`('id', 'in', pos_order_ids)` where
`pos_order_ids` is a list of tuple(<id>, <display_name>))
Part-of: odoo/odoo#104838
Reading only 'id' with `search_read` is equivalent to use `search` but
complexify the result usage. Fix all these bad usages.
Part-of: odoo/odoo#104838
Co-authored-by: Julien Castiaux <juc@odoo.com>
O2MIdMapper Class inherit of 'base' Model to change the behavior of
`create`.
Because it is in base and the `_import_current_module` is set in
models.py, move it at the end of create in models.py.
closesodoo/odoo#99550
Related: odoo/enterprise#33063
Signed-off-by: Vincent Schippefilt (vsc) <vsc@odoo.com>
- Deprecated `norecompute` (on Environment class) because it is useless
and do nothing.
- Deprecated `cache_restart` (`res.company`) because `clear_caches` do
the stuff.
- Deprecated `write_company_and_print_report` (`res.company`) because
since https://github.com/odoo/odoo/pull/33863/, it is unused
- Deprecated `open_company_edit_report` (`res.company`) because since
25f7040998, it is unused.
Part-of: odoo/odoo#99550
'Barcode GS1 Parser' test sent RPC to the server (moreover
it was wrong rpc). Fix it.
closesodoo/odoo#102239
Signed-off-by: Steve Van Essche <svs@odoo.com>
To be able to create easily a jsonb column in database,
we create a Json type Field. Currently, it is quite limited
field:
- We cannot modified the value in-place, we need to always set
the entire jsonify value.
- No domain operator is done to work with jsonb. Now, it works as a
text field.
closesodoo/odoo#103097
X-original-commit: 7eeba9d205d2dace571b5d0895ddba6290a512db
Related: odoo/enterprise#32729
Signed-off-by: Quentin De Paoli <qdp@odoo.com>
- In `unlink`, since https://github.com/odoo/odoo/pull/66938
modified is called on self for each batch of 1_000.
But it should be called on the batched records.
- In `write`, remove useless `records_to_inverse`
(there from ORM refactor but never used)
- make `_modified_triggers` more deterministic by
changing a `set` into `OrderedSet`.
closesodoo/odoo#100472
Signed-off-by: Raphael Collet <rco@odoo.com>
Because `quant_ids` (`stock.quant.package`) is a one2many inverse of
`package_id` and some depends use it.
It is important to have in index on package_id on `stock.quant`
closesodoo/odoo#100371
Signed-off-by: Arnold Moyaux (arm) <arm@odoo.com>
- Remove `_default_log_exceptions` of our `Cursor` class (unused except
in one test)
- Deprecated `serialized` args of `__init__` (Cursor class) and
`cursor()` (of Connection class), our cursor is always serialized.
- Remove `sql_log` attribute of Cursor class and replace it with
appropriate code to do the same stuff dynamically.
- Simplify some code
- Update some docstring
- Clean import
task-2766494
closesodoo/odoo#85078
Signed-off-by: Rémy Voet <ryv@odoo.com>