From 3f496c3b30626e75b72aefd213f220a393b77f39 Mon Sep 17 00:00:00 2001 From: "Touati Djamel (otd)" Date: Mon, 23 Jan 2023 16:22:13 +0000 Subject: [PATCH] [FIX] purchase: correctly calculate average price MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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) Signed-off-by: Djamel Touati (otd) --- addons/purchase/report/purchase_report.py | 28 ++++++++++++++- .../tests/test_purchase_order_report.py | 34 +++++++++++++++++++ 2 files changed, 61 insertions(+), 1 deletion(-) diff --git a/addons/purchase/report/purchase_report.py b/addons/purchase/report/purchase_report.py index 525fae40113..7aac1f26d82 100644 --- a/addons/purchase/report/purchase_report.py +++ b/addons/purchase/report/purchase_report.py @@ -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 diff --git a/addons/purchase/tests/test_purchase_order_report.py b/addons/purchase/tests/test_purchase_order_report.py index 2f060a7b571..0696dc5d10d 100644 --- a/addons/purchase/tests/test_purchase_order_report.py +++ b/addons/purchase/tests/test_purchase_order_report.py @@ -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)