[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.
This commit is contained in:
Simon Lejeune
2017-09-29 16:43:00 +02:00
parent 4524d93be0
commit 50f0b5ebaf
2 changed files with 153 additions and 5 deletions
+17 -5
View File
@@ -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):
@@ -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)