From c385790880d00de9503dce03bdcab2aa8f08cf4e Mon Sep 17 00:00:00 2001
From: Michele <dicroce.m@stesi.eu>
Date: Thu, 27 Apr 2023 14:35:31 +0000
Subject: [PATCH] [IMP] stock: unreserve only quantity required if available
 quantity is less than quantity required when action_done on stock move.

Example:
Now there is this behaviour
Inventory quantity 4
Reserved quantity 3
Available quantity 1
If i do a stock move of 2 pieces, it will unreserve ALL the stock move of the product.

With this PR it will unreserve only the pieces that are required minus the available quantity not reserved , in this case 2 (new stock move) - 1 (available quantity) = 1

closes odoo/odoo#121692

X-original-commit: 999c2045236161cc8d8a76ab6a33c17d1b124f25
Signed-off-by: William Henrotin (whe) <whe@odoo.com>
---
 addons/stock/models/stock_move_line.py |  1 +
 addons/stock/tests/test_move2.py       | 37 ++++++++++++++++++++++++++
 2 files changed, 38 insertions(+)

diff --git a/addons/stock/models/stock_move_line.py b/addons/stock/models/stock_move_line.py
index b7858bca0a23..41cf1f44d308 100644
--- a/addons/stock/models/stock_move_line.py
+++ b/addons/stock/models/stock_move_line.py
@@ -691,6 +691,7 @@ class StockMoveLine(models.Model):
             product_id, location_id, lot_id=lot_id, package_id=package_id, owner_id=owner_id, strict=True
         )
         if quantity > available_quantity:
+            quantity = quantity - available_quantity
             # We now have to find the move lines that reserved our now unavailable quantity. We
             # take care to exclude ourselves and the move lines were work had already been done.
             outdated_move_lines_domain = [
diff --git a/addons/stock/tests/test_move2.py b/addons/stock/tests/test_move2.py
index b6c188820457..043cbb5f865a 100644
--- a/addons/stock/tests/test_move2.py
+++ b/addons/stock/tests/test_move2.py
@@ -104,6 +104,43 @@ class TestPickShip(TestStockCommon):
         })
         return picking_pick, picking_pack, picking_ship
 
+    def test_unreserve_only_required_quantity(self):
+        product_unreserve = self.env['product.product'].create({
+            'name': 'product unreserve',
+            'type': 'product',
+            'categ_id': self.env.ref('product.product_category_all').id,
+        })
+        stock_location = self.env['stock.location'].browse(self.stock_location)
+        self.env['stock.quant']._update_available_quantity(product_unreserve, stock_location, 4.0)
+        quants = self.env['stock.quant']._gather(product_unreserve, stock_location, strict=True)
+        self.assertEqual(quants[0].reserved_quantity, 0)
+        move = self.MoveObj.create({
+            'name': product_unreserve.name,
+            'product_id': product_unreserve.id,
+            'product_uom_qty': 3,
+            'product_uom': product_unreserve.uom_id.id,
+            'state': 'confirmed',
+            'location_id': self.stock_location,
+            'location_dest_id': self.customer_location,
+        })
+        move._action_assign()
+        self.assertEqual(quants[0].reserved_quantity, 3)
+        move_2 = self.MoveObj.create({
+            'name': product_unreserve.name,
+            'product_id': product_unreserve.id,
+            'product_uom_qty': 2,
+            'quantity_done':2,
+            'product_uom': product_unreserve.uom_id.id,
+            'state': 'confirmed',
+            'location_id': self.stock_location,
+            'location_dest_id': self.customer_location,
+        })
+        move_2._action_assign()
+        move_2._action_done()
+        quants = self.env['stock.quant']._gather(product_unreserve, stock_location, strict=True)
+        self.assertEqual(quants[0].reserved_quantity, 2)
+
+
     def test_mto_moves(self):
         """
             10 in stock, do pick->ship and check ship is assigned when pick is done, then backorder of ship
-- 
GitLab