Skip to content

Commit

Permalink
[WIP][FIX] product pack price refactor
Browse files Browse the repository at this point in the history
  • Loading branch information
augusto-weiss committed May 9, 2024
1 parent b37d65f commit 3abeddf
Show file tree
Hide file tree
Showing 9 changed files with 97 additions and 96 deletions.
1 change: 1 addition & 0 deletions product_pack/models/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,4 @@
from . import product_pack_line
from . import product_product
from . import product_template
from . import product_pricelist
5 changes: 2 additions & 3 deletions product_pack/models/product_pack_line.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
50 changes: 50 additions & 0 deletions product_pack/models/product_pricelist.py
Original file line number Diff line number Diff line change
@@ -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
45 changes: 3 additions & 42 deletions product_pack/models/product_product.py
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand Down
7 changes: 4 additions & 3 deletions sale_product_pack/models/product_pack_line.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
)
32 changes: 32 additions & 0 deletions sale_product_pack/models/sale_order_line.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
8 changes: 4 additions & 4 deletions sale_product_pack/tests/test_sale_product_pack.py
Original file line number Diff line number Diff line change
Expand Up @@ -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]
)
Expand All @@ -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
Expand Down
43 changes: 0 additions & 43 deletions website_sale_product_pack/models/product_template.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Original file line number Diff line number Diff line change
Expand Up @@ -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):
Expand Down

0 comments on commit 3abeddf

Please sign in to comment.