From eb805e85f4a3bb7113c67feeb1d7101714bd4238 Mon Sep 17 00:00:00 2001 From: "Loan (lse)" Date: Wed, 27 Apr 2022 13:42:20 +0000 Subject: [PATCH] [FIX] point_of_sale: improve get_price performance Test case: ~ 16 000 products and using a pricelist with a fixed price for all products (so ~16 000 pricelist_item). To reproduce: Open PoS and add an item to the order -> Very slow and not responsive: ~5 seconds to do the operation & around 2 minutes to fully load the PoS. Issue: `get_price` JS method is called several times and is a bit slow (~200ms). Mostly because of the linear search in the pricelist to check if the product does match. Note that the function is called once per ProductItem rendered. Each time a product is added to the order the rendering is refreshed for all ProductItem displayed. So the performance impact is significant if several of them are displayed. To solve: As the pricelist per Product.id is constant, we store the possible applied pricelist records on the product itself. After this commit: Order does take a few milliseconds to be added (2 ms VS 200ms) Other notes: A. With the current OWL version, it is not possible to force the render on only some of the ProductItem. A more appropriate fix could be done with "fine-grained reactivity" using a more recent OWL version B. It was thought to store/cache the price value on the ProductItem itself. It does have its share of disadvantages (no check if the pricelist time period did start/end, etc.). Either way, with that being implemented, the PoS was still a bit slow (because of the 200ms per `_get_price` call) OPW-2826122 closes odoo/odoo#90245 X-original-commit: 6f0bfb36a11e52e20b90ca85fc8847faadcdc95e Signed-off-by: Trinh Jacky (trj) Signed-off-by: Masereel Pierre --- addons/point_of_sale/static/src/js/models.js | 42 ++++++++++++++++++-- 1 file changed, 38 insertions(+), 4 deletions(-) diff --git a/addons/point_of_sale/static/src/js/models.js b/addons/point_of_sale/static/src/js/models.js index 9e57819ad81..b09c6b3ff25 100644 --- a/addons/point_of_sale/static/src/js/models.js +++ b/addons/point_of_sale/static/src/js/models.js @@ -222,11 +222,47 @@ class PosGlobalState extends PosModel { _loadResPartner() { this.db.add_partners(this.partners); } + _assignApplicableItems(pricelist, correspondingProduct, pricelistItem) { + if (!(pricelist.id in correspondingProduct.applicablePricelistItems)) { + correspondingProduct.applicablePricelistItems[pricelist.id] = []; + } + correspondingProduct.applicablePricelistItems[pricelist.id].push(pricelistItem); + } _loadProductProduct(products) { + const productMap = {}; + const productTemplateMap = {}; + const modelProducts = products.map(product => { product.pos = this; + product.applicablePricelistItems = {}; + productMap[product.id] = product; + productTemplateMap[product.product_tmpl_id[0]] = (productTemplateMap[product.product_tmpl_id[0]] || []).concat(product); return Product.create(product); }); + + for (let pricelist of this.pricelists) { + for (const pricelistItem of pricelist.items) { + if (pricelistItem.product_id) { + let product_id = pricelistItem.product_id[0]; + let correspondingProduct = productMap[product_id]; + if (correspondingProduct) { + this._assignApplicableItems(pricelist, correspondingProduct, pricelistItem); + } + } + else if (pricelistItem.product_tmpl_id) { + let product_tmpl_id = pricelistItem.product_tmpl_id[0]; + let correspondingProducts = productTemplateMap[product_tmpl_id]; + for (let correspondingProduct of (correspondingProducts || [])) { + this._assignApplicableItems(pricelist, correspondingProduct, pricelistItem); + } + } + else { + for (const correspondingProduct of products) { + this._assignApplicableItems(pricelist, correspondingProduct, pricelistItem); + } + } + } + } this.db.add_products(modelProducts) } _loadPosPaymentMethod() { @@ -1076,10 +1112,8 @@ class Product extends PosModel { category = category.parent; } - var pricelist_items = _.filter(pricelist.items, function (item) { - return (! item.product_tmpl_id || item.product_tmpl_id[0] === self.product_tmpl_id) && - (! item.product_id || item.product_id[0] === self.id) && - (! item.categ_id || _.contains(category_ids, item.categ_id[0])) && + var pricelist_items = _.filter(self.applicablePricelistItems[pricelist.id], function (item) { + return (! item.categ_id || _.contains(category_ids, item.categ_id[0])) && (! item.date_start || moment.utc(item.date_start).isSameOrBefore(date)) && (! item.date_end || moment.utc(item.date_end).isSameOrAfter(date)); });