From c8d5ff55ffa552984e71391eff0b51baae5e6c90 Mon Sep 17 00:00:00 2001 From: Adrian Torres Date: Fri, 19 Oct 2018 12:47:04 +0000 Subject: [PATCH] [FIX] sale_coupon: restore SO processing time This optimizes the confirmation of a sale_order in the ecommerce by ~73%, for a cart with 160 unique items, the processing time goes from 7m46s to 39s, which is roughly the same amount of processing time if the module `sale_coupon` is not installed. This optimization is achieved in two steps: 1. By validating promotional/discounted products in batch instead of validating each product one at a time. 2. By performing an unlink() on invalid reward lines instead of sending a delete command by writing to the one2many, as delete commands are not (yet) performed in batch, which means record recomputation after every delete. opw-1893277 closes odoo/enterprise#2905 --- models/sale_coupon_program.py | 16 +++++++++++----- models/sale_order.py | 4 ++-- 2 files changed, 13 insertions(+), 7 deletions(-) diff --git a/models/sale_coupon_program.py b/models/sale_coupon_program.py index 74479b03f4e..b1ebe602175 100644 --- a/models/sale_coupon_program.py +++ b/models/sale_coupon_program.py @@ -209,15 +209,14 @@ class SaleCouponProgram(models.Model): or Buy 1 coke + get 1 coke free then check 2 cokes are on cart or not """ order_lines = order.order_line - order._get_reward_lines() - products_qties = dict.fromkeys([product for product in order_lines.mapped('product_id')], 0) + products = order_lines.mapped('product_id') + products_qties = dict.fromkeys(products, 0) for line in order_lines: products_qties[line.product_id] += line.product_uom_qty valid_programs = self.filtered(lambda program: not program.rule_products_domain) for program in self - valid_programs: - ordered_rule_products_qty = sum( - qty for product, qty in products_qties.items() - if program._is_valid_product(product) - ) + valid_products = program._get_valid_products(products) + ordered_rule_products_qty = sum(products_qties[product] for product in valid_products) # Avoid program if 1 ordered foo on a program '1 foo, 1 free foo' if program._is_valid_product(program.reward_product_id) and program.reward_type == 'product': line = order.order_line.filtered(lambda line: line.product_id == program.reward_product_id) @@ -268,8 +267,15 @@ class SaleCouponProgram(models.Model): return True def _is_valid_product(self, product): + # NOTE: if you override this method, think of also overriding _get_valid_products if self.rule_products_domain: domain = safe_eval(self.rule_products_domain) + [('id', '=', product.id)] return bool(self.env['product.product'].search_count(domain)) else: return True + + def _get_valid_products(self, products): + if self.rule_products_domain: + domain = safe_eval(self.rule_products_domain) + [('id', 'in', products.ids)] + return self.env['product.product'].search(domain) + return products diff --git a/models/sale_order.py b/models/sale_order.py index 1c0731650e1..5331fe1a157 100644 --- a/models/sale_order.py +++ b/models/sale_order.py @@ -347,8 +347,8 @@ class SaleOrder(models.Model): order.no_code_promo_program_ids -= programs_to_remove order.code_promo_program_id -= programs_to_remove order.applied_coupon_ids -= order.applied_coupon_ids.filtered(lambda coupon: coupon.program_id in programs_to_remove) - invalid_lines += order.order_line.filtered(lambda line: line.product_id.id in products_to_remove.ids) - order.write({'order_line': [(2, line.id, False) for line in invalid_lines]}) + invalid_lines |= order.order_line.filtered(lambda line: line.product_id.id in products_to_remove.ids) + invalid_lines.unlink() def _get_applied_programs_with_rewards_on_current_order(self): # Need to add filter on current order. Indeed, it has always been calculating reward line even if on next order (which is useless and do calculation for nothing)