details:   https://code.tryton.org/tryton/commit/2d2af73e62f2
branch:    default
user:      Cédric Krier <[email protected]>
date:      Fri Oct 17 17:59:53 2025 +0200
description:
        Allow a different gross price to be set for each variant

        Closes #14299
diffstat:

 modules/sale_point/CHANGELOG                     |   1 +
 modules/sale_point/product.py                    |  95 +++++++++++++++++++----
 modules/sale_point/sale.py                       |   4 +-
 modules/sale_point/tests/scenario_sale_point.rst |   3 +
 4 files changed, 85 insertions(+), 18 deletions(-)

diffs (153 lines):

diff -r ddc9995716f4 -r 2d2af73e62f2 modules/sale_point/CHANGELOG
--- a/modules/sale_point/CHANGELOG      Sat Nov 22 10:45:44 2025 +0100
+++ b/modules/sale_point/CHANGELOG      Fri Oct 17 17:59:53 2025 +0200
@@ -1,3 +1,4 @@
+* Allow a different gross price to be set for each variant
 
 Version 7.6.0 - 2025-04-28
 --------------------------
diff -r ddc9995716f4 -r 2d2af73e62f2 modules/sale_point/product.py
--- a/modules/sale_point/product.py     Sat Nov 22 10:45:44 2025 +0100
+++ b/modules/sale_point/product.py     Fri Oct 17 17:59:53 2025 +0200
@@ -7,7 +7,25 @@
 from trytond.pyson import Eval
 
 
-class Template(metaclass=PoolMeta):
+class _GrossPriceMixin:
+    __slots__ = ()
+
+    @fields.depends(
+        'gross_price', 'account_category', methods=['customer_taxes_used'])
+    def on_change_gross_price(self):
+        pool = Pool()
+        Date = pool.get('ir.date')
+        Tax = pool.get('account.tax')
+        if self.gross_price is None or not self.account_category:
+            return
+        today = Date.today()
+        self.list_price = round_price(Tax.reverse_compute(
+                self.gross_price,
+                self.customer_taxes_used,
+                today))
+
+
+class Template(_GrossPriceMixin, metaclass=PoolMeta):
     __name__ = 'product.template'
 
     gross_price = fields.MultiValue(fields.Numeric(
@@ -26,27 +44,72 @@
             return pool.get('product.gross_price')
         return super().multivalue_model(field)
 
-    @fields.depends(
-        'gross_price', 'account_category', methods=['customer_taxes_used'])
-    def on_change_gross_price(self):
+
+class Product(_GrossPriceMixin, metaclass=PoolMeta):
+    __name__ = 'product.product'
+
+    gross_price = fields.MultiValue(fields.Numeric(
+            "Gross Price", digits=price_digits,
+            states={
+                'invisible': ~Eval('salable', False),
+                },
+            help="The price with default tax included.\n"
+            "Leave empty to use the gross price of the product."))
+    gross_prices = fields.One2Many(
+        'product.gross_price', 'product', "Gross Prices")
+    gross_price_used = fields.Function(fields.Numeric(
+            "Gross Price", digits=price_digits,
+            help="The price with default tax included."),
+        'get_gross_price_used')
+
+    @classmethod
+    def multivalue_model(cls, field):
         pool = Pool()
-        Date = pool.get('ir.date')
-        Tax = pool.get('account.tax')
-        if self.gross_price is None or not self.account_category:
-            return
-        today = Date.today()
-        self.list_price = round_price(Tax.reverse_compute(
-                self.gross_price,
-                self.customer_taxes_used,
-                today))
+        if field == 'gross_price':
+            return pool.get('product.gross_price')
+        return super().multivalue_model(field)
+
+    def set_multivalue(self, name, value, save=True, **pattern):
+        if name == 'gross_price':
+            pattern.setdefault('template', self.template.id)
+        return super().set_multivalue(name, value, save=save, **pattern)
 
+    def get_multivalue(self, name, **pattern):
+        if name == 'gross_price':
+            pattern.setdefault('template', self.template.id)
+        return super().get_multivalue(name, **pattern)
 
-class Product(metaclass=PoolMeta):
-    __name__ = 'product.product'
+    @fields.depends('_parent_template.id')
+    def on_change_gross_price(self):
+        return super().on_change_gross_price()
+
+    def get_gross_price_used(self, name):
+        gross_price = self.get_multivalue('gross_price')
+        if gross_price is None:
+            gross_price = self.template.get_multivalue('gross_price')
+        return gross_price
 
 
 class GrossPrice(ModelSQL, CompanyValueMixin):
     __name__ = 'product.gross_price'
     template = fields.Many2One(
-        'product.template', "Template", ondelete='CASCADE')
+        'product.template', "Template", ondelete='CASCADE', required=True,
+        context={
+            'company': Eval('company', -1),
+            },
+        depends=['company'])
+    product = fields.Many2One(
+        'product.product', "Product", ondelete='CASCADE',
+        domain=[
+            ('template', '=', Eval('template', -1)),
+            ],
+        context={
+            'company': Eval('company', -1),
+            },
+        depends=['company'])
     gross_price = fields.Numeric("Gross Price", digits=price_digits)
+
+    @classmethod
+    def __setup__(cls):
+        super().__setup__()
+        cls.company.required = True
diff -r ddc9995716f4 -r 2d2af73e62f2 modules/sale_point/sale.py
--- a/modules/sale_point/sale.py        Sat Nov 22 10:45:44 2025 +0100
+++ b/modules/sale_point/sale.py        Fri Oct 17 17:59:53 2025 +0200
@@ -541,8 +541,8 @@
         self.unit_list_price = None
         if self.product and self.sale and self.sale.point:
             if (self.sale.point.tax_included
-                    and self.product.gross_price is not None):
-                self.unit_gross_price = self.product.gross_price
+                    and self.product.gross_price_used is not None):
+                self.unit_gross_price = self.product.gross_price_used
                 self.unit_list_price = round_price(Tax.reverse_compute(
                         self.unit_gross_price, self.taxes,
                         date=self.sale.date))
diff -r ddc9995716f4 -r 2d2af73e62f2 
modules/sale_point/tests/scenario_sale_point.rst
--- a/modules/sale_point/tests/scenario_sale_point.rst  Sat Nov 22 10:45:44 
2025 +0100
+++ b/modules/sale_point/tests/scenario_sale_point.rst  Fri Oct 17 17:59:53 
2025 +0200
@@ -67,6 +67,9 @@
     Decimal('9.9174')
     >>> template.save()
     >>> goods, = template.products
+    >>> goods.gross_price = Decimal('14.0000')
+    >>> goods.list_price
+    Decimal('11.5702')
 
     >>> template = ProductTemplate()
     >>> template.name = 'service'

Reply via email to