From a40183324616b0c8958b93d9f25b2c3842fe233e Mon Sep 17 00:00:00 2001 From: Gil Arasa Verge Date: Sun, 12 Nov 2023 22:55:57 +0100 Subject: [PATCH] [FIX] product_variant_configurator_manual_creation: faster has_pending_variants computation for products with huge number of variant combinations --- .../models/product_template.py | 58 ++++++++++++++++++- 1 file changed, 56 insertions(+), 2 deletions(-) diff --git a/product_variant_configurator_manual_creation/models/product_template.py b/product_variant_configurator_manual_creation/models/product_template.py index 69bebfee6..5866446ca 100644 --- a/product_variant_configurator_manual_creation/models/product_template.py +++ b/product_variant_configurator_manual_creation/models/product_template.py @@ -11,17 +11,71 @@ class ProductTemplate(models.Model): has_pending_variants = fields.Boolean( string="Has pending variants?", - compute="_compute_pending_variants", + compute="_compute_has_pending_variants", ) + def _possible_pending_variants_calculation(self): + # If the product has not been created yet, + # it is useless to check for pending variants. + if isinstance( + self.id, + models.NewId, + ): + return False + + # Use the maximum number of combinations to avoid infinite processing + # this value is also set in self._create_variant_ids() in `product` module + possible_combination_downcounter = 1000 + + product_template_attribute_values_per_line = [ + ptal.product_template_value_ids._only_active() + for ptal in self.valid_product_template_attribute_line_ids + ] + + # iterate over lines and check whether there is any newId + for line in product_template_attribute_values_per_line: + for product_template_attribute_value in line: + if isinstance( + product_template_attribute_value.id, + models.NewId, + ) or isinstance( + product_template_attribute_value.attribute_id.id, models.NewId + ): + return False + + # If line is empty (no configurable attribute values) + # remove it from the list so _cartesian_product still works + if not line: + product_template_attribute_values_per_line.remove(line) + + cartesian_generator = self._cartesian_product( + product_template_attribute_values_per_line, None + ) + + # next() the generator until it's exhausted or hits + # "possible_combination_downcounter" combinations + while next(cartesian_generator, None): + possible_combination_downcounter -= 1 + if possible_combination_downcounter == 0: + return False + + return True + @api.depends( "product_variant_ids", "attribute_line_ids", "attribute_line_ids.attribute_id", "attribute_line_ids.value_ids", ) - def _compute_pending_variants(self): + def _compute_has_pending_variants(self): for rec in self: + if not rec._possible_pending_variants_calculation(): + # Disable wizard as it will be impossible + # to compute all values without variant + rec.has_pending_variants = False + continue + + # proceed with the accurate calculation rec.has_pending_variants = bool(self._get_values_without_variant()) def _get_values_without_variant(self):