Skip to content

Commit

Permalink
[FIX] website_sale_product_minimal_price: No compute prices
Browse files Browse the repository at this point in the history
When the product has a sales price = 0, his variants has no extra price and a pricelist depends on other pricelists to compute the prices, the algorithm takes a random variant.

Doing this changes we take care all pricelists that depends on the main to compute the prices asociated to the product.

TT30398
  • Loading branch information
CarlosRoca13 committed Jun 18, 2021
1 parent b547a6f commit fe99a35
Show file tree
Hide file tree
Showing 5 changed files with 190 additions and 9 deletions.
53 changes: 44 additions & 9 deletions website_sale_product_minimal_price/models/product_template.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,48 @@
class ProductTemplate(models.Model):
_inherit = 'product.template'

def _get_product_subpricelists(self, pricelist_id):
return pricelist_id.item_ids.filtered(
lambda i: (
i.applied_on == "3_global"
or (
i.applied_on == "2_product_category"
and i.categ_id == self.categ_id
)
or (
i.applied_on == "1_product"
and i.product_tmpl_id == self
)
or (
i.applied_on == "0_product_variant"
and i.product_id in self.product_variant_ids
)
)
and i.compute_price == "formula"
and i.base == "pricelist"
).mapped("base_pricelist_id")

def _get_variants_from_pricelist(self, pricelist_ids):
return pricelist_ids.mapped("item_ids").filtered(
lambda i: i.product_id in self.product_variant_ids
)

def _get_pricelist_variant_items(self, pricelist_id):
res = self._get_variants_from_pricelist(pricelist_id)
next_pricelists = self._get_product_subpricelists(pricelist_id)
res |= self._get_variants_from_pricelist(next_pricelists)
visited_pricelists = pricelist_id
while next_pricelists:
pricelist = next_pricelists[0]
if pricelist not in visited_pricelists:
res |= self._get_variants_from_pricelist(pricelist)
next_pricelists |= self._get_product_subpricelists(pricelist)
next_pricelists -= pricelist
visited_pricelists |= pricelist
else:
next_pricelists -= pricelist
return res

def _get_cheapest_info(self, pricelist):
"""Helper method for getting the variant with lowest price."""
# TODO: Cache this method for getting better performance
Expand All @@ -20,10 +62,7 @@ def _get_cheapest_info(self, pricelist):
# Variants with extra price
variants_extra_price = self.product_variant_ids.filtered('price_extra')
variants_without_extra_price = self.product_variant_ids - variants_extra_price
# Avoid compute prices when pricelist has not item variants defined
variant_items = pricelist.item_ids.filtered(
lambda i: i.product_id in self.product_variant_ids
)
variant_items = self._get_pricelist_variant_items(pricelist)
if variant_items:
# Take into account only the variants defined in pricelist and one
# variant not defined to compute prices defined at template or
Expand Down Expand Up @@ -89,9 +128,5 @@ def _get_first_possible_combination(
product_id = self._get_cheapest_info(pricelist)[0]
product = self.env["product.product"].browse(product_id)
ptavs = product.product_template_attribute_value_ids
variant_attributes = ptavs.mapped("attribute_id")
# remove returned values that are variant specific
res.filtered(lambda x: x.attribute_id not in variant_attributes)
# and inject cheapest variant ones
res += ptavs
res = ptavs
return res
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
/* Copyright 2021 Carlos Roca
* License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). */

odoo.define("test_product_with_no_prices.tour", function (require) {
"use strict";

var tour = require("web_tour.tour");
var base = require("web_editor.base");

var steps = [
{
trigger: "a:contains('My product test with no prices')",
extra_trigger: ".product_price:has(span:contains('From '))",
},
{
trigger: "a[href='/shop']",
extra_trigger:
".product_price:has(span:contains('10.00'))",
},
{
trigger: "a:contains('My product test')",
extra_trigger: ".product_price:has(span:contains('10.00'))",
},
];
tour.register("test_product_with_no_prices",
{
url: "/shop",
test: true,
wait_for: base.ready(),
},
steps
);
return {
steps: steps,
};
});
1 change: 1 addition & 0 deletions website_sale_product_minimal_price/tests/__init__.py
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
from . import test_website_sale_product_minimal_price
from . import test_product_with_no_prices
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
# Copyright 2021 Tecnativa - Carlos Roca
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
from odoo.tests.common import HttpCase


class TestProductWithNoPrices(HttpCase):
""" With this test we are checking that the minimal price is set
when the product has not a price defined and the price of
variants depend on a subpricelist.
"""

def setUp(self):
super().setUp()
ProductAttribute = self.env['product.attribute']
ProductAttributeValue = self.env['product.attribute.value']
self.category = self.env["product.category"].create({
"name": "Test category"
})
self.product_attribute = ProductAttribute.create({
'name': 'Test',
'website_published': True,
'create_variant': 'always',
})
self.product_attribute_value_test_1 = ProductAttributeValue.create({
'name': 'Test v1',
'attribute_id': self.product_attribute.id,
})
self.product_attribute_value_test_2 = ProductAttributeValue.create({
'name': 'Test v2',
'attribute_id': self.product_attribute.id,
})
self.product_template = self.env['product.template'].create({
'name': 'My product test with no prices',
'website_published': True,
'type': 'consu',
'website_sequence': 5000,
'categ_id': self.category.id,
'attribute_line_ids': [
(0, 0, {
'attribute_id': self.product_attribute.id,
'value_ids': [
(4, self.product_attribute_value_test_1.id),
(4, self.product_attribute_value_test_2.id),
],
}),
],
})
self.variant_1 = self.product_template.product_variant_ids[0]
self.variant_2 = self.product_template.product_variant_ids[1]
self.pricelist_aux = self.env['product.pricelist'].create({
"name": "Test pricelist Aux",
"selectable": True,
"item_ids": [
(
0,
0,
{
"applied_on": "0_product_variant",
"product_id": self.variant_1.id,
"compute_price": "fixed",
"fixed_price": 10,
}
),
(
0,
0,
{
"applied_on": "0_product_variant",
"product_id": self.variant_2.id,
"compute_price": "fixed",
"fixed_price": 11,
}
)
]
})
self.pricelist_main = self.env['product.pricelist'].create({
"name": "Test pricelist Main",
"selectable": True,
"item_ids": [
(
0,
0,
{
"applied_on": "2_product_category",
"categ_id": self.category.id,
"compute_price": "formula",
"base": "pricelist",
"base_pricelist_id": self.pricelist_aux.id,
}
)
]
})
user = self.env.ref("base.user_admin")
user.property_product_pricelist = self.pricelist_main

def test_ui_website(self):
"""Test frontend tour."""
tour = (
"odoo.__DEBUG__.services['web_tour.tour']",
"test_product_with_no_prices",
)
self.browser_js(
url_path="/",
code="%s.run('%s')" % tour,
ready="%s.tours['%s'].ready" % tour,
login="admin"
)
2 changes: 2 additions & 0 deletions website_sale_product_minimal_price/views/assets.xml
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@
src="/web/static/src/js/fields/field_utils.js"/>
<script type="text/javascript"
src="/website_sale_product_minimal_price/static/src/js/website_sale_product_minimal_price.js"/>
<script type="text/javascript"
src="/website_sale_product_minimal_price/static/src/js/test_product_with_no_prices_tour.js"/>
</xpath>
</template>
</odoo>

0 comments on commit fe99a35

Please sign in to comment.