diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index eff7a1364c..ff33c79b96 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -64,7 +64,6 @@ exclude: | ^shopinvader_sale_automatic_workflow/| ^shopinvader_sale_communication/| ^shopinvader_sale_coupon/| - ^shopinvader_sale_packaging/| ^shopinvader_sale_packaging_wishlist/| ^shopinvader_sale_profile/| ^shopinvader_sale_report/| diff --git a/setup/shopinvader_restapi_sale_packaging/odoo/addons/shopinvader_restapi_sale_packaging b/setup/shopinvader_restapi_sale_packaging/odoo/addons/shopinvader_restapi_sale_packaging new file mode 120000 index 0000000000..af1258538d --- /dev/null +++ b/setup/shopinvader_restapi_sale_packaging/odoo/addons/shopinvader_restapi_sale_packaging @@ -0,0 +1 @@ +../../../../shopinvader_restapi_sale_packaging \ No newline at end of file diff --git a/setup/shopinvader_restapi_sale_packaging/setup.py b/setup/shopinvader_restapi_sale_packaging/setup.py new file mode 100644 index 0000000000..28c57bb640 --- /dev/null +++ b/setup/shopinvader_restapi_sale_packaging/setup.py @@ -0,0 +1,6 @@ +import setuptools + +setuptools.setup( + setup_requires=['setuptools-odoo'], + odoo_addon=True, +) diff --git a/shopinvader_restapi_sale_packaging/README.rst b/shopinvader_restapi_sale_packaging/README.rst new file mode 100644 index 0000000000..e69de29bb2 diff --git a/shopinvader_sale_packaging/__init__.py b/shopinvader_restapi_sale_packaging/__init__.py similarity index 52% rename from shopinvader_sale_packaging/__init__.py rename to shopinvader_restapi_sale_packaging/__init__.py index 5b1c56416a..99464a7510 100644 --- a/shopinvader_sale_packaging/__init__.py +++ b/shopinvader_restapi_sale_packaging/__init__.py @@ -1,2 +1 @@ from . import services -from . import models diff --git a/shopinvader_restapi_sale_packaging/__manifest__.py b/shopinvader_restapi_sale_packaging/__manifest__.py new file mode 100644 index 0000000000..36760f5821 --- /dev/null +++ b/shopinvader_restapi_sale_packaging/__manifest__.py @@ -0,0 +1,20 @@ +# Copyright 2020 Camptocamp SA +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +{ + "name": "Shopinvader Restapi Sale Packaging", + "Summary": """ + REST services take care of if products are sold by packagings. + """, + "version": "16.0.1.0.0", + "license": "AGPL-3", + "author": "Camptocamp, ACSONE", + "website": "https://github.com/shopinvader/odoo-shopinvader", + "depends": [ + "shopinvader_restapi", + "shopinvader_product", + "shopinvader_product_sale_packaging", + "sale_stock", + ], + "installable": True, +} diff --git a/shopinvader_restapi_sale_packaging/readme/CONTRIBUTORS.rst b/shopinvader_restapi_sale_packaging/readme/CONTRIBUTORS.rst new file mode 100644 index 0000000000..2f618596c2 --- /dev/null +++ b/shopinvader_restapi_sale_packaging/readme/CONTRIBUTORS.rst @@ -0,0 +1,2 @@ +* Simone Orsi +* Marie Lejeune diff --git a/shopinvader_sale_packaging/readme/CREDITS.rst b/shopinvader_restapi_sale_packaging/readme/CREDITS.rst similarity index 90% rename from shopinvader_sale_packaging/readme/CREDITS.rst rename to shopinvader_restapi_sale_packaging/readme/CREDITS.rst index 3038e84ea8..c36447fda7 100644 --- a/shopinvader_sale_packaging/readme/CREDITS.rst +++ b/shopinvader_restapi_sale_packaging/readme/CREDITS.rst @@ -2,3 +2,4 @@ The development of this module has been financially supported by: * Camptocamp * Cosanum +* Acsone diff --git a/shopinvader_restapi_sale_packaging/readme/DESCRIPTION.rst b/shopinvader_restapi_sale_packaging/readme/DESCRIPTION.rst new file mode 100644 index 0000000000..6f0c28dd57 --- /dev/null +++ b/shopinvader_restapi_sale_packaging/readme/DESCRIPTION.rst @@ -0,0 +1,3 @@ +Allow to purchase products by packaging. + +Available packaging are taken using `stock_packaging_calculator `_. diff --git a/shopinvader_sale_packaging/services/__init__.py b/shopinvader_restapi_sale_packaging/services/__init__.py similarity index 100% rename from shopinvader_sale_packaging/services/__init__.py rename to shopinvader_restapi_sale_packaging/services/__init__.py diff --git a/shopinvader_sale_packaging/services/abstract_sale.py b/shopinvader_restapi_sale_packaging/services/abstract_sale.py similarity index 90% rename from shopinvader_sale_packaging/services/abstract_sale.py rename to shopinvader_restapi_sale_packaging/services/abstract_sale.py index aac03b59cd..9f68c1300a 100644 --- a/shopinvader_sale_packaging/services/abstract_sale.py +++ b/shopinvader_restapi_sale_packaging/services/abstract_sale.py @@ -1,4 +1,5 @@ # Copyright 2020 Camptocamp (http://www.camptocamp.com). +# Copyright 2023 Acsone (http://www.acsone.eu). # @author Simone Orsi # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). from odoo.addons.component.core import AbstractComponent @@ -18,7 +19,7 @@ def _convert_one_line(self, line): "packaging_qty": 0.0, "packaging_by_qty": [], } - if line.product_packaging: + if line.product_packaging_id: pkg_vals = line.jsonify(self._parser_line_packaging(), one=True) res.update(pkg_vals) return res @@ -26,7 +27,7 @@ def _convert_one_line(self, line): def _parser_line_packaging(self): return [ ( - "product_packaging:packaging", + "product_packaging_id:packaging", lambda rec, fname: self._packaging_to_json(rec[fname]), ), ("product_packaging_qty:packaging_qty"), diff --git a/shopinvader_sale_packaging/services/cart.py b/shopinvader_restapi_sale_packaging/services/cart.py similarity index 74% rename from shopinvader_sale_packaging/services/cart.py rename to shopinvader_restapi_sale_packaging/services/cart.py index 5e7173449b..8a1d563469 100644 --- a/shopinvader_sale_packaging/services/cart.py +++ b/shopinvader_restapi_sale_packaging/services/cart.py @@ -1,4 +1,5 @@ # Copyright 2020 Camptocamp (http://www.camptocamp.com). +# Copyright 2023 Acsone (http://www.acsone.eu). # @author Simone Orsi # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). from odoo.addons.component.core import Component @@ -18,10 +19,10 @@ def _validator_update_item(self): return res def _prepare_cart_item(self, params, cart): - # TODO: in theory we should be able to skip prod qty - # since it's computed in `sale_order_line_packaging_qty ` res = super()._prepare_cart_item(params, cart) res.update(self._packaging_values_from_params(params)) + if {"product_packaging_id", "product_packaging_qty"}.issubset(res.keys()): + res.pop("product_uom_qty", None) return res def _get_line_copy_vals(self, line): @@ -29,7 +30,7 @@ def _get_line_copy_vals(self, line): if line.product_packaging_qty: res.update( { - "packaging_id": line.product_packaging.id, + "packaging_id": line.product_packaging_id.id, "packaging_qty": line.product_packaging_qty, } ) @@ -37,7 +38,7 @@ def _get_line_copy_vals(self, line): def _upgrade_cart_item_quantity_vals(self, item, params, **kw): res = super()._upgrade_cart_item_quantity_vals(item, params, **kw) - pkg_params = self._packaging_values_from_params(params) - if pkg_params: - res.update(pkg_params) + res.update(self._packaging_values_from_params(params)) + if {"product_packaging_id", "product_packaging_qty"}.issubset(res.keys()): + res.pop("product_uom_qty", None) return res diff --git a/shopinvader_sale_packaging/services/packaging_mixin.py b/shopinvader_restapi_sale_packaging/services/packaging_mixin.py similarity index 84% rename from shopinvader_sale_packaging/services/packaging_mixin.py rename to shopinvader_restapi_sale_packaging/services/packaging_mixin.py index 7e569c9976..19fd406058 100644 --- a/shopinvader_sale_packaging/services/packaging_mixin.py +++ b/shopinvader_restapi_sale_packaging/services/packaging_mixin.py @@ -1,4 +1,5 @@ # Copyright 2020 Camptocamp (http://www.camptocamp.com). +# Copyright 2023 Acsone (http://www.acsone.eu). # @author Simone Orsi # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). @@ -39,7 +40,7 @@ def _packaging_values_from_params(self, params): if "packaging_id" in params and "packaging_qty" in params: return { # Make sure packaging_id is wiped if we pass 0 - "product_packaging": params.pop("packaging_id") or False, + "product_packaging_id": params.pop("packaging_id") or False, "product_packaging_qty": params.pop("packaging_qty"), } return {} @@ -49,8 +50,8 @@ def _packaging_to_json(self, packaging): return None return { "id": packaging.id, - # Use packaging type name because it's translated - "name": packaging.packaging_type_id.name, - "code": packaging.packaging_type_id.code, + # Use packaging level name because it's translated + "name": packaging.packaging_level_id.name, + "code": packaging.packaging_level_id.code, "barcode": packaging.barcode, } diff --git a/shopinvader_sale_packaging/tests/__init__.py b/shopinvader_restapi_sale_packaging/tests/__init__.py similarity index 60% rename from shopinvader_sale_packaging/tests/__init__.py rename to shopinvader_restapi_sale_packaging/tests/__init__.py index a764200b28..180ead1382 100644 --- a/shopinvader_sale_packaging/tests/__init__.py +++ b/shopinvader_restapi_sale_packaging/tests/__init__.py @@ -1,3 +1,2 @@ from . import test_cart from . import test_sale -from . import test_product_data diff --git a/shopinvader_restapi_sale_packaging/tests/common.py b/shopinvader_restapi_sale_packaging/tests/common.py new file mode 100644 index 0000000000..739ff18bb2 --- /dev/null +++ b/shopinvader_restapi_sale_packaging/tests/common.py @@ -0,0 +1,31 @@ +# Copyright 2023 Acsone SA/NV +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). + +from odoo.tests.common import TransactionCase + + +class CommonPackagingCase(TransactionCase): + @classmethod + def setUpClass(cls): + super(CommonPackagingCase, cls).setUpClass() + cls.pkg_level_retail_box = cls.env["product.packaging.level"].create( + { + "name": "Retail Box", + "code": "pack", + "sequence": 3, + } + ) + cls.pkg_level_transport_box = cls.env["product.packaging.level"].create( + { + "name": "Transport Box", + "code": "case", + "sequence": 4, + } + ) + cls.pkg_level_pallet = cls.env["product.packaging.level"].create( + { + "name": "Pallet", + "code": "pallet", + "sequence": 5, + } + ) diff --git a/shopinvader_sale_packaging/tests/test_cart.py b/shopinvader_restapi_sale_packaging/tests/test_cart.py similarity index 62% rename from shopinvader_sale_packaging/tests/test_cart.py rename to shopinvader_restapi_sale_packaging/tests/test_cart.py index e0a00b6b52..713a3b1d31 100644 --- a/shopinvader_sale_packaging/tests/test_cart.py +++ b/shopinvader_restapi_sale_packaging/tests/test_cart.py @@ -1,21 +1,26 @@ # Copyright 2020 Camptocamp SA # Simone Orsi +# Copyright 2023 Acsone SA/NV # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). -from odoo.addons.shopinvader.tests.common import CommonCase -from odoo.addons.shopinvader.tests.test_cart_item import ItemCaseMixin +from odoo.addons.shopinvader_restapi.tests.common import CommonCase +from odoo.addons.shopinvader_restapi.tests.test_cart_item import ItemCaseMixin +from .common import CommonPackagingCase -class ConnectedItemCase(ItemCaseMixin, CommonCase): + +class ConnectedItemCase(ItemCaseMixin, CommonCase, CommonPackagingCase): @classmethod def setUpClass(cls): super().setUpClass() cls._setup_products() - cls.partner = cls.env.ref("shopinvader.partner_1") - cls.cart = cls.env.ref("shopinvader.sale_order_2") + cls.partner = cls.env.ref("shopinvader_restapi.partner_1") + cls.cart = cls.env.ref("shopinvader_restapi.sale_order_2") + # Packages for cls.product_1 cls.pkg_box = cls.env["product.packaging"].create( { "name": "Box", + "packaging_level_id": cls.pkg_level_retail_box.id, "product_id": cls.product_1.id, "qty": 50, "barcode": "BOX", @@ -24,6 +29,7 @@ def setUpClass(cls): cls.pkg_big_box = cls.env["product.packaging"].create( { "name": "Big Box", + "packaging_level_id": cls.pkg_level_transport_box.id, "product_id": cls.product_1.id, "qty": 200, "barcode": "BIGBOX", @@ -32,14 +38,22 @@ def setUpClass(cls): cls.pkg_pallet = cls.env["product.packaging"].create( { "name": "Pallet", + "packaging_level_id": cls.pkg_level_pallet.id, "product_id": cls.product_1.id, "qty": 2000, "barcode": "PALLET", } ) - # This module adds new keys: recompute - cls._refresh_json_data( - cls, cls.cart.mapped("order_line.product_id") + cls.product_1 + # Pallet package for product.product_product_24 (First SOL of cart) + cls.product_2 = cls.env.ref("product.product_product_24") + cls.pkg_pallet_2 = cls.env["product.packaging"].create( + { + "name": "Pallet product 2", + "packaging_level_id": cls.pkg_level_pallet.id, + "product_id": cls.product_2.id, + "qty": 2000, + "barcode": "PALLET 2", + } ) def setUp(self): @@ -53,8 +67,6 @@ def setUp(self): def test_add_item(self): self.remove_cart() last_order = self.env["sale.order"].search([], limit=1, order="id desc") - # TODO: in theory we should be able to skip prod qty - # since it's computed in `sale_order_line_packaging_qty ` cart = self.add_item( self.product_1.id, 1, @@ -67,7 +79,7 @@ def test_add_item(self): cart_line = cart["lines"]["items"][0] # check SO line values line = self.env["sale.order.line"].browse(cart_line["id"]) - self.assertEqual(line.product_packaging, self.pkg_pallet) + self.assertEqual(line.product_packaging_id, self.pkg_pallet) self.assertEqual(line.product_packaging_qty, 2.0) self.assertEqual(line.product_uom_qty, 4000) # Check cart line values @@ -76,22 +88,23 @@ def test_add_item(self): cart_line["packaging"], { "id": self.pkg_pallet.id, - "name": self.pkg_pallet.packaging_type_id.name, - "code": self.pkg_pallet.packaging_type_id.code, + "name": self.pkg_pallet.packaging_level_id.name, + "code": self.pkg_pallet.packaging_level_id.code, "barcode": self.pkg_pallet.barcode, }, ) self.assertEqual(cart_line["packaging_qty"], 2) - self.assertIn("sell_only_by_packaging", cart_line["product"]) def test_update_item(self): - line = self.cart.order_line[0] + line = self.cart.order_line.filtered( + lambda sol: sol.product_id == self.product_2 + ) product = line.product_id cart = self.update_item( - line.id, 1, packaging_id=self.pkg_pallet, packaging_qty=3.0 + line.id, 1, packaging_id=self.pkg_pallet_2, packaging_qty=3.0 ) # check SO line values - self.assertEqual(line.product_packaging, self.pkg_pallet) + self.assertEqual(line.product_packaging_id, self.pkg_pallet_2) self.assertEqual(line.product_packaging_qty, 3.0) self.assertEqual(line.product_uom_qty, 6000) # Check cart line values @@ -100,21 +113,22 @@ def test_update_item(self): self.assertEqual( cart_line["packaging"], { - "id": self.pkg_pallet.id, - "name": self.pkg_pallet.packaging_type_id.name, - "code": self.pkg_pallet.packaging_type_id.code, - "barcode": self.pkg_pallet.barcode, + "id": self.pkg_pallet_2.id, + "name": self.pkg_pallet_2.packaging_level_id.name, + "code": self.pkg_pallet_2.packaging_level_id.code, + "barcode": self.pkg_pallet_2.barcode, }, ) self.assertEqual(cart_line["packaging_qty"], 3.0) - self.assertIn("sell_only_by_packaging", cart_line["product"]) def test_copy_line(self): - line = self.cart.order_line[0] + line = self.cart.order_line.filtered( + lambda sol: sol.product_id == self.product_2 + ) product = line.product_id line.write( { - "product_packaging": self.pkg_pallet.id, + "product_packaging_id": self.pkg_pallet_2.id, "product_packaging_qty": 4.0, } ) @@ -125,23 +139,20 @@ def test_copy_line(self): cart_line = [ x for x in cart["lines"]["items"] if x["product"]["id"] == product.id ][0] - self.assertIn("sell_only_by_packaging", cart_line["product"]) self.check_product_and_qty(cart_line, product.id, 8000) # Check cart line values self.assertEqual( cart_line["packaging"], { - "id": self.pkg_pallet.id, - "name": self.pkg_pallet.packaging_type_id.name, - "code": self.pkg_pallet.packaging_type_id.code, - "barcode": self.pkg_pallet.barcode, + "id": self.pkg_pallet_2.id, + "name": self.pkg_pallet_2.packaging_level_id.name, + "code": self.pkg_pallet_2.packaging_level_id.code, + "barcode": self.pkg_pallet_2.barcode, }, ) self.assertEqual(cart_line["packaging_qty"], 4.0) # check SO line values line = self.env["sale.order.line"].browse(cart_line["id"]) - self.assertEqual(line.product_packaging, self.pkg_pallet) + self.assertEqual(line.product_packaging_id, self.pkg_pallet_2) self.assertEqual(line.product_packaging_qty, 4.0) self.assertEqual(line.product_uom_qty, 8000) - - # TODO: add tests for packaging computation diff --git a/shopinvader_sale_packaging/tests/test_sale.py b/shopinvader_restapi_sale_packaging/tests/test_sale.py similarity index 66% rename from shopinvader_sale_packaging/tests/test_sale.py rename to shopinvader_restapi_sale_packaging/tests/test_sale.py index a8f93857db..90c8d052fb 100644 --- a/shopinvader_sale_packaging/tests/test_sale.py +++ b/shopinvader_restapi_sale_packaging/tests/test_sale.py @@ -1,32 +1,34 @@ # Copyright 2020 Camptocamp SA # Simone Orsi +# Copyright 2023 Acsone SA/NV # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). -from odoo.addons.shopinvader.tests.common import CommonCase +from odoo.addons.shopinvader_restapi.tests.common import CommonCase +from .common import CommonPackagingCase -class TestSaleOrderPackaging(CommonCase): + +class TestSaleOrderPackaging(CommonCase, CommonPackagingCase): @classmethod def setUpClass(cls): super().setUpClass() - cls.sale = cls.env.ref("shopinvader.sale_order_2") - cls.sale_line1 = cls.env.ref("shopinvader.sale_order_line_4") - cls.sale_line2 = cls.env.ref("shopinvader.sale_order_line_5") + cls.sale = cls.env.ref("shopinvader_restapi.sale_order_2") + cls.sale_line1 = cls.env.ref("shopinvader_restapi.sale_order_line_4") + cls.sale_line2 = cls.env.ref("shopinvader_restapi.sale_order_line_5") cls.pkg_box = cls.env["product.packaging"].create( { "name": "Box", + "packaging_level_id": cls.pkg_level_retail_box.id, "product_id": cls.sale_line1.product_id.id, "qty": 100, "barcode": "BOX", } ) cls.sale_line1.write( - {"product_packaging": cls.pkg_box.id, "product_packaging_qty": 5} + {"product_packaging_id": cls.pkg_box.id, "product_packaging_qty": 5} ) cls.sale.action_confirm() - cls.partner = cls.env.ref("shopinvader.partner_1") - # This module adds new keys: recompute - cls._refresh_json_data(cls, cls.sale.mapped("order_line.product_id")) + cls.partner = cls.env.ref("shopinvader_restapi.partner_1") def setUp(self): super().setUp() @@ -44,8 +46,8 @@ def test_sale_line_data(self): line["packaging"], { "id": self.pkg_box.id, - "name": self.pkg_box.packaging_type_id.name, - "code": self.pkg_box.packaging_type_id.code, + "name": self.pkg_box.packaging_level_id.name, + "code": self.pkg_box.packaging_level_id.code, "barcode": self.pkg_box.barcode, }, ) @@ -55,4 +57,3 @@ def test_sale_line_data(self): self.assertEqual(line["packaging"], None) self.assertEqual(line["packaging_qty"], 0.0) self.assertEqual(line["qty"], self.sale_line2.product_uom_qty) - self.assertIn("sell_only_by_packaging", line["product"]) diff --git a/shopinvader_sale_packaging/README.rst b/shopinvader_sale_packaging/README.rst deleted file mode 100644 index 534013f01f..0000000000 --- a/shopinvader_sale_packaging/README.rst +++ /dev/null @@ -1,67 +0,0 @@ -========================== -Shopinvader Sale Packaging -========================== - -.. !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! - !! This file is generated by oca-gen-addon-readme !! - !! changes will be overwritten. !! - !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! - -.. |badge1| image:: https://img.shields.io/badge/maturity-Beta-yellow.png - :target: https://odoo-community.org/page/development-status - :alt: Beta -.. |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-shopinvader%2Fodoo--shopinvader-lightgray.png?logo=github - :target: https://github.com/shopinvader/odoo-shopinvader/tree/14.0/shopinvader_sale_packaging - :alt: shopinvader/odoo-shopinvader - -|badge1| |badge2| |badge3| - -Provide packaging information for products and allow to purchase them by packaging. - -Available packaging are taken using `stock_packaging_calculator `_. - -**Table of contents** - -.. contents:: - :local: - -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 SA - -Contributors -~~~~~~~~~~~~ - -* Simone Orsi - -Other credits -~~~~~~~~~~~~~ - -The development of this module has been financially supported by: - -* Camptocamp -* Cosanum - -Maintainers -~~~~~~~~~~~ - -This module is part of the `shopinvader/odoo-shopinvader `_ project on GitHub. - -You are welcome to contribute. diff --git a/shopinvader_sale_packaging/__manifest__.py b/shopinvader_sale_packaging/__manifest__.py deleted file mode 100644 index 9af9e50027..0000000000 --- a/shopinvader_sale_packaging/__manifest__.py +++ /dev/null @@ -1,25 +0,0 @@ -# Copyright 2020 Camptocamp SA -# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). - -{ - "name": "Shopinvader Sale Packaging", - "Summary": """ - Sell products by packaging. - """, - "version": "14.0.1.1.0", - "license": "AGPL-3", - "author": "Camptocamp SA", - "website": "https://github.com/shopinvader/odoo-shopinvader", - "depends": [ - "shopinvader", - "sale_stock", - "sale_by_packaging", - "stock_packaging_calculator_packaging_type", - ], - "data": [ - "data/ir_export_product.xml", - "views/product_packaging.xml", - "views/product_packaging_type.xml", - ], - "installable": False, -} diff --git a/shopinvader_sale_packaging/data/ir_export_product.xml b/shopinvader_sale_packaging/data/ir_export_product.xml deleted file mode 100644 index d80dcef588..0000000000 --- a/shopinvader_sale_packaging/data/ir_export_product.xml +++ /dev/null @@ -1,17 +0,0 @@ - - - - - packaging - - - - - sell_only_by_packaging - - - - diff --git a/shopinvader_sale_packaging/i18n/shopinvader_sale_packaging.pot b/shopinvader_sale_packaging/i18n/shopinvader_sale_packaging.pot deleted file mode 100644 index 64a1b02748..0000000000 --- a/shopinvader_sale_packaging/i18n/shopinvader_sale_packaging.pot +++ /dev/null @@ -1,72 +0,0 @@ -# Translation of Odoo Server. -# This file contains the translation of the following modules: -# * shopinvader_sale_packaging -# -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: shopinvader_sale_packaging -#: model:ir.model.fields,field_description:shopinvader_sale_packaging.field_product_packaging__display_name -#: model:ir.model.fields,field_description:shopinvader_sale_packaging.field_product_packaging_type__display_name -#: model:ir.model.fields,field_description:shopinvader_sale_packaging.field_shopinvader_variant__display_name -msgid "Display Name" -msgstr "" - -#. module: shopinvader_sale_packaging -#: model:ir.model.fields,field_description:shopinvader_sale_packaging.field_product_packaging__id -#: model:ir.model.fields,field_description:shopinvader_sale_packaging.field_product_packaging_type__id -#: model:ir.model.fields,field_description:shopinvader_sale_packaging.field_shopinvader_variant__id -msgid "ID" -msgstr "" - -#. module: shopinvader_sale_packaging -#: model:ir.model.fields,help:shopinvader_sale_packaging.field_product_packaging__shopinvader_display -#: model:ir.model.fields,help:shopinvader_sale_packaging.field_product_packaging_type__shopinvader_display -msgid "Include this packaging into Shopinvader product metadata." -msgstr "" - -#. module: shopinvader_sale_packaging -#: model:ir.model.fields,field_description:shopinvader_sale_packaging.field_product_packaging____last_update -#: model:ir.model.fields,field_description:shopinvader_sale_packaging.field_product_packaging_type____last_update -#: model:ir.model.fields,field_description:shopinvader_sale_packaging.field_shopinvader_variant____last_update -msgid "Last Modified on" -msgstr "" - -#. module: shopinvader_sale_packaging -#: model:ir.model.fields,field_description:shopinvader_sale_packaging.field_shopinvader_variant__packaging -msgid "Packaging" -msgstr "" - -#. module: shopinvader_sale_packaging -#: model:ir.model,name:shopinvader_sale_packaging.model_product_packaging -msgid "Product Packaging" -msgstr "" - -#. module: shopinvader_sale_packaging -#: model:ir.model.fields,field_description:shopinvader_sale_packaging.field_product_packaging__shopinvader_display -#: model:ir.model.fields,field_description:shopinvader_sale_packaging.field_product_packaging_type__shopinvader_display -msgid "Shopinvader Display" -msgstr "" - -#. module: shopinvader_sale_packaging -#: model:ir.model,name:shopinvader_sale_packaging.model_shopinvader_variant -msgid "Shopinvader Variant" -msgstr "" - -#. module: shopinvader_sale_packaging -#: model:ir.model.fields,help:shopinvader_sale_packaging.field_shopinvader_variant__packaging -msgid "Technical field to store packaging for the shop" -msgstr "" - -#. module: shopinvader_sale_packaging -#: model:ir.model,name:shopinvader_sale_packaging.model_product_packaging_type -msgid "Type management for product.packaging" -msgstr "" diff --git a/shopinvader_sale_packaging/models/__init__.py b/shopinvader_sale_packaging/models/__init__.py deleted file mode 100644 index 72e186e6f0..0000000000 --- a/shopinvader_sale_packaging/models/__init__.py +++ /dev/null @@ -1,3 +0,0 @@ -from . import shopinvader_variant -from . import product_packaging -from . import product_packaging_type diff --git a/shopinvader_sale_packaging/models/product_packaging.py b/shopinvader_sale_packaging/models/product_packaging.py deleted file mode 100644 index c6cd0d1f7a..0000000000 --- a/shopinvader_sale_packaging/models/product_packaging.py +++ /dev/null @@ -1,21 +0,0 @@ -# Copyright 2021 Camptocamp SA (http://www.camptocamp.com). -# @author Simone Orsi -# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl) -from odoo import api, fields, models - - -class ProductPackaging(models.Model): - - _inherit = "product.packaging" - - shopinvader_display = fields.Boolean( - compute="_compute_shopinvader_display", - readonly=False, - store=True, - help="Include this packaging into Shopinvader product metadata.", - ) - - @api.depends("packaging_type_id.shopinvader_display") - def _compute_shopinvader_display(self): - for record in self: - record.shopinvader_display = record.packaging_type_id.shopinvader_display diff --git a/shopinvader_sale_packaging/models/product_packaging_type.py b/shopinvader_sale_packaging/models/product_packaging_type.py deleted file mode 100644 index 0ff8932472..0000000000 --- a/shopinvader_sale_packaging/models/product_packaging_type.py +++ /dev/null @@ -1,14 +0,0 @@ -# Copyright 2021 Camptocamp SA (http://www.camptocamp.com). -# @author Simone Orsi -# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). - -from odoo import fields, models - - -class ProductPackagingType(models.Model): - _inherit = "product.packaging.type" - - shopinvader_display = fields.Boolean( - help="Include this packaging into Shopinvader product metadata.", - default=True, - ) diff --git a/shopinvader_sale_packaging/models/shopinvader_variant.py b/shopinvader_sale_packaging/models/shopinvader_variant.py deleted file mode 100644 index 209319bed2..0000000000 --- a/shopinvader_sale_packaging/models/shopinvader_variant.py +++ /dev/null @@ -1,68 +0,0 @@ -# Copyright 2020 Camptocamp (http://www.camptocamp.com). -# @author Simone Orsi -# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). - -from odoo import api, models - -from odoo.addons.base_sparse_field.models.fields import Serialized - - -class ShopinvaderVariant(models.Model): - _inherit = "shopinvader.variant" - - packaging = Serialized( - compute="_compute_packaging", - help="Technical field to store packaging for the shop", - store=True, - ) - - @api.depends(lambda self: self._compute_packaging_depends()) - def _compute_packaging(self): - for rec in self: - rec.packaging = rec._get_variant_packaging() - - def _compute_packaging_depends(self): - return ( - "lang_id", - "record_id.sell_only_by_packaging", - "record_id.packaging_ids.qty", - "record_id.packaging_ids.can_be_sold", - "record_id.packaging_ids.shopinvader_display", - "record_id.packaging_ids.barcode", - "record_id.packaging_ids.packaging_type_id.name", - ) - - def _get_variant_packaging(self): - res = [] - ctx = self._get_variant_packaging_ctx(self.backend_id) - rec = self.record_id.with_context(ctx) - contained_mapping = rec.packaging_contained_mapping or {} - packaging = rec._ordered_packaging() - can_be_sold_info = { - x["id"]: x["can_be_sold"] for x in self.packaging_ids.read(["can_be_sold"]) - } - for pkg in packaging: - pkg_info = self._prepare_qty_by_packaging_values(pkg, pkg.qty) - pkg_info["contained"] = contained_mapping.get(str(pkg.id)) - pkg_info["can_be_sold"] = can_be_sold_info.get(pkg.id, False) - if pkg.is_unit: - pkg_info["can_be_sold"] = not self.sell_only_by_packaging - res.append(pkg_info) - return res - - def _get_variant_packaging_ctx(self, backend): - return { - "lang": self.lang_id.code, - # consider only packaging that can be displayed - "_packaging_filter": lambda x: x.shopinvader_display, - "_packaging_values_handler": self._prepare_qty_by_packaging_values, - } - - 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, - "barcode": packaging.barcode, - } diff --git a/shopinvader_sale_packaging/readme/CONTRIBUTORS.rst b/shopinvader_sale_packaging/readme/CONTRIBUTORS.rst deleted file mode 100644 index f583948be8..0000000000 --- a/shopinvader_sale_packaging/readme/CONTRIBUTORS.rst +++ /dev/null @@ -1 +0,0 @@ -* Simone Orsi diff --git a/shopinvader_sale_packaging/readme/DESCRIPTION.rst b/shopinvader_sale_packaging/readme/DESCRIPTION.rst deleted file mode 100644 index 9b6ea93c00..0000000000 --- a/shopinvader_sale_packaging/readme/DESCRIPTION.rst +++ /dev/null @@ -1,3 +0,0 @@ -Provide packaging information for products and allow to purchase them by packaging. - -Available packaging are taken using `stock_packaging_calculator `_. diff --git a/shopinvader_sale_packaging/static/description/index.html b/shopinvader_sale_packaging/static/description/index.html deleted file mode 100644 index bf54f41c62..0000000000 --- a/shopinvader_sale_packaging/static/description/index.html +++ /dev/null @@ -1,424 +0,0 @@ - - - - - - -Shopinvader Sale Packaging - - - -
-

Shopinvader Sale Packaging

- - -

Beta License: AGPL-3 shopinvader/odoo-shopinvader

-

Provide packaging information for products and allow to purchase them by packaging.

-

Available packaging are taken using stock_packaging_calculator.

-

Table of contents

- -
-

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 SA
  • -
-
-
-

Contributors

- -
-
-

Other credits

-

The development of this module has been financially supported by:

-
    -
  • Camptocamp
  • -
  • Cosanum
  • -
-
-
-

Maintainers

-

This module is part of the shopinvader/odoo-shopinvader project on GitHub.

-

You are welcome to contribute.

-
-
-
- - diff --git a/shopinvader_sale_packaging/tests/test_product_data.py b/shopinvader_sale_packaging/tests/test_product_data.py deleted file mode 100644 index d08926b38a..0000000000 --- a/shopinvader_sale_packaging/tests/test_product_data.py +++ /dev/null @@ -1,125 +0,0 @@ -# Copyright 2020 Camptocamp SA -# Simone Orsi -# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). - -from odoo.addons.shopinvader.tests.common import CommonCase -from odoo.addons.stock_packaging_calculator.tests.utils import make_pkg_values - - -class TestProductPackagingData(CommonCase): - - maxDiff = None - - @classmethod - def setUpClass(cls): - super().setUpClass() - cls.product = product = cls.env.ref("product.product_product_6") - cls.type_retail_box = cls.env["product.packaging.type"].create( - {"name": "Retail Box", "code": "PACK", "sequence": 3} - ) - cls.type_transport_box = cls.env["product.packaging.type"].create( - {"name": "Transport Box", "code": "CASE", "sequence": 4} - ) - cls.type_pallet = cls.env["product.packaging.type"].create( - {"name": "Pallet", "code": "PALLET", "sequence": 5} - ) - cls.pkg_box = cls.env["product.packaging"].create( - { - "name": "Box", - "product_id": product.id, - "qty": 50, - "packaging_type_id": cls.type_retail_box.id, - "barcode": "BOX", - } - ) - cls.pkg_big_box = cls.env["product.packaging"].create( - { - "name": "Big Box", - "product_id": product.id, - "qty": 200, - "packaging_type_id": cls.type_transport_box.id, - "barcode": "BIGBOX", - } - ) - cls.pkg_pallet = cls.env["product.packaging"].create( - { - "name": "Pallet", - "product_id": product.id, - "qty": 2000, - "packaging_type_id": cls.type_pallet.id, - "barcode": "PALLET", - } - ) - cls._bind_products(cls, product) - cls.shop_variant = product.shopinvader_bind_ids[0] - # Let it compute once - cls.shop_variant._compute_packaging() - - def _default_expected(self): - return [ - make_pkg_values( - self.pkg_pallet, - can_be_sold=self.pkg_pallet.can_be_sold, - contained=[ - make_pkg_values( - self.pkg_big_box, - qty=10, - ) - ], - ), - make_pkg_values( - self.pkg_big_box, - can_be_sold=self.pkg_pallet.can_be_sold, - contained=[make_pkg_values(self.pkg_box, qty=4)], - ), - make_pkg_values( - self.pkg_box, - can_be_sold=self.pkg_pallet.can_be_sold, - contained=[make_pkg_values(self.product.uom_id, qty=50)], - ), - make_pkg_values( - self.product.uom_id, - can_be_sold=not self.product.sell_only_by_packaging, - qty=1.0, - contained=None, - ), - ] - - def test_product_data(self): - expected = self._default_expected() - self.assertEqual(self.shop_variant.packaging, expected) - - def test_product_data_recompute1(self): - # ensure it gets recomputed if packaging params change - self.type_pallet.name = "BLA BLA" - expected = self._default_expected() - expected[0]["name"] = "BLA BLA" - self.assertEqual(self.shop_variant.packaging, expected) - - def test_product_data_recompute2(self): - # ensure it gets recomputed if packaging params change - self.pkg_box.qty = 20 - self.pkg_big_box.qty = 400 - expected = self._default_expected() - expected[0]["contained"][0]["qty"] = 5.0 - expected[1]["qty"] = 400.0 - expected[1]["contained"][0]["qty"] = 20.0 - expected[2]["contained"][0]["qty"] = 20.0 - self.assertEqual(self.shop_variant.packaging, expected) - - def test_product_data_recompute3(self): - self.assertIn( - self.pkg_pallet.id, [x["id"] for x in self.shop_variant.packaging] - ) - self.pkg_pallet.shopinvader_display = False - self.assertNotIn( - self.pkg_pallet.id, [x["id"] for x in self.shop_variant.packaging] - ) - - def test_product_data_recompute4(self): - unit = self.shop_variant.packaging[-1] - self.assertTrue(unit["is_unit"]) - self.assertTrue(unit["can_be_sold"]) - self.product.sell_only_by_packaging = True - unit = self.shop_variant.packaging[-1] - self.assertFalse(unit["can_be_sold"]) diff --git a/shopinvader_sale_packaging/views/product_packaging.xml b/shopinvader_sale_packaging/views/product_packaging.xml deleted file mode 100644 index f1638c459a..0000000000 --- a/shopinvader_sale_packaging/views/product_packaging.xml +++ /dev/null @@ -1,35 +0,0 @@ - - - - product.packaging - - - - - - - - - - product.packaging - - - - - - - - - diff --git a/shopinvader_sale_packaging/views/product_packaging_type.xml b/shopinvader_sale_packaging/views/product_packaging_type.xml deleted file mode 100644 index e3fe08f690..0000000000 --- a/shopinvader_sale_packaging/views/product_packaging_type.xml +++ /dev/null @@ -1,15 +0,0 @@ - - - - product.packaging.type - - - - - - - -