From 1ae66862d5570ebdb97ba0301ff591c27b969254 Mon Sep 17 00:00:00 2001 From: Simon Lejeune Date: Tue, 3 Dec 2019 16:57:27 +0000 Subject: [PATCH] [REF] stock: products on hand action Instead of hardcoding a domain in the action, we add a field that will be used to apply the domain. The goal is to be able to define a static domain in the xml so we'll be able to default on the filter while still being able to remove it. Let's consider this usecase: Physical | \ WH Subcontracting | | Stock 5 units | 10 units With the hardcoded domain, we have two problems - the right part of the tree isn't visible - the user can add a quant in subcontracting but it will disappear at the creation since it isn't part of the hardcoded domain. The complicated part of the domain is the `_get_domain_locations` call. It will use multiple context keys, the current company and get the hierarchy of internal locations below warehouses to see what it should include or not. To be able to remove a domain, it should be declared in the xml and not hardcoded in the action. Since the domain is complex, we can't express it. So we add a computed field "on_hand" and its search method is overiden to call `_get_domain_locations`. task-2061825 closes odoo/odoo#40403 Signed-off-by: Simon Lejeune (sle) --- addons/stock/models/product.py | 5 ++--- addons/stock/models/stock_quant.py | 19 +++++++++++++++---- addons/stock/views/stock_quant_views.xml | 1 + 3 files changed, 18 insertions(+), 7 deletions(-) diff --git a/addons/stock/models/product.py b/addons/stock/models/product.py index 2b8e1de076c..3434a433e96 100644 --- a/addons/stock/models/product.py +++ b/addons/stock/models/product.py @@ -481,8 +481,7 @@ class Product(models.Model): # Be aware that the exact same function exists in product.template def action_open_quants(self): - location_domain = self._get_domain_locations()[0] - domain = expression.AND([[('product_id', 'in', self.ids)], location_domain]) + domain = [('product_id', 'in', self.ids)] hide_location = not self.user_has_groups('stock.group_stock_multi_locations') hide_lot = all([product.tracking == 'none' for product in self]) self = self.with_context(hide_location=hide_location, hide_lot=hide_lot) @@ -507,7 +506,7 @@ class Product(models.Model): else: self = self.with_context(product_tmpl_id=self.product_tmpl_id.id) ctx = dict(self.env.context) - ctx.update({'no_at_date': True}) + ctx.update({'no_at_date': True, 'search_default_on_hand': True}) return self.env['stock.quant'].with_context(ctx)._get_quants_action(domain) def action_update_quantity_on_hand(self): diff --git a/addons/stock/models/stock_quant.py b/addons/stock/models/stock_quant.py index b5a26d64b6c..475071a1c23 100644 --- a/addons/stock/models/stock_quant.py +++ b/addons/stock/models/stock_quant.py @@ -51,15 +51,13 @@ class StockQuant(models.Model): 'product.product', 'Product', domain=lambda self: self._domain_product_id(), ondelete='restrict', readonly=True, required=True, index=True, check_company=True) - # so user can filter on template in webclient product_tmpl_id = fields.Many2one( 'product.template', string='Product Template', related='product_id.product_tmpl_id', readonly=False) product_uom_id = fields.Many2one( 'uom.uom', 'Unit of Measure', readonly=True, related='product_id.uom_id') - company_id = fields.Many2one(related='location_id.company_id', - string='Company', store=True, readonly=True) + company_id = fields.Many2one(related='location_id.company_id', string='Company', store=True, readonly=True) location_id = fields.Many2one( 'stock.location', 'Location', domain=lambda self: self._domain_location_id(), @@ -93,6 +91,7 @@ class StockQuant(models.Model): compute='_compute_available_quantity') in_date = fields.Datetime('Incoming Date', readonly=True) tracking = fields.Selection(related='product_id.tracking', readonly=True) + on_hand = fields.Boolean('On Hand', store=False, search='_search_on_hand') @api.depends('quantity', 'reserved_quantity') def _compute_available_quantity(self): @@ -128,6 +127,18 @@ class StockQuant(models.Model): move = quant.env['stock.move'].with_context(inventory_mode=False).create(move_vals) move._action_done() + def _search_on_hand(self, operator, value): + """Handle the "on_hand" filter, indirectly calling `_get_domain_locations`.""" + if operator not in ['=', '!='] or not isinstance(value, bool): + raise UserError(_('Operation not supported')) + domain_loc = self.env['product.product']._get_domain_locations()[0] + quant_ids = [l['id'] for l in self.env['stock.quant'].search_read(domain_loc, ['id'])] + if (operator == '!=' and value is True) or (operator == '=' and value is False): + domain_operator = 'not in' + else: + domain_operator = 'in' + return [('id', domain_operator, quant_ids)] + @api.model def create(self, vals): """ Override to handle the "inventory mode" and create a quant as @@ -612,7 +623,7 @@ class StockQuant(models.Model): 'view_mode': 'list', 'res_model': 'stock.quant', 'type': 'ir.actions.act_window', - 'context': dict(self.env.context), + 'context': self.env.context, 'domain': domain or [], 'help': """

No Stock On Hand

diff --git a/addons/stock/views/stock_quant_views.xml b/addons/stock/views/stock_quant_views.xml index 4a1c7b64ab6..949a3e471b9 100644 --- a/addons/stock/views/stock_quant_views.xml +++ b/addons/stock/views/stock_quant_views.xml @@ -15,6 +15,7 @@ +