diff --git a/addons/stock/models/stock_move_line.py b/addons/stock/models/stock_move_line.py index f617d529493..9b07d7e75ef 100644 --- a/addons/stock/models/stock_move_line.py +++ b/addons/stock/models/stock_move_line.py @@ -860,3 +860,56 @@ class StockMoveLine(models.Model): 'views': [[False, "form"]], 'res_id': self.id, } + + def _get_revert_inventory_move_values(self): + self.ensure_one() + return { + 'name':_('%s [reverted]', self.reference), + 'product_id': self.product_id.id, + 'product_uom': self.product_uom_id.id, + 'product_uom_qty': self.qty_done, + 'company_id': self.company_id.id or self.env.company.id, + 'state': 'confirmed', + 'location_id': self.location_dest_id.id, + 'location_dest_id': self.location_id.id, + 'is_inventory': True, + 'move_line_ids': [(0, 0, { + 'product_id': self.product_id.id, + 'product_uom_id': self.product_uom_id.id, + 'qty_done': self.qty_done, + 'location_id': self.location_dest_id.id, + 'location_dest_id': self.location_id.id, + 'company_id': self.company_id.id or self.env.company.id, + 'lot_id': self.lot_id.id, + 'package_id': self.package_id.id, + 'result_package_id': self.package_id.id, + 'owner_id': self.owner_id.id, + })] + } + + def action_revert_inventory(self): + move_vals = [] + processed_move_line = self.env['stock.move.line'] + for move_line in self: + if move_line.is_inventory and not float_is_zero(move_line.qty_done, precision_digits=move_line.product_uom_id.rounding): + processed_move_line += move_line + move_vals.append(move_line._get_revert_inventory_move_values()) + if not processed_move_line: + return { + 'type': 'ir.actions.client', + 'tag': 'display_notification', + 'params': { + 'type': 'danger', + 'message': _("There are no inventory adjustments to revert."), + } + } + moves = self.env['stock.move'].create(move_vals) + moves._action_done() + return { + 'type': 'ir.actions.client', + 'tag': 'display_notification', + 'params': { + 'type': 'success', + 'message': _("The inventory adjustments have been reverted."), + } + } diff --git a/addons/stock/tests/test_quant_inventory_mode.py b/addons/stock/tests/test_quant_inventory_mode.py index 9b84c135cb6..066ff4c12cc 100644 --- a/addons/stock/tests/test_quant_inventory_mode.py +++ b/addons/stock/tests/test_quant_inventory_mode.py @@ -294,3 +294,37 @@ class TestEditableQuant(TransactionCase): warning = dupe_sn._onchange_serial_number() self.assertTrue(warning, 'Reuse of existing serial number not detected') self.assertEqual(list(warning.keys())[0], 'warning', 'Warning message was not returned') + + def test_revert_inventory_adjustment(self): + """Try to revert inventory adjustment""" + default_wh = self.env['stock.warehouse'].search([('company_id', '=', self.env.company.id)], limit=1) + default_stock_location = default_wh.lot_stock_id + quant = self.Quant.create({ + 'product_id': self.product.id, + 'location_id': default_stock_location.id, + 'inventory_quantity': 100, + }) + quant.action_apply_inventory() + move_lines = self.env['stock.move.line'].search([('product_id', '=', self.product.id), ('is_inventory', '=', True)]) + self.assertEqual(len(move_lines), 1, "One inventory adjustment move lines should have been created") + self.assertEqual(self.product.qty_available, 100, "Before revert inventory adjustment qty is 100") + move_lines.action_revert_inventory() + self.assertEqual(self.product.qty_available, 0, "After revert inventory adjustment qty is not zero") + + def test_multi_revert_inventory_adjustment(self): + """Try to revert inventory adjustment with multiple lines""" + default_wh = self.env['stock.warehouse'].search([('company_id', '=', self.env.company.id)], limit=1) + default_stock_location = default_wh.lot_stock_id + quant = self.Quant.create({ + 'product_id': self.product.id, + 'location_id': default_stock_location.id, + 'inventory_quantity': 100, + }) + quant.action_apply_inventory() + quant.inventory_quantity = 150 + quant.action_apply_inventory() + move_lines = self.env['stock.move.line'].search([('product_id', '=', self.product.id), ('is_inventory', '=', True)]) + self.assertEqual(self.product.qty_available, 150, "Before revert multi inventory adjustment qty is 150") + self.assertEqual(len(move_lines), 2, "Two inventory adjustment move lines should have been created") + move_lines.action_revert_inventory() + self.assertEqual(self.product.qty_available, 0, "After revert multi inventory adjustment qty is not zero") diff --git a/addons/stock/views/stock_move_line_views.xml b/addons/stock/views/stock_move_line_views.xml index 865b9a170d0..83a0cbb36bd 100644 --- a/addons/stock/views/stock_move_line_views.xml +++ b/addons/stock/views/stock_move_line_views.xml @@ -192,6 +192,16 @@ + + Revert Inventory Adjustment + + + code + + action = records.action_revert_inventory() + + + Moves History stock.move.line