[FIX] stock: update uom ratio in stock.quant.reserved_quantity

Steps to reproduce the issue:

1. Create a Storable Product and give it a UOM
2. Create an On Hand stock in a certain location
3. Go to Barcode and create a new Internal Transfer
4. Add the Product and with any quantity and click on Confirm (not Validate)
5. Edit the line of the Product (pencil icon), change the quantity and the UOM and click on Confirm
6. Validate the Transfer
7. Go back to the Product and click on the "On Hand" or the "Update Quantity" button
8. The reserved quantity is not null

Explanation:

When you change `stock.move.line.product_uom_id`, `stock.quant.reserved_quantity` (using `product.product.uom_id`) is not changed to reflect the new `uom.uom.factor`. You then have two routes:

- In `stock_barcode`, `stock.move.line.product_uom_id` changes first, `stock.move.line.quantity` change is triggered through `stock.move.line._inverse_qty_done` afterwards.
https://github.com/odoo/enterprise/blob/d04b69ba03877a9b4aae82fb061dca23b1bfc4bc/stock_barcode/models/stock_move_line.py#L58-L61
When calling `stock.move.line._synchronize_quant`, `stock.move.line.quantity_product_uom` will use the new `stock.move.line.product_uom_id` while `stock.quant.reserved_quantity` still reflects the old `uom.uom.factor`.
https://github.com/odoo/odoo/blob/1b0dbb3645ad8b52c5260f1cbbc4f6bdee48461e/addons/stock/models/stock_move_line.py#L421-L422
(e.g.: going from `1 Dozens` to `2 Units` would give you `1.09 Dozens` in `stock.quant.reserved_quantity` instead of `0.17`)

- There is a similar issue in _Inventory > Transfers > Internal_, where `stock.move.line.product_uom_id` changes at the same time instead. In that case, the whole operation will be done using the previous `stock.move.line.product_uom_id`, and changing `stock.move.line.product_uom_id` before changing `stock.move.line.quantity` would cause the same issue as in `stock_barcode`.
(e.g.: going from `1 Dozens` to `2 Units` would give you `2 Dozens` in `stock.quant.reserved_quantity` instead of `0.17`)

Suggested fix:

There is a first check to make sure one of the values has changed, then each one will be assigned through a condition:

- The first one will be `product_uom_id`, with which `uom.uom._compute_quantity` will be called.
- The second condition will be `quantity`, which will be set in a `vals.get` in the `qty` parameter of the compute.

opw-3798046

closes odoo/odoo#162380

X-original-commit: 2b8e073d082620ca3ac2342fa12c7f93a07335d1
Signed-off-by: Quentin Wolfs (quwo) <quwo@odoo.com>
Signed-off-by: Stroobant Paul (stpa) <stpa@odoo.com>
This commit is contained in:
Paul Stroobant
2024-04-19 11:09:26 +00:00
parent adc7fe15e7
commit a17d30e5db
2 changed files with 34 additions and 4 deletions
+5 -4
View File
@@ -408,9 +408,10 @@ class StockMoveLine(models.Model):
for ml in self:
if ml.product_id.type != 'product' or ml.state == 'done':
continue
if 'quantity' in vals:
new_reserved_qty = ml.product_uom_id._compute_quantity(
vals['quantity'], ml.product_id.uom_id, rounding_method='HALF-UP')
if 'quantity' in vals or 'product_uom_id' in vals:
new_ml_uom = updates.get('product_uom_id', ml.product_uom_id)
new_reserved_qty = new_ml_uom._compute_quantity(
vals.get('quantity', ml.quantity), ml.product_id.uom_id, rounding_method='HALF-UP')
# Make sure `reserved_uom_qty` is not negative.
if float_compare(new_reserved_qty, 0, precision_rounding=ml.product_id.uom_id.rounding) < 0:
raise UserError(_('Reserving a negative quantity is not allowed.'))
@@ -428,7 +429,7 @@ class StockMoveLine(models.Model):
lot=updates.get('lot_id', ml.lot_id), package=updates.get('package_id', ml.package_id),
owner=updates.get('owner_id', ml.owner_id))
if 'quantity' in vals and vals['quantity'] != ml.quantity:
if ('quantity' in vals and vals['quantity'] != ml.quantity) or 'product_uom_id' in vals:
moves_to_recompute_state |= ml.move_id
# When editing a done move line, the reserved availability of a potential chained move is impacted. Take care of running again `_action_assign` on the concerned moves.
+29
View File
@@ -6312,3 +6312,32 @@ class StockMove(TransactionCase):
line_form.lot_ids.add(sn01)
picking = picking_form.save()
self.assertEqual(picking.move_ids_without_package.lot_ids, sn01)
def test_change_move_line_uom(self):
"""Check the reserved_quantity of the quant is correctly updated when changing the UOM in the move line"""
Quant = self.env['stock.quant']
Quant._update_available_quantity(self.product, self.stock_location, 100)
quant = Quant._gather(self.product, self.stock_location)
move = self.env['stock.move'].create({
'name': 'Test move',
'product_id': self.product.id,
'product_uom_qty': 1,
'product_uom': self.product.uom_id.id,
'location_id': self.stock_location.id,
'location_dest_id': self.customer_location.id,
})
move._action_confirm()
move._action_assign()
ml = move.move_line_ids
# The product's uom is in units, which means we currently have 1 reserved unit
self.assertEqual(quant.reserved_quantity, 1)
# Firstly, we test changing the quantity and the uom together: 2 dozens = 24 reserved units
ml.write({'quantity': 2, 'product_uom_id': self.uom_dozen.id})
self.assertEqual(quant.reserved_quantity, 24)
self.assertEqual(ml.quantity * self.uom_dozen.ratio, 24)
# Secondly, we test changing only the uom: 2 units -> expected 2 units
ml.write({'product_uom_id': self.uom_unit.id})
self.assertEqual(quant.reserved_quantity, 2)
self.assertEqual(ml.quantity * self.uom_unit.ratio, 2)