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>

Reply via email to