From 34db3fd706c5c76dd36fa44e510295437b05badb Mon Sep 17 00:00:00 2001 From: clesgow Date: Fri, 27 Oct 2023 16:05:37 +0200 Subject: [PATCH] [FIX] stock: filter on 'closest' strategy Steps to reproduce: - Enable Storage locations - Add two lots in stock: - lot_shelf_2 in WH/Stock/Shelf 2 - lot_shelf_1 in WH/Stock/Shelf 1 - Enable the 'Closest Location' removal strategy on its product category - Create a delivery using this product and mark it as todo Issue: lot_shelf_2 will be selected even though it should take the one from Shelf 1. When filtering on the location, we need to filter on the location name instead of this is. Also, when creating a new quant on a product using the 'closest' location, the filter will create a traceback because it will try to filter on a NewId. closes odoo/odoo#140175 Signed-off-by: William Henrotin (whe) --- addons/stock/models/stock_quant.py | 4 ++-- addons/stock/tests/test_quant.py | 30 +++++++++++++++++++++--------- 2 files changed, 23 insertions(+), 11 deletions(-) diff --git a/addons/stock/models/stock_quant.py b/addons/stock/models/stock_quant.py index 4b4dca6fb8a..30614e5d22f 100644 --- a/addons/stock/models/stock_quant.py +++ b/addons/stock/models/stock_quant.py @@ -741,7 +741,7 @@ class StockQuant(models.Model): if removal_strategy == 'lifo': reverse = True elif removal_strategy == 'closest': - key = lambda q: (q.location_id, -q.id) + key = lambda q: (q.location_id.complete_name, -q.id) return key, reverse def _get_gather_domain(self, product_id, location_id, lot_id=None, package_id=None, owner_id=None, strict=False): @@ -770,7 +770,7 @@ class StockQuant(models.Model): removal_strategy = self._get_removal_strategy(product_id, location_id) domain = self._get_gather_domain(product_id, location_id, lot_id, package_id, owner_id, strict) domain, order = self._get_removal_strategy_domain_order(domain, removal_strategy, qty) - if self: + if self.ids: sort_key = self._get_removal_strategy_sort_key(removal_strategy) res = self.filtered_domain(domain).sorted(key=sort_key[0], reverse=sort_key[1]) else: diff --git a/addons/stock/tests/test_quant.py b/addons/stock/tests/test_quant.py index 7ef1fee7f1f..366f86a2b73 100644 --- a/addons/stock/tests/test_quant.py +++ b/addons/stock/tests/test_quant.py @@ -5,7 +5,7 @@ from datetime import datetime, timedelta from unittest.mock import patch from ast import literal_eval -from odoo import fields +from odoo import Command, fields from odoo.addons.mail.tests.common import mail_new_test_user from odoo.exceptions import ValidationError from odoo.tests.common import Form, TransactionCase @@ -625,6 +625,10 @@ class StockQuant(TransactionCase): """ Check that the Closest location strategy correctly applies when you have multiple lot received at different locations for a tracked product. """ + # Enable multi-locations to be able to set an origin location for delivery + grp_multi_loc = self.env.ref('stock.group_stock_multi_locations') + self.env.user.write({'groups_id': [Command.link(grp_multi_loc.id)]}) + closest_strategy = self.env['product.removal'].search([('method', '=', 'closest')]) self.stock_location.removal_strategy_id = closest_strategy lot1 = self.env['stock.lot'].create({ @@ -638,14 +642,22 @@ class StockQuant(TransactionCase): 'company_id': self.env.company.id, }) in_date = datetime.now() - # Add a product from lot1 in stock_location/subloc2 - self.env['stock.quant']._update_available_quantity(self.product_serial, self.stock_subloc2, 1.0, lot_id=lot1, in_date=in_date) - # Add a product from lot2 in stock_location/subloc3 - self.env['stock.quant']._update_available_quantity(self.product_serial, self.stock_subloc3, 1.0, lot_id=lot2, in_date=in_date) - # Require one unit of the product - quants = self.env['stock.quant']._get_reserve_quantity(self.product_serial, self.stock_location, 1) - # Default removal strategy is 'Closest location', so lot1 should be received as it was put in a closer location. (stock_location/subloc2 < stock_location/subloc3) - self.assertEqual(quants[0][0].lot_id.id, lot1.id) + # Add a product from lot1 in stock_location/subloc3 + self.env['stock.quant']._update_available_quantity(self.product_serial, self.stock_subloc3, 1.0, lot_id=lot1, in_date=in_date) + # Add a product from lot2 in stock_location/subloc2 + self.env['stock.quant']._update_available_quantity(self.product_serial, self.stock_subloc2, 1.0, lot_id=lot2, in_date=in_date) + # Require one unit of the product for a delivery + with Form(self.env['stock.picking']) as picking_form: + picking_form.picking_type_id = self.env.ref('stock.picking_type_out') + picking_form.location_id = self.stock_location + with picking_form.move_ids_without_package.new() as move_form: + move_form.product_id = self.product_serial + move_form.product_uom_qty = 1 + picking = picking_form.save() + picking.action_confirm() + + # Default removal strategy is 'Closest location', so lot2 should be received as it was put in a closer location. (stock_location/subloc2 < stock_location/subloc3) + self.assertEqual(picking.move_ids.lot_ids.id, lot2.id) def test_closest_removal_strategy_untracked(self): """ Check that the Closest location strategy correctly applies when you have multiple products received