Skip to content
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.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ TODO: add repo description.

Available addons
----------------
addon | version | maintainers | summary
addon | version | maintainers | summary.
--- | --- | --- | ---
[product_pack](product_pack/) | 16.0.1.1.0 | [![ernestotejeda](https://github.com/ernestotejeda.png?size=30px)](https://github.com/ernestotejeda) | This module allows you to set a product as a Pack
[purchase_product_pack](purchase_product_pack/) | 16.0.1.0.0 | | This module allows you to buy product packs
Expand Down
20 changes: 19 additions & 1 deletion product_pack/README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ Product Pack
!! This file is generated by oca-gen-addon-readme !!
!! changes will be overwritten. !!
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!! source digest: sha256:d6d79cfb1448cba3c8cc68f89101f6b53245f8a695effd2058ca881dac328748
!! source digest: sha256:6412314c32b470ebaa9ba73edd671e2513bf0b8c9faa4703dcb22faf76692670
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

.. |badge1| image:: https://img.shields.io/badge/maturity-Beta-yellow.png
Expand Down Expand Up @@ -70,6 +70,21 @@ The options of this field are the followings:
components prices. The pack product will be the only one that has price
and this one will be the price set in the pack product.

+-----------------------------+-----------------------------+---------------------------------+-----------------------------------------+----------------------+
| **Pack type** | **Show components on SO?** | **Sale price** | **Discount** | **Can be modified?** |
+=============================+=============================+=================================+=========================================+======================+
| **Detailed per components** | Yes, with their prices | Components + Pack | Applies to the price of the pack and | Yes, configurable |
| | | | the components | |
+-----------------------------+-----------------------------+---------------------------------+-----------------------------------------+----------------------+
| **Detailed - Totalized** | Yes, with their prices at 0 | Components | Applies to the total (pack + components)| No |
+-----------------------------+-----------------------------+---------------------------------+-----------------------------------------+----------------------+
| **Detailed - Ignored** | Yes, with their prices at 0 | Only Pack | Applies to the pack | No |
+-----------------------------+-----------------------------+---------------------------------+-----------------------------------------+----------------------+
| **No detailed** | No | Components | Applies to the total (pack + components)| No |
+-----------------------------+-----------------------------+---------------------------------+-----------------------------------------+----------------------+

**Note:** If pricelist enabled, Odoo will display the price according to the corresponding pricelist. In the case of a pricelist with discount policy "Show public price & discount to the customer" keep in mind that the "Non Detailed" and "Detailed - Totalized in main product" packs do not display the discount.

Known issues / Roadmap
======================

Expand Down Expand Up @@ -106,6 +121,9 @@ Contributors
* Juan José Scarafía
* Nicolas Mac Rouillon
* Katherine Zaoral
* Bruno Zanotti
* Augusto Weiss
* Nicolas Col
* `NaN·TIC <http://www.nan-tic.com>`_
* `Tecnativa <https://www.tecnativa.com>`_:

Expand Down
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
33 changes: 31 additions & 2 deletions product_pack/models/product_pack_line.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,35 @@ def _check_recursion(self):
)
pack_lines = pack_lines.mapped("product_id.pack_line_ids")

def get_price(self):
def _get_pack_line_price(self, pricelist, quantity, uom=None, date=False, **kwargs):
self.ensure_one()
return self.product_id.lst_price * self.quantity
if self.product_id._is_pack_to_be_handled():
price = pricelist._get_product_price(
self.product_id, quantity, uom=uom, date=date, **kwargs
)
else:
price = pricelist._compute_price_rule(
self.product_id, quantity, uom=uom, date=date, **kwargs
)[self.product_id.id][0]
return price * self.quantity

def _pack_line_price_compute(
self, price_type, uom=False, currency=False, company=False, date=False
):
packs, no_packs = self.product_id.split_pack_products()

pack_prices = {}
# If the component is a pack
for pack in packs:
pack_prices[pack.id] = pack.lst_price

# else
no_pack_prices = no_packs.price_compute(
price_type, uom, currency, company, date
)

prices = {**pack_prices, **no_pack_prices}
for line in self:
prices[line.product_id.id] *= line.quantity

return prices
48 changes: 48 additions & 0 deletions product_pack/models/product_pricelist.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
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 pack product, qty & uom.
:returns: unit price of the pack product + components, considering pricelist rules
"""
self.ensure_one()
if product._is_pack_to_be_handled():
# NOTE: This exception is to avoid adding the list price of the packs
# "totalized" and "non detailed". Should be removed to solve the issue #169.
if (
product.pack_type == "non_detailed"
or product.pack_component_price == "totalized"
):
pack_price = 0
else:
# Para packs "detailed", el precio es SOLO la suma de componentes
# No se suma el precio del pack en sí para evitar duplicación
pack_price = 0

for line in product.sudo().pack_line_ids:
pack_price += line._get_pack_line_price(
self, quantity, uom=uom, date=date, **kwargs
)
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 price for the specified pack product, qty & uom.

:returns: unit price of the pack product + components, considering pricelist rules
"""
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
70 changes: 22 additions & 48 deletions product_pack/models/product_product.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,62 +26,36 @@ 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()
packs, no_packs = self.with_context(whole_pack_price=True).split_pack_products()
ret_val = super(ProductProduct, no_packs)._compute_product_lst_price()
to_uom = None
uom = False
if "uom" in self._context:
to_uom = self.env["uom.uom"].browse([self._context["uom"]])
uom = self.env["uom.uom"].browse([self._context["uom"]])
for product in packs:
list_price = product.price_compute("list_price").get(product.id)
if to_uom:
list_price = product.uom_id._compute_price(list_price, to_uom)
# NOTE: This exception is to avoid adding the list price of the packs
# "totalized" and "non detailed". Should be removed to solve the issue #169.
if (
product.pack_type == "non_detailed"
or product.pack_component_price == "totalized"
):
list_price = 0
else:
list_price = product.price_compute("list_price", uom=uom).get(
product.id
)
list_price += sum(
product.pack_line_ids._pack_line_price_compute(
"list_price", uom=uom
).values()
)
product.lst_price = list_price + product.price_extra
return ret_val
3 changes: 3 additions & 0 deletions product_pack/readme/CONTRIBUTORS.rst
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@
* Juan José Scarafía
* Nicolas Mac Rouillon
* Katherine Zaoral
* Bruno Zanotti
* Augusto Weiss
* Nicolas Col
* `NaN·TIC <http://www.nan-tic.com>`_
* `Tecnativa <https://www.tecnativa.com>`_:

Expand Down
15 changes: 15 additions & 0 deletions product_pack/readme/USAGE.rst
Original file line number Diff line number Diff line change
Expand Up @@ -28,3 +28,18 @@ The options of this field are the followings:
* Ignored: will show each components but will not show
components prices. The pack product will be the only one that has price
and this one will be the price set in the pack product.

+-----------------------------+-----------------------------+---------------------------------+-----------------------------------------+----------------------+
| **Pack type** | **Show components on SO?** | **Sale price** | **Discount** | **Can be modified?** |
+=============================+=============================+=================================+=========================================+======================+
| **Detailed per components** | Yes, with their prices | Components + Pack | Applies to the price of the pack and | Yes, configurable |
| | | | the components | |
+-----------------------------+-----------------------------+---------------------------------+-----------------------------------------+----------------------+
| **Detailed - Totalized** | Yes, with their prices at 0 | Components | Applies to the total (pack + components)| No |
+-----------------------------+-----------------------------+---------------------------------+-----------------------------------------+----------------------+
| **Detailed - Ignored** | Yes, with their prices at 0 | Only Pack | Applies to the pack | No |
+-----------------------------+-----------------------------+---------------------------------+-----------------------------------------+----------------------+
| **No detailed** | No | Components | Applies to the total (pack + components)| No |
+-----------------------------+-----------------------------+---------------------------------+-----------------------------------------+----------------------+

**Note:** If pricelist enabled, Odoo will display the price according to the corresponding pricelist. In the case of a pricelist with discount policy "Show public price & discount to the customer" keep in mind that the "Non Detailed" and "Detailed - Totalized in main product" packs do not display the discount.
51 changes: 49 additions & 2 deletions product_pack/static/description/index.html
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
Expand Down Expand Up @@ -367,7 +366,7 @@ <h1 class="title">Product Pack</h1>
!! This file is generated by oca-gen-addon-readme !!
!! changes will be overwritten. !!
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!! source digest: sha256:d6d79cfb1448cba3c8cc68f89101f6b53245f8a695effd2058ca881dac328748
!! source digest: sha256:6412314c32b470ebaa9ba73edd671e2513bf0b8c9faa4703dcb22faf76692670
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -->
<p><a class="reference external image-reference" href="https://odoo-community.org/page/development-status"><img alt="Beta" src="https://img.shields.io/badge/maturity-Beta-yellow.png" /></a> <a class="reference external image-reference" href="http://www.gnu.org/licenses/agpl-3.0-standalone.html"><img alt="License: AGPL-3" src="https://img.shields.io/badge/licence-AGPL--3-blue.png" /></a> <a class="reference external image-reference" href="https://github.com/OCA/product-pack/tree/16.0/product_pack"><img alt="OCA/product-pack" src="https://img.shields.io/badge/github-OCA%2Fproduct--pack-lightgray.png?logo=github" /></a> <a class="reference external image-reference" href="https://translation.odoo-community.org/projects/product-pack-16-0/product-pack-16-0-product_pack"><img alt="Translate me on Weblate" src="https://img.shields.io/badge/weblate-Translate%20me-F47D42.png" /></a> <a class="reference external image-reference" href="https://runboat.odoo-community.org/builds?repo=OCA/product-pack&amp;target_branch=16.0"><img alt="Try me on Runboat" src="https://img.shields.io/badge/runboat-Try%20me-875A7B.png" /></a></p>
<p>This module allows you to define a product as a <em>Product Pack</em>. Each
Expand Down Expand Up @@ -422,6 +421,51 @@ <h1><a class="toc-backref" href="#toc-entry-1">Usage</a></h1>
and this one will be the price set in the pack product.</li>
</ul>
</blockquote>
<table border="1" class="docutils">
<colgroup>
<col width="19%" />
<col width="19%" />
<col width="21%" />
<col width="27%" />
<col width="14%" />
</colgroup>
<thead valign="bottom">
<tr><th class="head"><strong>Pack type</strong></th>
<th class="head"><strong>Show components on SO?</strong></th>
<th class="head"><strong>Sale price</strong></th>
<th class="head"><strong>Discount</strong></th>
<th class="head"><strong>Can be modified?</strong></th>
</tr>
</thead>
<tbody valign="top">
<tr><td><strong>Detailed per components</strong></td>
<td>Yes, with their prices</td>
<td>Components + Pack</td>
<td>Applies to the price of the pack and
the components</td>
<td>Yes, configurable</td>
</tr>
<tr><td><strong>Detailed - Totalized</strong></td>
<td>Yes, with their prices at 0</td>
<td>Components</td>
<td>Applies to the total (pack + components)</td>
<td>No</td>
</tr>
<tr><td><strong>Detailed - Ignored</strong></td>
<td>Yes, with their prices at 0</td>
<td>Only Pack</td>
<td>Applies to the pack</td>
<td>No</td>
</tr>
<tr><td><strong>No detailed</strong></td>
<td>No</td>
<td>Components</td>
<td>Applies to the total (pack + components)</td>
<td>No</td>
</tr>
</tbody>
</table>
<p><strong>Note:</strong> If pricelist enabled, Odoo will display the price according to the corresponding pricelist. In the case of a pricelist with discount policy “Show public price &amp; discount to the customer” keep in mind that the “Non Detailed” and “Detailed - Totalized in main product” packs do not display the discount.</p>
</div>
<div class="section" id="known-issues-roadmap">
<h1><a class="toc-backref" href="#toc-entry-2">Known issues / Roadmap</a></h1>
Expand Down Expand Up @@ -457,6 +501,9 @@ <h2><a class="toc-backref" href="#toc-entry-6">Contributors</a></h2>
<li>Juan José Scarafía</li>
<li>Nicolas Mac Rouillon</li>
<li>Katherine Zaoral</li>
<li>Bruno Zanotti</li>
<li>Augusto Weiss</li>
<li>Nicolas Col</li>
</ul>
</li>
<li><a class="reference external" href="http://www.nan-tic.com">NaN·TIC</a></li>
Expand Down
17 changes: 17 additions & 0 deletions product_pack/tests/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,3 +14,20 @@ def setUpClass(cls):
"name": "Company Pack 2",
}
cls.company_2 = cls.env["res.company"].create(vals)
cls.discount_pricelist = cls.env["product.pricelist"].create(
{
"name": "Discount",
"company_id": cls.env.company.id,
"item_ids": [
(
0,
0,
{
"applied_on": "3_global",
"compute_price": "percentage",
"percent_price": 10,
},
)
],
}
)
Loading
Loading