[FIX] mrp: keep reserved tracked products on Backorders
Steps to reproduce: - Create a subcontracted product with components tracked by lots - make a PO for 35 the product and send components from multiple lots - login as subcontractor on the portal - record uncomplete production and change the default consumed lots Bug: split production ignores the done quantities on the initial move on the Backorders components are considered to be consumed in the default order Fix: Consume components on the initial move as indicated by the user reserve the remaining components for the Backorders as usual opw-3186773 closes odoo/odoo#127467 X-original-commit: c39256ba6df257ee79fb66eab61617f5fec166b9 Signed-off-by: William Henrotin (whe) <whe@odoo.com> Signed-off-by: Walid Hanniche (waha) <waha@odoo.com>
This commit is contained in:
@@ -1789,15 +1789,30 @@ class MrpProduction(models.Model):
|
||||
|
||||
move = moves and moves.pop(0)
|
||||
move_qty_to_reserve = move.product_qty
|
||||
|
||||
for index, (quantity, move_line, ml_vals) in enumerate(ml_by_move):
|
||||
taken_qty = min(quantity, move_qty_to_reserve)
|
||||
taken_qty_uom = min(product_uom._compute_quantity(taken_qty, move_line.product_uom_id), move_line.qty_done)
|
||||
if float_is_zero(taken_qty_uom, precision_rounding=move_line.product_uom_id.rounding):
|
||||
continue
|
||||
move_line.with_context(bypass_reservation_update=True).reserved_uom_qty = taken_qty_uom
|
||||
move_qty_to_reserve -= taken_qty_uom
|
||||
ml_by_move[index] = (quantity - taken_qty_uom, move_line, ml_vals)
|
||||
|
||||
if float_compare(move_qty_to_reserve, 0, precision_rounding=move.product_uom.rounding) <= 0:
|
||||
assigned_moves.add(move.id)
|
||||
move = moves and moves.pop(0)
|
||||
move_qty_to_reserve = move and move.product_qty or 0
|
||||
|
||||
for quantity, move_line, ml_vals in ml_by_move:
|
||||
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)
|
||||
if move == initial_move:
|
||||
move_line.with_context(bypass_reservation_update=True).reserved_uom_qty = taken_qty_uom
|
||||
move_line.with_context(bypass_reservation_update=True).reserved_uom_qty += taken_qty_uom
|
||||
if set_consumed_qty:
|
||||
move_line.qty_done = taken_qty_uom
|
||||
move_line.qty_done += taken_qty_uom
|
||||
elif not float_is_zero(taken_qty_uom, precision_rounding=move_line.product_uom_id.rounding):
|
||||
new_ml_vals = dict(
|
||||
ml_vals,
|
||||
|
||||
@@ -225,6 +225,55 @@ class TestMrpProductionBackorder(TestMrpCommon):
|
||||
self.assertEqual(self.env['stock.quant']._get_available_quantity(p_final, self.stock_location, lot_id=lot_final), nb_product_todo, f'You should have the {nb_product_todo} final product in stock')
|
||||
self.assertEqual(len(production.procurement_group_id.mrp_production_ids), nb_product_todo)
|
||||
|
||||
def test_tracking_backorder_series_lot_2(self):
|
||||
"""
|
||||
Create a MO with component tracked by lots. Produce a part of the demand
|
||||
by using some specific lots (not the ones suggested by the onchange).
|
||||
The components' reservation of the backorder should consider which lots
|
||||
have been consumed in the initial MO
|
||||
"""
|
||||
production, _, _, p1, p2 = self.generate_mo(tracking_base_2='lot')
|
||||
lot1, lot2 = self.env['stock.lot'].create([{
|
||||
'name': f'lot_consumed_{i}',
|
||||
'product_id': p2.id,
|
||||
'company_id': self.env.company.id,
|
||||
} for i in range(2)])
|
||||
self.env['stock.quant']._update_available_quantity(p1, self.stock_location, 20)
|
||||
self.env['stock.quant']._update_available_quantity(p2, self.stock_location, 3, lot_id=lot1)
|
||||
self.env['stock.quant']._update_available_quantity(p2, self.stock_location, 2, lot_id=lot2)
|
||||
|
||||
production.action_assign()
|
||||
|
||||
production_form = Form(production)
|
||||
production_form.qty_producing = 3
|
||||
|
||||
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.qty_done = 4 * 3
|
||||
details_operation_form.save()
|
||||
|
||||
# Consume 1 Product from lot1 and 2 from lot 2
|
||||
p2_smls = production.move_raw_ids.filtered(lambda m: m.product_id == p2).move_line_ids
|
||||
self.assertEqual(len(p2_smls), 2, 'One for each lot')
|
||||
details_operation_form = Form(production.move_raw_ids.filtered(lambda m: m.product_id == p2), view=self.env.ref('stock.view_stock_move_operations'))
|
||||
with details_operation_form.move_line_ids.edit(0) as ml:
|
||||
ml.qty_done = 1
|
||||
ml.lot_id = lot1
|
||||
with details_operation_form.move_line_ids.edit(1) as ml:
|
||||
ml.qty_done = 2
|
||||
ml.lot_id = lot2
|
||||
details_operation_form.save()
|
||||
|
||||
production = production_form.save()
|
||||
action = production.button_mark_done()
|
||||
backorder = Form(self.env['mrp.production.backorder'].with_context(**action['context']))
|
||||
backorder.save().action_backorder()
|
||||
|
||||
p2_bo_mls = production.procurement_group_id.mrp_production_ids[-1].move_raw_ids.filtered(lambda m: m.product_id == p2).move_line_ids
|
||||
self.assertEqual(len(p2_bo_mls), 1)
|
||||
self.assertEqual(p2_bo_mls.lot_id, lot1)
|
||||
self.assertEqual(p2_bo_mls.reserved_qty, 2)
|
||||
|
||||
def test_tracking_backorder_series_serial_1(self):
|
||||
""" Create a MO of 4 tracked products (serial) with pbm_sam.
|
||||
all component is tracked by serial
|
||||
|
||||
Reference in New Issue
Block a user