Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[IMP] pricelist_cache: update price cache in chuncks #3060

Open
wants to merge 5 commits into
base: 14.0
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
20 changes: 20 additions & 0 deletions pricelist_cache/data/demo.xml
@@ -1,6 +1,18 @@
<?xml version="1.0" encoding="utf-8" ?>
<odoo>


<record id="product_template" model="product.template">
<field name="name">Test template</field>
<field name="categ_id" ref="product.product_category_5" />
<field name="standard_price">500.0</field>
<field name="list_price">750.0</field>
<field name="type">consu</field>
<field name="weight">0.01</field>
<field name="uom_id" ref="uom.product_uom_unit" />
<field name="uom_po_id" ref="uom.product_uom_unit" />
</record>

<!--root pricelist 0-->

<record id="list0" model="product.pricelist">
Expand Down Expand Up @@ -122,6 +134,14 @@
<field name="fixed_price">50</field>
</record>

<record id="item14" model="product.pricelist.item">
<field name="base">list_price</field>
<field name="applied_on">1_product</field>
<field name="pricelist_id" ref="list4" />
<field name="product_tmpl_id" ref="product_template" />
<field name="fixed_price">1000</field>
</record>

<!-- factor pricelist 5, based on pricelist 3-->
<record id="list5" model="product.pricelist">
<field name="name">Pricelist 5</field>
Expand Down
21 changes: 14 additions & 7 deletions pricelist_cache/models/product_pricelist.py
Expand Up @@ -3,7 +3,9 @@

from datetime import date

from odoo import api, fields, models
from odoo import api, fields, models, tools

from .product_pricelist_item import PRODUCT_BATCH


class Pricelist(models.Model):
Expand Down Expand Up @@ -59,13 +61,18 @@ def create(self, vals_list):
res = super().create(vals_list)
for record in res:
if record._is_factor_pricelist() or record._is_global_pricelist():
product_ids_to_cache = None
product_ids_to_cache = self.env["product.product"].search([]).ids
else:
product_ids_to_cache = record.item_ids.mapped("product_id").ids
cache_model = self.env["product.pricelist.cache"].with_delay()
cache_model.update_product_pricelist_cache(
product_ids=product_ids_to_cache, pricelist_ids=record.ids
)
product_ids_to_cache = record.item_ids._get_pricelist_product_ids()

for product_chunk_ids in tools.misc.split_every(
PRODUCT_BATCH, product_ids_to_cache
):
self.env[
"product.pricelist.cache"
].with_delay().update_product_pricelist_cache(
product_ids=product_chunk_ids, pricelist_ids=record.ids
)
return res

def _get_product_prices(self, product_ids):
Expand Down
7 changes: 7 additions & 0 deletions pricelist_cache/models/product_pricelist_cache.py
Expand Up @@ -150,6 +150,13 @@ def _get_product_ids_to_update(self, pricelist, product_ids):
lambda i: i.product_id.id in product_ids
)
product_ids_to_update = product_item_ids.mapped("product_id").ids
template_items = pricelist.item_ids.filtered(
lambda i: i.product_tmpl_id.product_variant_ids
)
if template_items:
variants_ids = template_items.product_tmpl_id.product_variant_ids
if any([True for id in variants_ids.ids if id in product_ids]):
product_ids_to_update.extend(variants_ids.ids)
Comment on lines +153 to +159

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This would be good to have it in one loop with the first. item_ids.filtered

else:
# No parent (for instance public pricelist), then update everything
product_ids_to_update = product_ids
Expand Down
28 changes: 22 additions & 6 deletions pricelist_cache/models/product_pricelist_item.py
Expand Up @@ -3,7 +3,9 @@

from collections import defaultdict

from odoo import fields, models
from odoo import fields, models, tools

PRODUCT_BATCH = 1000


class PricelistItem(models.Model):
Expand All @@ -25,6 +27,15 @@ def _has_date_range(self):
"""Returns whether any of the item records in recordset is based on dates."""
return any(bool(record.date_start or record.date_end) for record in self)

def _get_pricelist_product_ids(self):
product_ids = []
for rec in self:
if rec.product_tmpl_id.id:
product_ids = rec.product_tmpl_id.product_variant_ids.ids
else:
product_ids = rec.product_id.ids
return product_ids

def _get_pricelist_products_group(self):
"""Returns a mapping of products grouped by pricelist.

Expand All @@ -34,15 +45,19 @@ def _get_pricelist_products_group(self):
"""
pricelist_products = defaultdict(list)
for item in self:
pricelist_products[item.pricelist_id.id].append(item.product_id.id)
pricelist_products[item.pricelist_id.id].extend(
item._get_pricelist_product_ids()
)
return pricelist_products

def update_product_pricelist_cache(self):
"""Executed when a product item is modified. Filters items not based
on variants or based on dates, then updates the cache.
"""
# Filter items applied on variants
items = self.filtered(lambda i: i.applied_on == "0_product_variant")
items = self.filtered(
lambda i: i.applied_on in ["0_product_variant", "1_product"]
vuwnevska marked this conversation as resolved.
Show resolved Hide resolved
)
# Filter items based on dates
item_ids_to_update = []
for item in items:
Expand All @@ -58,6 +73,7 @@ def update_product_pricelist_cache(self):
# Update cache
cache_object = self.env["product.pricelist.cache"]
for pricelist_id, product_ids in pricelist_products.items():
cache_object.with_delay().update_product_pricelist_cache(
product_ids=product_ids, pricelist_ids=[pricelist_id]
)
for product_chunk_ids in tools.misc.split_every(PRODUCT_BATCH, product_ids):
cache_object.with_delay().update_product_pricelist_cache(
product_ids=product_chunk_ids, pricelist_ids=[pricelist_id]
)
20 changes: 18 additions & 2 deletions pricelist_cache/tests/common.py
Expand Up @@ -11,36 +11,42 @@
{"id": 18, "price": 79.0},
{"id": 19, "price": 100.0},
{"id": 20, "price": 47.0},
{"id": 39, "price": 750.0},
],
"pricelist_cache.list1": [
{"id": 17, "price": 75.0},
{"id": 18, "price": 79.0},
{"id": 19, "price": 100.0},
{"id": 20, "price": 47.0},
{"id": 39, "price": 750.0},
],
"pricelist_cache.list2": [
{"id": 17, "price": 50.0},
{"id": 18, "price": 79.0},
{"id": 19, "price": 100.0},
{"id": 20, "price": 47.0},
{"id": 39, "price": 750.0},
],
"pricelist_cache.list3": [
{"id": 17, "price": 25.0},
{"id": 18, "price": 79.0},
{"id": 19, "price": 100.0},
{"id": 20, "price": 47.0},
{"id": 39, "price": 750.0},
],
"pricelist_cache.list4": [
{"id": 17, "price": 15.0},
{"id": 18, "price": 50.0},
{"id": 19, "price": 100.0},
{"id": 20, "price": 47.0},
{"id": 39, "price": 1000.0},
],
"pricelist_cache.list5": [
{"id": 17, "price": 45.0},
{"id": 18, "price": 99.0},
{"id": 19, "price": 120.0},
{"id": 20, "price": 67.0},
{"id": 39, "price": 770.0},
],
}

Expand Down Expand Up @@ -88,7 +94,7 @@ def setUpClass(cls):
)
)
# Odoo does not seems to register hooks by itself when tests are run
# the following line registers them explicitely
# the following line registers them explicitly
cls.env["base.automation"]._register_hook()
cls.cache_model = cls.env["product.pricelist.cache"]
# root pricelists
Expand Down Expand Up @@ -120,9 +126,19 @@ def setUpClass(cls):
cls.p8 = cls.env.ref("product.product_product_8")
# P9 not in any pricelist
cls.p9 = cls.env.ref("product.product_product_9")
cls.p10 = cls.env["product.product"].search(
[
(
"product_tmpl_id",
"=",
cls.env.ref("pricelist_cache.product_template").id,
)
]
)

# TODO ugly
cls.products = cls.env["product.product"].browse(
[cls.p6.id, cls.p7.id, cls.p8.id, cls.p9.id]
[cls.p6.id, cls.p7.id, cls.p8.id, cls.p9.id, cls.p10.id]
)
cls.pricelist_items = cls.env["product.pricelist.item"]
cls.pricelist_items |= cls.list0.item_ids
Expand Down
12 changes: 7 additions & 5 deletions pricelist_cache/tests/test_pricelist_cache.py
Expand Up @@ -209,42 +209,44 @@ def test_retrieve_price_list(self):
cache_model = self.cache_model
# list0 cache
l0_cache = cache_model.get_cached_prices_for_pricelist(self.list0, products)
self.assertEqual(len(l0_cache), 4)
self.assertEqual(len(l0_cache), 5)
l0_p6_cache = l0_cache.filtered(lambda c: c.product_id == self.p6)
self.assertEqual(l0_p6_cache.price, 100.0)
l0_p8_cache = l0_cache.filtered(lambda c: c.product_id == self.p8)
self.assertEqual(l0_p6_cache.price, 100.0)
# list1 cache
l1_cache = cache_model.get_cached_prices_for_pricelist(self.list1, products)
self.assertEqual(len(l1_cache), 4)
self.assertEqual(len(l1_cache), 5)
l1_p6_cache = l1_cache.filtered(lambda c: c.product_id == self.p6)
self.assertEqual(l1_p6_cache.price, 75.0)
# p8 price should have been fetched from list0 cache.
l1_p8_cache = l1_cache.filtered(lambda c: c.product_id == self.p8)
self.assertEqual(l0_p8_cache, l1_p8_cache)
# list2 cache
l2_cache = cache_model.get_cached_prices_for_pricelist(self.list2, products)
self.assertEqual(len(l2_cache), 4)
self.assertEqual(len(l2_cache), 5)
l2_p6_cache = l2_cache.filtered(lambda c: c.product_id == self.p6)
self.assertEqual(l2_p6_cache.price, 50.0)
# p8 price should have been fetched from list0 cache.
l2_p8_cache = l2_cache.filtered(lambda c: c.product_id == self.p8)
self.assertEqual(l0_p8_cache, l2_p8_cache)
# list3 cache
l3_cache = cache_model.get_cached_prices_for_pricelist(self.list3, products)
self.assertEqual(len(l3_cache), 4)
self.assertEqual(len(l3_cache), 5)
l3_p6_cache = l3_cache.filtered(lambda c: c.product_id == self.p6)
self.assertEqual(l3_p6_cache.price, 25.0)
# p8 price should have been fetched from list0 cache.
l3_p8_cache = l3_cache.filtered(lambda c: c.product_id == self.p8)
self.assertEqual(l0_p8_cache, l3_p8_cache)
# list4 cache
l4_cache = cache_model.get_cached_prices_for_pricelist(self.list4, products)
self.assertEqual(len(l4_cache), 4)
self.assertEqual(len(l4_cache), 5)
l4_p6_cache = l4_cache.filtered(lambda c: c.product_id == self.p6)
self.assertEqual(l4_p6_cache.price, 15.0)
l4_p7_cache = l4_cache.filtered(lambda c: c.product_id == self.p7)
self.assertEqual(l4_p7_cache.price, 50.0)
l4_p10_cache = l4_cache.filtered(lambda c: c.product_id == self.p10)
self.assertEqual(l4_p10_cache.price, 1000.0)
# p8 price should have been fetched from list0 cache.
l4_p8_cache = l4_cache.filtered(lambda c: c.product_id == self.p8)
self.assertEqual(l0_p8_cache, l4_p8_cache)
Expand Down