From a450f7c0e0617630c85539ce04b3a7374d00cbbb Mon Sep 17 00:00:00 2001 From: Michael Tietz <78783475+mt-software-de@users.noreply.github.com> Date: Tue, 30 May 2023 09:26:32 +0200 Subject: [PATCH 1/2] [FIX] stock_picking_volume: stock.picking _compute_volume Calculate the volume only from not canceled moves --- stock_picking_volume/models/stock_picking.py | 14 ++-- .../tests/test_stock_picking_volume.py | 67 ++++++++++++++++--- 2 files changed, 66 insertions(+), 15 deletions(-) diff --git a/stock_picking_volume/models/stock_picking.py b/stock_picking_volume/models/stock_picking.py index 483a1bde0..74ceaae96 100644 --- a/stock_picking_volume/models/stock_picking.py +++ b/stock_picking_volume/models/stock_picking.py @@ -19,12 +19,18 @@ class StockPicking(models.Model): string="Volume unit of measure label", compute="_compute_volume_uom_name" ) - @api.depends("move_ids", "move_ids.volume") + @api.depends("move_ids", "move_ids.volume", "move_ids.state") def _compute_volume(self): for picking in self: - new_volume = sum(picking.move_ids.mapped("volume")) - if picking.volume != new_volume: - picking.volume = new_volume + moves = picking.move_ids + exclude_cancel = any(m.state != "cancel" for m in moves) + volume = 0 + for move in moves: + if move.state == "cancel" and exclude_cancel: + continue + volume += move.volume + if picking.volume != volume: + picking.volume = volume def _compute_volume_uom_name(self): self.volume_uom_name = self.env[ diff --git a/stock_picking_volume/tests/test_stock_picking_volume.py b/stock_picking_volume/tests/test_stock_picking_volume.py index a7e1cb127..2e4eac03e 100644 --- a/stock_picking_volume/tests/test_stock_picking_volume.py +++ b/stock_picking_volume/tests/test_stock_picking_volume.py @@ -19,17 +19,7 @@ class TestStockPickingVolume(TransactionCase): ) cls.loc_stock = cls.wh.lot_stock_id cls.loc_customer = cls.env.ref("stock.stock_location_customers") - cls.product_template = cls.env["product.template"].create( - { - "name": "Unittest P1", - "product_length": 10.0, - "product_width": 5.0, - "product_height": 3.0, - "uom_id": cls.env.ref("uom.product_uom_unit").id, - "type": "product", - } - ) - cls.product = cls.product_template.product_variant_ids + cls.product = cls._create_product("Unittest P1", 10.0, 5.0, 3.0) cls.picking_type_out = cls.env.ref("stock.picking_type_out") cls.picking = cls.env["stock.picking"].create( { @@ -53,6 +43,21 @@ class TestStockPickingVolume(TransactionCase): } ) + @classmethod + def _create_product(cls, name, length, width, height): + product = cls.env["product.product"].create( + { + "name": name, + "product_length": length, + "product_width": width, + "product_height": height, + "uom_id": cls.env.ref("uom.product_uom_unit").id, + "dimensional_uom_id": cls.env.ref("uom.product_uom_meter").id, + "type": "product", + } + ) + return product + def _set_product_qty(self, product, qty): self.env["stock.quant"]._update_available_quantity(product, self.loc_stock, qty) @@ -139,3 +144,43 @@ class TestStockPickingVolume(TransactionCase): self.picking.button_validate() self.picking.action_cancel() self.assertEqual(self.picking.volume, 750) + + def test_picking_with_canceled_move(self): + """ + Data: + one picking with two move lines with 5 units of product + Test Case: + set 5 unit of product as available + get the volume of the picking + Expected result: + volume is 5 * 10 * 5 * 3 = 750 + The volume is computed from the expected quantity + """ + product2 = self._create_product("Product2", 10.0, 5.0, 3.0) + self._set_product_qty(self.product, 5) + self._set_product_qty(product2, 5) + self.picking.write( + { + "move_ids": [ + ( + 0, + 0, + { + "name": product2.name, + "product_id": product2.id, + "product_uom": product2.uom_id.id, + "product_uom_qty": 5.0, + "location_id": self.loc_stock.id, + "location_dest_id": self.loc_customer.id, + }, + ) + ] + } + ) + self.picking.action_confirm() + self.picking.action_assign() + self.picking.invalidate_cache() + self.assertEqual(self.picking.volume, 750 * 2) + self.picking.move_ids[1]._action_cancel() + self.picking.invalidate_cache() + self.assertEqual(self.picking.volume, 750) From 404a2a21eec491322f2cb4c931b7659688378cfd Mon Sep 17 00:00:00 2001 From: Michael Tietz Date: Tue, 11 Jul 2023 16:35:36 +0200 Subject: [PATCH 2/2] [IMP] stock_picking_volume: Move _get_volume_for_qty to product.product --- stock_picking_volume/models/__init__.py | 1 + .../models/product_product.py | 27 +++++++++++++++++++ stock_picking_volume/models/stock_move.py | 19 +------------ .../tests/test_stock_picking_volume.py | 7 +++++ .../models/__init__.py | 2 +- .../{stock_move.py => product_product.py} | 18 ++++++------- .../tests/test_stock_move_volume.py | 4 +-- 7 files changed, 48 insertions(+), 30 deletions(-) create mode 100644 stock_picking_volume/models/product_product.py rename stock_picking_volume_packaging/models/{stock_move.py => product_product.py} (56%) diff --git a/stock_picking_volume/models/__init__.py b/stock_picking_volume/models/__init__.py index a33bde1e8..a8d792a80 100644 --- a/stock_picking_volume/models/__init__.py +++ b/stock_picking_volume/models/__init__.py @@ -1,2 +1,3 @@ +from . import product_product from . import stock_move from . import stock_picking diff --git a/stock_picking_volume/models/product_product.py b/stock_picking_volume/models/product_product.py new file mode 100644 index 000000000..38e8a0aa0 --- /dev/null +++ b/stock_picking_volume/models/product_product.py @@ -0,0 +1,27 @@ +# Copyright 2023 Michael Tietz (MT Software) +# Copyright 2023 ACSONE SA/NV +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). +from odoo import models + + +class ProductProduct(models.Model): + _inherit = "product.product" + + def _get_volume_for_qty(self, qty, from_uom=None): + """Return the volume for the given qty. + + This method is meant to be inherited to change the volume + computation for a specific product. + + qty: float quantity to compute the volume for. + from_uom: uom of given qty + + An override of this method could take into account the packaging + of the product to compute the volume. (using the volume information + on the packaging provided by the module stock_quant_package_dimension + and the method product_qty_by_packaging on the product provided by the + module stock_packaging_calculator) + """ + self.ensure_one() + qty = from_uom and from_uom._compute_quantity(qty, self.uom_id) or qty + return qty * self.volume diff --git a/stock_picking_volume/models/stock_move.py b/stock_picking_volume/models/stock_move.py index f86b662a8..38b9165e5 100644 --- a/stock_picking_volume/models/stock_move.py +++ b/stock_picking_volume/models/stock_move.py @@ -26,27 +26,10 @@ class StockMove(models.Model): qty = move.product_uom_qty if move.state in ("partially_available", "assigned"): qty = move.reserved_availability - new_volume = move._get_volume_for_qty(qty=qty) + new_volume = move.product_id._get_volume_for_qty(qty, move.product_uom) if move.volume != new_volume: move.volume = new_volume - def _get_volume_for_qty(self, qty): - """Return the volume for the move. - - This method is meant to be inherited to change the volume - computation for a specific move. - - qty: float quantity to compute the volume for. - - An override of this method could take into account the packaging - of the product to compute the volume. (using the volume information - on the packaging provided by the module stock_quant_package_dimension - and the method product_qty_by_packaging on the product provided by the - module stock_packaging_calculator) - """ - self.ensure_one() - return qty * self.product_id.volume - def _compute_volume_uom_name(self): self.volume_uom_name = self.env[ "product.template" diff --git a/stock_picking_volume/tests/test_stock_picking_volume.py b/stock_picking_volume/tests/test_stock_picking_volume.py index 2e4eac03e..3e0b7d967 100644 --- a/stock_picking_volume/tests/test_stock_picking_volume.py +++ b/stock_picking_volume/tests/test_stock_picking_volume.py @@ -184,3 +184,10 @@ class TestStockPickingVolume(TransactionCase): self.picking.move_ids[1]._action_cancel() self.picking.invalidate_cache() self.assertEqual(self.picking.volume, 750) + + def test_product_volume(self): + self.assertEqual(self.product._get_volume_for_qty(5), 750) + from_uom = self.env.ref("uom.product_uom_dozen") + self.assertEqual( + self.product._get_volume_for_qty(5 * from_uom.factor, from_uom), 750 + ) diff --git a/stock_picking_volume_packaging/models/__init__.py b/stock_picking_volume_packaging/models/__init__.py index 6bda2d242..5c74c8c30 100644 --- a/stock_picking_volume_packaging/models/__init__.py +++ b/stock_picking_volume_packaging/models/__init__.py @@ -1 +1 @@ -from . import stock_move +from . import product_product diff --git a/stock_picking_volume_packaging/models/stock_move.py b/stock_picking_volume_packaging/models/product_product.py similarity index 56% rename from stock_picking_volume_packaging/models/stock_move.py rename to stock_picking_volume_packaging/models/product_product.py index 0a5fdde4a..e6d1571c9 100644 --- a/stock_picking_volume_packaging/models/stock_move.py +++ b/stock_picking_volume_packaging/models/product_product.py @@ -1,26 +1,26 @@ # Copyright 2020-2022 Camptocamp SA # Copyright 2023 ACSONE SA/NV +# Copyright 2023 Michael Tietz (MT Software) # License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). from odoo import models -class StockMove(models.Model): +class ProductProduct(models.Model): + _inherit = "product.product" - _inherit = "stock.move" - - def _get_volume_for_qty(self, qty): + def _get_volume_for_qty(self, qty, from_uom=None): self.ensure_one() - product = self.product_id - if not product.packaging_ids.filtered("volume"): - return super()._get_volume_for_qty(qty=qty) - packagings_with_volume = product.with_context( + if not self.packaging_ids.filtered("volume"): + return super()._get_volume_for_qty(qty, from_uom) + qty = from_uom and from_uom._compute_quantity(qty, self.uom_id) or qty + packagings_with_volume = self.with_context( _packaging_filter=lambda p: p.volume ).product_qty_by_packaging(qty) volume = 0 for packaging_info in packagings_with_volume: if packaging_info.get("is_unit"): - pack_volume = product.volume + pack_volume = self.volume else: packaging = self.env["product.packaging"].browse(packaging_info["id"]) pack_volume = packaging.volume diff --git a/stock_picking_volume_packaging/tests/test_stock_move_volume.py b/stock_picking_volume_packaging/tests/test_stock_move_volume.py index 4b4e0b4b2..d6d735738 100644 --- a/stock_picking_volume_packaging/tests/test_stock_move_volume.py +++ b/stock_picking_volume_packaging/tests/test_stock_move_volume.py @@ -51,7 +51,7 @@ class TestStockMoveVolume(TransactionCase): move = self.env["stock.move"].new( {"product_id": self.product, "product_uom_qty": 16} ) - self.assertEqual(move._get_volume_for_qty(16), 2400) + self.assertEqual(move.product_id._get_volume_for_qty(16), 2400) def test_move_volume_package_with_dimension(self): """ @@ -87,4 +87,4 @@ class TestStockMoveVolume(TransactionCase): {"product_id": self.product, "product_uom_qty": 16} ) - self.assertEqual(move._get_volume_for_qty(16), 153) + self.assertEqual(move.product_id._get_volume_for_qty(16), 153)