Permalink
Browse files

[FIX] stock: unreserve drop all the processed work

Usecase to reproduce(or any other way to lower initial demand on move):
- Build a MO with 5 products to do
- Produce 3
- Update the quantity on the mo to 3
-> All the consumed quantity on move lines is dropped.

It happens because lowering the initial demand on a move
will call unlink on its move lines. (increase it will
update the move's state and reassign if it comes from a supplier)

Instead of dropping and reassign the move in order to change
the reservation, it is possible to directly write the new
reserved quantity on the move lines and that will update the
reservation on qaunt directly. Also we don't want move lines
without reserved quantity and quantity done thus we drop them.

closes #29941
  • Loading branch information...
amoyaux committed Jan 4, 2019
1 parent 617652b commit df7953f3031a2d3b0f2abf30a41df4955fb6b553
@@ -439,7 +439,10 @@ def _update_raw_move(self, bom_line, line_data):
move = self.move_raw_ids.filtered(lambda x: x.bom_line_id.id == bom_line.id and x.state not in ('done', 'cancel'))
if move:
if quantity > 0:
move[0].write({'product_uom_qty': quantity})
move[0]._decrease_reserved_quanity(quantity)
move[0].with_context(do_not_unreserve=True).write({'product_uom_qty': quantity})
move[0]._recompute_state()
move[0]._action_assign()
move.unit_factor = quantity / move.raw_material_production_id.product_qty
elif quantity < 0: # Do not remove 0 lines
if move[0].quantity_done > 0:
@@ -182,6 +182,24 @@ def action_explode(self):
self.sudo().unlink()
return processed_moves

def _decrease_reserved_quanity(self, quantity):
""" Decrease the reservation on move lines but keeps the
all other data.
"""
move_line_to_unlink = self.env['stock.move.line']
for move in self:
reserved_quantity = quantity
for move_line in self.move_line_ids:
if move_line.product_uom_qty > reserved_quantity:
move_line.product_uom_qty = reserved_quantity
else:
move_line.product_uom_qty = 0
reserved_quantity -= move_line.product_uom_qty
if not move_line.product_uom_qty and not move_line.qty_done:
move_line_to_unlink |= move_line
move_line_to_unlink.unlink()
return True

def _prepare_phantom_move_values(self, bom_line, quantity):
return {
'picking_id': self.picking_id.id if self.picking_id else False,
@@ -617,3 +617,49 @@ def test_product_produce_4(self):

mo.button_mark_done()
self.assertEqual(mo.state, 'done', "Production order should be in done state.")

def test_product_produce_5(self):
""" Build 5 final products with different consumed lots,
then edit the finished quantity and update the Manufacturing
order quantity. Then check if the produced quantity do not
change and it is possible to close the MO.
"""
self.stock_location = self.env.ref('stock.stock_location_stock')
mo, bom, p_final, p1, p2 = self.generate_mo(tracking_base_1='lot')
self.assertEqual(len(mo), 1, 'MO should have been created')

lot_1 = self.env['stock.production.lot'].create({
'name': 'lot1',
'product_id': p1.id,
})
lot_2 = self.env['stock.production.lot'].create({
'name': 'lot2',
'product_id': p1.id,
})

self.env['stock.quant']._update_available_quantity(p1, self.stock_location, 10, lot_id=lot_1)
self.env['stock.quant']._update_available_quantity(p1, self.stock_location, 10, lot_id=lot_2)

self.env['stock.quant']._update_available_quantity(p2, self.stock_location, 5)
mo.action_assign()

produce_wizard = self.env['mrp.product.produce'].with_context({
'active_id': mo.id,
'active_ids': [mo.id],
}).create({
'product_qty': 5.0,
})

for produce_line in produce_wizard.produce_line_ids:
produce_line.qty_done = produce_line.qty_to_consume
produce_wizard.do_produce()

mo.move_finished_ids.move_line_ids.qty_done -= 1
update_quantity_wizard = self.env['change.production.qty'].create({
'mo_id': mo.id,
'product_qty': 4,
})
update_quantity_wizard.change_prod_qty()

self.assertEqual(mo.move_raw_ids.filtered(lambda m: m.product_id == p1).quantity_done, 20, 'Update the produce quantity should not impact already produced quantity.')
mo.button_mark_done()
@@ -50,7 +50,6 @@ def change_prod_qty(self):
qty_produced = production.product_id.uom_id._compute_quantity(sum(done_moves.mapped('product_qty')), production.product_uom_id)
factor = production.product_uom_id._compute_quantity(production.product_qty - qty_produced, production.bom_id.product_uom_id) / production.bom_id.product_qty
boms, lines = production.bom_id.explode(production.product_id, factor, picking_type=production.bom_id.picking_type_id)
done_quantities = {move: move.quantity_done for move in production.move_raw_ids}
for line, line_data in lines:
production._update_raw_move(line, line_data)
operation_bom_qty = {}
@@ -60,9 +59,6 @@ def change_prod_qty(self):
self._update_product_to_produce(production, production.product_qty - qty_produced)
moves = production.move_raw_ids.filtered(lambda x: x.state not in ('done', 'cancel'))
moves._action_assign()
for move in production.move_raw_ids:
if move.quantity_done != done_quantities[move]:
move._set_quantity_done(done_quantities[move])
for wo in production.workorder_ids:
operation = wo.operation_id
if operation_bom_qty.get(operation.id):

0 comments on commit df7953f

Please sign in to comment.