[FIX] stock: cancel picking with scrapped SM

Cancelling a picking with scrapped and done SM will cancel this SM too.

To reproduce the issue:
1. In Settings, enable "Storage Locations"
2. Create a storable product P
3. Update the quantity of P: 10 in WH/Stock
4. Create a planned delivery order DO with 2 x P
5. Mark DO as todo, Check availability
6. Scrap 2 x P
7. Cancel DO
8. Open the SM of the scrapping

Error: The SM is cancelled. This is incorrect: the move was done, so
there is a quant with 2 x P in scrap location. There is now an
incoherence between quants and SMs report.

OPW-2805604

closes odoo/odoo#89987

X-original-commit: bd3324acc3ec227438d2cb25a1df1e0fca2a2789
Signed-off-by: Arnold Moyaux <arm@odoo.com>
Signed-off-by: Adrien Widart <awt@odoo.com>
This commit is contained in:
Adrien Widart
2022-04-28 14:59:58 +02:00
parent 044e253ac2
commit 0a98036387
3 changed files with 43 additions and 3 deletions
+2 -2
View File
@@ -1565,7 +1565,7 @@ class StockMove(models.Model):
def _action_cancel(self):
if any(move.state == 'done' and not move.scrapped for move in self):
raise UserError(_('You cannot cancel a stock move that has been set to \'Done\'. Create a return in order to reverse the moves which took place.'))
moves_to_cancel = self.filtered(lambda m: m.state != 'cancel')
moves_to_cancel = self.filtered(lambda m: m.state != 'cancel' and not (m.state == 'done' and m.scrapped))
# self cannot contain moves that are either cancelled or done, therefore we can safely
# unlink all associated move_line_ids
moves_to_cancel._do_unreserve()
@@ -1580,7 +1580,7 @@ class StockMove(models.Model):
if all(state in ('done', 'cancel') for state in siblings_states):
move.move_dest_ids.write({'procure_method': 'make_to_stock'})
move.move_dest_ids.write({'move_orig_ids': [(3, move.id, 0)]})
self.write({
moves_to_cancel.write({
'state': 'cancel',
'move_orig_ids': [(5, 0, 0)],
'procure_method': 'make_to_stock',
+1 -1
View File
@@ -523,7 +523,7 @@ class Picking(models.Model):
'''
picking_moves_state_map = defaultdict(dict)
picking_move_lines = defaultdict(set)
for move in self.env['stock.move'].search([('picking_id', 'in', self.ids)]):
for move in self.env['stock.move'].search([('picking_id', 'in', self.ids), ('scrapped', '=', False)]):
picking_id = move.picking_id
move_state = move.state
picking_moves_state_map[picking_id.id].update({
+40
View File
@@ -2180,3 +2180,43 @@ class TestStockFlow(TestStockCommon):
picking.write({'partner_id': partner_2.id})
self.assertEqual(picking.move_ids.partner_id, partner_2)
def test_cancel_picking_with_scrapped_products(self):
"""
The user scraps some products of a picking, then cancel this picking
The test ensures that the scrapped SM is not cancelled
"""
stock_location = self.env['stock.location'].browse(self.stock_location)
self.env['stock.quant']._update_available_quantity(self.productA, stock_location, 10)
picking = self.env['stock.picking'].create({
'picking_type_id': self.picking_type_out,
'location_id': self.supplier_location,
'location_dest_id': self.stock_location,
})
move = self.env['stock.move'].create({
'name': self.productA.name,
'product_id': self.productA.id,
'product_uom_qty': 1,
'product_uom': self.productA.uom_id.id,
'picking_id': picking.id,
'location_id': self.supplier_location,
'location_dest_id': self.stock_location,
})
picking.action_confirm()
picking.action_assign()
scrap = self.env['stock.scrap'].create({
'picking_id': picking.id,
'product_id': self.productA.id,
'product_uom_id': self.productA.uom_id.id,
'scrap_qty': 1.0,
})
scrap.do_scrap()
picking.action_cancel()
self.assertEqual(picking.state, 'cancel')
self.assertEqual(move.state, 'cancel')
self.assertEqual(scrap.move_id.state, 'done')