[FIX] mrp: reservation rounding for under-consumption backorders
Steps to reproduce: 1. Create a BoM with a component of 1 quantity 2. Create a manufacturing order with this BoM 3. Set the quantity to produce to 5 and confirm 4. Set the producing quantity to 3.1 5. Set the quantity done for the raw material to 3.09 6. Mark the MO as done and create a backorder 7. Click 'Unreserve' on the backorder 8. 'It is not possible to unreserve more products of ...' Before this commit: `_compute_quantity` is called with the default rounding method, rounding quantity UP, which introduces reservation inconsistencies. After this commit: `_compute_quantity` rounds with the HALF-UP method, preventing rounding errors. Also, use the correct UoM in float comparisons. opw-3601737 closes odoo/odoo#144620 X-original-commit: f6c15e131e253c3780ce6daa53b0cf27f60fa464 Signed-off-by: Quentin Wolfs (quwo) <quwo@odoo.com> Signed-off-by: Walravens Mathieu (wama) <wama@odoo.com>
This commit is contained in:
@@ -1836,19 +1836,19 @@ class MrpProduction(models.Model):
|
||||
product_uom = initial_move.product_id.uom_id
|
||||
if not initial_move.picked:
|
||||
for move_line in initial_move.move_line_ids:
|
||||
available_qty = move_line.product_uom_id._compute_quantity(move_line.quantity, product_uom)
|
||||
if float_compare(available_qty, 0, precision_rounding=move_line.product_uom_id.rounding) <= 0:
|
||||
available_qty = move_line.product_uom_id._compute_quantity(move_line.quantity, product_uom, rounding_method="HALF-UP")
|
||||
if float_compare(available_qty, 0, precision_rounding=product_uom.rounding) <= 0:
|
||||
continue
|
||||
ml_by_move.append((available_qty, move_line, move_line.copy_data()[0]))
|
||||
|
||||
moves = list(initial_move | backorder_moves)
|
||||
|
||||
move = moves and moves.pop(0)
|
||||
move_qty_to_reserve = move.product_qty
|
||||
move_qty_to_reserve = move.product_qty # Product UoM
|
||||
|
||||
for index, (quantity, move_line, ml_vals) in enumerate(ml_by_move):
|
||||
taken_qty = min(quantity, move_qty_to_reserve)
|
||||
taken_qty_uom = product_uom._compute_quantity(taken_qty, move_line.product_uom_id)
|
||||
taken_qty_uom = product_uom._compute_quantity(taken_qty, move_line.product_uom_id, rounding_method="HALF-UP")
|
||||
if float_is_zero(taken_qty_uom, precision_rounding=move_line.product_uom_id.rounding):
|
||||
continue
|
||||
move_line.write({
|
||||
@@ -1867,7 +1867,7 @@ class MrpProduction(models.Model):
|
||||
while float_compare(quantity, 0, precision_rounding=product_uom.rounding) > 0 and move:
|
||||
# Do not create `stock.move.line` if there is no initial demand on `stock.move`
|
||||
taken_qty = min(move_qty_to_reserve, quantity)
|
||||
taken_qty_uom = product_uom._compute_quantity(taken_qty, move_line.product_uom_id)
|
||||
taken_qty_uom = product_uom._compute_quantity(taken_qty, move_line.product_uom_id, rounding_method="HALF-UP")
|
||||
if move == initial_move:
|
||||
move_line.quantity += taken_qty_uom
|
||||
elif not float_is_zero(taken_qty_uom, precision_rounding=move_line.product_uom_id.rounding):
|
||||
|
||||
@@ -322,6 +322,35 @@ class TestMrpProductionBackorder(TestMrpCommon):
|
||||
# 300 Grams consumed and 700 reserved
|
||||
self.assertAlmostEqual(self.env['stock.quant']._gather(product_component, self.stock_location).reserved_quantity, 0.7)
|
||||
|
||||
def test_rounding_backorder(self):
|
||||
"""test backorder component rounding doesn't introduce reservation issues"""
|
||||
production, _, _, p1, p2 = self.generate_mo(qty_final=5, qty_base_1=1, qty_base_2=1)
|
||||
|
||||
self.env['stock.quant']._update_available_quantity(p1, self.stock_location, 100)
|
||||
self.env['stock.quant']._update_available_quantity(p2, self.stock_location, 100)
|
||||
|
||||
production.action_assign()
|
||||
|
||||
production_form = Form(production)
|
||||
production_form.qty_producing = 3.1
|
||||
production = production_form.save()
|
||||
|
||||
details_operation_form = Form(production.move_raw_ids.filtered(lambda m: m.product_id == p1), view=self.env.ref('stock.view_stock_move_operations'))
|
||||
with details_operation_form.move_line_ids.edit(0) as ml:
|
||||
ml.quantity = 3.09
|
||||
|
||||
details_operation_form.save()
|
||||
|
||||
action = production.button_mark_done()
|
||||
backorder_form = Form(self.env['mrp.production.backorder'].with_context(**action['context']))
|
||||
backorder_form.save().action_backorder()
|
||||
backorder = production.procurement_group_id.mrp_production_ids[-1]
|
||||
# 3.09 consumed and 1.9 reserved
|
||||
self.assertAlmostEqual(self.env['stock.quant']._gather(p1, self.stock_location).reserved_quantity, 1.9)
|
||||
self.assertAlmostEqual(backorder.move_raw_ids.filtered(lambda m: m.product_id == p1).move_line_ids.quantity, 1.9)
|
||||
|
||||
# Make sure we don't have an unreserve errors
|
||||
backorder.do_unreserve()
|
||||
|
||||
def test_tracking_backorder_series_serial_1(self):
|
||||
""" Create a MO of 4 tracked products (serial) with pbm_sam.
|
||||
|
||||
Reference in New Issue
Block a user