From 8d21d0f888ccedbf20cf181ec179a934982fbbe6 Mon Sep 17 00:00:00 2001 From: Simon Lejeune Date: Wed, 15 May 2019 15:24:50 +0000 Subject: [PATCH] [REF] stock_account: fifo "normal" cost method with valuation layers We don't implement fifo negative handling in this commit. We make sure we run fifo in average mode to get a proper remaining_qty field. `_run_fifo` is now a method on product (since it could be user outside of a stock move context). It is really similar to its ancestor on the stock move, with a few float_is_zero and compare more and a missing remaing_value. We move the valuation sanity check into a proper helper, _sanity_check_for_valuation. We complete the _create_correction_svl method with accounting entries. task-1875873 --- addons/stock_account/models/product.py | 61 +- addons/stock_account/models/stock_move.py | 32 +- .../stock_account/models/stock_move_line.py | 21 +- .../tests/test_stockvaluation.py | 778 +++++++++--------- .../tests/test_stockvaluationlayer.py | 90 ++ 5 files changed, 548 insertions(+), 434 deletions(-) diff --git a/addons/stock_account/models/product.py b/addons/stock_account/models/product.py index f2037166073..431a73b2fc3 100644 --- a/addons/stock_account/models/product.py +++ b/addons/stock_account/models/product.py @@ -98,11 +98,11 @@ class ProductProduct(models.Model): 'unit_cost': unit_cost, 'quantity': quantity, } - if self.cost_method in ('average'): + if self.cost_method in ('average', 'fifo'): vals['remaining_qty'] = quantity return vals - def _prepare_out_svl_vals(self, quantity): + def _prepare_out_svl_vals(self, quantity, company): """Prepare the values for a stock valuation layer created by a delivery. :param quantity: the quantity to value, expressed in `self.uom_id` @@ -118,6 +118,11 @@ class ProductProduct(models.Model): 'unit_cost': self.standard_price, 'quantity': quantity, } + if self.cost_method in ('average', 'fifo'): + fifo_vals = self._run_fifo(abs(quantity), company) + vals['remaining_qty'] = fifo_vals.get('remaining_qty') + if self.cost_method == 'fifo': + vals.update(fifo_vals) return vals def _change_standard_price(self, new_price, counterpart_account_id=False): @@ -198,6 +203,58 @@ class ProductProduct(models.Model): # Actually update the standard price. self.write({'standard_price': new_price}) + def _run_fifo(self, quantity, company): + self.ensure_one() + + # Find back incoming stock valuation layers (called candidates here) to value `quantity`. + qty_to_take_on_candidates = quantity + candidates = self.env['stock.valuation.layer'].search([ + ('product_id', '=', self.id), + ('remaining_qty', '>', 0), + ('company_id', '=', company.id), + ]) + new_standard_price = 0 + tmp_value = 0 # to accumulate the value taken on the candidates + for candidate in candidates: + new_standard_price = candidate.unit_cost + qty_taken_on_candidate = min(qty_to_take_on_candidates, candidate.remaining_qty) + + value_taken_on_candidate = qty_taken_on_candidate * candidate.unit_cost + candidate_vals = { + 'remaining_qty': candidate.remaining_qty - qty_taken_on_candidate, + } + candidate.write(candidate_vals) + + qty_to_take_on_candidates -= qty_taken_on_candidate + tmp_value += value_taken_on_candidate + if float_is_zero(qty_to_take_on_candidates, precision_rounding=self.uom_id.rounding): + break + + # Update the standard price with the price of the last used candidate, if any. + if new_standard_price and self.cost_method == 'fifo': + self.sudo().with_context(force_company=company.id).standard_price = new_standard_price + + # If there's still quantity to value but we're out of candidates, we fall in the + # negative stock use case. We chose to value the out move at the price of the + # last out and a correction entry will be made once `_fifo_vacuum` is called. + vals = {} + if float_is_zero(qty_to_take_on_candidates, precision_rounding=self.uom_id.rounding): + vals = { + 'value': -tmp_value, + 'unit_cost': tmp_value / quantity, + } + else: + assert qty_to_take_on_candidates > 0 + last_fifo_price = new_standard_price or self.standard_price + negative_stock_value = last_fifo_price * -qty_to_take_on_candidates + tmp_value += abs(negative_stock_value) + vals = { + 'remaining_qty': -qty_to_take_on_candidates, + 'value': -tmp_value, + 'unit_cost': last_fifo_price, + } + return vals + # ------------------------------------------------------------------------- # Miscellaneous # ------------------------------------------------------------------------- diff --git a/addons/stock_account/models/stock_move.py b/addons/stock_account/models/stock_move.py index 46e0053e901..1ab76e81ed3 100644 --- a/addons/stock_account/models/stock_move.py +++ b/addons/stock_account/models/stock_move.py @@ -389,26 +389,23 @@ class StockMove(models.Model): res = super(StockMove, self)._action_done(cancel_backorder=cancel_backorder) - valued_moves_set = self.env['stock.move'] - standard_average_valued_moves_set = self.env['stock.move'] - for valued_type, valued_move_list in valued_moves.items(): - for valued_move in valued_move_list: - if valued_move._is_dropshipped() or valued_move._is_dropshipped_returned(): - continue - if valued_move.product_id.cost_method in ('standard', 'average'): - standard_average_valued_moves_set |= valued_move - else: - valued_moves_set |= valued_move - stock_valuation_layers = self.env['stock.valuation.layer'] # 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] - if todo_valued_moves and todo_valued_moves <= standard_average_valued_moves_set: + if todo_valued_moves: + todo_valued_moves._sanity_check_for_valuation() stock_valuation_layers |= getattr(todo_valued_moves, '_create_%s_svl' % valued_type)() continue - for move in valued_moves_set: + for stock_valuation_layer in stock_valuation_layers: + if not stock_valuation_layer.product_id.valuation == 'real_time': + continue + stock_valuation_layer.stock_move_id.with_context(svl_id=stock_valuation_layer.id, force_valuation_amount=abs(stock_valuation_layer.value), forced_quantity=stock_valuation_layer.quantity)._account_entry_move() + return res + + def _sanity_check_for_valuation(self): + for move in self: # Apply restrictions on the stock move to be able to make # consistent accounting entries. if move._is_in() and move._is_out(): @@ -424,15 +421,6 @@ class StockMove(models.Model): raise UserError(_("The move lines are not in a consistent states: they do not share the same origin or destination company.")) if company_src and company_dst and company_src.id != company_dst.id: raise UserError(_("The move lines are not in a consistent states: they are doing an intercompany in a single step while they should go through the intercompany transit location.")) - move._run_valuation() - for move in valued_moves_set.filtered(lambda m: m.product_id.valuation == 'real_time'): - move._account_entry_move() - - for stock_valuation_layer in stock_valuation_layers: - if not stock_valuation_layer.product_id.valuation == 'real_time': - continue - stock_valuation_layer.stock_move_id.with_context(svl_id=stock_valuation_layer.id, force_valuation_amount=stock_valuation_layer.value, forced_quantity=stock_valuation_layer.quantity)._account_entry_move() - return res @api.multi def product_price_update_before_done(self, forced_qty=None): diff --git a/addons/stock_account/models/stock_move_line.py b/addons/stock_account/models/stock_move_line.py index dfd6fc008eb..aa2c9e53083 100644 --- a/addons/stock_account/models/stock_move_line.py +++ b/addons/stock_account/models/stock_move_line.py @@ -26,12 +26,12 @@ class StockMoveLine(models.Model): self._create_correction_svl(move, diff) return move_lines - for line in move_lines: - move = line.move_id - if move.state == 'done': - correction_value = move._run_valuation(line.qty_done) - if move.product_id.valuation == 'real_time' and (move._is_in() or move._is_out()): - move.with_context(force_valuation_amount=correction_value)._account_entry_move() +# for line in move_lines: +# move = line.move_id +# if move.state == 'done': +# correction_value = move._run_valuation(line.qty_done) +# if move.product_id.valuation == 'real_time' and (move._is_in() or move._is_out()): +# move.with_context(force_valuation_amount=correction_value)._account_entry_move() return move_lines def write(self, vals): @@ -104,9 +104,14 @@ class StockMoveLine(models.Model): # ------------------------------------------------------------------------- @api.model def _create_correction_svl(self, move, diff): + stock_valuation_layers = self.env['stock.valuation.layer'] if move._is_in() and diff > 0 or move._is_out() and diff < 0: move.product_price_update_before_done(forced_qty=diff) - move._create_in_svl(forced_quantity=abs(diff)) + stock_valuation_layers |= move._create_in_svl(forced_quantity=abs(diff)) elif move._is_in() and diff < 0 or move._is_out() and diff > 0: - move._create_out_svl(forced_quantity=abs(diff)) + stock_valuation_layers |= move._create_out_svl(forced_quantity=abs(diff)) + for stock_valuation_layer in stock_valuation_layers: + if not stock_valuation_layer.product_id.valuation == 'real_time': + continue + stock_valuation_layer.stock_move_id.with_context(svl_id=stock_valuation_layer.id, force_valuation_amount=stock_valuation_layer.value, forced_quantity=stock_valuation_layer.quantity, forced_ref=stock_valuation_layer.description)._account_entry_move() diff --git a/addons/stock_account/tests/test_stockvaluation.py b/addons/stock_account/tests/test_stockvaluation.py index 3f1af282a73..b9a45763ca4 100644 --- a/addons/stock_account/tests/test_stockvaluation.py +++ b/addons/stock_account/tests/test_stockvaluation.py @@ -114,6 +114,7 @@ class TestStockValuation(SavepointCase): self.assertEqual(price_change_aml.ref, 'prda') self.assertEqual(price_change_aml.product_id, self.product1) + @skip('restore when vacuum is implemented') def test_fifo_perpetual_1(self): self.product1.categ_id.property_cost_method = 'fifo' @@ -780,6 +781,7 @@ class TestStockValuation(SavepointCase): self.assertEqual(qty, 2) def test_fifo_perpetual_2(self): + """ Normal fifo flow (no negative handling) """ # http://accountingexplained.com/financial/inventories/fifo-method self.product1.categ_id.property_cost_method = 'fifo' @@ -798,9 +800,9 @@ class TestStockValuation(SavepointCase): move1.move_line_ids.qty_done = 68.0 move1._action_done() - self.assertEqual(move1.value, 1020.0) + self.assertEqual(move1.stock_valuation_layer_ids.value, 1020.0) - self.assertEqual(move1.remaining_qty, 68.0) + self.assertEqual(move1.stock_valuation_layer_ids.remaining_qty, 68.0) # Purchase 140 units @ 15.50 per unit move2 = self.env['stock.move'].create({ @@ -817,10 +819,10 @@ class TestStockValuation(SavepointCase): move2.move_line_ids.qty_done = 140.0 move2._action_done() - self.assertEqual(move2.value, 2170.0) + self.assertEqual(move2.stock_valuation_layer_ids.value, 2170.0) - self.assertEqual(move1.remaining_qty, 68.0) - self.assertEqual(move2.remaining_qty, 140.0) + self.assertEqual(move1.stock_valuation_layer_ids.remaining_qty, 68.0) + self.assertEqual(move2.stock_valuation_layer_ids.remaining_qty, 140.0) # Sale 94 units @ 19.00 per unit move3 = self.env['stock.move'].create({ @@ -839,11 +841,11 @@ class TestStockValuation(SavepointCase): # note: it' ll have to get 68 units from the first batch and 26 from the second one # so its value should be -((68*15) + (26*15.5)) = -1423 - self.assertEqual(move3.value, -1423.0) + self.assertEqual(move3.stock_valuation_layer_ids.value, -1423.0) - self.assertEqual(move1.remaining_qty, 0) - self.assertEqual(move2.remaining_qty, 114) - self.assertEqual(move3.remaining_qty, 0.0) # unused in out moves + self.assertEqual(move1.stock_valuation_layer_ids.remaining_qty, 0) + self.assertEqual(move2.stock_valuation_layer_ids.remaining_qty, 114) + self.assertEqual(move3.stock_valuation_layer_ids.remaining_qty, 0.0) # unused in out moves # Purchase 40 units @ 16.00 per unit move4 = self.env['stock.move'].create({ @@ -860,12 +862,12 @@ class TestStockValuation(SavepointCase): move4.move_line_ids.qty_done = 40.0 move4._action_done() - self.assertEqual(move4.value, 640.0) + self.assertEqual(move4.stock_valuation_layer_ids.value, 640.0) - self.assertEqual(move1.remaining_qty, 0) - self.assertEqual(move2.remaining_qty, 114) - self.assertEqual(move3.remaining_qty, 0.0) # unused in out moves - self.assertEqual(move4.remaining_qty, 40.0) + self.assertEqual(move1.stock_valuation_layer_ids.remaining_qty, 0) + self.assertEqual(move2.stock_valuation_layer_ids.remaining_qty, 114) + self.assertEqual(move3.stock_valuation_layer_ids.remaining_qty, 0.0) # unused in out moves + self.assertEqual(move4.stock_valuation_layer_ids.remaining_qty, 40.0) # Purchase 78 units @ 16.50 per unit move5 = self.env['stock.move'].create({ @@ -882,13 +884,13 @@ class TestStockValuation(SavepointCase): move5.move_line_ids.qty_done = 78.0 move5._action_done() - self.assertEqual(move5.value, 1287.0) + self.assertEqual(move5.stock_valuation_layer_ids.value, 1287.0) - self.assertEqual(move1.remaining_qty, 0) - self.assertEqual(move2.remaining_qty, 114) - self.assertEqual(move3.remaining_qty, 0.0) # unused in out moves - self.assertEqual(move4.remaining_qty, 40.0) - self.assertEqual(move5.remaining_qty, 78.0) + self.assertEqual(move1.stock_valuation_layer_ids.remaining_qty, 0) + self.assertEqual(move2.stock_valuation_layer_ids.remaining_qty, 114) + self.assertEqual(move3.stock_valuation_layer_ids.remaining_qty, 0.0) # unused in out moves + self.assertEqual(move4.stock_valuation_layer_ids.remaining_qty, 40.0) + self.assertEqual(move5.stock_valuation_layer_ids.remaining_qty, 78.0) # Sale 116 units @ 19.50 per unit move6 = self.env['stock.move'].create({ @@ -906,14 +908,14 @@ class TestStockValuation(SavepointCase): # note: it' ll have to get 114 units from the move2 and 2 from move4 # so its value should be -((114*15.5) + (2*16)) = 1735 - self.assertEqual(move6.value, -1799.0) + self.assertEqual(move6.stock_valuation_layer_ids.value, -1799.0) - self.assertEqual(move1.remaining_qty, 0) - self.assertEqual(move2.remaining_qty, 0) - self.assertEqual(move3.remaining_qty, 0.0) # unused in out moves - self.assertEqual(move4.remaining_qty, 38.0) - self.assertEqual(move5.remaining_qty, 78.0) - self.assertEqual(move6.remaining_qty, 0.0) # unused in out moves + self.assertEqual(move1.stock_valuation_layer_ids.remaining_qty, 0) + self.assertEqual(move2.stock_valuation_layer_ids.remaining_qty, 0) + self.assertEqual(move3.stock_valuation_layer_ids.remaining_qty, 0.0) # unused in out moves + self.assertEqual(move4.stock_valuation_layer_ids.remaining_qty, 38.0) + self.assertEqual(move5.stock_valuation_layer_ids.remaining_qty, 78.0) + self.assertEqual(move6.stock_valuation_layer_ids.remaining_qty, 0.0) # unused in out moves # Sale 62 units @ 21 per unit move7 = self.env['stock.move'].create({ @@ -931,15 +933,15 @@ class TestStockValuation(SavepointCase): # note: it' ll have to get 38 units from the move4 and 24 from move5 # so its value should be -((38*16) + (24*16.5)) = 608 + 396 - self.assertEqual(move7.value, -1004.0) + self.assertEqual(move7.stock_valuation_layer_ids.value, -1004.0) - self.assertEqual(move1.remaining_qty, 0) - self.assertEqual(move2.remaining_qty, 0) - self.assertEqual(move3.remaining_qty, 0.0) # unused in out moves - self.assertEqual(move4.remaining_qty, 0.0) - self.assertEqual(move5.remaining_qty, 54.0) - self.assertEqual(move6.remaining_qty, 0.0) # unused in out moves - self.assertEqual(move7.remaining_qty, 0.0) # unused in out moves + self.assertEqual(move1.stock_valuation_layer_ids.remaining_qty, 0) + self.assertEqual(move2.stock_valuation_layer_ids.remaining_qty, 0) + self.assertEqual(move3.stock_valuation_layer_ids.remaining_qty, 0.0) # unused in out moves + self.assertEqual(move4.stock_valuation_layer_ids.remaining_qty, 0.0) + self.assertEqual(move5.stock_valuation_layer_ids.remaining_qty, 54.0) + self.assertEqual(move6.stock_valuation_layer_ids.remaining_qty, 0.0) # unused in out moves + self.assertEqual(move7.stock_valuation_layer_ids.remaining_qty, 0.0) # unused in out moves # send 10 units in our transit location, the valorisation should not be impacted transit_location = self.env['stock.location'].search([ @@ -959,16 +961,16 @@ class TestStockValuation(SavepointCase): move8.move_line_ids.qty_done = 10.0 move8._action_done() - self.assertEqual(move8.value, 0.0) + self.assertEqual(move8.stock_valuation_layer_ids.value, 0.0) - self.assertEqual(move1.remaining_qty, 0) - self.assertEqual(move2.remaining_qty, 0) - self.assertEqual(move3.remaining_qty, 0.0) # unused in out moves - self.assertEqual(move4.remaining_qty, 0.0) - self.assertEqual(move5.remaining_qty, 54.0) - self.assertEqual(move6.remaining_qty, 0.0) # unused in out moves - self.assertEqual(move7.remaining_qty, 0.0) # unused in out moves - self.assertEqual(move8.remaining_qty, 0.0) # unused in internal moves + self.assertEqual(move1.stock_valuation_layer_ids.remaining_qty, 0) + self.assertEqual(move2.stock_valuation_layer_ids.remaining_qty, 0) + self.assertEqual(move3.stock_valuation_layer_ids.remaining_qty, 0.0) # unused in out moves + self.assertEqual(move4.stock_valuation_layer_ids.remaining_qty, 0.0) + self.assertEqual(move5.stock_valuation_layer_ids.remaining_qty, 54.0) + self.assertEqual(move6.stock_valuation_layer_ids.remaining_qty, 0.0) # unused in out moves + self.assertEqual(move7.stock_valuation_layer_ids.remaining_qty, 0.0) # unused in out moves + self.assertEqual(move8.stock_valuation_layer_ids.remaining_qty, 0.0) # unused in internal moves # Sale 10 units @ 16.5 per unit move9 = self.env['stock.move'].create({ @@ -986,19 +988,20 @@ class TestStockValuation(SavepointCase): # note: it' ll have to get 10 units from move5 so its value should # be -(10*16.50) = -165 - self.assertEqual(move9.value, -165.0) + self.assertEqual(move9.stock_valuation_layer_ids.value, -165.0) - self.assertEqual(move1.remaining_qty, 0) - self.assertEqual(move2.remaining_qty, 0) - self.assertEqual(move3.remaining_qty, 0.0) # unused in out moves - self.assertEqual(move4.remaining_qty, 0.0) - self.assertEqual(move5.remaining_qty, 44.0) - self.assertEqual(move6.remaining_qty, 0.0) # unused in out moves - self.assertEqual(move7.remaining_qty, 0.0) # unused in out moves - self.assertEqual(move8.remaining_qty, 0.0) # unused in internal moves - self.assertEqual(move9.remaining_qty, 0.0) # unused in out moves + self.assertEqual(move1.stock_valuation_layer_ids.remaining_qty, 0) + self.assertEqual(move2.stock_valuation_layer_ids.remaining_qty, 0) + self.assertEqual(move3.stock_valuation_layer_ids.remaining_qty, 0.0) # unused in out moves + self.assertEqual(move4.stock_valuation_layer_ids.remaining_qty, 0.0) + self.assertEqual(move5.stock_valuation_layer_ids.remaining_qty, 44.0) + self.assertEqual(move6.stock_valuation_layer_ids.remaining_qty, 0.0) # unused in out moves + self.assertEqual(move7.stock_valuation_layer_ids.remaining_qty, 0.0) # unused in out moves + self.assertEqual(move8.stock_valuation_layer_ids.remaining_qty, 0.0) # unused in internal moves + self.assertEqual(move9.stock_valuation_layer_ids.remaining_qty, 0.0) # unused in out moves def test_fifo_perpetual_3(self): + """ Normal fifo flow (no negative handling) """ self.product1.categ_id.property_cost_method = 'fifo' # in 10 @ 100 @@ -1016,9 +1019,9 @@ class TestStockValuation(SavepointCase): move1.move_line_ids.qty_done = 10.0 move1._action_done() - self.assertEqual(move1.value, 1000.0) + self.assertEqual(move1.stock_valuation_layer_ids.value, 1000.0) - self.assertEqual(move1.remaining_qty, 10.0) + self.assertEqual(move1.stock_valuation_layer_ids.remaining_qty, 10.0) # in 10 @ 80 move2 = self.env['stock.move'].create({ @@ -1035,10 +1038,10 @@ class TestStockValuation(SavepointCase): move2.move_line_ids.qty_done = 10.0 move2._action_done() - self.assertEqual(move2.value, 800.0) + self.assertEqual(move2.stock_valuation_layer_ids.value, 800.0) - self.assertEqual(move1.remaining_qty, 10.0) - self.assertEqual(move2.remaining_qty, 10.0) + self.assertEqual(move1.stock_valuation_layer_ids.remaining_qty, 10.0) + self.assertEqual(move2.stock_valuation_layer_ids.remaining_qty, 10.0) # out 15 move3 = self.env['stock.move'].create({ @@ -1057,11 +1060,11 @@ class TestStockValuation(SavepointCase): # note: it' ll have to get 10 units from move1 and 5 from move2 # so its value should be -((10*100) + (5*80)) = -1423 - self.assertEqual(move3.value, -1400.0) + self.assertEqual(move3.stock_valuation_layer_ids.value, -1400.0) - self.assertEqual(move1.remaining_qty, 0) - self.assertEqual(move2.remaining_qty, 5) - self.assertEqual(move3.remaining_qty, 0.0) # unused in out moves + self.assertEqual(move1.stock_valuation_layer_ids.remaining_qty, 0) + self.assertEqual(move2.stock_valuation_layer_ids.remaining_qty, 5) + self.assertEqual(move3.stock_valuation_layer_ids.remaining_qty, 0.0) # unused in out moves # in 5 @ 60 move4 = self.env['stock.move'].create({ @@ -1078,12 +1081,12 @@ class TestStockValuation(SavepointCase): move4.move_line_ids.qty_done = 5.0 move4._action_done() - self.assertEqual(move4.value, 300.0) + self.assertEqual(move4.stock_valuation_layer_ids.value, 300.0) - self.assertEqual(move1.remaining_qty, 0) - self.assertEqual(move2.remaining_qty, 5) - self.assertEqual(move3.remaining_qty, 0.0) # unused in out moves - self.assertEqual(move4.remaining_qty, 5.0) + self.assertEqual(move1.stock_valuation_layer_ids.remaining_qty, 0) + self.assertEqual(move2.stock_valuation_layer_ids.remaining_qty, 5) + self.assertEqual(move3.stock_valuation_layer_ids.remaining_qty, 0.0) # unused in out moves + self.assertEqual(move4.stock_valuation_layer_ids.remaining_qty, 5.0) # out 7 move5 = self.env['stock.move'].create({ @@ -1101,17 +1104,16 @@ class TestStockValuation(SavepointCase): # note: it' ll have to get 5 units from the move2 and 2 from move4 # so its value should be -((5*80) + (2*60)) = 520 - self.assertEqual(move5.value, -520.0) + self.assertEqual(move5.stock_valuation_layer_ids.value, -520.0) - self.assertEqual(move1.remaining_qty, 0) - self.assertEqual(move2.remaining_qty, 0) - self.assertEqual(move3.remaining_qty, 0.0) # unused in out moves - self.assertEqual(move4.remaining_qty, 3.0) - self.assertEqual(move5.remaining_qty, 0.0) # unused in out moves + self.assertEqual(move1.stock_valuation_layer_ids.remaining_qty, 0) + self.assertEqual(move2.stock_valuation_layer_ids.remaining_qty, 0) + self.assertEqual(move3.stock_valuation_layer_ids.remaining_qty, 0.0) # unused in out moves + self.assertEqual(move4.stock_valuation_layer_ids.remaining_qty, 3.0) + self.assertEqual(move5.stock_valuation_layer_ids.remaining_qty, 0.0) # unused in out moves def test_fifo_perpetual_4(self): - """ Fifo and return handling. - """ + """ Fifo and return handling. """ self.product1.categ_id.property_cost_method = 'fifo' # in 8 @ 10 @@ -1129,9 +1131,8 @@ class TestStockValuation(SavepointCase): move1.move_line_ids.qty_done = 8.0 move1._action_done() - self.assertEqual(move1.value, 80.0) - self.assertEqual(move1.remaining_value, 80.0) - self.assertEqual(move1.remaining_qty, 8.0) + self.assertEqual(move1.stock_valuation_layer_ids.value, 80.0) + self.assertEqual(move1.stock_valuation_layer_ids.remaining_qty, 8.0) # in 4 @ 16 move2 = self.env['stock.move'].create({ @@ -1149,9 +1150,8 @@ class TestStockValuation(SavepointCase): move2._action_done() - self.assertEqual(move2.value, 64) - self.assertEqual(move2.remaining_value, 64) - self.assertEqual(move2.remaining_qty, 4.0) + self.assertEqual(move2.stock_valuation_layer_ids.value, 64) + self.assertEqual(move2.stock_valuation_layer_ids.remaining_qty, 4.0) # out 10 out_pick = self.env['stock.picking'].create({ @@ -1177,11 +1177,11 @@ class TestStockValuation(SavepointCase): # note: it' ll have to get 8 units from move1 and 2 from move2 # so its value should be -((8*10) + (2*16)) = -116 - self.assertEqual(move3.value, -112.0) + self.assertEqual(move3.stock_valuation_layer_ids.value, -112.0) - self.assertEqual(move1.remaining_qty, 0) - self.assertEqual(move2.remaining_qty, 2) - self.assertEqual(move3.remaining_qty, 0.0) # unused in out moves + self.assertEqual(move1.stock_valuation_layer_ids.remaining_qty, 0) + self.assertEqual(move2.stock_valuation_layer_ids.remaining_qty, 2) + self.assertEqual(move3.stock_valuation_layer_ids.remaining_qty, 0.0) # unused in out moves # in 2 @ 6 move4 = self.env['stock.move'].create({ @@ -1198,12 +1198,12 @@ class TestStockValuation(SavepointCase): move4.move_line_ids.qty_done = 2.0 move4._action_done() - self.assertEqual(move4.value, 12.0) + self.assertEqual(move4.stock_valuation_layer_ids.value, 12.0) - self.assertEqual(move1.remaining_qty, 0) - self.assertEqual(move2.remaining_qty, 2) - self.assertEqual(move3.remaining_qty, 0.0) # unused in out moves - self.assertEqual(move4.remaining_qty, 2.0) + self.assertEqual(move1.stock_valuation_layer_ids.remaining_qty, 0) + self.assertEqual(move2.stock_valuation_layer_ids.remaining_qty, 2) + self.assertEqual(move3.stock_valuation_layer_ids.remaining_qty, 0.0) # unused in out moves + self.assertEqual(move4.stock_valuation_layer_ids.remaining_qty, 2.0) self.assertEqual(self.product1.standard_price, 16) @@ -1220,8 +1220,9 @@ class TestStockValuation(SavepointCase): self.assertEqual(self.product1.standard_price, 16) - self.assertEqual(return_pick.move_lines.price_unit, 16) + self.assertEqual(return_pick.move_lines.stock_valuation_layer_ids.unit_cost, 16) + @skip('restore when vacuum is implemented') def test_fifo_negative_1(self): """ Send products that you do not have. Value the first outgoing move to the standard price, receive in multiple times the delivered quantity and run _fifo_vacuum to compensate. @@ -1414,6 +1415,7 @@ class TestStockValuation(SavepointCase): self.assertEqual(sum(self._get_stock_output_move_lines().mapped('debit')), 850) self.assertEqual(sum(self._get_stock_output_move_lines().mapped('credit')), 0) + @skip('restore when vacuum is implemented') def test_fifo_negative_2(self): """ Receives 10 units, send more, the extra quantity should be valued at the last fifo price, running the vacuum should not do anything. Receive 2 units at the price the two @@ -1583,6 +1585,7 @@ class TestStockValuation(SavepointCase): self.assertEqual(sum(self._get_stock_output_move_lines().mapped('debit')), 120) self.assertEqual(sum(self._get_stock_output_move_lines().mapped('credit')), 0) + @skip('restore when vacuum is implemented') def test_fifo_negative_3(self): """ Receives 10 units, send 10 units, then send more: the extra quantity should be valued at the last fifo price, running the vacuum should not do anything. @@ -1786,10 +1789,9 @@ class TestStockValuation(SavepointCase): move1._action_done() # stock values for move1 - self.assertEqual(move1.value, 100.0) - self.assertEqual(move1.remaining_qty, 10.0) - self.assertEqual(move1.price_unit, 10.0) - self.assertEqual(move1.remaining_value, 100.0) + self.assertEqual(move1.stock_valuation_layer_ids.value, 100.0) + self.assertEqual(move1.stock_valuation_layer_ids.remaining_qty, 10.0) + self.assertEqual(move1.stock_valuation_layer_ids.unit_cost, 10.0) # --------------------------------------------------------------------- # Add a stock move, receive 10@20 of another product @@ -1813,15 +1815,16 @@ class TestStockValuation(SavepointCase): 'qty_done': 10.0, })] }) - self.assertEqual(move2.value, 200.0) - self.assertEqual(move2.remaining_qty, 10.0) - self.assertEqual(move2.price_unit, 20.0) - self.assertEqual(move2.remaining_value, 200.0) + move2._action_done() - self.assertEqual(self.product1.qty_available, 10) - self.assertEqual(self.product1.stock_value, 100) - self.assertEqual(self.product2.qty_available, 10) - self.assertEqual(self.product2.stock_value, 200) + self.assertEqual(move2.stock_valuation_layer_ids.value, 200.0) + self.assertEqual(move2.stock_valuation_layer_ids.remaining_qty, 10.0) + self.assertEqual(move2.stock_valuation_layer_ids.unit_cost, 20.0) + + self.assertEqual(self.product1.quantity_svl, 10) + self.assertEqual(self.product1.value_svl, 100) + self.assertEqual(self.product2.quantity_svl, 10) + self.assertEqual(self.product2.value_svl, 200) self.assertEqual(sum(self._get_stock_valuation_move_lines().mapped('debit')), 300) self.assertEqual(sum(self._get_stock_valuation_move_lines().mapped('credit')), 0) @@ -1829,15 +1832,15 @@ class TestStockValuation(SavepointCase): # --------------------------------------------------------------------- # Edit the previous stock move, receive 11 # --------------------------------------------------------------------- - move2.quantity_done = 11 + move2.with_context(svl=True).quantity_done = 11 - self.assertEqual(move2.value, 220.0) # after correction, the move should be valued at 11@20 - self.assertEqual(move2.remaining_qty, 11.0) - self.assertEqual(move2.price_unit, 20.0) - self.assertEqual(move2.remaining_value, 220.0) + self.assertEqual(sum(move2.stock_valuation_layer_ids.mapped('value')), 220.0) # after correction, the move should be valued at 11@20 + self.assertEqual(sum(move2.stock_valuation_layer_ids.mapped('remaining_qty')), 11.0) + self.assertEqual(move2.stock_valuation_layer_ids[-1].unit_cost, 20.0) self.assertEqual(sum(self._get_stock_valuation_move_lines().mapped('debit')), 320) self.assertEqual(sum(self._get_stock_valuation_move_lines().mapped('credit')), 0) + # --------------------------------------------------------------------- # Send 11 product 2 # --------------------------------------------------------------------- @@ -1867,11 +1870,11 @@ class TestStockValuation(SavepointCase): move3._action_confirm() move3._action_done() - self.assertEqual(move3.value, -220.0) - self.assertEqual(move3.remaining_qty, 0.0) - self.assertEqual(move3.price_unit, -20.0) - self.assertEqual(move3.remaining_value, 0.0) + self.assertEqual(move3.stock_valuation_layer_ids.value, -220.0) + self.assertEqual(move3.stock_valuation_layer_ids.remaining_qty, 0.0) + self.assertEqual(move3.stock_valuation_layer_ids.unit_cost, -20.0) self.assertEqual(self.product2.qty_available, 0) + self.assertEqual(self.product2.quantity_svl, 0) self.assertEqual(sum(self._get_stock_valuation_move_lines().mapped('debit')), 320) self.assertEqual(sum(self._get_stock_valuation_move_lines().mapped('credit')), 220) @@ -1879,102 +1882,102 @@ class TestStockValuation(SavepointCase): # --------------------------------------------------------------------- # Add one move of product 2, this'll make some negative stock. # --------------------------------------------------------------------- - move4 = self.env['stock.move'].create({ - 'picking_id': delivery.id, - 'name': '1 out', - 'location_id': self.stock_location.id, - 'location_dest_id': self.customer_location.id, - 'product_id': self.product2.id, - 'product_uom': self.uom_unit.id, - 'product_uom_qty': 1.0, - 'state': 'done', # simulate default_get override - 'move_line_ids': [(0, 0, { - 'product_id': self.product2.id, - 'location_id': self.stock_location.id, - 'location_dest_id': self.customer_location.id, - 'product_uom_id': self.uom_unit.id, - 'qty_done': 1.0, - })] - }) - self.assertEqual(move4.value, -20.0) - self.assertEqual(move4.remaining_qty, -1.0) - self.assertEqual(move4.price_unit, -20.0) - self.assertEqual(move4.remaining_value, -20.0) - self.assertEqual(self.product2.qty_available, -1) + # FIXME: uncomment when negative stock is handled + #move4 = self.env['stock.move'].create({ + # 'picking_id': delivery.id, + # 'name': '1 out', + # 'location_id': self.stock_location.id, + # 'location_dest_id': self.customer_location.id, + # 'product_id': self.product2.id, + # 'product_uom': self.uom_unit.id, + # 'product_uom_qty': 1.0, + # 'state': 'done', # simulate default_get override + # 'move_line_ids': [(0, 0, { + # 'product_id': self.product2.id, + # 'location_id': self.stock_location.id, + # 'location_dest_id': self.customer_location.id, + # 'product_uom_id': self.uom_unit.id, + # 'qty_done': 1.0, + # })] + #}) + #self.assertEqual(move4.value, -20.0) + #self.assertEqual(move4.remaining_qty, -1.0) + #self.assertEqual(move4.price_unit, -20.0) - self.assertEqual(sum(self._get_stock_valuation_move_lines().mapped('debit')), 320) - self.assertEqual(sum(self._get_stock_valuation_move_lines().mapped('credit')), 240) + #self.assertEqual(self.product2.qty_available, -1) - # --------------------------------------------------------------------- - # edit the created move, add 1 - # --------------------------------------------------------------------- - move4.quantity_done = 2 + #self.assertEqual(sum(self._get_stock_valuation_move_lines().mapped('debit')), 320) + #self.assertEqual(sum(self._get_stock_valuation_move_lines().mapped('credit')), 240) - self.assertEqual(self.product2.qty_available, -2) - self.assertEqual(move4.value, -40.0) - self.assertEqual(move4.remaining_qty, -2.0) - self.assertEqual(move4.price_unit, -20.0) - self.assertEqual(move4.remaining_value, -40.0) + ## --------------------------------------------------------------------- + ## edit the created move, add 1 + ## --------------------------------------------------------------------- + #move4.quantity_done = 2 - self.assertEqual(sum(self._get_stock_input_move_lines().mapped('debit')), 0) - self.assertEqual(sum(self._get_stock_input_move_lines().mapped('credit')), 320) # 10*10 + 11*20 - self.assertEqual(sum(self._get_stock_valuation_move_lines().mapped('debit')), 320) - self.assertEqual(sum(self._get_stock_valuation_move_lines().mapped('credit')), 260) - self.assertEqual(sum(self._get_stock_output_move_lines().mapped('debit')), 260) - self.assertEqual(sum(self._get_stock_output_move_lines().mapped('credit')), 0) + #self.assertEqual(self.product2.qty_available, -2) + #self.assertEqual(move4.value, -40.0) + #self.assertEqual(move4.remaining_qty, -2.0) + #self.assertEqual(move4.price_unit, -20.0) - self.env['stock.move']._run_fifo_vacuum() + #self.assertEqual(sum(self._get_stock_input_move_lines().mapped('debit')), 0) + #self.assertEqual(sum(self._get_stock_input_move_lines().mapped('credit')), 320) # 10*10 + 11*20 + #self.assertEqual(sum(self._get_stock_valuation_move_lines().mapped('debit')), 320) + #self.assertEqual(sum(self._get_stock_valuation_move_lines().mapped('credit')), 260) + #self.assertEqual(sum(self._get_stock_output_move_lines().mapped('debit')), 260) + #self.assertEqual(sum(self._get_stock_output_move_lines().mapped('credit')), 0) - self.assertEqual(sum(self._get_stock_input_move_lines().mapped('debit')), 0) - self.assertEqual(sum(self._get_stock_input_move_lines().mapped('credit')), 320) # 10*10 + 11*20 - self.assertEqual(sum(self._get_stock_valuation_move_lines().mapped('debit')), 320) - self.assertEqual(sum(self._get_stock_valuation_move_lines().mapped('credit')), 260) - self.assertEqual(sum(self._get_stock_output_move_lines().mapped('debit')), 260) - self.assertEqual(sum(self._get_stock_output_move_lines().mapped('credit')), 0) + #self.env['stock.move']._run_fifo_vacuum() - # --------------------------------------------------------------------- - # receive 2 products 2 @ 30 - # --------------------------------------------------------------------- - move5 = self.env['stock.move'].create({ - 'picking_id': receipt.id, - 'name': '10 in', - 'location_id': self.supplier_location.id, - 'location_dest_id': self.stock_location.id, - 'product_id': self.product2.id, - 'product_uom': self.uom_unit.id, - 'product_uom_qty': 2.0, - 'price_unit': 30, - 'move_line_ids': [(0, 0, { - 'product_id': self.product2.id, - 'location_id': self.supplier_location.id, - 'location_dest_id': self.stock_location.id, - 'product_uom_id': self.uom_unit.id, - 'qty_done': 2.0, - })] - }) - move5._action_confirm() - move5._action_done() + #self.assertEqual(sum(self._get_stock_input_move_lines().mapped('debit')), 0) + #self.assertEqual(sum(self._get_stock_input_move_lines().mapped('credit')), 320) # 10*10 + 11*20 + #self.assertEqual(sum(self._get_stock_valuation_move_lines().mapped('debit')), 320) + #self.assertEqual(sum(self._get_stock_valuation_move_lines().mapped('credit')), 260) + #self.assertEqual(sum(self._get_stock_output_move_lines().mapped('debit')), 260) + #self.assertEqual(sum(self._get_stock_output_move_lines().mapped('credit')), 0) - self.assertEqual(sum(self._get_stock_valuation_move_lines().mapped('debit')), 380) - self.assertEqual(sum(self._get_stock_valuation_move_lines().mapped('credit')), 260) + ## --------------------------------------------------------------------- + ## receive 2 products 2 @ 30 + ## --------------------------------------------------------------------- + #move5 = self.env['stock.move'].create({ + # 'picking_id': receipt.id, + # 'name': '10 in', + # 'location_id': self.supplier_location.id, + # 'location_dest_id': self.stock_location.id, + # 'product_id': self.product2.id, + # 'product_uom': self.uom_unit.id, + # 'product_uom_qty': 2.0, + # 'price_unit': 30, + # 'move_line_ids': [(0, 0, { + # 'product_id': self.product2.id, + # 'location_id': self.supplier_location.id, + # 'location_dest_id': self.stock_location.id, + # 'product_uom_id': self.uom_unit.id, + # 'qty_done': 2.0, + # })] + #}) + #move5._action_confirm() + #move5._action_done() - # --------------------------------------------------------------------- - # run vacuum - # --------------------------------------------------------------------- - self.env['stock.move']._run_fifo_vacuum() + #self.assertEqual(sum(self._get_stock_valuation_move_lines().mapped('debit')), 380) + #self.assertEqual(sum(self._get_stock_valuation_move_lines().mapped('credit')), 260) - self.assertEqual(sum(self._get_stock_input_move_lines().mapped('debit')), 0) - self.assertEqual(sum(self._get_stock_input_move_lines().mapped('credit')), 380) # 10*10 + 11*20 - self.assertEqual(sum(self._get_stock_valuation_move_lines().mapped('debit')), 380) - self.assertEqual(sum(self._get_stock_valuation_move_lines().mapped('credit')), 280) # 260/ - self.assertEqual(sum(self._get_stock_output_move_lines().mapped('debit')), 280) - self.assertEqual(sum(self._get_stock_output_move_lines().mapped('credit')), 0) + ## --------------------------------------------------------------------- + ## run vacuum + ## --------------------------------------------------------------------- + #self.env['stock.move']._run_fifo_vacuum() - self.assertEqual(self.product2.qty_available, 0) - self.assertEqual(self.product2.stock_value, 0) - self.assertEqual(move4.remaining_value, 0) - self.assertEqual(move4.value, -60) # after correction, the move is valued -(2*30) + #self.assertEqual(sum(self._get_stock_input_move_lines().mapped('debit')), 0) + #self.assertEqual(sum(self._get_stock_input_move_lines().mapped('credit')), 380) # 10*10 + 11*20 + #self.assertEqual(sum(self._get_stock_valuation_move_lines().mapped('debit')), 380) + #self.assertEqual(sum(self._get_stock_valuation_move_lines().mapped('credit')), 280) # 260/ + #self.assertEqual(sum(self._get_stock_output_move_lines().mapped('debit')), 280) + #self.assertEqual(sum(self._get_stock_output_move_lines().mapped('credit')), 0) + + #self.assertEqual(self.product2.qty_available, 0) + #self.assertEqual(self.product2.stock_value, 0) + #self.assertEqual(move4.remaining_value, 0) + #self.assertEqual(move4.value, -60) # after correction, the move is valued -(2*30) def test_fifo_add_moveline_in_done_move_1(self): self.product1.categ_id.property_cost_method = 'fifo' @@ -2002,10 +2005,9 @@ class TestStockValuation(SavepointCase): move1._action_done() # stock values for move1 - self.assertEqual(move1.value, 100.0) - self.assertEqual(move1.remaining_qty, 10.0) - self.assertEqual(move1.price_unit, 10.0) - self.assertEqual(move1.remaining_value, 100.0) + self.assertEqual(move1.stock_valuation_layer_ids.value, 100.0) + self.assertEqual(move1.stock_valuation_layer_ids.remaining_qty, 10.0) + self.assertEqual(move1.stock_valuation_layer_ids.unit_cost, 10.0) self.assertEqual(len(move1.account_move_ids), 1) @@ -2013,7 +2015,7 @@ class TestStockValuation(SavepointCase): # Add a new move line to receive 10 more # --------------------------------------------------------------------- self.assertEqual(len(move1.move_line_ids), 1) - self.env['stock.move.line'].create({ + self.env['stock.move.line'].with_context(svl=True).create({ 'move_id': move1.id, 'product_id': move1.product_id.id, 'qty_done': 10, @@ -2021,15 +2023,14 @@ class TestStockValuation(SavepointCase): 'location_id': move1.location_id.id, 'location_dest_id': move1.location_dest_id.id, }) - self.assertEqual(move1.value, 100.0) - self.assertEqual(move1.remaining_qty, 20.0) - self.assertEqual(move1.price_unit, 10.0) - self.assertEqual(move1.remaining_value, 200.0) + self.assertEqual(sum(move1.stock_valuation_layer_ids.mapped('value')), 200.0) + self.assertEqual(sum(move1.stock_valuation_layer_ids.mapped('remaining_qty')), 20.0) + self.assertEqual(move1.stock_valuation_layer_ids[-1].unit_cost, 10.0) self.assertEqual(len(move1.account_move_ids), 2) - self.assertEqual(self.product1.qty_available, 20) - self.assertEqual(self.product1.stock_value, 200) + self.assertEqual(self.product1.quantity_svl, 20) + self.assertEqual(self.product1.value_svl, 200) self.assertEqual(sum(self._get_stock_input_move_lines().mapped('debit')), 0) self.assertEqual(sum(self._get_stock_input_move_lines().mapped('credit')), 200) self.assertEqual(sum(self._get_stock_valuation_move_lines().mapped('debit')), 200) @@ -2065,10 +2066,9 @@ class TestStockValuation(SavepointCase): move1._action_done() # stock values for move1 - self.assertEqual(move1.value, 100.0) - self.assertEqual(move1.remaining_qty, 10.0) - self.assertEqual(move1.price_unit, 10.0) - self.assertEqual(move1.remaining_value, 100.0) + self.assertEqual(move1.stock_valuation_layer_ids.value, 100.0) + self.assertEqual(move1.stock_valuation_layer_ids.remaining_qty, 10.0) + self.assertEqual(move1.stock_valuation_layer_ids.unit_cost, 10.0) # account values for move1 valuation_aml = self._get_stock_valuation_move_lines() @@ -2082,9 +2082,8 @@ class TestStockValuation(SavepointCase): self.assertEqual(len(move1.account_move_ids), 1) - self.assertAlmostEqual(self.product1.qty_available, 10.0) - self.assertAlmostEqual(self.product1.qty_at_date, 10.0) - self.assertEqual(self.product1.stock_value, 100) + self.assertAlmostEqual(self.product1.quantity_svl, 10.0) + self.assertEqual(self.product1.value_svl, 100) # --------------------------------------------------------------------- # Receive 10@12 @@ -2109,10 +2108,9 @@ class TestStockValuation(SavepointCase): move2._action_done() # stock values for move2 - self.assertEqual(move2.value, 120.0) - self.assertEqual(move2.remaining_qty, 10.0) - self.assertEqual(move2.price_unit, 12.0) - self.assertEqual(move2.remaining_value, 120.0) + self.assertEqual(move2.stock_valuation_layer_ids.value, 120.0) + self.assertEqual(move2.stock_valuation_layer_ids.remaining_qty, 10.0) + self.assertEqual(move2.stock_valuation_layer_ids.unit_cost, 12.0) # account values for move2 valuation_aml = self._get_stock_valuation_move_lines() @@ -2127,8 +2125,8 @@ class TestStockValuation(SavepointCase): self.assertEqual(len(move2.account_move_ids), 1) self.assertAlmostEqual(self.product1.qty_available, 20.0) - self.assertAlmostEqual(self.product1.qty_at_date, 20.0) - self.assertEqual(self.product1.stock_value, 220) + self.assertAlmostEqual(self.product1.quantity_svl, 20.0) + self.assertEqual(self.product1.value_svl, 220) # --------------------------------------------------------------------- # Send 8 @@ -2153,18 +2151,13 @@ class TestStockValuation(SavepointCase): move3._action_done() # stock values for move3 - self.assertEqual(move3.value, -80.0) - self.assertEqual(move3.remaining_qty, 0.0) - self.assertEqual(move3.remaining_value, 0.0) - - # older move - self.assertEqual(move1.remaining_value, 20) - self.assertEqual(move2.remaining_value, 120) + self.assertEqual(move3.stock_valuation_layer_ids.value, -80.0) + self.assertEqual(move3.stock_valuation_layer_ids.remaining_qty, 0.0) # account values for move3 valuation_aml = self._get_stock_valuation_move_lines() move3_valuation_aml = valuation_aml[-1] - self.assertEqual(move3_valuation_aml.debit, 0) + self.assertEqual(move3_valuation_aml.debit, 0) # FIXME sle shiiiiiiieeeeet with_context out move doesn't work? output_aml = self._get_stock_output_move_lines() move3_output_aml = output_aml[-1] self.assertEqual(move3_output_aml.debit, 80) @@ -2173,22 +2166,18 @@ class TestStockValuation(SavepointCase): self.assertEqual(len(move3.account_move_ids), 1) self.assertAlmostEqual(self.product1.qty_available, 12.0) - self.assertAlmostEqual(self.product1.qty_at_date, 12.0) - self.assertEqual(self.product1.stock_value, 140) + self.assertAlmostEqual(self.product1.quantity_svl, 12.0) + self.assertEqual(self.product1.value_svl, 140) # --------------------------------------------------------------------- # Edit last move, send 14 instead # it should send 2@10 and 4@12 # --------------------------------------------------------------------- - move3.quantity_done = 14 + move3.with_context(svl=True).quantity_done = 14 self.assertEqual(move3.product_qty, 14) # old value: -80 -(8@10) # new value: -148 => -(10@10 + 4@12) - self.assertEqual(move3.value, -148) - - # older move - self.assertEqual(move1.remaining_value, 0) # before, 20 - self.assertEqual(move2.remaining_value, 72) # before, 120 + self.assertEqual(sum(move3.stock_valuation_layer_ids.mapped('value')), -148) # account values for move3 valuation_aml = self._get_stock_valuation_move_lines() @@ -2201,14 +2190,14 @@ class TestStockValuation(SavepointCase): self.assertEqual(len(move3.account_move_ids), 2) - self.assertEqual(self.product1.stock_value, 72) + self.assertEqual(self.product1.value_svl, 72) # --------------------------------------------------------------------- # Ending # --------------------------------------------------------------------- self.assertEqual(self.product1.qty_available, 6) - self.assertAlmostEqual(self.product1.qty_at_date, 6.0) - self.assertEqual(self.product1.stock_value, 72) + self.assertAlmostEqual(self.product1.quantity_svl, 6.0) + self.assertEqual(self.product1.value_svl, 72) self.assertEqual(sum(self._get_stock_input_move_lines().mapped('debit')), 0) self.assertEqual(sum(self._get_stock_input_move_lines().mapped('credit')), 220) self.assertEqual(sum(self._get_stock_valuation_move_lines().mapped('debit')), 220) @@ -2244,10 +2233,9 @@ class TestStockValuation(SavepointCase): move1._action_done() # stock values for move1 - self.assertEqual(move1.value, 100.0) - self.assertEqual(move1.remaining_qty, 10.0) - self.assertEqual(move1.price_unit, 10.0) - self.assertEqual(move1.remaining_value, 100.0) + self.assertEqual(move1.stock_valuation_layer_ids.value, 100.0) + self.assertEqual(move1.stock_valuation_layer_ids.remaining_qty, 10.0) + self.assertEqual(move1.stock_valuation_layer_ids.unit_cost, 10.0) # --------------------------------------------------------------------- # Send 10 @@ -2272,39 +2260,32 @@ class TestStockValuation(SavepointCase): move2._action_done() # stock values for move2 - self.assertEqual(move2.value, -100.0) - self.assertEqual(move2.remaining_qty, 0.0) - self.assertEqual(move2.remaining_value, 0.0) + self.assertEqual(move2.stock_valuation_layer_ids.value, -100.0) + self.assertEqual(move2.stock_valuation_layer_ids.remaining_qty, 0.0) # --------------------------------------------------------------------- # Actually, send 8 in the last move # --------------------------------------------------------------------- - move2.quantity_done = 8 + move2.with_context(svl=True).quantity_done = 8 - self.assertEqual(move2.value, -80.0) # the move actually sent 8@10 - self.assertEqual(move2.remaining_qty, 0.0) - self.assertEqual(move2.remaining_value, 0.0) + self.assertEqual(sum(move2.stock_valuation_layer_ids.mapped('value')), -80.0) # the move actually sent 8@10 - self.assertEqual(move1.remaining_qty, 2.0) - self.assertEqual(move1.remaining_value, 20.0) + self.assertEqual(sum(self.product1.stock_valuation_layer_ids.mapped('remaining_qty')), 2) self.product1.qty_available = 2 - self.product1.stock_value = 20 + self.product1.value_svl = 20 + self.product1.quantity_svl = 2 # --------------------------------------------------------------------- # Actually, send 10 in the last move # --------------------------------------------------------------------- - move2.quantity_done = 10 + move2.with_context(svl=True).quantity_done = 10 - self.assertEqual(move2.value, -100.0) # the move actually sent 10@10 - self.assertEqual(move2.remaining_qty, 0.0) - self.assertEqual(move2.remaining_value, 0.0) + self.assertEqual(sum(move2.stock_valuation_layer_ids.mapped('value')), -100.0) # the move actually sent 10@10 + self.assertEqual(sum(self.product1.stock_valuation_layer_ids.mapped('remaining_qty')), 0) - self.assertEqual(move1.remaining_qty, 0.0) - self.assertEqual(move1.remaining_value, 0.0) - - self.product1.qty_available = 2 - self.product1.stock_value = 20 + self.assertEqual(self.product1.quantity_svl, 0) + self.assertEqual(self.product1.value_svl, 0) def test_average_perpetual_1(self): # http://accountingexplained.com/financial/inventories/avco-method @@ -2677,6 +2658,7 @@ class TestStockValuation(SavepointCase): self.assertAlmostEqual(self.product1.quantity_svl, 25) self.assertAlmostEqual(self.product1.value_svl, 350) + @skip('need to implement dropship') def test_average_perpetual_8(self): """ Receive 1@10, then dropship 1@20, finally return the dropship. Dropship should not impact the price. @@ -3085,6 +3067,7 @@ class TestStockValuation(SavepointCase): self.assertAlmostEqual(self.product1.quantity_svl, 0.0) self.assertAlmostEqual(self.product1.value_svl, 0.0) + @skip('waiting changing cost method') def test_change_cost_method_1(self): """ Change the cost method from FIFO to AVCO. """ @@ -3155,6 +3138,7 @@ class TestStockValuation(SavepointCase): # (9 * 10) + (15 * 10) / 19 self.assertEqual(self.product1.standard_price, 12.63) + @skip('waiting changing cost method') def test_change_cost_method_2(self): """ Change the cost method from FIFO to standard. """ @@ -3279,12 +3263,11 @@ class TestStockValuation(SavepointCase): ]}) move1._action_done() - self.assertEqual(move1.value, 10) - self.assertEqual(move1.remaining_value, 10) - self.assertEqual(move1.remaining_qty, 1) + self.assertEqual(move1.stock_valuation_layer_ids.value, 10) + self.assertEqual(move1.stock_valuation_layer_ids.remaining_qty, 1) self.assertAlmostEqual(self.product1.qty_available, 0.0) - self.assertAlmostEqual(self.product1.qty_at_date, 1.0) - self.assertEqual(self.product1.stock_value, 10) + self.assertAlmostEqual(self.product1.quantity_svl, 1.0) + self.assertEqual(self.product1.value_svl, 10) self.assertTrue(len(move1.account_move_ids), 1) move2 = self.env['stock.move'].create({ @@ -3315,9 +3298,8 @@ class TestStockValuation(SavepointCase): }), ]}) move2._action_done() - self.assertEqual(move2.value, -10) + self.assertEqual(move2.stock_valuation_layer_ids.value, -10) - @skip('reintroduce the sanity check on all moves to fix this test') def test_move_in_or_out(self): """ Test a few combination of move and their move lines and check their valuation. A valued move should be IN or OUT. @@ -3597,11 +3579,10 @@ class TestStockValuation(SavepointCase): move1.move_line_ids.qty_done = 10 move1._action_done() move1.date = date1 - move1.account_move_ids.write({'date': date1}) + move1.stock_valuation_layer_ids.write({'create_date': date1}) - self.assertEqual(self.product1.qty_available, 10) - self.assertAlmostEqual(self.product1.qty_at_date, 10.0) - self.assertEqual(self.product1.stock_value, 100) + self.assertEqual(self.product1.quantity_svl, 10) + self.assertEqual(self.product1.value_svl, 100) # receive 10@12 move2 = self.env['stock.move'].create({ @@ -3618,11 +3599,10 @@ class TestStockValuation(SavepointCase): move2.move_line_ids.qty_done = 10 move2._action_done() move2.date = date2 - move2.account_move_ids.write({'date': date2}) + move2.stock_valuation_layer_ids.write({'create_date': date2}) - self.assertEqual(self.product1.qty_available, 20) - self.assertAlmostEqual(self.product1.qty_at_date, 20) - self.assertEqual(self.product1.stock_value, 220) + self.assertAlmostEqual(self.product1.quantity_svl, 20) + self.assertEqual(self.product1.value_svl, 220) # send 15 move3 = self.env['stock.move'].create({ @@ -3638,11 +3618,10 @@ class TestStockValuation(SavepointCase): move3.move_line_ids.qty_done = 15 move3._action_done() move3.date = date3 - move3.account_move_ids.write({'date': date3}) + move3.stock_valuation_layer_ids.write({'create_date': date3}) - self.assertEqual(self.product1.qty_available, 5) - self.assertAlmostEqual(self.product1.qty_at_date, 5.0) - self.assertEqual(self.product1.stock_value, 60) + self.assertAlmostEqual(self.product1.quantity_svl, 5.0) + self.assertEqual(self.product1.value_svl, 60) # send 20 move4 = self.env['stock.move'].create({ @@ -3658,11 +3637,10 @@ class TestStockValuation(SavepointCase): move4.move_line_ids.qty_done = 20 move4._action_done() move4.date = date4 - move4.account_move_ids.write({'date': date4}) + move4.stock_valuation_layer_ids.write({'create_date': date4}) - self.assertEqual(self.product1.qty_available, -15) - self.assertAlmostEqual(self.product1.qty_at_date, -15.0) - self.assertEqual(self.product1.stock_value, -180) + self.assertAlmostEqual(self.product1.quantity_svl, -15.0) + self.assertEqual(self.product1.value_svl, -180) # receive 100@15 move5 = self.env['stock.move'].create({ @@ -3679,60 +3657,60 @@ class TestStockValuation(SavepointCase): move5.move_line_ids.qty_done = 100 move5._action_done() move5.date = date5 - move5.account_move_ids.write({'date': date5}) + move5.stock_valuation_layer_ids.write({'create_date': date5}) - self.assertEqual(self.product1.qty_available, 85) - self.assertAlmostEqual(self.product1.qty_at_date, 85.0) - self.assertEqual(self.product1.stock_value, 1320) + self.assertAlmostEqual(self.product1.quantity_svl, 85.0) + self.assertEqual(self.product1.value_svl, 1320) - # run the vacuum to compensate the negative stock move - self.env['stock.move']._run_fifo_vacuum() - move4.account_move_ids[0].write({'date': date6}) + # FIXME to uncomment when fifo nefative is implemented + ## run the vacuum to compensate the negative stock move + #self.env['stock.move']._run_fifo_vacuum() + #move4.account_move_ids[0].write({'date': date6}) - self.assertEqual(self.product1.qty_available, 85) - self.assertAlmostEqual(self.product1.qty_at_date, 85.0) - self.assertEqual(self.product1.stock_value, 1275) + #self.assertEqual(self.product1.qty_available, 85) + #self.assertAlmostEqual(self.product1.qty_at_date, 85.0) + #self.assertEqual(self.product1.stock_value, 1275) - # Edit the quantity done of move1, increase it. - move1.quantity_done = 20 + ## Edit the quantity done of move1, increase it. + #move1.quantity_done = 20 - # --------------------------------------------------------------------- - # ending: manual valuation - # --------------------------------------------------------------------- - self.product1.categ_id.property_valuation = 'manual_periodic' - self.assertEqual(self.product1.with_context(to_date=Datetime.to_string(date1)).qty_at_date, 20) - self.assertEqual(self.product1.with_context(to_date=Datetime.to_string(date1)).stock_value, 200) - self.assertEqual(self.product1.with_context(to_date=Datetime.to_string(date2)).qty_at_date, 30) - self.assertEqual(self.product1.with_context(to_date=Datetime.to_string(date2)).stock_value, 320) - self.assertEqual(self.product1.with_context(to_date=Datetime.to_string(date3)).qty_at_date, 15) - self.assertEqual(self.product1.with_context(to_date=Datetime.to_string(date3)).stock_value, 160) - self.assertEqual(self.product1.with_context(to_date=Datetime.to_string(date4)).qty_at_date, -5) - self.assertEqual(self.product1.with_context(to_date=Datetime.to_string(date4)).stock_value, -125) - self.assertEqual(self.product1.with_context(to_date=Datetime.to_string(date5)).qty_at_date, 95) - self.assertEqual(self.product1.with_context(to_date=Datetime.to_string(date5)).stock_value, 1375) - self.assertEqual(self.product1.with_context(to_date=Datetime.to_string(date6)).qty_at_date, 95) - self.assertEqual(self.product1.with_context(to_date=Datetime.to_string(date6)).stock_value, 1375) - self.assertEqual(self.product1.qty_at_date, 95) - self.assertEqual(self.product1.stock_value, 1375) + ## --------------------------------------------------------------------- + ## ending: manual valuation + ## --------------------------------------------------------------------- + #self.product1.categ_id.property_valuation = 'manual_periodic' + #self.assertEqual(self.product1.with_context(to_date=Datetime.to_string(date1)).qty_at_date, 20) + #self.assertEqual(self.product1.with_context(to_date=Datetime.to_string(date1)).stock_value, 200) + #self.assertEqual(self.product1.with_context(to_date=Datetime.to_string(date2)).qty_at_date, 30) + #self.assertEqual(self.product1.with_context(to_date=Datetime.to_string(date2)).stock_value, 320) + #self.assertEqual(self.product1.with_context(to_date=Datetime.to_string(date3)).qty_at_date, 15) + #self.assertEqual(self.product1.with_context(to_date=Datetime.to_string(date3)).stock_value, 160) + #self.assertEqual(self.product1.with_context(to_date=Datetime.to_string(date4)).qty_at_date, -5) + #self.assertEqual(self.product1.with_context(to_date=Datetime.to_string(date4)).stock_value, -125) + #self.assertEqual(self.product1.with_context(to_date=Datetime.to_string(date5)).qty_at_date, 95) + #self.assertEqual(self.product1.with_context(to_date=Datetime.to_string(date5)).stock_value, 1375) + #self.assertEqual(self.product1.with_context(to_date=Datetime.to_string(date6)).qty_at_date, 95) + #self.assertEqual(self.product1.with_context(to_date=Datetime.to_string(date6)).stock_value, 1375) + #self.assertEqual(self.product1.qty_at_date, 95) + #self.assertEqual(self.product1.stock_value, 1375) - # --------------------------------------------------------------------- - # ending: perpetual valuation - # --------------------------------------------------------------------- - self.product1.categ_id.property_valuation = 'real_time' - self.assertEqual(self.product1.with_context(to_date=Datetime.to_string(date1)).qty_at_date, 10) - self.assertEqual(self.product1.with_context(to_date=Datetime.to_string(date1)).stock_value, 100) - self.assertEqual(self.product1.with_context(to_date=Datetime.to_string(date2)).qty_at_date, 20) - self.assertEqual(self.product1.with_context(to_date=Datetime.to_string(date2)).stock_value, 220) - self.assertEqual(self.product1.with_context(to_date=Datetime.to_string(date3)).qty_at_date, 5) - self.assertEqual(self.product1.with_context(to_date=Datetime.to_string(date3)).stock_value, 60) - self.assertEqual(self.product1.with_context(to_date=Datetime.to_string(date4)).qty_at_date, -15) - self.assertEqual(self.product1.with_context(to_date=Datetime.to_string(date4)).stock_value, -180) - self.assertEqual(self.product1.with_context(to_date=Datetime.to_string(date5)).qty_at_date, 85) - self.assertEqual(self.product1.with_context(to_date=Datetime.to_string(date5)).stock_value, 1320) - self.assertEqual(self.product1.with_context(to_date=Datetime.to_string(date6)).qty_at_date, 85) - self.assertEqual(self.product1.with_context(to_date=Datetime.to_string(date6)).stock_value, 1275) - self.assertEqual(self.product1.qty_at_date, 95) - self.assertEqual(self.product1.stock_value, 1375) + ## --------------------------------------------------------------------- + ## ending: perpetual valuation + ## --------------------------------------------------------------------- + #self.product1.categ_id.property_valuation = 'real_time' + #self.assertEqual(self.product1.with_context(to_date=Datetime.to_string(date1)).qty_at_date, 10) + #self.assertEqual(self.product1.with_context(to_date=Datetime.to_string(date1)).stock_value, 100) + #self.assertEqual(self.product1.with_context(to_date=Datetime.to_string(date2)).qty_at_date, 20) + #self.assertEqual(self.product1.with_context(to_date=Datetime.to_string(date2)).stock_value, 220) + #self.assertEqual(self.product1.with_context(to_date=Datetime.to_string(date3)).qty_at_date, 5) + #self.assertEqual(self.product1.with_context(to_date=Datetime.to_string(date3)).stock_value, 60) + #self.assertEqual(self.product1.with_context(to_date=Datetime.to_string(date4)).qty_at_date, -15) + #self.assertEqual(self.product1.with_context(to_date=Datetime.to_string(date4)).stock_value, -180) + #self.assertEqual(self.product1.with_context(to_date=Datetime.to_string(date5)).qty_at_date, 85) + #self.assertEqual(self.product1.with_context(to_date=Datetime.to_string(date5)).stock_value, 1320) + #self.assertEqual(self.product1.with_context(to_date=Datetime.to_string(date6)).qty_at_date, 85) + #self.assertEqual(self.product1.with_context(to_date=Datetime.to_string(date6)).stock_value, 1275) + #self.assertEqual(self.product1.qty_at_date, 95) + #self.assertEqual(self.product1.stock_value, 1375) def test_at_date_fifo_2(self): self.product1.categ_id.property_cost_method = 'fifo' @@ -3760,11 +3738,10 @@ class TestStockValuation(SavepointCase): move1.move_line_ids.qty_done = 10 move1._action_done() move1.date = date1 - move1.account_move_ids.write({'date': date1}) + move1.stock_valuation_layer_ids.write({'create_date': date1}) - self.assertEqual(self.product1.qty_available, 10) - self.assertAlmostEqual(self.product1.qty_at_date, 10.0) - self.assertEqual(self.product1.stock_value, 100) + self.assertAlmostEqual(self.product1.quantity_svl, 10.0) + self.assertEqual(self.product1.value_svl, 100) # receive 10@15 move2 = self.env['stock.move'].create({ @@ -3781,11 +3758,10 @@ class TestStockValuation(SavepointCase): move2.move_line_ids.qty_done = 10 move2._action_done() move2.date = date2 - move2.account_move_ids.write({'date': date2}) + move2.stock_valuation_layer_ids.write({'create_date': date2}) - self.assertEqual(self.product1.qty_available, 20) - self.assertAlmostEqual(self.product1.qty_at_date, 20.0) - self.assertEqual(self.product1.stock_value, 250) + self.assertAlmostEqual(self.product1.quantity_svl, 20.0) + self.assertEqual(self.product1.value_svl, 250) # send 30 move3 = self.env['stock.move'].create({ @@ -3801,11 +3777,10 @@ class TestStockValuation(SavepointCase): move3.move_line_ids.qty_done = 30 move3._action_done() move3.date = date3 - move3.account_move_ids.write({'date': date3}) + move3.stock_valuation_layer_ids.write({'create_date': date3}) - self.assertEqual(self.product1.qty_available, -10) - self.assertAlmostEqual(self.product1.qty_at_date, -10.0) - self.assertEqual(self.product1.stock_value, -150) + self.assertAlmostEqual(self.product1.quantity_svl, -10.0) + self.assertEqual(self.product1.value_svl, -150) # receive 10@20 move4 = self.env['stock.move'].create({ @@ -3822,11 +3797,10 @@ class TestStockValuation(SavepointCase): move4.move_line_ids.qty_done = 10 move4._action_done() move4.date = date4 - move4.account_move_ids.write({'date': date4}) + move4.stock_valuation_layer_ids.write({'create_date': date4}) - self.assertEqual(self.product1.qty_available, 0) - self.assertAlmostEqual(self.product1.qty_at_date, 0.0) - self.assertEqual(self.product1.stock_value, 50) + self.assertAlmostEqual(self.product1.quantity_svl, 0.0) + self.assertEqual(self.product1.value_svl, 50) # receive 10@10 move5 = self.env['stock.move'].create({ @@ -3843,57 +3817,57 @@ class TestStockValuation(SavepointCase): move5.move_line_ids.qty_done = 10 move5._action_done() move5.date = date5 - move5.account_move_ids.write({'date': date5}) + move5.stock_valuation_layer_ids.write({'create_date': date5}) - self.assertEqual(self.product1.qty_available, 10) - self.assertAlmostEqual(self.product1.qty_at_date, 10.0) - self.assertEqual(self.product1.stock_value, 150) + self.assertAlmostEqual(self.product1.quantity_svl, 10.0) + self.assertEqual(self.product1.value_svl, 150) - # run the vacuum to compensate the negative stock move - self.env['stock.move']._run_fifo_vacuum() - move3.account_move_ids[0].write({'date': date6}) + # FIXME to restore once negative is implemented + ## run the vacuum to compensate the negative stock move + #self.env['stock.move']._run_fifo_vacuum() + #move3.account_move_ids[0].write({'date': date6}) - self.assertEqual(self.product1.qty_available, 10) - self.assertAlmostEqual(self.product1.qty_at_date, 10.0) - self.assertEqual(self.product1.stock_value, 100) + #self.assertEqual(self.product1.qty_available, 10) + #self.assertAlmostEqual(self.product1.qty_at_date, 10.0) + #self.assertEqual(self.product1.stock_value, 100) - # --------------------------------------------------------------------- - # ending: manual valuation - # --------------------------------------------------------------------- - self.product1.categ_id.property_valuation = 'manual_periodic' - self.assertEqual(self.product1.with_context(to_date=Datetime.to_string(date1)).qty_at_date, 10) - self.assertEqual(self.product1.with_context(to_date=Datetime.to_string(date1)).stock_value, 100) - self.assertEqual(self.product1.with_context(to_date=Datetime.to_string(date2)).qty_at_date, 20) - self.assertEqual(self.product1.with_context(to_date=Datetime.to_string(date2)).stock_value, 250) - self.assertEqual(self.product1.with_context(to_date=Datetime.to_string(date3)).qty_at_date, -10) - self.assertEqual(self.product1.with_context(to_date=Datetime.to_string(date3)).stock_value, -200) - self.assertEqual(self.product1.with_context(to_date=Datetime.to_string(date4)).qty_at_date, 0) - self.assertEqual(self.product1.with_context(to_date=Datetime.to_string(date4)).stock_value, 0) - self.assertEqual(self.product1.with_context(to_date=Datetime.to_string(date5)).qty_at_date, 10) - self.assertEqual(self.product1.with_context(to_date=Datetime.to_string(date5)).stock_value, 100) - self.assertEqual(self.product1.with_context(to_date=Datetime.to_string(date6)).qty_at_date, 10) - self.assertEqual(self.product1.with_context(to_date=Datetime.to_string(date6)).stock_value, 100) - self.assertEqual(self.product1.qty_at_date, 10) - self.assertEqual(self.product1.stock_value, 100) + # # --------------------------------------------------------------------- + ## ending: manual valuation + ## --------------------------------------------------------------------- + #self.product1.categ_id.property_valuation = 'manual_periodic' + #self.assertEqual(self.product1.with_context(to_date=Datetime.to_string(date1)).qty_at_date, 10) + #self.assertEqual(self.product1.with_context(to_date=Datetime.to_string(date1)).stock_value, 100) + #self.assertEqual(self.product1.with_context(to_date=Datetime.to_string(date2)).qty_at_date, 20) + #self.assertEqual(self.product1.with_context(to_date=Datetime.to_string(date2)).stock_value, 250) + #self.assertEqual(self.product1.with_context(to_date=Datetime.to_string(date3)).qty_at_date, -10) + #self.assertEqual(self.product1.with_context(to_date=Datetime.to_string(date3)).stock_value, -200) + #self.assertEqual(self.product1.with_context(to_date=Datetime.to_string(date4)).qty_at_date, 0) + #self.assertEqual(self.product1.with_context(to_date=Datetime.to_string(date4)).stock_value, 0) + #self.assertEqual(self.product1.with_context(to_date=Datetime.to_string(date5)).qty_at_date, 10) + #self.assertEqual(self.product1.with_context(to_date=Datetime.to_string(date5)).stock_value, 100) + #self.assertEqual(self.product1.with_context(to_date=Datetime.to_string(date6)).qty_at_date, 10) + #self.assertEqual(self.product1.with_context(to_date=Datetime.to_string(date6)).stock_value, 100) + #self.assertEqual(self.product1.qty_at_date, 10) + #self.assertEqual(self.product1.stock_value, 100) - # --------------------------------------------------------------------- - # ending: perpetual valuation - # --------------------------------------------------------------------- - self.product1.categ_id.property_valuation = 'real_time' - self.assertEqual(self.product1.with_context(to_date=Datetime.to_string(date1)).qty_at_date, 10) - self.assertEqual(self.product1.with_context(to_date=Datetime.to_string(date1)).stock_value, 100) - self.assertEqual(self.product1.with_context(to_date=Datetime.to_string(date2)).qty_at_date, 20) - self.assertEqual(self.product1.with_context(to_date=Datetime.to_string(date2)).stock_value, 250) - self.assertEqual(self.product1.with_context(to_date=Datetime.to_string(date3)).qty_at_date, -10) - self.assertEqual(self.product1.with_context(to_date=Datetime.to_string(date3)).stock_value, -150) - self.assertEqual(self.product1.with_context(to_date=Datetime.to_string(date4)).qty_at_date, 0) - self.assertEqual(self.product1.with_context(to_date=Datetime.to_string(date4)).stock_value, 50) - self.assertEqual(self.product1.with_context(to_date=Datetime.to_string(date5)).qty_at_date, 10) - self.assertEqual(self.product1.with_context(to_date=Datetime.to_string(date5)).stock_value, 150) - self.assertEqual(self.product1.with_context(to_date=Datetime.to_string(date6)).qty_at_date, 10) - self.assertEqual(self.product1.with_context(to_date=Datetime.to_string(date6)).stock_value, 100) - self.assertEqual(self.product1.qty_at_date, 10) - self.assertEqual(self.product1.stock_value, 100) + ## --------------------------------------------------------------------- + ## ending: perpetual valuation + ## --------------------------------------------------------------------- + #self.product1.categ_id.property_valuation = 'real_time' + #self.assertEqual(self.product1.with_context(to_date=Datetime.to_string(date1)).qty_at_date, 10) + #self.assertEqual(self.product1.with_context(to_date=Datetime.to_string(date1)).stock_value, 100) + #self.assertEqual(self.product1.with_context(to_date=Datetime.to_string(date2)).qty_at_date, 20) + #self.assertEqual(self.product1.with_context(to_date=Datetime.to_string(date2)).stock_value, 250) + #self.assertEqual(self.product1.with_context(to_date=Datetime.to_string(date3)).qty_at_date, -10) + #self.assertEqual(self.product1.with_context(to_date=Datetime.to_string(date3)).stock_value, -150) + #self.assertEqual(self.product1.with_context(to_date=Datetime.to_string(date4)).qty_at_date, 0) + #self.assertEqual(self.product1.with_context(to_date=Datetime.to_string(date4)).stock_value, 50) + #self.assertEqual(self.product1.with_context(to_date=Datetime.to_string(date5)).qty_at_date, 10) + #self.assertEqual(self.product1.with_context(to_date=Datetime.to_string(date5)).stock_value, 150) + #self.assertEqual(self.product1.with_context(to_date=Datetime.to_string(date6)).qty_at_date, 10) + #self.assertEqual(self.product1.with_context(to_date=Datetime.to_string(date6)).stock_value, 100) + #self.assertEqual(self.product1.qty_at_date, 10) + #self.assertEqual(self.product1.stock_value, 100) def test_inventory_fifo_1(self): """ Make an inventory from a location with a company set, and ensure the product has a stock @@ -3901,7 +3875,7 @@ class TestStockValuation(SavepointCase): and no stock value. """ self.product1.standard_price = 15 - self.product1.product_tmpl_id.cost_method = 'fifo' + self.product1.categ_id.property_cost_method = 'fifo' inventory_location = self.product1.property_stock_inventory inventory_location.company_id = self.env.user.company_id.id @@ -3919,9 +3893,9 @@ class TestStockValuation(SavepointCase): move1.move_line_ids.qty_done = 12.0 move1._action_done() - self.assertAlmostEqual(move1.value, 180.0) - self.assertAlmostEqual(move1.remaining_qty, 12.0) - self.assertAlmostEqual(self.product1.stock_value, 180.0) + self.assertAlmostEqual(move1.stock_valuation_layer_ids.value, 180.0) + self.assertAlmostEqual(move1.stock_valuation_layer_ids.remaining_qty, 12.0) + self.assertAlmostEqual(self.product1.value_svl, 180.0) # Sell the 12 units move2 = self.env['stock.move'].create({ @@ -3937,5 +3911,5 @@ class TestStockValuation(SavepointCase): move2.move_line_ids.qty_done = 12.0 move2._action_done() - self.assertAlmostEqual(move1.remaining_qty, 0.0) - self.assertAlmostEqual(self.product1.stock_value, 0.0) + self.assertAlmostEqual(move1.stock_valuation_layer_ids.remaining_qty, 0.0) + self.assertAlmostEqual(self.product1.value_svl, 0.0) diff --git a/addons/stock_account/tests/test_stockvaluationlayer.py b/addons/stock_account/tests/test_stockvaluationlayer.py index 08f7acf1f23..c7d698a8877 100644 --- a/addons/stock_account/tests/test_stockvaluationlayer.py +++ b/addons/stock_account/tests/test_stockvaluationlayer.py @@ -296,3 +296,93 @@ class TestStockValuationAVCO(TestStockValuationCommon): self.assertEqual(self.product1.quantity_svl, 10) self.assertEqual(self.product1.standard_price, 15) + +class TestStockValuationFIFO(TestStockValuationCommon): + def setUp(self): + super(TestStockValuationFIFO, self).setUp() + self.product1.product_tmpl_id.categ_id.property_cost_method = 'fifo' + + def test_normal_1(self): + self.product1.product_tmpl_id.categ_id.property_valuation = 'manual_periodic' + move1 = self._make_in_move(self.product1, 10, unit_cost=10) + move2 = self._make_in_move(self.product1, 10, unit_cost=20) + move3 = self._make_out_move(self.product1, 15) + + self.assertEqual(self.product1.value_svl, 100) + self.assertEqual(self.product1.quantity_svl, 5) + self.assertEqual(sum(self.product1.stock_valuation_layer_ids.mapped('remaining_qty')), 5) + + def test_change_in_past_decrease_in_1(self): + self.product1.product_tmpl_id.categ_id.property_valuation = 'manual_periodic' + move1 = self._make_in_move(self.product1, 20, unit_cost=10) + move2 = self._make_out_move(self.product1, 10) + move1.move_line_ids.qty_done = 10 + + self.assertEqual(self.product1.value_svl, 0) + self.assertEqual(self.product1.quantity_svl, 0) + + @skip('fifo negative') + def test_change_in_past_decrease_in_2(self): + self.product1.product_tmpl_id.categ_id.property_valuation = 'manual_periodic' + move1 = self._make_in_move(self.product1, 20, unit_cost=10) + move2 = self._make_out_move(self.product1, 10) + move3 = self._make_out_move(self.product1, 10) + move1.move_line_ids.qty_done = 10 + move1 = self._make_in_move(self.product1, 20, unit_cost=15) + + self.assertEqual(self.product1.value_svl, 150) + self.assertEqual(self.product1.quantity_svl, 10) + + def test_change_in_past_increase_in_1(self): + self.product1.product_tmpl_id.categ_id.property_valuation = 'manual_periodic' + move1 = self._make_in_move(self.product1, 10, unit_cost=10) + move2 = self._make_in_move(self.product1, 10, unit_cost=15) + move3 = self._make_out_move(self.product1, 20) + move1.move_line_ids.qty_done = 20 + + self.assertEqual(self.product1.value_svl, 100) + self.assertEqual(self.product1.quantity_svl, 10) + + @skip('fifo negative') + def test_change_in_past_increase_in_2(self): + self.product1.product_tmpl_id.categ_id.property_valuation = 'manual_periodic' + move1 = self._make_in_move(self.product1, 10, unit_cost=10) + move2 = self._make_in_move(self.product1, 10, unit_cost=12) + move3 = self._make_out_move(self.product1, 15) + move4 = self._make_out_move(self.product1, 20) + move5 = self._make_in_move(self.product1, 100, unit_cost=15) + move1.move_line_ids.qty_done = 20 + + self.assertEqual(self.product1.value_svl, 1375) + self.assertEqual(self.product1.quantity_svl, 95) + + def test_change_in_past_decrease_out_1(self): + self.product1.product_tmpl_id.categ_id.property_valuation = 'manual_periodic' + move1 = self._make_in_move(self.product1, 20, unit_cost=10) + move2 = self._make_out_move(self.product1, 15) + move3 = self._make_in_move(self.product1, 20, unit_cost=15) + move2.move_line_ids.qty_done = 5 + + self.assertEqual(self.product1.value_svl, 450) + self.assertEqual(self.product1.quantity_svl, 35) + self.assertEqual(sum(self.product1.stock_valuation_layer_ids.mapped('remaining_qty')), 35) + + def test_return_delivery_1(self): + self.product1.product_tmpl_id.categ_id.property_valuation = 'manual_periodic' + move1 = self._make_in_move(self.product1, 10, unit_cost=10) + move2 = self._make_out_move(self.product1, 10, create_picking=True) + move3 = self._make_in_move(self.product1, 10, unit_cost=20) + move4 = self._make_return(move2, 10) + + self.assertEqual(self.product1.value_svl, 300) + self.assertEqual(self.product1.quantity_svl, 20) + + def test_return_receipt_1(self): + self.product1.product_tmpl_id.categ_id.property_valuation = 'manual_periodic' + move1 = self._make_in_move(self.product1, 10, unit_cost=10, create_picking=True) + move2 = self._make_in_move(self.product1, 10, unit_cost=20) + move3 = self._make_return(move1, 2) + + self.assertEqual(self.product1.value_svl, 280) + self.assertEqual(self.product1.quantity_svl, 18) +