From 3abeddfb63ad637031da4262eebcdb51e8bd3de7 Mon Sep 17 00:00:00 2001 From: augusto-weiss Date: Wed, 8 May 2024 12:30:07 -0300 Subject: [PATCH] [WIP][FIX] product pack price refactor --- product_pack/models/__init__.py | 1 + product_pack/models/product_pack_line.py | 5 +- product_pack/models/product_pricelist.py | 50 +++++++++++++++++++ product_pack/models/product_product.py | 45 ++--------------- sale_product_pack/models/product_pack_line.py | 7 +-- sale_product_pack/models/sale_order_line.py | 32 ++++++++++++ .../tests/test_sale_product_pack.py | 8 +-- .../models/product_template.py | 43 ---------------- .../tests/test_website_sale_product_pack.py | 2 +- 9 files changed, 97 insertions(+), 96 deletions(-) create mode 100644 product_pack/models/product_pricelist.py diff --git a/product_pack/models/__init__.py b/product_pack/models/__init__.py index 626eb1a8..37bee3ed 100644 --- a/product_pack/models/__init__.py +++ b/product_pack/models/__init__.py @@ -3,3 +3,4 @@ from . import product_pack_line from . import product_product from . import product_template +from . import product_pricelist diff --git a/product_pack/models/product_pack_line.py b/product_pack/models/product_pack_line.py index 5a105777..88f3fd4b 100644 --- a/product_pack/models/product_pack_line.py +++ b/product_pack/models/product_pack_line.py @@ -54,6 +54,5 @@ def _check_recursion(self): ) pack_lines = pack_lines.mapped("product_id.pack_line_ids") - def get_price(self): - self.ensure_one() - return self.product_id.lst_price * self.quantity + def _compute_price(self, base_price): + return base_price * self.quantity diff --git a/product_pack/models/product_pricelist.py b/product_pack/models/product_pricelist.py new file mode 100644 index 00000000..50a8f34c --- /dev/null +++ b/product_pack/models/product_pricelist.py @@ -0,0 +1,50 @@ +from odoo import models + + +class Pricelist(models.Model): + _inherit = "product.pricelist" + + def _get_product_price(self, product, quantity, uom=None, date=False, **kwargs): + """Compute the pricelist price for the specified product, qty & uom. + + Note: self.ensure_one() + + :returns: unit price of the product, considering pricelist rules + :rtype: float + """ + self.ensure_one() + if product._is_pack_to_be_handled(): + pack_lines = product.sudo().pack_line_ids.mapped("product_id") + pack_line_prices = self._compute_price_rule( + pack_lines, quantity, uom=uom, date=date, **kwargs + ) + pack_price = self._compute_price_rule( + product, quantity, uom=uom, date=date, **kwargs + )[product.id][0] + for line in product.sudo().pack_line_ids: + pack_price += line._compute_price( + base_price=pack_line_prices[line.product_id.id][0] + ) + return pack_price + else: + return super()._get_product_price( + product=product, quantity=quantity, uom=uom, date=date, **kwargs + ) + + def _get_products_price(self, products, quantity, uom=None, date=False, **kwargs): + """Compute the pricelist prices for the specified products, qty & uom. + + Note: self.ensure_one() + + :returns: dict{product_id: product price}, considering the current pricelist + :rtype: dict + """ + packs, no_packs = products.split_pack_products() + res = super()._get_products_price( + no_packs, quantity=quantity, uom=uom, date=date, **kwargs + ) + for pack in packs: + res[pack.id] = self._get_product_price( + product=pack, quantity=quantity, uom=uom, date=date, **kwargs + ) + return res diff --git a/product_pack/models/product_product.py b/product_pack/models/product_product.py index 1379281f..397dfc96 100644 --- a/product_pack/models/product_product.py +++ b/product_pack/models/product_product.py @@ -26,52 +26,13 @@ def get_pack_lines(self): can be overloaded to introduce filtering function by date, etc...""" return self.mapped("pack_line_ids") + def _is_pack_to_be_handled(self): + return self.product_tmpl_id._is_pack_to_be_handled() + def split_pack_products(self): packs = self.filtered(lambda p: p.product_tmpl_id._is_pack_to_be_handled()) return packs, (self - packs) - def price_compute( - self, price_type, uom=False, currency=False, company=False, date=False - ): - packs, no_packs = self.split_pack_products() - prices = super(ProductProduct, no_packs).price_compute( - price_type, uom, currency, company, date - ) - for product in packs.with_context(prefetch_fields=False): - pack_price = 0.0 - for pack_line in product.sudo().pack_line_ids: - pack_price += pack_line.get_price() - pricelist_id_or_name = self._context.get("pricelist") - # if there is a pricelist on the context the returned prices are on - # that currency but, if the pack product has a different currency - # it will be converted again by pp._compute_price_rule, so if - # that is the case we convert the amounts to the pack currency - if pricelist_id_or_name: - pricelist = None - if isinstance(pricelist_id_or_name, list): - pricelist_id_or_name = pricelist_id_or_name[0] - if isinstance(pricelist_id_or_name, str): - pricelist_name_search = self.env["product.pricelist"].name_search( - pricelist_id_or_name, operator="=", limit=1 - ) - if pricelist_name_search: - pricelist = self.env["product.pricelist"].browse( - [pricelist_name_search[0][0]] - ) - elif isinstance(pricelist_id_or_name, int): - pricelist = self.env["product.pricelist"].browse( - pricelist_id_or_name - ) - if pricelist and pricelist.currency_id != product.currency_id: - pack_price = pricelist.currency_id._convert( - pack_price, - product.currency_id, - self.company_id or self.env.company, - fields.Date.today(), - ) - prices[product.id] = pack_price - return prices - @api.depends("list_price", "price_extra") def _compute_product_lst_price(self): packs, no_packs = self.split_pack_products() diff --git a/sale_product_pack/models/product_pack_line.py b/sale_product_pack/models/product_pack_line.py index 52272769..110233da 100644 --- a/sale_product_pack/models/product_pack_line.py +++ b/sale_product_pack/models/product_pack_line.py @@ -46,6 +46,7 @@ def get_sale_order_line_vals(self, line, order): ) return vals - def get_price(self): - self.ensure_one() - return super().get_price() * (1 - self.sale_discount / 100.0) + def _compute_price(self, base_price): + return super()._compute_price(base_price=base_price) * ( + 1 - self.sale_discount / 100.0 + ) diff --git a/sale_product_pack/models/sale_order_line.py b/sale_product_pack/models/sale_order_line.py index 5c2d79c8..07fbfd03 100644 --- a/sale_product_pack/models/sale_order_line.py +++ b/sale_product_pack/models/sale_order_line.py @@ -126,3 +126,35 @@ def action_open_parent_pack_product_view(self): "view_mode": "tree,form", "domain": domain, } + + def _get_pack_line_discount(self): + """returns the discount settled in the parent pack lines""" + self.ensure_one() + discount = 0.0 + if self.pack_parent_line_id.pack_component_price == "detailed": + for pack_line in self.pack_parent_line_id.product_id.pack_line_ids: + if pack_line.product_id == self.product_id: + discount = pack_line.sale_discount + break + return discount + + @api.depends("product_id", "product_uom", "product_uom_qty") + def _compute_discount(self): + res = super()._compute_discount() + for pack_line in self.filtered("pack_parent_line_id"): + pack_line.discount = pack_line._get_pack_line_discount() + return res + + def _get_pricelist_price(self): + """Compute the price given by the pricelist for the given line information. + + :return: the product sales price in the order currency (without taxes) + :rtype: float + """ + price = super()._get_pricelist_price() + + if self.product_id.product_tmpl_id._is_pack_to_be_handled(): + price = self.order_id.pricelist_id._get_product_price( + product=self.product_id.product_tmpl_id, quantity=1.0 + ) + return price diff --git a/sale_product_pack/tests/test_sale_product_pack.py b/sale_product_pack/tests/test_sale_product_pack.py index d8de1222..4a65a92d 100644 --- a/sale_product_pack/tests/test_sale_product_pack.py +++ b/sale_product_pack/tests/test_sale_product_pack.py @@ -158,13 +158,13 @@ def test_create_totalized_price_order_line(self): (self.sale_order.order_line - line).mapped("price_subtotal"), [0, 0, 0] ) # Pack price is equal to the sum of component prices - self.assertAlmostEqual(line.price_subtotal, 2662.5) + self.assertAlmostEqual(line.price_subtotal, 2693.25) self.assertAlmostEqual(self._get_component_prices_sum(product_tp), 2662.5) # Update pricelist with a discount self.sale_order.pricelist_id = self.discount_pricelist self.sale_order.action_update_prices() - self.assertAlmostEqual(line.price_subtotal, 2396.25) + self.assertAlmostEqual(line.price_subtotal, 2423.93) self.assertEqual( (self.sale_order.order_line - line).mapped("price_subtotal"), [0, 0, 0] ) @@ -183,13 +183,13 @@ def test_create_non_detailed_price_order_line(self): # not a detailed one self.assertEqual(self.sale_order.order_line, line) # Pack price is equal to the sum of component prices - self.assertAlmostEqual(line.price_subtotal, 2662.5) + self.assertAlmostEqual(line.price_subtotal, 2693.25) self.assertAlmostEqual(self._get_component_prices_sum(product_ndtp), 2662.5) # Update pricelist with a discount self.sale_order.pricelist_id = self.discount_pricelist self.sale_order.action_update_prices() - self.assertAlmostEqual(line.price_subtotal, 2396.25) + self.assertAlmostEqual(line.price_subtotal, 2423.93) def test_update_qty(self): # Ensure the quantities are always updated diff --git a/website_sale_product_pack/models/product_template.py b/website_sale_product_pack/models/product_template.py index dfde21ce..52fb31c0 100644 --- a/website_sale_product_pack/models/product_template.py +++ b/website_sale_product_pack/models/product_template.py @@ -48,46 +48,3 @@ def check_website_published(self): "pack_parents": ", ".join(published.mapped("name")), } ) - - # Neccessary for the website_sale_product_pack module because the price in /shop - # is calculated by the product.template.price_compute method - def price_compute( - self, price_type, uom=False, currency=False, company=False, date=False - ): - templates_with_packs, templates_without_packs = self.split_pack_products() - prices = super(ProductTemplate, templates_without_packs).price_compute( - price_type, uom, currency, company, date - ) - for template in templates_with_packs.with_context(prefetch_fields=False): - pack_price = 0.0 - for pack_line in template.sudo().pack_line_ids: - pack_price += pack_line.get_price() - pricelist_id_or_name = self._context.get("pricelist") - # if there is a pricelist on the context the returned prices are on - # that currency but, if the pack product has a different currency - # it will be converted again by pp._compute_price_rule, so if - # that is the case we convert the amounts to the pack currency - if pricelist_id_or_name: - if isinstance(pricelist_id_or_name, list): - pricelist_id_or_name = pricelist_id_or_name[0] - if isinstance(pricelist_id_or_name, str): - pricelist_name_search = self.env["product.pricelist"].name_search( - pricelist_id_or_name, operator="=", limit=1 - ) - if pricelist_name_search: - pricelist = self.env["product.pricelist"].browse( - [pricelist_name_search[0][0]] - ) - elif isinstance(pricelist_id_or_name, int): - pricelist = self.env["product.pricelist"].browse( - pricelist_id_or_name - ) - if pricelist and pricelist.currency_id != template.currency_id: - pack_price = pricelist.currency_id._convert( - pack_price, - template.currency_id, - self.company_id or self.env.company, - fields.Date.today(), - ) - prices[template.id] = pack_price - return prices diff --git a/website_sale_product_pack/tests/test_website_sale_product_pack.py b/website_sale_product_pack/tests/test_website_sale_product_pack.py index 49560b66..5e75dea6 100644 --- a/website_sale_product_pack/tests/test_website_sale_product_pack.py +++ b/website_sale_product_pack/tests/test_website_sale_product_pack.py @@ -101,7 +101,7 @@ def test_create_totalized_price_order_line(self): # All component lines have zero as subtotal self.assertEqual((sale.order_line - line).mapped("price_subtotal"), [0, 0, 0]) # Pack price is equal to the sum of component prices - self.assertEqual(line.price_subtotal, 2662.5) + self.assertEqual(line.price_subtotal, 2693.25) self.assertEqual(self._get_component_prices_sum(self.product_pdt), 2662.5) def test_create_non_detailed_price_order_line(self):