From 7ef11e3c96893dffc1bad93917c6955a38ac6002 Mon Sep 17 00:00:00 2001 From: Simon Lejeune Date: Thu, 11 Jul 2019 14:03:56 +0000 Subject: [PATCH] [REF] stock_account: valuation layer and not admin user Carefully sudo when necessary. task-2034044 closes odoo/odoo#34802 Signed-off-by: Simon Lejeune (sle) --- addons/stock_account/models/product.py | 26 ++++++---- addons/stock_account/models/stock_move.py | 11 +++-- .../tests/test_stockvaluation.py | 47 +++++++++++++++++++ 3 files changed, 69 insertions(+), 15 deletions(-) diff --git a/addons/stock_account/models/product.py b/addons/stock_account/models/product.py index 9760d87de0c..18df38c2f1d 100644 --- a/addons/stock_account/models/product.py +++ b/addons/stock_account/models/product.py @@ -169,10 +169,11 @@ class ProductProduct(models.Model): for product in self: if product.cost_method not in ('standard', 'average'): continue - if float_is_zero(product.quantity_svl, precision_rounding=product.uom_id.rounding): + quantity_svl = product.sudo().quantity_svl + if float_is_zero(quantity_svl, precision_rounding=product.uom_id.rounding): continue diff = new_price - product.standard_price - value = company_id.currency_id.round(product.quantity_svl * diff) + value = company_id.currency_id.round(quantity_svl * diff) if company_id.currency_id.is_zero(value): continue @@ -184,7 +185,7 @@ class ProductProduct(models.Model): 'quantity': 0, } svl_vals_list.append(svl_vals) - stock_valuation_layers = self.env['stock.valuation.layer'].create(svl_vals_list) + stock_valuation_layers = self.env['stock.valuation.layer'].sudo().create(svl_vals_list) # Handle account moves. product_accounts = {product.id: product.product_tmpl_id.get_product_accounts() for product in self} @@ -233,7 +234,7 @@ class ProductProduct(models.Model): account_moves.post() # Actually update the standard price. - self.write({'standard_price': new_price}) + self.with_context(force_company=company_id.id).sudo().write({'standard_price': new_price}) def _run_fifo(self, quantity, company): self.ensure_one() @@ -291,16 +292,21 @@ class ProductProduct(models.Model): } return vals - def _run_fifo_vacuum(self): + def _run_fifo_vacuum(self, company=None): """Compensate layer valued at an estimated price with the price of future receipts if any. If the estimated price is equals to the real price, no layer is created but the original layer is marked as compensated. + + :param company: recordset of `res.company` to limit the execution of the vacuum """ self.ensure_one() - svls_to_vacuum = self.env['stock.valuation.layer'].search([ + if company is None: + company = self.env.company + svls_to_vacuum = self.env['stock.valuation.layer'].sudo().search([ ('product_id', '=', self.id), ('remaining_qty', '<', 0), ('stock_move_id', '!=', False), + ('company_id', '=', company.id), ]) for svl_to_vacuum in svls_to_vacuum: domain = [ @@ -313,7 +319,7 @@ class ProductProduct(models.Model): ('create_date', '=', svl_to_vacuum.create_date), ('id', '>', svl_to_vacuum.id) ] - candidates = self.env['stock.valuation.layer'].search(domain) + candidates = self.env['stock.valuation.layer'].sudo().search(domain) if not candidates: continue qty_to_take_on_candidates = abs(svl_to_vacuum.remaining_qty) @@ -359,7 +365,7 @@ class ProductProduct(models.Model): 'company_id': move.company_id.id, 'description': 'Revaluation of %s (negative inventory)' % move.picking_id.name or move.name, } - vacuum_svl = self.env['stock.valuation.layer'].create(vals) + vacuum_svl = self.env['stock.valuation.layer'].sudo().create(vals) # Create the account move. if self.valuation != 'real_time': @@ -683,7 +689,7 @@ class ProductCategory(models.Model): % (self.display_name, self.property_valuation, new_valuation) out_svl_vals_list, products_orig_quantity_svl, products = Product\ ._svl_empty_stock(description, product_category=product_category) - out_stock_valuation_layers = SVL.create(out_svl_vals_list) + out_stock_valuation_layers = SVL.sudo().create(out_svl_vals_list) if product_category.property_valuation == 'real_time': move_vals_list += Product._svl_empty_stock_am(out_stock_valuation_layers) impacted_categories[product_category] = (products, description, products_orig_quantity_svl) @@ -693,7 +699,7 @@ class ProductCategory(models.Model): for product_category, (products, description, products_orig_quantity_svl) in impacted_categories.items(): # Replenish the stock with the new cost method. in_svl_vals_list = products._svl_replenish_stock(description, products_orig_quantity_svl) - in_stock_valuation_layers = SVL.create(in_svl_vals_list) + in_stock_valuation_layers = SVL.sudo().create(in_svl_vals_list) if product_category.property_valuation == 'real_time': move_vals_list += Product._svl_replenish_stock_am(in_stock_valuation_layers) diff --git a/addons/stock_account/models/stock_move.py b/addons/stock_account/models/stock_move.py index cd77791fd02..0273b42b897 100644 --- a/addons/stock_account/models/stock_move.py +++ b/addons/stock_account/models/stock_move.py @@ -160,7 +160,7 @@ class StockMove(models.Model): if forced_quantity: svl_vals['description'] = 'Correction of %s (modification of past move)' % move.picking_id.name or move.name svl_vals_list.append(svl_vals) - return self.env['stock.valuation.layer'].create(svl_vals_list) + return self.env['stock.valuation.layer'].sudo().create(svl_vals_list) def _create_out_svl(self, forced_quantity=None): """Create a `stock.valuation.layer` from `self`. @@ -179,7 +179,7 @@ class StockMove(models.Model): if forced_quantity: svl_vals['description'] = 'Correction of %s (modification of past move)' % move.picking_id.name or move.name svl_vals_list.append(svl_vals) - return self.env['stock.valuation.layer'].create(svl_vals_list) + return self.env['stock.valuation.layer'].sudo().create(svl_vals_list) def _create_dropshipped_svl(self, forced_quantity=None): """Create a `stock.valuation.layer` from `self`. @@ -218,7 +218,7 @@ class StockMove(models.Model): } out_vals.update(common_vals) svl_vals_list.append(out_vals) - return self.env['stock.valuation.layer'].create(svl_vals_list) + return self.env['stock.valuation.layer'].sudo().create(svl_vals_list) def _create_dropshipped_returned_svl(self, forced_quantity=None): """Create a `stock.valuation.layer` from `self`. @@ -242,7 +242,7 @@ class StockMove(models.Model): res = super(StockMove, self)._action_done(cancel_backorder=cancel_backorder) - stock_valuation_layers = self.env['stock.valuation.layer'] + stock_valuation_layers = self.env['stock.valuation.layer'].sudo() # Create the valuation layers in batch by calling `moves._create_valued_type_svl`. for valued_type in self._get_valued_types(): todo_valued_moves = valued_moves[valued_type] @@ -260,8 +260,9 @@ class StockMove(models.Model): # For every in move, run the vacuum for the linked product. products_to_vacuum = valued_moves['in'].mapped('product_id') + company = valued_moves['in'].mapped('company_id') and valued_moves['in'].mapped('company_id')[0] or self.env.company for product_to_vacuum in products_to_vacuum: - product_to_vacuum._run_fifo_vacuum() + product_to_vacuum._run_fifo_vacuum(company) return res diff --git a/addons/stock_account/tests/test_stockvaluation.py b/addons/stock_account/tests/test_stockvaluation.py index 1b642e0ecb4..3d8d1244739 100644 --- a/addons/stock_account/tests/test_stockvaluation.py +++ b/addons/stock_account/tests/test_stockvaluation.py @@ -29,6 +29,13 @@ class TestStockValuation(SavepointCase): 'type': 'product', 'categ_id': cls.env.ref('product.product_category_all').id, }) + cls.inventory_user = cls.env['res.users'].create({ + 'name': 'Pauline Poivraisselle', + 'login': 'pauline', + 'email': 'p.p@example.com', + 'notification_type': 'inbox', + 'groups_id': [(6, 0, [cls.env.ref('stock.group_stock_user').id])] + }) cls.product1.categ_id.property_valuation = 'real_time' cls.product2.categ_id.property_valuation = 'real_time' @@ -2674,6 +2681,46 @@ class TestStockValuation(SavepointCase): self.assertAlmostEqual(self.product1.quantity_svl, 0.0) self.assertAlmostEqual(self.product1.value_svl, 0.0) + def test_standard_manual_2(self): + """Validate a receipt as a regular stock user.""" + self.product1.categ_id.property_cost_method = 'standard' + self.product1.categ_id.property_valuation = 'manual_periodic' + + self.product1.with_user(self.inventory_user)._change_standard_price(10) + + move1 = self.env['stock.move'].with_user(self.inventory_user).create({ + 'name': 'IN 10 units', + 'location_id': self.supplier_location.id, + 'location_dest_id': self.stock_location.id, + 'product_id': self.product1.id, + 'product_uom': self.uom_unit.id, + 'product_uom_qty': 10.0, + }) + move1._action_confirm() + move1._action_assign() + move1.move_line_ids.qty_done = 10.0 + move1._action_done() + + def test_standard_perpetual_1(self): + """Validate a receipt as a regular stock user.""" + self.product1.categ_id.property_cost_method = 'standard' + self.product1.categ_id.property_valuation = 'real_time' + + self.product1.with_user(self.inventory_user)._change_standard_price(10) + + move1 = self.env['stock.move'].with_user(self.inventory_user).create({ + 'name': 'IN 10 units', + 'location_id': self.supplier_location.id, + 'location_dest_id': self.stock_location.id, + 'product_id': self.product1.id, + 'product_uom': self.uom_unit.id, + 'product_uom_qty': 10.0, + }) + move1._action_confirm() + move1._action_assign() + move1.move_line_ids.qty_done = 10.0 + move1._action_done() + def test_change_cost_method_1(self): """ Change the cost method from FIFO to AVCO. """