From ab121290e59a9c8ab5c155250a70148c31678b1a Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Iv=C3=A0n=20Todorovich?= <ivan.todorovich@gmail.com>
Date: Thu, 15 Jun 2023 15:53:05 +0000
Subject: [PATCH] [IMP] product: price computation performance

This commit improves the performance of `_is_applicable_for`, by:

1) Leveraging the `product.category.parent_path` field.
2) Use `applied_on` for the `if` conditions, which is ~100% faster than using
   the many2one fields due to the related record initialization.

These micro-optimizations are important because of the way `_is_applicable_for`
is used.

See: https://github.com/odoo/odoo/blob/0a5d84289/addons/product/models/product_pricelist.py#L169-L193

Which, for demostration purposes, could be simplified to:

```
for product, qty, partner in products_qty_partner:
    for rule in items:
        if not rule._is_applicable_for(product, qty_in_product_uom):
            continue
```

On a database with 470 products, and about the same number of pricelist items,
these optimization result in a ~30% speedup when computing prices for all
products at once. It may be even better depending on how many nested product
categories are used in the database and in the pricelist rules.

closes odoo/odoo#124376

Signed-off-by: Victor Feyens (vfe) <vfe@odoo.com>
---
 addons/product/models/product_pricelist.py | 21 +++++++++------------
 1 file changed, 9 insertions(+), 12 deletions(-)

diff --git a/addons/product/models/product_pricelist.py b/addons/product/models/product_pricelist.py
index b3e241c0081a..97a77d25d663 100644
--- a/addons/product/models/product_pricelist.py
+++ b/addons/product/models/product_pricelist.py
@@ -619,30 +619,27 @@ class PricelistItem(models.Model):
         if self.min_quantity and qty_in_product_uom < self.min_quantity:
             res = False
 
-        elif self.categ_id:
-            # Applied on a specific category
-            cat = product.categ_id
-            while cat:
-                if cat.id == self.categ_id.id:
-                    break
-                cat = cat.parent_id
-            if not cat:
+        elif self.applied_on == "2_product_category":
+            if (
+                product.categ_id != self.categ_id
+                and not product.categ_id.parent_path.startswith(self.categ_id.parent_path)
+            ):
                 res = False
         else:
             # Applied on a specific product template/variant
             if is_product_template:
-                if self.product_tmpl_id and product.id != self.product_tmpl_id.id:
+                if self.applied_on == "1_product" and product.id != self.product_tmpl_id.id:
                     res = False
-                elif self.product_id and not (
+                elif self.applied_on == "0_product_variant" and not (
                     product.product_variant_count == 1
                     and product.product_variant_id.id == self.product_id.id
                 ):
                     # product self acceptable on template if has only one variant
                     res = False
             else:
-                if self.product_tmpl_id and product.product_tmpl_id.id != self.product_tmpl_id.id:
+                if self.applied_on == "1_product" and product.product_tmpl_id.id != self.product_tmpl_id.id:
                     res = False
-                elif self.product_id and product.id != self.product_id.id:
+                elif self.applied_on == "0_product_variant" and product.id != self.product_id.id:
                     res = False
 
         return res
-- 
GitLab