Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion website_sale_product_minimal_price/__manifest__.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
{
"name": "Website Sale Product Minimal Price",
"summary": "Display minimal price for products that has variants",
"version": "19.0.1.1.0",
"version": "19.0.1.1.1",
"development_status": "Production/Stable",
"maintainers": ["sergio-teruel"],
"category": "Website",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,13 @@ class ProductTemplate(models.Model):
def _get_website_current_pricelist(self, website=None):
website = website or self.env["website"].get_current_website()
if request and getattr(request, "pricelist", False):
return request.pricelist
# In Odoo 19 request.pricelist is a lazy() proxy
# (website_sale/models/ir_http.py). Returning it as-is makes the
# in-place union in _get_pricelist_variant_items
# ("visited_pricelists |= pricelist") delegate to
# product.pricelist.__ior__, which no longer exists on recordsets,
# raising AttributeError. Materialise it into a real recordset.
return self.env["product.pricelist"].browse(request.pricelist.ids)
pricelist = website._get_and_cache_current_pricelist()
if pricelist:
return pricelist
Expand Down
4 changes: 4 additions & 0 deletions website_sale_product_minimal_price/readme/CONTRIBUTORS.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,7 @@
- [Studio73](https://www.studio73.es):

- Alex Garcia

- [Akyado](https://www.akyado.com):

- Antoine Guex
11 changes: 11 additions & 0 deletions website_sale_product_minimal_price/readme/HISTORY.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
## 19.0.1.1.1 (2026-08-03)

### Bugfixes

- Fix `AttributeError: 'product.pricelist' object has no attribute '__ior__'`
raised on the shop page for multi-variant products whose pricelist has
sub-pricelists (`compute_price = formula`, `base_pricelist_id` set). In
Odoo 19 `request.pricelist` is a `lazy()` proxy, so the in-place union
`visited_pricelists |= pricelist` in `_get_pricelist_variant_items` delegated
to the now-removed `product.pricelist.__ior__`.
`_get_website_current_pricelist` now returns a materialised recordset.
17 changes: 14 additions & 3 deletions website_sale_product_minimal_price/tests/test_product_template.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
from unittest.mock import MagicMock, patch
from unittest.mock import patch

from odoo.http import request
from odoo.tests import TransactionCase, tagged
from odoo.tools import lazy


@tagged("post_install", "-at_install")
Expand Down Expand Up @@ -48,10 +49,20 @@ def setUpClass(cls):
def test_get_website_current_pricelist(self):
from odoo.addons.website_sale.tests.common import MockRequest

pricelist = self.env["product.pricelist"].create({"name": "Request Pricelist"})
with MockRequest(self.env, website=self.website):
request.pricelist = MagicMock()
# In Odoo 19 ``request.pricelist`` is a ``lazy()`` proxy. The method
# must return a materialised recordset, otherwise the in-place union
# in ``_get_pricelist_variant_items`` ("visited_pricelists |= ...")
# raises ``AttributeError: 'product.pricelist' object has no
# attribute '__ior__'``.
request.pricelist = lazy(lambda: pricelist)
res = self.product_tmpl._get_website_current_pricelist()
self.assertEqual(res, request.pricelist)
self.assertEqual(res, pricelist)
# Regression: the result must support in-place set operations.
accumulator = self.env["product.pricelist"]
accumulator |= res
self.assertEqual(accumulator, pricelist)
with patch.object(
type(self.website), "_get_and_cache_current_pricelist"
) as mock_get_cache:
Expand Down
Loading