details: https://code.tryton.org/tryton/commit/1fd1b8cb6ac6
branch: default
user: Cédric Krier <[email protected]>
date: Mon Mar 17 18:24:14 2025 +0100
description:
Add product volume and weight to price list formula
Closes #13926
diffstat:
modules/product_measurements/CHANGELOG | 1 +
modules/product_measurements/product.py | 70
+++++++++
modules/product_measurements/product.xml | 7 +
modules/product_measurements/setup.py | 7 +
modules/product_measurements/tests/test_module.py | 77
+++++++++-
modules/product_measurements/tryton.cfg | 7 +
modules/product_measurements/view/product_price_list_line_form.xml | 11 +
7 files changed, 178 insertions(+), 2 deletions(-)
diffs (257 lines):
diff -r 70af6f8634b2 -r 1fd1b8cb6ac6 modules/product_measurements/CHANGELOG
--- a/modules/product_measurements/CHANGELOG Mon Oct 27 19:20:23 2025 +0100
+++ b/modules/product_measurements/CHANGELOG Mon Mar 17 18:24:14 2025 +0100
@@ -1,3 +1,4 @@
+* Add volume and weight to price list formula
Version 7.6.0 - 2025-04-28
--------------------------
diff -r 70af6f8634b2 -r 1fd1b8cb6ac6 modules/product_measurements/product.py
--- a/modules/product_measurements/product.py Mon Oct 27 19:20:23 2025 +0100
+++ b/modules/product_measurements/product.py Mon Mar 17 18:24:14 2025 +0100
@@ -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 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 -r 70af6f8634b2 -r 1fd1b8cb6ac6 modules/product_measurements/product.xml
--- a/modules/product_measurements/product.xml Mon Oct 27 19:20:23 2025 +0100
+++ b/modules/product_measurements/product.xml Mon Mar 17 18:24:14 2025 +0100
@@ -11,4 +11,11 @@
</record>
</data>
+ <data depends="product_price_list">
+ <record model="ir.ui.view" id="product_price_list_line_view_form">
+ <field name="model">product.price_list.line</field>
+ <field name="inherit"
ref="product_price_list.price_list_line_view_form"/>
+ <field name="name">product_price_list_line_form</field>
+ </record>
+ </data>
</tryton>
diff -r 70af6f8634b2 -r 1fd1b8cb6ac6 modules/product_measurements/setup.py
--- a/modules/product_measurements/setup.py Mon Oct 27 19:20:23 2025 +0100
+++ b/modules/product_measurements/setup.py Mon Mar 17 18:24:14 2025 +0100
@@ -47,6 +47,10 @@
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 @@
license='GPL-3',
python_requires='>=3.9',
install_requires=requires,
+ extras_require={
+ 'test': tests_require,
+ },
zip_safe=False,
entry_points="""
[trytond.modules]
diff -r 70af6f8634b2 -r 1fd1b8cb6ac6
modules/product_measurements/tests/test_module.py
--- a/modules/product_measurements/tests/test_module.py Mon Oct 27 19:20:23
2025 +0100
+++ b/modules/product_measurements/tests/test_module.py Mon Mar 17 18:24:14
2025 +0100
@@ -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 -r 70af6f8634b2 -r 1fd1b8cb6ac6 modules/product_measurements/tryton.cfg
--- a/modules/product_measurements/tryton.cfg Mon Oct 27 19:20:23 2025 +0100
+++ b/modules/product_measurements/tryton.cfg Mon Mar 17 18:24:14 2025 +0100
@@ -4,6 +4,8 @@
ir
product
res
+extras_depend:
+ product_price_list
xml:
product.xml
@@ -11,3 +13,8 @@
model:
product.Template
product.Product
+
+[register product_price_list]
+model:
+ product.PriceList
+ product.PriceListLine
diff -r 70af6f8634b2 -r 1fd1b8cb6ac6
modules/product_measurements/view/product_price_list_line_form.xml
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/modules/product_measurements/view/product_price_list_line_form.xml
Mon Mar 17 18:24:14 2025 +0100
@@ -0,0 +1,11 @@
+<?xml version="1.0"?>
+<!-- This file is part of Tryton. The COPYRIGHT file at the top level of
+this repository contains the full copyright notices and license terms. -->
+<data>
+ <xpath expr="//field[@name='formula']" position="after">
+ <label name="volume_uom"/>
+ <field name="volume_uom" widget="selection"/>
+ <label name="weight_uom"/>
+ <field name="weight_uom" widget="selection"/>
+ </xpath>
+</data>