From 0655a516e269a2f87cded7e0b0db47ddaf801f2f Mon Sep 17 00:00:00 2001 From: Simone Orsi Date: Tue, 26 May 2020 10:40:07 +0200 Subject: [PATCH 01/25] Add stock_packaging_calculator --- stock_packaging_calculator/__init__.py | 1 + stock_packaging_calculator/__manifest__.py | 15 +++++ stock_packaging_calculator/models/__init__.py | 1 + stock_packaging_calculator/models/product.py | 51 ++++++++++++++ .../readme/CONTRIBUTORS.rst | 1 + .../readme/DESCRIPTION.rst | 18 +++++ stock_packaging_calculator/readme/ROADMAP.rst | 4 ++ stock_packaging_calculator/tests/__init__.py | 1 + .../tests/test_packaging_calc.py | 66 +++++++++++++++++++ 9 files changed, 158 insertions(+) create mode 100644 stock_packaging_calculator/__init__.py create mode 100644 stock_packaging_calculator/__manifest__.py create mode 100644 stock_packaging_calculator/models/__init__.py create mode 100644 stock_packaging_calculator/models/product.py create mode 100644 stock_packaging_calculator/readme/CONTRIBUTORS.rst create mode 100644 stock_packaging_calculator/readme/DESCRIPTION.rst create mode 100644 stock_packaging_calculator/readme/ROADMAP.rst create mode 100644 stock_packaging_calculator/tests/__init__.py create mode 100644 stock_packaging_calculator/tests/test_packaging_calc.py diff --git a/stock_packaging_calculator/__init__.py b/stock_packaging_calculator/__init__.py new file mode 100644 index 000000000..0650744f6 --- /dev/null +++ b/stock_packaging_calculator/__init__.py @@ -0,0 +1 @@ +from . import models diff --git a/stock_packaging_calculator/__manifest__.py b/stock_packaging_calculator/__manifest__.py new file mode 100644 index 000000000..4399bba90 --- /dev/null +++ b/stock_packaging_calculator/__manifest__.py @@ -0,0 +1,15 @@ +# Copyright 2020 Camptocamp SA +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl) +{ + "name": "Stock packaging calculator", + "summary": "Compute product quantity to pick by packaging", + "version": "13.0.1.0.0", + "development_status": "Alpha", + "category": "Warehouse Management", + "website": "https://github.com/OCA/stock-logistics-warehouse", + "author": "Camptocamp, Odoo Community Association (OCA)", + "license": "AGPL-3", + "application": False, + "installable": True, + "depends": ["product"], +} diff --git a/stock_packaging_calculator/models/__init__.py b/stock_packaging_calculator/models/__init__.py new file mode 100644 index 000000000..9649db77a --- /dev/null +++ b/stock_packaging_calculator/models/__init__.py @@ -0,0 +1 @@ +from . import product diff --git a/stock_packaging_calculator/models/product.py b/stock_packaging_calculator/models/product.py new file mode 100644 index 000000000..5dd73bac3 --- /dev/null +++ b/stock_packaging_calculator/models/product.py @@ -0,0 +1,51 @@ +# Copyright 2020 Camptocamp SA +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl) + +from odoo import models +from odoo.tools import float_compare + + +class Product(models.Model): + _inherit = "product.product" + + def product_qty_by_packaging(self, prod_qty, min_unit=None): + """Calculate quantity by packaging. + + Limitation: fractional quantities are lost. + + :prod_qty: total qty to satisfy. + :min_unit: minimal unit of measure as a tuple (qty, name). + Default: to UoM unit. + :returns: list of tuple in the form [(qty_per_package, package_name)] + + """ + packagings = [(x.qty, x.name) for x in self.packaging_ids] + if min_unit is None: + # You can pass `False` to skip it. + single_unit = self.uom_id + min_unit = (single_unit.factor, single_unit.name) + if min_unit: + packagings.append(min_unit) + return self._product_qty_by_packaging( + sorted(packagings, reverse=True), prod_qty + ) + + def _product_qty_by_packaging(self, pkg_by_qty, qty): + """Produce a list of tuple of packaging qty and packaging name.""" + # TODO: refactor to handle fractional quantities (eg: 0.5 Kg) + res = [] + for pkg_qty, pkg in pkg_by_qty: + qty_per_pkg, qty = self._qty_by_pkg(pkg_qty, qty) + if qty_per_pkg: + res.append((qty_per_pkg, pkg)) + if not qty: + break + return res + + def _qty_by_pkg(self, pkg_qty, qty): + """Calculate qty needed for given package qty.""" + qty_per_pkg = 0 + while float_compare(qty - pkg_qty, 0.0, precision_digits=3) >= 0.0: + qty -= pkg_qty + qty_per_pkg += 1 + return qty_per_pkg, qty diff --git a/stock_packaging_calculator/readme/CONTRIBUTORS.rst b/stock_packaging_calculator/readme/CONTRIBUTORS.rst new file mode 100644 index 000000000..f583948be --- /dev/null +++ b/stock_packaging_calculator/readme/CONTRIBUTORS.rst @@ -0,0 +1 @@ +* Simone Orsi diff --git a/stock_packaging_calculator/readme/DESCRIPTION.rst b/stock_packaging_calculator/readme/DESCRIPTION.rst new file mode 100644 index 000000000..b470f8a94 --- /dev/null +++ b/stock_packaging_calculator/readme/DESCRIPTION.rst @@ -0,0 +1,18 @@ + +Basic module providing an helper method to calculate the quantity of product by packaging. + +Imagine you have the following packagings: + +* Pallet: 1000 Units +* Big box: 500 Units +* Box: 50 Units + +and you have to pick from your warehouse 2860 Units. + +Then you can do: + + >>> product.product_qty_by_packaging(2860) + + [(2, "Pallet"), (1, "Big Box"), (7, "Box"), (10, "Units")] + +With this you can show a proper message to warehouse operators to quickly pick the quantity they need. diff --git a/stock_packaging_calculator/readme/ROADMAP.rst b/stock_packaging_calculator/readme/ROADMAP.rst new file mode 100644 index 000000000..e4881d5e6 --- /dev/null +++ b/stock_packaging_calculator/readme/ROADMAP.rst @@ -0,0 +1,4 @@ +TODO + +1. Fractional quantities (eg: 0.5 Kg) are lost when counting units +2. Maybe rely on `packaging_uom` diff --git a/stock_packaging_calculator/tests/__init__.py b/stock_packaging_calculator/tests/__init__.py new file mode 100644 index 000000000..04c47e711 --- /dev/null +++ b/stock_packaging_calculator/tests/__init__.py @@ -0,0 +1 @@ +from . import test_packaging_calc diff --git a/stock_packaging_calculator/tests/test_packaging_calc.py b/stock_packaging_calculator/tests/test_packaging_calc.py new file mode 100644 index 000000000..c5836b941 --- /dev/null +++ b/stock_packaging_calculator/tests/test_packaging_calc.py @@ -0,0 +1,66 @@ +# Copyright 2020 Camptocamp SA +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl) +from odoo.tests import SavepointCase + + +class TestCalc(SavepointCase): + @classmethod + def setUpClass(cls): + super().setUpClass() + cls.env = cls.env(context=dict(cls.env.context, tracking_disable=True)) + cls.uom_unit = cls.env.ref("uom.product_uom_unit") + cls.uom_kg = cls.env.ref("uom.product_uom_kgm") + cls.product_a = cls.env["product.product"].create( + { + "name": "Product A", + "type": "product", + "uom_id": cls.uom_unit.id, + "uom_po_id": cls.uom_unit.id, + } + ) + cls.pkg_box = cls.env["product.packaging"].create( + {"name": "Box", "product_id": cls.product_a.id, "qty": 50} + ) + cls.pkg_big_box = cls.env["product.packaging"].create( + {"name": "Big Box", "product_id": cls.product_a.id, "qty": 200} + ) + cls.pkg_pallet = cls.env["product.packaging"].create( + {"name": "Pallet", "product_id": cls.product_a.id, "qty": 2000} + ) + + def test_calc_1(self): + """Test easy behavior 1.""" + self.assertEqual( + self.product_a.product_qty_by_packaging(2655), + [(1, "Pallet"), (3, "Big Box"), (1, "Box"), (5, self.uom_unit.name)], + ) + + def test_calc_2(self): + """Test easy behavior 2.""" + self.assertEqual( + self.product_a.product_qty_by_packaging(350), [(1, "Big Box"), (3, "Box")] + ) + + def test_calc_3(self): + """Test easy behavior 3.""" + self.assertEqual( + self.product_a.product_qty_by_packaging(80), + [(1, "Box"), (30, self.uom_unit.name)], + ) + + def test_calc_4(self): + """Test minimal unit override.""" + self.assertEqual( + self.product_a.product_qty_by_packaging(80, min_unit=(5, "Pack 5")), + [(1, "Box"), (6, "Pack 5")], + ) + + def test_calc_5(self): + """Test no minimal unit.""" + self.assertEqual( + self.product_a.product_qty_by_packaging(80, min_unit=False), [(1, "Box")] + ) + + def test_calc_6(self): + """Test fractional qty is lost.""" + self.assertEqual(self.product_a.product_qty_by_packaging(50.5), [(1, "Box")]) From b8ef0cbc75353093498b41e723980ed39c5d5345 Mon Sep 17 00:00:00 2001 From: Simone Orsi Date: Mon, 8 Jun 2020 09:56:12 +0200 Subject: [PATCH 02/25] stock_packaging_calculator: make product uom the minimal unit Customizing the minimal unit was not needed at all. This way we always assume the precision is the on of the UoM. --- stock_packaging_calculator/models/product.py | 20 +++++++++---------- .../tests/test_packaging_calc.py | 13 ------------ 2 files changed, 9 insertions(+), 24 deletions(-) diff --git a/stock_packaging_calculator/models/product.py b/stock_packaging_calculator/models/product.py index 5dd73bac3..68907d2a4 100644 --- a/stock_packaging_calculator/models/product.py +++ b/stock_packaging_calculator/models/product.py @@ -8,24 +8,19 @@ from odoo.tools import float_compare class Product(models.Model): _inherit = "product.product" - def product_qty_by_packaging(self, prod_qty, min_unit=None): + def product_qty_by_packaging(self, prod_qty): """Calculate quantity by packaging. + The minimal quantity is always represented by the UoM of the product. + Limitation: fractional quantities are lost. :prod_qty: total qty to satisfy. - :min_unit: minimal unit of measure as a tuple (qty, name). - Default: to UoM unit. :returns: list of tuple in the form [(qty_per_package, package_name)] - """ packagings = [(x.qty, x.name) for x in self.packaging_ids] - if min_unit is None: - # You can pass `False` to skip it. - single_unit = self.uom_id - min_unit = (single_unit.factor, single_unit.name) - if min_unit: - packagings.append(min_unit) + # Add minimal unit + packagings.append((self.uom_id.factor, self.uom_id.name)) return self._product_qty_by_packaging( sorted(packagings, reverse=True), prod_qty ) @@ -45,7 +40,10 @@ class Product(models.Model): def _qty_by_pkg(self, pkg_qty, qty): """Calculate qty needed for given package qty.""" qty_per_pkg = 0 - while float_compare(qty - pkg_qty, 0.0, precision_digits=3) >= 0.0: + while ( + float_compare(qty - pkg_qty, 0.0, precision_digits=self.uom_id.rounding) + >= 0.0 + ): qty -= pkg_qty qty_per_pkg += 1 return qty_per_pkg, qty diff --git a/stock_packaging_calculator/tests/test_packaging_calc.py b/stock_packaging_calculator/tests/test_packaging_calc.py index c5836b941..af1c11541 100644 --- a/stock_packaging_calculator/tests/test_packaging_calc.py +++ b/stock_packaging_calculator/tests/test_packaging_calc.py @@ -48,19 +48,6 @@ class TestCalc(SavepointCase): [(1, "Box"), (30, self.uom_unit.name)], ) - def test_calc_4(self): - """Test minimal unit override.""" - self.assertEqual( - self.product_a.product_qty_by_packaging(80, min_unit=(5, "Pack 5")), - [(1, "Box"), (6, "Pack 5")], - ) - - def test_calc_5(self): - """Test no minimal unit.""" - self.assertEqual( - self.product_a.product_qty_by_packaging(80, min_unit=False), [(1, "Box")] - ) - def test_calc_6(self): """Test fractional qty is lost.""" self.assertEqual(self.product_a.product_qty_by_packaging(50.5), [(1, "Box")]) From d411a3ac098dada7ce3ad42b6db5bed5fa29154c Mon Sep 17 00:00:00 2001 From: oca-travis Date: Mon, 8 Jun 2020 08:00:41 +0000 Subject: [PATCH 03/25] [UPD] Update stock_packaging_calculator.pot --- .../i18n/stock_packaging_calculator.pot | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 stock_packaging_calculator/i18n/stock_packaging_calculator.pot diff --git a/stock_packaging_calculator/i18n/stock_packaging_calculator.pot b/stock_packaging_calculator/i18n/stock_packaging_calculator.pot new file mode 100644 index 000000000..775124a33 --- /dev/null +++ b/stock_packaging_calculator/i18n/stock_packaging_calculator.pot @@ -0,0 +1,19 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * stock_packaging_calculator +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 13.0\n" +"Report-Msgid-Bugs-To: \n" +"Last-Translator: \n" +"Language-Team: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: \n" +"Plural-Forms: \n" + +#. module: stock_packaging_calculator +#: model:ir.model,name:stock_packaging_calculator.model_product_product +msgid "Product" +msgstr "" From 3de5642d78f413b09b8efb990db131e888131391 Mon Sep 17 00:00:00 2001 From: Simone Orsi Date: Mon, 8 Jun 2020 10:18:02 +0200 Subject: [PATCH 04/25] stock_packaging_calculator: return dict instead of tuple Allows to ship more information with each element in the list. --- stock_packaging_calculator/models/product.py | 27 ++++++++++----- .../tests/test_packaging_calc.py | 33 ++++++++++++------- 2 files changed, 40 insertions(+), 20 deletions(-) diff --git a/stock_packaging_calculator/models/product.py b/stock_packaging_calculator/models/product.py index 68907d2a4..d5dca58e1 100644 --- a/stock_packaging_calculator/models/product.py +++ b/stock_packaging_calculator/models/product.py @@ -1,9 +1,14 @@ # Copyright 2020 Camptocamp SA # License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl) +from collections import namedtuple + from odoo import models from odoo.tools import float_compare +# Unify records as we mix up w/ UoM +Packaging = namedtuple("Packaging", "id name qty") + class Product(models.Model): _inherit = "product.product" @@ -16,23 +21,29 @@ class Product(models.Model): Limitation: fractional quantities are lost. :prod_qty: total qty to satisfy. - :returns: list of tuple in the form [(qty_per_package, package_name)] + :with_subpackaging: include calculation of contained packagings. + eg: 1 pallet contains 4 big boxes and 6 little boxes. + :returns: list of dict in the form + [{id: 1, qty: qty_per_package, name: package_name}] """ - packagings = [(x.qty, x.name) for x in self.packaging_ids] + packagings = [Packaging(x.id, x.name, x.qty) for x in self.packaging_ids] # Add minimal unit - packagings.append((self.uom_id.factor, self.uom_id.name)) + packagings.append( + Packaging(self.uom_id.id, self.uom_id.name, self.uom_id.factor) + ) return self._product_qty_by_packaging( - sorted(packagings, reverse=True), prod_qty + sorted(packagings, reverse=True, key=lambda x: x.qty), prod_qty, ) def _product_qty_by_packaging(self, pkg_by_qty, qty): - """Produce a list of tuple of packaging qty and packaging name.""" + """Produce a list of dictionaries of packaging info.""" # TODO: refactor to handle fractional quantities (eg: 0.5 Kg) res = [] - for pkg_qty, pkg in pkg_by_qty: - qty_per_pkg, qty = self._qty_by_pkg(pkg_qty, qty) + for pkg in pkg_by_qty: + qty_per_pkg, qty = self._qty_by_pkg(pkg.qty, qty) if qty_per_pkg: - res.append((qty_per_pkg, pkg)) + value = {"id": pkg.id, "qty": qty_per_pkg, "name": pkg.name} + res.append(value) if not qty: break return res diff --git a/stock_packaging_calculator/tests/test_packaging_calc.py b/stock_packaging_calculator/tests/test_packaging_calc.py index af1c11541..b25d8cb9c 100644 --- a/stock_packaging_calculator/tests/test_packaging_calc.py +++ b/stock_packaging_calculator/tests/test_packaging_calc.py @@ -30,24 +30,33 @@ class TestCalc(SavepointCase): def test_calc_1(self): """Test easy behavior 1.""" - self.assertEqual( - self.product_a.product_qty_by_packaging(2655), - [(1, "Pallet"), (3, "Big Box"), (1, "Box"), (5, self.uom_unit.name)], - ) + expected = [ + {"id": self.pkg_pallet.id, "qty": 1, "name": self.pkg_pallet.name}, + {"id": self.pkg_big_box.id, "qty": 3, "name": self.pkg_big_box.name}, + {"id": self.pkg_box.id, "qty": 1, "name": self.pkg_box.name}, + {"id": self.uom_unit.id, "qty": 5, "name": self.uom_unit.name}, + ] + self.assertEqual(self.product_a.product_qty_by_packaging(2655), expected) def test_calc_2(self): """Test easy behavior 2.""" - self.assertEqual( - self.product_a.product_qty_by_packaging(350), [(1, "Big Box"), (3, "Box")] - ) + expected = [ + {"id": self.pkg_big_box.id, "qty": 1, "name": self.pkg_big_box.name}, + {"id": self.pkg_box.id, "qty": 3, "name": self.pkg_box.name}, + ] + self.assertEqual(self.product_a.product_qty_by_packaging(350), expected) def test_calc_3(self): """Test easy behavior 3.""" - self.assertEqual( - self.product_a.product_qty_by_packaging(80), - [(1, "Box"), (30, self.uom_unit.name)], - ) + expected = [ + {"id": self.pkg_box.id, "qty": 1, "name": self.pkg_box.name}, + {"id": self.uom_unit.id, "qty": 30, "name": self.uom_unit.name}, + ] + self.assertEqual(self.product_a.product_qty_by_packaging(80), expected) def test_calc_6(self): """Test fractional qty is lost.""" - self.assertEqual(self.product_a.product_qty_by_packaging(50.5), [(1, "Box")]) + expected = [ + {"id": self.pkg_box.id, "qty": 1, "name": self.pkg_box.name}, + ] + self.assertEqual(self.product_a.product_qty_by_packaging(50.5), expected) From 03a1d8ffc4ddb745df7dadfb9c282e2217df6bbc Mon Sep 17 00:00:00 2001 From: OCA-git-bot Date: Mon, 8 Jun 2020 08:25:57 +0000 Subject: [PATCH 05/25] [UPD] README.rst --- stock_packaging_calculator/README.rst | 103 ++++ .../static/description/index.html | 449 ++++++++++++++++++ 2 files changed, 552 insertions(+) create mode 100644 stock_packaging_calculator/README.rst create mode 100644 stock_packaging_calculator/static/description/index.html diff --git a/stock_packaging_calculator/README.rst b/stock_packaging_calculator/README.rst new file mode 100644 index 000000000..54e81badc --- /dev/null +++ b/stock_packaging_calculator/README.rst @@ -0,0 +1,103 @@ +========================== +Stock packaging calculator +========================== + +.. !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! This file is generated by oca-gen-addon-readme !! + !! changes will be overwritten. !! + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + +.. |badge1| image:: https://img.shields.io/badge/maturity-Alpha-red.png + :target: https://odoo-community.org/page/development-status + :alt: Alpha +.. |badge2| image:: https://img.shields.io/badge/licence-AGPL--3-blue.png + :target: http://www.gnu.org/licenses/agpl-3.0-standalone.html + :alt: License: AGPL-3 +.. |badge3| image:: https://img.shields.io/badge/github-OCA%2Fstock--logistics--warehouse-lightgray.png?logo=github + :target: https://github.com/OCA/stock-logistics-warehouse/tree/13.0/stock_packaging_calculator + :alt: OCA/stock-logistics-warehouse +.. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png + :target: https://translation.odoo-community.org/projects/stock-logistics-warehouse-13-0/stock-logistics-warehouse-13-0-stock_packaging_calculator + :alt: Translate me on Weblate +.. |badge5| image:: https://img.shields.io/badge/runbot-Try%20me-875A7B.png + :target: https://runbot.odoo-community.org/runbot/153/13.0 + :alt: Try me on Runbot + +|badge1| |badge2| |badge3| |badge4| |badge5| + + +Basic module providing an helper method to calculate the quantity of product by packaging. + +Imagine you have the following packagings: + +* Pallet: 1000 Units +* Big box: 500 Units +* Box: 50 Units + +and you have to pick from your warehouse 2860 Units. + +Then you can do: + + >>> product.product_qty_by_packaging(2860) + + [(2, "Pallet"), (1, "Big Box"), (7, "Box"), (10, "Units")] + +With this you can show a proper message to warehouse operators to quickly pick the quantity they need. + +.. IMPORTANT:: + This is an alpha version, the data model and design can change at any time without warning. + Only for development or testing purpose, do not use in production. + `More details on development status `_ + +**Table of contents** + +.. contents:: + :local: + +Known issues / Roadmap +====================== + +TODO + +1. Fractional quantities (eg: 0.5 Kg) are lost when counting units +2. Maybe rely on `packaging_uom` + +Bug Tracker +=========== + +Bugs are tracked on `GitHub Issues `_. +In case of trouble, please check there if your issue has already been reported. +If you spotted it first, help us smashing it by providing a detailed and welcomed +`feedback `_. + +Do not contact contributors directly about support or help with technical issues. + +Credits +======= + +Authors +~~~~~~~ + +* Camptocamp + +Contributors +~~~~~~~~~~~~ + +* Simone Orsi + +Maintainers +~~~~~~~~~~~ + +This module is maintained by the OCA. + +.. image:: https://odoo-community.org/logo.png + :alt: Odoo Community Association + :target: https://odoo-community.org + +OCA, or the Odoo Community Association, is a nonprofit organization whose +mission is to support the collaborative development of Odoo features and +promote its widespread use. + +This module is part of the `OCA/stock-logistics-warehouse `_ project on GitHub. + +You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute. diff --git a/stock_packaging_calculator/static/description/index.html b/stock_packaging_calculator/static/description/index.html new file mode 100644 index 000000000..b786d10ea --- /dev/null +++ b/stock_packaging_calculator/static/description/index.html @@ -0,0 +1,449 @@ + + + + + + +Stock packaging calculator + + + +
+

Stock packaging calculator

+ + +

Alpha License: AGPL-3 OCA/stock-logistics-warehouse Translate me on Weblate Try me on Runbot

+

Basic module providing an helper method to calculate the quantity of product by packaging.

+

Imagine you have the following packagings:

+
    +
  • Pallet: 1000 Units
  • +
  • Big box: 500 Units
  • +
  • Box: 50 Units
  • +
+

and you have to pick from your warehouse 2860 Units.

+

Then you can do:

+
+
+>>> product.product_qty_by_packaging(2860)
+
+

[(2, “Pallet”), (1, “Big Box”), (7, “Box”), (10, “Units”)]

+
+

With this you can show a proper message to warehouse operators to quickly pick the quantity they need.

+
+

Important

+

This is an alpha version, the data model and design can change at any time without warning. +Only for development or testing purpose, do not use in production. +More details on development status

+
+

Table of contents

+ +
+

Known issues / Roadmap

+

TODO

+
    +
  1. Fractional quantities (eg: 0.5 Kg) are lost when counting units
  2. +
  3. Maybe rely on packaging_uom
  4. +
+
+
+

Bug Tracker

+

Bugs are tracked on GitHub Issues. +In case of trouble, please check there if your issue has already been reported. +If you spotted it first, help us smashing it by providing a detailed and welcomed +feedback.

+

Do not contact contributors directly about support or help with technical issues.

+
+
+

Credits

+
+

Authors

+
    +
  • Camptocamp
  • +
+
+
+

Contributors

+ +
+
+

Maintainers

+

This module is maintained by the OCA.

+Odoo Community Association +

OCA, or the Odoo Community Association, is a nonprofit organization whose +mission is to support the collaborative development of Odoo features and +promote its widespread use.

+

This module is part of the OCA/stock-logistics-warehouse project on GitHub.

+

You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute.

+
+
+
+ + From 0b76b06a0fc5db6d512b8b22c662904ee11c5b33 Mon Sep 17 00:00:00 2001 From: OCA-git-bot Date: Mon, 8 Jun 2020 08:25:58 +0000 Subject: [PATCH 06/25] [ADD] icon.png --- .../static/description/icon.png | Bin 0 -> 9455 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 stock_packaging_calculator/static/description/icon.png diff --git a/stock_packaging_calculator/static/description/icon.png b/stock_packaging_calculator/static/description/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..3a0328b516c4980e8e44cdb63fd945757ddd132d GIT binary patch literal 9455 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 z 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 Xviia!U7SGha1wx#SCgwmn*{w2TRX*I literal 0 HcmV?d00001 From 49d45b461cbba324c8cc65d42c5b4a314466e72e Mon Sep 17 00:00:00 2001 From: Simone Orsi Date: Mon, 8 Jun 2020 11:47:32 +0200 Subject: [PATCH 07/25] stock_packaging_calculator: add contained packaging compute Optionally include contained packaging qty. --- stock_packaging_calculator/models/product.py | 26 ++++-- .../readme/DESCRIPTION.rst | 17 ---- stock_packaging_calculator/readme/USAGE.rst | 36 ++++++++ .../tests/test_packaging_calc.py | 89 ++++++++++++++++++- 4 files changed, 145 insertions(+), 23 deletions(-) create mode 100644 stock_packaging_calculator/readme/USAGE.rst diff --git a/stock_packaging_calculator/models/product.py b/stock_packaging_calculator/models/product.py index d5dca58e1..2679b0fd6 100644 --- a/stock_packaging_calculator/models/product.py +++ b/stock_packaging_calculator/models/product.py @@ -13,7 +13,7 @@ Packaging = namedtuple("Packaging", "id name qty") class Product(models.Model): _inherit = "product.product" - def product_qty_by_packaging(self, prod_qty): + def product_qty_by_packaging(self, prod_qty, with_contained=False): """Calculate quantity by packaging. The minimal quantity is always represented by the UoM of the product. @@ -21,28 +21,44 @@ class Product(models.Model): Limitation: fractional quantities are lost. :prod_qty: total qty to satisfy. - :with_subpackaging: include calculation of contained packagings. + :with_contained: include calculation of contained packagings. + eg: 1 pallet contains 4 big boxes and 6 little boxes. + :returns: list of dict in the form + [{id: 1, qty: qty_per_package, name: package_name}] + + If `with_contained` is passed, each element will include + the quantity of smaller packaging, like: + + {contained: [{id: 1, qty: 4, name: "Big box"}]} """ packagings = [Packaging(x.id, x.name, x.qty) for x in self.packaging_ids] # Add minimal unit packagings.append( + # NOTE: the ID here could clash w/ one of the packaging's. + # If you create a mapping based on IDs, keep this in mind. Packaging(self.uom_id.id, self.uom_id.name, self.uom_id.factor) ) return self._product_qty_by_packaging( - sorted(packagings, reverse=True, key=lambda x: x.qty), prod_qty, + sorted(packagings, reverse=True, key=lambda x: x.qty), + prod_qty, + with_contained=with_contained, ) - def _product_qty_by_packaging(self, pkg_by_qty, qty): + def _product_qty_by_packaging(self, pkg_by_qty, qty, with_contained=False): """Produce a list of dictionaries of packaging info.""" # TODO: refactor to handle fractional quantities (eg: 0.5 Kg) res = [] - for pkg in pkg_by_qty: + for i, pkg in enumerate(pkg_by_qty): qty_per_pkg, qty = self._qty_by_pkg(pkg.qty, qty) if qty_per_pkg: value = {"id": pkg.id, "qty": qty_per_pkg, "name": pkg.name} + if with_contained: + value["contained"] = self._product_qty_by_packaging( + pkg_by_qty[i + 1 :], pkg.qty + ) res.append(value) if not qty: break diff --git a/stock_packaging_calculator/readme/DESCRIPTION.rst b/stock_packaging_calculator/readme/DESCRIPTION.rst index b470f8a94..1d6fceb5c 100644 --- a/stock_packaging_calculator/readme/DESCRIPTION.rst +++ b/stock_packaging_calculator/readme/DESCRIPTION.rst @@ -1,18 +1 @@ - Basic module providing an helper method to calculate the quantity of product by packaging. - -Imagine you have the following packagings: - -* Pallet: 1000 Units -* Big box: 500 Units -* Box: 50 Units - -and you have to pick from your warehouse 2860 Units. - -Then you can do: - - >>> product.product_qty_by_packaging(2860) - - [(2, "Pallet"), (1, "Big Box"), (7, "Box"), (10, "Units")] - -With this you can show a proper message to warehouse operators to quickly pick the quantity they need. diff --git a/stock_packaging_calculator/readme/USAGE.rst b/stock_packaging_calculator/readme/USAGE.rst new file mode 100644 index 000000000..73f05a708 --- /dev/null +++ b/stock_packaging_calculator/readme/USAGE.rst @@ -0,0 +1,36 @@ +Imagine you have the following packagings: + +* Pallet: 1000 Units +* Big box: 500 Units +* Box: 50 Units + +and you have to pick from your warehouse 2860 Units. + +Then you can do: + + .. code-block:: + + >>> product.product_qty_by_packaging(2860) + + [ + {"id": 1, "qty": 2, "name": "Pallet"}, + {"id": 2, "qty": 1, "name": "Big box"}, + {"id": 3, "qty": 7, "name": "Box"}, + {"id": 100, "qty": 10, "name": "Units"}, + ] + +With this you can show a proper message to warehouse operators to quickly pick the quantity they need. + +Optionally you can get contained packaging by passing `with_contained` flag: + + + .. code-block:: + + >>> product.product_qty_by_packaging(2860, with_contained=True) + + [ + {"id": 1, "qty": 2, "name": "Pallet", "contained": [{"id": 2, "qty": 2, "name": "Big box"}]}, + {"id": 2, "qty": 1, "name": "Big box", "contained": [{"id": 3, "qty": 10, "name": "Box"}]}, + {"id": 3, "qty": 7, "name": "Box", "contained": [{"id": 100, "qty": 50, "name": "Units"}]}, + {"id": 100, "qty": 10, "name": "Units", "contained": []},}, + ] diff --git a/stock_packaging_calculator/tests/test_packaging_calc.py b/stock_packaging_calculator/tests/test_packaging_calc.py index b25d8cb9c..39d03e01b 100644 --- a/stock_packaging_calculator/tests/test_packaging_calc.py +++ b/stock_packaging_calculator/tests/test_packaging_calc.py @@ -9,7 +9,6 @@ class TestCalc(SavepointCase): super().setUpClass() cls.env = cls.env(context=dict(cls.env.context, tracking_disable=True)) cls.uom_unit = cls.env.ref("uom.product_uom_unit") - cls.uom_kg = cls.env.ref("uom.product_uom_kgm") cls.product_a = cls.env["product.product"].create( { "name": "Product A", @@ -60,3 +59,91 @@ class TestCalc(SavepointCase): {"id": self.pkg_box.id, "qty": 1, "name": self.pkg_box.name}, ] self.assertEqual(self.product_a.product_qty_by_packaging(50.5), expected) + + def test_calc_sub1(self): + """Test contained packaging behavior 1.""" + expected = [ + { + "id": self.pkg_pallet.id, + "qty": 1, + "name": self.pkg_pallet.name, + "contained": [ + { + "id": self.pkg_big_box.id, + "qty": 10, + "name": self.pkg_big_box.name, + }, + ], + }, + { + "id": self.pkg_big_box.id, + "qty": 3, + "name": self.pkg_big_box.name, + "contained": [ + {"id": self.pkg_box.id, "qty": 4, "name": self.pkg_box.name}, + ], + }, + { + "id": self.pkg_box.id, + "qty": 1, + "name": self.pkg_box.name, + "contained": [ + {"id": self.uom_unit.id, "qty": 50, "name": self.uom_unit.name}, + ], + }, + { + "id": self.uom_unit.id, + "qty": 5, + "name": self.uom_unit.name, + "contained": [], + }, + ] + self.assertEqual( + self.product_a.product_qty_by_packaging(2655, with_contained=True), + expected, + ) + + def test_calc_sub2(self): + """Test contained packaging behavior 1.""" + self.pkg_box.qty = 30 + expected = [ + { + "id": self.pkg_pallet.id, + "qty": 1, + "name": self.pkg_pallet.name, + "contained": [ + { + "id": self.pkg_big_box.id, + "qty": 10, + "name": self.pkg_big_box.name, + }, + ], + }, + { + "id": self.pkg_big_box.id, + "qty": 3, + "name": self.pkg_big_box.name, + "contained": [ + {"id": self.pkg_box.id, "qty": 6, "name": self.pkg_box.name}, + {"id": self.uom_unit.id, "qty": 20, "name": self.uom_unit.name}, + ], + }, + { + "id": self.pkg_box.id, + "qty": 1, + "name": self.pkg_box.name, + "contained": [ + {"id": self.uom_unit.id, "qty": 30, "name": self.uom_unit.name}, + ], + }, + { + "id": self.uom_unit.id, + "qty": 25, + "name": self.uom_unit.name, + "contained": [], + }, + ] + self.assertEqual( + self.product_a.product_qty_by_packaging(2655, with_contained=True), + expected, + ) From 60ba21960f520b29ea21562fd7c5ad7dd7bd8260 Mon Sep 17 00:00:00 2001 From: OCA-git-bot Date: Tue, 9 Jun 2020 08:09:46 +0000 Subject: [PATCH 08/25] [UPD] README.rst --- stock_packaging_calculator/README.rst | 43 ++++++++--- .../static/description/index.html | 77 ++++++++++++------- 2 files changed, 83 insertions(+), 37 deletions(-) diff --git a/stock_packaging_calculator/README.rst b/stock_packaging_calculator/README.rst index 54e81badc..5bde39e25 100644 --- a/stock_packaging_calculator/README.rst +++ b/stock_packaging_calculator/README.rst @@ -25,9 +25,21 @@ Stock packaging calculator |badge1| |badge2| |badge3| |badge4| |badge5| - Basic module providing an helper method to calculate the quantity of product by packaging. +.. IMPORTANT:: + This is an alpha version, the data model and design can change at any time without warning. + Only for development or testing purpose, do not use in production. + `More details on development status `_ + +**Table of contents** + +.. contents:: + :local: + +Usage +===== + Imagine you have the following packagings: * Pallet: 1000 Units @@ -38,21 +50,32 @@ and you have to pick from your warehouse 2860 Units. Then you can do: - >>> product.product_qty_by_packaging(2860) + .. code-block:: - [(2, "Pallet"), (1, "Big Box"), (7, "Box"), (10, "Units")] + >>> product.product_qty_by_packaging(2860) + + [ + {"id": 1, "qty": 2, "name": "Pallet"}, + {"id": 2, "qty": 1, "name": "Big box"}, + {"id": 3, "qty": 7, "name": "Box"}, + {"id": 100, "qty": 10, "name": "Units"}, + ] With this you can show a proper message to warehouse operators to quickly pick the quantity they need. -.. IMPORTANT:: - This is an alpha version, the data model and design can change at any time without warning. - Only for development or testing purpose, do not use in production. - `More details on development status `_ +Optionally you can get contained packaging by passing `with_contained` flag: -**Table of contents** -.. contents:: - :local: + .. code-block:: + + >>> product.product_qty_by_packaging(2860, with_contained=True) + + [ + {"id": 1, "qty": 2, "name": "Pallet", "contained": [{"id": 2, "qty": 2, "name": "Big box"}]}, + {"id": 2, "qty": 1, "name": "Big box", "contained": [{"id": 3, "qty": 10, "name": "Box"}]}, + {"id": 3, "qty": 7, "name": "Box", "contained": [{"id": 100, "qty": 50, "name": "Units"}]}, + {"id": 100, "qty": 10, "name": "Units", "contained": []},}, + ] Known issues / Roadmap ====================== diff --git a/stock_packaging_calculator/static/description/index.html b/stock_packaging_calculator/static/description/index.html index b786d10ea..62793c551 100644 --- a/stock_packaging_calculator/static/description/index.html +++ b/stock_packaging_calculator/static/description/index.html @@ -369,21 +369,6 @@ ul.auto-toc { !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -->

Alpha License: AGPL-3 OCA/stock-logistics-warehouse Translate me on Weblate Try me on Runbot

Basic module providing an helper method to calculate the quantity of product by packaging.

-

Imagine you have the following packagings:

-
    -
  • Pallet: 1000 Units
  • -
  • Big box: 500 Units
  • -
  • Box: 50 Units
  • -
-

and you have to pick from your warehouse 2860 Units.

-

Then you can do:

-
-
->>> product.product_qty_by_packaging(2860)
-
-

[(2, “Pallet”), (1, “Big Box”), (7, “Box”), (10, “Units”)]

-
-

With this you can show a proper message to warehouse operators to quickly pick the quantity they need.

Important

This is an alpha version, the data model and design can change at any time without warning. @@ -393,18 +378,56 @@ Only for development or testing purpose, do not use in production.

Table of contents

+
+

Usage

+

Imagine you have the following packagings:

+
    +
  • Pallet: 1000 Units
  • +
  • Big box: 500 Units
  • +
  • Box: 50 Units
  • +
+

and you have to pick from your warehouse 2860 Units.

+

Then you can do:

+
+
+>>> product.product_qty_by_packaging(2860)
+
+[
+    {"id": 1, "qty": 2, "name": "Pallet"},
+    {"id": 2, "qty": 1, "name": "Big box"},
+    {"id": 3, "qty": 7, "name": "Box"},
+    {"id": 100, "qty": 10, "name": "Units"},
+]
+
+
+

With this you can show a proper message to warehouse operators to quickly pick the quantity they need.

+

Optionally you can get contained packaging by passing with_contained flag:

+
+
+>>> product.product_qty_by_packaging(2860, with_contained=True)
+
+[
+    {"id": 1, "qty": 2, "name": "Pallet", "contained": [{"id": 2, "qty": 2, "name": "Big box"}]},
+    {"id": 2, "qty": 1, "name": "Big box", "contained": [{"id": 3, "qty": 10, "name": "Box"}]},
+    {"id": 3, "qty": 7, "name": "Box", "contained": [{"id": 100, "qty": 50, "name": "Units"}]},
+    {"id": 100, "qty": 10, "name": "Units", "contained": []},},
+]
+
+
+
-

Known issues / Roadmap

+

Known issues / Roadmap

TODO

  1. Fractional quantities (eg: 0.5 Kg) are lost when counting units
  2. @@ -412,7 +435,7 @@ Only for development or testing purpose, do not use in production.
-

Bug Tracker

+

Bug Tracker

Bugs are tracked on GitHub Issues. In case of trouble, please check there if your issue has already been reported. If you spotted it first, help us smashing it by providing a detailed and welcomed @@ -420,21 +443,21 @@ If you spotted it first, help us smashing it by providing a detailed and welcome

Do not contact contributors directly about support or help with technical issues.

-

Credits

+

Credits

-

Authors

+

Authors

  • Camptocamp
-

Maintainers

+

Maintainers

This module is maintained by the OCA.

Odoo Community Association

OCA, or the Odoo Community Association, is a nonprofit organization whose From a9beb26ed941a2f642a9886b2e2e413452c083fe Mon Sep 17 00:00:00 2001 From: OCA-git-bot Date: Tue, 9 Jun 2020 08:09:47 +0000 Subject: [PATCH 09/25] stock_packaging_calculator 13.0.1.1.0 --- stock_packaging_calculator/__manifest__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stock_packaging_calculator/__manifest__.py b/stock_packaging_calculator/__manifest__.py index 4399bba90..731c263b0 100644 --- a/stock_packaging_calculator/__manifest__.py +++ b/stock_packaging_calculator/__manifest__.py @@ -3,7 +3,7 @@ { "name": "Stock packaging calculator", "summary": "Compute product quantity to pick by packaging", - "version": "13.0.1.0.0", + "version": "13.0.1.1.0", "development_status": "Alpha", "category": "Warehouse Management", "website": "https://github.com/OCA/stock-logistics-warehouse", From 35d3eb7cc27a5fe30ff7a0af47ebb390496241f3 Mon Sep 17 00:00:00 2001 From: Simone Orsi Date: Tue, 9 Jun 2020 10:35:32 +0200 Subject: [PATCH 10/25] stock_packaging_calculator: make contained mapping computed Allows to reuse the mapping every time is needed. --- stock_packaging_calculator/models/product.py | 66 +++++++++++++++---- .../tests/test_packaging_calc.py | 49 +++++++++++++- 2 files changed, 99 insertions(+), 16 deletions(-) diff --git a/stock_packaging_calculator/models/product.py b/stock_packaging_calculator/models/product.py index 2679b0fd6..4d74f5652 100644 --- a/stock_packaging_calculator/models/product.py +++ b/stock_packaging_calculator/models/product.py @@ -3,16 +3,45 @@ from collections import namedtuple -from odoo import models +from odoo import api, models from odoo.tools import float_compare +from odoo.addons.base_sparse_field.models.fields import Serialized + # Unify records as we mix up w/ UoM -Packaging = namedtuple("Packaging", "id name qty") +Packaging = namedtuple("Packaging", "id name qty is_unit") class Product(models.Model): _inherit = "product.product" + packaging_contained_mapping = Serialized( + compute="_compute_packaging_contained_mapping", + help="Technical field to store contained packaging. ", + ) + + @api.depends("packaging_ids.qty") + def _compute_packaging_contained_mapping(self): + for rec in self: + rec.packaging_contained_mapping = rec._packaging_contained_mapping() + + def _packaging_contained_mapping(self): + """Produce a mapping of packaging and contained packagings. + + Used mainly for `product_qty_by_packaging` but can be used + to display info as you prefer. + + :returns: a dictionary in the form {pkg.id: [contained packages]} + """ + res = {} + packaging = self._ordered_packaging() + for i, pkg in enumerate(packaging): + if pkg.is_unit: + # skip minimal unit + continue + res[pkg.id] = self._product_qty_by_packaging(packaging[i + 1 :], pkg.qty) + return res + def product_qty_by_packaging(self, prod_qty, with_contained=False): """Calculate quantity by packaging. @@ -34,31 +63,42 @@ class Product(models.Model): {contained: [{id: 1, qty: 4, name: "Big box"}]} """ - packagings = [Packaging(x.id, x.name, x.qty) for x in self.packaging_ids] + return self._product_qty_by_packaging( + self._ordered_packaging(), prod_qty, with_contained=with_contained, + ) + + def _ordered_packaging(self): + """Prepare packaging ordered by qty and exclude empty ones.""" + packagings = [ + Packaging(x.id, x.name, x.qty, False) + for x in self.packaging_ids + # Exclude the ones w/ zero qty as they are useless for the math + if x.qty + ] # Add minimal unit packagings.append( # NOTE: the ID here could clash w/ one of the packaging's. # If you create a mapping based on IDs, keep this in mind. - Packaging(self.uom_id.id, self.uom_id.name, self.uom_id.factor) - ) - return self._product_qty_by_packaging( - sorted(packagings, reverse=True, key=lambda x: x.qty), - prod_qty, - with_contained=with_contained, + # You can use `is_unit` to check this. + Packaging(self.uom_id.id, self.uom_id.name, self.uom_id.factor, True) ) + return sorted(packagings, reverse=True, key=lambda x: x.qty) def _product_qty_by_packaging(self, pkg_by_qty, qty, with_contained=False): """Produce a list of dictionaries of packaging info.""" # TODO: refactor to handle fractional quantities (eg: 0.5 Kg) res = [] - for i, pkg in enumerate(pkg_by_qty): + for pkg in pkg_by_qty: qty_per_pkg, qty = self._qty_by_pkg(pkg.qty, qty) if qty_per_pkg: value = {"id": pkg.id, "qty": qty_per_pkg, "name": pkg.name} if with_contained: - value["contained"] = self._product_qty_by_packaging( - pkg_by_qty[i + 1 :], pkg.qty - ) + contained = None + if not pkg.is_unit: + mapping = self.packaging_contained_mapping + # integer keys are serialized as strings :/ + contained = mapping.get(str(pkg.id)) + value["contained"] = contained res.append(value) if not qty: break diff --git a/stock_packaging_calculator/tests/test_packaging_calc.py b/stock_packaging_calculator/tests/test_packaging_calc.py index 39d03e01b..3482d4329 100644 --- a/stock_packaging_calculator/tests/test_packaging_calc.py +++ b/stock_packaging_calculator/tests/test_packaging_calc.py @@ -4,6 +4,10 @@ from odoo.tests import SavepointCase class TestCalc(SavepointCase): + + at_install = False + post_install = True + @classmethod def setUpClass(cls): super().setUpClass() @@ -12,7 +16,6 @@ class TestCalc(SavepointCase): cls.product_a = cls.env["product.product"].create( { "name": "Product A", - "type": "product", "uom_id": cls.uom_unit.id, "uom_po_id": cls.uom_unit.id, } @@ -27,6 +30,46 @@ class TestCalc(SavepointCase): {"name": "Pallet", "product_id": cls.product_a.id, "qty": 2000} ) + def test_contained_mapping(self): + self.assertEqual( + self.product_a.packaging_contained_mapping, + { + str(self.pkg_pallet.id): [ + { + "id": self.pkg_big_box.id, + "qty": 10, + "name": self.pkg_big_box.name, + }, + ], + str(self.pkg_big_box.id): [ + {"id": self.pkg_box.id, "qty": 4, "name": self.pkg_box.name}, + ], + str(self.pkg_box.id): [ + {"id": self.uom_unit.id, "qty": 50, "name": self.uom_unit.name}, + ], + }, + ) + # Update pkg qty + self.pkg_pallet.qty = 4000 + self.assertEqual( + self.product_a.packaging_contained_mapping, + { + str(self.pkg_pallet.id): [ + { + "id": self.pkg_big_box.id, + "qty": 20, + "name": self.pkg_big_box.name, + }, + ], + str(self.pkg_big_box.id): [ + {"id": self.pkg_box.id, "qty": 4, "name": self.pkg_box.name}, + ], + str(self.pkg_box.id): [ + {"id": self.uom_unit.id, "qty": 50, "name": self.uom_unit.name}, + ], + }, + ) + def test_calc_1(self): """Test easy behavior 1.""" expected = [ @@ -95,7 +138,7 @@ class TestCalc(SavepointCase): "id": self.uom_unit.id, "qty": 5, "name": self.uom_unit.name, - "contained": [], + "contained": None, }, ] self.assertEqual( @@ -140,7 +183,7 @@ class TestCalc(SavepointCase): "id": self.uom_unit.id, "qty": 25, "name": self.uom_unit.name, - "contained": [], + "contained": None, }, ] self.assertEqual( From 860555f49822313bf5411785a6f3356619d2676a Mon Sep 17 00:00:00 2001 From: oca-travis Date: Mon, 22 Jun 2020 12:42:15 +0000 Subject: [PATCH 11/25] [UPD] Update stock_packaging_calculator.pot --- .../i18n/stock_packaging_calculator.pot | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/stock_packaging_calculator/i18n/stock_packaging_calculator.pot b/stock_packaging_calculator/i18n/stock_packaging_calculator.pot index 775124a33..92a274c4a 100644 --- a/stock_packaging_calculator/i18n/stock_packaging_calculator.pot +++ b/stock_packaging_calculator/i18n/stock_packaging_calculator.pot @@ -13,7 +13,17 @@ msgstr "" "Content-Transfer-Encoding: \n" "Plural-Forms: \n" +#. module: stock_packaging_calculator +#: model:ir.model.fields,field_description:stock_packaging_calculator.field_product_product__packaging_contained_mapping +msgid "Packaging Contained Mapping" +msgstr "" + #. module: stock_packaging_calculator #: model:ir.model,name:stock_packaging_calculator.model_product_product msgid "Product" msgstr "" + +#. module: stock_packaging_calculator +#: model:ir.model.fields,help:stock_packaging_calculator.field_product_product__packaging_contained_mapping +msgid "Technical field to store contained packaging. " +msgstr "" From e77326a25c509066bd3fe7afaa2c00c871415a08 Mon Sep 17 00:00:00 2001 From: OCA-git-bot Date: Mon, 22 Jun 2020 13:05:22 +0000 Subject: [PATCH 12/25] stock_packaging_calculator 13.0.1.2.0 --- stock_packaging_calculator/__manifest__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stock_packaging_calculator/__manifest__.py b/stock_packaging_calculator/__manifest__.py index 731c263b0..3285010a5 100644 --- a/stock_packaging_calculator/__manifest__.py +++ b/stock_packaging_calculator/__manifest__.py @@ -3,7 +3,7 @@ { "name": "Stock packaging calculator", "summary": "Compute product quantity to pick by packaging", - "version": "13.0.1.1.0", + "version": "13.0.1.2.0", "development_status": "Alpha", "category": "Warehouse Management", "website": "https://github.com/OCA/stock-logistics-warehouse", From 423cd926c63d66471618f6ce85aafcfd6734ac7f Mon Sep 17 00:00:00 2001 From: Simone Orsi Date: Tue, 30 Jun 2020 14:16:42 +0200 Subject: [PATCH 13/25] stock_packaging_calculator: add support for packaging filter --- stock_packaging_calculator/models/product.py | 9 +++++++-- .../tests/test_packaging_calc.py | 14 ++++++++++++++ 2 files changed, 21 insertions(+), 2 deletions(-) diff --git a/stock_packaging_calculator/models/product.py b/stock_packaging_calculator/models/product.py index 4d74f5652..88332de81 100644 --- a/stock_packaging_calculator/models/product.py +++ b/stock_packaging_calculator/models/product.py @@ -68,10 +68,15 @@ class Product(models.Model): ) def _ordered_packaging(self): - """Prepare packaging ordered by qty and exclude empty ones.""" + """Prepare packaging ordered by qty and exclude empty ones. + + Use ctx key `_packaging_filter` to pass a function to filter packaging + to be considered. + """ + custom_filter = self.env.context.get("_packaging_filter", lambda x: x) packagings = [ Packaging(x.id, x.name, x.qty, False) - for x in self.packaging_ids + for x in self.packaging_ids.filtered(custom_filter) # Exclude the ones w/ zero qty as they are useless for the math if x.qty ] diff --git a/stock_packaging_calculator/tests/test_packaging_calc.py b/stock_packaging_calculator/tests/test_packaging_calc.py index 3482d4329..fcee8dd5a 100644 --- a/stock_packaging_calculator/tests/test_packaging_calc.py +++ b/stock_packaging_calculator/tests/test_packaging_calc.py @@ -103,6 +103,20 @@ class TestCalc(SavepointCase): ] self.assertEqual(self.product_a.product_qty_by_packaging(50.5), expected) + def test_calc_filter(self): + """Test packaging filter.""" + expected = [ + {"id": self.pkg_big_box.id, "qty": 13, "name": self.pkg_big_box.name}, + {"id": self.pkg_box.id, "qty": 1, "name": self.pkg_box.name}, + {"id": self.uom_unit.id, "qty": 5, "name": self.uom_unit.name}, + ] + self.assertEqual( + self.product_a.with_context( + _packaging_filter=lambda x: x != self.pkg_pallet + ).product_qty_by_packaging(2655), + expected, + ) + def test_calc_sub1(self): """Test contained packaging behavior 1.""" expected = [ From 311c50025a165c918e18989c13bdcb3d8aa2aa52 Mon Sep 17 00:00:00 2001 From: Simone Orsi Date: Tue, 30 Jun 2020 14:29:58 +0200 Subject: [PATCH 14/25] stock_packaging_calculator: add support for custom packaging name --- stock_packaging_calculator/models/product.py | 6 +++++- .../tests/test_packaging_calc.py | 19 +++++++++++++++++++ 2 files changed, 24 insertions(+), 1 deletion(-) diff --git a/stock_packaging_calculator/models/product.py b/stock_packaging_calculator/models/product.py index 88332de81..b81ac2107 100644 --- a/stock_packaging_calculator/models/product.py +++ b/stock_packaging_calculator/models/product.py @@ -72,10 +72,14 @@ class Product(models.Model): Use ctx key `_packaging_filter` to pass a function to filter packaging to be considered. + + Use ctx key `_packaging_name_getter` to pass a function to change + the display name of the packaging. """ custom_filter = self.env.context.get("_packaging_filter", lambda x: x) + name_getter = self.env.context.get("_packaging_name_getter", lambda x: x.name) packagings = [ - Packaging(x.id, x.name, x.qty, False) + Packaging(x.id, name_getter(x), x.qty, False) for x in self.packaging_ids.filtered(custom_filter) # Exclude the ones w/ zero qty as they are useless for the math if x.qty diff --git a/stock_packaging_calculator/tests/test_packaging_calc.py b/stock_packaging_calculator/tests/test_packaging_calc.py index fcee8dd5a..7a6b643b3 100644 --- a/stock_packaging_calculator/tests/test_packaging_calc.py +++ b/stock_packaging_calculator/tests/test_packaging_calc.py @@ -117,6 +117,25 @@ class TestCalc(SavepointCase): expected, ) + def test_calc_name_get(self): + """Test custom name getter.""" + expected = [ + {"id": self.pkg_pallet.id, "qty": 1, "name": "FOO " + self.pkg_pallet.name}, + { + "id": self.pkg_big_box.id, + "qty": 3, + "name": "FOO " + self.pkg_big_box.name, + }, + {"id": self.pkg_box.id, "qty": 1, "name": "FOO " + self.pkg_box.name}, + {"id": self.uom_unit.id, "qty": 5, "name": self.uom_unit.name}, + ] + self.assertEqual( + self.product_a.with_context( + _packaging_name_getter=lambda x: "FOO " + x.name + ).product_qty_by_packaging(2655), + expected, + ) + def test_calc_sub1(self): """Test contained packaging behavior 1.""" expected = [ From 9a4b3a6c46669dbd9e5f03129458e0d4d7658710 Mon Sep 17 00:00:00 2001 From: OCA-git-bot Date: Wed, 1 Jul 2020 06:51:32 +0000 Subject: [PATCH 15/25] stock_packaging_calculator 13.0.1.3.0 --- stock_packaging_calculator/__manifest__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stock_packaging_calculator/__manifest__.py b/stock_packaging_calculator/__manifest__.py index 3285010a5..1fa33ec03 100644 --- a/stock_packaging_calculator/__manifest__.py +++ b/stock_packaging_calculator/__manifest__.py @@ -3,7 +3,7 @@ { "name": "Stock packaging calculator", "summary": "Compute product quantity to pick by packaging", - "version": "13.0.1.2.0", + "version": "13.0.1.3.0", "development_status": "Alpha", "category": "Warehouse Management", "website": "https://github.com/OCA/stock-logistics-warehouse", From 181228f877433feaa5d241c1a10f802fbef7213a Mon Sep 17 00:00:00 2001 From: Simone Orsi Date: Wed, 15 Jul 2020 17:29:44 +0200 Subject: [PATCH 16/25] Re-license stock_packaging_calculator w/ LGPL --- stock_packaging_calculator/README.rst | 6 +++--- stock_packaging_calculator/__manifest__.py | 4 ++-- stock_packaging_calculator/models/product.py | 2 +- stock_packaging_calculator/static/description/index.html | 2 +- stock_packaging_calculator/tests/test_packaging_calc.py | 2 +- 5 files changed, 8 insertions(+), 8 deletions(-) diff --git a/stock_packaging_calculator/README.rst b/stock_packaging_calculator/README.rst index 5bde39e25..d6cc846cd 100644 --- a/stock_packaging_calculator/README.rst +++ b/stock_packaging_calculator/README.rst @@ -10,9 +10,9 @@ Stock packaging calculator .. |badge1| image:: https://img.shields.io/badge/maturity-Alpha-red.png :target: https://odoo-community.org/page/development-status :alt: Alpha -.. |badge2| image:: https://img.shields.io/badge/licence-AGPL--3-blue.png - :target: http://www.gnu.org/licenses/agpl-3.0-standalone.html - :alt: License: AGPL-3 +.. |badge2| image:: https://img.shields.io/badge/licence-LGPL--3-blue.png + :target: http://www.gnu.org/licenses/lgpl-3.0-standalone.html + :alt: License: LGPL-3 .. |badge3| image:: https://img.shields.io/badge/github-OCA%2Fstock--logistics--warehouse-lightgray.png?logo=github :target: https://github.com/OCA/stock-logistics-warehouse/tree/13.0/stock_packaging_calculator :alt: OCA/stock-logistics-warehouse diff --git a/stock_packaging_calculator/__manifest__.py b/stock_packaging_calculator/__manifest__.py index 1fa33ec03..3ab1b5ebd 100644 --- a/stock_packaging_calculator/__manifest__.py +++ b/stock_packaging_calculator/__manifest__.py @@ -1,5 +1,5 @@ # Copyright 2020 Camptocamp SA -# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl) +# License LGPL-3.0 or later (https://www.gnu.org/licenses/lgpl) { "name": "Stock packaging calculator", "summary": "Compute product quantity to pick by packaging", @@ -8,7 +8,7 @@ "category": "Warehouse Management", "website": "https://github.com/OCA/stock-logistics-warehouse", "author": "Camptocamp, Odoo Community Association (OCA)", - "license": "AGPL-3", + "license": "LGPL-3", "application": False, "installable": True, "depends": ["product"], diff --git a/stock_packaging_calculator/models/product.py b/stock_packaging_calculator/models/product.py index b81ac2107..752af5d3f 100644 --- a/stock_packaging_calculator/models/product.py +++ b/stock_packaging_calculator/models/product.py @@ -1,5 +1,5 @@ # Copyright 2020 Camptocamp SA -# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl) +# License LGPL-3.0 or later (https://www.gnu.org/licenses/lgpl) from collections import namedtuple diff --git a/stock_packaging_calculator/static/description/index.html b/stock_packaging_calculator/static/description/index.html index 62793c551..ebff58a67 100644 --- a/stock_packaging_calculator/static/description/index.html +++ b/stock_packaging_calculator/static/description/index.html @@ -367,7 +367,7 @@ ul.auto-toc { !! This file is generated by oca-gen-addon-readme !! !! changes will be overwritten. !! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! --> -

Alpha License: AGPL-3 OCA/stock-logistics-warehouse Translate me on Weblate Try me on Runbot

+

Alpha License: LGPL-3 OCA/stock-logistics-warehouse Translate me on Weblate Try me on Runbot

Basic module providing an helper method to calculate the quantity of product by packaging.

Important

diff --git a/stock_packaging_calculator/tests/test_packaging_calc.py b/stock_packaging_calculator/tests/test_packaging_calc.py index 7a6b643b3..b3b4fbbd6 100644 --- a/stock_packaging_calculator/tests/test_packaging_calc.py +++ b/stock_packaging_calculator/tests/test_packaging_calc.py @@ -1,5 +1,5 @@ # Copyright 2020 Camptocamp SA -# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl) +# License LGPL-3.0 or later (https://www.gnu.org/licenses/lgpl) from odoo.tests import SavepointCase From 06aa71884185583ca42cc713878c290650b2d4d7 Mon Sep 17 00:00:00 2001 From: OCA-git-bot Date: Thu, 16 Jul 2020 11:23:27 +0000 Subject: [PATCH 17/25] stock_packaging_calculator 13.0.1.4.0 --- stock_packaging_calculator/__manifest__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stock_packaging_calculator/__manifest__.py b/stock_packaging_calculator/__manifest__.py index 3ab1b5ebd..cfbf256af 100644 --- a/stock_packaging_calculator/__manifest__.py +++ b/stock_packaging_calculator/__manifest__.py @@ -3,7 +3,7 @@ { "name": "Stock packaging calculator", "summary": "Compute product quantity to pick by packaging", - "version": "13.0.1.3.0", + "version": "13.0.1.4.0", "development_status": "Alpha", "category": "Warehouse Management", "website": "https://github.com/OCA/stock-logistics-warehouse", From 37d44c72e6528c6c7f779ddf8030e33a73d71801 Mon Sep 17 00:00:00 2001 From: Tonow-c2c Date: Mon, 6 Jul 2020 16:26:25 +0200 Subject: [PATCH 18/25] [IMP][stock_packaging_calculator] Add key is_unit for in the packaging calculator return --- stock_packaging_calculator/models/product.py | 7 +- .../tests/test_packaging_calc.py | 137 +++++++++++++++--- 2 files changed, 125 insertions(+), 19 deletions(-) diff --git a/stock_packaging_calculator/models/product.py b/stock_packaging_calculator/models/product.py index 752af5d3f..31e8db70b 100644 --- a/stock_packaging_calculator/models/product.py +++ b/stock_packaging_calculator/models/product.py @@ -100,7 +100,12 @@ class Product(models.Model): for pkg in pkg_by_qty: qty_per_pkg, qty = self._qty_by_pkg(pkg.qty, qty) if qty_per_pkg: - value = {"id": pkg.id, "qty": qty_per_pkg, "name": pkg.name} + value = { + "id": pkg.id, + "qty": qty_per_pkg, + "name": pkg.name, + "is_unit": pkg.is_unit, + } if with_contained: contained = None if not pkg.is_unit: diff --git a/stock_packaging_calculator/tests/test_packaging_calc.py b/stock_packaging_calculator/tests/test_packaging_calc.py index b3b4fbbd6..19207c592 100644 --- a/stock_packaging_calculator/tests/test_packaging_calc.py +++ b/stock_packaging_calculator/tests/test_packaging_calc.py @@ -39,13 +39,24 @@ class TestCalc(SavepointCase): "id": self.pkg_big_box.id, "qty": 10, "name": self.pkg_big_box.name, + "is_unit": False, }, ], str(self.pkg_big_box.id): [ - {"id": self.pkg_box.id, "qty": 4, "name": self.pkg_box.name}, + { + "id": self.pkg_box.id, + "qty": 4, + "name": self.pkg_box.name, + "is_unit": False, + }, ], str(self.pkg_box.id): [ - {"id": self.uom_unit.id, "qty": 50, "name": self.uom_unit.name}, + { + "id": self.uom_unit.id, + "qty": 50, + "name": self.uom_unit.name, + "is_unit": False, + }, ], }, ) @@ -59,13 +70,24 @@ class TestCalc(SavepointCase): "id": self.pkg_big_box.id, "qty": 20, "name": self.pkg_big_box.name, + "is_unit": False, }, ], str(self.pkg_big_box.id): [ - {"id": self.pkg_box.id, "qty": 4, "name": self.pkg_box.name}, + { + "id": self.pkg_box.id, + "qty": 4, + "name": self.pkg_box.name, + "is_unit": False, + }, ], str(self.pkg_box.id): [ - {"id": self.uom_unit.id, "qty": 50, "name": self.uom_unit.name}, + { + "id": self.uom_unit.id, + "qty": 50, + "name": self.uom_unit.name, + "is_unit": False, + }, ], }, ) @@ -73,42 +95,102 @@ class TestCalc(SavepointCase): def test_calc_1(self): """Test easy behavior 1.""" expected = [ - {"id": self.pkg_pallet.id, "qty": 1, "name": self.pkg_pallet.name}, - {"id": self.pkg_big_box.id, "qty": 3, "name": self.pkg_big_box.name}, - {"id": self.pkg_box.id, "qty": 1, "name": self.pkg_box.name}, - {"id": self.uom_unit.id, "qty": 5, "name": self.uom_unit.name}, + { + "id": self.pkg_pallet.id, + "qty": 1, + "name": self.pkg_pallet.name, + "is_unit": False, + }, + { + "id": self.pkg_big_box.id, + "qty": 3, + "name": self.pkg_big_box.name, + "is_unit": False, + }, + { + "id": self.pkg_box.id, + "qty": 1, + "name": self.pkg_box.name, + "is_unit": False, + }, + { + "id": self.uom_unit.id, + "qty": 5, + "name": self.uom_unit.name, + "is_unit": True, + }, ] self.assertEqual(self.product_a.product_qty_by_packaging(2655), expected) def test_calc_2(self): """Test easy behavior 2.""" expected = [ - {"id": self.pkg_big_box.id, "qty": 1, "name": self.pkg_big_box.name}, - {"id": self.pkg_box.id, "qty": 3, "name": self.pkg_box.name}, + { + "id": self.pkg_big_box.id, + "qty": 1, + "name": self.pkg_big_box.name, + "is_unit": False, + }, + { + "id": self.pkg_box.id, + "qty": 3, + "name": self.pkg_box.name, + "is_unit": False, + }, ] self.assertEqual(self.product_a.product_qty_by_packaging(350), expected) def test_calc_3(self): """Test easy behavior 3.""" expected = [ - {"id": self.pkg_box.id, "qty": 1, "name": self.pkg_box.name}, - {"id": self.uom_unit.id, "qty": 30, "name": self.uom_unit.name}, + { + "id": self.pkg_box.id, + "qty": 1, + "name": self.pkg_box.name, + "is_unit": False, + }, + { + "id": self.uom_unit.id, + "qty": 30, + "name": self.uom_unit.name, + "is_unit": True, + }, ] self.assertEqual(self.product_a.product_qty_by_packaging(80), expected) def test_calc_6(self): """Test fractional qty is lost.""" expected = [ - {"id": self.pkg_box.id, "qty": 1, "name": self.pkg_box.name}, + { + "id": self.pkg_box.id, + "qty": 1, + "name": self.pkg_box.name, + "is_unit": False, + }, ] self.assertEqual(self.product_a.product_qty_by_packaging(50.5), expected) def test_calc_filter(self): """Test packaging filter.""" expected = [ - {"id": self.pkg_big_box.id, "qty": 13, "name": self.pkg_big_box.name}, - {"id": self.pkg_box.id, "qty": 1, "name": self.pkg_box.name}, - {"id": self.uom_unit.id, "qty": 5, "name": self.uom_unit.name}, + { + "id": self.pkg_big_box.id, + "qty": 13, + "name": self.pkg_big_box.name, + "is_unit": False, + }, + { + "id": self.pkg_box.id, + "qty": 1, + "name": self.pkg_box.name, + "is_unit": False, + }, + { + "id": self.uom_unit.id, + "qty": 5, + "name": self.uom_unit.name, + "is_unit": True, + }, ] self.assertEqual( self.product_a.with_context( @@ -125,9 +207,20 @@ class TestCalc(SavepointCase): "id": self.pkg_big_box.id, "qty": 3, "name": "FOO " + self.pkg_big_box.name, + "is_unit": False, + }, + { + "id": self.pkg_box.id, + "qty": 1, + "name": "FOO " + self.pkg_box.name, + "is_unit": False, + }, + { + "id": self.uom_unit.id, + "qty": 5, + "name": self.uom_unit.name, + "is_unit": True, }, - {"id": self.pkg_box.id, "qty": 1, "name": "FOO " + self.pkg_box.name}, - {"id": self.uom_unit.id, "qty": 5, "name": self.uom_unit.name}, ] self.assertEqual( self.product_a.with_context( @@ -143,6 +236,7 @@ class TestCalc(SavepointCase): "id": self.pkg_pallet.id, "qty": 1, "name": self.pkg_pallet.name, + "is_unit": False, "contained": [ { "id": self.pkg_big_box.id, @@ -155,6 +249,7 @@ class TestCalc(SavepointCase): "id": self.pkg_big_box.id, "qty": 3, "name": self.pkg_big_box.name, + "is_unit": False, "contained": [ {"id": self.pkg_box.id, "qty": 4, "name": self.pkg_box.name}, ], @@ -163,6 +258,7 @@ class TestCalc(SavepointCase): "id": self.pkg_box.id, "qty": 1, "name": self.pkg_box.name, + "is_unit": False, "contained": [ {"id": self.uom_unit.id, "qty": 50, "name": self.uom_unit.name}, ], @@ -171,6 +267,7 @@ class TestCalc(SavepointCase): "id": self.uom_unit.id, "qty": 5, "name": self.uom_unit.name, + "is_unit": True, "contained": None, }, ] @@ -192,6 +289,7 @@ class TestCalc(SavepointCase): "id": self.pkg_big_box.id, "qty": 10, "name": self.pkg_big_box.name, + "is_unit": False, }, ], }, @@ -199,6 +297,7 @@ class TestCalc(SavepointCase): "id": self.pkg_big_box.id, "qty": 3, "name": self.pkg_big_box.name, + "is_unit": False, "contained": [ {"id": self.pkg_box.id, "qty": 6, "name": self.pkg_box.name}, {"id": self.uom_unit.id, "qty": 20, "name": self.uom_unit.name}, @@ -208,6 +307,7 @@ class TestCalc(SavepointCase): "id": self.pkg_box.id, "qty": 1, "name": self.pkg_box.name, + "is_unit": False, "contained": [ {"id": self.uom_unit.id, "qty": 30, "name": self.uom_unit.name}, ], @@ -216,6 +316,7 @@ class TestCalc(SavepointCase): "id": self.uom_unit.id, "qty": 25, "name": self.uom_unit.name, + "is_unit": True, "contained": None, }, ] From bac56d52ae884dff65dafb823ad1abeb5562eee9 Mon Sep 17 00:00:00 2001 From: Akim Juillerat Date: Thu, 16 Jul 2020 10:35:53 +0200 Subject: [PATCH 19/25] stock_packaging_calculator: Add hook on packaging values Fix tests --- stock_packaging_calculator/models/product.py | 16 ++++--- .../tests/test_packaging_calc.py | 48 +++++++++++++++---- 2 files changed, 50 insertions(+), 14 deletions(-) diff --git a/stock_packaging_calculator/models/product.py b/stock_packaging_calculator/models/product.py index 31e8db70b..4d670716f 100644 --- a/stock_packaging_calculator/models/product.py +++ b/stock_packaging_calculator/models/product.py @@ -63,6 +63,7 @@ class Product(models.Model): {contained: [{id: 1, qty: 4, name: "Big box"}]} """ + self.ensure_one() return self._product_qty_by_packaging( self._ordered_packaging(), prod_qty, with_contained=with_contained, ) @@ -100,12 +101,7 @@ class Product(models.Model): for pkg in pkg_by_qty: qty_per_pkg, qty = self._qty_by_pkg(pkg.qty, qty) if qty_per_pkg: - value = { - "id": pkg.id, - "qty": qty_per_pkg, - "name": pkg.name, - "is_unit": pkg.is_unit, - } + value = self._prepare_qty_by_packaging_values(pkg, qty_per_pkg) if with_contained: contained = None if not pkg.is_unit: @@ -128,3 +124,11 @@ class Product(models.Model): qty -= pkg_qty qty_per_pkg += 1 return qty_per_pkg, qty + + def _prepare_qty_by_packaging_values(self, packaging, qty_per_pkg): + return { + "id": packaging.id, + "qty": qty_per_pkg, + "name": packaging.name, + "is_unit": packaging.is_unit, + } diff --git a/stock_packaging_calculator/tests/test_packaging_calc.py b/stock_packaging_calculator/tests/test_packaging_calc.py index 19207c592..c7ab405bd 100644 --- a/stock_packaging_calculator/tests/test_packaging_calc.py +++ b/stock_packaging_calculator/tests/test_packaging_calc.py @@ -55,7 +55,7 @@ class TestCalc(SavepointCase): "id": self.uom_unit.id, "qty": 50, "name": self.uom_unit.name, - "is_unit": False, + "is_unit": True, }, ], }, @@ -86,7 +86,7 @@ class TestCalc(SavepointCase): "id": self.uom_unit.id, "qty": 50, "name": self.uom_unit.name, - "is_unit": False, + "is_unit": True, }, ], }, @@ -202,7 +202,12 @@ class TestCalc(SavepointCase): def test_calc_name_get(self): """Test custom name getter.""" expected = [ - {"id": self.pkg_pallet.id, "qty": 1, "name": "FOO " + self.pkg_pallet.name}, + { + "id": self.pkg_pallet.id, + "qty": 1, + "name": "FOO " + self.pkg_pallet.name, + "is_unit": False, + }, { "id": self.pkg_big_box.id, "qty": 3, @@ -242,6 +247,7 @@ class TestCalc(SavepointCase): "id": self.pkg_big_box.id, "qty": 10, "name": self.pkg_big_box.name, + "is_unit": False, }, ], }, @@ -251,7 +257,12 @@ class TestCalc(SavepointCase): "name": self.pkg_big_box.name, "is_unit": False, "contained": [ - {"id": self.pkg_box.id, "qty": 4, "name": self.pkg_box.name}, + { + "id": self.pkg_box.id, + "qty": 4, + "name": self.pkg_box.name, + "is_unit": False, + }, ], }, { @@ -260,7 +271,12 @@ class TestCalc(SavepointCase): "name": self.pkg_box.name, "is_unit": False, "contained": [ - {"id": self.uom_unit.id, "qty": 50, "name": self.uom_unit.name}, + { + "id": self.uom_unit.id, + "qty": 50, + "name": self.uom_unit.name, + "is_unit": True, + }, ], }, { @@ -284,6 +300,7 @@ class TestCalc(SavepointCase): "id": self.pkg_pallet.id, "qty": 1, "name": self.pkg_pallet.name, + "is_unit": False, "contained": [ { "id": self.pkg_big_box.id, @@ -299,8 +316,18 @@ class TestCalc(SavepointCase): "name": self.pkg_big_box.name, "is_unit": False, "contained": [ - {"id": self.pkg_box.id, "qty": 6, "name": self.pkg_box.name}, - {"id": self.uom_unit.id, "qty": 20, "name": self.uom_unit.name}, + { + "id": self.pkg_box.id, + "qty": 6, + "name": self.pkg_box.name, + "is_unit": False, + }, + { + "id": self.uom_unit.id, + "qty": 20, + "name": self.uom_unit.name, + "is_unit": True, + }, ], }, { @@ -309,7 +336,12 @@ class TestCalc(SavepointCase): "name": self.pkg_box.name, "is_unit": False, "contained": [ - {"id": self.uom_unit.id, "qty": 30, "name": self.uom_unit.name}, + { + "id": self.uom_unit.id, + "qty": 30, + "name": self.uom_unit.name, + "is_unit": True, + }, ], }, { From 50f2fd68804bb7637a44092f3062ded31a7f966c Mon Sep 17 00:00:00 2001 From: Simone Orsi Date: Fri, 17 Jul 2020 17:20:45 +0200 Subject: [PATCH 20/25] stock_packaging_calculator 13.0.1.5.0 --- stock_packaging_calculator/__manifest__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stock_packaging_calculator/__manifest__.py b/stock_packaging_calculator/__manifest__.py index cfbf256af..d5aa12d87 100644 --- a/stock_packaging_calculator/__manifest__.py +++ b/stock_packaging_calculator/__manifest__.py @@ -3,7 +3,7 @@ { "name": "Stock packaging calculator", "summary": "Compute product quantity to pick by packaging", - "version": "13.0.1.4.0", + "version": "13.0.1.5.0", "development_status": "Alpha", "category": "Warehouse Management", "website": "https://github.com/OCA/stock-logistics-warehouse", From 5d0de4b14325353ea115b074624221c0ec2f95ed Mon Sep 17 00:00:00 2001 From: Simone Orsi Date: Mon, 3 Aug 2020 10:20:33 +0200 Subject: [PATCH 21/25] packaging_calculator: fix sorting Make sure unit is always the last element in the list. --- stock_packaging_calculator/models/product.py | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/stock_packaging_calculator/models/product.py b/stock_packaging_calculator/models/product.py index 4d670716f..89eadf5c5 100644 --- a/stock_packaging_calculator/models/product.py +++ b/stock_packaging_calculator/models/product.py @@ -79,12 +79,16 @@ class Product(models.Model): """ custom_filter = self.env.context.get("_packaging_filter", lambda x: x) name_getter = self.env.context.get("_packaging_name_getter", lambda x: x.name) - packagings = [ - Packaging(x.id, name_getter(x), x.qty, False) - for x in self.packaging_ids.filtered(custom_filter) - # Exclude the ones w/ zero qty as they are useless for the math - if x.qty - ] + packagings = sorted( + [ + Packaging(x.id, name_getter(x), x.qty, False) + for x in self.packaging_ids.filtered(custom_filter) + # Exclude the ones w/ zero qty as they are useless for the math + if x.qty + ], + reverse=True, + key=lambda x: x.qty, + ) # Add minimal unit packagings.append( # NOTE: the ID here could clash w/ one of the packaging's. @@ -92,7 +96,7 @@ class Product(models.Model): # You can use `is_unit` to check this. Packaging(self.uom_id.id, self.uom_id.name, self.uom_id.factor, True) ) - return sorted(packagings, reverse=True, key=lambda x: x.qty) + return packagings def _product_qty_by_packaging(self, pkg_by_qty, qty, with_contained=False): """Produce a list of dictionaries of packaging info.""" From e713b1d8055b931fda5336c59dafa97d07187875 Mon Sep 17 00:00:00 2001 From: Simone Orsi Date: Mon, 3 Aug 2020 10:30:06 +0200 Subject: [PATCH 22/25] packaging_calculator: allow custom value handler Use _packaging_values_handler ctx key to pass your own handler for specific on demand overrides. --- stock_packaging_calculator/models/product.py | 5 ++++- .../tests/test_packaging_calc.py | 18 ++++++++++++++++++ 2 files changed, 22 insertions(+), 1 deletion(-) diff --git a/stock_packaging_calculator/models/product.py b/stock_packaging_calculator/models/product.py index 89eadf5c5..69a85ec09 100644 --- a/stock_packaging_calculator/models/product.py +++ b/stock_packaging_calculator/models/product.py @@ -102,10 +102,13 @@ class Product(models.Model): """Produce a list of dictionaries of packaging info.""" # TODO: refactor to handle fractional quantities (eg: 0.5 Kg) res = [] + prepare_values = self.env.context.get( + "_packaging_values_handler", self._prepare_qty_by_packaging_values + ) for pkg in pkg_by_qty: qty_per_pkg, qty = self._qty_by_pkg(pkg.qty, qty) if qty_per_pkg: - value = self._prepare_qty_by_packaging_values(pkg, qty_per_pkg) + value = prepare_values(pkg, qty_per_pkg) if with_contained: contained = None if not pkg.is_unit: diff --git a/stock_packaging_calculator/tests/test_packaging_calc.py b/stock_packaging_calculator/tests/test_packaging_calc.py index c7ab405bd..1bc721086 100644 --- a/stock_packaging_calculator/tests/test_packaging_calc.py +++ b/stock_packaging_calculator/tests/test_packaging_calc.py @@ -234,6 +234,24 @@ class TestCalc(SavepointCase): expected, ) + def test_calc_custom_values(self): + """Test custom values handler.""" + expected = [ + {"my_qty": 1, "foo": self.pkg_pallet.name}, + {"my_qty": 3, "foo": self.pkg_big_box.name}, + {"my_qty": 1, "foo": self.pkg_box.name}, + {"my_qty": 5, "foo": self.uom_unit.name}, + ] + self.assertEqual( + self.product_a.with_context( + _packaging_values_handler=lambda pkg, qty_per_pkg: { + "my_qty": qty_per_pkg, + "foo": pkg.name, + } + ).product_qty_by_packaging(2655), + expected, + ) + def test_calc_sub1(self): """Test contained packaging behavior 1.""" expected = [ From 5c2e751416fc2312d0672dba7f3d344130c86d97 Mon Sep 17 00:00:00 2001 From: OCA-git-bot Date: Tue, 11 Aug 2020 13:57:32 +0000 Subject: [PATCH 23/25] stock_packaging_calculator 13.0.1.6.0 --- stock_packaging_calculator/__manifest__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stock_packaging_calculator/__manifest__.py b/stock_packaging_calculator/__manifest__.py index d5aa12d87..d31c5853c 100644 --- a/stock_packaging_calculator/__manifest__.py +++ b/stock_packaging_calculator/__manifest__.py @@ -3,7 +3,7 @@ { "name": "Stock packaging calculator", "summary": "Compute product quantity to pick by packaging", - "version": "13.0.1.5.0", + "version": "13.0.1.6.0", "development_status": "Alpha", "category": "Warehouse Management", "website": "https://github.com/OCA/stock-logistics-warehouse", From 02ba4eebfebb35d4b3b295b4e9dca4c4e0c46f3c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20BEAU?= Date: Wed, 6 Jan 2021 15:03:10 +0100 Subject: [PATCH 24/25] [IMP] stock_packaging_calculator: black, isort, prettier --- .../odoo/addons/stock_packaging_calculator | 1 + setup/stock_packaging_calculator/setup.py | 6 ++++++ stock_packaging_calculator/models/product.py | 4 +++- 3 files changed, 10 insertions(+), 1 deletion(-) create mode 120000 setup/stock_packaging_calculator/odoo/addons/stock_packaging_calculator create mode 100644 setup/stock_packaging_calculator/setup.py diff --git a/setup/stock_packaging_calculator/odoo/addons/stock_packaging_calculator b/setup/stock_packaging_calculator/odoo/addons/stock_packaging_calculator new file mode 120000 index 000000000..d3e128c8d --- /dev/null +++ b/setup/stock_packaging_calculator/odoo/addons/stock_packaging_calculator @@ -0,0 +1 @@ +../../../../stock_packaging_calculator \ No newline at end of file diff --git a/setup/stock_packaging_calculator/setup.py b/setup/stock_packaging_calculator/setup.py new file mode 100644 index 000000000..28c57bb64 --- /dev/null +++ b/setup/stock_packaging_calculator/setup.py @@ -0,0 +1,6 @@ +import setuptools + +setuptools.setup( + setup_requires=['setuptools-odoo'], + odoo_addon=True, +) diff --git a/stock_packaging_calculator/models/product.py b/stock_packaging_calculator/models/product.py index 69a85ec09..a4327076c 100644 --- a/stock_packaging_calculator/models/product.py +++ b/stock_packaging_calculator/models/product.py @@ -65,7 +65,9 @@ class Product(models.Model): """ self.ensure_one() return self._product_qty_by_packaging( - self._ordered_packaging(), prod_qty, with_contained=with_contained, + self._ordered_packaging(), + prod_qty, + with_contained=with_contained, ) def _ordered_packaging(self): From 38f3b3cbaa43a47fe97fa389ea9e98915ec4e2e1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20BEAU?= Date: Wed, 6 Jan 2021 15:03:10 +0100 Subject: [PATCH 25/25] [MIG] stock_packaging_calculator: Migration to 14.0 --- stock_packaging_calculator/__manifest__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stock_packaging_calculator/__manifest__.py b/stock_packaging_calculator/__manifest__.py index d31c5853c..856e147c3 100644 --- a/stock_packaging_calculator/__manifest__.py +++ b/stock_packaging_calculator/__manifest__.py @@ -3,7 +3,7 @@ { "name": "Stock packaging calculator", "summary": "Compute product quantity to pick by packaging", - "version": "13.0.1.6.0", + "version": "14.0.1.0.0", "development_status": "Alpha", "category": "Warehouse Management", "website": "https://github.com/OCA/stock-logistics-warehouse",