601eb8f144cebc0590c8f00a2986f615f52b0dfd
9
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
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> |
||
|
|
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
|
||
|
|
b03c227e88 |
[REF] models: refactor fields_view_get, load_views
Refactor the `load_views` API so it no longer sends multiple times the same fields description. e.g. When `load_views` is called to get the kanban, tree and form views, the list of fields of the model was sent 4 times: - Once for each view, with only the fields used in the view, in `['fields_views']['kanban']['fields']` for instance - Once globally, with all the fields of the model, in `['fields']` The goal of this revision is to change that so it sends the list of all fields only once. In addition, if a view contains x2many fields, the fields description of the comodel is also sent. It was sent in the `views` key of the view fields dict. e.g. When calling `load_views` of `res.partner` to get the kanban, tree and form views, the `res.partner` fields description was actually sent 6 times: - Once for each view - Once globally - Once for each view of the many2many field `child_ids` of the form view, in - `['fields_views']['form']['fields']['child_ids']['views']['kanban']['fields']` - `['fields_views']['form']['fields']['child_ids']['views']['form']['fields']` The change suggested in this revision is to: - Remove the fields description for each view in `['fields_views']`. As it no longer contains the fields, the key becomes `['views']` instead of `['fields_views']`. - Replace the dict key `['fields']` by `['models']`, which is a dict with as key the model name and as values the model fields description. It contains the fields description for all models implied in the view: the model of the main view and the model of all one2many and many2many fields. With this change, the fields description will only be sent once by model implied in the view. In addition, the web client was getting the information about the fields sometimes in the global fields description list (e.g. `['fields']`), sometimes in the fields description list of the view type (e.g. `['fields_views']['form']['fields']`), making it a pain to try to make changes / performance gain in these field description dictionaries, because you never knew in which dict the web client was getting its info. Now, as there is only one place to get the fields description from, it's clearer and cleaner. - one2many and many2many fields views are passed directly in the main view architecture rather than being put in the `views` key of the field description. This is actually easier to treat by the web client, and this will allow in a future work to cache an entire view in one block of text rather than having to combine multiple cached blocks of text to return one view. - one2many and many2many fields which do not have directly embedded views have their views directly injected in the architecture, so the web client doesn't have to do RPC calls to `load_views` for each one2many and many2many fields not having embedded views. For instance, this allow to reduce the number of RPC calls to `load_views` from 8 to 1 when loading the form of `product.product`. Currently, this behavior is limited to 1 level deep but we consider making it go all the way down in future works. We did not do it for the moment because in certain cases it rises the processing time and the size (bytes) too much. e.g. the sale.order view can be 5 levels deep, meaning you can reach 4 dialogs on top the main view. ``` sale.order form > order_line > sale.order.line form > invoice_lines > account.move.line form > asset_ids > account.asset form > depreciation_move_ids > account.move form. ``` This will also benefit in future works to cache an entire view in one block of text rather to having to combine multiple cached block of text to get one view. - `fields_view_get` becomes `get_view`. As it no longer returns the fields description, keeping the `fields` in the name `fields_view_get` no longer makes sense. Hence removing `fields` from the method name, it becomes `view_get`. As it gets renamed anyway, we take the opportunity to rename it `get_view`, which is more in line with the general getter/setter guidelines in the model object world. - `_fields_view_get` becomes `_get_view`. For the same reasons than above. - `load_views` becomes `get_views`. This is not mandatory, there is no technical reason to rename `load_views` as it practically sends the same info as before, the view architectures and their fields description. Just in another way. We just take the opportunity of this pull request to suggest a cleaner API: `_get_view`, `get_view` and `get_views`. - Arguments `toolbar=False, submenu=False` fo the methods `_fields_view_get` and `fields_view_get` are converted to a kwargs `**options` in `_get_view` and `get_view`. The rationale is that submenu was already no longer used (deprecated) and the mobile options is introduced. The mobile options is necessary to tell the server to send the mobile views for x2many fields (kanban instead of tree). Instead of adding a new argument each time we add a new option to `fields_view_get`, it seems wiser to have a kwargs `**options` to avoid to re-write all overrides each time a new option is introduced. - `_fields_view_get` returned a dict containing the arch in text and some of the view information. Now, `get_view` returns a tuple with the view architecture as an `etree` node, and the view as a browse record. The rationale is that all overrides of `_fields_view_get` were about modifying the arch only (e.g. changing the address format/re-organizing the address related field nodes of the partner according to the company country). To do so, all these overrides were doing `etree.fromstring` to parse the arch which was sent in text to convert it to an `etree`, then operations were done on the `etree`, and then `etree.tostring` was called to convert back the arch to string. With this change of signature to send the arch as an `etree`, all these back and forth `etree.fromstring` -> `etree.tostring` are avoided, allowing some performance gain and less code in the end. - A cleanup of the keys returned in the dict of `fields_view_get` has been performed in `get_view`: - `fields` is removed, as explained above, - `view_id` is renamed `id`, - `name` is removed, it was unused by the web client, - `type` is removed, it was unused by the web client, - `field_parent` is removed, it was unused by the web client, - `base_model` is removed, it was unused by the web client. - `filters` is moved from the global dict returned by `load_views` (now `get_views`) to the dict returned by `fields_view_get` (now `get_view`) as it applies only to the `search` view type. - Retro-compatible methods for the 3 methods `fields_view_get`, `_fields_view_get` and `load_views` are provided, with deprecation warnings in them. - The web client could cache the model fields description (as it already caches the views), so it doesn't need to fetch them again if it asks for another view of a model for which he already has the fields description. If we do so, `get_views` could return only the list of models used by the views, without the fields description as of now, and the web client would then call `fields_get` independently only for the models for which it doesn't have yet the fields description. This would avoid the server to return the fields description and to call `fields_get`, which is costly, for each `get_views`, therefore gaining performances. - Inject the views of the one2many and many2many fields all the way down, unlimited depth level, as explained above. - Cache with `ormcache` the architecture of back-end views. This is already done for qweb views, it's not done for back-end views. Therefore the postprocessing of the views is performed for each `get_views`, which is costly, while the view architecture doesn't change for users belonging to the same groups, according to the groups implied by the view. This pull request is co-authored by Aaron Bohy (aab) for the web client part and Denis Ledoux (dle) for the server part. Part-of: odoo/odoo#87522 |
||
|
|
03641610c2 |
[REF] *: convert all modules to new asset system
Conversion of all modules to the new manifest assets declaration. Part of task: 2352566 Co-authored-by: Julien Mougenot <jum@odoo.com> Co-authored-by: Simon Genin <ges@odoo.com> |
||
|
|
6f2a6f7f2e |
[FIX] core: invalidation of computed editable field in one2many
This fixes the issue introduced by revision
|
||
|
|
ca903577d2 |
[REF] test_*: Use Command helper for x2many
Task: 2366606 |
||
|
|
62355b6ad3 |
[FIX] test_new_api: Fix bad shared cache usage for stored computed fields
Purpose
=======
Since the 13.0, a regression has been introduced in hr_timesheet.
Considering:
- The account.analytic.line model, representing a timesheet entry
- The project.task model, representing a task, on which we have the fields:
- timesheet_ids: One2many
- planned_hours: Computed (sudo), stored, representing the sum of the timesheet amounts
- An ir.rule restricting the timesheets CRUD to his own only.
A user can only see its own timesheets on a task, but the field "Planned Hours",
which is stored-compute_sudo, should take all the timesheet lines into account
However, when adding a new line and then recomputing the value, no existing line
from another user is binded on self, then the value is erased and saved on the
database.
Specification
=============
This commit introduces a test that illustrates that bad usage of a shared cache.
A correct way to fix this could be to avoid using the cache if an ir.rule is pointing
to the related model. That would force the recomputation in a real sudoed environment.
closes odoo/odoo#55408
Taskid: 2285924
X-original-commit: 50e229fe66b2a82717d4260287e18a875bbd26f5
Signed-off-by: Yannick Tivisse (yti) <yti@odoo.com>
|
||
|
|
fd50ba9fb8 |
[IMP] core: do not recompute stored field on new record
The use case is a cache miss of a stored field with compute on a new record with origin. The field should be computed only when explicitly triggered, i.e., when a dependency has been modified. Otherwise it should be fetched from the origin record. closes odoo/odoo#47353 Signed-off-by: Raphael Collet (rco) <rco@openerp.com> |
||
|
|
4aa5ba35af | [IMP] base/test_*: Adapt tests to work with/without demo data |