diff --git a/addons/stock/models/stock_move.py b/addons/stock/models/stock_move.py
index 924e9e55697..8881619d854 100644
--- a/addons/stock/models/stock_move.py
+++ b/addons/stock/models/stock_move.py
@@ -564,6 +564,8 @@ class StockMove(models.Model):
# messages according to the state of the stock.move records.
receipt_moves_to_reassign = self.env['stock.move']
move_to_recompute_state = self.env['stock.move']
+ if 'product_uom' in vals and any(move.state == 'done' for move in self):
+ raise UserError(_('You cannot change the UoM for a stock move that has been set to \'Done\'.'))
if 'product_uom_qty' in vals:
move_to_unreserve = self.env['stock.move']
for move in self.filtered(lambda m: m.state not in ('done', 'draft') and m.picking_id):
diff --git a/addons/stock/models/stock_move_line.py b/addons/stock/models/stock_move_line.py
index dce5a7c7cc6..3eb991c8b31 100644
--- a/addons/stock/models/stock_move_line.py
+++ b/addons/stock/models/stock_move_line.py
@@ -306,7 +306,8 @@ class StockMoveLine(models.Model):
('lot_id', 'stock.production.lot'),
('package_id', 'stock.quant.package'),
('result_package_id', 'stock.quant.package'),
- ('owner_id', 'res.partner')
+ ('owner_id', 'res.partner'),
+ ('product_uom_id', 'uom.uom')
]
updates = {}
for key, model in triggers:
@@ -381,7 +382,7 @@ class StockMoveLine(models.Model):
mls = mls.filtered(lambda ml: not float_is_zero(ml.qty_done - vals['qty_done'], precision_rounding=ml.product_uom_id.rounding))
for ml in mls:
# undo the original move line
- qty_done_orig = ml.move_id.product_uom._compute_quantity(ml.qty_done, ml.move_id.product_id.uom_id, rounding_method='HALF-UP')
+ qty_done_orig = ml.product_uom_id._compute_quantity(ml.qty_done, ml.move_id.product_id.uom_id, rounding_method='HALF-UP')
in_date = Quant._update_available_quantity(ml.product_id, ml.location_dest_id, -qty_done_orig, lot_id=ml.lot_id,
package_id=ml.result_package_id, owner_id=ml.owner_id)[1]
Quant._update_available_quantity(ml.product_id, ml.location_id, qty_done_orig, lot_id=ml.lot_id,
@@ -396,7 +397,8 @@ class StockMoveLine(models.Model):
package_id = updates.get('package_id', ml.package_id)
result_package_id = updates.get('result_package_id', ml.result_package_id)
owner_id = updates.get('owner_id', ml.owner_id)
- quantity = ml.move_id.product_uom._compute_quantity(qty_done, ml.move_id.product_id.uom_id, rounding_method='HALF-UP')
+ product_uom_id = updates.get('product_uom_id', ml.product_uom_id)
+ quantity = product_uom_id._compute_quantity(qty_done, ml.move_id.product_id.uom_id, rounding_method='HALF-UP')
if not ml.move_id._should_bypass_reservation(location_id):
ml._free_reservation(product_id, location_id, quantity, lot_id=lot_id, package_id=package_id, owner_id=owner_id)
if not float_is_zero(quantity, precision_digits=precision):
diff --git a/addons/stock/tests/test_move.py b/addons/stock/tests/test_move.py
index fc83fb97d6a..06b44335bed 100644
--- a/addons/stock/tests/test_move.py
+++ b/addons/stock/tests/test_move.py
@@ -3997,6 +3997,39 @@ class StockMove(TransactionCase):
self.assertEqual(self.env['stock.quant']._get_available_quantity(self.product_lot, self.stock_location, lot_id=lot1, package_id=package1), 0.0)
self.assertEqual(self.env['stock.quant']._get_available_quantity(self.product_lot, self.stock_location, lot_id=lot2, package_id=package1), 1.0)
+ def test_edit_done_move_line_14(self):
+ """ Test that editing a done stock move line with a different UoM from its stock move correctly
+ updates the quant when its qty and/or its UoM is edited. Also check that we don't allow editing
+ a done stock move's UoM.
+ """
+ move1 = self.env['stock.move'].create({
+ 'name': 'test_edit_moveline',
+ 'location_id': self.supplier_location.id,
+ 'location_dest_id': self.stock_location.id,
+ 'product_id': self.product.id,
+ 'product_uom': self.uom_unit.id,
+ 'product_uom_qty': 12.0,
+ })
+ move1._action_confirm()
+ move1._action_assign()
+ move1.move_line_ids.product_uom_id = self.uom_dozen
+ move1.move_line_ids.qty_done = 1
+ move1._action_done()
+ self.assertEqual(self.env['stock.quant']._get_available_quantity(self.product, self.stock_location), 12.0)
+
+ move1.move_line_ids.qty_done = 2
+ self.assertEqual(self.env['stock.quant']._get_available_quantity(self.product, self.stock_location), 24.0)
+ self.assertEqual(move1.product_uom_qty, 24.0)
+ self.assertEqual(move1.product_qty, 24.0)
+
+ move1.move_line_ids.product_uom_id = self.uom_unit
+ self.assertEqual(self.env['stock.quant']._get_available_quantity(self.product, self.stock_location), 2.0)
+ self.assertEqual(move1.product_uom_qty, 2.0)
+ self.assertEqual(move1.product_qty, 2.0)
+
+ with self.assertRaises(UserError):
+ move1.product_uom = self.uom_dozen
+
def test_immediate_validate_1(self):
""" In a picking with a single available move, clicking on validate without filling any
quantities should open a wizard asking to process all the reservation (so, the whole move).
diff --git a/addons/stock/views/stock_move_views.xml b/addons/stock/views/stock_move_views.xml
index b4e60cd177a..e249cc1f0a7 100644
--- a/addons/stock/views/stock_move_views.xml
+++ b/addons/stock/views/stock_move_views.xml
@@ -75,7 +75,7 @@
-
+
@@ -237,7 +237,10 @@
-
+
@@ -266,7 +269,7 @@
-
+
diff --git a/addons/stock/views/stock_picking_views.xml b/addons/stock/views/stock_picking_views.xml
index 7f258aeb545..89a8df3a371 100644
--- a/addons/stock/views/stock_picking_views.xml
+++ b/addons/stock/views/stock_picking_views.xml
@@ -387,7 +387,7 @@
-
+
-
+