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