diff --git a/stock_secondary_unit/models/stock_move.py b/stock_secondary_unit/models/stock_move.py
index 49821a399..22c1580e1 100644
--- a/stock_secondary_unit/models/stock_move.py
+++ b/stock_secondary_unit/models/stock_move.py
@@ -4,31 +4,45 @@ from odoo import api, fields, models
from odoo.tools.float_utils import float_round
-class StockSecondaryUnitMixin(models.AbstractModel):
- _name = "stock.secondary.unit.mixin"
- _description = "Stock Secondary Unit Mixin"
-
- secondary_uom_id = fields.Many2one(
- comodel_name="product.secondary.unit", string="Second unit"
- )
- secondary_uom_qty = fields.Float(
- string="Secondary Qty", digits="Product Unit of Measure"
- )
-
-
class StockMove(models.Model):
- _inherit = ["stock.move", "stock.secondary.unit.mixin"]
+ _inherit = ["stock.move", "product.secondary.unit.mixin"]
_name = "stock.move"
+ _secondary_unit_fields = {
+ "qty_field": "product_uom_qty",
+ "uom_field": "product_uom",
+ }
+
+ product_uom_qty = fields.Float(
+ store=True, readonly=False, compute="_compute_product_uom_qty", copy=True
+ )
+
+ @api.depends("secondary_uom_qty", "secondary_uom_id")
+ def _compute_product_uom_qty(self):
+ self._compute_helper_target_field_qty()
+
+ @api.onchange("product_uom")
+ def onchange_product_uom_for_secondary(self):
+ self._onchange_helper_product_uom_for_secondary()
def _merge_moves_fields(self):
res = super()._merge_moves_fields()
- res["secondary_uom_qty"] = self[-1:].secondary_uom_qty
+ res["secondary_uom_qty"] = sum(self.mapped("secondary_uom_qty"))
return res
+ @api.model
+ def _prepare_merge_moves_distinct_fields(self):
+ """Don't merge moves with distinct secondary units"""
+ distinct_fields = super()._prepare_merge_moves_distinct_fields()
+ distinct_fields += ["secondary_uom_id"]
+ return distinct_fields
+
class StockMoveLine(models.Model):
- _inherit = ["stock.move.line", "stock.secondary.unit.mixin"]
+ _inherit = ["stock.move.line", "product.secondary.unit.mixin"]
_name = "stock.move.line"
+ _secondary_unit_fields = {"qty_field": "qty_done", "uom_field": "product_uom_id"}
+
+ qty_done = fields.Float(store=True, readonly=False, compute="_compute_qty_done")
@api.model
def create(self, vals):
@@ -45,3 +59,7 @@ class StockMoveLine(models.Model):
{"secondary_uom_qty": qty, "secondary_uom_id": move.secondary_uom_id.id}
)
return super().create(vals)
+
+ @api.depends("secondary_uom_id", "secondary_uom_qty")
+ def _compute_qty_done(self):
+ self._compute_helper_target_field_qty()
diff --git a/stock_secondary_unit/tests/test_stock_secondary_unit.py b/stock_secondary_unit/tests/test_stock_secondary_unit.py
index 5059a43b3..404c2bbbc 100644
--- a/stock_secondary_unit/tests/test_stock_secondary_unit.py
+++ b/stock_secondary_unit/tests/test_stock_secondary_unit.py
@@ -1,17 +1,24 @@
# Copyright 2018 Tecnativa - Sergio Teruel
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
-from odoo.tests import SavepointCase
+from odoo.tests import Form, SavepointCase, tagged
+@tagged("-at_install", "post_install")
class TestProductSecondaryUnit(SavepointCase):
- at_install = False
- post_install = True
-
@classmethod
def setUpClass(cls):
super().setUpClass()
+ # Active multiple units of measure security group for user
+ cls.env.user.groups_id = [(4, cls.env.ref("uom.group_uom").id)]
+ cls.StockPicking = cls.env["stock.picking"]
cls.warehouse = cls.env.ref("stock.warehouse0")
+ cls.location_supplier = cls.env.ref("stock.stock_location_suppliers")
+ cls.location_stock = cls.env.ref("stock.stock_location_stock")
+ cls.picking_type_in = cls.env.ref("stock.picking_type_in")
+ cls.picking_type_out = cls.env.ref("stock.picking_type_out")
+
cls.product_uom_kg = cls.env.ref("uom.product_uom_kgm")
+ cls.product_uom_ton = cls.env.ref("uom.product_uom_ton")
cls.product_uom_unit = cls.env.ref("uom.product_uom_unit")
ProductAttribute = cls.env["product.attribute"]
ProductAttributeValue = cls.env["product.attribute.value"]
@@ -34,7 +41,7 @@ class TestProductSecondaryUnit(SavepointCase):
0,
{
"code": "A",
- "name": "unit-700",
+ "name": "unit-500",
"uom_id": cls.product_uom_unit.id,
"factor": 0.5,
},
@@ -49,6 +56,16 @@ class TestProductSecondaryUnit(SavepointCase):
"factor": 0.9,
},
),
+ (
+ 0,
+ 0,
+ {
+ "code": "C",
+ "name": "box 10",
+ "uom_id": cls.product_uom_unit.id,
+ "factor": 10,
+ },
+ ),
],
"attribute_line_ids": [
(
@@ -97,22 +114,19 @@ class TestProductSecondaryUnit(SavepointCase):
def test_03_stock_picking_secondary_unit(self):
StockPicking = self.env["stock.picking"]
product1 = self.product_template.product_variant_ids[0]
- location = self.env.ref("stock.stock_location_suppliers")
- location_dest = self.env.ref("stock.stock_location_stock")
- picking_type = self.env.ref("stock.picking_type_in")
move_vals = {
"product_id": product1.id,
"name": product1.display_name,
"secondary_uom_id": product1.secondary_uom_ids[0].id,
"product_uom": product1.uom_id.id,
"product_uom_qty": 10.0,
- "location_id": location.id,
- "location_dest_id": location_dest.id,
+ "location_id": self.location_supplier.id,
+ "location_dest_id": self.location_stock.id,
}
do_vals = {
- "location_id": location.id,
- "location_dest_id": location_dest.id,
- "picking_type_id": picking_type.id,
+ "location_id": self.location_supplier.id,
+ "location_dest_id": self.location_stock.id,
+ "picking_type_id": self.picking_type_in.id,
"move_ids_without_package": [
(0, None, move_vals),
(0, None, move_vals),
@@ -130,3 +144,83 @@ class TestProductSecondaryUnit(SavepointCase):
)
self.assertEquals(uom_qty, 20.0)
self.assertEquals(secondary_uom_qty, 40.0)
+
+ def test_picking_secondary_unit(self):
+ product = self.product_template.product_variant_ids[0]
+ with Form(
+ self.StockPicking.with_context(
+ planned_picking=True, default_picking_type_id=self.picking_type_out.id,
+ )
+ ) as picking_form:
+ with picking_form.move_ids_without_package.new() as move:
+ move.product_id = product
+ move.secondary_uom_qty = 1
+ move.secondary_uom_id = product.secondary_uom_ids[0]
+ self.assertEqual(move.product_uom_qty, 0.5)
+ move.secondary_uom_qty = 2
+ self.assertEqual(move.product_uom_qty, 1)
+ move.secondary_uom_id = product.secondary_uom_ids[1]
+ self.assertEqual(move.product_uom_qty, 1.8)
+ move.product_uom_qty = 5
+ self.assertAlmostEqual(move.secondary_uom_qty, 5.56, 2)
+ # Change uom from stock move line
+ move.secondary_uom_qty = 1
+ move.secondary_uom_id = product.secondary_uom_ids[2]
+ self.assertEqual(move.product_uom_qty, 10)
+ move.product_uom = self.product_uom_ton
+ self.assertAlmostEqual(move.secondary_uom_qty, 1000, 2)
+
+ picking = picking_form.save()
+ picking.action_confirm()
+ with Form(picking) as picking_form:
+ # Test detail operations
+ with picking_form.move_line_ids_without_package.new() as move:
+ move.product_id = product
+ move.secondary_uom_qty = 1
+ move.secondary_uom_id = product.secondary_uom_ids[0]
+ self.assertEqual(move.qty_done, 0.5)
+ move.secondary_uom_qty = 2
+ self.assertEqual(move.qty_done, 1)
+ move.secondary_uom_id = product.secondary_uom_ids[1]
+ self.assertEqual(move.qty_done, 1.8)
+ move.qty_done = 5
+ self.assertAlmostEqual(move.secondary_uom_qty, 5.56, 2)
+
+ def test_secondary_unit_merge_move_diff_uom(self):
+ product = self.product_template.product_variant_ids[0]
+ with Form(
+ self.StockPicking.with_context(
+ planned_picking=True, default_picking_type_id=self.picking_type_out.id,
+ )
+ ) as picking_form:
+ with picking_form.move_ids_without_package.new() as move:
+ move.product_id = product
+ move.secondary_uom_qty = 1
+ move.secondary_uom_id = product.secondary_uom_ids[0]
+ with picking_form.move_ids_without_package.new() as move:
+ move.product_id = product
+ move.secondary_uom_qty = 1
+ move.secondary_uom_id = product.secondary_uom_ids[1]
+ picking = picking_form.save()
+ picking.action_confirm()
+ self.assertEquals(len(picking.move_lines), 2)
+
+ def test_secondary_unit_merge_move_same_uom(self):
+ product = self.product_template.product_variant_ids[0]
+ with Form(
+ self.StockPicking.with_context(
+ planned_picking=True, default_picking_type_id=self.picking_type_out.id,
+ )
+ ) as picking_form:
+ with picking_form.move_ids_without_package.new() as move:
+ move.product_id = product
+ move.secondary_uom_qty = 1
+ move.secondary_uom_id = product.secondary_uom_ids[0]
+ with picking_form.move_ids_without_package.new() as move:
+ move.product_id = product
+ move.secondary_uom_qty = 1
+ move.secondary_uom_id = product.secondary_uom_ids[0]
+ picking = picking_form.save()
+ picking.action_confirm()
+ self.assertEquals(len(picking.move_lines), 1)
+ self.assertEquals(picking.move_lines.secondary_uom_qty, 2)
diff --git a/stock_secondary_unit/views/stock_picking_views.xml b/stock_secondary_unit/views/stock_picking_views.xml
index 0b913b24b..a712c5775 100644
--- a/stock_secondary_unit/views/stock_picking_views.xml
+++ b/stock_secondary_unit/views/stock_picking_views.xml
@@ -12,11 +12,15 @@
expr="//field[@name='move_ids_without_package']/tree/field[@name='product_uom_qty']"
position="before"
>
-
+