Commit Graph
87 Commits
Author SHA1 Message Date
Rémy Voet (ryv)andWilliam Henrotin a62baec7f6 [FIX] core: fix onchange first snapshot
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>
2023-09-18 22:17:06 +00:00
Chong Wang (cwg) 506d0cc4ec [FIX] core: fix cached translations
before this commit:
translations updated by `update_field_translation` api cannot be detected by
t-cache and some cached data whose model overrides `write` with an extra
'clear_caches()'

Step to reproduce:
- Create a mega menu, select any template, `Odoo Menu` for the example
- Install another language on the website
- Go to the translated version of your website and enter translate mode
- Change "Camera" in the mega menu to something else
- Save

The change won't be replicated, looking like it did nothing.
From there, removing or adding `edit_translations=1` in the URL will
use different cache version of the page's views and you will see the
outdated value on one and the correct on the other one.

after this commit:
`update_field_translation` will call `write`
it does the following 4 important things
1. mark field as modified
2. execute logics in the override `write` method
3. update write_date if needed to support t-cache

opw-3305117

X-original-commit: 2beb466668e4eb80d7c3ca3947445fb1cb141cff
Part-of: odoo/odoo#135277
2023-09-13 12:19:10 +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
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
Raphael Collet fef1ce7eda [IMP] test_new_api: add more tests about _check_company()
The tests did not cover the cases where:
 - the main record has no company;
 - the linked record has no company.

closes odoo/odoo#129044

Signed-off-by: Raphael Collet <rco@odoo.com>
2023-07-20 05:02:58 +02:00
Raphael Collet 59afbf6686 [FIX] web: web_read() on new records with _inherits
Part-of: odoo/odoo#127718
2023-07-10 18:16:19 +02:00
Rémy Voet (ryv) 78165067a6 [FIX] core: fix semantic of 'not any' operator with many2one field.
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
2023-07-07 16:42:44 +02:00
Yannick Tivisse b1f7e56f79 [IMP] base: Remove private res.partner type
- Improve performances, as the ir.rule restricting private partners
  visibility is also applied on res.users by inheritance, on each
  prefetch.
- Solve the issue of partners set as followers on records (eg: application
  form) and then made private, making them impossible to contact via the
  chatter.
- Solve the multiple access issues when trying to access the bank
  account, or the private address for non HR people like the accountants
  forcing the usage of sudo in the business code.

TaskID: 3101400
2023-07-05 14:21:28 +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
Abdelouahab (abla) d10a40b14d [FIX] fields: read unstored one2many field without search function
> The bug is not present in 16.2+, but we keep the test

To Reproduce:
=============

- on a contact add a one2many field using studio
- in related field choose a field that is not stored and doesn't have a search function implemented
- close studio and notice all the lines of the selected field are listed on the contact even if are not linked to it

Problem:
========
- when searching a field that is not stored and doesn't have a search function, all the lines are returned

Solution:
=========
in this usecase filter the returned lines and only keep the ones linked to the record

opw-3265982

closes odoo/odoo#123563

X-original-commit: 7b29e9dff1d5ef97c5fb2a2ae0709bb21fd3fa3a
Signed-off-by: Rémy Voet <ryv@odoo.com>
Signed-off-by: abla001 <abla@odoo.com>
2023-06-05 14:50:03 +02:00
Tommy (tong) 685cb96ec2 [IMP] base: add support to company_dependent for html fields
Issue:

 Html fields cannot add company_dependent

Cause:

 Neven have html type for company property

Solution:

 Add html type inside ir.property

closes odoo/odoo#121243

Signed-off-by: Rémy Voet <ryv@odoo.com>
2023-06-02 22:28:52 +02:00
Ivan Yelizariev 101acf3773 [FIX] core: fix infinite loops with child_of/parent_of
1.

Infinite loop may happen on using `parent_of`\`child_of` when there is a
recursion in the tree (e.g. a record is marked as a parent of itself). Fix it by
excluding seen records from the next iteration.

2.

Another problem with `child_of` is `parent_id` that references to another model.
For example, the `parent_id` may come from inherited model. It's the case with
`res.users` and `res.partner` models. It may lead to a random search results.
Avoid that by raising exception in case of wrong usage of the `child_of`
operator.

STEPS:

In demo data, there is a partner called "Wood Corner" that is `res.partner(9,)`
that has 3 sub-contacts. If we give Portal access to two of them, we end up with
a database, where we have a `res.users(9,)` record that has a partner, which has a
`parent_id` to "Wood corner". So this way, the user id is the same as the user's
partner's parent contact id.

After that open a shell and type:

```
env['res.partner'].search([["user_ids", "child_of", 9]])
```

BEFORE: infinite loop (without change n.1) or random search results (when change
n.1 is applied)
AFTER: ValueError exception

---

opw-2729740

closes odoo/odoo#123353

X-original-commit: 2e1adc0c3e33fcf7989d27bb4d1c2e3c019faf2b
Signed-off-by: Ivan Elizaryev (iel) <iel@odoo.com>
Signed-off-by: Raphael Collet <rco@odoo.com>
2023-06-02 17:00:10 +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
Vincent Schippefilt 7d276aa941 [IMP] web: add reference fields to web_read
add support for fields of type `reference` and `many2one_reference` to `web_read` and `unity_web_search_read`

for both you can add a field_spec requesting fields of the "co-model":

request:
```python
{
    #reference
    'field_reference':
        {
            'fields': {'write_date': {}},
        }

    #many2one_reference
    'm2o_reference_id':
        {
            'fields': {'display_name': {}, 'write_date': {}},
        },
    'm2o_reference_model': {}
}
```
response:
```python
{
    'id': ...,
    #reference
    'field_reference': {
        'id': {'id': 3, 'model': 'comodel_name'},
        'write_date': '2004-11-23 11:30'
    }

    #many2one_reference
    'm2o_reference_id': {
        'id': 3,
        'display_name': "special first day",
        'write_date': '2004-11-23 11:30'
    },
    'm2o_reference_model': 'comodel_name',
}
```

closes odoo/odoo#119995

Task-id: 3284222
Signed-off-by: Raphael Collet <rco@odoo.com>
2023-05-04 00:46:53 +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
Vincent Schippefilt 7d2baaa0c7 [IMP] web: project unity_read
Introduce an optimised way of reading a graph of data from the webclient.

Before this commit:
When reading data from the webclient it could at most read multiple ids of the same model in one RPC.
This mean that when reading x2many or specific information on many2one, that could only be done after the initial read (when the client knows the ids of the comodels) and model by model.

After this commit:
Introduce methods web_read and web_search_read_unity. Both method receive a specificiation for the fields instead of a list of fields. The specification can request fields from the model, as well as follow relations and request fields for each relations, recursively.

Example of web_read specification for account_move
```python
{'name': {}},
{'date': {}},
{'journal_id': {'fields': {'display_name':{}}}
},
...
{'invoice_line_ids' :
    {
        'fields': {
            'journal_id' : {'fields': {'display_name:{}}},
            'move_name' : {},
            ...
            'tax_ids' : {
                fields: {
                    'display_name':{},
                    ...
                }
            }
        }
    }
}
```

Result for this example with 2 invoice lines
```python
{
    'id': 1234,
    'name' : 'invoice name ABC',
    'journal_id: {
        'id': 999,
        'display_name': 'Customer Invoices'
    },
    ...
    'invoice_line_ids': [
        {
            'id': 666,
            'journal_id': {
                'id': 999,
                'display_name': 'Customer Invoices'
            },
            'move_name': 'a move name',
            'tax_ids': [
                {
                    'id': 333,
                    'display_name: "15% tax",
                    ...
                }
            ]
        },
        {
            'id': 667,
            'journal_id': {
                'id': 999,
                'display_name': 'Customer Invoices'
            },
            'move_name': 'another move name',
            'tax_ids': [
                {
                    'id': 334,
                    'display_name: "21% customer tax",
                    ...
                }
            ]
        }
    ]

}
```

closes odoo/odoo#119034

Signed-off-by: Vincent Schippefilt (vsc) <vsc@odoo.com>
2023-04-21 08:02:17 +02:00
Louis Wicket (wil) 9afe7c74c9 [IMP] *: remove "French spacing" 👺
According to Wiktionary, French spacing is "the archaic practice (though
still current in French) of inserting a space around colons, semicolons,
question marks, and exclamation marks". This is not standard practice in
English and most languages of the world.

The purpose of this commit is to start purging the code from this typo,
as it may reflect poorly on the software for some people.

closes odoo/odoo#114533

Related: odoo/enterprise#37853
Signed-off-by: Sébastien Theys (seb) <seb@odoo.com>
2023-03-14 15:52:10 +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 ed762a3cef [FIX] core: field recomputed on more records than expected
The issue occurs when a computed field depends on a many2many field with
a corresponding inverse field on its comodel.  Consider two models like

class User(models.Model):
    _name = _description = 'test_new_api.user'

    group_ids = fields.Many2many('test_new_api.group')
    group_count = fields.Integer(compute='_compute_group_count', store=True)

    @api.depends('group_ids')
    def _compute_group_count(self):
        for user in self:
            user.group_count = len(user.group_ids)

class Group(models.Model):
    _name = _description = 'test_new_api.group'

    user_ids = fields.Many2many('test_new_api.user')

When a user is added to a group with

    group.write({'user_ids': [Command.link(user.id)]})

we expect the field `group_count` to be recomputed on `user` only, but
it is actually triggered on *all* the records in `group.user_ids`.  This
is a real performance issue when there are many records in the relation.

The explanation comes from the fact that
 - the framework considers the field `user_ids` is modified on `group`;
 - the field `group_count` implicitly depends on `group_ids.user_ids`,
   which makes it triggered on the users `u` such that `u.group_ids`
   intersects `group`.

The solution consists in handling the dependencies on inverse many2many
field in the field itself.  The field no longer adds the implicit
dependency on its inverse field in the trigger tree, but instead
determines which records in the comodel are actually impacted by the
relation change in the method field.write().

closes odoo/odoo#111943

X-original-commit: bb3a6e378b5f6b2e14b74ce4efb6d749ad54cb1e
Signed-off-by: Raphael Collet <rco@odoo.com>
2023-02-04 18:48:07 +01:00
Martin Trigaux a349b0a89b [IMP] test_new_api: move logger to top level
closes odoo/odoo#111764

X-original-commit: ca83a7879aed132ad2ff216405f745b5bd2ebb80
Signed-off-by: Martin Trigaux (mat) <mat@odoo.com>
2023-02-03 09:28:38 +01:00
Adrien Widart (awt) e762362ef0 [FIX] core: find currency when writing monetary field
When creating a record, if a value is related to a monetary field and if
the currency field is a non-stored related one, the value will not be
rounding: when writing the monetary value in the database, we first call
`convert_to_column`. In the parameter, `record` is empty (it is not yet
created) and `values` only contains the stored values (so the currency
value is not present). As a result, `currency` will not be defined and
the value will not be rounded.

OPW-2955202

closes odoo/odoo#106283

X-original-commit: 28196693b50b9d424887391ad13db69c5f5dfe4f
Related: odoo/enterprise#34265
Signed-off-by: Raphael Collet <rco@odoo.com>
Signed-off-by: Adrien Widart <awt@odoo.com>
2022-11-23 11:26:28 +01:00
Rémy Voet (ryv) 29e9d99364 [FIX] core: field indexed by trigram cannot become translated
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`

closes odoo/odoo#105295

Signed-off-by: Raphael Collet <rco@odoo.com>
2022-11-08 13:36:05 +01:00
Rémy Voet (ryv) 0787f150b1 [FIX] core: make index naming without conflicts
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

closes odoo/odoo#100736

Related: odoo/upgrade#3957
Signed-off-by: Raphael Collet <rco@odoo.com>
2022-11-07 15:57:28 +01:00
Rémy Voet (ryv) 1cfc896f2c [IMP] core: add Json field
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.

closes odoo/odoo#103097

X-original-commit: 7eeba9d205d2dace571b5d0895ddba6290a512db
Related: odoo/enterprise#32729
Signed-off-by: Quentin De Paoli <qdp@odoo.com>
2022-10-17 10:11:09 +02:00
Chong Wang (cwg) 25e4a53753 [FIX] core: fix search domain containing empty False None
For non-translated field, when search(['name', 'in', list_right])
The behavior of False and the behavior of None in list_right should be the same.

Translated fields need customized process for 'in' operator

X-original-commit: a9a3c666f08c5612432dba40a69c7cfdc54ba96e
Part-of: odoo/odoo#103031
2022-10-11 14:10:16 +02:00
Chong Wang (cwg) 105e0b9ef2 [FIX] core: search translated fields with characters needed to be escaped
The trigram index function jsonb_path_query_array("column_name", '$.*')::text
uses all translations' representations to build the indexed text. So the
original text needs to be JSON-escaped correctly to match it.

X-original-commit: 7547df664945dddcb839e4903068f7f25ecfc08c
Part-of: odoo/odoo#103031
2022-10-11 14:10:16 +02:00
Tom De CaluwéandRaphael Collet 3a4a7b161b [FIX] core: recompute fields triggered by indirectly modified relational fields
The cache currectly fails to correctly invalidate relational fields that depend
on a non-relational field. Two passes of invalidation are done, to reflect
dependencies on both the old and the new written values. In the first pass
only relational fields are considered, as explained in the comments:

> It is best explained with a simple example: consider two sales orders SO1 and
SO2. The computed total amount on sales orders indirectly depends on the
many2one field 'order_id' linking lines to their sales order.  Now consider the
following code:
>
> line = so1.line_ids[0]      # pick a line from SO1
> line.order_id = so2         # move the line to SO2
>
> In this situation, the total amount must be recomputed on *both* sales order:
the line's order before the modification, and the line's order after the
modification.

The written values can be seen as the roots of a dependency forest (a
collection of dependency trees). Before this commit all non-relational roots
and their corresponding trees were filtered out during the first pass. However,
this approach is wrong, as relational fields can also depend on non-relational
fields. Instead, the complete dependency forest has to be traversed, skipping
invalidation for non-relational fields during the first pass.

The test that was previously included accidentally succeeded because of a
separate and unrelated bug in the orm domain parser: in certain one2many or
many2many leafs the domain parser would not take into consideration the domain
included in the definition of the field. As a result, the test still passed
by accident, because the records that no longer matched the domain after the
write were still invalidated during the second pass.

The problem can clearly be demonstrated, however, when the dependency is
generated by a compute function.

closes odoo/odoo#101038

X-original-commit: d4a5827b42d80f0f830455dcd2056701eb09aed1
Signed-off-by: Rémy Voet <ryv@odoo.com>
Signed-off-by: Raphael Collet <rco@odoo.com>
Co-authored-by: Raphael Collet <rco@odoo.com>
2022-09-24 19:12:26 +02:00
ef00294e71 [IMP] core: store translated fields as JSONB columns
Translated fields no longer use the model ir.translation.  Instead they store
all their values as JSON, and store them into JSONB columns in the model's
table.  The field's column value is either NULL or a JSON dict mapping language
codes to text (the field's value in the corresponding language), and must
contain an entry for key 'en_US' (as it is used as a fallback for all other
languages).  Empty text is allowed in translation values, but not NULL.

Here are examples for a field with translate=True:

    NULL
    {"en_US": "Foo"}
    {"en_US": "Foo", "fr_FR": "Bar", "nl_NL": "Baz"}
    {"en_US": "Foo", "fr_FR": "", "nl_NL": "Baz"}

Like before, writing False to the field makes it NULL, i.e., False in all
languages.  However, writing "" to the field makes its value empty in the
current language, but does not discard the values in the other languages.

Here are examples for a field with translate=xml_translate:

    NULL
    {"en_US": "<div>Foo<p>Bar</p></div>", "fr_FR": "<div>Fou<p>Barre</p></div>"}

Change for callable(translate) fields: one can now write any value in any
language on such a field.  The new value will be adapted in all languages, based
on the mapping of terms between languages in the old values.  Basically the
structure of the value must remain the same in all languages, like before.

Reading a translated field is now both simpler and faster than the former
implementation.  We fetch the value of the field in the current language by
coalescing its value with the 'en_US' value of the field:

    SELECT id, COALESCE(name->>'fr_FR', name->>'en_US') AS name ...

The raw cache of the field contains either None or a dict which is conceptually
a subset of the JSON value in database (except for missing languages).  For the
sake of simplicity, most cache operations deal with the dict and return the text
value in the current language.

Trigram indexes have been adapted to the new storing strategy, and should enable
to search in any language.  Before this change, only the source value of the
field ('en_US') could be indexed.

Computed stored translated fields are not supported by the framework, because of
the complexity of the computation itself: the field would need to be computed in
all active languages.  We chose to not provide any hook to compute a field in
all languages at once, and the framework always invokes a compute method once to
recompute it.

Code translations are no longer stored into the database.  They become static,
and are extracted from the PO files when needed.  The worker simply uses a cache
with extracted code translations for performance.  This is reasonable, since
fr_FR code translations for all modules takes around 2MB of memory, and the
cache can be shared among all registries in the worker.  Changing code
translations requires to update the corresponding PO file and reloading the
worker(s).

Performance summary:
 (+) reading 'model' translated fields is faster
 (+) reading 'model_terms' translated fields is much faster (no need to inject
     translations into the source value)
 (+) searching translated fields with operator 'ilike' is much faster when the
     field is indexed with 'trigram'
 (+) updating translated fields requires less ORM flushing
 (-) importing translations from PO files is 2x slower

Some extra fixes:
 - make field 'name' of ir.actions.actions translated; because of the PG
   inheritance, this is necessary to make the column definition consistent in
   all models that inherit from ir.actions.actions.
 - add some backend API for the web/website client for editing translations
 - move methods get_field_string() to model ir.model.fields
 - move _load_module_terms to model ir.module.module
 - adapt tests in test_impex, test_new_api
 - because env.lang is injected into SQL queries, its returned value is
   now guaranteed to correspond to a valid active language or None
 - remove wizard to insert missing translations (no longer makes sense)

task-id: 2081307

Co-authored-by: Fabien Pinckaers <fp@openerp.com>
Co-authored-by: Raphael Collet <rco@odoo.com>
2022-09-15 22:37:50 +02:00
Rémy Voet (ryv) 54d75a28cd [IMP] test_new_api: add performance test for modified.
Part-of: odoo/odoo#99274
2022-09-02 23:16:06 +02:00
std-odoo 87307a9010 [IMP] base: add new "Properties" fields
Purpose
=======

Add a new field "Properties" to be able to light customization of workflows
based on a parent model. Those properties acts in some ways like Odoo fields
without requiring specific columns e.g. add new properties on tasks of a
specific project.

Usage
=====

Define properties on a parent model (e.g. project) with

```
    attributes_definition = fields.PropertiesDefinition('Message Properties')
```

It defines properties available on children: types, default, value, model for
relational properties,...

Use it on children records (e.g. task) with

```
    attributes = fields.Properties(
        string='Properties',
        definition='parent_id.attributes_definition',
    )
```

Technical
=========

Parent | Properties definition
------------------------------
The properties definition is stored on the parent, on a JSON field.
This definition contains the type of the properties, the default value,
the model of the many2one,...
```
[
    {
        'name': 'name',
        'string': 'Name',
        'type': 'char',
        'default': 'Default Name',
    }, {
        'name': 'partner_id',
        'string': 'Partner',
        'type': 'many2one',
        'comodel': 'res.partner',
    },
]
```

Child | Properties values
-------------------------
The value is stored on the child, using a Properties field.
```
{
    'name': 'Mitchel',
    'partner_id': 1337,
}
```

When we read this field, we will automatically read the definition on
the parent, and merge both JSON into one, so the web client has the
value of each property, and their definition.

```
[
    {
        'name': 'name',
        'string': 'Name',
        'type': 'char',
        'default': 'Default Name',
        'value': 'Mitchel',
    }, {
        'name': 'partner_id',
        'string': 'Partner',
        'type': 'many2one',
        'comodel': 'res.partner',
        'value': 1337,
    },
]
```

Integrity
---------
If we remove a property on the parent, we won't update the child value.

Instead, when we read the child properties, we will filter them based
on the parent. So the removed properties will be removed the next time
we write on the field.

In the same logic, the many2one existence is checked when we read the
field. There's no foreign key between the integer stored in the JSON
in the SQL row corresponding to the record in database.

Write
-----
We can write on the Properties field with a list of field definition
+ value.

Some types are not JSONifiable (like the date, datetime), they are
stored as string in database and parsed when we read the value.

In order to update the parent definition by writing on the child,
you need to add the dict key `definition_changed` or
`definition_deleted`. This is because we need to be able to know
if the definition has been changed without doing extra SQL queries.

Access rights
-------------
A user can add a many2one / many2many property to a model only if he
has the access rights to it.

Many2one / Many2many
--------------------

The model choice of a many2one / many2many properties was subject to
changes.

First implementation stored models in both parent and children to easily
spot changes and avoid complex queries when fetching records, trying to
synchronize them, ...

As this leads to storing a lot of duplicated content we choose to instead
reset the value on the child if the model has been change. We generate a
new name for the property. So it behaves like if we removed the property
and created a new one.

To be able to restore the old value (e.g. if by mistake we changed the
model, and go back to the old model), we store the initial states.

Task-2852259

Part-of: odoo/odoo#95184
2022-08-29 23:46:06 +02:00
Romain Derie cf844e34dd [IMP] core: allow some users to bypass the sanitize of HTML field
Add the possibility to flag a HTML field as `sanitize_overridable`.
The sanitizer will then be bypassed if the user doing the operation is
part of the new group `base.group_sanitize_override`.
The "Settings" users are part of that new group.

If such a user wrote some HTML that would have normally been removed by
the sanitizer, then users without the right to bypass the sanitizer
won't be able to write on that field anymore.
Otherwise, it would sanitize the previously written data.
Such cases are detected, and the modification prevented by the system,
which will warn the user about it.

For instance, with a `field.html(sanitize_overridable=True)`: one being
part of `group_sanitize_override` could write `<script></script>`.
Then, someone not part of the group trying to add an element inside that
field like appending a `<p/>` -> `<script></script><p>New Content</p>`
would not be able to because it would go through the sanitizer and
ultimately, removing part of the original value:
`<p>New Content</p>` (`<script></script>` would be removed).

== Real use case ==
In the website builder, there is 2 editor rights:
- group_website_publisher: restricted editor
- group_website_designer: editor & designer

The designer editor can edit pages and views, while the restricted
editor can't do anything unless he is part of other groups.
In edit mode, the restricted editor will only be able to edit fields of
record he has access to.
For instance, being a sales manager allows you to edit a product
description on the website.
Being an event manager -> edit event. Slide manager -> Slides etc.
As those restricted editor are able to edit those fields, they are
(almost) always sanitized to prevent them to introduce malicious code.

Since those fields are sanitized, even admins / designer editor are not
able to fully use the website builder in such fields.

Some clients don't really care about that sanitation, they'd prefer to
avoid it as they trust their manager and would prefer to have the full
builder capability instead.
This is typically the case in small project (butcher, hairdresser,
reseller etc) and in SMEs.

With the new `sanitize_overridable` feature, they will be able to do
that, as the "Designer & Editor" group now also receive the group
`base.group_sanitize_override` (done in next commit).

Part-of: odoo/odoo#97398
2022-08-24 23:03:23 +02:00
Fabien Pinckaers 3363e55cac [IMP] cleanup of help messages in all modules
This commit aims at removing unuseful help message to:
1/ reduce translators work, to focus on more useful translations
2/ not sending unuseful information in load_views
3/ reduce help message to useful messages, so that we can mark
   fields having a tooltip in the future UI.
4/ some cleanup of existing messages too

The main use cases:
- REMOVED: help redundant with the field name, providing no extra info
- MOVED TO COMMENT: technical help messages, that should not be in UX

closes odoo/odoo#97279

Signed-off-by: Fabien Pinckaers <fp@odoo.com>
2022-08-02 00:26:53 +02:00
Denis Ledoux f2f5ce7790 [IMP] repair: convert repair uom and location onchanges to compute
This allows to create a repair.order record without
the need to call the onchanges to set the uom and locations
or to set them manually during the `create` call.

For instance, this makes easier to create repair orders
using XMLRPC when you do not use multiple UOMs or multiple locations.

closes odoo/odoo#95321

Signed-off-by: Raphael Collet <rco@odoo.com>
2022-07-07 17:30:35 +02:00
Raphael ColletandVincent Schippefilt eb67feb590 [FIX] *: cache consistency
In module mail, invalidating 'message_ids' on a mail thread also
invalidates its inverse field 'res_id' on messages.  If you haven't
flushed it before, your cache will be inconsistent, as shown by the test
/mail:TestMailgateway.test_message_process_bounce_records_channel.

In module purchase_stock, add depends on report.stock.quantity.  This
ensures that when the model is queried after changes in other models,
the data on which the SQL view depends is flushed to the database before
querying that model's table.

closes odoo/odoo#66938

Related: odoo/enterprise#16722
Signed-off-by: Raphael Collet <rco@odoo.com>
Co-authored-by: Vincent Schippefilt <vsc@odoo.com>
2022-07-05 11:35:01 +02:00
Raphael Collet 6cf8db906f [REF] *: adapt code to new flush API
closes odoo/odoo#87527

Related: odoo/upgrade#3497
Related: odoo/enterprise#26939
Signed-off-by: Raphael Collet <rco@odoo.com>
2022-05-25 18:00:47 +02:00
Raphael ColletandDidier Debondt 036ef5b96d [FIX] core: use EXISTS in domain conditions on many2many fields
This fixes two problems that occur with conditions on many2many fields:
the subtle bug caused by NULL values that are returned by subqueries,
and the performance of those subqueries.

The problem with NULL values returned by subqueries is the fact that
they make the SQL condition undetermined instead of false, and this
discards some of the results returned by a query.  Simply consider:

    id NOT IN (1, 2, 3)

The value id=3 makes the condition false, while i=4 makes it true.  Now
consider that the set also includes a NULL value, like:

    id NOT IN (1, 2, 3, NULL)

The value id=3 makes the condition false, but the value id=4 makes it
undetermined.  Therefore this condition is actually undetermined for all
possible values of id, and a query containing that condition simply
returns nothing!

Regarding performance, consider a domain like [('tag_ids', '=', False)].
It is currently translated in SQL as

    "model".id NOT IN (
        SELECT "model_id"
        FROM "model_tag_rel"
        WHERE "model_id" IS NOT NULL
    )

PostgreSQL does not handle well this "NOT IN" expression when the
relation table is large, and uses some very inefficient query plan in
that case.  The inefficient query plan is possibly related to the
special handling of NULL values, by the way.  This commit changes the
above expression to a "NOT EXISTS" expression (as below), which is
executed with a much more efficient query plan, even with large tables.

    NOT EXISTS (
        SELECT 1
        FROM "model_tag_rel"
        WHERE "model_tag_rel"."model_id" = "model".id
    )

This change was motivated by a use-case with 2.7M account moves, 11M
account move lines and a many2many relation table with 2.3M lines
(`account_move_line_account_tax_rel`).  The query was timing out (15
minutes), and it now takes 15 seconds.

We have replaced all the conditions on many2many fields to use the
relational operators EXISTS and NOT EXISTS.

A test has been added to reflect a case where the many2many table
contains NULL values, like the one for channel_partner in regards to
partner_id and guest_id.

task-2753782

X-original-commit: 334b2aa2aa437d2ed86130f2ed426025f8fe4b86
Part-of: odoo/odoo#87285
Co-authored-by: Didier Debondt <did@odoo.com>
2022-03-25 17:47:43 +01:00
Rémy Voet (ryv) e1785e820a [IMP] base: add prefetching group feature
The field prefetching mechanism was poorly customizable.  Before this,
we could only tell if a field was prefetched with other fields or not at
all.  We have no way to inform the framework, like: "When I need data of
that field, prefetch these other fields, which are likely be used in the
same transaction".

From now on, the `prefetch` attribute is used as a grouping key for
prefetching fields.  When a field is fetched, all the fields with the
same value for `prefetch` are taken for prefetching.

For example, consider a small set of fields that are rarely used, except
for one flow A using them.  You want to prefetch those fields only in
the flow A, and you want to fetch them in a single query.  With the new
feature, simply set `prefetch=A` for some string `A` on those fields,
and they will be grouped for prefetching.

closes odoo/odoo#85220

Signed-off-by: Rémy Voet <ryv@odoo.com>
2022-03-03 11:03:55 +00:00
Julien Castiaux e655e8da8a Revert "[IMP] base: add prefetching group feature"
This reverts commit 041fe5e21e.

Part-of: odoo/odoo#85199
2022-02-23 12:59:52 +00:00
Rémy Voet (ryv) 041fe5e21e [IMP] base: add prefetching group feature
The field prefetching mechanism was poorly customizable.  Before this,
we could only tell if a field was prefetched with other fields or not at
all.  We have no way to inform the framework, like: "When I need data of
that field, prefetch these other fields, which are likely be used in the
same transaction".

From now on, the `prefetch` attribute is used as a grouping key for
prefetching fields.  When a field is fetched, all the fields with the
same value for `prefetch` are taken for prefetching.

For example, consider a small set of fields that are rarely used, except
for one flow A using them.  You want to prefetch those fields only in
the flow A, and you want to fetch them in a single query.  With the new
feature, simply set `prefetch=A` for some string `A` on those fields,
and they will be grouped for prefetching.

Part-of: odoo/odoo#83818
2022-02-23 10:01:59 +00:00
Yannick TivisseandRémy Voet c983ae0fa2 [FIX] core: update cache on parent_path when updated
Purpose
=======

The parent_path is invalidated from cache when computing it from scratch
(with method `_parent_store_compute`) but not when it is recomputed
(method `_parent_store_update`).

The issue is that a computed field depending on 'parent_path'
won't receive the updated value using the cache, and could lead
to inconsistencies if the developer is not aware of that.

Specification
=============

On `_parent_store_update`, set the new value of 'parent_path' in the
cache before marking the records as modified.

task-2766452

closes odoo/odoo#84795

X-original-commit: 861854a7598f31c0b971560484e3eb576af006b4
Signed-off-by: Raphael Collet <rco@odoo.com>
Signed-off-by: Rémy Voet <ryv@odoo.com>
Co-authored-by: Rémy Voet <ryv@odoo.com>
2022-02-17 16:41:47 +00:00
Victor Feyens 856c1691c7 [IMP] core: improve test coverage of selection fields
Part-of: odoo/odoo#83104
2022-02-08 11:07:46 +00:00
Victor Feyens a6c786090c [IMP] test_new_api: test precompute monetary fields
Part-of: odoo/odoo#81423
2022-02-07 14:17:50 +00:00
Rémy Voet (ryv) 5a573c6f18 [IMP] base: don't prefetch translate field by default
Issue
-----
Via the field prefetch mechanism, when we need a value of one field
(not in cache of course), the ORM will prefetch all fields
(which has the attribute to `prefetch=True`, the default value of this
attribute is `True`) for all record ids in `_prefetch_ids`.
Then, for each translate fields (where translate is not a callable)
the ORM need to make a `LEFT JOIN` on the `ir_translation` to fetch the
translated value. For big model, it leads to a simple `SELECT` with
several `LEFT JOIN` on ir_translation but each LEFT JOIN have a cost
in the planner time (a small cost in the execution time) of PostgreSQL.

By example, for `product.template` (stock/sale/purchase installed),
there are 6 LEFT JOIN to get all translated fields (5 of this
fields are rarely used).

Proposed solution
-----------------
Deactivate the prefetch by default for all translate fields expect if
this field is the `_rec_name` of the model (which is more likely to
be used).

In the example on the `product.template`:
Without prefetching the translated fields, there is only one LEFT JOIN
(the name, which is translated but is the `_rec_name` of the model).
With the 6 translated fields to fetch, the
query takes 5 ms to plan and 2 ms to execute VS with 1 translate field,
it 1 ms to plan and 1.5 ms to execute.

Side change note
----------------
- All translate of fields of `website.seo.metadata` should be prefetch
to avoid lot of website errors (it is because, website put in cache data
in sudo before reading it without sudo)
- `description` (`mail.message.subtype`), `subject` (`mail.template`),
`body_html` (`mail.template`) should be prefetch to avoid lot of extra
query from mail module.
- `vat_label` (`res.country`) should be prefetch to avoid a extra query
for each website page.
- Increase some queryCount (when it is legit, due to `subtitle` of
`blog_post` or `description` of `event.type.ticket`, etc)

task-2738029

closes odoo/odoo#82896

Signed-off-by: Raphael Collet <rco@odoo.com>
2022-01-28 14:09:56 +00:00
Raphael Collet 6af689fd0a [FIX] base: fix search on company depend fields
This is a bunch of fixes (65 corner cases) for the search on
company-dependent fields:

Without a default value:
- `char` fields:
    - operators `not like`/`not ilike` don't return records with unset value
    - `(..., '=', False)` doesn't return records with unset value
    - `(..., '!=', '<string>')` doesn't return records with unset value
    - `(..., 'in', [..., False])` doesn't return records with unset value
    - `(..., 'not in', value)` without `False` inside `value` doesn't return records with unset value
- `date` and `datetime` fields:
    - `(..., '!=', <Date/datetime>)` doesn't return records with unset value
    - `(..., '=', False)` doesn't return records with unset value
- `many2one` fields:
    - operators `not like`/`not ilike` don't return records with unset value
    - `(..., 'in', [..., False])` doesn't return records with unset value
    - `(..., 'not in', value)` without `False` inside `value` doesn't return records with unset value
- `boolean` fields:
    - `(..., '=', False)` and `(..., '!=', True)` don't return records with unset and `False` values
- `integer`/`float` fields:
    - `(..., '!=', <number>)` doesn't return records with unset value

With a truthy default value:
- `many2one` fields:
    - operators `not like`/`not ilike` don't return records with unset value
    - `(..., '=', False)` returns the record with the default value (which isn't `False`)
- `boolean` fields:
    - `(..., '=', False)` doesn't return records with unset and `False` value
    - `(..., '!=', False)` returns records with unset and `False` value
- `integer`/`float` fields:
    - all `(..., operator, value)` which include value 0, return all records with unset value, even if the default value does not satisfy the domain

closes odoo/odoo#80994

X-original-commit: 3e3be652ece83420782070bdb13da2c3a4930046
Signed-off-by: Raphael Collet <rco@odoo.com>
2021-12-07 17:10:48 +00:00
d04a5b5c8c [REF] core: compute fields before database insertion.
Until now, stored compute fields were computed after database insertion.
This meant that required fields should not be computed, for instance,
unless some hackish code was added to make it work.  Another trick was
to provide some default value, but this actually prevents the field for
being computed after insertion.

This commit provides a field parameter to specify that the field should
be precomputed: adding precompute=True on the field definition force the
method create() to compute its value before inserting the new record in
the database.  For the reason explained below, precomputing fields is
not always correct, and therefore the default remains to not precompute
a field.

Some stored fields must be computed after insertion, for instance:
* statistics fields computed with search/read_group/...
* fields referencing the current record (res.partner.commercial_partner_id)
* fields referencing another record that does not exist yet (think about
  records created by one2many fields)
* fields depending on the create_date/write_date/create_uid/write_uid

Those fields shouldn't be defined with precompute=True, which triggers
their computation post record creation.  This is why, by safety, we
consider the default behavior to be precompute=False.

closes odoo/odoo#80449

Signed-off-by: Raphael Collet (rco) <rco@openerp.com>
Co-authored-by: Raphael Collet <rco@odoo.com>
Co-authored-by: Yannick Tivisse <yti@odoo.com>
2021-11-30 14:30:48 +00:00
Raphael Collet aa0cdacced [FIX] core: related field attributes
A related field copies the attributes from its target field, except for
the attributes defined on the related field itself.  The implementation
of this feature was not working properly for attributes with a truthy
default value.

closes odoo/odoo#79025

X-original-commit: dec4a7ec478fa02f19dee8c8426c88d17dc3c7f3
Signed-off-by: Raphael Collet (rco) <rco@openerp.com>
2021-10-26 16:06:48 +00:00
Raphael Collet 56660e39e6 [FIX] core: one2many linking non-existing lines
The processing of the LINK command in a one2many should fail when the
line being linked does not exist.  However, there is one case where it
should not fail: when that line existed and was linked before applying
the commands.  That use-case may seem strange, but it actually exists:
deleting a line in a sales order automatically deletes the corresponding
reward lines.

closes odoo/odoo#78318

X-original-commit: 4bfe1b5cab10d3171d386d76799a7d7729f49154
Signed-off-by: Raphael Collet (rco) <rco@openerp.com>
2021-10-13 18:24:42 +00:00
william-andre d3f4ce1152 [IMP] core: allow set [value] in selection ondelete
In some cases we migh want to change to a value that isn't the default
one when uninstalling.
For instance: when we uninstall the module `event_sale`, a product has
the field `detailed_type` set to `event`, which is a subtype of `service`.
So we want to update the value to `service` when uninstalling instead of
the default value, which would be `consu` and wouldn't make any sense.

Part-of: odoo/odoo#77876
2021-10-11 10:15:48 +00:00
Xavier Morel 5e81361f80 [FIX] *: opt parent_path fields out of unaccent
closes odoo/odoo#76436

Related: odoo/enterprise#20822
Signed-off-by: Xavier Morel (xmo) <xmo@odoo.com>
2021-09-20 12:53:01 +00:00
Raphael ColletandWilliam André 71fc2e55f9 [FIX] core: inherited computed fields in onchange
Assume that model A has a computed field G that depends on both fields
F1 and F2.  Assume that model B inherits from A (_inherits).  When G is
computed during an onchange, it must use the form values for all its
dependencies F1 and F2.  Before this commit, only the field triggering
the onchange was assigned on the parent record (see (*) below.)

                      record        parent
    --- ------------+-------------+------------
     initial state  | F1=0, F2=0  | F1=0, F2=0
     assign F1=1    | F1=1, F2=0  | F1=1, F2=0
     assign F2=2    | F1=1, F2=1  | F1=0, F2=1 (*)

The commit also includes another patch: when assigning the parent
record, the ORM determines the dependent records (in this case, the main
record in the onchange).  If the delegate field (many2one from B to A)
has an inverse one2many field, the ORM uses that field to determine
dependent records.  However, in the case of a new record, that field is
not in cache, and its value is determined to be... empty!  The patch
consists in "fixing" the value of that field when we determine the
parent record (when the delegate field is accessed.)

Part-of: odoo/odoo#76042
Co-authored-by: William André <wan@odoo.com>
2021-09-06 18:29:03 +00:00