[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 <adwid@users.noreply.github.com>
This commit is contained in:
Adrien Widart
2021-06-29 14:45:02 +00:00
parent 75efaca796
commit b3fdc5827c
3 changed files with 45 additions and 8 deletions
+2 -8
View File
@@ -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
+4
View File
@@ -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
@@ -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)