mirror of
https://github.com/OCA/stock-logistics-warehouse.git
synced 2025-01-21 14:27:28 +02:00
@@ -1,2 +1,3 @@
|
||||
from . import product_product
|
||||
from . import stock_move
|
||||
from . import stock_picking
|
||||
|
||||
27
stock_picking_volume/models/product_product.py
Normal file
27
stock_picking_volume/models/product_product.py
Normal file
@@ -0,0 +1,27 @@
|
||||
# Copyright 2023 Michael Tietz (MT Software) <mtietz@mt-software.de>
|
||||
# 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
|
||||
@@ -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"
|
||||
|
||||
@@ -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[
|
||||
|
||||
@@ -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,50 @@ 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)
|
||||
|
||||
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
|
||||
)
|
||||
|
||||
@@ -1 +1 @@
|
||||
from . import stock_move
|
||||
from . import product_product
|
||||
|
||||
@@ -1,26 +1,26 @@
|
||||
# Copyright 2020-2022 Camptocamp SA
|
||||
# Copyright 2023 ACSONE SA/NV
|
||||
# Copyright 2023 Michael Tietz (MT Software) <mtietz@mt-software.de>
|
||||
# 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
|
||||
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user