[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:
Walid
2023-07-05 17:34:13 +02:00
parent 1a71f8cff7
commit a1deba1186
2 changed files with 66 additions and 2 deletions
+17 -2
View File
@@ -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,
+49
View File
@@ -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