From 0486e2e58ab75c7f6d3ab7d65fba9467216b1468 Mon Sep 17 00:00:00 2001
From: "Adrien Widart (awt)" <awt@odoo.com>
Date: Mon, 7 Nov 2022 13:54:19 +0000
Subject: [PATCH] [FIX] mrp_subcontracting_dropshipping: return to stock
 location

When returning a dropshipped and subcontracted product to an internal
location, the received quantity of the PO line will be incorrect

To reproduce the issue:
1. In Settings, enable "Storage Locations"
2. Create two storable products P_compo, P_Finished
3. Create a BoM:
    - Product: P_finished
    - Type: Subcontracting
    - Subcontractors: a subcontractor S
    - Components: 1 x P_compo
4. In Locations, edit WH/Stock:
    - Return location: True
5. Create and confirm a PO:
    - Vendor: S
    - Deliver To: Dropship
    - Drop Ship Address: a partner P
    - Products: 1 x P_finished
6. Validate the receipt
7. Create a return with 1 x P_finished:
    - Update SO/PO quantities: True
    - Return Location: WH/Stock
8. Validate the return
9. Go back to the PO

Error: The qty received is 2, it should be 1 (it should not be 0 since
the product has been returned to an internal location)

In `/purchase_stock._compute_qty_received`, there is already a code to
anticipate such a situation (i.e., a user who returns a dropshipped
product to his stock location):
https://github.com/odoo/odoo/blob/bf3c398f0644f690c64815b2b6e298aed7bedd70/addons/purchase_stock/models/purchase.py#L300-L305
However, the methods `_is_dropshipped` and `_is_dropshipped_returned` do
not include the feature subcontracting + dropshipping. This is the
reason why, in `_compute_qty_received`, the above condition is not
respected and why we add the return stock move to the received qty.

OPW-3030895

Part-of: odoo/odoo#105185
---
 .../models/__init__.py                        |  1 +
 .../models/stock_move.py                      | 24 +++++++++++++++++++
 .../tests/test_purchase_subcontracting.py     | 18 +++++++++++++-
 3 files changed, 42 insertions(+), 1 deletion(-)
 create mode 100644 addons/mrp_subcontracting_dropshipping/models/stock_move.py

diff --git a/addons/mrp_subcontracting_dropshipping/models/__init__.py b/addons/mrp_subcontracting_dropshipping/models/__init__.py
index ac9517445aee..b8548572ee3f 100644
--- a/addons/mrp_subcontracting_dropshipping/models/__init__.py
+++ b/addons/mrp_subcontracting_dropshipping/models/__init__.py
@@ -1,4 +1,5 @@
 # -*- coding: utf-8 -*-
 # Part of Odoo. See LICENSE file for full copyright and licensing details.
 
+from . import stock_move
 from . import stock_picking
diff --git a/addons/mrp_subcontracting_dropshipping/models/stock_move.py b/addons/mrp_subcontracting_dropshipping/models/stock_move.py
new file mode 100644
index 000000000000..fd507f22b192
--- /dev/null
+++ b/addons/mrp_subcontracting_dropshipping/models/stock_move.py
@@ -0,0 +1,24 @@
+# -*- coding: utf-8 -*-
+# Part of Odoo. See LICENSE file for full copyright and licensing details.
+
+from odoo import models
+
+
+class StockMove(models.Model):
+    _inherit = "stock.move"
+
+    def _is_dropshipped(self):
+        res = super()._is_dropshipped()
+        return res or (
+                self.partner_id.property_stock_subcontractor.parent_path
+                and self.partner_id.property_stock_subcontractor.parent_path in self.location_id.parent_path
+                and self.location_dest_id.usage == 'customer'
+        )
+
+    def _is_dropshipped_returned(self):
+        res = super()._is_dropshipped_returned()
+        return res or (
+                self.location_id.usage == 'customer'
+                and self.partner_id.property_stock_subcontractor.parent_path
+                and self.partner_id.property_stock_subcontractor.parent_path in self.location_dest_id.parent_path
+        )
diff --git a/addons/mrp_subcontracting_dropshipping/tests/test_purchase_subcontracting.py b/addons/mrp_subcontracting_dropshipping/tests/test_purchase_subcontracting.py
index 9145bbbd7649..5d7e0d485e60 100644
--- a/addons/mrp_subcontracting_dropshipping/tests/test_purchase_subcontracting.py
+++ b/addons/mrp_subcontracting_dropshipping/tests/test_purchase_subcontracting.py
@@ -138,7 +138,8 @@ class TestSubcontractingDropshippingFlows(TestMrpSubcontractingCommon):
     def test_po_to_customer(self):
         """
         Create and confirm a PO with a subcontracted move. The picking type of
-        the PO is 'Dropship' and the delivery address a customer.
+        the PO is 'Dropship' and the delivery address a customer. Then, process
+        a return with the stock location as destination
         """
         subcontractor, client = self.env['res.partner'].create([
             {'name': 'SuperSubcontractor'},
@@ -193,6 +194,21 @@ class TestSubcontractingDropshippingFlows(TestMrpSubcontractingCommon):
         self.assertEqual(mo.state, 'done')
         self.assertEqual(po.order_line.qty_received, 1)
 
+        stock_location = self.warehouse.lot_stock_id
+        stock_location.return_location = True
+        return_form = Form(self.env['stock.return.picking'].with_context(active_ids=delivery.ids, active_id=delivery.id, active_model='stock.picking'))
+        return_form.location_id = stock_location
+        return_wizard = return_form.save()
+        return_picking_id, _pick_type_id = return_wizard._create_returns()
+
+        delivery_return = self.env['stock.picking'].browse(return_picking_id)
+        delivery_return.move_line_ids.qty_done = 1.0
+        delivery_return.button_validate()
+
+        self.assertEqual(delivery_return.state, 'done')
+        self.assertEqual(p_finished.qty_available, 1, 'One product has been returned to the stock location, so it should be available')
+        self.assertEqual(po.order_line.qty_received, 1, 'One product has been returned to the stock location, so we should still consider it as received')
+
     def test_po_to_subcontractor(self):
         """
         Create and confirm a PO with a subcontracted move. The bought product is
-- 
GitLab