diff --git a/modules/product_measurements/CHANGELOG b/modules/product_measurements/CHANGELOG index ad367aa4ea..24b55f5f69 100644 --- a/modules/product_measurements/CHANGELOG +++ b/modules/product_measurements/CHANGELOG @@ -1,3 +1,4 @@ +* Add volume and weight to price list formula Version 7.6.0 - 2025-04-28 -------------------------- diff --git a/modules/product_measurements/product.py b/modules/product_measurements/product.py index 656985b3e6..25ee33302e 100644 --- a/modules/product_measurements/product.py +++ b/modules/product_measurements/product.py @@ -1,5 +1,8 @@ # This file is part of Tryton. The COPYRIGHT file at the top level of # this repository contains the full copyright notices and license terms. + +from decimal import Decimal + from trytond.model import fields from trytond.pool import Pool, PoolMeta from trytond.pyson import Bool, Eval, Id @@ -120,3 +123,70 @@ class Template(metaclass=PoolMeta): class Product(metaclass=PoolMeta): __name__ = 'product.product' + + +class PriceList(metaclass=PoolMeta): + __name__ = 'product.price_list' + + def get_context_formula(self, product, quantity, uom, pattern=None): + pool = Pool() + UoM = pool.get('product.uom') + ModelData = pool.get('ir.model.data') + + liter = UoM(ModelData.get_id('product', 'uom_liter')) + kilogram = UoM(ModelData.get_id('product', 'uom_kilogram')) + + context = super().get_context_formula( + product, quantity, uom, pattern=pattern) + volume = weight = 0 + if product: + if product.volume is not None: + volume = UoM.compute_qty( + product.volume_uom, product.volume, liter, round=False) + if product.weight is not None: + weight = UoM.compute_qty( + product.weight_uom, product.weight, kilogram, round=False) + context['names']['volume'] = Decimal(str(volume)) + context['names']['weight'] = Decimal(str(weight)) + return context + + +class PriceListLine(metaclass=PoolMeta): + __name__ = 'product.price_list.line' + + volume_uom = fields.Many2One( + 'product.uom', "Volume UoM", + domain=[('category', '=', Id('product', 'uom_cat_volume'))], + help="Leave empty for liter.") + weight_uom = fields.Many2One( + 'product.uom', "Weight UoM", + domain=[('category', '=', Id('product', 'uom_cat_weight'))], + help="Leave empty for kilogram.") + + @classmethod + def __setup__(cls): + super().__setup__() + cls.formula.help += ("\n" + "-volume: the volume of 1 unit of product\n" + "-weight: the weight of 1 unit of product") + + def get_unit_price(self, **context): + pool = Pool() + UoM = pool.get('product.uom') + ModelData = pool.get('ir.model.data') + + if self.volume_uom: + context['names'] = context['names'].copy() + liter = UoM(ModelData.get_id('product', 'uom_liter')) + volume = UoM.compute_qty( + liter, float(context['names']['volume']), self.volume_uom, + round=False) + context['names']['volume'] = Decimal(str(volume)) + if self.weight_uom: + context['names'] = context['names'].copy() + kilogram = UoM(ModelData.get_id('product', 'uom_kilogram')) + weight = UoM.compute_qty( + kilogram, float(context['names']['weight']), self.weight_uom, + round=False) + context['names']['weight'] = Decimal(str(weight)) + return super().get_unit_price(**context) diff --git a/modules/product_measurements/product.xml b/modules/product_measurements/product.xml index c1be0f025d..fc0bebe3b4 100644 --- a/modules/product_measurements/product.xml +++ b/modules/product_measurements/product.xml @@ -11,4 +11,11 @@ this repository contains the full copyright notices and license terms. --> + + + product.price_list.line + + product_price_list_line_form + + diff --git a/modules/product_measurements/setup.py b/modules/product_measurements/setup.py index bd07f9f7ab..0f7c47cc9f 100755 --- a/modules/product_measurements/setup.py +++ b/modules/product_measurements/setup.py @@ -47,6 +47,10 @@ for dep in info.get('depends', []): requires.append(get_require_version('trytond_%s' % dep)) requires.append(get_require_version('trytond')) +tests_require = [ + get_require_version('trytond_product_price_list'), + ] + setup(name=name, version=version, description='Tryton module to add measurements to product', @@ -116,6 +120,9 @@ setup(name=name, license='GPL-3', python_requires='>=3.9', install_requires=requires, + extras_require={ + 'test': tests_require, + }, zip_safe=False, entry_points=""" [trytond.modules] diff --git a/modules/product_measurements/tests/test_module.py b/modules/product_measurements/tests/test_module.py index c0493c8bff..f39aecd96f 100644 --- a/modules/product_measurements/tests/test_module.py +++ b/modules/product_measurements/tests/test_module.py @@ -1,12 +1,85 @@ # This file is part of Tryton. The COPYRIGHT file at the top level of # this repository contains the full copyright notices and license terms. -from trytond.tests.test_tryton import ModuleTestCase +from decimal import Decimal + +from trytond.modules.company.tests import create_company, set_company +from trytond.pool import Pool +from trytond.tests.test_tryton import ModuleTestCase, with_transaction class ProductMeasurementsTestCase(ModuleTestCase): - 'Test ProductMeasurements module' + "Test Product Measurements module" module = 'product_measurements' + extras = ['product_price_list'] + + @with_transaction() + def test_price_list_context_formula_volume(self): + "Test price list context formula with volume" + pool = Pool() + Template = pool.get('product.template') + Product = pool.get('product.product') + UoM = pool.get('product.uom') + PriceList = pool.get('product.price_list') + + unit, = UoM.search([('name', '=', "Unit")]) + cm3, = UoM.search([('name', '=', "Cubic centimeter")]) + inch3, = UoM.search([('name', '=', "Cubic inch")]) + + company = create_company() + with set_company(company): + template = Template( + name="Product", default_uom=unit, products=None, + list_price=Decimal('100'), + volume=2, volume_uom=cm3) + template.save() + product = Product(template=template) + product.save() + + price_list = PriceList( + name="List", price='list_price', + lines=[{ + 'formula': 'unit_price + 5 * volume', + 'volume_uom': inch3}]) + price_list.save() + + self.assertEqual( + price_list.compute(product, 5, unit), + Decimal('100.6102')) + + @with_transaction() + def test_price_list_context_formula_weight(self): + "Test price list context formula with weight" + pool = Pool() + Template = pool.get('product.template') + Product = pool.get('product.product') + UoM = pool.get('product.uom') + PriceList = pool.get('product.price_list') + + unit, = UoM.search([('name', '=', "Unit")]) + gram, = UoM.search([('name', '=', "Gram")]) + pound, = UoM.search([('name', '=', "Pound")]) + + company = create_company() + with set_company(company): + template = Template( + name="Product", default_uom=unit, products=None, + list_price=Decimal('100'), + weight=5, weight_uom=gram) + template.save() + product = Product(template=template) + product.save() + + price_list = PriceList( + name="List", price='list_price', + lines=[{ + 'formula': 'unit_price + 5 * weight', + 'weight_uom': pound}]) + price_list.save() + + self.assertEqual( + price_list.compute(product, 5, unit), + Decimal('100.0551')) del ModuleTestCase diff --git a/modules/product_measurements/tryton.cfg b/modules/product_measurements/tryton.cfg index 1e0687bba9..3bbd4a5dd1 100644 --- a/modules/product_measurements/tryton.cfg +++ b/modules/product_measurements/tryton.cfg @@ -4,6 +4,8 @@ depends: ir product res +extras_depend: + product_price_list xml: product.xml @@ -11,3 +13,8 @@ xml: model: product.Template product.Product + +[register product_price_list] +model: + product.PriceList + product.PriceListLine diff --git a/modules/product_measurements/view/product_price_list_line_form.xml b/modules/product_measurements/view/product_price_list_line_form.xml new file mode 100644 index 0000000000..83e08e3763 --- /dev/null +++ b/modules/product_measurements/view/product_price_list_line_form.xml @@ -0,0 +1,11 @@ + + + + + +