From b3fdc5827ce21e303bb45efd763a595c994dd386 Mon Sep 17 00:00:00 2001 From: Adrien Widart Date: Fri, 25 Jun 2021 16:50:29 +0000 Subject: [PATCH] [FIX] pos_sale: compute weight and volume for sale report When selling several times a product in a POS, if the volume and weight of this product are defined, the sales report will be incorrect To reproduce the error: 1. Create a product P: - Product Type: Consumable - Available in POS: True - Weight: 1 - Volume: 1 2. Start a POS session 3. Sell 3 x P 4. Sell 1 x P 5. In Sales > Reporting > Sales, select the pivot view 6. Remove all filters and add this one: - Product Variant: P 7. In Measures, select "Gross Weight" and "Volume" Error: Total weight and volume are incorrect, they are equal to 8 instead of 4 For each POS order line, an SQL request computes several fields to generate the associated sale report. Among them, here is how the volume is computed: https://github.com/odoo/odoo/blob/056246665f02c331ba0589618cf030482709f1da/addons/pos_sale/report/sale_report.py#L60-L63 So, let's say we are generating the sale report associated with the POS order line of step 3. Since there are not enough constraints in the volume calculation, the SQL request will select all POS order lines with product P (even those associated with other orders than the one in step 3) and add up all the volumes. Therefore, the volume of the sale report associated with the POS order line from step 3 will be `3 + 1 = 4` which is incorrect (it should be 3). Same thing will happen with the sale report associated with the POS order line of step 4 (its volume will be 4 instead of 1). As a result, on pivot view, the volume displayed will be the sum of these values, i.e. `4 + 4 = 8`, which is incorrect The nested SQL request is actually useless and the volume can be directly computed. The problem is the same with the weight. OPW-2527163 closes odoo/odoo#72946 X-original-commit: e7f296ae863a2bad6ca2305e68411e2062947543 Signed-off-by: Adrien Widart --- addons/pos_sale/report/sale_report.py | 10 +---- addons/pos_sale/tests/__init__.py | 4 ++ addons/pos_sale/tests/test_pos_sale_report.py | 39 +++++++++++++++++++ 3 files changed, 45 insertions(+), 8 deletions(-) create mode 100644 addons/pos_sale/tests/__init__.py create mode 100644 addons/pos_sale/tests/test_pos_sale_report.py diff --git a/addons/pos_sale/report/sale_report.py b/addons/pos_sale/report/sale_report.py index 8238f5938ee..ac9bed1fad6 100644 --- a/addons/pos_sale/report/sale_report.py +++ b/addons/pos_sale/report/sale_report.py @@ -53,14 +53,8 @@ class SaleReport(models.Model): partner.country_id AS country_id, partner.industry_id AS industry_id, partner.commercial_partner_id AS commercial_partner_id, - (select sum(t.weight*l.qty/u.factor) from pos_order_line l - join product_product p on (l.product_id=p.id) - left join product_template t on (p.product_tmpl_id=t.id) - left join uom_uom u on (u.id=t.uom_id)) AS weight, - (select sum(t.volume*l.qty/u.factor) from pos_order_line l - join product_product p on (l.product_id=p.id) - left join product_template t on (p.product_tmpl_id=t.id) - left join uom_uom u on (u.id=t.uom_id)) AS volume, + (sum(t.weight) * l.qty / u.factor) AS weight, + (sum(t.volume) * l.qty / u.factor) AS volume, l.discount as discount, sum((l.price_unit * l.discount * l.qty / 100.0 / CASE COALESCE(pos.currency_rate, 0) WHEN 0 THEN 1.0 ELSE pos.currency_rate END)) as discount_amount, NULL as order_id diff --git a/addons/pos_sale/tests/__init__.py b/addons/pos_sale/tests/__init__.py new file mode 100644 index 00000000000..5c825d0763a --- /dev/null +++ b/addons/pos_sale/tests/__init__.py @@ -0,0 +1,4 @@ +# -*- coding: utf-8 -*- +# Part of Odoo. See LICENSE file for full copyright and licensing details. + +from . import test_pos_sale_report diff --git a/addons/pos_sale/tests/test_pos_sale_report.py b/addons/pos_sale/tests/test_pos_sale_report.py new file mode 100644 index 00000000000..1c1509ca4d4 --- /dev/null +++ b/addons/pos_sale/tests/test_pos_sale_report.py @@ -0,0 +1,39 @@ +# -*- coding: utf-8 -*- +# Part of Odoo. See LICENSE file for full copyright and licensing details. + +import odoo + +from odoo.addons.point_of_sale.tests.common import TestPoSCommon + + +@odoo.tests.tagged('post_install', '-at_install') +class TestPoSSaleReport(TestPoSCommon): + + def setUp(self): + super(TestPoSSaleReport, self).setUp() + self.config = self.basic_config + self.product0 = self.create_product('Product 0', self.categ_basic, 0.0, 0.0) + + def test_weight_and_volume(self): + self.product0.product_tmpl_id.weight = 3 + self.product0.product_tmpl_id.volume = 4 + + self.open_new_session() + session = self.pos_session + orders = [] + + # Process two orders + orders.append(self.create_ui_order_data([(self.product0, 3)])) + orders.append(self.create_ui_order_data([(self.product0, 1)])) + self.env['pos.order'].create_from_ui(orders) + # Duplicate the first line of the first order + session.order_ids[0].lines.copy() + + session.action_pos_session_closing_control() + + # Orders are reversed, so reports[0] will correspond to the second order + reports = self.env['sale.report'].sudo().search([('product_id', '=', self.product0.id)], order='id desc', limit=2) + self.assertEqual(reports[0].weight, 3) + self.assertEqual(reports[0].volume, 4) + self.assertEqual(reports[1].weight, 18) + self.assertEqual(reports[1].volume, 24)