The goal of this lib is to play the onchange methods to get values for the fields not yet filled into the received list of values used to createor update a record. Therefore, the values from the user must be preserved. If we don't process on this way and you play the onchange methods on a sale.order for a new line with 'product_id' and 'product_uom_qty', the product_uom_qty will be reset to 1 when the onchange for product_id will be played and we'll lost the initial value for product_uom_qty
Before this change, the inmemory record was populated from values copied from the exisitng record.
It was no predictable to know in advance which values were required to correctly play onchange methods. Therefore all the values defined on the model were copied from the exising record to populate the inmemory record. The side effect of this approach was that in a lot of cases, a lot of useless values was copied leading to performance issue with computed fields.
With this change, we use the current record to call the onchange methods in an onchange context to avoid direct write to the database each time a new value is assigned by an onchange. At the end of the process, we restore the current record to its original state and return a dictionary with only the fields modified by the onchange.
We must avoid to rely on the order in which computed fields (including related fields) and constrains methods are applied.
Due to a recent change into the ORM, the contrains on the model_id into CustomInfoTemplate is now called AFTER the recompute of the related model field into property_ids.info_value_ids
As side effect, when the constrains is called, the model on the info value is already updated with the new value and we no more know the old value....
The goal of the modified method is to create or remove the relationship
(in the M2m relation tabel) between the tested model (such as
sale_order) and the exception rules. When the ORM writes on
ExceptionRule.sale_ids (using the example of sale_exception), it will
first proceeds with these updates:
* an UPDATE on exception_rule to set the write_date
* INSERT or DELETE on the relation table
* but then, as "write" is called on the exception rule, the ORM will
trigger the api.depends to recompute all the "main_exception_ids"
of the records (sales, ...) related to it, leading to an UPDATE
for each sale order
We end up with RowExclusiveLock on such records:
* All the records of the relation table added / deleted for the current
sale order
* All the records of exception_rule matching the current sale order
* All the records of sale_order related to the exception rules matching
the current sale order
The first one is expected, the next 2 are not. We can remove the lock on
the exception_rule table by removing `_log_access`, however in any case,
the main_exception_ids computed field will continue to lock many sale
orders, effectively preventing 2 sales orders with the same exception
to be confirmed at the same time.
Reversing the write by writing on SaleOrder instead of ExceptionRule
fixes the 2 unexpected locks. It should not result in more queries: the
"to remove" part generates a DELETE on the relation table for the rule
to remove and the "to add" part generates an INSERT for the rule to add,
both will be exactly the same in both cases.
Related to #1642
Replaces #1638
In the documentation.
The method called by '_check_exception' has a side effect, it writes
on 'exception.rule' + on the Many2many relation between it and
the related model (such as sale.order). When decorated by
@api.constrains, any error during the method will be caught and
re-raised as "ValidationError". This part of code is very prone to
concurrent updates as 2 sales having the same exception will both write
on the same 'exception.rule'. A concurrent update (OperationalError) is
re-raised as ValidationError, and then is not retried properly.
Calling the same method in create/write has the same effect than
@api.constrains without shadowing the exception type.
Full explanation:
OCA/server-tools#1642