From 29c30b9dfc76e2944e0ca84b0d678311488c70d7 Mon Sep 17 00:00:00 2001 From: Simone Orsi Date: Tue, 26 May 2020 10:40:07 +0200 Subject: [PATCH 01/45] 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 0715a5ae3d3438961de5a56713349f932bb1e40f Mon Sep 17 00:00:00 2001 From: Simone Orsi Date: Mon, 8 Jun 2020 09:56:12 +0200 Subject: [PATCH 02/45] 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 8796f35572c7427af7bddc0dd3389ad04995343e Mon Sep 17 00:00:00 2001 From: oca-travis Date: Mon, 8 Jun 2020 08:00:41 +0000 Subject: [PATCH 03/45] [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 4c997b882de52876fa1e6dcf3057dc6d7058ad67 Mon Sep 17 00:00:00 2001 From: Simone Orsi Date: Mon, 8 Jun 2020 10:18:02 +0200 Subject: [PATCH 04/45] 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 69ac7de34a8fae663570415acc2ece2320c9bc74 Mon Sep 17 00:00:00 2001 From: OCA-git-bot Date: Mon, 8 Jun 2020 08:25:57 +0000 Subject: [PATCH 05/45] [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 9115aceaf54e1ff9dd4f44a0a79d50df34205484 Mon Sep 17 00:00:00 2001 From: OCA-git-bot Date: Mon, 8 Jun 2020 08:25:58 +0000 Subject: [PATCH 06/45] [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 359d599713f70bf6dcefc41b460491159b513dd7 Mon Sep 17 00:00:00 2001 From: Simone Orsi Date: Mon, 8 Jun 2020 11:47:32 +0200 Subject: [PATCH 07/45] 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 9a843df065701ae4699733fb34175e367400ec31 Mon Sep 17 00:00:00 2001 From: OCA-git-bot Date: Tue, 9 Jun 2020 08:09:46 +0000 Subject: [PATCH 08/45] [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 5511806ae4470cfa485f948247501240a278bd8f Mon Sep 17 00:00:00 2001 From: OCA-git-bot Date: Tue, 9 Jun 2020 08:09:47 +0000 Subject: [PATCH 09/45] 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 0636167004d5e6bf427878d10160052aad1a3346 Mon Sep 17 00:00:00 2001 From: Simone Orsi Date: Tue, 9 Jun 2020 10:35:32 +0200 Subject: [PATCH 10/45] 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 b805191d52d49a97ec0a0d078f7d4906ad6509bf Mon Sep 17 00:00:00 2001 From: oca-travis Date: Mon, 22 Jun 2020 12:42:15 +0000 Subject: [PATCH 11/45] [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 391ce22c7789a9be60e5a43e11823d32420faa8f Mon Sep 17 00:00:00 2001 From: OCA-git-bot Date: Mon, 22 Jun 2020 13:05:22 +0000 Subject: [PATCH 12/45] 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 b0e981932030652042ff4ff727a50ddda45a0bf1 Mon Sep 17 00:00:00 2001 From: Simone Orsi Date: Tue, 30 Jun 2020 14:16:42 +0200 Subject: [PATCH 13/45] 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 4cc5819beded2bbb6657d06d9a2c6080cc320f58 Mon Sep 17 00:00:00 2001 From: Simone Orsi Date: Tue, 30 Jun 2020 14:29:58 +0200 Subject: [PATCH 14/45] 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 fbb564ef0c55ebbbb0e91a1ac1cf3c04b56f91a4 Mon Sep 17 00:00:00 2001 From: OCA-git-bot Date: Wed, 1 Jul 2020 06:51:32 +0000 Subject: [PATCH 15/45] 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 e4b3ec7690decc697b3e33d6db4bf5e00a6fee40 Mon Sep 17 00:00:00 2001 From: Simone Orsi Date: Wed, 15 Jul 2020 17:29:44 +0200 Subject: [PATCH 16/45] 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 043227b18667f133d9f79e52d81faf9175ddcb56 Mon Sep 17 00:00:00 2001 From: OCA-git-bot Date: Thu, 16 Jul 2020 11:23:27 +0000 Subject: [PATCH 17/45] 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 8e27481ac6545e70dc04730ab9bb23ff167afb95 Mon Sep 17 00:00:00 2001 From: Tonow-c2c Date: Mon, 6 Jul 2020 16:26:25 +0200 Subject: [PATCH 18/45] [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 050c658ab1708534b4f92ae1b29c21e31aea4c93 Mon Sep 17 00:00:00 2001 From: Akim Juillerat Date: Thu, 16 Jul 2020 10:35:53 +0200 Subject: [PATCH 19/45] 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 92c244ac0d0acaa943ba4cc3ce9594980de01e5c Mon Sep 17 00:00:00 2001 From: Simone Orsi Date: Fri, 17 Jul 2020 17:20:45 +0200 Subject: [PATCH 20/45] 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 7be28d9f2f4a7abb9acc556bb3dcf8e80f4dce16 Mon Sep 17 00:00:00 2001 From: Simone Orsi Date: Mon, 3 Aug 2020 10:20:33 +0200 Subject: [PATCH 21/45] 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 c0e5e4babc2a8c937b17787db8606c6bc4eda648 Mon Sep 17 00:00:00 2001 From: Simone Orsi Date: Mon, 3 Aug 2020 10:30:06 +0200 Subject: [PATCH 22/45] 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 dcde9d28b670516ac4c74effe086c9eaa26a9efd Mon Sep 17 00:00:00 2001 From: OCA-git-bot Date: Tue, 11 Aug 2020 13:57:32 +0000 Subject: [PATCH 23/45] 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 af15012c127b97ab1f0007332bee45122c8eb312 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/45] [IMP] stock_packaging_calculator: black, isort, prettier --- stock_packaging_calculator/models/product.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) 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 6e597bf58192afb829545473a31992a89d2dc02d 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/45] [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", From 88301f5bd7812f82541cb0a8508ed6f10e481272 Mon Sep 17 00:00:00 2001 From: oca-travis Date: Fri, 5 Feb 2021 09:49:42 +0000 Subject: [PATCH 26/45] [UPD] Update stock_packaging_calculator.pot --- .../i18n/stock_packaging_calculator.pot | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/stock_packaging_calculator/i18n/stock_packaging_calculator.pot b/stock_packaging_calculator/i18n/stock_packaging_calculator.pot index 92a274c4a..664b39319 100644 --- a/stock_packaging_calculator/i18n/stock_packaging_calculator.pot +++ b/stock_packaging_calculator/i18n/stock_packaging_calculator.pot @@ -4,7 +4,7 @@ # msgid "" msgstr "" -"Project-Id-Version: Odoo Server 13.0\n" +"Project-Id-Version: Odoo Server 14.0\n" "Report-Msgid-Bugs-To: \n" "Last-Translator: \n" "Language-Team: \n" @@ -13,6 +13,21 @@ msgstr "" "Content-Transfer-Encoding: \n" "Plural-Forms: \n" +#. module: stock_packaging_calculator +#: model:ir.model.fields,field_description:stock_packaging_calculator.field_product_product__display_name +msgid "Display Name" +msgstr "" + +#. module: stock_packaging_calculator +#: model:ir.model.fields,field_description:stock_packaging_calculator.field_product_product__id +msgid "ID" +msgstr "" + +#. module: stock_packaging_calculator +#: model:ir.model.fields,field_description:stock_packaging_calculator.field_product_product____last_update +msgid "Last Modified on" +msgstr "" + #. module: stock_packaging_calculator #: model:ir.model.fields,field_description:stock_packaging_calculator.field_product_product__packaging_contained_mapping msgid "Packaging Contained Mapping" From 3952dea9be0ee5d23dd5e389c589d5cb936357c5 Mon Sep 17 00:00:00 2001 From: OCA-git-bot Date: Fri, 5 Feb 2021 10:01:08 +0000 Subject: [PATCH 27/45] [UPD] README.rst --- stock_packaging_calculator/README.rst | 10 +++++----- .../static/description/index.html | 6 +++--- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/stock_packaging_calculator/README.rst b/stock_packaging_calculator/README.rst index d6cc846cd..1a28cd164 100644 --- a/stock_packaging_calculator/README.rst +++ b/stock_packaging_calculator/README.rst @@ -14,13 +14,13 @@ Stock packaging calculator :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 + :target: https://github.com/OCA/stock-logistics-warehouse/tree/14.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 + :target: https://translation.odoo-community.org/projects/stock-logistics-warehouse-14-0/stock-logistics-warehouse-14-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 + :target: https://runbot.odoo-community.org/runbot/153/14.0 :alt: Try me on Runbot |badge1| |badge2| |badge3| |badge4| |badge5| @@ -91,7 +91,7 @@ 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 `_. +`feedback `_. Do not contact contributors directly about support or help with technical issues. @@ -121,6 +121,6 @@ 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. +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 index ebff58a67..0bfe447f7 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: LGPL-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

@@ -439,7 +439,7 @@ Only for development or testing purpose, do not use in production.

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.

+feedback.

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

@@ -463,7 +463,7 @@ If you spotted it first, help us smashing it by providing a detailed and welcome

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.

+

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 9ebb2e7e11c4ef1823cd9d3c6d58a8b9d38e1563 Mon Sep 17 00:00:00 2001 From: OCA-git-bot Date: Fri, 5 Feb 2021 10:01:09 +0000 Subject: [PATCH 28/45] stock_packaging_calculator 14.0.1.0.1 --- 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 856e147c3..f2d7f8685 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": "14.0.1.0.0", + "version": "14.0.1.0.1", "development_status": "Alpha", "category": "Warehouse Management", "website": "https://github.com/OCA/stock-logistics-warehouse", From f14a906c361e6ac2d9a5997422c29a457593e09f Mon Sep 17 00:00:00 2001 From: Simone Orsi Date: Tue, 11 May 2021 16:09:28 +0200 Subject: [PATCH 29/45] s_packaging_calculator: include barcode --- stock_packaging_calculator/models/product.py | 7 +- .../tests/test_packaging_calc.py | 322 ++++-------------- stock_packaging_calculator/tests/utils.py | 24 ++ 3 files changed, 99 insertions(+), 254 deletions(-) create mode 100644 stock_packaging_calculator/tests/utils.py diff --git a/stock_packaging_calculator/models/product.py b/stock_packaging_calculator/models/product.py index a4327076c..13ae10f3d 100644 --- a/stock_packaging_calculator/models/product.py +++ b/stock_packaging_calculator/models/product.py @@ -9,7 +9,7 @@ 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 is_unit") +Packaging = namedtuple("Packaging", "id name qty barcode is_unit") class Product(models.Model): @@ -83,7 +83,7 @@ class Product(models.Model): name_getter = self.env.context.get("_packaging_name_getter", lambda x: x.name) packagings = sorted( [ - Packaging(x.id, name_getter(x), x.qty, False) + Packaging(x.id, name_getter(x), x.qty, x.barcode, 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 @@ -96,7 +96,7 @@ class Product(models.Model): # NOTE: the ID here could clash w/ one of the packaging's. # If you create a mapping based on IDs, keep this in mind. # You can use `is_unit` to check this. - Packaging(self.uom_id.id, self.uom_id.name, self.uom_id.factor, True) + Packaging(self.uom_id.id, self.uom_id.name, self.uom_id.factor, None, True) ) return packagings @@ -140,4 +140,5 @@ class Product(models.Model): "qty": qty_per_pkg, "name": packaging.name, "is_unit": packaging.is_unit, + "barcode": packaging.barcode, } diff --git a/stock_packaging_calculator/tests/test_packaging_calc.py b/stock_packaging_calculator/tests/test_packaging_calc.py index 1bc721086..99470229b 100644 --- a/stock_packaging_calculator/tests/test_packaging_calc.py +++ b/stock_packaging_calculator/tests/test_packaging_calc.py @@ -2,11 +2,14 @@ # License LGPL-3.0 or later (https://www.gnu.org/licenses/lgpl) from odoo.tests import SavepointCase +from .utils import make_pkg_values + class TestCalc(SavepointCase): at_install = False post_install = True + maxDiff = None @classmethod def setUpClass(cls): @@ -21,43 +24,32 @@ class TestCalc(SavepointCase): } ) cls.pkg_box = cls.env["product.packaging"].create( - {"name": "Box", "product_id": cls.product_a.id, "qty": 50} + {"name": "Box", "product_id": cls.product_a.id, "qty": 50, "barcode": "BOX"} ) cls.pkg_big_box = cls.env["product.packaging"].create( - {"name": "Big Box", "product_id": cls.product_a.id, "qty": 200} + { + "name": "Big Box", + "product_id": cls.product_a.id, + "qty": 200, + "barcode": "BIGBOX", + } ) cls.pkg_pallet = cls.env["product.packaging"].create( - {"name": "Pallet", "product_id": cls.product_a.id, "qty": 2000} + { + "name": "Pallet", + "product_id": cls.product_a.id, + "qty": 2000, + "barcode": "PALLET", + } ) 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, - "is_unit": False, - }, - ], - str(self.pkg_big_box.id): [ - { - "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, - "is_unit": True, - }, - ], + str(self.pkg_pallet.id): [make_pkg_values(self.pkg_big_box, qty=10)], + str(self.pkg_big_box.id): [make_pkg_values(self.pkg_box, qty=4)], + str(self.pkg_box.id): [make_pkg_values(self.uom_unit, qty=50)], }, ) # Update pkg qty @@ -65,132 +57,51 @@ class TestCalc(SavepointCase): 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, - "is_unit": False, - }, - ], - str(self.pkg_big_box.id): [ - { - "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, - "is_unit": True, - }, - ], + str(self.pkg_pallet.id): [make_pkg_values(self.pkg_big_box, qty=20)], + str(self.pkg_big_box.id): [make_pkg_values(self.pkg_box, qty=4)], + str(self.pkg_box.id): [make_pkg_values(self.uom_unit, qty=50)], }, ) def test_calc_1(self): """Test easy behavior 1.""" expected = [ - { - "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, - }, + make_pkg_values(self.pkg_pallet, qty=1), + make_pkg_values(self.pkg_big_box, qty=3), + make_pkg_values(self.pkg_box, qty=1), + make_pkg_values(self.uom_unit, qty=5), ] 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, - "is_unit": False, - }, - { - "id": self.pkg_box.id, - "qty": 3, - "name": self.pkg_box.name, - "is_unit": False, - }, + make_pkg_values(self.pkg_big_box, qty=1), + make_pkg_values(self.pkg_box, qty=3), ] 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, - "is_unit": False, - }, - { - "id": self.uom_unit.id, - "qty": 30, - "name": self.uom_unit.name, - "is_unit": True, - }, + make_pkg_values(self.pkg_box, qty=1), + make_pkg_values(self.uom_unit, qty=30), ] 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, - "is_unit": False, - }, + make_pkg_values(self.pkg_box, qty=1), ] 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, - "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, - }, + make_pkg_values(self.pkg_big_box, qty=13), + make_pkg_values(self.pkg_box, qty=1), + make_pkg_values(self.uom_unit, qty=5), ] self.assertEqual( self.product_a.with_context( @@ -202,30 +113,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, - "is_unit": False, - }, - { - "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, - }, + make_pkg_values(self.pkg_pallet, qty=1, name="FOO " + self.pkg_pallet.name), + make_pkg_values( + self.pkg_big_box, qty=3, name="FOO " + self.pkg_big_box.name + ), + make_pkg_values(self.pkg_box, qty=1, name="FOO " + self.pkg_box.name), + make_pkg_values(self.uom_unit, qty=5, name=self.uom_unit.name), ] self.assertEqual( self.product_a.with_context( @@ -255,55 +148,20 @@ class TestCalc(SavepointCase): def test_calc_sub1(self): """Test contained packaging behavior 1.""" expected = [ - { - "id": self.pkg_pallet.id, - "qty": 1, - "name": self.pkg_pallet.name, - "is_unit": False, - "contained": [ - { - "id": self.pkg_big_box.id, - "qty": 10, - "name": self.pkg_big_box.name, - "is_unit": False, - }, - ], - }, - { - "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, - "is_unit": False, - }, - ], - }, - { - "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, - "is_unit": True, - }, - ], - }, - { - "id": self.uom_unit.id, - "qty": 5, - "name": self.uom_unit.name, - "is_unit": True, - "contained": None, - }, + make_pkg_values( + self.pkg_pallet, + qty=1, + contained=[make_pkg_values(self.pkg_big_box, qty=10)], + ), + make_pkg_values( + self.pkg_big_box, + qty=3, + contained=[make_pkg_values(self.pkg_box, qty=4)], + ), + make_pkg_values( + self.pkg_box, qty=1, contained=[make_pkg_values(self.uom_unit, qty=50)], + ), + make_pkg_values(self.uom_unit, qty=5, contained=None), ] self.assertEqual( self.product_a.product_qty_by_packaging(2655, with_contained=True), @@ -311,64 +169,26 @@ class TestCalc(SavepointCase): ) def test_calc_sub2(self): - """Test contained packaging behavior 1.""" + """Test contained packaging behavior 2.""" self.pkg_box.qty = 30 expected = [ - { - "id": self.pkg_pallet.id, - "qty": 1, - "name": self.pkg_pallet.name, - "is_unit": False, - "contained": [ - { - "id": self.pkg_big_box.id, - "qty": 10, - "name": self.pkg_big_box.name, - "is_unit": False, - }, + make_pkg_values( + self.pkg_pallet, + qty=1, + contained=[make_pkg_values(self.pkg_big_box, qty=10)], + ), + make_pkg_values( + self.pkg_big_box, + qty=3, + contained=[ + make_pkg_values(self.pkg_box, qty=6), + make_pkg_values(self.uom_unit, qty=20), ], - }, - { - "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, - "is_unit": False, - }, - { - "id": self.uom_unit.id, - "qty": 20, - "name": self.uom_unit.name, - "is_unit": True, - }, - ], - }, - { - "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, - "is_unit": True, - }, - ], - }, - { - "id": self.uom_unit.id, - "qty": 25, - "name": self.uom_unit.name, - "is_unit": True, - "contained": None, - }, + ), + make_pkg_values( + self.pkg_box, qty=1, contained=[make_pkg_values(self.uom_unit, qty=30)], + ), + make_pkg_values(self.uom_unit, qty=25, contained=None), ] self.assertEqual( self.product_a.product_qty_by_packaging(2655, with_contained=True), diff --git a/stock_packaging_calculator/tests/utils.py b/stock_packaging_calculator/tests/utils.py new file mode 100644 index 000000000..cd9e12560 --- /dev/null +++ b/stock_packaging_calculator/tests/utils.py @@ -0,0 +1,24 @@ +# Copyright 2021 Camptocamp SA +# License LGPL-3.0 or later (https://www.gnu.org/licenses/lgpl) + + +def make_pkg_values(record, **kw): + """Helper to generate test values for packaging. + """ + if record._name == "uom.uom": + is_unit = True + barcode = None + qty = record.factor + elif record._name == "product.packaging": + qty = record.qty + is_unit = False + barcode = record.barcode + values = { + "id": record.id, + "name": record.name, + "qty": qty, + "barcode": barcode, + "is_unit": is_unit, + } + values.update(kw) + return values From 38baba3a6e3f38e8e54f28b7838efced5c025605 Mon Sep 17 00:00:00 2001 From: Simone Orsi Date: Fri, 28 May 2021 09:03:14 +0200 Subject: [PATCH 30/45] s_packaging_calculator: ease override of packaging name --- stock_packaging_calculator/models/product.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/stock_packaging_calculator/models/product.py b/stock_packaging_calculator/models/product.py index 13ae10f3d..cdf83edf9 100644 --- a/stock_packaging_calculator/models/product.py +++ b/stock_packaging_calculator/models/product.py @@ -80,7 +80,9 @@ class Product(models.Model): 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) + name_getter = self.env.context.get( + "_packaging_name_getter", self._packaging_name_getter + ) packagings = sorted( [ Packaging(x.id, name_getter(x), x.qty, x.barcode, False) @@ -100,6 +102,9 @@ class Product(models.Model): ) return packagings + def _packaging_name_getter(self, packaging): + return packaging.name + 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) From 051911e88439dab828e029c10dd1c282f120748f Mon Sep 17 00:00:00 2001 From: Simone Orsi Date: Fri, 28 May 2021 09:04:51 +0200 Subject: [PATCH 31/45] s_packaging_calculator: add product_qty_by_packaging_as_str Retrieve quickly packagin bty qty as a string. --- stock_packaging_calculator/models/product.py | 58 +++++++++++++++++++ stock_packaging_calculator/tests/__init__.py | 1 + stock_packaging_calculator/tests/common.py | 42 ++++++++++++++ .../tests/test_packaging_calc.py | 42 +------------- .../tests/test_pkg_qty_str.py | 50 ++++++++++++++++ 5 files changed, 153 insertions(+), 40 deletions(-) create mode 100644 stock_packaging_calculator/tests/common.py create mode 100644 stock_packaging_calculator/tests/test_pkg_qty_str.py diff --git a/stock_packaging_calculator/models/product.py b/stock_packaging_calculator/models/product.py index cdf83edf9..026c7971b 100644 --- a/stock_packaging_calculator/models/product.py +++ b/stock_packaging_calculator/models/product.py @@ -1,6 +1,8 @@ # Copyright 2020 Camptocamp SA +# @author: Simone Orsi # License LGPL-3.0 or later (https://www.gnu.org/licenses/lgpl) +import unicodedata from collections import namedtuple from odoo import api, models @@ -11,6 +13,8 @@ from odoo.addons.base_sparse_field.models.fields import Serialized # Unify records as we mix up w/ UoM Packaging = namedtuple("Packaging", "id name qty barcode is_unit") +NO_BREAK_SPACE_CHAR = unicodedata.lookup("NO-BREAK SPACE") + class Product(models.Model): _inherit = "product.product" @@ -147,3 +151,57 @@ class Product(models.Model): "is_unit": packaging.is_unit, "barcode": packaging.barcode, } + + def product_qty_by_packaging_as_str(self, prod_qty, include_total_units=False): + """Return a string representing the qty of each packaging. + + :param prod_qty: the qty of current product to translate to pkg qty + :param include_total_units: includes total qty required initially + """ + self.ensure_one() + if not prod_qty: + return "" + + qty_by_packaging = self.product_qty_by_packaging(prod_qty) + if not qty_by_packaging: + return "" + + # Exclude unit qty and reuse it later + unit_qty = None + has_only_units = True + _qty_by_packaging = [] + for pkg_qty in qty_by_packaging: + if pkg_qty["is_unit"]: + unit_qty = pkg_qty["qty"] + continue + has_only_units = False + _qty_by_packaging.append(pkg_qty) + # Browse them all at once + records = self.env["product.packaging"].browse( + [x["id"] for x in _qty_by_packaging] + ) + _qty_by_packaging_as_str = self.env.context.get( + "_qty_by_packaging_as_str", self._qty_by_packaging_as_str + ) + # Collect all strings representations + as_string = [] + for record, info in zip(records, _qty_by_packaging): + bit = _qty_by_packaging_as_str(record, info["qty"]) + if bit: + as_string.append(bit) + # Restore unit information if any. + # Skip it if we get only units in the count. + if unit_qty and not has_only_units: + as_string.append(f"{unit_qty} {self.uom_id.name}") + # We want to avoid line break here as this string + # can be used by reports + res = f",{NO_BREAK_SPACE_CHAR}".join(as_string) + if include_total_units and not has_only_units: + res += " " + self._qty_by_packaging_total_units(prod_qty) + return res + + def _qty_by_packaging_as_str(self, packaging, qty): + return f"{qty} {packaging.name}" + + def _qty_by_packaging_total_units(self, prod_qty): + return f"({prod_qty} {self.uom_id.name})" diff --git a/stock_packaging_calculator/tests/__init__.py b/stock_packaging_calculator/tests/__init__.py index 04c47e711..441177cdc 100644 --- a/stock_packaging_calculator/tests/__init__.py +++ b/stock_packaging_calculator/tests/__init__.py @@ -1 +1,2 @@ from . import test_packaging_calc +from . import test_pkg_qty_str diff --git a/stock_packaging_calculator/tests/common.py b/stock_packaging_calculator/tests/common.py new file mode 100644 index 000000000..c528958ce --- /dev/null +++ b/stock_packaging_calculator/tests/common.py @@ -0,0 +1,42 @@ +# Copyright 2020 Camptocamp SA +# License LGPL-3.0 or later (https://www.gnu.org/licenses/lgpl) +from odoo.tests import SavepointCase + + +class TestCommon(SavepointCase): + + at_install = False + post_install = True + maxDiff = None + + @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.product_a = cls.env["product.product"].create( + { + "name": "Product A", + "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, "barcode": "BOX"} + ) + cls.pkg_big_box = cls.env["product.packaging"].create( + { + "name": "Big Box", + "product_id": cls.product_a.id, + "qty": 200, + "barcode": "BIGBOX", + } + ) + cls.pkg_pallet = cls.env["product.packaging"].create( + { + "name": "Pallet", + "product_id": cls.product_a.id, + "qty": 2000, + "barcode": "PALLET", + } + ) diff --git a/stock_packaging_calculator/tests/test_packaging_calc.py b/stock_packaging_calculator/tests/test_packaging_calc.py index 99470229b..c51347e40 100644 --- a/stock_packaging_calculator/tests/test_packaging_calc.py +++ b/stock_packaging_calculator/tests/test_packaging_calc.py @@ -1,48 +1,10 @@ # Copyright 2020 Camptocamp SA # License LGPL-3.0 or later (https://www.gnu.org/licenses/lgpl) -from odoo.tests import SavepointCase - +from .common import TestCommon from .utils import make_pkg_values -class TestCalc(SavepointCase): - - at_install = False - post_install = True - maxDiff = None - - @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.product_a = cls.env["product.product"].create( - { - "name": "Product A", - "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, "barcode": "BOX"} - ) - cls.pkg_big_box = cls.env["product.packaging"].create( - { - "name": "Big Box", - "product_id": cls.product_a.id, - "qty": 200, - "barcode": "BIGBOX", - } - ) - cls.pkg_pallet = cls.env["product.packaging"].create( - { - "name": "Pallet", - "product_id": cls.product_a.id, - "qty": 2000, - "barcode": "PALLET", - } - ) - +class TestCalc(TestCommon): def test_contained_mapping(self): self.assertEqual( self.product_a.packaging_contained_mapping, diff --git a/stock_packaging_calculator/tests/test_pkg_qty_str.py b/stock_packaging_calculator/tests/test_pkg_qty_str.py new file mode 100644 index 000000000..93998846c --- /dev/null +++ b/stock_packaging_calculator/tests/test_pkg_qty_str.py @@ -0,0 +1,50 @@ +# Copyright 2021 Camptocamp SA +# License LGPL-3.0 or later (https://www.gnu.org/licenses/lgpl) +from .common import TestCommon + + +class TestAsStr(TestCommon): + def test_as_str(self): + self.assertEqual(self.product_a.product_qty_by_packaging_as_str(10), "") + self.assertEqual(self.product_a.product_qty_by_packaging_as_str(100), "2 Box") + self.assertEqual( + self.product_a.product_qty_by_packaging_as_str(250), "1 Big Box,\xa01 Box" + ) + self.assertEqual( + self.product_a.product_qty_by_packaging_as_str(255), + "1 Big Box,\xa01 Box,\xa05 Units", + ) + + def test_as_str_w_units(self): + self.assertEqual( + self.product_a.product_qty_by_packaging_as_str( + 10, include_total_units=True + ), + "", + ) + self.assertEqual( + self.product_a.product_qty_by_packaging_as_str( + 100, include_total_units=True + ), + "2 Box (100 Units)", + ) + self.assertEqual( + self.product_a.product_qty_by_packaging_as_str( + 250, include_total_units=True + ), + "1 Big Box,\xa01 Box (250 Units)", + ) + self.assertEqual( + self.product_a.product_qty_by_packaging_as_str( + 255, include_total_units=True + ), + "1 Big Box,\xa01 Box,\xa05 Units (255 Units)", + ) + + def test_as_str_custom_name(self): + self.assertEqual( + self.product_a.with_context( + _qty_by_packaging_as_str=lambda pkg, qty: f"{pkg.name} {qty} FOO" + ).product_qty_by_packaging_as_str(250), + "Big Box 1 FOO,\xa0Box 1 FOO", + ) From 45e3e2e137558719900413c792a92ffb2f308896 Mon Sep 17 00:00:00 2001 From: Simone Orsi Date: Fri, 28 May 2021 08:59:14 +0200 Subject: [PATCH 32/45] s_packaging_calculator: add handy mixin This mixin can be used to provide qty by packaging features to any model. --- stock_packaging_calculator/models/__init__.py | 1 + .../models/product_qty_by_packaging_mixin.py | 48 +++++++++++++++++++ 2 files changed, 49 insertions(+) create mode 100644 stock_packaging_calculator/models/product_qty_by_packaging_mixin.py diff --git a/stock_packaging_calculator/models/__init__.py b/stock_packaging_calculator/models/__init__.py index 9649db77a..fb73bb4d1 100644 --- a/stock_packaging_calculator/models/__init__.py +++ b/stock_packaging_calculator/models/__init__.py @@ -1 +1,2 @@ from . import product +from . import product_qty_by_packaging_mixin diff --git a/stock_packaging_calculator/models/product_qty_by_packaging_mixin.py b/stock_packaging_calculator/models/product_qty_by_packaging_mixin.py new file mode 100644 index 000000000..4721fe862 --- /dev/null +++ b/stock_packaging_calculator/models/product_qty_by_packaging_mixin.py @@ -0,0 +1,48 @@ +# Copyright 2021 Camptocamp SA +# @author: Simone Orsi +# @author: Sébastien Alix +# License LGPL-3.0 or later (https://www.gnu.org/licenses/lgpl) +from odoo import api, fields, models + + +class ProductQtyByPackagingMixin(models.AbstractModel): + """Allow displaying product qty by packaging. + """ + + _name = "product.qty_by_packaging.mixin" + _description = "Product Qty By Packaging (Mixin)" + + _qty_by_pkg__product_field_name = "product_id" + _qty_by_pkg__qty_field_name = None + + product_qty_by_packaging_display = fields.Char( + compute="_compute_product_qty_by_packaging_display", string="Qty by packaging" + ) + + def _product_qty_by_packaging_display_depends(self): + depends = [] + if self._qty_by_pkg__product_field_name: + depends.append(self._qty_by_pkg__product_field_name) + if self._qty_by_pkg__qty_field_name: + depends.append(self._qty_by_pkg__qty_field_name) + return depends + + @api.depends_context("lang", "qty_by_pkg_total_units") + @api.depends(lambda self: self._product_qty_by_packaging_display_depends()) + def _compute_product_qty_by_packaging_display(self): + include_total_units = self.env.context.get("qty_by_pkg_total_units", False) + for record in self: + value = "" + product = record._qty_by_packaging_get_product() + if product: + value = product.product_qty_by_packaging_as_str( + record._qty_by_packaging_get_qty(), + include_total_units=include_total_units, + ) + record.product_qty_by_packaging_display = value + + def _qty_by_packaging_get_product(self): + return self[self._qty_by_pkg__product_field_name] + + def _qty_by_packaging_get_qty(self): + return self[self._qty_by_pkg__qty_field_name] From a2134f26aca99c7c2033813e0e4050fe2fec9c12 Mon Sep 17 00:00:00 2001 From: Simone Orsi Date: Thu, 3 Jun 2021 13:03:04 +0200 Subject: [PATCH 33/45] s_packaging_calculator: contained mapping depends on lang Otherwise translations won't be taken into account. --- stock_packaging_calculator/models/product.py | 1 + 1 file changed, 1 insertion(+) diff --git a/stock_packaging_calculator/models/product.py b/stock_packaging_calculator/models/product.py index 026c7971b..57fc4dbdc 100644 --- a/stock_packaging_calculator/models/product.py +++ b/stock_packaging_calculator/models/product.py @@ -24,6 +24,7 @@ class Product(models.Model): help="Technical field to store contained packaging. ", ) + @api.depends_context("lang") @api.depends("packaging_ids.qty") def _compute_packaging_contained_mapping(self): for rec in self: From 99246b3a004b727f795108cf8a2d51fb1e1fa975 Mon Sep 17 00:00:00 2001 From: Simone Orsi Date: Fri, 11 Jun 2021 13:08:24 +0200 Subject: [PATCH 34/45] s_packaging_calculator: include units when units only on demand --- stock_packaging_calculator/models/product.py | 11 ++++++++--- .../models/product_qty_by_packaging_mixin.py | 4 +++- .../tests/test_pkg_qty_str.py | 19 +++++++++++++++++-- 3 files changed, 28 insertions(+), 6 deletions(-) diff --git a/stock_packaging_calculator/models/product.py b/stock_packaging_calculator/models/product.py index 57fc4dbdc..71d706368 100644 --- a/stock_packaging_calculator/models/product.py +++ b/stock_packaging_calculator/models/product.py @@ -153,11 +153,16 @@ class Product(models.Model): "barcode": packaging.barcode, } - def product_qty_by_packaging_as_str(self, prod_qty, include_total_units=False): + def product_qty_by_packaging_as_str( + self, prod_qty, include_total_units=False, only_packaging=False + ): """Return a string representing the qty of each packaging. :param prod_qty: the qty of current product to translate to pkg qty :param include_total_units: includes total qty required initially + :param only_packaging: exclude units if you have only units. + IOW: if the qty does not match any packaging and this flag is true + you'll get an empty string instead of `N units`. """ self.ensure_one() if not prod_qty: @@ -191,8 +196,8 @@ class Product(models.Model): if bit: as_string.append(bit) # Restore unit information if any. - # Skip it if we get only units in the count. - if unit_qty and not has_only_units: + include_units = (has_only_units and not only_packaging) or not has_only_units + if unit_qty and include_units: as_string.append(f"{unit_qty} {self.uom_id.name}") # We want to avoid line break here as this string # can be used by reports diff --git a/stock_packaging_calculator/models/product_qty_by_packaging_mixin.py b/stock_packaging_calculator/models/product_qty_by_packaging_mixin.py index 4721fe862..9065a7b1f 100644 --- a/stock_packaging_calculator/models/product_qty_by_packaging_mixin.py +++ b/stock_packaging_calculator/models/product_qty_by_packaging_mixin.py @@ -27,10 +27,11 @@ class ProductQtyByPackagingMixin(models.AbstractModel): depends.append(self._qty_by_pkg__qty_field_name) return depends - @api.depends_context("lang", "qty_by_pkg_total_units") + @api.depends_context("lang", "qty_by_pkg_total_units", "qty_by_pkg_only_packaging") @api.depends(lambda self: self._product_qty_by_packaging_display_depends()) def _compute_product_qty_by_packaging_display(self): include_total_units = self.env.context.get("qty_by_pkg_total_units", False) + only_packaging = self.env.context.get("qty_by_pkg_only_packaging", False) for record in self: value = "" product = record._qty_by_packaging_get_product() @@ -38,6 +39,7 @@ class ProductQtyByPackagingMixin(models.AbstractModel): value = product.product_qty_by_packaging_as_str( record._qty_by_packaging_get_qty(), include_total_units=include_total_units, + only_packaging=only_packaging, ) record.product_qty_by_packaging_display = value diff --git a/stock_packaging_calculator/tests/test_pkg_qty_str.py b/stock_packaging_calculator/tests/test_pkg_qty_str.py index 93998846c..7fca487de 100644 --- a/stock_packaging_calculator/tests/test_pkg_qty_str.py +++ b/stock_packaging_calculator/tests/test_pkg_qty_str.py @@ -5,7 +5,10 @@ from .common import TestCommon class TestAsStr(TestCommon): def test_as_str(self): - self.assertEqual(self.product_a.product_qty_by_packaging_as_str(10), "") + self.assertEqual(self.product_a.product_qty_by_packaging_as_str(10), "10 Units") + self.assertEqual( + self.product_a.product_qty_by_packaging_as_str(10, only_packaging=True), "" + ) self.assertEqual(self.product_a.product_qty_by_packaging_as_str(100), "2 Box") self.assertEqual( self.product_a.product_qty_by_packaging_as_str(250), "1 Big Box,\xa01 Box" @@ -14,13 +17,18 @@ class TestAsStr(TestCommon): self.product_a.product_qty_by_packaging_as_str(255), "1 Big Box,\xa01 Box,\xa05 Units", ) + # only_packaging has no impact if we get not only units + self.assertEqual( + self.product_a.product_qty_by_packaging_as_str(255, only_packaging=True), + "1 Big Box,\xa01 Box,\xa05 Units", + ) def test_as_str_w_units(self): self.assertEqual( self.product_a.product_qty_by_packaging_as_str( 10, include_total_units=True ), - "", + "10 Units", ) self.assertEqual( self.product_a.product_qty_by_packaging_as_str( @@ -40,6 +48,13 @@ class TestAsStr(TestCommon): ), "1 Big Box,\xa01 Box,\xa05 Units (255 Units)", ) + # only_packaging has no impact if we get not only units + self.assertEqual( + self.product_a.product_qty_by_packaging_as_str( + 255, include_total_units=True, only_packaging=True + ), + "1 Big Box,\xa01 Box,\xa05 Units (255 Units)", + ) def test_as_str_custom_name(self): self.assertEqual( From dd77e4240ff90cf3d9c9466add42d230370a53ef Mon Sep 17 00:00:00 2001 From: Simone Orsi Date: Thu, 15 Jul 2021 16:57:16 +0200 Subject: [PATCH 35/45] s_packaging_calculator: improve test util This way no matter who's changing the behavior of the name getter we'll get the right name. --- stock_packaging_calculator/tests/utils.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/stock_packaging_calculator/tests/utils.py b/stock_packaging_calculator/tests/utils.py index cd9e12560..8c885894c 100644 --- a/stock_packaging_calculator/tests/utils.py +++ b/stock_packaging_calculator/tests/utils.py @@ -5,6 +5,7 @@ def make_pkg_values(record, **kw): """Helper to generate test values for packaging. """ + name = record.name if record._name == "uom.uom": is_unit = True barcode = None @@ -13,9 +14,11 @@ def make_pkg_values(record, **kw): qty = record.qty is_unit = False barcode = record.barcode + if record.product_id: + name = record.product_id._packaging_name_getter(record) values = { "id": record.id, - "name": record.name, + "name": name, "qty": qty, "barcode": barcode, "is_unit": is_unit, From e1f70afde35392f5e23af5b0a15fac5d66bded9d Mon Sep 17 00:00:00 2001 From: oca-travis Date: Mon, 7 Jun 2021 12:22:57 +0000 Subject: [PATCH 36/45] [UPD] Update stock_packaging_calculator.pot --- .../i18n/stock_packaging_calculator.pot | 44 ------------------- 1 file changed, 44 deletions(-) delete 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 deleted file mode 100644 index 664b39319..000000000 --- a/stock_packaging_calculator/i18n/stock_packaging_calculator.pot +++ /dev/null @@ -1,44 +0,0 @@ -# Translation of Odoo Server. -# This file contains the translation of the following modules: -# * stock_packaging_calculator -# -msgid "" -msgstr "" -"Project-Id-Version: Odoo Server 14.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.fields,field_description:stock_packaging_calculator.field_product_product__display_name -msgid "Display Name" -msgstr "" - -#. module: stock_packaging_calculator -#: model:ir.model.fields,field_description:stock_packaging_calculator.field_product_product__id -msgid "ID" -msgstr "" - -#. module: stock_packaging_calculator -#: model:ir.model.fields,field_description:stock_packaging_calculator.field_product_product____last_update -msgid "Last Modified on" -msgstr "" - -#. 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 f669dcd98c55c1b99a820a0db39f8e32a751b286 Mon Sep 17 00:00:00 2001 From: nguyen hoang hiep Date: Tue, 14 Sep 2021 05:57:42 +0000 Subject: [PATCH 37/45] [IMP] stock_packaging_calculator: black, isort, prettier --- .../i18n/stock_packaging_calculator.pot | 54 +++++++++++++++++++ .../models/product_qty_by_packaging_mixin.py | 3 +- .../tests/test_packaging_calc.py | 8 ++- stock_packaging_calculator/tests/utils.py | 3 +- 4 files changed, 62 insertions(+), 6 deletions(-) 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..1cfae5477 --- /dev/null +++ b/stock_packaging_calculator/i18n/stock_packaging_calculator.pot @@ -0,0 +1,54 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * stock_packaging_calculator +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 14.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.fields,field_description:stock_packaging_calculator.field_product_qty_by_packaging_mixin__display_name +msgid "Display Name" +msgstr "" + +#. module: stock_packaging_calculator +#: model:ir.model.fields,field_description:stock_packaging_calculator.field_product_qty_by_packaging_mixin__id +msgid "ID" +msgstr "" + +#. module: stock_packaging_calculator +#: model:ir.model.fields,field_description:stock_packaging_calculator.field_product_qty_by_packaging_mixin____last_update +msgid "Last Modified on" +msgstr "" + +#. 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,name:stock_packaging_calculator.model_product_qty_by_packaging_mixin +msgid "Product Qty By Packaging (Mixin)" +msgstr "" + +#. module: stock_packaging_calculator +#: model:ir.model.fields,field_description:stock_packaging_calculator.field_product_qty_by_packaging_mixin__product_qty_by_packaging_display +msgid "Qty by packaging" +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 "" diff --git a/stock_packaging_calculator/models/product_qty_by_packaging_mixin.py b/stock_packaging_calculator/models/product_qty_by_packaging_mixin.py index 9065a7b1f..693ad561a 100644 --- a/stock_packaging_calculator/models/product_qty_by_packaging_mixin.py +++ b/stock_packaging_calculator/models/product_qty_by_packaging_mixin.py @@ -6,8 +6,7 @@ from odoo import api, fields, models class ProductQtyByPackagingMixin(models.AbstractModel): - """Allow displaying product qty by packaging. - """ + """Allow displaying product qty by packaging.""" _name = "product.qty_by_packaging.mixin" _description = "Product Qty By Packaging (Mixin)" diff --git a/stock_packaging_calculator/tests/test_packaging_calc.py b/stock_packaging_calculator/tests/test_packaging_calc.py index c51347e40..3d4bbfa85 100644 --- a/stock_packaging_calculator/tests/test_packaging_calc.py +++ b/stock_packaging_calculator/tests/test_packaging_calc.py @@ -121,7 +121,9 @@ class TestCalc(TestCommon): contained=[make_pkg_values(self.pkg_box, qty=4)], ), make_pkg_values( - self.pkg_box, qty=1, contained=[make_pkg_values(self.uom_unit, qty=50)], + self.pkg_box, + qty=1, + contained=[make_pkg_values(self.uom_unit, qty=50)], ), make_pkg_values(self.uom_unit, qty=5, contained=None), ] @@ -148,7 +150,9 @@ class TestCalc(TestCommon): ], ), make_pkg_values( - self.pkg_box, qty=1, contained=[make_pkg_values(self.uom_unit, qty=30)], + self.pkg_box, + qty=1, + contained=[make_pkg_values(self.uom_unit, qty=30)], ), make_pkg_values(self.uom_unit, qty=25, contained=None), ] diff --git a/stock_packaging_calculator/tests/utils.py b/stock_packaging_calculator/tests/utils.py index 8c885894c..26c4b357b 100644 --- a/stock_packaging_calculator/tests/utils.py +++ b/stock_packaging_calculator/tests/utils.py @@ -3,8 +3,7 @@ def make_pkg_values(record, **kw): - """Helper to generate test values for packaging. - """ + """Helper to generate test values for packaging.""" name = record.name if record._name == "uom.uom": is_unit = True From 8e2b721618ef3f06ba40cc8b12f4b7269837c73a Mon Sep 17 00:00:00 2001 From: oca-travis Date: Tue, 21 Sep 2021 05:51:07 +0000 Subject: [PATCH 38/45] [UPD] Update stock_packaging_calculator.pot --- stock_packaging_calculator/i18n/stock_packaging_calculator.pot | 3 +++ 1 file changed, 3 insertions(+) diff --git a/stock_packaging_calculator/i18n/stock_packaging_calculator.pot b/stock_packaging_calculator/i18n/stock_packaging_calculator.pot index 1cfae5477..bb3d494b1 100644 --- a/stock_packaging_calculator/i18n/stock_packaging_calculator.pot +++ b/stock_packaging_calculator/i18n/stock_packaging_calculator.pot @@ -14,16 +14,19 @@ msgstr "" "Plural-Forms: \n" #. module: stock_packaging_calculator +#: model:ir.model.fields,field_description:stock_packaging_calculator.field_product_product__display_name #: model:ir.model.fields,field_description:stock_packaging_calculator.field_product_qty_by_packaging_mixin__display_name msgid "Display Name" msgstr "" #. module: stock_packaging_calculator +#: model:ir.model.fields,field_description:stock_packaging_calculator.field_product_product__id #: model:ir.model.fields,field_description:stock_packaging_calculator.field_product_qty_by_packaging_mixin__id msgid "ID" msgstr "" #. module: stock_packaging_calculator +#: model:ir.model.fields,field_description:stock_packaging_calculator.field_product_product____last_update #: model:ir.model.fields,field_description:stock_packaging_calculator.field_product_qty_by_packaging_mixin____last_update msgid "Last Modified on" msgstr "" From ef048daf862b30fde26e5f1fa18ce34f22b09f24 Mon Sep 17 00:00:00 2001 From: OCA-git-bot Date: Tue, 21 Sep 2021 06:07:56 +0000 Subject: [PATCH 39/45] stock_packaging_calculator 14.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 f2d7f8685..711cf3f14 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": "14.0.1.0.1", + "version": "14.0.1.1.0", "development_status": "Alpha", "category": "Warehouse Management", "website": "https://github.com/OCA/stock-logistics-warehouse", From acb6344463de7102b70bc70a5cdb52d92d1fba1b Mon Sep 17 00:00:00 2001 From: Christopher Ormaza Date: Mon, 13 Dec 2021 13:14:34 -0500 Subject: [PATCH 40/45] [IMP] stock_packaging_calculator: black, isort, prettier --- stock_packaging_calculator/models/product.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/stock_packaging_calculator/models/product.py b/stock_packaging_calculator/models/product.py index 71d706368..5f9342424 100644 --- a/stock_packaging_calculator/models/product.py +++ b/stock_packaging_calculator/models/product.py @@ -89,12 +89,12 @@ class Product(models.Model): "_packaging_name_getter", self._packaging_name_getter ) packagings = sorted( - [ + ( Packaging(x.id, name_getter(x), x.qty, x.barcode, 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, ) From e067ec70e86fd00a682055084b275e5172a22be5 Mon Sep 17 00:00:00 2001 From: Christopher Ormaza Date: Mon, 13 Dec 2021 13:25:18 -0500 Subject: [PATCH 41/45] [15.0][MIG] stock_packaging_calculator --- stock_packaging_calculator/README.rst | 3 ++- stock_packaging_calculator/__manifest__.py | 2 +- stock_packaging_calculator/readme/CONTRIBUTORS.rst | 1 + stock_packaging_calculator/tests/common.py | 4 ++-- 4 files changed, 6 insertions(+), 4 deletions(-) diff --git a/stock_packaging_calculator/README.rst b/stock_packaging_calculator/README.rst index 1a28cd164..c0561d5ee 100644 --- a/stock_packaging_calculator/README.rst +++ b/stock_packaging_calculator/README.rst @@ -23,7 +23,7 @@ Stock packaging calculator :target: https://runbot.odoo-community.org/runbot/153/14.0 :alt: Try me on Runbot -|badge1| |badge2| |badge3| |badge4| |badge5| +|badge1| |badge2| |badge3| |badge4| |badge5| Basic module providing an helper method to calculate the quantity of product by packaging. @@ -107,6 +107,7 @@ Contributors ~~~~~~~~~~~~ * Simone Orsi +* Christopher Ormaza Maintainers ~~~~~~~~~~~ diff --git a/stock_packaging_calculator/__manifest__.py b/stock_packaging_calculator/__manifest__.py index 711cf3f14..63bb0de56 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": "14.0.1.1.0", + "version": "15.0.1.0.0", "development_status": "Alpha", "category": "Warehouse Management", "website": "https://github.com/OCA/stock-logistics-warehouse", diff --git a/stock_packaging_calculator/readme/CONTRIBUTORS.rst b/stock_packaging_calculator/readme/CONTRIBUTORS.rst index f583948be..5daf85c2e 100644 --- a/stock_packaging_calculator/readme/CONTRIBUTORS.rst +++ b/stock_packaging_calculator/readme/CONTRIBUTORS.rst @@ -1 +1,2 @@ * Simone Orsi +* Christopher Ormaza diff --git a/stock_packaging_calculator/tests/common.py b/stock_packaging_calculator/tests/common.py index c528958ce..4aa0579a5 100644 --- a/stock_packaging_calculator/tests/common.py +++ b/stock_packaging_calculator/tests/common.py @@ -1,9 +1,9 @@ # Copyright 2020 Camptocamp SA # License LGPL-3.0 or later (https://www.gnu.org/licenses/lgpl) -from odoo.tests import SavepointCase +from odoo.tests import TransactionCase -class TestCommon(SavepointCase): +class TestCommon(TransactionCase): at_install = False post_install = True From 1fc80b089631a2b47b1a432e3de592ee9e73551d Mon Sep 17 00:00:00 2001 From: oca-ci Date: Tue, 22 Feb 2022 18:06:56 +0000 Subject: [PATCH 42/45] [UPD] Update stock_packaging_calculator.pot --- .../i18n/stock_packaging_calculator.pot | 20 +------------------ 1 file changed, 1 insertion(+), 19 deletions(-) diff --git a/stock_packaging_calculator/i18n/stock_packaging_calculator.pot b/stock_packaging_calculator/i18n/stock_packaging_calculator.pot index bb3d494b1..3a5552568 100644 --- a/stock_packaging_calculator/i18n/stock_packaging_calculator.pot +++ b/stock_packaging_calculator/i18n/stock_packaging_calculator.pot @@ -4,7 +4,7 @@ # msgid "" msgstr "" -"Project-Id-Version: Odoo Server 14.0\n" +"Project-Id-Version: Odoo Server 15.0\n" "Report-Msgid-Bugs-To: \n" "Last-Translator: \n" "Language-Team: \n" @@ -13,24 +13,6 @@ msgstr "" "Content-Transfer-Encoding: \n" "Plural-Forms: \n" -#. module: stock_packaging_calculator -#: model:ir.model.fields,field_description:stock_packaging_calculator.field_product_product__display_name -#: model:ir.model.fields,field_description:stock_packaging_calculator.field_product_qty_by_packaging_mixin__display_name -msgid "Display Name" -msgstr "" - -#. module: stock_packaging_calculator -#: model:ir.model.fields,field_description:stock_packaging_calculator.field_product_product__id -#: model:ir.model.fields,field_description:stock_packaging_calculator.field_product_qty_by_packaging_mixin__id -msgid "ID" -msgstr "" - -#. module: stock_packaging_calculator -#: model:ir.model.fields,field_description:stock_packaging_calculator.field_product_product____last_update -#: model:ir.model.fields,field_description:stock_packaging_calculator.field_product_qty_by_packaging_mixin____last_update -msgid "Last Modified on" -msgstr "" - #. module: stock_packaging_calculator #: model:ir.model.fields,field_description:stock_packaging_calculator.field_product_product__packaging_contained_mapping msgid "Packaging Contained Mapping" From 987f96c904a0d437ad8188f3fd29c01cd92cf149 Mon Sep 17 00:00:00 2001 From: OCA-git-bot Date: Tue, 22 Feb 2022 18:11:26 +0000 Subject: [PATCH 43/45] [UPD] README.rst --- stock_packaging_calculator/README.rst | 12 ++++++------ .../static/description/index.html | 7 ++++--- 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/stock_packaging_calculator/README.rst b/stock_packaging_calculator/README.rst index c0561d5ee..adf3a79d7 100644 --- a/stock_packaging_calculator/README.rst +++ b/stock_packaging_calculator/README.rst @@ -14,16 +14,16 @@ Stock packaging calculator :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/14.0/stock_packaging_calculator + :target: https://github.com/OCA/stock-logistics-warehouse/tree/15.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-14-0/stock-logistics-warehouse-14-0-stock_packaging_calculator + :target: https://translation.odoo-community.org/projects/stock-logistics-warehouse-15-0/stock-logistics-warehouse-15-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/14.0 + :target: https://runbot.odoo-community.org/runbot/153/15.0 :alt: Try me on Runbot -|badge1| |badge2| |badge3| |badge4| |badge5| +|badge1| |badge2| |badge3| |badge4| |badge5| Basic module providing an helper method to calculate the quantity of product by packaging. @@ -91,7 +91,7 @@ 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 `_. +`feedback `_. Do not contact contributors directly about support or help with technical issues. @@ -122,6 +122,6 @@ 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. +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 index 0bfe447f7..0745b2c6d 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: LGPL-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

@@ -439,7 +439,7 @@ Only for development or testing purpose, do not use in production.

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.

+feedback.

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

@@ -454,6 +454,7 @@ If you spotted it first, help us smashing it by providing a detailed and welcome

Contributors

@@ -463,7 +464,7 @@ If you spotted it first, help us smashing it by providing a detailed and welcome

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.

+

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 d5af2da0004b31b68d87ec5dff41f0ca57a9d4ab Mon Sep 17 00:00:00 2001 From: Thierry Ducrest Date: Mon, 10 Oct 2022 13:55:30 +0200 Subject: [PATCH 44/45] [IMP] stock_packaging_calculator: black, isort, prettier --- .../odoo/addons/stock_packaging_calculator | 1 + setup/stock_packaging_calculator/setup.py | 6 ++++++ 2 files changed, 7 insertions(+) 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, +) From 7a62f663ee6d0c2393ad2d09c79e88d6308e6efd Mon Sep 17 00:00:00 2001 From: Thierry Ducrest Date: Mon, 10 Oct 2022 14:08:14 +0200 Subject: [PATCH 45/45] [MIG] stock_packaging_calculator: Migration to 16.0 --- stock_packaging_calculator/__manifest__.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/stock_packaging_calculator/__manifest__.py b/stock_packaging_calculator/__manifest__.py index 63bb0de56..b7e7dfee9 100644 --- a/stock_packaging_calculator/__manifest__.py +++ b/stock_packaging_calculator/__manifest__.py @@ -3,8 +3,8 @@ { "name": "Stock packaging calculator", "summary": "Compute product quantity to pick by packaging", - "version": "15.0.1.0.0", - "development_status": "Alpha", + "version": "16.0.1.0.0", + "development_status": "Beta", "category": "Warehouse Management", "website": "https://github.com/OCA/stock-logistics-warehouse", "author": "Camptocamp, Odoo Community Association (OCA)",