Commit Graph
521 Commits
Author SHA1 Message Date
Gorash ba1a5509fa [IMP] base: Remove context dependencies from get_views method
In this viewtiverse, the heroes remove the context dependencies for
`get_views`, from the views and python fields (such as domain). To reduce
inconsistencies and the number of rpc.

Current issues:
* There may be inconsistencies in views at the JavaScript level. Some
overrides modify the behavior of get_views or domains on fields via
context keys, therefore by changing the action, the rendering may be
different. However, these views are cached. However, the cache key
(Javascript) does not reflect the entire context, and requires additional
post-processing from the server.
* Multiple rpc for the same rendering. get_views being dependent on the
context, as soon as it changes, a new rpc is performed. In most cases,
when JavaScript needs the same view, there is no change depending on the
context, the rpc is useless.
* Inconsistency when rendering subviews, some views could be different
depending on the context, this context can be modified in the view itself
via the context attributes. However, the JavaScript client does not redo
an rpc for each change of these sub-contexts. Therefore the result may be
inconsistent.

Solution:
Limit as much as possible the number of context keys provided when calling
get_views, and use the context provided as a cache key. The authorized
keys are 'lang' and '*_view_ref'. For the cache key, options are added in
the get_views method.
Instead of using the context, it is inserted into python expressions.
This will be evaluated by JavaScript and thus avoids inconsistencies.

task-3414108
task-3414068

closes odoo/odoo#135145

Related: odoo/enterprise#47584
Signed-off-by: Raphael Collet <rco@odoo.com>
2023-09-21 16:52:10 +00:00
Chong Wang (cwg) 13957b6281 [FIX] core: support cr.execute_values
psycopg2.extras.execute_values was introduced in PR #101237
however it pypasses the override logic for cr.execute. As a result
1. --log-sql cannot log these queries
2. assertQueryCount cannot notice these queries
...

This commit create a new api cr.execute_values to support the same SQL feature
without losing the override logic for cr.execute

closes odoo/odoo#131190

Related: odoo/enterprise#47374
Signed-off-by: Rémy Voet (ryv) <ryv@odoo.com>
2023-09-14 11:22:42 +00:00
std-odoo 88f4c4dd36 [IMP] base: properties, do not allow to select transient models
Purpose
=======
Do not allow to select transient models in relational properties.

Task-3032464

Part-of: odoo/odoo#103510
2023-09-08 09:46:56 +00:00
std-odoo 6c566c2814 [IMP] base: properties, better name verification
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
2023-09-08 09:46:56 +00:00
std-odoo 6412575f4b [IMP] base: allow to use default_ context key for properties
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
2023-09-08 09:46:56 +00:00
std-odoo 29c7b23e22 [IMP] base, web: add the "separator" properties type
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
2023-08-29 09:11:51 +00:00
Jeremy Kersten 9a442591f5 [IMP] fields: log diff of html field when you cannot edit it
Since commit cf844e34, we have a new group `group_sanitize_override`
that allow to prevent users from adding code that will be evaluated.

To know if a user can edit a field if they are restricted editor and
without this group, we do the diff between the normalized version and
the sanitized.

It is difficult to debug in production why a user cannot edit a html
field because we don't have the log of this diff.
Now we add the unified diff into the log. It should not occur too often
and if necessary we will reduce the occurrences in the log later.
(if debug mode, if loaded into the iframe [in edit mode with @], ...)

closes odoo/odoo#133395

X-original-commit: d60759d637a85d2220cee7566483bf3cff93b810
Signed-off-by: Romain Derie (rde) <rde@odoo.com>
Signed-off-by: Jérémy Kersten <jke@odoo.com>
2023-08-29 01:32:47 +00:00
std-odoo 5a23c4dd1e [FIX] web: fix the unity read with relational properties
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
2023-08-28 16:42:03 +00:00
Gorash 75a105f46a [REF] base/all: Update modifier syntax: remove 'states' from fields
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
2023-08-18 09:49:11 +02:00
Gorash 5419d0ef7a [REF] base,all: Update modifier syntax: clean fields keys
Part-of: odoo/odoo#104741
2023-08-18 09:49:10 +02:00
Gorash cdaa761ced [REF] base: Update modifier syntax (invisible, required, readonly)
Goal:
* Simplified modifiers to only have one way to define modifiers;
* Remove states attributes on python field;
* Use python expression in view `required`, `readonly`, `invisible`;
* More accurate validation of xml views.

This commit change the syntax to python expression. The next commit
will update/convert all xml views.

Before this commit:
* the `required`, `readonly` and `invisible` attributes can only have
values of `True`, `False`, 1, 0 or a python expression to use the
context;
* the `attrs` attribute define a dict. The key of this dict was
`required`, `readonly` and `invisible` and the values are the domain or
a string representing a domain to be evaluate as python expression.
This python expressions was evaluate by the javascript with view fields
and other contextual values as: context, uid, parent, active_id,
active_ids, active_model, allowed_company_ids, current_company_id.
* the `states` attribute in the view was a comma separated list of the
state. This list was combined with the `invisible` attribute;
* the `invisible` attribute on python field is used as default value;
* the `states` attribute on python field was dictionnary with state as
key and list of tuple. This structure was combined with `readonly` view
attribute.
* After combining, the resulting domains of the different attributes
`required`, `readonly` and `invisible` are evaluated with the values of
the fields. The `invisible` attributes is splitted into two use:
`invisible` and `column_invisible`.

After this commit:
* The attributes `required`, `readonly`, `invisible` and
`column_invisible` define python expression. This python expressions
are evaluate by the javascript with view fields and other contextual
values as: context, uid, parent, active_id, active_ids, active_model,
allowed_company_ids, current_company_id.

The domains can contains contextual value and will be evaluate by the
javascript.

```xml
    <field name="field_a" readonly="not context.get('show_a')" attrs="{'readonly': [('field_b', '!=', False), ('field_c', '=', parent.c)]}"/>
    <field name="field_b" states="draft"/>
```
will be replaced by
```xml
    <field name="field_a" readonly="not context.get('show_a') or field_b and field_c == parent.c"/>
    <field name="field_b" invisible="state != 'draft'"/>
```

Some inherited views will be modified differently in order to maintain
the previous behavior:

```xml
    <field name="field_a" readonly="not context.get('show_a')" attrs="{'invisible': [('field_b', '!=', False)]}">
```
```xml
    <field name="field_a" position="attributes">
        <attribute name="attrs">{'readonly': [('field_c', '=', False)], 'invisible': [('field_d', '!=', '3')]}<attribute>
    </field>
```
will be replaced by
```xml
    <field name="field_a" readonly="not context.get('show_a')" invisible="field_b">
```
```xml
    <field name="field_a" position="attributes">
        <attribute name="readonly" add="(not field_c)" separator=" or "/>
        <attribute name="invisible">field_d != 3<attribute>
    </field>
```

Validation:
A stricter control is made on the level of the attributes (modifiers)
and the fields necessary for these. The use of the previous attributes
'attr' and 'states' triggers an error (these no longer exist after the
application of the migration script)

task-2495504

Part-of: odoo/odoo#104741
2023-08-18 09:49:08 +02:00
Louis Wicket (wil) 537b0ce712 [REM] base, *: remove binary_field_real_user from context
Formerly, using sudo() on a record had the effect of replacing the
current user with the superuser. But sometimes, knowing who the "real
user" was was necessary, so we needed to store it somewhere. This is
basically why the key `binary_field_real_user` was introduced in the
context: to keep track of who the user was before switching to sudo
mode.

Since 1e6c3bec2c, however, switching to
sudo mode no longer changes the current user; meaning that the
`binary_field_real_user` is no longer necessary.

This commit removes the remaining occurrences of the now useless
`binary_field_real_user` from the code.

* = hr, portal, web_editor

closes odoo/odoo#92032

Enterprise: https://github.com/odoo/enterprise/pull/27649
Related: odoo/enterprise#27649
Signed-off-by: Louis Wicket (wil) <wil@odoo.com>
2023-08-16 10:23:49 +02:00
Raphael Collet 7bfbd75d57 [FIX] core: web_read() on new records
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
2023-07-24 20:17:49 +02:00
william-andre 0d30cc2bc9 [IMP] models: manage sub companies with check_company
Allow some models to be delegated to the main company of the object.
For instance:
* Have company Parent and Child
* Child has access to all the accounts of Parent
* When creating a document in Child using an account of Parent, there
  shouldn't be a consistency error.

In order to do that, a new method `_check_company_domain` has been added.
This method is used when `check_company=True` is set on the field
declaration to:
* filter relational fields in the UI [^1]
* validate relational fields when writing on them in `create` and `write`

That method should also be used in the business code for models likely
to be shared amongst company branches instead of hardcoding the domain
based on `company_id`.
The same applies on domain restriction on the field declaration: the
flag `check_company` should be prefered instead of providing the domain
explicitly based on `company_id`.

Since a lot of validation and filtering will now be done based on
`parent_of`/`child_of` of the company, some support has been added for
that special case in `filtered_domain` in order to avoid doing
additional queries.

task-3371677

[^1]: because of that change, support has been added on the python
interpreter of the client to allow concatenatng domains.

Part-of: odoo/odoo#125642
2023-07-20 11:49:05 +02:00
Raphael Collet 96a98f4f4c [FIX] web: web_read() on new records with _inherits
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.

closes odoo/odoo#128878

Signed-off-by: Raphael Collet <rco@odoo.com>
2023-07-18 19:28:13 +02:00
Benoit Socias 0ba3617f9d [IMP] base, web_editor: precompute and use resized webp on upload
`libwebp` cannot be used to perform resize operations on webp images on
the server.

This commit uploads all resizes of webp images whenever a webp image is
uploaded:
- resized to any smaller size in 1024, 512, 256 & 128,
- each size converted to jpg as well to be usable in `wkhtmltopdf`.

When an Image field requires `_imageProcessing` to resize a webp image,
it retrieves the pre-resized image instead of running through the
Pillow toolbox.

Pre-converted images are stored as `ir.attachment`s with:
- `res_model` = ir.attachment
- `res_id` = id of transformed attachment
  i.e. id of original size webp for resized images
       id of webp for jpeg-converted images
- `description` = "format: image/jpeg" for jpeg conversions,
                  "resize: [size]" for resized images.

task-2774352

Part-of: odoo/odoo#85494
2023-07-15 05:10:46 +02:00
Raphael Collet 884522ebd1 [FIX] core: infinite recursion when accessing Html field on new record
closes odoo/odoo#127718

Related: odoo/enterprise#43815
Signed-off-by: Raphael Collet <rco@odoo.com>
2023-07-10 18:16:19 +02:00
Chong Wang (cwg) 967f6b6121 [FIX] website: fix to_translate tag
Before the task
when the default language for a website is not en_US, and users try to
edit_translations from the website, all terms are marked "translated"
because odoo adds
`data-oe-translation-state="to translate"` if the term is not extracted from
the en_US value and is the same as its en_US term

After this commit:
if the record's bounded website's default language is lang_base
odoo adds
`data-oe-translation-state="to translate"` if the term is not extracted from
the lang_base value and is the same as its lang_base term

closes odoo/odoo#127468

Task-id: 3344973
X-original-commit: 7c20510bda2f1312a9392df445ee38aea7b2267b
Signed-off-by: Julien Castiaux (juc) <juc@odoo.com>
Signed-off-by: Chong Wang (cwg) <cwg@odoo.com>
2023-07-07 12:09:54 +02:00
Rémy Voet (ryv) 3c62ca1eb9 [REM] core: remove name_get API
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
2023-06-28 17:41:19 +02:00
snd f75779ba3d [IMP] stock: show lot properties on quants
Since stock.quant are directly related to their lot/serial, we want to be able to show the lot properties on the related quants.
We also want to be able to filter quants based on the properties of their lot/serial.

closes odoo/odoo#125197

Signed-off-by: Arnold Moyaux (arm) <arm@odoo.com>
2023-06-28 12:30:50 +02:00
Raphael Collet 00e86b1552 [FIX] core: new record has one2many field with real records
Part-of: odoo/odoo#124612
2023-06-13 12:49:23 +02:00
std-odoo c056f858fd [FIX] web: do not keep useless properties keys in definition
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

closes odoo/odoo#124129

X-original-commit: 35c99a9110cec3317f36d4ddffae5196b58939e5
Signed-off-by: Thibault Delavallee (tde) <tde@openerp.com>
2023-06-08 09:09:57 +02:00
std-odoo b90d471e6b [FIX] base: fix selection properties in list view
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

closes odoo/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>
2023-06-06 11:45:19 +02:00
Alvaro FuentesandChong Wang f7fca3faf9 [FIX] core: do not udpate translation terms from text to xml
Steps to reproduce the issue:
* In a clean v16 db install base_setup
* Log in into DB, switch language to French (install and activate it)
* Upgrade to saas-16.1

Issue: The main settings page under Companies settings (Sociétés in Frech) shows
```
<span class="o_form_label">Mise en page du document</span> <span class="fa fa-lg fa-building-o" title="Les valeurs définies ici sont spécifiques à l'entreprise." aria-label="Values set here are company-specific." groups="base.group_multi_company" role="img"/>
```
instead of just
```
Mise en page du document
```

The issue comes from this difference, 16.0:
https://github.com/odoo/odoo/blob/fb02720aed0b0be00df8f5d0e1932b948c300b92/addons/base_setup/views/res_config_settings_views.xml#L78-L79
vs saas-16.1:
https://github.com/odoo/odoo/blob/80bc702ecfe6f704af05f9830d272b162018f2ad/addons/base_setup/views/res_config_settings_views.xml#L79

Note that `Document Layout` is used in two different contexts. In 16.0
it comes within an xml/html block containing a `<span>` tag, and more
importantly it is the **text** part of the xml tag. In saas-16.1 the
same `Document Layout` is used as an **attribute** of the `setting` tag.
Thus we cannot blindly assign the whole term (block with tags) to the
attribute when upgrading to saas-16.1, it is only safe to update the
translation from xml to text. Updating a text entry with something that
seems to have other xml elements is unsafe and can lead to the issue
showcased here.

For more context, at the time of updating the terms here is the
situation:
* `closest_matches` is `['Document Layout']`
* `closest_term` is `Document Layout`
* `old_term` is
  ```
  <span class="o_form_label">Document Layout</span>
                                        <span class="fa fa-lg fa-building-o" title="Values set here are company-specific." aria-label="Values set here are company-specific." groups="base.group_multi_company" role="img"/>
  ```

closes odoo/odoo#123540

X-original-commit: 6cd49293fe4d0d86fbbfa2476e113deadeba7348
Signed-off-by: Wang Chong (cwg) <cwg@odoo.com>
Signed-off-by: Alvaro Fuentes Suarez (afu) <afu@odoo.com>
Co-authored-by: Chong Wang(cwg) <cwg@odoo.com>
2023-06-02 23:38:58 +02:00
Rémy Voet (ryv) 4e0eed85d6 [FIX] core: maximum recursion because of active fields.
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
2023-05-25 16:29:26 +02:00
Rémy Voet (ryv) 8f600f8a4b [REV] core: handle recursion error when resolving stored fields
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
2023-05-25 16:29:26 +02:00
Chong Wang (cwg) e03754a9d4 [FIX] core: write terms to en_US if not activated
Before this commit:
when en_US is not activated and the user changes terms slightly for
ir.ui.view.arch, the new term will be treated as a typo fix or style change, and
won't be populated to other languages. As a result, in the form view, arch_base
field which displays the en_US translation of the arch_db will still be the
content before the change(wrong and strange).

After this commit:
when write a model_terms field when en_US is not activated, its en_US value will
always be overwritten.

opw-3265418

closes odoo/odoo#122006

X-original-commit: e7fd2361ab83f3a02ada09c18094adc5c7517227
Signed-off-by: Raphael Collet <rco@odoo.com>
Signed-off-by: Wang Chong (cwg) <cwg@odoo.com>
2023-05-23 14:52:05 +02:00
Xavier Morel 390384ecbb [FIX] core: handle recursion error when resolving stored fields
Issue discovered in the uninstall (and reinstall) of sale_project: a
dump has ~100 tasks, when reinstalling `sale_line_id` has to be
initialised, this is done by marking `sale_line_id` on all extant
tasks as to-recompute, which triggers their computation on the next
`flush`.

Because it's a recursive field, `Field.recompute` ensures only one
record at a time gets recomputed (as there could be cross-dependencies
in the recorset which protection would prevent from resolving).

As the field computation runs, it accesses itself, which triggers a
cache miss, which triggers a `_fetch_field` (to get the currently
stored value), this calls `_read`, which flushes the field we're
trying to read.

The problem here is that for efficiency the cache miss will look for
all records in the cache without a value for the
field (`_in_cache_without`) and try to `fetch` on them as well. This
means rather than not doing anything in flush, we're going to
`Field.recompute` on all records except the one selected the first
time around, which repeats the cycle until there is no more additional
record found in `_in_cache_without`, which could trigger the next
round of `recompute`, and the entire thing unwinds, and we probably
perform a ton of unnecessary additional `compute_value`.

Except that doesn't even happen, because the process from one compute
to the next takes 12~13 stack frames, which given the default
recursion limit of 1000 gives a hard limit of 76 fields before hitting
a RecursionError. As this is less than 100, a recursion error [is what
we get](https://runbot.odoo.com/runbot/build/31726625).

In 15.2, this was fixed by only expanding the fetch on non-recursive
fields, pessimizing recursive
fields (5c2511115b14299516fce4aa3737a62faaf5b653). Test-wise this only
impacted mail performances and in a relatively minor manner.

In 16.0, the mail tests actually match already (so that part was
skipped by the cherrypicking) however this impacts the knowledge perf
tests much more significantly e.g. `test_article_creation_multi_roots`
gets +9 queries when creating 10 top-level articles, which is a bit
much.

So use an alternative which is ugly as hell but which I didn't
consider for 15.2 (may want to backport it one day if the current fix
is an issue): catch the recursion error and use the existing
fallback (of fetching just the requested record's field without
expanding the recordset).

This likely makes for a pretty inefficient situation in the original
case as we're certainly going to hit the recursion limit repeatedly,
but that still fixes the issue, and it avoids deoptimising cases which
fall short of the recursion limit (resolving under 60 records or
so).

Plus despite creating giant stacks we might actually get good
efficiency as we're going to hit recursion limits repeatedly but
that's pure python, once we fall below the limit we can resolve
everything at once with a single SQL query (or something along those
lines).

X-original-commit: 9e71094582ec4c9b719431e77538da8f91ffa9e3
Part-of: odoo/odoo#121522
2023-05-16 15:55:37 +02:00
Raphael Collet 58bd33ccde [IMP] core: make onchange2() work with properties fields
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.

closes odoo/odoo#120457

Signed-off-by: Raphael Collet <rco@odoo.com>
2023-05-05 18:08:10 +02:00
std-odoo dc95819257 [IMP] base: do not write in database when we have invalid properties names
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
2023-05-05 18:08:09 +02:00
f5e6494da3 [IMP] web: introduce onchange2
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.

closes odoo/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>
2023-04-28 14:44:00 +02:00
Chong Wang (cwg) 26b9b656c6 [FIX] core: allow write translation for non-existing record
before this commit:
    record = env['model.name'].browse(id)
if record doesn't exist in the database and call
   record.translated_field_name = value
Then _get_stored_translation will raise
   TypeError: 'NoneType' object is not subscriptable

after this commit:
like write non-translated field, the value can be written to the cache, but not
the database and no error will be raised.
Note: The feature is only for the original ORM 'write', if the 'overriden write'
reads other fields of the non-existing record, a MissingError will be raised.

closes odoo/odoo#119202

X-original-commit: 3ba7ca28a68acccb8eb25f117900c1aa1980264a
Signed-off-by: Raphael Collet <rco@odoo.com>
Signed-off-by: Wang Chong (cwg) <cwg@odoo.com>
2023-04-20 15:22:01 +02:00
Rémy Voet (ryv) cfbcc71c0a [FIX] core: sequence fields doens't have default group_operator
Part-of: odoo/odoo#110737
2023-04-19 21:58:26 +02:00
Rémy Voet (ryv) 603c16b1a0 [FIX] core,fleet: remove group_operator for Many2oneReference
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
2023-04-19 21:58:26 +02:00
Raphael Collet 4c5cdf5f09 [FIX] core: new() always rounds floats that are put in cache
When new records are used to precompute some fields, the computations
are expected to use the "right" values for floats, in particular float
fields are expected to be rounded.

closes odoo/odoo#118518

Signed-off-by: Raphael Collet <rco@odoo.com>
2023-04-17 16:34:47 +02:00
bve-odoo 6c2b889a28 [FIX] *: prevent warnings on runbot and sentry noisy tracebacks
As we have a team dedicated to work on analysing tracebacks
with sentry, and to takedown all tracebacks occuring on the saas,
we do an effort to avoid unecessary warnings and tracebacks
for both sentry and the runbot.

closes odoo/odoo#112101

Signed-off-by: Rémy Voet <ryv@odoo.com>
2023-02-27 17:45:35 +01:00
Chong Wang (cwg) 7e19621fbc [FIX] base: fix translate in dev xml mode
Before this commit:
in the --dev=xml mode, terms in ir_ui_view.arch cannot be correctly wrapped with
translation tags in the edit_translation=True context

After this commit:
The logic of get_trans_func is moved to _compute_arch of model ir.ui.view,
since ir_ui_view is its only use case and the hack
  with_context (edit_translation=None)
makes the logic very confusing as a method for Fields.

task-3225622

X-original-commit: 05ebdce14227b767bbddeab38e31f1eb747d9887
Part-of: odoo/odoo#117256
2023-03-31 15:52:36 +02:00
Julien Banken 88c256d8d8 [FIX] web: standardize behavior for int and float property fields
Currently, the property fields of type integer and decimal does not
behave like the standard fields. There are the following issues:

1. The property fields of type integer and decimal do not fallback to
   the value 0 or 0.0 when the field is emptied.
2. The user can not write the value 0 in the property field of type
   integer and decimal. The value gets discarded whenever the user
   unfocuses the input field.

This commit will fix those two issues and ensure that the property
fields have the same behavior as the standard fields.

task-3226202

closes odoo/odoo#117253

X-original-commit: 08c317743ba90802f471058a8f12d7211f9eb150
Signed-off-by: Thibault Delavallee (tde) <tde@openerp.com>
2023-03-31 09:48:46 +02:00
Chong Wang (cwg) f03e84da21 [FIX] core: allow translated sized char fields
in the commit 763fbb1c of #101115
an assert is added for translated sized char fields

It is too strict for some legacy customized fields, we decide to remove it

closes odoo/odoo#116538

X-original-commit: 44f09e927d0f9338a42a3f121377db00cc70f714
Signed-off-by: Raphael Collet <rco@odoo.com>
Signed-off-by: Wang Chong (cwg) <cwg@odoo.com>
2023-03-24 19:21:40 +01:00
Rémy Voet (ryv) 9b7c372602 [IMP] core: avoid queries when creating records with one2many values
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.

closes odoo/odoo#114726

X-original-commit: 428827e5213ee002a5b50d864f8ce1f24d1842b3
Signed-off-by: Raphael Collet <rco@odoo.com>
2023-03-09 15:54:53 +01:00
Raphael Collet be278780f7 [FIX] core: first onchange when adding a line in a one2many field
Consider a form view with a one2many field, which has no form subview.
Also the form view of the comodel (the one2many field's lines) contains
the inverse many2one field of the one2many field.  When adding a new
line on some existing record, the form view shows the many2one field as
empty, instead of being the main record.

Explanation: the form view of the line invokes onchange() with the main
record's values (dict) as the value of the many2one field.  Inside
onchange(), the field is actually set to a new record corresponding to
the main record.  Alas, when that value is sent back to the form, the
new record is serialized as False.

Solution: let onchange() serialize the new record as its origin record
instead.

closes odoo/odoo#114648

X-original-commit: d137ea4915da8d46909fcb082b5a88fd347874a4
Signed-off-by: Raphael Collet <rco@odoo.com>
2023-03-08 11:37:10 +01:00
std-odoo 1c2f29fc56 [IMP] base: allow to order record by properties fields values
Purpose
=======
Allow to sort records by properties fields values.

Task-2980121

Part-of: odoo/odoo#101901
2023-03-07 01:59:05 +01:00
std-odoo 0583a5ddd0 [IMP] base: search on properties fields
Purpose
=======
Allow to search properties fields.

For now, the properties fields are meant to be used with the frontend
top bar filter on views in a later commit, and so everything as been
kept as simple as possible for that particular use case.

Technical
=========
Relational properties
---------------------
We can not search on relational properties using the rec name, or
using one of their fields.

The following domains do not work
```
[('properties.partner_id', 'ilike', 'alice')]
[('properties.partner_ids', 'ilike', 'alice')]
[('properties.user_id.name', 'ilike', 'bob')]
[('properties.user_ids.name', 'ilike', 'bob')]
```

Instead, we should give the ids
```
[('properties.partner_id', '=', 55)]
[('properties.partner_ids', 'in', 55)]
```

IN operator
-----------
The "in" operator is used to either search specific tags / many2many
values, either doing a "OR like" condition on "non-array" value.

It can be used if the left or the right side is not an array
```
[('properties.my_char', 'in', ['a', 'b'])]
[('properties.my_tags', 'in', 'a')]
```

But it can't be used if both side of the condition are array, because
it will require to check type in SQL (which is technically possible,
but add extra complexity, so for now we keep the feature as simple as
possible).

So the following domain is not supported.
```
[('properties.my_tags', 'in', ['a', 'b'])]
```

Instead we should use the "OR" operator
```
['|', ('properties.my_tags', 'in', 'a'), ('properties.my_tags', 'in', 'b')]
```

Tags / many2many - "=" operator
-------------------------------
Tags and many2many have to be searched using the "in" operator.

So the following domain will return unexpected results and raise a warning.
```
[('attributes.mytags', '=', ['a', 'b'])]
```

The reason is that we don't want to dynamically check the type in SQL,
and so we need the "in" domain operator to know what to do in SQL.

Task-2980121

Part-of: odoo/odoo#101901
2023-03-07 01:59:04 +01:00
std-odoo da48700b59 [IMP] base: do not modify the cache when reading properties fields in batch
Purpose
=======
Do not modify the cache in order to improve the performance when we
read in batch. Instead we create a new batched method "convert_to_read"
that check existence in batch, and generate a dict with the result.

Now, all the properties field checks (many2one existence, selection option
still exists, tag value stiff exist, etc) and done in "convert_to_read".

It means that doing `record.properties` won't do all those checks.

Having the batched field fetch at the convert_to_record required too
many changes for the scope of this task, so "record.properties" having
the cache values unchecked is an acceptable tradeoff currently, hence
we batch convert_to_read.

Task-2980121

Part-of: odoo/odoo#101901
2023-03-07 01:59:04 +01:00
Raphael Collet 4e6f1d7805 [FIX] core: invalidate the cache of forbidden records when raising AccessError
Issue: checking access rules fetches some data in cache.  Sometimes,
accessing a forbidden record does not crash because of the data left in
cache.  This is the case when accessing a field on a record, like in the
field accessor method:

    try:
        records._fetch_field(f)     # (1)
    except AccessError:
        record._fetch_field(f)      # (2)

The field f is included in the data fetched to check access rules in the
prefetch set of record (1).  Therefore, when trying to fetch the same
field in (2), there is nothing to fetch and no access error occurs.

Part-of: odoo/odoo#112126
2023-03-05 15:12:55 +01:00
Raphael Collet e962860c6f [IMP] core: introduce search_fetch() and fetch()
This fulfills the goal of searching and fetching fields in a single SQL
query.  We introduce the new method search_fetch() for that purpose.
Also introduce method fetch() to fetch some fields for a recordset if
they are not in cache yet.

The call graph is as follows:

    search()        calls   search_fetch()
    search_read()   calls   search_fetch() and _read_format()
    read()          calls   fetch() and _read_format()

    search_count()  calls   _search()
    search_fetch()  calls   _search() and _fetch_query()
    fetch()         calls   _search() and _fetch_query()

The methods _search() and _fetch_query() are usually the ones to
override to implement business-specific logic.  The method _search()
returns a Query object to retrieve the records that satisfy the given
domain and are accessible for reading.  The method _fetch_query() uses a
Query object to retrieve fields from the database and store them in
cache.

Also use search_fetch() to save one query in search_read() and the
reading of one2many fields.

Part-of: odoo/odoo#112126
2023-03-05 15:12:55 +01:00
Raphael Collet 136eb34f07 [IMP] core: _search() no longer uses a default order
This simplifies the use of subqueries by avoiding some costly default
order on the model or the idiotic order='id'.  Method _flush_search()
has been adapted accordingly.

Part-of: odoo/odoo#112126
2023-03-05 15:12:54 +01:00
Rémy Voet (ryv) 529a363519 [REM] core: remove useless return value from Field.write.
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.

closes odoo/odoo#111108

Signed-off-by: Raphael Collet <rco@odoo.com>
2023-02-17 12:07:08 +01:00
Rémy Voet (ryv) 90b6334aca [REM] core: simplify _update of _RelationalMulti
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
2023-02-17 12:07:07 +01:00
Rémy Voet (ryv) b998d9a5de [REM] base: remove useless _remove_inverses of Many2oneReference
The `_remove_inverses` method of `Many2oneReference` class
has been unused since its introduction. Remove it.

Part-of: odoo/odoo#111108
2023-02-17 12:07:07 +01:00