[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:
@@ -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
|
||||
|
||||
@@ -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)
|
||||
Reference in New Issue
Block a user