Skip to content

Commit 3abeddf

Browse files
committed
[WIP][FIX] product pack price refactor
1 parent b37d65f commit 3abeddf

File tree

9 files changed

+97
-96
lines changed

9 files changed

+97
-96
lines changed

product_pack/models/__init__.py

+1
Original file line numberDiff line numberDiff line change
@@ -3,3 +3,4 @@
33
from . import product_pack_line
44
from . import product_product
55
from . import product_template
6+
from . import product_pricelist

product_pack/models/product_pack_line.py

+2-3
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,5 @@ def _check_recursion(self):
5454
)
5555
pack_lines = pack_lines.mapped("product_id.pack_line_ids")
5656

57-
def get_price(self):
58-
self.ensure_one()
59-
return self.product_id.lst_price * self.quantity
57+
def _compute_price(self, base_price):
58+
return base_price * self.quantity
+50
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
from odoo import models
2+
3+
4+
class Pricelist(models.Model):
5+
_inherit = "product.pricelist"
6+
7+
def _get_product_price(self, product, quantity, uom=None, date=False, **kwargs):
8+
"""Compute the pricelist price for the specified product, qty & uom.
9+
10+
Note: self.ensure_one()
11+
12+
:returns: unit price of the product, considering pricelist rules
13+
:rtype: float
14+
"""
15+
self.ensure_one()
16+
if product._is_pack_to_be_handled():
17+
pack_lines = product.sudo().pack_line_ids.mapped("product_id")
18+
pack_line_prices = self._compute_price_rule(
19+
pack_lines, quantity, uom=uom, date=date, **kwargs
20+
)
21+
pack_price = self._compute_price_rule(
22+
product, quantity, uom=uom, date=date, **kwargs
23+
)[product.id][0]
24+
for line in product.sudo().pack_line_ids:
25+
pack_price += line._compute_price(
26+
base_price=pack_line_prices[line.product_id.id][0]
27+
)
28+
return pack_price
29+
else:
30+
return super()._get_product_price(
31+
product=product, quantity=quantity, uom=uom, date=date, **kwargs
32+
)
33+
34+
def _get_products_price(self, products, quantity, uom=None, date=False, **kwargs):
35+
"""Compute the pricelist prices for the specified products, qty & uom.
36+
37+
Note: self.ensure_one()
38+
39+
:returns: dict{product_id: product price}, considering the current pricelist
40+
:rtype: dict
41+
"""
42+
packs, no_packs = products.split_pack_products()
43+
res = super()._get_products_price(
44+
no_packs, quantity=quantity, uom=uom, date=date, **kwargs
45+
)
46+
for pack in packs:
47+
res[pack.id] = self._get_product_price(
48+
product=pack, quantity=quantity, uom=uom, date=date, **kwargs
49+
)
50+
return res

product_pack/models/product_product.py

+3-42
Original file line numberDiff line numberDiff line change
@@ -26,52 +26,13 @@ def get_pack_lines(self):
2626
can be overloaded to introduce filtering function by date, etc..."""
2727
return self.mapped("pack_line_ids")
2828

29+
def _is_pack_to_be_handled(self):
30+
return self.product_tmpl_id._is_pack_to_be_handled()
31+
2932
def split_pack_products(self):
3033
packs = self.filtered(lambda p: p.product_tmpl_id._is_pack_to_be_handled())
3134
return packs, (self - packs)
3235

33-
def price_compute(
34-
self, price_type, uom=False, currency=False, company=False, date=False
35-
):
36-
packs, no_packs = self.split_pack_products()
37-
prices = super(ProductProduct, no_packs).price_compute(
38-
price_type, uom, currency, company, date
39-
)
40-
for product in packs.with_context(prefetch_fields=False):
41-
pack_price = 0.0
42-
for pack_line in product.sudo().pack_line_ids:
43-
pack_price += pack_line.get_price()
44-
pricelist_id_or_name = self._context.get("pricelist")
45-
# if there is a pricelist on the context the returned prices are on
46-
# that currency but, if the pack product has a different currency
47-
# it will be converted again by pp._compute_price_rule, so if
48-
# that is the case we convert the amounts to the pack currency
49-
if pricelist_id_or_name:
50-
pricelist = None
51-
if isinstance(pricelist_id_or_name, list):
52-
pricelist_id_or_name = pricelist_id_or_name[0]
53-
if isinstance(pricelist_id_or_name, str):
54-
pricelist_name_search = self.env["product.pricelist"].name_search(
55-
pricelist_id_or_name, operator="=", limit=1
56-
)
57-
if pricelist_name_search:
58-
pricelist = self.env["product.pricelist"].browse(
59-
[pricelist_name_search[0][0]]
60-
)
61-
elif isinstance(pricelist_id_or_name, int):
62-
pricelist = self.env["product.pricelist"].browse(
63-
pricelist_id_or_name
64-
)
65-
if pricelist and pricelist.currency_id != product.currency_id:
66-
pack_price = pricelist.currency_id._convert(
67-
pack_price,
68-
product.currency_id,
69-
self.company_id or self.env.company,
70-
fields.Date.today(),
71-
)
72-
prices[product.id] = pack_price
73-
return prices
74-
7536
@api.depends("list_price", "price_extra")
7637
def _compute_product_lst_price(self):
7738
packs, no_packs = self.split_pack_products()

sale_product_pack/models/product_pack_line.py

+4-3
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ def get_sale_order_line_vals(self, line, order):
4646
)
4747
return vals
4848

49-
def get_price(self):
50-
self.ensure_one()
51-
return super().get_price() * (1 - self.sale_discount / 100.0)
49+
def _compute_price(self, base_price):
50+
return super()._compute_price(base_price=base_price) * (
51+
1 - self.sale_discount / 100.0
52+
)

sale_product_pack/models/sale_order_line.py

+32
Original file line numberDiff line numberDiff line change
@@ -126,3 +126,35 @@ def action_open_parent_pack_product_view(self):
126126
"view_mode": "tree,form",
127127
"domain": domain,
128128
}
129+
130+
def _get_pack_line_discount(self):
131+
"""returns the discount settled in the parent pack lines"""
132+
self.ensure_one()
133+
discount = 0.0
134+
if self.pack_parent_line_id.pack_component_price == "detailed":
135+
for pack_line in self.pack_parent_line_id.product_id.pack_line_ids:
136+
if pack_line.product_id == self.product_id:
137+
discount = pack_line.sale_discount
138+
break
139+
return discount
140+
141+
@api.depends("product_id", "product_uom", "product_uom_qty")
142+
def _compute_discount(self):
143+
res = super()._compute_discount()
144+
for pack_line in self.filtered("pack_parent_line_id"):
145+
pack_line.discount = pack_line._get_pack_line_discount()
146+
return res
147+
148+
def _get_pricelist_price(self):
149+
"""Compute the price given by the pricelist for the given line information.
150+
151+
:return: the product sales price in the order currency (without taxes)
152+
:rtype: float
153+
"""
154+
price = super()._get_pricelist_price()
155+
156+
if self.product_id.product_tmpl_id._is_pack_to_be_handled():
157+
price = self.order_id.pricelist_id._get_product_price(
158+
product=self.product_id.product_tmpl_id, quantity=1.0
159+
)
160+
return price

sale_product_pack/tests/test_sale_product_pack.py

+4-4
Original file line numberDiff line numberDiff line change
@@ -158,13 +158,13 @@ def test_create_totalized_price_order_line(self):
158158
(self.sale_order.order_line - line).mapped("price_subtotal"), [0, 0, 0]
159159
)
160160
# Pack price is equal to the sum of component prices
161-
self.assertAlmostEqual(line.price_subtotal, 2662.5)
161+
self.assertAlmostEqual(line.price_subtotal, 2693.25)
162162
self.assertAlmostEqual(self._get_component_prices_sum(product_tp), 2662.5)
163163

164164
# Update pricelist with a discount
165165
self.sale_order.pricelist_id = self.discount_pricelist
166166
self.sale_order.action_update_prices()
167-
self.assertAlmostEqual(line.price_subtotal, 2396.25)
167+
self.assertAlmostEqual(line.price_subtotal, 2423.93)
168168
self.assertEqual(
169169
(self.sale_order.order_line - line).mapped("price_subtotal"), [0, 0, 0]
170170
)
@@ -183,13 +183,13 @@ def test_create_non_detailed_price_order_line(self):
183183
# not a detailed one
184184
self.assertEqual(self.sale_order.order_line, line)
185185
# Pack price is equal to the sum of component prices
186-
self.assertAlmostEqual(line.price_subtotal, 2662.5)
186+
self.assertAlmostEqual(line.price_subtotal, 2693.25)
187187
self.assertAlmostEqual(self._get_component_prices_sum(product_ndtp), 2662.5)
188188

189189
# Update pricelist with a discount
190190
self.sale_order.pricelist_id = self.discount_pricelist
191191
self.sale_order.action_update_prices()
192-
self.assertAlmostEqual(line.price_subtotal, 2396.25)
192+
self.assertAlmostEqual(line.price_subtotal, 2423.93)
193193

194194
def test_update_qty(self):
195195
# Ensure the quantities are always updated

website_sale_product_pack/models/product_template.py

-43
Original file line numberDiff line numberDiff line change
@@ -48,46 +48,3 @@ def check_website_published(self):
4848
"pack_parents": ", ".join(published.mapped("name")),
4949
}
5050
)
51-
52-
# Neccessary for the website_sale_product_pack module because the price in /shop
53-
# is calculated by the product.template.price_compute method
54-
def price_compute(
55-
self, price_type, uom=False, currency=False, company=False, date=False
56-
):
57-
templates_with_packs, templates_without_packs = self.split_pack_products()
58-
prices = super(ProductTemplate, templates_without_packs).price_compute(
59-
price_type, uom, currency, company, date
60-
)
61-
for template in templates_with_packs.with_context(prefetch_fields=False):
62-
pack_price = 0.0
63-
for pack_line in template.sudo().pack_line_ids:
64-
pack_price += pack_line.get_price()
65-
pricelist_id_or_name = self._context.get("pricelist")
66-
# if there is a pricelist on the context the returned prices are on
67-
# that currency but, if the pack product has a different currency
68-
# it will be converted again by pp._compute_price_rule, so if
69-
# that is the case we convert the amounts to the pack currency
70-
if pricelist_id_or_name:
71-
if isinstance(pricelist_id_or_name, list):
72-
pricelist_id_or_name = pricelist_id_or_name[0]
73-
if isinstance(pricelist_id_or_name, str):
74-
pricelist_name_search = self.env["product.pricelist"].name_search(
75-
pricelist_id_or_name, operator="=", limit=1
76-
)
77-
if pricelist_name_search:
78-
pricelist = self.env["product.pricelist"].browse(
79-
[pricelist_name_search[0][0]]
80-
)
81-
elif isinstance(pricelist_id_or_name, int):
82-
pricelist = self.env["product.pricelist"].browse(
83-
pricelist_id_or_name
84-
)
85-
if pricelist and pricelist.currency_id != template.currency_id:
86-
pack_price = pricelist.currency_id._convert(
87-
pack_price,
88-
template.currency_id,
89-
self.company_id or self.env.company,
90-
fields.Date.today(),
91-
)
92-
prices[template.id] = pack_price
93-
return prices

website_sale_product_pack/tests/test_website_sale_product_pack.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,7 @@ def test_create_totalized_price_order_line(self):
101101
# All component lines have zero as subtotal
102102
self.assertEqual((sale.order_line - line).mapped("price_subtotal"), [0, 0, 0])
103103
# Pack price is equal to the sum of component prices
104-
self.assertEqual(line.price_subtotal, 2662.5)
104+
self.assertEqual(line.price_subtotal, 2693.25)
105105
self.assertEqual(self._get_component_prices_sum(self.product_pdt), 2662.5)
106106

107107
def test_create_non_detailed_price_order_line(self):

0 commit comments

Comments
 (0)