[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:
@@ -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)
|
||||
Reference in New Issue
Block a user