Skip to content

Commit

Permalink
[FIX] stock: update uom ratio in stock.quant.reserved_quantity
Browse files Browse the repository at this point in the history
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#160656

Signed-off-by: Quentin Wolfs (quwo) <quwo@odoo.com>
  • Loading branch information
stpa-odoo authored and DjamelTouati committed Apr 15, 2024
1 parent e732bc2 commit 7e4d5c0
Show file tree
Hide file tree
Showing 2 changed files with 34 additions and 3 deletions.
8 changes: 5 additions & 3 deletions addons/stock/models/stock_move_line.py
Original file line number Diff line number Diff line change
Expand Up @@ -372,9 +372,11 @@ def write(self, vals):
if updates or 'product_uom_qty' in vals:
for ml in self.filtered(lambda ml: ml.state in ['partially_available', 'assigned'] and ml.product_id.type == 'product'):

if 'product_uom_qty' in vals:
new_product_uom_qty = ml.product_uom_id._compute_quantity(
vals['product_uom_qty'], ml.product_id.uom_id, rounding_method='HALF-UP')
if 'product_uom_qty' in vals or 'product_uom_id' in vals:
new_ml_uom = updates.get('product_uom_id', ml.product_uom_id)
new_product_uom_qty = new_ml_uom._compute_quantity(
vals.get('product_uom_qty', ml.product_uom_qty), ml.product_id.uom_id, rounding_method='HALF-UP')

# Make sure `product_uom_qty` is not negative.
if float_compare(new_product_uom_qty, 0, precision_rounding=ml.product_id.uom_id.rounding) < 0:
raise UserError(_('Reserving a negative quantity is not allowed.'))
Expand Down
29 changes: 29 additions & 0 deletions addons/stock/tests/test_move.py
Original file line number Diff line number Diff line change
Expand Up @@ -6124,3 +6124,32 @@ def test_internal_transfer_with_tracked_product(self):
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({'product_uom_qty': 2, 'product_uom_id': self.uom_dozen.id})
self.assertEqual(quant.reserved_quantity, 24)
self.assertEqual(ml.product_uom_qty * 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.product_uom_qty * self.uom_unit.ratio, 2)

0 comments on commit 7e4d5c0

Please sign in to comment.