From 5e51ab1f4ef38b96cbd7814449860a42cfa409ed Mon Sep 17 00:00:00 2001 From: "David (dafr)" Date: Thu, 20 Apr 2023 16:28:00 +0200 Subject: [PATCH] [FIX] mrp_subcontracting_purchase: fix received_qty with return move Method _is_purchase_return had a complex check intended to detect subcontract return move. This Check would incorrectly pass for subcontract move when the destination location (input) does not belong to the customer warehouse. To fix this issue, we can check the field StockMove.is_subcontract, however this field is only available if mrp_subcontracting is installed. Furthermore, the check itself make no sens in purchase_stock if the module mrp_subcontracting is not install Hence, we can move this check to mrp_subcontracting_purchase. With Purchase & Mrp Subcontracting installed: - Create Component C, consumable - Create Product P, storable, Set Vendor V under Purchase Tab - Create BOM for P, subcontracted with Vendor V, and C as component - In the Warehouse, set 2 steps reception - Set Input parent location to 'Physical Location' - Create PO to vendor V, product P, confirm, receive Product => Received Qty for P in PO show 0 OPW-3216011 X-original-commit: bf666d33af4ac32563e159cb6e79483d6f37b3d6 Part-of: odoo/odoo#147344 --- .../mrp_subcontracting/models/stock_move.py | 9 ++++ .../models/__init__.py | 1 + .../models/stock_move.py | 12 +++++ .../tests/test_mrp_subcontracting_purchase.py | 48 +++++++++++++++++++ addons/purchase_stock/models/stock_move.py | 7 +-- 5 files changed, 71 insertions(+), 6 deletions(-) create mode 100644 addons/mrp_subcontracting_purchase/models/stock_move.py diff --git a/addons/mrp_subcontracting/models/stock_move.py b/addons/mrp_subcontracting/models/stock_move.py index 5492302a221..87f2b3ef3d6 100644 --- a/addons/mrp_subcontracting/models/stock_move.py +++ b/addons/mrp_subcontracting/models/stock_move.py @@ -320,3 +320,12 @@ class StockMove(models.Model): if self.env.user.has_group('base.group_portal') and not self.env.su: if vals.get('state') == 'done': raise AccessError(_("Portal users cannot create a stock move with a state 'Done' or change the current state to 'Done'.")) + + def _is_subcontract_return(self): + self.ensure_one() + subcontracting_location = self.picking_id.partner_id.with_company(self.company_id).property_stock_subcontractor + return ( + not self.is_subcontract + and self.origin_returned_move_id.is_subcontract + and self.location_dest_id.id == subcontracting_location.id + ) diff --git a/addons/mrp_subcontracting_purchase/models/__init__.py b/addons/mrp_subcontracting_purchase/models/__init__.py index 6576bfb375b..59cbd6c77d3 100644 --- a/addons/mrp_subcontracting_purchase/models/__init__.py +++ b/addons/mrp_subcontracting_purchase/models/__init__.py @@ -3,6 +3,7 @@ from . import account_move_line from . import purchase_order +from . import stock_move from . import stock_picking from . import stock_rule from . import stock_valuation_layer diff --git a/addons/mrp_subcontracting_purchase/models/stock_move.py b/addons/mrp_subcontracting_purchase/models/stock_move.py new file mode 100644 index 00000000000..2fd9b630007 --- /dev/null +++ b/addons/mrp_subcontracting_purchase/models/stock_move.py @@ -0,0 +1,12 @@ +# -*- 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_purchase_return(self): + res = super()._is_purchase_return() + return res or self._is_subcontract_return() diff --git a/addons/mrp_subcontracting_purchase/tests/test_mrp_subcontracting_purchase.py b/addons/mrp_subcontracting_purchase/tests/test_mrp_subcontracting_purchase.py index f255094108d..b29548e5589 100644 --- a/addons/mrp_subcontracting_purchase/tests/test_mrp_subcontracting_purchase.py +++ b/addons/mrp_subcontracting_purchase/tests/test_mrp_subcontracting_purchase.py @@ -251,6 +251,54 @@ class MrpSubcontractingPurchaseTest(TestMrpSubcontractingCommon): self.assertTrue(orderpoint) self.assertEqual(orderpoint.warehouse_id, self.warehouse) + def test_purchase_and_return03(self): + """ + With 2 steps receipt and an input location child of Physical Location (instead of WH) + The user buys 10 x a subcontracted product P. He receives the 10 + products and then does a return with 3 x P. The test ensures that the + final received quantity is correctly computed + """ + # Set 2 steps receipt + self.warehouse.write({"reception_steps": "two_steps"}) + # Set 'Input' parent location to 'Physical locations' + physical_locations = self.env.ref("stock.stock_location_locations") + input_location = self.warehouse.wh_input_stock_loc_id + input_location.write({"location_id": physical_locations.id}) + + # Create Purchase + po = self.env['purchase.order'].create({ + 'partner_id': self.subcontractor_partner1.id, + 'order_line': [(0, 0, { + 'name': self.finished2.name, + 'product_id': self.finished2.id, + 'product_uom_qty': 10, + 'product_uom': self.finished2.uom_id.id, + 'price_unit': 1, + })], + }) + po.button_confirm() + + # Receive Products + receipt = po.picking_ids + receipt.move_lines.quantity_done = 10 + receipt.button_validate() + + self.assertEqual(po.order_line.qty_received, 10.0) + + # Return Products + return_form = Form(self.env['stock.return.picking'].with_context(active_id=receipt.id, active_model='stock.picking')) + with return_form.product_return_moves.edit(0) as line: + line.quantity = 3 + line.to_refund = True + return_wizard = return_form.save() + return_id, _ = return_wizard._create_returns() + + return_picking = self.env['stock.picking'].browse(return_id) + return_picking.move_lines.quantity_done = 3 + return_picking.button_validate() + + self.assertEqual(po.order_line.qty_received, 7.0) + def test_subcontracting_resupply_price_diff(self): """Test that the price difference is correctly computed when a subcontracted product is resupplied. diff --git a/addons/purchase_stock/models/stock_move.py b/addons/purchase_stock/models/stock_move.py index b65e7b3a81b..5859b998376 100644 --- a/addons/purchase_stock/models/stock_move.py +++ b/addons/purchase_stock/models/stock_move.py @@ -201,12 +201,7 @@ class StockMove(models.Model): def _is_purchase_return(self): self.ensure_one() - return self.location_dest_id.usage == "supplier" or ( - self.location_dest_id.usage == "internal" - and self.location_id.usage != "supplier" - and self.warehouse_id - and self.location_dest_id not in self.env["stock.location"].search([("id", "child_of", self.warehouse_id.view_location_id.id)]) - ) + return self.location_dest_id.usage == "supplier" def _get_all_related_aml(self): # The back and for between account_move and account_move_line is necessary to catch the