diff --git a/addons/product/models/product_template.py b/addons/product/models/product_template.py index aba72f2c74e2d..8dc8d209a4655 100644 --- a/addons/product/models/product_template.py +++ b/addons/product/models/product_template.py @@ -1263,8 +1263,14 @@ def _cartesian_product(self, product_template_attribute_values_per_line, parent_ line_index = 0 # determines which ptav we're working on current_ptav = None - + max_iterations = self.env['ir.config_parameter'].sudo().get_param('product.combination_limit', 200000) + count = 0 while True: + count += 1 + if count > max_iterations: + _logger.warning("'product.combination_limit' %s reached for product_template %s product_template_attribute_values_per_line %s parent_combination %s.", + count, self.id, product_template_attribute_values_per_line, parent_combination) + break current_line_values = product_template_attribute_values_per_line[line_index] current_ptav_index = value_index_per_line[line_index] diff --git a/addons/product/tests/test_product_attribute_value_config.py b/addons/product/tests/test_product_attribute_value_config.py index e77e0895f1dcf..d8b838eb8e05e 100644 --- a/addons/product/tests/test_product_attribute_value_config.py +++ b/addons/product/tests/test_product_attribute_value_config.py @@ -792,3 +792,15 @@ def test_copy_extra_prices_of_product_attribute_values(self): 'price_extra' ) self.assertEqual(extra_prices, copied_extra_prices) + + def test_combination_infinite_loop(self): + """Test infinite-loop (while True) to get combinations""" + product_tmpls = self.env['product.template'].search([ + ('attribute_line_ids.attribute_id.create_variant', '=', 'always'), + ('attribute_line_ids.product_template_value_ids', '!=', False)]) + with self.assertLogs("odoo.addons.product.models.product_template", level="WARNING") as capture: + for product_tmpl in product_tmpls: + product_tmpl.attribute_line_ids.product_template_value_ids.unlink() + product_tmpl._get_first_possible_combination() + limit_warned = any("product.combination_limit" in rec.message for rec in capture.records) + self.assertTrue(limit_warned, "The combination was not stuck")