From 79ebe1060d7a6303dbd5ff6e401aec5337161883 Mon Sep 17 00:00:00 2001 From: Martin Trigaux Date: Thu, 23 Oct 2014 17:14:19 +0200 Subject: [PATCH 1/2] [FIX] product: pricelist, uom and price_surcharge The price_surcharge attribute must be computed based on the reference unit of measure (divided by the factor). This is to make sure than 12 units and 1 dozen have the same price after pricelist computation (opw 599727). Added test checking the correctness of pricelist computation based on unit of measures. --- addons/product/pricelist.py | 7 ++- addons/product/tests/__init__.py | 3 +- addons/product/tests/test_pricelist.py | 70 ++++++++++++++++++++++++++ 3 files changed, 78 insertions(+), 2 deletions(-) create mode 100644 addons/product/tests/test_pricelist.py diff --git a/addons/product/pricelist.py b/addons/product/pricelist.py index d4ed2567afc..0dd6b45acda 100644 --- a/addons/product/pricelist.py +++ b/addons/product/pricelist.py @@ -297,7 +297,12 @@ class product_pricelist(osv.osv): price = price * (1.0+(res['price_discount'] or 0.0)) if res['price_round']: price = tools.float_round(price, precision_rounding=res['price_round']) - price += (res['price_surcharge'] or 0.0) + if context.get('uom'): + # compute price_surcharge based on reference uom + factor = product_uom_obj.browse(cr, uid, context.get('uom'), context=context).factor + else: + factor = 1.0 + price += (res['price_surcharge'] or 0.0) / factor if res['price_min_margin']: price = max(price, price_limit+res['price_min_margin']) if res['price_max_margin']: diff --git a/addons/product/tests/__init__.py b/addons/product/tests/__init__.py index 850189ac0f4..4a1ce43351b 100644 --- a/addons/product/tests/__init__.py +++ b/addons/product/tests/__init__.py @@ -1,5 +1,6 @@ -from . import test_uom +from . import test_uom, test_pricelist fast_suite = [ test_uom, + test_pricelist ] diff --git a/addons/product/tests/test_pricelist.py b/addons/product/tests/test_pricelist.py new file mode 100644 index 00000000000..dd9130e1be5 --- /dev/null +++ b/addons/product/tests/test_pricelist.py @@ -0,0 +1,70 @@ +from openerp.tests.common import TransactionCase + +class TestPricelist(TransactionCase): + """Tests for unit of measure conversion""" + + def setUp(self): + super(TestPricelist, self).setUp() + cr, uid, context = self.cr, self.uid, {} + self.ir_model_data = self.registry('ir.model.data') + self.product_product = self.registry('product.product') + self.product_pricelist = self.registry('product.pricelist') + self.uom = self.registry('product.uom') + + self.usb_adapter_id = self.ir_model_data.get_object_reference(cr, uid, 'product', 'product_product_48')[1] + self.datacard_id = self.ir_model_data.get_object_reference(cr, uid, 'product', 'product_template_46')[1] + self.unit_id = self.ir_model_data.get_object_reference(cr, uid, 'product', 'product_uom_unit')[1] + self.dozen_id = self.ir_model_data.get_object_reference(cr, uid, 'product', 'product_uom_dozen')[1] + + self.public_pricelist_id = self.ir_model_data.get_object_reference(cr, uid, 'product', 'list0')[1] + self.sale_pricelist_id = self.product_pricelist.create(cr, uid, { + 'name': 'Sale pricelist', + 'type': 'sale', + 'version_id': [(0, 0, { + 'name': 'v1.0', + 'items_id': [(0, 0, { + 'name': 'Discount 10%', + 'base': 1, # based on public price + 'price_discount': -0.1, + 'product_id': self.usb_adapter_id + }), (0, 0, { + 'name': 'Discount -0.5', + 'base': 1, # based on public price + 'price_surcharge': -0.5, + 'product_id': self.datacard_id + })] + })] + }, context=context) + + def test_10_discount(self): + # Make sure the price using a pricelist is the same than without after + # applying the computation manually + cr, uid, context = self.cr, self.uid, {} + + public_context = dict(context, pricelist=self.public_pricelist_id) + pricelist_context = dict(context, pricelist=self.sale_pricelist_id) + + usb_adapter_without_pricelist = self.product_product.browse(cr, uid, self.usb_adapter_id, context=public_context) + usb_adapter_with_pricelist = self.product_product.browse(cr, uid, self.usb_adapter_id, context=pricelist_context) + self.assertEqual(usb_adapter_with_pricelist.price, usb_adapter_without_pricelist.price*0.9) + + datacard_without_pricelist = self.product_product.browse(cr, uid, self.datacard_id, context=public_context) + datacard_with_pricelist = self.product_product.browse(cr, uid, self.datacard_id, context=pricelist_context) + self.assertEqual(datacard_with_pricelist.price, datacard_without_pricelist.price-0.5) + + # Make sure that changing the unit of measure does not break the unit + # price (after converting) + unit_context = dict(context, + pricelist=self.sale_pricelist_id, + uom=self.unit_id) + dozen_context = dict(context, + pricelist=self.sale_pricelist_id, + uom=self.dozen_id) + + usb_adapter_unit = self.product_product.browse(cr, uid, self.usb_adapter_id, context=unit_context) + usb_adapter_dozen = self.product_product.browse(cr, uid, self.usb_adapter_id, context=dozen_context) + self.assertAlmostEqual(usb_adapter_unit.price*12, usb_adapter_dozen.price) + + datacard_unit = self.product_product.browse(cr, uid, self.datacard_id, context=unit_context) + datacard_dozen = self.product_product.browse(cr, uid, self.datacard_id, context=dozen_context) + self.assertAlmostEqual(datacard_unit.price*12, datacard_dozen.price) From 42bf0a567003168029508e0b501d6aef7876e41f Mon Sep 17 00:00:00 2001 From: Martin Trigaux Date: Fri, 24 Oct 2014 16:07:55 +0200 Subject: [PATCH 2/2] [FIX] product_visible_discount: discount with different unit of measures The computation of the price without pricelist should take care of the unit of measure. e.g. if computing discount for objects in dozen (on a product with price in unit), returned unit price should be (price*12) where 12 is the factor to go from dozen to unit. Otherwise the compared prices (with and without pricelist) would not use the same unit of measure and the comparaison would be inconsistent. (opw 599727) --- addons/product_visible_discount/product_visible_discount.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/addons/product_visible_discount/product_visible_discount.py b/addons/product_visible_discount/product_visible_discount.py index 595deba1983..f173e844b27 100644 --- a/addons/product_visible_discount/product_visible_discount.py +++ b/addons/product_visible_discount/product_visible_discount.py @@ -44,6 +44,7 @@ class sale_order_line(osv.osv): fiscal_position=False, flag=False, context=None): def get_real_price(res_dict, product_id, qty, uom, pricelist): + """Retrieve the price before applying the pricelist""" item_obj = self.pool.get('product.pricelist.item') price_type_obj = self.pool.get('product.price.type') product_obj = self.pool.get('product.product') @@ -60,9 +61,8 @@ class sale_order_line(osv.osv): factor = 1.0 if uom and uom != product.uom_id.id: - product_uom_obj = self.pool.get('product.uom') - uom_data = product_uom_obj.browse(cr, uid, product.uom_id.id) - factor = uom_data.factor + # the unit price is in a different uom + factor = self.pool['product.uom']._compute_qty(cr, uid, uom, 1.0, product.uom_id.id) return product_read[field_name] * factor