[FIX] sale: ensure widget is alive before executing code

Steps to reproduce the bug:
- Add a delay (for example 5 seconds) at the beginning of the
`/sale/get_combination_info_website` route.
- Add a delay (for example 10 seconds) in a `willStart` method in the
`Wysiwyg`.
- Go on a product page and enter edit mode.
- Click on the the product image.

-> The "Replace" button does not appear.

In this situation, the added delays represent slow rpc answers. Let's
analyse the flow of instructions in order to better understand the
problem:
- When you go on a product page, the `WebsiteSale` public widget is
started. `_getCombinationInfo` of `VariantMixin` is then called through
the `start()` of the public widget. As the rpc is taking time to answer,
`_onChangeCombination()` is not directly called.
- When entering edit mode, the `WebsiteSale` public widget is destroyed.
Due to the edit mode, the `o_editable` class has been added on editable
elements.
- After the first added delay and thanks to the rpc answer,
`_updateProductImage()` is called through `_onChangeCombination()`.
Because the `editor_enable` class has not been added to the document
body yet (due to the second added rpc), the `_updateProductImage()`
method replaces some elements of the DOM and by doing so, removes the
`o_editable` class of some elements. Consequently, the snippet option
linked to the image is not displayed.

The problem here is that the `_updateProductImage` method is called even
if its associated public widget has been destroyed. To solve the
problem, we first check that the associated widget is alive before
handling the result of the rpc answer in the `VariantMixin` mixin.

Note that the first idea was to use `this._rpc()` instead of
`ajax.jsonRpc()` in the mixin. Indeed, the advantage of using
`this._rpc()` is that it already ensures that the associated widget is
alive before handling the result of the rpc answer. The problem is that
some widgets that use the `VariantMixin` are created in such a way that
`this._rpc()` can not be used on them (for example
`OptionalProductsModal`).

Related to runbot-28700

closes odoo/odoo#149145

X-original-commit: 599e112ba7580617c61226b73355035c89af3395
Signed-off-by: Quentin Smetz (qsm) <qsm@odoo.com>
This commit is contained in:
Louis (loco)
2024-02-06 11:49:56 +00:00
parent a813104ef4
commit 4f29bebb02
@@ -79,6 +79,9 @@ var VariantMixin = {
'context': this.context,
...this._getOptionalCombinationInfoParam($currentOptionalProduct),
}).then((combinationData) => {
if (this._shouldIgnoreRpcResult()) {
return;
}
this._onChangeCombination(ev, $currentOptionalProduct, combinationData);
this._checkExclusions($currentOptionalProduct, childCombination, combinationData.parent_exclusions);
});
@@ -98,6 +101,9 @@ var VariantMixin = {
'context': this.context,
...this._getOptionalCombinationInfoParam($parent),
}).then((combinationData) => {
if (this._shouldIgnoreRpcResult()) {
return;
}
this._onChangeCombination(ev, $parent, combinationData);
this._checkExclusions($parent, combination, combinationData.parent_exclusions);
});
@@ -721,6 +727,18 @@ var VariantMixin = {
.addClass("active");
},
/**
* Return true if the current object has been destroyed.
* This function has been added as a fix to know if the result of a rpc
* should be handled. Indeed, "this._rpc()" can not be used as it is not
* supported by some elements that use this mixin.
*
* @private
*/
_shouldIgnoreRpcResult() {
return (typeof this.isDestroyed === "function" && this.isDestroyed());
},
/**
* Extension point for website_sale
*