[FIX] purchase: correctly calculate average price
cherry-pick of a4904fb4889c16322494981f5a1be586683704bd
Steps to reproduce the bug:
- Create a storable product “P1”
- costing method: avco
- Create a PO:
- Add the product “P1”:
- Line 1: Qty= 10, price= $50
- Line 2: Qty=1, price= $10
- Confirm the PO and receive the product
- Go to purchase → Reporting → Purchase Analysis
Problem:
The average price is incorrect, the current calculation is:
(50 + 10) / 2 = 30
The average should take into account the quantities purchased in each
line, And not simply the number of line, so the correct calculation
should be:
((10 * 50) + (10 * 1)) / 11 = 46.36
The SQL query is correct, it is when applying the read_group that the
calculation is incorrect, we should override it to make a personalized
calculation of the average.
opw-3136406
closes odoo/odoo#136097
X-original-commit: cd84549a3e5ea09d230dc1d02e05456a2372fd34
Signed-off-by: William Henrotin (whe) <whe@odoo.com>
Signed-off-by: Djamel Touati (otd) <otd@odoo.com>
This commit is contained in:
committed by
Djamel Touati
parent
17b2597482
commit
3f496c3b30
@@ -5,7 +5,7 @@
|
||||
# Please note that these reports are not multi-currency !!!
|
||||
#
|
||||
|
||||
from odoo import fields, models
|
||||
from odoo import fields, models, api
|
||||
|
||||
|
||||
class PurchaseReport(models.Model):
|
||||
@@ -147,3 +147,29 @@ class PurchaseReport(models.Model):
|
||||
currency_table.rate
|
||||
"""
|
||||
return group_by_str
|
||||
|
||||
@api.model
|
||||
def read_group(self, domain, fields, groupby, offset=0, limit=None, orderby=False, lazy=True):
|
||||
"""
|
||||
This is a hack to allow us to correctly calculate the average price of product.
|
||||
"""
|
||||
if 'price_average:avg' in fields:
|
||||
fields.extend(['aggregated_qty_ordered:array_agg(qty_ordered)'])
|
||||
fields.extend(['aggregated_price_average:array_agg(price_average)'])
|
||||
|
||||
res = []
|
||||
if fields:
|
||||
res = super(PurchaseReport, self).read_group(domain, fields, groupby, offset=offset, limit=limit, orderby=orderby, lazy=lazy)
|
||||
|
||||
if 'price_average:avg' in fields:
|
||||
qties = 'aggregated_qty_ordered'
|
||||
special_field = 'aggregated_price_average'
|
||||
for data in res:
|
||||
if data[special_field] and data[qties]:
|
||||
total_unit_cost = sum(float(value) * float(qty) for value, qty in zip(data[special_field], data[qties]) if qty and value)
|
||||
total_qty_ordered = sum(float(qty) for qty in data[qties] if qty)
|
||||
data['price_average'] = (total_unit_cost / total_qty_ordered) if total_qty_ordered else 0
|
||||
del data[special_field]
|
||||
del data[qties]
|
||||
|
||||
return res
|
||||
|
||||
@@ -184,3 +184,37 @@ class TestPurchaseOrderReport(AccountTestInvoicingCommon):
|
||||
po_2.flush_model()
|
||||
report = self.env['purchase.report'].search([('product_id', "=", self.product_a.id)])
|
||||
self.assertEqual(report.currency_id, self.env.company.currency_id)
|
||||
|
||||
def test_avg_price_calculation(self):
|
||||
"""
|
||||
Check that the average price is calculated based on the quantity ordered in each line
|
||||
PO:
|
||||
- 10 unit of product A -> price $50
|
||||
- 1 unit of product A -> price $10
|
||||
Total qty_ordered: 11
|
||||
avergae price: 46.36 = ((10 * 50) + (10 * 1)) / 11
|
||||
"""
|
||||
po = self.env['purchase.order'].create({
|
||||
'partner_id': self.partner_a.id,
|
||||
'order_line': [
|
||||
(0, 0, {
|
||||
'product_id': self.product_a.id,
|
||||
'product_qty': 10.0,
|
||||
'price_unit': 50.0,
|
||||
}),
|
||||
(0, 0, {
|
||||
'product_id': self.product_a.id,
|
||||
'product_qty': 1.0,
|
||||
'price_unit': 10.0,
|
||||
}),
|
||||
],
|
||||
})
|
||||
po.button_confirm()
|
||||
po.flush_model()
|
||||
report = self.env['purchase.report'].read_group(
|
||||
[('product_id', '=', self.product_a.id)],
|
||||
['qty_ordered', 'price_average:avg'],
|
||||
['product_id'],
|
||||
)
|
||||
self.assertEqual(report[0]['qty_ordered'], 11)
|
||||
self.assertEqual(round(report[0]['price_average'], 2), 46.36)
|
||||
|
||||
Reference in New Issue
Block a user