From 255aaef265d8a5f5b109d75255c3d8b856f8564f Mon Sep 17 00:00:00 2001 From: Suherdy Yacob Date: Fri, 3 Apr 2026 17:12:15 +0700 Subject: [PATCH] feat: add skip_location_restriction context flag to bypass custom domain filtering in stock location and lot searches --- models/stock_location.py | 60 +++++++++++++++++++++++----------------- 1 file changed, 34 insertions(+), 26 deletions(-) diff --git a/models/stock_location.py b/models/stock_location.py index e80a907..b22d8c6 100644 --- a/models/stock_location.py +++ b/models/stock_location.py @@ -73,13 +73,18 @@ class StockQuant(models.Model): """Override to apply location restrictions during reservation (gather)""" result_domain = super()._get_gather_domain(product_id, location_id, lot_id, package_id, owner_id, strict) - # FIX: If reserving from a non-internal location, DO NOT apply internal restrictions. + ctx = self.env.context + # 0. Bypass if skip flag is set + if ctx.get('skip_location_restriction'): + return result_domain + + # 1. FIX: If reserving from a non-internal location, DO NOT apply internal restrictions. if location_id and location_id.usage != 'internal': return result_domain allowed_location_ids = self._get_allowed_locations() if allowed_location_ids: - # 1. Smart Domain Swap: find if there's already a location_id restriction (like child_of) + # 2. Smart Domain Swap: find if there's already a location_id restriction (like child_of) found_collision = False new_domain = [] for leaf in result_domain: @@ -101,11 +106,10 @@ class StockQuant(models.Model): def _search(self, domain, offset=0, limit=None, order=None, *args, **kwargs): """Override to apply location restrictions during search (e.g. catalog or list selection)""" - # CRITICAL FIX: If we are searching for specific IDs (e.g., Odoo is fetching selected records for save), - # we must NOT restrict the search results or we trigger 'Missing Product' validation errors - # on backend fallback create logic. + ctx = self.env.context + # 0. CRITICAL FIX: If searching for specific IDs or skip flag set, bypass custom filtering search_by_id = any(isinstance(leaf, (list, tuple)) and leaf[0] == 'id' for leaf in domain) - if search_by_id: + if search_by_id or ctx.get('skip_location_restriction'): return super()._search(domain, offset=offset, limit=limit, order=order, *args, **kwargs) allowed_location_ids = self._get_allowed_locations() @@ -131,7 +135,6 @@ class StockQuant(models.Model): return super()._search(domain, offset=offset, limit=limit, order=order, *args, **kwargs) - class StockLocation(models.Model): _inherit = 'stock.location' @@ -157,28 +160,33 @@ class StockLot(models.Model): # FIX: If we are searching for specific IDs, bypass custom filtering to avoid backend failures search_by_id = any(isinstance(leaf, (list, tuple)) and leaf[0] == 'id' for leaf in domain) - if search_by_id: + if search_by_id or ctx.get('skip_location_restriction'): return super()._search(domain, offset=offset, limit=limit, order=order, *args, **kwargs) - active_picking_id = ctx.get('active_picking_id') - loc_id = ctx.get('default_location_id') + # 1. Identify which locations we should look into for quants + allowed_location_ids = self.env['stock.quant']._get_allowed_locations() - if active_picking_id and loc_id: - loc = self.env['stock.location'].sudo().browse(loc_id) - if loc.exists() and loc.usage != 'supplier': - quant_domain = [ - ('location_id', 'child_of', loc.id), - ('quantity', '>', 0), - ('lot_id', '!=', False) - ] - product_id = ctx.get('default_product_id') - if product_id: - quant_domain.append(('product_id', '=', product_id)) - - quants = self.env['stock.quant'].sudo().search(quant_domain) - lot_ids = list(set(quants.mapped('lot_id').ids)) - - domain = Domain.AND([domain, [('id', 'in', lot_ids)]]) + # 2. If no explicit allowed locations, fallback to the default source location in context + if not allowed_location_ids: + loc_id = ctx.get('default_location_id') + if loc_id: + allowed_location_ids = [loc_id] + + if allowed_location_ids: + quant_domain = [ + ('location_id', 'in', allowed_location_ids), + ('quantity', '>', 0), + ('lot_id', '!=', False) + ] + product_id = ctx.get('default_product_id') + if product_id: + quant_domain.append(('product_id', '=', product_id)) + + # Use internal bypass when searching quants to filter lots + quants = self.env['stock.quant'].with_context(skip_location_restriction=True).sudo().search(quant_domain) + lot_ids = list(set(quants.mapped('lot_id').ids)) + + domain = Domain.AND([domain, [('id', 'in', lot_ids)]]) return super()._search(domain, offset=offset, limit=limit, order=order, *args, **kwargs)