[FIX] stock_account: price_unit after edit

- Product category: FIFO (real_time) / Automatic
- Product: storable, nvoicing based on delivered quantity, FIFO category
- Receive 3 units @ 30
- Create and confirm a SO for 1 unit
- Deliver requested qty to customer
- Unlock the picking, update the qty done to 3, lock.
- Invoice delivered qty

In the journal entry of the customer invoice, the COGS entry is 180
instead of 90.

The root cause of this is the incorrect price unit on the modified stock
move, which equals to -60 instead of -30.

The price unit should compute the weigthed average of the already
delivered quantity with the extra quantity.

opw-2047719

closes odoo/odoo#36065

Signed-off-by: Nicolas Martinelli (nim) <nim@odoo.com>
This commit is contained in:
Nicolas Martinelli
2019-08-26 13:12:48 +00:00
parent cfbaba81c2
commit 0aaf7307de
2 changed files with 16 additions and 1 deletions
+12 -1
View File
@@ -272,6 +272,7 @@ class StockMove(models.Model):
qty_to_take_on_candidates = quantity or valued_quantity
candidates = move.product_id._get_fifo_candidates_in_move_with_company(move.company_id.id)
new_standard_price = 0
tmp_qty = 0
tmp_value = 0 # to accumulate the value taken on the candidates
for candidate in candidates:
new_standard_price = candidate.price_unit
@@ -292,6 +293,7 @@ class StockMove(models.Model):
candidate.write(candidate_vals)
qty_to_take_on_candidates -= qty_taken_on_candidate
tmp_qty += qty_taken_on_candidate
tmp_value += value_taken_on_candidate
if qty_to_take_on_candidates == 0:
break
@@ -305,9 +307,18 @@ class StockMove(models.Model):
# 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.
if qty_to_take_on_candidates == 0:
# If the move is not valued yet we compute the price_unit based on the value taken on
# the candidates.
# If the move has already been valued, it means that we editing the qty_done on the
# move. In this case, the price_unit computation should take into account the quantity
# already valued and the new quantity taken.
if not move.value:
price_unit = -tmp_value / (move.product_qty or quantity)
else:
price_unit = (-(tmp_value) + move.value) / (tmp_qty + move.product_qty)
move.write({
'value': -tmp_value if not quantity else move.value or -tmp_value, # outgoing move are valued negatively
'price_unit': -tmp_value / (move.product_qty or quantity),
'price_unit': price_unit,
})
elif qty_to_take_on_candidates > 0:
last_fifo_price = new_standard_price or move.product_id.standard_price
@@ -2152,6 +2152,7 @@ class TestStockValuation(TransactionCase):
# stock values for move3
self.assertEqual(move3.value, -80.0)
self.assertAlmostEqual(move3.price_unit, -10.0)
self.assertEqual(move3.remaining_qty, 0.0)
self.assertEqual(move3.remaining_value, 0.0)
@@ -2183,6 +2184,7 @@ class TestStockValuation(TransactionCase):
# old value: -80 -(8@10)
# new value: -148 => -(10@10 + 4@12)
self.assertEqual(move3.value, -148)
self.assertAlmostEqual(move3.price_unit, -148.0/14.0)
# older move
self.assertEqual(move1.remaining_value, 0) # before, 20
@@ -2271,6 +2273,7 @@ class TestStockValuation(TransactionCase):
# stock values for move2
self.assertEqual(move2.value, -100.0)
self.assertAlmostEqual(move2.price_unit, -10.0)
self.assertEqual(move2.remaining_qty, 0.0)
self.assertEqual(move2.remaining_value, 0.0)
@@ -2280,6 +2283,7 @@ class TestStockValuation(TransactionCase):
move2.quantity_done = 8
self.assertEqual(move2.value, -80.0) # the move actually sent 8@10
self.assertAlmostEqual(move2.price_unit, -10.0)
self.assertEqual(move2.remaining_qty, 0.0)
self.assertEqual(move2.remaining_value, 0.0)