[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:
@@ -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.
|
||||
|
||||
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user