diff --git a/addons/stock_account/models/stock.py b/addons/stock_account/models/stock.py index 8648cea460f..f638dcfb3d8 100644 --- a/addons/stock_account/models/stock.py +++ b/addons/stock_account/models/stock.py @@ -198,7 +198,10 @@ class StockMove(models.Model): def _run_fifo(self, move, quantity=None): move.ensure_one() # Find back incoming stock moves (called candidates here) to value this move. - qty_to_take_on_candidates = quantity or move.product_qty + valued_move_lines = move.move_line_ids.filtered(lambda ml: ml.location_id._should_be_valued() and not ml.location_dest_id._should_be_valued()) + valued_quantity = sum(valued_move_lines.mapped('qty_done')) + + qty_to_take_on_candidates = quantity or valued_quantity candidates = move.product_id._get_fifo_candidates_in_move() new_standard_price = 0 tmp_value = 0 # to accumulate the value taken on the candidates @@ -252,32 +255,37 @@ class StockMove(models.Model): def _run_valuation(self, quantity=None): self.ensure_one() if self._is_in(): + valued_move_lines = self.move_line_ids.filtered(lambda ml: not ml.location_id._should_be_valued() and ml.location_dest_id._should_be_valued()) + valued_quantity = sum(valued_move_lines.mapped('qty_done')) + if self.product_id.cost_method in ['fifo', 'average']: price_unit = self._get_price_unit() - value = price_unit * (quantity or self.product_qty) + value = price_unit * (quantity or valued_quantity) vals = { 'price_unit': price_unit, 'value': value if quantity is None or not self.value else self.value, 'remaining_value': value if quantity is None else self.remaining_value + value, } if self.product_id.cost_method == 'fifo': - vals['remaining_qty'] = self.product_qty if quantity is None else self.remaining_qty + quantity + vals['remaining_qty'] = valued_quantity if quantity is None else self.remaining_qty + quantity self.write(vals) else: # standard - value = self.product_id.standard_price * (quantity or self.product_qty) + value = self.product_id.standard_price * (quantity or valued_quantity) self.write({ 'price_unit': self.product_id.standard_price, 'value': value if quantity is None or not self.value else self.value, }) elif self._is_out(): + valued_move_lines = self.move_line_ids.filtered(lambda ml: ml.location_id._should_be_valued() and not ml.location_dest_id._should_be_valued()) + valued_quantity = sum(valued_move_lines.mapped('qty_done')) if self.product_id.cost_method == 'fifo': self.env['stock.move']._run_fifo(self, quantity=quantity) elif self.product_id.cost_method in ['standard', 'average']: curr_rounding = self.company_id.currency_id.rounding - value = -float_round(self.product_id.standard_price * (self.product_qty if quantity is None else quantity), precision_rounding=curr_rounding) + value = -float_round(self.product_id.standard_price * (valued_quantity if quantity is None else quantity), precision_rounding=curr_rounding) self.write({ 'value': value if quantity is None else self.value + value, - 'price_unit': value / self.product_qty, + 'price_unit': value / valued_quantity, }) def _action_done(self): diff --git a/addons/stock_account/tests/test_stockvaluation.py b/addons/stock_account/tests/test_stockvaluation.py index 9bae64584ca..d5b06439b16 100644 --- a/addons/stock_account/tests/test_stockvaluation.py +++ b/addons/stock_account/tests/test_stockvaluation.py @@ -2644,3 +2644,93 @@ class TestStockValuation(TransactionCase): # (9 * 10) + (15 * 10) / 19 self.assertEqual(self.product1.standard_price, 12.63) + def test_fifo_sublocation_valuation_1(self): + """ Set the main stock as a view location. Receive 2 units of a + product, put 1 unit in an internal sublocation and the second + one in a scrap sublocation. Only a single unit, the one in the + internal sublocation, should be valued. Then, send these two + quants to a customer, only the one in the internal location + should be valued. + """ + self.product1.product_tmpl_id.cost_method = 'fifo' + + view_location = self.env['stock.location'].create({'name': 'view', 'usage': 'view'}) + subloc1 = self.env['stock.location'].create({ + 'name': 'internal', + 'usage': 'internal', + 'location_id': view_location.id, + }) + # sane settings for a scrap location, company_id doesn't matter + subloc2 = self.env['stock.location'].create({ + 'name': 'scrap', + 'usage': 'inventory', + 'location_id': view_location.id, + 'scrap_location': True, + }) + + move1 = self.env['stock.move'].create({ + 'name': '2 units @ 10.00 per unit', + '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': 2.0, + 'price_unit': 10, + }) + move1._action_confirm() + move1._action_assign() + + move1.write({'move_line_ids': [ + (0, None, { + 'product_id': self.product1.id, + 'qty_done': 1, + 'location_id': self.supplier_location.id, + 'location_dest_id': subloc1.id, + 'product_uom_id': self.uom_unit.id + }), + (0, None, { + 'product_id': self.product1.id, + 'qty_done': 1, + 'location_id': self.supplier_location.id, + 'location_dest_id': subloc2.id, + 'product_uom_id': self.uom_unit.id + }), + ]}) + + move1._action_done() + self.assertEqual(move1.value, 10) + self.assertEqual(move1.remaining_value, 10) + self.assertEqual(move1.remaining_qty, 1) + self.assertEqual(self.product1.stock_value, 10) + self.assertTrue(len(move1.account_move_ids), 1) + + move2 = self.env['stock.move'].create({ + 'name': '2 units out', + 'location_id': self.stock_location.id, + 'location_dest_id': self.customer_location.id, + 'product_id': self.product1.id, + 'product_uom': self.uom_unit.id, + 'product_uom_qty': 2.0, + }) + move2._action_confirm() + move2._action_assign() + + move2.write({'move_line_ids': [ + (0, None, { + 'product_id': self.product1.id, + 'qty_done': 1, + 'location_id': subloc1.id, + 'location_dest_id': self.supplier_location.id, + 'product_uom_id': self.uom_unit.id + }), + (0, None, { + 'product_id': self.product1.id, + 'qty_done': 1, + 'location_id': subloc2.id, + 'location_dest_id': self.supplier_location.id, + 'product_uom_id': self.uom_unit.id + }), + ]}) + move2._action_done() + self.assertEqual(move2.value, -10) +