summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSimon Lejeune <sle@openerp.com>2017-09-28 11:23:25 +0200
committerSimon Lejeune <sle@openerp.com>2017-09-29 16:43:00 +0200
commit50f0b5ebaf47f7693d92d66fd12eae7fa9fb4c64 (patch)
tree2f48a02ba15e12930c65305538da4cb05a640eb5
parent4524d93be06ada059907c086c529b270c9002da2 (diff)
[REF] stock_account: cost method changes
When changing the costing method for a FIFO product to AVCO or standard, we update the standard price of the product to the average cost in stock.
-rw-r--r--addons/stock_account/models/product.py22
-rw-r--r--addons/stock_account/tests/test_stockvaluation.py136
2 files changed, 153 insertions, 5 deletions
diff --git a/addons/stock_account/models/product.py b/addons/stock_account/models/product.py
index d35299cf0a6..10d80789938 100644
--- a/addons/stock_account/models/product.py
+++ b/addons/stock_account/models/product.py
@@ -58,6 +58,15 @@ class ProductTemplate(models.Model):
@api.one
def _set_cost_method(self):
+ # When going from FIFO to AVCO or to standard, we update the standard price with the
+ # average value in stock.
+ if self.property_cost_method == 'fifo' and self.cost_method in ['average', 'standard']:
+ # Cannot use the `stock_value` computed field as it's already invalidated when
+ # entering this method.
+ valuation = sum([variant._sum_remaining_values() for variant in self.product_variant_ids])
+ qty_available = self.with_context(company_owned=True).qty_available
+ if qty_available:
+ self.standard_price = valuation / qty_available
return self.write({'property_cost_method': self.cost_method})
@api.multi
@@ -158,17 +167,20 @@ class ProductProduct(models.Model):
candidates = self.env['stock.move'].search(domain, order='date, id')
return candidates
+ def _sum_remaining_values(self):
+ StockMove = self.env['stock.move']
+ domain = [('product_id', '=', self.id)] + StockMove._get_all_base_domain()
+ moves = StockMove.search(domain)
+ return sum(moves.mapped('remaining_value'))
+
@api.multi
- @api.depends('stock_move_ids.product_qty', 'stock_move_ids.state')
+ @api.depends('stock_move_ids.product_qty', 'stock_move_ids.state', 'product_tmpl_id.cost_method')
def _compute_stock_value(self):
for product in self:
if product.cost_method in ['standard', 'average']:
product.stock_value = product.standard_price * product.with_context(company_owned=True).qty_available
elif product.cost_method == 'fifo':
- StockMove = self.env['stock.move']
- domain = [('product_id', '=', product.id)] + StockMove._get_all_base_domain()
- moves = StockMove.search(domain)
- product.stock_value = sum(moves.mapped('remaining_value'))
+ product.stock_value = product._sum_remaining_values()
@api.multi
def action_open_product_moves(self):
diff --git a/addons/stock_account/tests/test_stockvaluation.py b/addons/stock_account/tests/test_stockvaluation.py
index ab8cae3b905..3059e927acc 100644
--- a/addons/stock_account/tests/test_stockvaluation.py
+++ b/addons/stock_account/tests/test_stockvaluation.py
@@ -2149,3 +2149,139 @@ class TestStockValuation(TransactionCase):
self.assertEqual(move7.value, 100.0)
self.assertEqual(self.product1.standard_price, 10)
+
+ def test_change_cost_method_1(self):
+ """ Change the cost method from FIFO to AVCO.
+ """
+ # ---------------------------------------------------------------------
+ # Use FIFO, make some operations
+ # ---------------------------------------------------------------------
+ self.product1.product_tmpl_id.cost_method = 'fifo'
+
+ # receive 10@10
+ move1 = self.env['stock.move'].create({
+ 'name': '10 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': 10.0,
+ 'price_unit': 10,
+ })
+ move1._action_confirm()
+ move1._action_assign()
+ move1.move_line_ids.qty_done = 10.0
+ move1._action_done()
+
+ # receive 10@15
+ move2 = self.env['stock.move'].create({
+ 'name': '10 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': 10.0,
+ 'price_unit': 15,
+ })
+ move2._action_confirm()
+ move2._action_assign()
+ move2.move_line_ids.qty_done = 10.0
+ move2._action_done()
+
+ # sell 1
+ move3 = self.env['stock.move'].create({
+ 'name': 'Sale 5 units',
+ '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': 1.0,
+ })
+ move3._action_confirm()
+ move3._action_assign()
+ move3.move_line_ids.qty_done = 1.0
+ move3._action_done()
+
+ self.assertEqual(self.product1.stock_value, 240)
+
+ # ---------------------------------------------------------------------
+ # Change the production valuation to AVCO
+ # ---------------------------------------------------------------------
+ self.product1.with_context(debug=True).product_tmpl_id.cost_method = 'average'
+
+ # valuation should stay to ~240
+ self.assertAlmostEqual(self.product1.stock_value, 240, delta=0.03)
+
+ # no accounting entry should be created
+
+ # the cost should now be 12,65
+ # (9 * 10) + (15 * 10) / 19
+ self.assertEqual(self.product1.standard_price, 12.63)
+
+ def test_change_cost_method_2(self):
+ """ Change the cost method from FIFO to standard.
+ """
+ # ---------------------------------------------------------------------
+ # Use FIFO, make some operations
+ # ---------------------------------------------------------------------
+ self.product1.product_tmpl_id.cost_method = 'fifo'
+
+ # receive 10@10
+ move1 = self.env['stock.move'].create({
+ 'name': '10 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': 10.0,
+ 'price_unit': 10,
+ })
+ move1._action_confirm()
+ move1._action_assign()
+ move1.move_line_ids.qty_done = 10.0
+ move1._action_done()
+
+ # receive 10@15
+ move2 = self.env['stock.move'].create({
+ 'name': '10 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': 10.0,
+ 'price_unit': 15,
+ })
+ move2._action_confirm()
+ move2._action_assign()
+ move2.move_line_ids.qty_done = 10.0
+ move2._action_done()
+
+ # sell 1
+ move3 = self.env['stock.move'].create({
+ 'name': 'Sale 5 units',
+ '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': 1.0,
+ })
+ move3._action_confirm()
+ move3._action_assign()
+ move3.move_line_ids.qty_done = 1.0
+ move3._action_done()
+
+ self.assertEqual(self.product1.stock_value, 240)
+
+ # ---------------------------------------------------------------------
+ # Change the production valuation to AVCO
+ # ---------------------------------------------------------------------
+ self.product1.with_context(debug=True).product_tmpl_id.cost_method = 'standard'
+
+ # valuation should stay to ~240
+ self.assertAlmostEqual(self.product1.stock_value, 240, delta=0.03)
+
+ # no accounting entry should be created
+
+ # the cost should now be 12,65
+ # (9 * 10) + (15 * 10) / 19
+ self.assertEqual(self.product1.standard_price, 12.63) \ No newline at end of file